Skip to content

Commit

Permalink
test: Update eggsampler/acme to support draft-ietf-acme-ari-03 (#7470)
Browse files Browse the repository at this point in the history
`Eggsampler/acme` v3.6.0 has been
[released](https://github.com/eggsampler/acme/releases/tag/v3.6.0). I've
updated the ARI integration tests to issue replacement orders.

Fixes #7463

---------

Co-authored-by: Aaron Gable <aaron@letsencrypt.org>
  • Loading branch information
pgporada and aarongable authored May 8, 2024
1 parent c1561b0 commit 42b2240
Show file tree
Hide file tree
Showing 11 changed files with 178 additions and 164 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ require (
github.com/aws/aws-sdk-go-v2/config v1.26.3
github.com/aws/aws-sdk-go-v2/service/s3 v1.50.2
github.com/aws/smithy-go v1.20.0
github.com/eggsampler/acme/v3 v3.5.0
github.com/eggsampler/acme/v3 v3.6.0
github.com/go-jose/go-jose/v4 v4.0.1
github.com/go-logr/stdr v1.2.2
github.com/go-sql-driver/mysql v1.5.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,8 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/eggsampler/acme/v3 v3.5.0 h1:tM8IXhS95HLm2LGxRDI3yzQrs7iZ9mKep1JjQhTIsUo=
github.com/eggsampler/acme/v3 v3.5.0/go.mod h1:/qh0rKC/Dh7Jj+p4So7DbWmFNzC4dpcpK53r226Fhuo=
github.com/eggsampler/acme/v3 v3.6.0 h1:TbQYoWlpl62fTdJq5i2LHBDY6h3LDU3pPAdyoUSQMOc=
github.com/eggsampler/acme/v3 v3.6.0/go.mod h1:/qh0rKC/Dh7Jj+p4So7DbWmFNzC4dpcpK53r226Fhuo=
github.com/envoyproxy/protoc-gen-validate v1.0.2 h1:QkIBuU5k+x7/QXPvPPnWXWlCdaBFApVqftFV6k087DA=
github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE=
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
Expand Down
57 changes: 35 additions & 22 deletions test/integration/ari_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,18 @@
package integration

import (
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/x509/pkix"
"math/big"
"os"
"testing"
"time"

"github.com/eggsampler/acme/v3"

"github.com/letsencrypt/boulder/test"
ocsp_helper "github.com/letsencrypt/boulder/test/ocsp/helper"
)

// certID matches the ASN.1 structure of the CertID sequence defined by RFC6960.
Expand Down Expand Up @@ -45,28 +44,44 @@ func TestARI(t *testing.T) {
test.AssertNotError(t, err, "failed to issue test cert")

cert := ir.certs[0]
issuer, err := ocsp_helper.GetIssuer(cert)
test.AssertNotError(t, err, "failed to get issuer cert")

eri, err := client.GetRenewalInfo(cert, issuer, crypto.SHA256)
ari, err := client.GetRenewalInfo(cert)
test.AssertNotError(t, err, "ARI request should have succeeded")
test.AssertEquals(t, eri.SuggestedWindow.Start.Sub(time.Now()).Round(time.Hour), 1415*time.Hour)
test.AssertEquals(t, eri.SuggestedWindow.End.Sub(time.Now()).Round(time.Hour), 1463*time.Hour)
test.AssertEquals(t, eri.RetryAfter.Sub(time.Now()).Round(time.Hour), 6*time.Hour)

// Revoke the cert, re-request ARI, and the window should now be in the past.
test.AssertEquals(t, ari.SuggestedWindow.Start.Sub(time.Now()).Round(time.Hour), 1415*time.Hour)
test.AssertEquals(t, ari.SuggestedWindow.End.Sub(time.Now()).Round(time.Hour), 1463*time.Hour)
test.AssertEquals(t, ari.RetryAfter.Sub(time.Now()).Round(time.Hour), 6*time.Hour)

// TODO(@pgporada): Clean this up when 'test/config/{sa,wfe2}.json' sets
// TrackReplacementCertificatesARI=true.
if os.Getenv("BOULDER_CONFIG_DIR") == "test/config-next" {
// Make a new order which indicates that it replaces the cert issued above.
_, order, err := makeClientAndOrder(client, key, []string{name}, true, cert)
test.AssertNotError(t, err, "failed to issue test cert")
replaceID, err := acme.GenerateARICertID(cert)
test.AssertNotError(t, err, "failed to generate ARI certID")
test.AssertEquals(t, order.Replaces, replaceID)
test.AssertNotEquals(t, order.Replaces, "")

// Try it again and verify it fails
_, order, err = makeClientAndOrder(client, key, []string{name}, true, cert)
test.AssertError(t, err, "subsequent ARI replacements for a replaced cert should fail, but didn't")
} else {
// ARI is disabled so we only use the client to POST the replacement
// order, but we never finalize it.
replacementOrder, err := client.ReplacementOrder(client.Account, cert, []acme.Identifier{{Type: "dns", Value: name}})
test.AssertNotError(t, err, "ARI replacement request should have succeeded")
test.AssertNotEquals(t, replacementOrder.Replaces, "")
}

// Revoke the cert and re-request ARI. The renewal window should now be in
// the past indicating to the client that a renewal should happen
// immediately.
err = client.RevokeCertificate(client.Account, cert, client.PrivateKey, 0)
test.AssertNotError(t, err, "failed to revoke cert")

eri, err = client.GetRenewalInfo(cert, issuer, crypto.SHA256)
test.AssertNotError(t, err, "ARI request should have succeeded")
test.Assert(t, eri.SuggestedWindow.End.Before(time.Now()), "suggested window should end in the past")
test.Assert(t, eri.SuggestedWindow.Start.Before(eri.SuggestedWindow.End), "suggested window should start before it ends")

// Check that marking the cert as replaced succeeds, but don't check that
// any server state has been updated (because that doesn't happen, yet).
err = client.UpdateRenewalInfo(client.Account, cert, issuer, crypto.SHA256, true)
ari, err = client.GetRenewalInfo(cert)
test.AssertNotError(t, err, "ARI request should have succeeded")
test.Assert(t, ari.SuggestedWindow.End.Before(time.Now()), "suggested window should end in the past")
test.Assert(t, ari.SuggestedWindow.Start.Before(ari.SuggestedWindow.End), "suggested window should start before it ends")

// Try to make a new cert for a new domain, but sabotage the CT logs so
// issuance fails. Recover the precert from CT, then request ARI and check
Expand All @@ -79,10 +94,8 @@ func TestARI(t *testing.T) {

cert, err = ctFindRejection([]string{name})
test.AssertNotError(t, err, "failed to find rejected precert")
issuer, err = ocsp_helper.GetIssuer(cert)
test.AssertNotError(t, err, "failed to get issuer cert")

eri, err = client.GetRenewalInfo(cert, issuer, crypto.SHA256)
ari, err = client.GetRenewalInfo(cert)
test.AssertError(t, err, "ARI request should have failed")
test.AssertEquals(t, err.(acme.Problem).Status, 404)
}
13 changes: 9 additions & 4 deletions test/integration/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ func delHTTP01Response(token string) error {
return nil
}

func makeClientAndOrder(c *client, csrKey *ecdsa.PrivateKey, domains []string, cn bool) (*client, *acme.Order, error) {
func makeClientAndOrder(c *client, csrKey *ecdsa.PrivateKey, domains []string, cn bool, certToReplace *x509.Certificate) (*client, *acme.Order, error) {
var err error
if c == nil {
c, err = makeClient()
Expand All @@ -102,7 +102,12 @@ func makeClientAndOrder(c *client, csrKey *ecdsa.PrivateKey, domains []string, c
for _, domain := range domains {
ids = append(ids, acme.Identifier{Type: "dns", Value: domain})
}
order, err := c.Client.NewOrder(c.Account, ids)
var order acme.Order
if certToReplace != nil {
order, err = c.Client.ReplacementOrder(c.Account, certToReplace, ids)
} else {
order, err = c.Client.NewOrder(c.Account, ids)
}
if err != nil {
return nil, nil, err
}
Expand Down Expand Up @@ -151,7 +156,7 @@ type issuanceResult struct {
func authAndIssue(c *client, csrKey *ecdsa.PrivateKey, domains []string, cn bool) (*issuanceResult, error) {
var err error

c, order, err := makeClientAndOrder(c, csrKey, domains, cn)
c, order, err := makeClientAndOrder(c, csrKey, domains, cn, nil)
if err != nil {
return nil, err
}
Expand All @@ -169,7 +174,7 @@ type issuanceResultAllChains struct {
}

func authAndIssueFetchAllChains(c *client, csrKey *ecdsa.PrivateKey, domains []string, cn bool) (*issuanceResultAllChains, error) {
c, order, err := makeClientAndOrder(c, csrKey, domains, cn)
c, order, err := makeClientAndOrder(c, csrKey, domains, cn, nil)
if err != nil {
return nil, err
}
Expand Down
2 changes: 1 addition & 1 deletion vendor/github.com/eggsampler/acme/v3/account.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions vendor/github.com/eggsampler/acme/v3/acme.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

150 changes: 46 additions & 104 deletions vendor/github.com/eggsampler/acme/v3/ari.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion vendor/github.com/eggsampler/acme/v3/challenge.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 42b2240

Please sign in to comment.