github.com/letsencrypt/boulder@v0.20251208.0/test/integration/ari_test.go (about) 1 //go:build integration 2 3 package integration 4 5 import ( 6 "crypto/ecdsa" 7 "crypto/elliptic" 8 "crypto/rand" 9 "crypto/x509/pkix" 10 "math/big" 11 "os" 12 "testing" 13 "time" 14 15 "github.com/eggsampler/acme/v3" 16 17 "github.com/letsencrypt/boulder/test" 18 ) 19 20 // certID matches the ASN.1 structure of the CertID sequence defined by RFC6960. 21 type certID struct { 22 HashAlgorithm pkix.AlgorithmIdentifier 23 IssuerNameHash []byte 24 IssuerKeyHash []byte 25 SerialNumber *big.Int 26 } 27 28 func TestARIAndReplacement(t *testing.T) { 29 t.Parallel() 30 31 // Setup 32 client, err := makeClient("mailto:example@letsencrypt.org") 33 test.AssertNotError(t, err, "creating acme client") 34 key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) 35 test.AssertNotError(t, err, "creating random cert key") 36 37 // Issue a cert, request ARI, and check that both the suggested window and 38 // the retry-after header are approximately the right amount of time in the 39 // future. 40 name := random_domain() 41 ir, err := authAndIssue(client, key, []acme.Identifier{{Type: "dns", Value: name}}, true, "") 42 test.AssertNotError(t, err, "failed to issue test cert") 43 44 cert := ir.certs[0] 45 ari, err := client.GetRenewalInfo(cert) 46 test.AssertNotError(t, err, "ARI request should have succeeded") 47 test.AssertEquals(t, ari.SuggestedWindow.Start.Sub(time.Now()).Round(time.Hour), 1418*time.Hour) 48 test.AssertEquals(t, ari.SuggestedWindow.End.Sub(time.Now()).Round(time.Hour), 1461*time.Hour) 49 test.AssertEquals(t, ari.RetryAfter.Sub(time.Now()).Round(time.Hour), 6*time.Hour) 50 51 // Make a new order which indicates that it replaces the cert issued above, 52 // and verify that the replacement order succeeds. 53 _, order, err := makeClientAndOrder(client, key, []acme.Identifier{{Type: "dns", Value: name}}, true, "", cert) 54 test.AssertNotError(t, err, "failed to issue test cert") 55 replaceID, err := acme.GenerateARICertID(cert) 56 test.AssertNotError(t, err, "failed to generate ARI certID") 57 test.AssertEquals(t, order.Replaces, replaceID) 58 test.AssertNotEquals(t, order.Replaces, "") 59 60 // Retrieve the order and verify that it has the correct replaces field. 61 resp, err := client.FetchOrder(client.Account, order.URL) 62 test.AssertNotError(t, err, "failed to fetch order") 63 if os.Getenv("BOULDER_CONFIG_DIR") == "test/config-next" { 64 test.AssertEquals(t, resp.Replaces, order.Replaces) 65 } else { 66 test.AssertEquals(t, resp.Replaces, "") 67 } 68 69 // Try another replacement order and verify that it fails. 70 _, order, err = makeClientAndOrder(client, key, []acme.Identifier{{Type: "dns", Value: name}}, true, "", cert) 71 test.AssertError(t, err, "subsequent ARI replacements for a replaced cert should fail, but didn't") 72 test.AssertContains(t, err.Error(), "urn:ietf:params:acme:error:alreadyReplaced") 73 test.AssertContains(t, err.Error(), "already has a replacement order") 74 test.AssertContains(t, err.Error(), "error code 409") 75 } 76 77 func TestARIShortLived(t *testing.T) { 78 t.Parallel() 79 80 // Setup 81 client, err := makeClient("mailto:example@letsencrypt.org") 82 test.AssertNotError(t, err, "creating acme client") 83 key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) 84 test.AssertNotError(t, err, "creating random cert key") 85 86 // Issue a short-lived cert, request ARI, and check that both the suggested 87 // window and the retry-after header are approximately the right amount of 88 // time in the future. 89 ir, err := authAndIssue(client, key, []acme.Identifier{{Type: "dns", Value: random_domain()}}, true, "shortlived") 90 test.AssertNotError(t, err, "failed to issue test cert") 91 92 cert := ir.certs[0] 93 ari, err := client.GetRenewalInfo(cert) 94 test.AssertNotError(t, err, "ARI request should have succeeded") 95 test.AssertEquals(t, ari.SuggestedWindow.Start.Sub(time.Now()).Round(time.Hour), 78*time.Hour) 96 test.AssertEquals(t, ari.SuggestedWindow.End.Sub(time.Now()).Round(time.Hour), 81*time.Hour) 97 test.AssertEquals(t, ari.RetryAfter.Sub(time.Now()).Round(time.Hour), 6*time.Hour) 98 } 99 100 func TestARIRevoked(t *testing.T) { 101 t.Parallel() 102 103 // Setup 104 client, err := makeClient("mailto:example@letsencrypt.org") 105 test.AssertNotError(t, err, "creating acme client") 106 key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) 107 test.AssertNotError(t, err, "creating random cert key") 108 109 // Issue a cert, revoke it, request ARI, and check that the suggested window 110 // is in the past, indicating that a renewal should happen immediately. 111 ir, err := authAndIssue(client, key, []acme.Identifier{{Type: "dns", Value: random_domain()}}, true, "") 112 test.AssertNotError(t, err, "failed to issue test cert") 113 114 cert := ir.certs[0] 115 err = client.RevokeCertificate(client.Account, cert, client.PrivateKey, 0) 116 test.AssertNotError(t, err, "failed to revoke cert") 117 118 ari, err := client.GetRenewalInfo(cert) 119 test.AssertNotError(t, err, "ARI request should have succeeded") 120 test.Assert(t, ari.SuggestedWindow.End.Before(time.Now()), "suggested window should end in the past") 121 test.Assert(t, ari.SuggestedWindow.Start.Before(ari.SuggestedWindow.End), "suggested window should start before it ends") 122 } 123 124 func TestARIForPrecert(t *testing.T) { 125 t.Parallel() 126 127 // Setup 128 client, err := makeClient("mailto:example@letsencrypt.org") 129 test.AssertNotError(t, err, "creating acme client") 130 key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) 131 test.AssertNotError(t, err, "creating random cert key") 132 133 // Try to make a new cert for a new domain, but sabotage the CT logs so 134 // issuance fails. 135 name := random_domain() 136 err = ctAddRejectHost(name) 137 test.AssertNotError(t, err, "failed to add ct-test-srv reject host") 138 _, err = authAndIssue(client, key, []acme.Identifier{{Type: "dns", Value: name}}, true, "") 139 test.AssertError(t, err, "expected error from authAndIssue, was nil") 140 141 // Recover the precert from CT, then request ARI and check 142 // that it fails, because we don't serve ARI for non-issued certs. 143 cert, err := ctFindRejection([]string{name}) 144 test.AssertNotError(t, err, "failed to find rejected precert") 145 146 _, err = client.GetRenewalInfo(cert) 147 test.AssertError(t, err, "ARI request should have failed") 148 test.AssertEquals(t, err.(acme.Problem).Status, 404) 149 }