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  }