From 64d9ad7b383c2c5dc600a4462c0629d116c2467d Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Fri, 20 Jan 2023 16:54:55 +0100 Subject: [PATCH 1/9] Validate Subject Common Name for Orders with Permanent Identifier --- acme/order.go | 9 +++ acme/order_test.go | 196 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 205 insertions(+) diff --git a/acme/order.go b/acme/order.go index 7748df22..f5aac95a 100644 --- a/acme/order.go +++ b/acme/order.go @@ -165,6 +165,15 @@ func (o *Order) Finalize(ctx context.Context, db DB, csr *x509.CertificateReques for i := range o.Identifiers { if o.Identifiers[i].Type == PermanentIdentifier { permanentIdentifier = o.Identifiers[i].Value + // the first (and only) Permanent Identifier that gets added to the certificate + // should be equal to the Subject Common Name if it's set. If not equal, the CSR + // is rejected, because the Common Name hasn't been challenged in that case. This + // could result in unauthorized access if a relying system relies on the Common + // Name in its authorization logic. + if csr.Subject.CommonName != "" && csr.Subject.CommonName != permanentIdentifier { + return NewError(ErrorBadCSRType, "CSR Subject Common Name does not match identifiers exactly: "+ + "CSR Subject Common Name = %s, Order Permanent Identifier = %s", csr.Subject.CommonName, permanentIdentifier) + } break } } diff --git a/acme/order_test.go b/acme/order_test.go index 606e9f71..133eec25 100644 --- a/acme/order_test.go +++ b/acme/order_test.go @@ -4,7 +4,9 @@ import ( "context" "crypto/x509" "crypto/x509/pkix" + "encoding/asn1" "encoding/json" + "fmt" "net" "net/url" "reflect" @@ -386,6 +388,41 @@ func TestOrder_Finalize(t *testing.T) { err: NewErrorISE("unrecognized order status: %s", o.Status), } }, + "fail/non-matching-permanent-identifier-common-name": func(t *testing.T) test { + now := clock.Now() + o := &Order{ + ID: "oID", + AccountID: "accID", + Status: StatusReady, + ExpiresAt: now.Add(5 * time.Minute), + AuthorizationIDs: []string{"a", "b"}, + Identifiers: []Identifier{ + {Type: "permanent-identifier", Value: "a-permanent-identifier"}, + }, + } + csr := &x509.CertificateRequest{ + Subject: pkix.Name{ + CommonName: "a-different-identifier", + }, + ExtraExtensions: []pkix.Extension{ + { + Id: asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 8, 3}, + Value: []byte("a-permanent-identifier"), + }, + }, + } + return test{ + o: o, + csr: csr, + err: &Error{ + Type: "urn:ietf:params:acme:error:badCSR", + Detail: "The CSR is unacceptable", + Status: 400, + Err: fmt.Errorf("CSR Subject Common Name does not match identifiers exactly: "+ + "CSR Subject Common Name = %s, Order Permanent Identifier = %s", csr.Subject.CommonName, "a-permanent-identifier"), + }, + } + }, "fail/error-provisioner-auth": func(t *testing.T) test { now := clock.Now() o := &Order{ @@ -617,6 +654,165 @@ func TestOrder_Finalize(t *testing.T) { err: NewErrorISE("error updating order oID: force"), } }, + "ok/permanent-identifier": func(t *testing.T) test { + now := clock.Now() + o := &Order{ + ID: "oID", + AccountID: "accID", + Status: StatusReady, + ExpiresAt: now.Add(5 * time.Minute), + AuthorizationIDs: []string{"a", "b"}, + Identifiers: []Identifier{ + {Type: "permanent-identifier", Value: "a-permanent-identifier"}, + }, + } + csr := &x509.CertificateRequest{ + Subject: pkix.Name{ + CommonName: "a-permanent-identifier", + }, + ExtraExtensions: []pkix.Extension{ + { + Id: asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 8, 3}, + Value: []byte("a-permanent-identifier"), + }, + }, + } + + leaf := &x509.Certificate{ + Subject: pkix.Name{CommonName: "a-permanent-identifier"}, + ExtraExtensions: []pkix.Extension{ + { + Id: asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 8, 3}, + Value: []byte("a-permanent-identifier"), + }, + }, + } + inter := &x509.Certificate{Subject: pkix.Name{CommonName: "inter"}} + root := &x509.Certificate{Subject: pkix.Name{CommonName: "root"}} + + return test{ + o: o, + csr: csr, + prov: &MockProvisioner{ + MauthorizeSign: func(ctx context.Context, token string) ([]provisioner.SignOption, error) { + assert.Equals(t, token, "") + return nil, nil + }, + MgetOptions: func() *provisioner.Options { + return nil + }, + }, + ca: &mockSignAuth{ + sign: func(_csr *x509.CertificateRequest, signOpts provisioner.SignOptions, extraOpts ...provisioner.SignOption) ([]*x509.Certificate, error) { + assert.Equals(t, _csr, csr) + return []*x509.Certificate{leaf, inter, root}, nil + }, + }, + db: &MockDB{ + MockCreateCertificate: func(ctx context.Context, cert *Certificate) error { + cert.ID = "certID" + assert.Equals(t, cert.AccountID, o.AccountID) + assert.Equals(t, cert.OrderID, o.ID) + assert.Equals(t, cert.Leaf, leaf) + assert.Equals(t, cert.Intermediates, []*x509.Certificate{inter, root}) + return nil + }, + MockUpdateOrder: func(ctx context.Context, updo *Order) error { + assert.Equals(t, updo.CertificateID, "certID") + assert.Equals(t, updo.Status, StatusValid) + assert.Equals(t, updo.ID, o.ID) + assert.Equals(t, updo.AccountID, o.AccountID) + assert.Equals(t, updo.ExpiresAt, o.ExpiresAt) + assert.Equals(t, updo.AuthorizationIDs, o.AuthorizationIDs) + assert.Equals(t, updo.Identifiers, o.Identifiers) + return nil + }, + }, + } + }, + "ok/permanent-identifier-only": func(t *testing.T) test { + now := clock.Now() + o := &Order{ + ID: "oID", + AccountID: "accID", + Status: StatusReady, + ExpiresAt: now.Add(5 * time.Minute), + AuthorizationIDs: []string{"a", "b"}, + Identifiers: []Identifier{ + {Type: "dns", Value: "foo.internal"}, + {Type: "permanent-identifier", Value: "a-permanent-identifier"}, + }, + } + csr := &x509.CertificateRequest{ + Subject: pkix.Name{ + CommonName: "a-permanent-identifier", + }, + DNSNames: []string{"foo.internal"}, + ExtraExtensions: []pkix.Extension{ + { + Id: asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 8, 3}, + Value: []byte("a-permanent-identifier"), + }, + }, + } + + leaf := &x509.Certificate{ + Subject: pkix.Name{CommonName: "a-permanent-identifier"}, + ExtraExtensions: []pkix.Extension{ + { + Id: asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 8, 3}, + Value: []byte("a-permanent-identifier"), + }, + }, + } + inter := &x509.Certificate{Subject: pkix.Name{CommonName: "inter"}} + root := &x509.Certificate{Subject: pkix.Name{CommonName: "root"}} + + return test{ + o: o, + csr: csr, + prov: &MockProvisioner{ + MauthorizeSign: func(ctx context.Context, token string) ([]provisioner.SignOption, error) { + assert.Equals(t, token, "") + return nil, nil + }, + MgetOptions: func() *provisioner.Options { + return nil + }, + }, + // TODO(hs): we should work on making the mocks more realistic. Ideally, we should get rid of + // the mock entirely, relying on an instances of provisioner, authority and DB (possibly hardest), so + // that behavior of the tests is what an actual CA would do. We could gradually phase them out by + // using the mocking functions as a wrapper for actual test helpers generated per test case or per + // function that's tested. + ca: &mockSignAuth{ + sign: func(_csr *x509.CertificateRequest, signOpts provisioner.SignOptions, extraOpts ...provisioner.SignOption) ([]*x509.Certificate, error) { + assert.Equals(t, _csr, csr) + return []*x509.Certificate{leaf, inter, root}, nil + }, + }, + db: &MockDB{ + MockCreateCertificate: func(ctx context.Context, cert *Certificate) error { + cert.ID = "certID" + assert.Equals(t, cert.AccountID, o.AccountID) + assert.Equals(t, cert.OrderID, o.ID) + assert.Equals(t, cert.Leaf, leaf) + assert.Equals(t, cert.Intermediates, []*x509.Certificate{inter, root}) + return nil + }, + MockUpdateOrder: func(ctx context.Context, updo *Order) error { + assert.Equals(t, updo.CertificateID, "certID") + assert.Equals(t, updo.Status, StatusValid) + assert.Equals(t, updo.ID, o.ID) + assert.Equals(t, updo.AccountID, o.AccountID) + assert.Equals(t, updo.ExpiresAt, o.ExpiresAt) + assert.Equals(t, updo.AuthorizationIDs, o.AuthorizationIDs) + assert.Equals(t, updo.Identifiers, o.Identifiers) + return nil + }, + }, + } + }, "ok/new-cert-dns": func(t *testing.T) test { now := clock.Now() o := &Order{ From 1c38113e44d6baa3d87b9fc540cdcd28371a5a9b Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Thu, 26 Jan 2023 13:24:25 +0100 Subject: [PATCH 2/9] Add ACME `Subproblem` for more detailed ACME client-side errors When validating an ACME challenge (`device-attest-01` in this case, but it's also true for others), and validation fails, the CA didn't return a lot of information about why the challenge had failed. By introducing the ACME `Subproblem` type, an ACME `Error` can include some additional information about what went wrong when validating the challenge. This is a WIP commit. The `Subproblem` isn't created in many code paths yet, just for the `step` format at the moment. Will probably follow up with some more improvements to how the ACME error is handled. Also need to cleanup some debug things (q.Q) --- acme/api/handler.go | 3 ++ acme/challenge.go | 19 +++++++++++-- acme/db/nosql/challenge.go | 5 +++- acme/errors.go | 56 +++++++++++++++++++++++++++++++++----- go.mod | 5 +++- go.sum | 7 +++++ 6 files changed, 83 insertions(+), 12 deletions(-) diff --git a/acme/api/handler.go b/acme/api/handler.go index e6aad131..8f3b51db 100644 --- a/acme/api/handler.go +++ b/acme/api/handler.go @@ -10,6 +10,7 @@ import ( "time" "github.com/go-chi/chi" + "github.com/ryboe/q" "github.com/smallstep/certificates/acme" "github.com/smallstep/certificates/api" @@ -355,6 +356,8 @@ func GetChallenge(w http.ResponseWriter, r *http.Request) { return } + q.Q(ch) + linker.LinkChallenge(ctx, ch, azID) w.Header().Add("Link", link(linker.GetLink(ctx, acme.AuthzLinkType, azID), "up")) diff --git a/acme/challenge.go b/acme/challenge.go index 1a45a252..9eca34a5 100644 --- a/acme/challenge.go +++ b/acme/challenge.go @@ -29,6 +29,8 @@ import ( "github.com/smallstep/certificates/authority/provisioner" "go.step.sm/crypto/jose" "go.step.sm/crypto/pemutil" + + "github.com/ryboe/q" ) type ChallengeType string @@ -404,6 +406,8 @@ func deviceAttest01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose } case "step": data, err := doStepAttestationFormat(ctx, prov, ch, jwk, &att) + q.Q(data) + q.Q(err) if err != nil { var acmeError *Error if errors.As(err, &acmeError) { @@ -415,12 +419,20 @@ func deviceAttest01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose return WrapErrorISE(err, "error validating attestation") } - // Validate Apple's ClientIdentifier (Identifier.Value) with device - // identifiers. + // Validate the YubiKey serial number from the attestation + // certificate with the challenged Order value. // // Note: We might want to use an external service for this. + q.Q(data.SerialNumber, ch.Value) if data.SerialNumber != ch.Value { - return storeError(ctx, db, ch, true, NewError(ErrorBadAttestationStatementType, "permanent identifier does not match")) + q.Q("not the same") + subproblem := NewSubproblemWithIdentifier( + ErrorMalformedType, + Identifier{Type: "permanent-identifier", Value: ch.Value}, + "challenge identifier %q doesn't match the attested hardware identifier %q", ch.Value, data.SerialNumber, + ) + s2 := NewSubproblem(ErrorMalformedType, "test") + return storeError(ctx, db, ch, true, NewError(ErrorBadAttestationStatementType, "permanent identifier does not match").AddSubproblems(subproblem, s2)) } default: return storeError(ctx, db, ch, true, NewError(ErrorBadAttestationStatementType, "unexpected attestation object format")) @@ -752,6 +764,7 @@ func KeyAuthorization(token string, jwk *jose.JSONWebKey) (string, error) { // storeError the given error to an ACME error and saves using the DB interface. func storeError(ctx context.Context, db DB, ch *Challenge, markInvalid bool, err *Error) error { ch.Error = err + q.Q(err) if markInvalid { ch.Status = StatusInvalid } diff --git a/acme/db/nosql/challenge.go b/acme/db/nosql/challenge.go index f84a6f4e..05d23a1f 100644 --- a/acme/db/nosql/challenge.go +++ b/acme/db/nosql/challenge.go @@ -6,6 +6,7 @@ import ( "time" "github.com/pkg/errors" + "github.com/ryboe/q" "github.com/smallstep/certificates/acme" "github.com/smallstep/nosql" ) @@ -19,7 +20,7 @@ type dbChallenge struct { Value string `json:"value"` ValidatedAt string `json:"validatedAt"` CreatedAt time.Time `json:"createdAt"` - Error *acme.Error `json:"error"` + Error *acme.Error `json:"error"` // TODO(hs): a bit dangerous; should become db-specific type } func (dbc *dbChallenge) clone() *dbChallenge { @@ -29,6 +30,7 @@ func (dbc *dbChallenge) clone() *dbChallenge { func (db *DB) getDBChallenge(ctx context.Context, id string) (*dbChallenge, error) { data, err := db.db.Get(challengeTable, []byte(id)) + q.Q(data) if nosql.IsErrNotFound(err) { return nil, acme.NewError(acme.ErrorMalformedType, "challenge %s not found", id) } else if err != nil { @@ -39,6 +41,7 @@ func (db *DB) getDBChallenge(ctx context.Context, id string) (*dbChallenge, erro if err := json.Unmarshal(data, dbch); err != nil { return nil, errors.Wrap(err, "error unmarshaling dbChallenge") } + q.Q(dbch) return dbch, nil } diff --git a/acme/errors.go b/acme/errors.go index a969bd96..95053908 100644 --- a/acme/errors.go +++ b/acme/errors.go @@ -270,14 +270,36 @@ var ( } ) -// Error represents an ACME +// Error represents an ACME Error type Error struct { - Type string `json:"type"` - Detail string `json:"detail"` - Subproblems []interface{} `json:"subproblems,omitempty"` - Identifier interface{} `json:"identifier,omitempty"` - Err error `json:"-"` - Status int `json:"-"` + Type string `json:"type"` + Detail string `json:"detail"` + Subproblems []Subproblem `json:"subproblems,omitempty"` + + // The "identifier" field MUST NOT be present at the top level in ACME + // problem documents. It can only be present in subproblems. + // Subproblems need not all have the same type, and they do not need to + // match the top level type. + Identifier Identifier `json:"identifier,omitempty"` // TODO(hs): seems unused and MUST NOT be present; this can likely be removed + Err error `json:"-"` + Status int `json:"-"` +} + +// Subproblem represents an ACME subproblem. It's fairly +// similar to an ACME error, but differs in that it can't +// include subproblems itself, the error is reflected +// in the Detail property and doesn't have a Status. +type Subproblem struct { + Type string `json:"type"` + Detail string `json:"detail"` + Identifier *Identifier `json:"identifier,omitempty"` +} + +// AddSubproblems adds the Subproblems to Error. It +// returns the Error, allowing for fluent addition. +func (e *Error) AddSubproblems(subproblems ...Subproblem) *Error { + e.Subproblems = append(e.Subproblems, subproblems...) + return e } // NewError creates a new Error type. @@ -285,6 +307,26 @@ func NewError(pt ProblemType, msg string, args ...interface{}) *Error { return newError(pt, errors.Errorf(msg, args...)) } +// NewSubproblem creates a new Subproblem. The msg and args +// are used to create a new error, which is set as the Detail, allowing +// for more detailed error messages to be returned to the ACME client. +func NewSubproblem(pt ProblemType, msg string, args ...interface{}) Subproblem { + e := newError(pt, fmt.Errorf(msg, args...)) + s := Subproblem{ + Type: e.Type, + Detail: e.Err.Error(), + } + return s +} + +// NewSubproblemWithIdentifier creates a new Subproblem with a specific ACME +// Identifier. It calls NewSubproblem and sets the Identifier. +func NewSubproblemWithIdentifier(pt ProblemType, identifier Identifier, msg string, args ...interface{}) Subproblem { + s := NewSubproblem(pt, msg, args...) + s.Identifier = &identifier + return s +} + func newError(pt ProblemType, err error) *Error { meta, ok := errorMap[pt] if !ok { diff --git a/go.mod b/go.mod index 4fcfae3e..34bd40fd 100644 --- a/go.mod +++ b/go.mod @@ -28,7 +28,7 @@ require ( github.com/hashicorp/vault/api/auth/approle v0.3.0 github.com/hashicorp/vault/api/auth/kubernetes v0.3.0 github.com/jhump/protoreflect v1.9.0 // indirect - github.com/kr/pretty v0.3.0 // indirect + github.com/kr/pretty v0.3.1 // indirect github.com/mattn/go-colorable v0.1.8 // indirect github.com/mattn/go-isatty v0.0.13 // indirect github.com/micromdm/scep/v2 v2.1.0 @@ -122,6 +122,7 @@ require ( github.com/jackc/pgx/v4 v4.17.2 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/klauspost/compress v1.15.11 // indirect + github.com/kr/text v0.2.0 // indirect github.com/manifoldco/promptui v0.9.0 // indirect github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect github.com/miekg/pkcs11 v1.1.1 // indirect @@ -133,8 +134,10 @@ require ( github.com/oklog/run v1.0.0 // indirect github.com/pierrec/lz4 v2.5.2+incompatible // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/rogpeppe/go-internal v1.9.0 // indirect github.com/russross/blackfriday/v2 v2.0.1 // indirect github.com/ryanuber/go-glob v1.0.0 // indirect + github.com/ryboe/q v1.0.18 // indirect github.com/shopspring/decimal v1.2.0 // indirect github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect github.com/spf13/cast v1.4.1 // indirect diff --git a/go.sum b/go.sum index 2bb94368..343d38e8 100644 --- a/go.sum +++ b/go.sum @@ -444,6 +444,8 @@ github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfn github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= @@ -549,6 +551,7 @@ github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0 github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pierrec/lz4 v2.5.2+incompatible h1:WCjObylUIOlKy/+7Abdn34TLIkXiA4UWUMhxq9m9ZXI= github.com/pierrec/lz4 v2.5.2+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -582,6 +585,8 @@ github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6L github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/xid v1.4.0 h1:qd7wPTDkN6KQx2VmMBLrpHkiyQwgFXRnkOLacUiaSNY= github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= @@ -594,6 +599,8 @@ github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= +github.com/ryboe/q v1.0.18 h1:uTonPt1eZjy7GSpB0XpYpsCvX+Yf9f+M4CUKuH2r+vg= +github.com/ryboe/q v1.0.18/go.mod h1:elqvVf/GBuZHvZ9gvHv4MKM6NZAMz2rFajnTgQZ46wU= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= From edee01c80c639623b3433853d139cf458a55b2a8 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Thu, 26 Jan 2023 13:41:01 +0100 Subject: [PATCH 3/9] Refactor debug utility --- acme/api/handler.go | 2 +- acme/challenge.go | 4 ++-- acme/db/nosql/challenge.go | 6 ++++-- utils/debug/q/debug.go | 13 +++++++++++++ 4 files changed, 20 insertions(+), 5 deletions(-) create mode 100644 utils/debug/q/debug.go diff --git a/acme/api/handler.go b/acme/api/handler.go index 8f3b51db..ca5de02e 100644 --- a/acme/api/handler.go +++ b/acme/api/handler.go @@ -10,13 +10,13 @@ import ( "time" "github.com/go-chi/chi" - "github.com/ryboe/q" "github.com/smallstep/certificates/acme" "github.com/smallstep/certificates/api" "github.com/smallstep/certificates/api/render" "github.com/smallstep/certificates/authority" "github.com/smallstep/certificates/authority/provisioner" + "github.com/smallstep/certificates/utils/debug/q" ) func link(url, typ string) string { diff --git a/acme/challenge.go b/acme/challenge.go index 9eca34a5..72d8d171 100644 --- a/acme/challenge.go +++ b/acme/challenge.go @@ -26,11 +26,11 @@ import ( "time" "github.com/fxamacker/cbor/v2" - "github.com/smallstep/certificates/authority/provisioner" "go.step.sm/crypto/jose" "go.step.sm/crypto/pemutil" - "github.com/ryboe/q" + "github.com/smallstep/certificates/authority/provisioner" + "github.com/smallstep/certificates/utils/debug/q" ) type ChallengeType string diff --git a/acme/db/nosql/challenge.go b/acme/db/nosql/challenge.go index 05d23a1f..bb1c4080 100644 --- a/acme/db/nosql/challenge.go +++ b/acme/db/nosql/challenge.go @@ -6,9 +6,11 @@ import ( "time" "github.com/pkg/errors" - "github.com/ryboe/q" - "github.com/smallstep/certificates/acme" + "github.com/smallstep/nosql" + + "github.com/smallstep/certificates/acme" + "github.com/smallstep/certificates/utils/debug/q" ) type dbChallenge struct { diff --git a/utils/debug/q/debug.go b/utils/debug/q/debug.go new file mode 100644 index 00000000..e12c73cd --- /dev/null +++ b/utils/debug/q/debug.go @@ -0,0 +1,13 @@ +package q + +import ( + ryboeq "github.com/ryboe/q" +) + +func Q(v ...interface{}) { + // TODO(hs): do or do not call ryboeq.Q based on e.g. debug flag, + // runtime (go run vs. build), based on compiled or not. Goal would be + // to not debug in prod builds at all times. Ideally, never leave a leftover + // call to q.Q in the code, so panic if there is? + ryboeq.Q(v...) +} From 60a9e41c1c68f5ebfcc8564c8addd609fdee5263 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Thu, 26 Jan 2023 14:59:08 +0100 Subject: [PATCH 4/9] Remove `Identifier` from top level ACME `Errors` --- acme/account_test.go | 1 - acme/api/account_test.go | 3 --- acme/api/eab_test.go | 2 -- acme/api/handler_test.go | 4 ---- acme/api/middleware_test.go | 11 ----------- acme/api/order_test.go | 3 --- acme/api/revoke_test.go | 3 --- acme/errors.go | 18 ++++++++---------- 8 files changed, 8 insertions(+), 37 deletions(-) diff --git a/acme/account_test.go b/acme/account_test.go index 88718a9a..b8ce7276 100644 --- a/acme/account_test.go +++ b/acme/account_test.go @@ -135,7 +135,6 @@ func TestExternalAccountKey_BindTo(t *testing.T) { if assert.True(t, errors.As(err, &ae)) { assert.Equals(t, ae.Type, tt.err.Type) assert.Equals(t, ae.Detail, tt.err.Detail) - assert.Equals(t, ae.Identifier, tt.err.Identifier) assert.Equals(t, ae.Subproblems, tt.err.Subproblems) } } else { diff --git a/acme/api/account_test.go b/acme/api/account_test.go index 3f8641b8..d46c9eed 100644 --- a/acme/api/account_test.go +++ b/acme/api/account_test.go @@ -388,7 +388,6 @@ func TestHandler_GetOrdersByAccountID(t *testing.T) { assert.Equals(t, ae.Type, tc.err.Type) assert.Equals(t, ae.Detail, tc.err.Detail) - assert.Equals(t, ae.Identifier, tc.err.Identifier) assert.Equals(t, ae.Subproblems, tc.err.Subproblems) assert.Equals(t, res.Header["Content-Type"], []string{"application/problem+json"}) } else { @@ -828,7 +827,6 @@ func TestHandler_NewAccount(t *testing.T) { assert.Equals(t, ae.Type, tc.err.Type) assert.Equals(t, ae.Detail, tc.err.Detail) - assert.Equals(t, ae.Identifier, tc.err.Identifier) assert.Equals(t, ae.Subproblems, tc.err.Subproblems) assert.Equals(t, res.Header["Content-Type"], []string{"application/problem+json"}) } else { @@ -1032,7 +1030,6 @@ func TestHandler_GetOrUpdateAccount(t *testing.T) { assert.Equals(t, ae.Type, tc.err.Type) assert.Equals(t, ae.Detail, tc.err.Detail) - assert.Equals(t, ae.Identifier, tc.err.Identifier) assert.Equals(t, ae.Subproblems, tc.err.Subproblems) assert.Equals(t, res.Header["Content-Type"], []string{"application/problem+json"}) } else { diff --git a/acme/api/eab_test.go b/acme/api/eab_test.go index c923a2f6..14dbdad1 100644 --- a/acme/api/eab_test.go +++ b/acme/api/eab_test.go @@ -866,7 +866,6 @@ func TestHandler_validateExternalAccountBinding(t *testing.T) { assert.Equals(t, ae.Status, tc.err.Status) assert.HasPrefix(t, ae.Err.Error(), tc.err.Err.Error()) assert.Equals(t, ae.Detail, tc.err.Detail) - assert.Equals(t, ae.Identifier, tc.err.Identifier) assert.Equals(t, ae.Subproblems, tc.err.Subproblems) } } else { @@ -1145,7 +1144,6 @@ func Test_validateEABJWS(t *testing.T) { assert.Equals(t, tc.err.Status, err.Status) assert.HasPrefix(t, err.Err.Error(), tc.err.Err.Error()) assert.Equals(t, tc.err.Detail, err.Detail) - assert.Equals(t, tc.err.Identifier, err.Identifier) assert.Equals(t, tc.err.Subproblems, err.Subproblems) } else { assert.Nil(t, err) diff --git a/acme/api/handler_test.go b/acme/api/handler_test.go index e814aaba..7ef7cd68 100644 --- a/acme/api/handler_test.go +++ b/acme/api/handler_test.go @@ -193,7 +193,6 @@ func TestHandler_GetDirectory(t *testing.T) { assert.Equals(t, ae.Type, tc.err.Type) assert.Equals(t, ae.Detail, tc.err.Detail) - assert.Equals(t, ae.Identifier, tc.err.Identifier) assert.Equals(t, ae.Subproblems, tc.err.Subproblems) assert.Equals(t, res.Header["Content-Type"], []string{"application/problem+json"}) } else { @@ -366,7 +365,6 @@ func TestHandler_GetAuthorization(t *testing.T) { assert.Equals(t, ae.Type, tc.err.Type) assert.Equals(t, ae.Detail, tc.err.Detail) - assert.Equals(t, ae.Identifier, tc.err.Identifier) assert.Equals(t, ae.Subproblems, tc.err.Subproblems) assert.Equals(t, res.Header["Content-Type"], []string{"application/problem+json"}) } else { @@ -509,7 +507,6 @@ func TestHandler_GetCertificate(t *testing.T) { assert.Equals(t, ae.Type, tc.err.Type) assert.HasPrefix(t, ae.Detail, tc.err.Detail) - assert.Equals(t, ae.Identifier, tc.err.Identifier) assert.Equals(t, ae.Subproblems, tc.err.Subproblems) assert.Equals(t, res.Header["Content-Type"], []string{"application/problem+json"}) } else { @@ -768,7 +765,6 @@ func TestHandler_GetChallenge(t *testing.T) { assert.Equals(t, ae.Type, tc.err.Type) assert.Equals(t, ae.Detail, tc.err.Detail) - assert.Equals(t, ae.Identifier, tc.err.Identifier) assert.Equals(t, ae.Subproblems, tc.err.Subproblems) assert.Equals(t, res.Header["Content-Type"], []string{"application/problem+json"}) } else { diff --git a/acme/api/middleware_test.go b/acme/api/middleware_test.go index faff0616..3db3773c 100644 --- a/acme/api/middleware_test.go +++ b/acme/api/middleware_test.go @@ -93,7 +93,6 @@ func TestHandler_addNonce(t *testing.T) { assert.Equals(t, ae.Type, tc.err.Type) assert.Equals(t, ae.Detail, tc.err.Detail) - assert.Equals(t, ae.Identifier, tc.err.Identifier) assert.Equals(t, ae.Subproblems, tc.err.Subproblems) assert.Equals(t, res.Header["Content-Type"], []string{"application/problem+json"}) } else { @@ -147,7 +146,6 @@ func TestHandler_addDirLink(t *testing.T) { assert.Equals(t, ae.Type, tc.err.Type) assert.Equals(t, ae.Detail, tc.err.Detail) - assert.Equals(t, ae.Identifier, tc.err.Identifier) assert.Equals(t, ae.Subproblems, tc.err.Subproblems) assert.Equals(t, res.Header["Content-Type"], []string{"application/problem+json"}) } else { @@ -252,7 +250,6 @@ func TestHandler_verifyContentType(t *testing.T) { assert.Equals(t, ae.Type, tc.err.Type) assert.Equals(t, ae.Detail, tc.err.Detail) - assert.Equals(t, ae.Identifier, tc.err.Identifier) assert.Equals(t, ae.Subproblems, tc.err.Subproblems) assert.Equals(t, res.Header["Content-Type"], []string{"application/problem+json"}) } else { @@ -320,7 +317,6 @@ func TestHandler_isPostAsGet(t *testing.T) { assert.Equals(t, ae.Type, tc.err.Type) assert.Equals(t, ae.Detail, tc.err.Detail) - assert.Equals(t, ae.Identifier, tc.err.Identifier) assert.Equals(t, ae.Subproblems, tc.err.Subproblems) assert.Equals(t, res.Header["Content-Type"], []string{"application/problem+json"}) } else { @@ -410,7 +406,6 @@ func TestHandler_parseJWS(t *testing.T) { assert.Equals(t, ae.Type, tc.err.Type) assert.Equals(t, ae.Detail, tc.err.Detail) - assert.Equals(t, ae.Identifier, tc.err.Identifier) assert.Equals(t, ae.Subproblems, tc.err.Subproblems) assert.Equals(t, res.Header["Content-Type"], []string{"application/problem+json"}) } else { @@ -606,7 +601,6 @@ func TestHandler_verifyAndExtractJWSPayload(t *testing.T) { assert.Equals(t, ae.Type, tc.err.Type) assert.Equals(t, ae.Detail, tc.err.Detail) - assert.Equals(t, ae.Identifier, tc.err.Identifier) assert.Equals(t, ae.Subproblems, tc.err.Subproblems) assert.Equals(t, res.Header["Content-Type"], []string{"application/problem+json"}) } else { @@ -808,7 +802,6 @@ func TestHandler_lookupJWK(t *testing.T) { assert.Equals(t, ae.Type, tc.err.Type) assert.Equals(t, ae.Detail, tc.err.Detail) - assert.Equals(t, ae.Identifier, tc.err.Identifier) assert.Equals(t, ae.Subproblems, tc.err.Subproblems) assert.Equals(t, res.Header["Content-Type"], []string{"application/problem+json"}) } else { @@ -1008,7 +1001,6 @@ func TestHandler_extractJWK(t *testing.T) { assert.Equals(t, ae.Type, tc.err.Type) assert.Equals(t, ae.Detail, tc.err.Detail) - assert.Equals(t, ae.Identifier, tc.err.Identifier) assert.Equals(t, ae.Subproblems, tc.err.Subproblems) assert.Equals(t, res.Header["Content-Type"], []string{"application/problem+json"}) } else { @@ -1384,7 +1376,6 @@ func TestHandler_validateJWS(t *testing.T) { assert.Equals(t, ae.Type, tc.err.Type) assert.Equals(t, ae.Detail, tc.err.Detail) - assert.Equals(t, ae.Identifier, tc.err.Identifier) assert.Equals(t, ae.Subproblems, tc.err.Subproblems) assert.Equals(t, res.Header["Content-Type"], []string{"application/problem+json"}) } else { @@ -1567,7 +1558,6 @@ func TestHandler_extractOrLookupJWK(t *testing.T) { assert.Equals(t, ae.Type, tc.err.Type) assert.Equals(t, ae.Detail, tc.err.Detail) - assert.Equals(t, ae.Identifier, tc.err.Identifier) assert.Equals(t, ae.Subproblems, tc.err.Subproblems) assert.Equals(t, res.Header["Content-Type"], []string{"application/problem+json"}) } else { @@ -1652,7 +1642,6 @@ func TestHandler_checkPrerequisites(t *testing.T) { assert.FatalError(t, json.Unmarshal(bytes.TrimSpace(body), &ae)) assert.Equals(t, ae.Type, tc.err.Type) assert.Equals(t, ae.Detail, tc.err.Detail) - assert.Equals(t, ae.Identifier, tc.err.Identifier) assert.Equals(t, ae.Subproblems, tc.err.Subproblems) assert.Equals(t, res.Header["Content-Type"], []string{"application/problem+json"}) } else { diff --git a/acme/api/order_test.go b/acme/api/order_test.go index b7b58b7f..9f03c547 100644 --- a/acme/api/order_test.go +++ b/acme/api/order_test.go @@ -486,7 +486,6 @@ func TestHandler_GetOrder(t *testing.T) { assert.Equals(t, ae.Type, tc.err.Type) assert.Equals(t, ae.Detail, tc.err.Detail) - assert.Equals(t, ae.Identifier, tc.err.Identifier) assert.Equals(t, ae.Subproblems, tc.err.Subproblems) assert.Equals(t, res.Header["Content-Type"], []string{"application/problem+json"}) } else { @@ -1846,7 +1845,6 @@ func TestHandler_NewOrder(t *testing.T) { assert.Equals(t, ae.Type, tc.err.Type) assert.Equals(t, ae.Detail, tc.err.Detail) - assert.Equals(t, ae.Identifier, tc.err.Identifier) assert.Equals(t, ae.Subproblems, tc.err.Subproblems) assert.Equals(t, res.Header["Content-Type"], []string{"application/problem+json"}) } else { @@ -2144,7 +2142,6 @@ func TestHandler_FinalizeOrder(t *testing.T) { assert.Equals(t, ae.Type, tc.err.Type) assert.Equals(t, ae.Detail, tc.err.Detail) - assert.Equals(t, ae.Identifier, tc.err.Identifier) assert.Equals(t, ae.Subproblems, tc.err.Subproblems) assert.Equals(t, res.Header["Content-Type"], []string{"application/problem+json"}) } else { diff --git a/acme/api/revoke_test.go b/acme/api/revoke_test.go index 240ac748..c4182400 100644 --- a/acme/api/revoke_test.go +++ b/acme/api/revoke_test.go @@ -1090,7 +1090,6 @@ func TestHandler_RevokeCert(t *testing.T) { assert.Equals(t, ae.Type, tc.err.Type) assert.Equals(t, ae.Detail, tc.err.Detail) - assert.Equals(t, ae.Identifier, tc.err.Identifier) assert.Equals(t, ae.Subproblems, tc.err.Subproblems) assert.Equals(t, res.Header["Content-Type"], []string{"application/problem+json"}) } else { @@ -1230,7 +1229,6 @@ func TestHandler_isAccountAuthorized(t *testing.T) { assert.Equals(t, acmeErr.Type, tc.err.Type) assert.Equals(t, acmeErr.Status, tc.err.Status) assert.Equals(t, acmeErr.Detail, tc.err.Detail) - assert.Equals(t, acmeErr.Identifier, tc.err.Identifier) assert.Equals(t, acmeErr.Subproblems, tc.err.Subproblems) }) @@ -1323,7 +1321,6 @@ func Test_wrapUnauthorizedError(t *testing.T) { assert.Equals(t, acmeErr.Type, tc.want.Type) assert.Equals(t, acmeErr.Status, tc.want.Status) assert.Equals(t, acmeErr.Detail, tc.want.Detail) - assert.Equals(t, acmeErr.Identifier, tc.want.Identifier) assert.Equals(t, acmeErr.Subproblems, tc.want.Subproblems) }) } diff --git a/acme/errors.go b/acme/errors.go index 95053908..44f367a0 100644 --- a/acme/errors.go +++ b/acme/errors.go @@ -275,14 +275,8 @@ type Error struct { Type string `json:"type"` Detail string `json:"detail"` Subproblems []Subproblem `json:"subproblems,omitempty"` - - // The "identifier" field MUST NOT be present at the top level in ACME - // problem documents. It can only be present in subproblems. - // Subproblems need not all have the same type, and they do not need to - // match the top level type. - Identifier Identifier `json:"identifier,omitempty"` // TODO(hs): seems unused and MUST NOT be present; this can likely be removed - Err error `json:"-"` - Status int `json:"-"` + Err error `json:"-"` + Status int `json:"-"` } // Subproblem represents an ACME subproblem. It's fairly @@ -290,8 +284,12 @@ type Error struct { // include subproblems itself, the error is reflected // in the Detail property and doesn't have a Status. type Subproblem struct { - Type string `json:"type"` - Detail string `json:"detail"` + Type string `json:"type"` + Detail string `json:"detail"` + // The "identifier" field MUST NOT be present at the top level in ACME + // problem documents. It can only be present in subproblems. + // Subproblems need not all have the same type, and they do not need to + // match the top level type. Identifier *Identifier `json:"identifier,omitempty"` } From ed61c5df5f2d7911f4b708668380362a48ff06ae Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Thu, 26 Jan 2023 15:36:15 +0100 Subject: [PATCH 5/9] Cleanup some leftover debug statements --- acme/challenge.go | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/acme/challenge.go b/acme/challenge.go index 72d8d171..db1f989f 100644 --- a/acme/challenge.go +++ b/acme/challenge.go @@ -30,7 +30,6 @@ import ( "go.step.sm/crypto/pemutil" "github.com/smallstep/certificates/authority/provisioner" - "github.com/smallstep/certificates/utils/debug/q" ) type ChallengeType string @@ -406,8 +405,6 @@ func deviceAttest01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose } case "step": data, err := doStepAttestationFormat(ctx, prov, ch, jwk, &att) - q.Q(data) - q.Q(err) if err != nil { var acmeError *Error if errors.As(err, &acmeError) { @@ -423,16 +420,13 @@ func deviceAttest01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose // certificate with the challenged Order value. // // Note: We might want to use an external service for this. - q.Q(data.SerialNumber, ch.Value) if data.SerialNumber != ch.Value { - q.Q("not the same") subproblem := NewSubproblemWithIdentifier( ErrorMalformedType, Identifier{Type: "permanent-identifier", Value: ch.Value}, "challenge identifier %q doesn't match the attested hardware identifier %q", ch.Value, data.SerialNumber, ) - s2 := NewSubproblem(ErrorMalformedType, "test") - return storeError(ctx, db, ch, true, NewError(ErrorBadAttestationStatementType, "permanent identifier does not match").AddSubproblems(subproblem, s2)) + return storeError(ctx, db, ch, true, NewError(ErrorBadAttestationStatementType, "permanent identifier does not match").AddSubproblems(subproblem)) } default: return storeError(ctx, db, ch, true, NewError(ErrorBadAttestationStatementType, "unexpected attestation object format")) @@ -764,7 +758,6 @@ func KeyAuthorization(token string, jwk *jose.JSONWebKey) (string, error) { // storeError the given error to an ACME error and saves using the DB interface. func storeError(ctx context.Context, db DB, ch *Challenge, markInvalid bool, err *Error) error { ch.Error = err - q.Q(err) if markInvalid { ch.Status = StatusInvalid } From 2ab9beb7edc8dd294052ee60099e3ff5460c9515 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Fri, 27 Jan 2023 15:36:48 +0100 Subject: [PATCH 6/9] Add tests for `deviceAttest01Validate` --- acme/challenge.go | 21 +- acme/challenge_test.go | 982 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 954 insertions(+), 49 deletions(-) diff --git a/acme/challenge.go b/acme/challenge.go index db1f989f..18921b70 100644 --- a/acme/challenge.go +++ b/acme/challenge.go @@ -80,10 +80,9 @@ func (ch *Challenge) ToLog() (interface{}, error) { return string(b), nil } -// Validate attempts to validate the challenge. Stores changes to the Challenge -// type using the DB interface. -// satisfactorily validated, the 'status' and 'validated' attributes are -// updated. +// Validate attempts to validate the Challenge. Stores changes to the Challenge +// type using the DB interface. If the Challenge is validated, the 'status' and +// 'validated' attributes are updated. func (ch *Challenge) Validate(ctx context.Context, db DB, jwk *jose.JSONWebKey, payload []byte) error { // If already valid or invalid then return without performing validation. if ch.Status != StatusPending { @@ -336,21 +335,21 @@ func dns01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSONWebK return nil } -type Payload struct { +type payloadType struct { AttObj string `json:"attObj"` Error string `json:"error"` } -type AttestationObject struct { +type attestationObject struct { Format string `json:"fmt"` AttStatement map[string]interface{} `json:"attStmt,omitempty"` } // TODO(bweeks): move attestation verification to a shared package. -// TODO(bweeks): define new error type for failed attestation validation. func deviceAttest01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSONWebKey, payload []byte) error { - var p Payload + var p payloadType if err := json.Unmarshal(payload, &p); err != nil { + return WrapErrorISE(err, "error unmarshalling JSON") } if p.Error != "" { @@ -363,7 +362,7 @@ func deviceAttest01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose return WrapErrorISE(err, "error base64 decoding attObj") } - att := AttestationObject{} + att := attestationObject{} if err := cbor.Unmarshal(attObj, &att); err != nil { return WrapErrorISE(err, "error unmarshalling CBOR") } @@ -475,7 +474,7 @@ type appleAttestationData struct { Certificate *x509.Certificate } -func doAppleAttestationFormat(ctx context.Context, prov Provisioner, ch *Challenge, att *AttestationObject) (*appleAttestationData, error) { +func doAppleAttestationFormat(ctx context.Context, prov Provisioner, ch *Challenge, att *attestationObject) (*appleAttestationData, error) { // Use configured or default attestation roots if none is configured. roots, ok := prov.GetAttestationRoots() if !ok { @@ -576,7 +575,7 @@ type stepAttestationData struct { SerialNumber string } -func doStepAttestationFormat(ctx context.Context, prov Provisioner, ch *Challenge, jwk *jose.JSONWebKey, att *AttestationObject) (*stepAttestationData, error) { +func doStepAttestationFormat(ctx context.Context, prov Provisioner, ch *Challenge, jwk *jose.JSONWebKey, att *attestationObject) (*stepAttestationData, error) { // Use configured or default attestation roots if none is configured. roots, ok := prov.GetAttestationRoots() if !ok { diff --git a/acme/challenge_test.go b/acme/challenge_test.go index 1aa9f6ab..23d28d4e 100644 --- a/acme/challenge_test.go +++ b/acme/challenge_test.go @@ -15,6 +15,7 @@ import ( "encoding/asn1" "encoding/base64" "encoding/hex" + "encoding/json" "encoding/pem" "errors" "fmt" @@ -33,6 +34,7 @@ import ( "github.com/smallstep/assert" "github.com/smallstep/certificates/authority/config" "github.com/smallstep/certificates/authority/provisioner" + sassert "github.com/stretchr/testify/assert" "go.step.sm/crypto/jose" "go.step.sm/crypto/keyutil" "go.step.sm/crypto/minica" @@ -50,6 +52,23 @@ func (m *mockClient) TLSDial(network, addr string, tlsConfig *tls.Config) (*tls. return m.tlsDial(network, addr, tlsConfig) } +func mustNonAttestationProvisioner(t *testing.T) Provisioner { + t.Helper() + + prov := &provisioner.ACME{ + Type: "ACME", + Name: "acme", + Challenges: []provisioner.ACMEChallenge{provisioner.HTTP_01}, + } + if err := prov.Init(provisioner.Config{ + Claims: config.GlobalProvisionerClaims, + }); err != nil { + t.Fatal(err) + } + prov.AttestationFormats = []provisioner.ACMEAttestationFormat{"bogus-format"} // results in no attestation formats enabled + return prov +} + func mustAttestationProvisioner(t *testing.T, roots []byte) Provisioner { t.Helper() @@ -266,12 +285,14 @@ func TestKeyAuthorization(t *testing.T) { func TestChallenge_Validate(t *testing.T) { type test struct { - ch *Challenge - vc Client - jwk *jose.JSONWebKey - db DB - srv *httptest.Server - err *Error + ch *Challenge + vc Client + jwk *jose.JSONWebKey + db DB + srv *httptest.Server + payload []byte + ctx context.Context + err *Error } tests := map[string]func(t *testing.T) test{ "ok/already-valid": func(t *testing.T) test { @@ -629,6 +650,125 @@ func TestChallenge_Validate(t *testing.T) { jwk: jwk, } }, + "fail/device-attest-01": func(t *testing.T) test { + ch := &Challenge{ + ID: "chID", + Token: "token", + Type: "device-attest-01", + Status: StatusPending, + Value: "12345678", + } + payload, err := json.Marshal(struct { + Error string `json:"error"` + }{ + Error: "an error", + }) + sassert.NoError(t, err) + return test{ + ch: ch, + payload: payload, + db: &MockDB{ + MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { + sassert.Equal(t, "chID", updch.ID) + sassert.Equal(t, "token", updch.Token) + sassert.Equal(t, StatusInvalid, updch.Status) + sassert.Equal(t, ChallengeType("device-attest-01"), updch.Type) + sassert.Equal(t, "12345678", updch.Value) + + err := NewError(ErrorRejectedIdentifierType, "payload contained error: an error") + + sassert.EqualError(t, err.Err, updch.Error.Err.Error()) + sassert.Equal(t, err.Type, updch.Error.Type) + sassert.Equal(t, err.Detail, updch.Error.Detail) + sassert.Equal(t, err.Status, updch.Error.Status) + sassert.Equal(t, err.Detail, updch.Error.Detail) + + return errors.New("force") + }, + }, + err: NewError(ErrorServerInternalType, "failure saving error to acme challenge: force"), + } + }, + "ok/device-attest-01": func(t *testing.T) test { + ctx := context.Background() + ca, err := minica.New() + sassert.NoError(t, err) + caRoot := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: ca.Root.Raw}) + ctx = NewProvisionerContext(ctx, mustAttestationProvisioner(t, caRoot)) + makeLeaf := func(signer crypto.Signer, serialNumber []byte) *x509.Certificate { + leaf, err := ca.Sign(&x509.Certificate{ + Subject: pkix.Name{CommonName: "attestation cert"}, + PublicKey: signer.Public(), + ExtraExtensions: []pkix.Extension{ + {Id: oidYubicoSerialNumber, Value: serialNumber}, + }, + }) + if err != nil { + t.Fatal(err) + } + return leaf + } + + signer, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + sassert.NoError(t, err) + serialNumber, err := asn1.Marshal(1234) + sassert.NoError(t, err) + leaf := makeLeaf(signer, serialNumber) + + jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) + sassert.NoError(t, err) + token := "token" + keyAuth, err := KeyAuthorization(token, jwk) + sassert.NoError(t, err) + keyAuthSum := sha256.Sum256([]byte(keyAuth)) + sig, err := signer.Sign(rand.Reader, keyAuthSum[:], crypto.SHA256) + sassert.NoError(t, err) + cborSig, err := cbor.Marshal(sig) + sassert.NoError(t, err) + + ch := &Challenge{ + ID: "chID", + Token: token, + Type: "device-attest-01", + Status: StatusPending, + Value: "1234", + } + attObj, err := cbor.Marshal(struct { + Format string `json:"fmt"` + AttStatement map[string]interface{} `json:"attStmt,omitempty"` + }{ + Format: "step", + AttStatement: map[string]interface{}{ + "x5c": []interface{}{leaf.Raw, ca.Intermediate.Raw}, + "alg": -7, + "sig": cborSig, + }, + }) + sassert.NoError(t, err) + payload, err := json.Marshal(struct { + AttObj string `json:"attObj"` + }{ + AttObj: base64.RawURLEncoding.EncodeToString(attObj), + }) + sassert.NoError(t, err) + return test{ + ch: ch, + payload: payload, + ctx: ctx, + jwk: jwk, + db: &MockDB{ + MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { + sassert.Equal(t, "chID", updch.ID) + sassert.Equal(t, token, updch.Token) + sassert.Equal(t, StatusValid, updch.Status) + sassert.Equal(t, ChallengeType("device-attest-01"), updch.Type) + sassert.Equal(t, "1234", updch.Value) + + return nil + }, + }, + } + }, } for name, run := range tests { t.Run(name, func(t *testing.T) { @@ -638,8 +778,12 @@ func TestChallenge_Validate(t *testing.T) { defer tc.srv.Close() } - ctx := NewClientContext(context.Background(), tc.vc) - if err := tc.ch.Validate(ctx, tc.db, tc.jwk, nil); err != nil { + ctx := tc.ctx + if ctx == nil { + ctx = context.Background() + } + ctx = NewClientContext(ctx, tc.vc) + if err := tc.ch.Validate(ctx, tc.db, tc.jwk, tc.payload); err != nil { if assert.NotNil(t, tc.err) { var k *Error if errors.As(err, &k) { @@ -2568,7 +2712,7 @@ func Test_doAppleAttestationFormat(t *testing.T) { ctx context.Context prov Provisioner ch *Challenge - att *AttestationObject + att *attestationObject } tests := []struct { name string @@ -2576,7 +2720,7 @@ func Test_doAppleAttestationFormat(t *testing.T) { want *appleAttestationData wantErr bool }{ - {"ok", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{}, &AttestationObject{ + {"ok", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{}, &attestationObject{ Format: "apple", AttStatement: map[string]interface{}{ "x5c": []interface{}{leaf.Raw, ca.Intermediate.Raw}, @@ -2588,49 +2732,49 @@ func Test_doAppleAttestationFormat(t *testing.T) { SEPVersion: "16.0", Certificate: leaf, }, false}, - {"fail apple issuer", args{ctx, mustAttestationProvisioner(t, nil), &Challenge{}, &AttestationObject{ + {"fail apple issuer", args{ctx, mustAttestationProvisioner(t, nil), &Challenge{}, &attestationObject{ Format: "apple", AttStatement: map[string]interface{}{ "x5c": []interface{}{leaf.Raw, ca.Intermediate.Raw}, }, }}, nil, true}, - {"fail missing x5c", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{}, &AttestationObject{ + {"fail missing x5c", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{}, &attestationObject{ Format: "apple", AttStatement: map[string]interface{}{ "foo": "bar", }, }}, nil, true}, - {"fail empty issuer", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{}, &AttestationObject{ + {"fail empty issuer", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{}, &attestationObject{ Format: "apple", AttStatement: map[string]interface{}{ "x5c": []interface{}{}, }, }}, nil, true}, - {"fail leaf type", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{}, &AttestationObject{ + {"fail leaf type", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{}, &attestationObject{ Format: "apple", AttStatement: map[string]interface{}{ "x5c": []interface{}{"leaf", ca.Intermediate.Raw}, }, }}, nil, true}, - {"fail leaf parse", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{}, &AttestationObject{ + {"fail leaf parse", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{}, &attestationObject{ Format: "apple", AttStatement: map[string]interface{}{ "x5c": []interface{}{leaf.Raw[:100], ca.Intermediate.Raw}, }, }}, nil, true}, - {"fail intermediate type", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{}, &AttestationObject{ + {"fail intermediate type", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{}, &attestationObject{ Format: "apple", AttStatement: map[string]interface{}{ "x5c": []interface{}{leaf.Raw, "intermediate"}, }, }}, nil, true}, - {"fail intermediate parse", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{}, &AttestationObject{ + {"fail intermediate parse", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{}, &attestationObject{ Format: "apple", AttStatement: map[string]interface{}{ "x5c": []interface{}{leaf.Raw, ca.Intermediate.Raw[:100]}, }, }}, nil, true}, - {"fail verify", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{}, &AttestationObject{ + {"fail verify", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{}, &attestationObject{ Format: "apple", AttStatement: map[string]interface{}{ "x5c": []interface{}{leaf.Raw}, @@ -2726,7 +2870,7 @@ func Test_doStepAttestationFormat(t *testing.T) { prov Provisioner ch *Challenge jwk *jose.JSONWebKey - att *AttestationObject + att *attestationObject } tests := []struct { name string @@ -2734,7 +2878,7 @@ func Test_doStepAttestationFormat(t *testing.T) { want *stepAttestationData wantErr bool }{ - {"ok", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{Token: "token"}, jwk, &AttestationObject{ + {"ok", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{Token: "token"}, jwk, &attestationObject{ Format: "step", AttStatement: map[string]interface{}{ "x5c": []interface{}{leaf.Raw, ca.Intermediate.Raw}, @@ -2745,7 +2889,7 @@ func Test_doStepAttestationFormat(t *testing.T) { SerialNumber: "1234", Certificate: leaf, }, false}, - {"fail yubico issuer", args{ctx, mustAttestationProvisioner(t, nil), &Challenge{Token: "token"}, jwk, &AttestationObject{ + {"fail yubico issuer", args{ctx, mustAttestationProvisioner(t, nil), &Challenge{Token: "token"}, jwk, &attestationObject{ Format: "step", AttStatement: map[string]interface{}{ "x5c": []interface{}{leaf.Raw, ca.Intermediate.Raw}, @@ -2753,7 +2897,7 @@ func Test_doStepAttestationFormat(t *testing.T) { "sig": cborSig, }, }}, nil, true}, - {"fail x5c type", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{Token: "token"}, jwk, &AttestationObject{ + {"fail x5c type", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{Token: "token"}, jwk, &attestationObject{ Format: "step", AttStatement: map[string]interface{}{ "x5c": [][]byte{leaf.Raw, ca.Intermediate.Raw}, @@ -2761,7 +2905,7 @@ func Test_doStepAttestationFormat(t *testing.T) { "sig": cborSig, }, }}, nil, true}, - {"fail x5c empty", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{Token: "token"}, jwk, &AttestationObject{ + {"fail x5c empty", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{Token: "token"}, jwk, &attestationObject{ Format: "step", AttStatement: map[string]interface{}{ "x5c": []interface{}{}, @@ -2769,7 +2913,7 @@ func Test_doStepAttestationFormat(t *testing.T) { "sig": cborSig, }, }}, nil, true}, - {"fail leaf type", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{Token: "token"}, jwk, &AttestationObject{ + {"fail leaf type", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{Token: "token"}, jwk, &attestationObject{ Format: "step", AttStatement: map[string]interface{}{ "x5c": []interface{}{"leaf", ca.Intermediate.Raw}, @@ -2777,7 +2921,7 @@ func Test_doStepAttestationFormat(t *testing.T) { "sig": cborSig, }, }}, nil, true}, - {"fail leaf parse", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{Token: "token"}, jwk, &AttestationObject{ + {"fail leaf parse", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{Token: "token"}, jwk, &attestationObject{ Format: "step", AttStatement: map[string]interface{}{ "x5c": []interface{}{leaf.Raw[:100], ca.Intermediate.Raw}, @@ -2785,7 +2929,7 @@ func Test_doStepAttestationFormat(t *testing.T) { "sig": cborSig, }, }}, nil, true}, - {"fail intermediate type", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{Token: "token"}, jwk, &AttestationObject{ + {"fail intermediate type", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{Token: "token"}, jwk, &attestationObject{ Format: "step", AttStatement: map[string]interface{}{ "x5c": []interface{}{leaf.Raw, "intermediate"}, @@ -2793,7 +2937,7 @@ func Test_doStepAttestationFormat(t *testing.T) { "sig": cborSig, }, }}, nil, true}, - {"fail intermediate parse", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{Token: "token"}, jwk, &AttestationObject{ + {"fail intermediate parse", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{Token: "token"}, jwk, &attestationObject{ Format: "step", AttStatement: map[string]interface{}{ "x5c": []interface{}{leaf.Raw, ca.Intermediate.Raw[:100]}, @@ -2801,7 +2945,7 @@ func Test_doStepAttestationFormat(t *testing.T) { "sig": cborSig, }, }}, nil, true}, - {"fail verify", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{Token: "token"}, jwk, &AttestationObject{ + {"fail verify", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{Token: "token"}, jwk, &attestationObject{ Format: "step", AttStatement: map[string]interface{}{ "x5c": []interface{}{leaf.Raw}, @@ -2809,7 +2953,7 @@ func Test_doStepAttestationFormat(t *testing.T) { "sig": cborSig, }, }}, nil, true}, - {"fail sig type", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{Token: "token"}, jwk, &AttestationObject{ + {"fail sig type", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{Token: "token"}, jwk, &attestationObject{ Format: "step", AttStatement: map[string]interface{}{ "x5c": []interface{}{leaf.Raw, ca.Intermediate.Raw}, @@ -2817,7 +2961,7 @@ func Test_doStepAttestationFormat(t *testing.T) { "sig": string(cborSig), }, }}, nil, true}, - {"fail sig unmarshal", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{Token: "token"}, jwk, &AttestationObject{ + {"fail sig unmarshal", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{Token: "token"}, jwk, &attestationObject{ Format: "step", AttStatement: map[string]interface{}{ "x5c": []interface{}{leaf.Raw, ca.Intermediate.Raw}, @@ -2825,7 +2969,7 @@ func Test_doStepAttestationFormat(t *testing.T) { "sig": []byte("bad-sig"), }, }}, nil, true}, - {"fail keyAuthorization", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{Token: "token"}, &jose.JSONWebKey{Key: []byte("not an asymmetric key")}, &AttestationObject{ + {"fail keyAuthorization", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{Token: "token"}, &jose.JSONWebKey{Key: []byte("not an asymmetric key")}, &attestationObject{ Format: "step", AttStatement: map[string]interface{}{ "x5c": []interface{}{leaf.Raw, ca.Intermediate.Raw}, @@ -2833,7 +2977,7 @@ func Test_doStepAttestationFormat(t *testing.T) { "sig": cborSig, }, }}, nil, true}, - {"fail sig verify P-256", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{Token: "token"}, jwk, &AttestationObject{ + {"fail sig verify P-256", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{Token: "token"}, jwk, &attestationObject{ Format: "step", AttStatement: map[string]interface{}{ "x5c": []interface{}{leaf.Raw, ca.Intermediate.Raw}, @@ -2841,7 +2985,7 @@ func Test_doStepAttestationFormat(t *testing.T) { "sig": otherCBORSig, }, }}, nil, true}, - {"fail sig verify P-384", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{Token: "token"}, jwk, &AttestationObject{ + {"fail sig verify P-384", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{Token: "token"}, jwk, &attestationObject{ Format: "step", AttStatement: map[string]interface{}{ "x5c": []interface{}{makeLeaf(mustSigner("EC", "P-384", 0), serialNumber).Raw, ca.Intermediate.Raw}, @@ -2849,7 +2993,7 @@ func Test_doStepAttestationFormat(t *testing.T) { "sig": cborSig, }, }}, nil, true}, - {"fail sig verify RSA", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{Token: "token"}, jwk, &AttestationObject{ + {"fail sig verify RSA", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{Token: "token"}, jwk, &attestationObject{ Format: "step", AttStatement: map[string]interface{}{ "x5c": []interface{}{makeLeaf(mustSigner("RSA", "", 2048), serialNumber).Raw, ca.Intermediate.Raw}, @@ -2857,7 +3001,7 @@ func Test_doStepAttestationFormat(t *testing.T) { "sig": cborSig, }, }}, nil, true}, - {"fail sig verify Ed25519", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{Token: "token"}, jwk, &AttestationObject{ + {"fail sig verify Ed25519", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{Token: "token"}, jwk, &attestationObject{ Format: "step", AttStatement: map[string]interface{}{ "x5c": []interface{}{makeLeaf(mustSigner("OKP", "Ed25519", 0), serialNumber).Raw, ca.Intermediate.Raw}, @@ -2865,7 +3009,7 @@ func Test_doStepAttestationFormat(t *testing.T) { "sig": cborSig, }, }}, nil, true}, - {"fail unmarshal serial number", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{Token: "token"}, jwk, &AttestationObject{ + {"fail unmarshal serial number", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{Token: "token"}, jwk, &attestationObject{ Format: "step", AttStatement: map[string]interface{}{ "x5c": []interface{}{makeLeaf(signer, []byte("bad-serial")).Raw, ca.Intermediate.Raw}, @@ -2951,7 +3095,7 @@ func Test_doStepAttestationFormat_noCAIntermediate(t *testing.T) { prov Provisioner ch *Challenge jwk *jose.JSONWebKey - att *AttestationObject + att *attestationObject } tests := []struct { name string @@ -2959,7 +3103,7 @@ func Test_doStepAttestationFormat_noCAIntermediate(t *testing.T) { want *stepAttestationData wantErr bool }{ - {"fail no intermediate", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{Token: "token"}, jwk, &AttestationObject{ + {"fail no intermediate", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{Token: "token"}, jwk, &attestationObject{ Format: "step", AttStatement: map[string]interface{}{ "x5c": []interface{}{leaf.Raw, ca.Intermediate.Raw}, @@ -2981,3 +3125,765 @@ func Test_doStepAttestationFormat_noCAIntermediate(t *testing.T) { }) } } + +func Test_deviceAttest01Validate(t *testing.T) { + invalidPayload := "!?" + errorPayload, err := json.Marshal(struct { + Error string `json:"error"` + }{ + Error: "an error", + }) + sassert.NoError(t, err) + errorBase64Payload, err := json.Marshal(struct { + AttObj string `json:"attObj"` + }{ + AttObj: "?!", + }) + sassert.NoError(t, err) + errorCBORPayload, err := json.Marshal(struct { + AttObj string `json:"attObj"` + }{ + AttObj: "AAAA", + }) + sassert.NoError(t, err) + type args struct { + ctx context.Context + ch *Challenge + db DB + jwk *jose.JSONWebKey + payload []byte + } + type test struct { + args args + wantErr *Error + } + tests := map[string]func(t *testing.T) test{ + "fail/json.Unmarshal": func(t *testing.T) test { + return test{ + args: args{ + ch: &Challenge{ + ID: "chID", + Token: "token", + Type: "device-attest-01", + Status: StatusPending, + Value: "12345678", + }, + payload: []byte(invalidPayload), + }, + wantErr: NewErrorISE("error unmarshalling JSON: invalid character '!' looking for beginning of value"), + } + + }, + "fail/storeError": func(t *testing.T) test { + return test{ + args: args{ + ch: &Challenge{ + ID: "chID", + Token: "token", + Type: "device-attest-01", + Status: StatusPending, + Value: "12345678", + }, + payload: errorPayload, + db: &MockDB{ + MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { + sassert.Equal(t, "chID", updch.ID) + sassert.Equal(t, "token", updch.Token) + sassert.Equal(t, StatusInvalid, updch.Status) + sassert.Equal(t, ChallengeType("device-attest-01"), updch.Type) + sassert.Equal(t, "12345678", updch.Value) + + err := NewError(ErrorRejectedIdentifierType, "payload contained error: an error") + + sassert.EqualError(t, err.Err, updch.Error.Err.Error()) + sassert.Equal(t, err.Type, updch.Error.Type) + sassert.Equal(t, err.Detail, updch.Error.Detail) + sassert.Equal(t, err.Status, updch.Error.Status) + sassert.Equal(t, err.Detail, updch.Error.Detail) + + return errors.New("force") + }, + }, + }, + wantErr: NewErrorISE("failure saving error to acme challenge: force"), + } + }, + "ok/storeError-return-nil": func(t *testing.T) test { + return test{ + args: args{ + ch: &Challenge{ + ID: "chID", + Token: "token", + Type: "device-attest-01", + Status: StatusPending, + Value: "12345678", + }, + payload: errorPayload, + db: &MockDB{ + MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { + sassert.Equal(t, "chID", updch.ID) + sassert.Equal(t, "token", updch.Token) + sassert.Equal(t, StatusInvalid, updch.Status) + sassert.Equal(t, ChallengeType("device-attest-01"), updch.Type) + sassert.Equal(t, "12345678", updch.Value) + + err := NewError(ErrorRejectedIdentifierType, "payload contained error: an error") + + sassert.EqualError(t, err.Err, updch.Error.Err.Error()) + sassert.Equal(t, err.Type, updch.Error.Type) + sassert.Equal(t, err.Detail, updch.Error.Detail) + sassert.Equal(t, err.Status, updch.Error.Status) + sassert.Equal(t, err.Detail, updch.Error.Detail) + + return nil + }, + }, + }, + wantErr: nil, + } + }, + "fail/base64-decode": func(t *testing.T) test { + return test{ + args: args{ + ch: &Challenge{ + ID: "chID", + Token: "token", + Type: "device-attest-01", + Status: StatusPending, + Value: "12345678", + }, + payload: errorBase64Payload, + }, + wantErr: NewErrorISE("error base64 decoding attObj: illegal base64 data at input byte 0"), + } + }, + "fail/cbor.Unmarshal": func(t *testing.T) test { + return test{ + args: args{ + ch: &Challenge{ + ID: "chID", + Token: "token", + Type: "device-attest-01", + Status: StatusPending, + Value: "12345678", + }, + payload: errorCBORPayload, + }, + wantErr: NewErrorISE("error unmarshalling CBOR: cbor: cannot unmarshal positive integer into Go value of type acme.attestationObject"), + } + }, + "ok/prov.IsAttestationFormatEnabled": func(t *testing.T) test { + ca, err := minica.New() + sassert.NoError(t, err) + makeLeaf := func(signer crypto.Signer, serialNumber []byte) *x509.Certificate { + leaf, err := ca.Sign(&x509.Certificate{ + Subject: pkix.Name{CommonName: "attestation cert"}, + PublicKey: signer.Public(), + ExtraExtensions: []pkix.Extension{ + {Id: oidYubicoSerialNumber, Value: serialNumber}, + }, + }) + if err != nil { + t.Fatal(err) + } + return leaf + } + signer, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + sassert.NoError(t, err) + serialNumber, err := asn1.Marshal(1234) + sassert.NoError(t, err) + leaf := makeLeaf(signer, serialNumber) + jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) + sassert.NoError(t, err) + token := "token" + keyAuth, err := KeyAuthorization(token, jwk) + sassert.NoError(t, err) + keyAuthSum := sha256.Sum256([]byte(keyAuth)) + sig, err := signer.Sign(rand.Reader, keyAuthSum[:], crypto.SHA256) + sassert.NoError(t, err) + cborSig, err := cbor.Marshal(sig) + sassert.NoError(t, err) + ctx := NewProvisionerContext(context.Background(), mustNonAttestationProvisioner(t)) + attObj, err := cbor.Marshal(struct { + Format string `json:"fmt"` + AttStatement map[string]interface{} `json:"attStmt,omitempty"` + }{ + Format: "step", + AttStatement: map[string]interface{}{ + "x5c": []interface{}{leaf.Raw, ca.Intermediate.Raw}, + "alg": -7, + "sig": cborSig, + }, + }) + sassert.NoError(t, err) + payload, err := json.Marshal(struct { + AttObj string `json:"attObj"` + }{ + AttObj: base64.RawURLEncoding.EncodeToString(attObj), + }) + sassert.NoError(t, err) + return test{ + args: args{ + ctx: ctx, + ch: &Challenge{ + ID: "chID", + Token: "token", + Type: "device-attest-01", + Status: StatusPending, + Value: "12345678", + }, + payload: payload, + db: &MockDB{ + MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { + sassert.Equal(t, "chID", updch.ID) + sassert.Equal(t, "token", updch.Token) + sassert.Equal(t, StatusInvalid, updch.Status) + sassert.Equal(t, ChallengeType("device-attest-01"), updch.Type) + sassert.Equal(t, "12345678", updch.Value) + + err := NewError(ErrorBadAttestationStatementType, "attestation format %q is not enabled", "step") + + sassert.EqualError(t, err.Err, updch.Error.Err.Error()) + sassert.Equal(t, err.Type, updch.Error.Type) + sassert.Equal(t, err.Detail, updch.Error.Detail) + sassert.Equal(t, err.Status, updch.Error.Status) + sassert.Equal(t, err.Detail, updch.Error.Detail) + + return nil + }, + }, + }, + wantErr: nil, + } + }, + "ok/doAppleAttestationFormat-storeError": func(t *testing.T) test { + ctx := NewProvisionerContext(context.Background(), mustAttestationProvisioner(t, nil)) + attObj, err := cbor.Marshal(struct { + Format string `json:"fmt"` + AttStatement map[string]interface{} `json:"attStmt,omitempty"` + }{ + Format: "apple", + AttStatement: map[string]interface{}{}, + }) + sassert.NoError(t, err) + payload, err := json.Marshal(struct { + AttObj string `json:"attObj"` + }{ + AttObj: base64.RawURLEncoding.EncodeToString(attObj), + }) + sassert.NoError(t, err) + return test{ + args: args{ + ctx: ctx, + ch: &Challenge{ + ID: "chID", + Token: "token", + Type: "device-attest-01", + Status: StatusPending, + Value: "12345678", + }, + payload: payload, + db: &MockDB{ + MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { + sassert.Equal(t, "chID", updch.ID) + sassert.Equal(t, "token", updch.Token) + sassert.Equal(t, StatusInvalid, updch.Status) + sassert.Equal(t, ChallengeType("device-attest-01"), updch.Type) + sassert.Equal(t, "12345678", updch.Value) + + err := NewError(ErrorBadAttestationStatementType, "x5c not present") + + sassert.EqualError(t, err.Err, updch.Error.Err.Error()) + sassert.Equal(t, err.Type, updch.Error.Type) + sassert.Equal(t, err.Detail, updch.Error.Detail) + sassert.Equal(t, err.Status, updch.Error.Status) + sassert.Equal(t, err.Detail, updch.Error.Detail) + + return nil + }, + }, + }, + wantErr: nil, + } + }, + "ok/doAppleAttestationFormat-non-matching-nonce": func(t *testing.T) test { + ca, err := minica.New() + sassert.NoError(t, err) + signer, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + sassert.NoError(t, err) + caRoot := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: ca.Root.Raw}) + leaf, err := ca.Sign(&x509.Certificate{ + Subject: pkix.Name{CommonName: "attestation cert"}, + PublicKey: signer.Public(), + ExtraExtensions: []pkix.Extension{ + {Id: oidAppleSerialNumber, Value: []byte("serial-number")}, + {Id: oidAppleUniqueDeviceIdentifier, Value: []byte("udid")}, + {Id: oidAppleSecureEnclaveProcessorOSVersion, Value: []byte("16.0")}, + {Id: oidAppleNonce, Value: []byte("nonce")}, + }, + }) + sassert.NoError(t, err) + ctx := NewProvisionerContext(context.Background(), mustAttestationProvisioner(t, caRoot)) + attObj, err := cbor.Marshal(struct { + Format string `json:"fmt"` + AttStatement map[string]interface{} `json:"attStmt,omitempty"` + }{ + Format: "apple", + AttStatement: map[string]interface{}{ + "x5c": []interface{}{leaf.Raw, ca.Intermediate.Raw}, + }, + }) + sassert.NoError(t, err) + payload, err := json.Marshal(struct { + AttObj string `json:"attObj"` + }{ + AttObj: base64.RawURLEncoding.EncodeToString(attObj), + }) + sassert.NoError(t, err) + return test{ + args: args{ + ctx: ctx, + ch: &Challenge{ + ID: "chID", + Token: "token", + Type: "device-attest-01", + Status: StatusPending, + Value: "12345678", + }, + payload: payload, + db: &MockDB{ + MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { + sassert.Equal(t, "chID", updch.ID) + sassert.Equal(t, "token", updch.Token) + sassert.Equal(t, StatusInvalid, updch.Status) + sassert.Equal(t, ChallengeType("device-attest-01"), updch.Type) + sassert.Equal(t, "12345678", updch.Value) + + err := NewError(ErrorBadAttestationStatementType, "challenge token does not match") + + sassert.EqualError(t, updch.Error.Err, err.Err.Error()) + sassert.Equal(t, err.Type, updch.Error.Type) + sassert.Equal(t, err.Detail, updch.Error.Detail) + sassert.Equal(t, err.Status, updch.Error.Status) + sassert.Equal(t, err.Detail, updch.Error.Detail) + + return nil + }, + }, + }, + wantErr: nil, + } + }, + "ok/doAppleAttestationFormat-non-matching-challenge-value": func(t *testing.T) test { + ca, err := minica.New() + sassert.NoError(t, err) + signer, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + sassert.NoError(t, err) + caRoot := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: ca.Root.Raw}) + nonce := sha256.Sum256([]byte("nonce")) + leaf, err := ca.Sign(&x509.Certificate{ + Subject: pkix.Name{CommonName: "attestation cert"}, + PublicKey: signer.Public(), + ExtraExtensions: []pkix.Extension{ + {Id: oidAppleSerialNumber, Value: []byte("serial-number")}, + {Id: oidAppleUniqueDeviceIdentifier, Value: []byte("udid")}, + {Id: oidAppleSecureEnclaveProcessorOSVersion, Value: []byte("16.0")}, + {Id: oidAppleNonce, Value: nonce[:]}, + }, + }) + sassert.NoError(t, err) + ctx := NewProvisionerContext(context.Background(), mustAttestationProvisioner(t, caRoot)) + attObj, err := cbor.Marshal(struct { + Format string `json:"fmt"` + AttStatement map[string]interface{} `json:"attStmt,omitempty"` + }{ + Format: "apple", + AttStatement: map[string]interface{}{ + "x5c": []interface{}{leaf.Raw, ca.Intermediate.Raw}, + }, + }) + sassert.NoError(t, err) + payload, err := json.Marshal(struct { + AttObj string `json:"attObj"` + }{ + AttObj: base64.RawURLEncoding.EncodeToString(attObj), + }) + sassert.NoError(t, err) + return test{ + args: args{ + ctx: ctx, + ch: &Challenge{ + ID: "chID", + Token: "nonce", + Type: "device-attest-01", + Status: StatusPending, + Value: "non-matching-value", + }, + payload: payload, + db: &MockDB{ + MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { + sassert.Equal(t, "chID", updch.ID) + sassert.Equal(t, "nonce", updch.Token) + sassert.Equal(t, StatusInvalid, updch.Status) + sassert.Equal(t, ChallengeType("device-attest-01"), updch.Type) + sassert.Equal(t, "non-matching-value", updch.Value) + + err := NewError(ErrorBadAttestationStatementType, "permanent identifier does not match") + + sassert.EqualError(t, updch.Error.Err, err.Err.Error()) + sassert.Equal(t, err.Type, updch.Error.Type) + sassert.Equal(t, err.Detail, updch.Error.Detail) + sassert.Equal(t, err.Status, updch.Error.Status) + sassert.Equal(t, err.Detail, updch.Error.Detail) + + return nil + }, + }, + }, + wantErr: nil, + } + }, + "ok/doStepAttestationFormat-storeError": func(t *testing.T) test { + ca, err := minica.New() + sassert.NoError(t, err) + caRoot := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: ca.Root.Raw}) + signer, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + sassert.NoError(t, err) + jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) + sassert.NoError(t, err) + token := "token" + keyAuth, err := KeyAuthorization(token, jwk) + sassert.NoError(t, err) + keyAuthSum := sha256.Sum256([]byte(keyAuth)) + sig, err := signer.Sign(rand.Reader, keyAuthSum[:], crypto.SHA256) + sassert.NoError(t, err) + cborSig, err := cbor.Marshal(sig) + sassert.NoError(t, err) + ctx := NewProvisionerContext(context.Background(), mustAttestationProvisioner(t, caRoot)) + attObj, err := cbor.Marshal(struct { + Format string `json:"fmt"` + AttStatement map[string]interface{} `json:"attStmt,omitempty"` + }{ + Format: "step", + AttStatement: map[string]interface{}{ + "alg": -7, + "sig": cborSig, + }, + }) + sassert.NoError(t, err) + payload, err := json.Marshal(struct { + AttObj string `json:"attObj"` + }{ + AttObj: base64.RawURLEncoding.EncodeToString(attObj), + }) + sassert.NoError(t, err) + return test{ + args: args{ + ctx: ctx, + ch: &Challenge{ + ID: "chID", + Token: "token", + Type: "device-attest-01", + Status: StatusPending, + Value: "12345678", + }, + payload: payload, + db: &MockDB{ + MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { + sassert.Equal(t, "chID", updch.ID) + sassert.Equal(t, "token", updch.Token) + sassert.Equal(t, StatusInvalid, updch.Status) + sassert.Equal(t, ChallengeType("device-attest-01"), updch.Type) + sassert.Equal(t, "12345678", updch.Value) + + err := NewError(ErrorBadAttestationStatementType, "x5c not present") + + sassert.EqualError(t, updch.Error.Err, err.Err.Error()) + sassert.Equal(t, err.Type, updch.Error.Type) + sassert.Equal(t, err.Detail, updch.Error.Detail) + sassert.Equal(t, err.Status, updch.Error.Status) + sassert.Equal(t, err.Detail, updch.Error.Detail) + + return nil + }, + }, + }, + wantErr: nil, + } + }, + "ok/doStepAttestationFormat-non-matching-identifier": func(t *testing.T) test { + ca, err := minica.New() + sassert.NoError(t, err) + caRoot := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: ca.Root.Raw}) + signer, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + sassert.NoError(t, err) + jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) + sassert.NoError(t, err) + token := "token" + keyAuth, err := KeyAuthorization(token, jwk) + sassert.NoError(t, err) + keyAuthSum := sha256.Sum256([]byte(keyAuth)) + sig, err := signer.Sign(rand.Reader, keyAuthSum[:], crypto.SHA256) + sassert.NoError(t, err) + cborSig, err := cbor.Marshal(sig) + sassert.NoError(t, err) + ctx := NewProvisionerContext(context.Background(), mustAttestationProvisioner(t, caRoot)) + makeLeaf := func(signer crypto.Signer, serialNumber []byte) *x509.Certificate { + leaf, err := ca.Sign(&x509.Certificate{ + Subject: pkix.Name{CommonName: "attestation cert"}, + PublicKey: signer.Public(), + ExtraExtensions: []pkix.Extension{ + {Id: oidYubicoSerialNumber, Value: serialNumber}, + }, + }) + if err != nil { + t.Fatal(err) + } + return leaf + } + sassert.NoError(t, err) + serialNumber, err := asn1.Marshal(87654321) + sassert.NoError(t, err) + leaf := makeLeaf(signer, serialNumber) + attObj, err := cbor.Marshal(struct { + Format string `json:"fmt"` + AttStatement map[string]interface{} `json:"attStmt,omitempty"` + }{ + Format: "step", + AttStatement: map[string]interface{}{ + "x5c": []interface{}{leaf.Raw, ca.Intermediate.Raw}, + "alg": -7, + "sig": cborSig, + }, + }) + sassert.NoError(t, err) + payload, err := json.Marshal(struct { + AttObj string `json:"attObj"` + }{ + AttObj: base64.RawURLEncoding.EncodeToString(attObj), + }) + sassert.NoError(t, err) + return test{ + args: args{ + ctx: ctx, + ch: &Challenge{ + ID: "chID", + Token: "token", + Type: "device-attest-01", + Status: StatusPending, + Value: "12345678", + }, + payload: payload, + db: &MockDB{ + MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { + sassert.Equal(t, "chID", updch.ID) + sassert.Equal(t, "token", updch.Token) + sassert.Equal(t, StatusInvalid, updch.Status) + sassert.Equal(t, ChallengeType("device-attest-01"), updch.Type) + sassert.Equal(t, "12345678", updch.Value) + + err := NewError(ErrorBadAttestationStatementType, "permanent identifier does not match"). + AddSubproblems(NewSubproblemWithIdentifier( + ErrorMalformedType, + Identifier{Type: "permanent-identifier", Value: "12345678"}, + "challenge identifier \"12345678\" doesn't match the attested hardware identifier \"87654321\"", + )) + + sassert.EqualError(t, err.Err, updch.Error.Err.Error()) + sassert.Equal(t, err.Type, updch.Error.Type) + sassert.Equal(t, err.Detail, updch.Error.Detail) + sassert.Equal(t, err.Status, updch.Error.Status) + sassert.Equal(t, err.Detail, updch.Error.Detail) + sassert.Equal(t, err.Subproblems, updch.Error.Subproblems) + + return nil + }, + }, + jwk: jwk, + }, + wantErr: nil, + } + }, + "ok/unknown-attestation-format": func(t *testing.T) test { + ca, err := minica.New() + sassert.NoError(t, err) + signer, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + sassert.NoError(t, err) + jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) + sassert.NoError(t, err) + token := "token" + keyAuth, err := KeyAuthorization(token, jwk) + sassert.NoError(t, err) + keyAuthSum := sha256.Sum256([]byte(keyAuth)) + sig, err := signer.Sign(rand.Reader, keyAuthSum[:], crypto.SHA256) + sassert.NoError(t, err) + cborSig, err := cbor.Marshal(sig) + sassert.NoError(t, err) + ctx := NewProvisionerContext(context.Background(), mustNonAttestationProvisioner(t)) + makeLeaf := func(signer crypto.Signer, serialNumber []byte) *x509.Certificate { + leaf, err := ca.Sign(&x509.Certificate{ + Subject: pkix.Name{CommonName: "attestation cert"}, + PublicKey: signer.Public(), + ExtraExtensions: []pkix.Extension{ + {Id: oidYubicoSerialNumber, Value: serialNumber}, + }, + }) + if err != nil { + t.Fatal(err) + } + return leaf + } + sassert.NoError(t, err) + serialNumber, err := asn1.Marshal(87654321) + sassert.NoError(t, err) + leaf := makeLeaf(signer, serialNumber) + attObj, err := cbor.Marshal(struct { + Format string `json:"fmt"` + AttStatement map[string]interface{} `json:"attStmt,omitempty"` + }{ + Format: "bogus-format", + AttStatement: map[string]interface{}{ + "x5c": []interface{}{leaf.Raw, ca.Intermediate.Raw}, + "alg": -7, + "sig": cborSig, + }, + }) + sassert.NoError(t, err) + payload, err := json.Marshal(struct { + AttObj string `json:"attObj"` + }{ + AttObj: base64.RawURLEncoding.EncodeToString(attObj), + }) + sassert.NoError(t, err) + return test{ + args: args{ + ctx: ctx, + ch: &Challenge{ + ID: "chID", + Token: "token", + Type: "device-attest-01", + Status: StatusPending, + Value: "12345678", + }, + payload: payload, + db: &MockDB{ + MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { + sassert.Equal(t, "chID", updch.ID) + sassert.Equal(t, "token", updch.Token) + sassert.Equal(t, StatusInvalid, updch.Status) + sassert.Equal(t, ChallengeType("device-attest-01"), updch.Type) + sassert.Equal(t, "12345678", updch.Value) + + err := NewError(ErrorBadAttestationStatementType, "unexpected attestation object format") + + sassert.EqualError(t, err.Err, updch.Error.Err.Error()) + sassert.Equal(t, err.Type, updch.Error.Type) + sassert.Equal(t, err.Detail, updch.Error.Detail) + sassert.Equal(t, err.Status, updch.Error.Status) + sassert.Equal(t, err.Detail, updch.Error.Detail) + sassert.Equal(t, err.Subproblems, updch.Error.Subproblems) + + return nil + }, + }, + jwk: jwk, + }, + wantErr: nil, + } + }, + "fail/db.UpdateChallenge": func(t *testing.T) test { + ca, err := minica.New() + sassert.NoError(t, err) + caRoot := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: ca.Root.Raw}) + signer, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + sassert.NoError(t, err) + jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) + sassert.NoError(t, err) + token := "token" + keyAuth, err := KeyAuthorization(token, jwk) + sassert.NoError(t, err) + keyAuthSum := sha256.Sum256([]byte(keyAuth)) + sig, err := signer.Sign(rand.Reader, keyAuthSum[:], crypto.SHA256) + sassert.NoError(t, err) + cborSig, err := cbor.Marshal(sig) + sassert.NoError(t, err) + ctx := NewProvisionerContext(context.Background(), mustAttestationProvisioner(t, caRoot)) + makeLeaf := func(signer crypto.Signer, serialNumber []byte) *x509.Certificate { + leaf, err := ca.Sign(&x509.Certificate{ + Subject: pkix.Name{CommonName: "attestation cert"}, + PublicKey: signer.Public(), + ExtraExtensions: []pkix.Extension{ + {Id: oidYubicoSerialNumber, Value: serialNumber}, + }, + }) + if err != nil { + t.Fatal(err) + } + return leaf + } + sassert.NoError(t, err) + serialNumber, err := asn1.Marshal(12345678) + sassert.NoError(t, err) + leaf := makeLeaf(signer, serialNumber) + attObj, err := cbor.Marshal(struct { + Format string `json:"fmt"` + AttStatement map[string]interface{} `json:"attStmt,omitempty"` + }{ + Format: "step", + AttStatement: map[string]interface{}{ + "x5c": []interface{}{leaf.Raw, ca.Intermediate.Raw}, + "alg": -7, + "sig": cborSig, + }, + }) + sassert.NoError(t, err) + payload, err := json.Marshal(struct { + AttObj string `json:"attObj"` + }{ + AttObj: base64.RawURLEncoding.EncodeToString(attObj), + }) + sassert.NoError(t, err) + return test{ + args: args{ + ctx: ctx, + ch: &Challenge{ + ID: "chID", + Token: "token", + Type: "device-attest-01", + Status: StatusPending, + Value: "12345678", + }, + payload: payload, + db: &MockDB{ + MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { + sassert.Equal(t, "chID", updch.ID) + sassert.Equal(t, "token", updch.Token) + sassert.Equal(t, StatusValid, updch.Status) + sassert.Equal(t, ChallengeType("device-attest-01"), updch.Type) + sassert.Equal(t, "12345678", updch.Value) + + return errors.New("force") + }, + }, + jwk: jwk, + }, + wantErr: NewError(ErrorServerInternalType, "error updating challenge: force"), + } + }, + } + for name, run := range tests { + t.Run(name, func(t *testing.T) { + tc := run(t) + + if err := deviceAttest01Validate(tc.args.ctx, tc.args.ch, tc.args.db, tc.args.jwk, tc.args.payload); err != nil { + sassert.NotNil(t, tc.wantErr) + sassert.EqualError(t, tc.wantErr, err.Error()) + return + } + + sassert.Nil(t, tc.wantErr) + + // TODO: more validations? + }) + } +} From 0f9128c87317e5cb86539a0c7a31f8f4b1c87f5a Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Fri, 27 Jan 2023 15:43:57 +0100 Subject: [PATCH 7/9] Fix linting issue and order of test SUT --- acme/challenge.go | 1 - acme/challenge_test.go | 14 +++++++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/acme/challenge.go b/acme/challenge.go index 18921b70..7d1f4dee 100644 --- a/acme/challenge.go +++ b/acme/challenge.go @@ -349,7 +349,6 @@ type attestationObject struct { func deviceAttest01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSONWebKey, payload []byte) error { var p payloadType if err := json.Unmarshal(payload, &p); err != nil { - return WrapErrorISE(err, "error unmarshalling JSON") } if p.Error != "" { diff --git a/acme/challenge_test.go b/acme/challenge_test.go index 23d28d4e..13423cf9 100644 --- a/acme/challenge_test.go +++ b/acme/challenge_test.go @@ -677,7 +677,7 @@ func TestChallenge_Validate(t *testing.T) { err := NewError(ErrorRejectedIdentifierType, "payload contained error: an error") - sassert.EqualError(t, err.Err, updch.Error.Err.Error()) + sassert.EqualError(t, updch.Error.Err, err.Err.Error()) sassert.Equal(t, err.Type, updch.Error.Type) sassert.Equal(t, err.Detail, updch.Error.Detail) sassert.Equal(t, err.Status, updch.Error.Status) @@ -3195,7 +3195,7 @@ func Test_deviceAttest01Validate(t *testing.T) { err := NewError(ErrorRejectedIdentifierType, "payload contained error: an error") - sassert.EqualError(t, err.Err, updch.Error.Err.Error()) + sassert.EqualError(t, updch.Error.Err, err.Err.Error()) sassert.Equal(t, err.Type, updch.Error.Type) sassert.Equal(t, err.Detail, updch.Error.Detail) sassert.Equal(t, err.Status, updch.Error.Status) @@ -3229,7 +3229,7 @@ func Test_deviceAttest01Validate(t *testing.T) { err := NewError(ErrorRejectedIdentifierType, "payload contained error: an error") - sassert.EqualError(t, err.Err, updch.Error.Err.Error()) + sassert.EqualError(t, updch.Error.Err, err.Err.Error()) sassert.Equal(t, err.Type, updch.Error.Type) sassert.Equal(t, err.Detail, updch.Error.Detail) sassert.Equal(t, err.Status, updch.Error.Status) @@ -3343,7 +3343,7 @@ func Test_deviceAttest01Validate(t *testing.T) { err := NewError(ErrorBadAttestationStatementType, "attestation format %q is not enabled", "step") - sassert.EqualError(t, err.Err, updch.Error.Err.Error()) + sassert.EqualError(t, updch.Error.Err, err.Err.Error()) sassert.Equal(t, err.Type, updch.Error.Type) sassert.Equal(t, err.Detail, updch.Error.Detail) sassert.Equal(t, err.Status, updch.Error.Status) @@ -3393,7 +3393,7 @@ func Test_deviceAttest01Validate(t *testing.T) { err := NewError(ErrorBadAttestationStatementType, "x5c not present") - sassert.EqualError(t, err.Err, updch.Error.Err.Error()) + sassert.EqualError(t, updch.Error.Err, err.Err.Error()) sassert.Equal(t, err.Type, updch.Error.Type) sassert.Equal(t, err.Detail, updch.Error.Detail) sassert.Equal(t, err.Status, updch.Error.Status) @@ -3689,7 +3689,7 @@ func Test_deviceAttest01Validate(t *testing.T) { "challenge identifier \"12345678\" doesn't match the attested hardware identifier \"87654321\"", )) - sassert.EqualError(t, err.Err, updch.Error.Err.Error()) + sassert.EqualError(t, updch.Error.Err, err.Err.Error()) sassert.Equal(t, err.Type, updch.Error.Type) sassert.Equal(t, err.Detail, updch.Error.Detail) sassert.Equal(t, err.Status, updch.Error.Status) @@ -3776,7 +3776,7 @@ func Test_deviceAttest01Validate(t *testing.T) { err := NewError(ErrorBadAttestationStatementType, "unexpected attestation object format") - sassert.EqualError(t, err.Err, updch.Error.Err.Error()) + sassert.EqualError(t, updch.Error.Err, err.Err.Error()) sassert.Equal(t, err.Type, updch.Error.Type) sassert.Equal(t, err.Detail, updch.Error.Detail) sassert.Equal(t, err.Status, updch.Error.Status) From 0f1c509e4bd3f466dc36f1a67621d3b3ffe95a0c Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Tue, 31 Jan 2023 23:48:53 +0100 Subject: [PATCH 8/9] Remove debug utility --- acme/api/handler.go | 3 --- acme/db/nosql/challenge.go | 3 --- go.mod | 4 ---- go.sum | 2 -- utils/debug/q/debug.go | 13 ------------- 5 files changed, 25 deletions(-) delete mode 100644 utils/debug/q/debug.go diff --git a/acme/api/handler.go b/acme/api/handler.go index ca5de02e..e6aad131 100644 --- a/acme/api/handler.go +++ b/acme/api/handler.go @@ -16,7 +16,6 @@ import ( "github.com/smallstep/certificates/api/render" "github.com/smallstep/certificates/authority" "github.com/smallstep/certificates/authority/provisioner" - "github.com/smallstep/certificates/utils/debug/q" ) func link(url, typ string) string { @@ -356,8 +355,6 @@ func GetChallenge(w http.ResponseWriter, r *http.Request) { return } - q.Q(ch) - linker.LinkChallenge(ctx, ch, azID) w.Header().Add("Link", link(linker.GetLink(ctx, acme.AuthzLinkType, azID), "up")) diff --git a/acme/db/nosql/challenge.go b/acme/db/nosql/challenge.go index bb1c4080..c9224574 100644 --- a/acme/db/nosql/challenge.go +++ b/acme/db/nosql/challenge.go @@ -10,7 +10,6 @@ import ( "github.com/smallstep/nosql" "github.com/smallstep/certificates/acme" - "github.com/smallstep/certificates/utils/debug/q" ) type dbChallenge struct { @@ -32,7 +31,6 @@ func (dbc *dbChallenge) clone() *dbChallenge { func (db *DB) getDBChallenge(ctx context.Context, id string) (*dbChallenge, error) { data, err := db.db.Get(challengeTable, []byte(id)) - q.Q(data) if nosql.IsErrNotFound(err) { return nil, acme.NewError(acme.ErrorMalformedType, "challenge %s not found", id) } else if err != nil { @@ -43,7 +41,6 @@ func (db *DB) getDBChallenge(ctx context.Context, id string) (*dbChallenge, erro if err := json.Unmarshal(data, dbch); err != nil { return nil, errors.Wrap(err, "error unmarshaling dbChallenge") } - q.Q(dbch) return dbch, nil } diff --git a/go.mod b/go.mod index e4f5848f..784827b2 100644 --- a/go.mod +++ b/go.mod @@ -56,8 +56,6 @@ require ( gopkg.in/square/go-jose.v2 v2.6.0 ) -require github.com/ryboe/q v1.0.18 - require ( cloud.google.com/go/compute v1.14.0 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect @@ -124,7 +122,6 @@ require ( github.com/jackc/pgx/v4 v4.17.2 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/klauspost/compress v1.15.11 // indirect - github.com/kr/text v0.2.0 // indirect github.com/manifoldco/promptui v0.9.0 // indirect github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect github.com/miekg/pkcs11 v1.1.1 // indirect @@ -136,7 +133,6 @@ require ( github.com/oklog/run v1.0.0 // indirect github.com/pierrec/lz4 v2.5.2+incompatible // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/rogpeppe/go-internal v1.9.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/ryanuber/go-glob v1.0.0 // indirect github.com/shopspring/decimal v1.2.0 // indirect diff --git a/go.sum b/go.sum index 6760b242..83aab357 100644 --- a/go.sum +++ b/go.sum @@ -599,8 +599,6 @@ github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= -github.com/ryboe/q v1.0.18 h1:uTonPt1eZjy7GSpB0XpYpsCvX+Yf9f+M4CUKuH2r+vg= -github.com/ryboe/q v1.0.18/go.mod h1:elqvVf/GBuZHvZ9gvHv4MKM6NZAMz2rFajnTgQZ46wU= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= diff --git a/utils/debug/q/debug.go b/utils/debug/q/debug.go deleted file mode 100644 index e12c73cd..00000000 --- a/utils/debug/q/debug.go +++ /dev/null @@ -1,13 +0,0 @@ -package q - -import ( - ryboeq "github.com/ryboe/q" -) - -func Q(v ...interface{}) { - // TODO(hs): do or do not call ryboeq.Q based on e.g. debug flag, - // runtime (go run vs. build), based on compiled or not. Goal would be - // to not debug in prod builds at all times. Ideally, never leave a leftover - // call to q.Q in the code, so panic if there is? - ryboeq.Q(v...) -} From 3a6fc5e0b4d7603e53511eb6d8744b2714f02f59 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Tue, 31 Jan 2023 23:49:34 +0100 Subject: [PATCH 9/9] Remove dependency on `smallstep/assert` in ACME challenge tests --- acme/challenge_test.go | 1643 +++++++++++++++++++++------------------- 1 file changed, 878 insertions(+), 765 deletions(-) diff --git a/acme/challenge_test.go b/acme/challenge_test.go index 13423cf9..fb94d8a7 100644 --- a/acme/challenge_test.go +++ b/acme/challenge_test.go @@ -31,13 +31,15 @@ import ( "time" "github.com/fxamacker/cbor/v2" - "github.com/smallstep/assert" - "github.com/smallstep/certificates/authority/config" - "github.com/smallstep/certificates/authority/provisioner" - sassert "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.step.sm/crypto/jose" "go.step.sm/crypto/keyutil" "go.step.sm/crypto/minica" + + "github.com/smallstep/certificates/authority/config" + "github.com/smallstep/certificates/authority/provisioner" ) type mockClient struct { @@ -106,16 +108,17 @@ func Test_storeError(t *testing.T) { ch: ch, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Value, ch.Value) - assert.Equals(t, updch.Status, StatusValid) - - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, "zap.internal", updch.Value) + assert.Equal(t, StatusValid, updch.Status) + + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return errors.New("force") }, }, @@ -133,16 +136,17 @@ func Test_storeError(t *testing.T) { ch: ch, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Value, ch.Value) - assert.Equals(t, updch.Status, StatusValid) - - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, "zap.internal", updch.Value) + assert.Equal(t, StatusValid, updch.Status) + + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return NewError(ErrorMalformedType, "bar") }, }, @@ -160,16 +164,17 @@ func Test_storeError(t *testing.T) { ch: ch, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Value, ch.Value) - assert.Equals(t, updch.Status, StatusValid) - - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, "zap.internal", updch.Value) + assert.Equal(t, StatusValid, updch.Status) + + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return nil }, }, @@ -186,16 +191,17 @@ func Test_storeError(t *testing.T) { ch: ch, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Value, ch.Value) - assert.Equals(t, updch.Status, StatusInvalid) - - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, "zap.internal", updch.Value) + assert.Equal(t, StatusInvalid, updch.Status) + + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return nil }, }, @@ -207,16 +213,15 @@ func Test_storeError(t *testing.T) { t.Run(name, func(t *testing.T) { tc := run(t) if err := storeError(context.Background(), tc.db, tc.ch, tc.markInvalid, err); err != nil { - if assert.NotNil(t, tc.err) { + if assert.Error(t, tc.err) { var k *Error if errors.As(err, &k) { - assert.Equals(t, k.Type, tc.err.Type) - assert.Equals(t, k.Detail, tc.err.Detail) - assert.Equals(t, k.Status, tc.err.Status) - assert.Equals(t, k.Err.Error(), tc.err.Err.Error()) - assert.Equals(t, k.Detail, tc.err.Detail) + assert.Equal(t, tc.err.Type, k.Type) + assert.Equal(t, tc.err.Detail, k.Detail) + assert.Equal(t, tc.err.Status, k.Status) + assert.Equal(t, tc.err.Err.Error(), k.Err.Error()) } else { - assert.FatalError(t, errors.New("unexpected error type")) + assert.Fail(t, "unexpected error type") } } } else { @@ -236,7 +241,7 @@ func TestKeyAuthorization(t *testing.T) { tests := map[string]func(t *testing.T) test{ "fail/jwk-thumbprint-error": func(t *testing.T) test { jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - assert.FatalError(t, err) + require.NoError(t, err) jwk.Key = "foo" return test{ token: "1234", @@ -247,9 +252,9 @@ func TestKeyAuthorization(t *testing.T) { "ok": func(t *testing.T) test { token := "1234" jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - assert.FatalError(t, err) + require.NoError(t, err) thumbprint, err := jwk.Thumbprint(crypto.SHA256) - assert.FatalError(t, err) + require.NoError(t, err) encPrint := base64.RawURLEncoding.EncodeToString(thumbprint) return test{ token: token, @@ -262,21 +267,20 @@ func TestKeyAuthorization(t *testing.T) { t.Run(name, func(t *testing.T) { tc := run(t) if ka, err := KeyAuthorization(tc.token, tc.jwk); err != nil { - if assert.NotNil(t, tc.err) { + if assert.Error(t, tc.err) { var k *Error if errors.As(err, &k) { - assert.Equals(t, k.Type, tc.err.Type) - assert.Equals(t, k.Detail, tc.err.Detail) - assert.Equals(t, k.Status, tc.err.Status) - assert.Equals(t, k.Err.Error(), tc.err.Err.Error()) - assert.Equals(t, k.Detail, tc.err.Detail) + assert.Equal(t, tc.err.Type, k.Type) + assert.Equal(t, tc.err.Detail, k.Detail) + assert.Equal(t, tc.err.Status, k.Status) + assert.Equal(t, tc.err.Err.Error(), k.Err.Error()) } else { - assert.FatalError(t, errors.New("unexpected error type")) + assert.Fail(t, "unexpected error type") } } } else { if assert.Nil(t, tc.err) { - assert.Equals(t, tc.exp, ka) + assert.Equal(t, tc.exp, ka) } } }) @@ -339,18 +343,19 @@ func TestChallenge_Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Type, ch.Type) - assert.Equals(t, updch.Status, ch.Status) - assert.Equals(t, updch.Value, ch.Value) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, ChallengeType("http-01"), updch.Type) + assert.Equal(t, "zap.internal", updch.Value) + assert.Equal(t, StatusPending, updch.Status) err := NewError(ErrorConnectionType, "error doing http GET for url http://zap.internal/.well-known/acme-challenge/%s: force", ch.Token) - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return errors.New("force") }, }, @@ -375,18 +380,19 @@ func TestChallenge_Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Type, ch.Type) - assert.Equals(t, updch.Status, ch.Status) - assert.Equals(t, updch.Value, ch.Value) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, ChallengeType("http-01"), updch.Type) + assert.Equal(t, "zap.internal", updch.Value) + assert.Equal(t, StatusPending, updch.Status) err := NewError(ErrorConnectionType, "error doing http GET for url http://zap.internal/.well-known/acme-challenge/%s: force", ch.Token) - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return nil }, }, @@ -416,18 +422,19 @@ func TestChallenge_Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Type, ch.Type) - assert.Equals(t, updch.Status, ch.Status) - assert.Equals(t, updch.Value, ch.Value) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, ChallengeType("http-01"), updch.Type) + assert.Equal(t, "zap.internal", updch.Value) + assert.Equal(t, StatusPending, updch.Status) err := NewError(ErrorConnectionType, "error doing http GET for url http://zap.internal:8080/.well-known/acme-challenge/%s: force", ch.Token) - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return nil }, }, @@ -451,19 +458,20 @@ func TestChallenge_Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Type, ch.Type) - assert.Equals(t, updch.Status, ch.Status) - assert.Equals(t, updch.Value, ch.Value) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, ChallengeType("dns-01"), updch.Type) + assert.Equal(t, "zap.internal", updch.Value) + assert.Equal(t, StatusPending, updch.Status) err := NewError(ErrorDNSType, "error looking up TXT records for domain %s: force", ch.Value) - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return errors.New("force") }, }, @@ -488,19 +496,20 @@ func TestChallenge_Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Type, ch.Type) - assert.Equals(t, updch.Status, ch.Status) - assert.Equals(t, updch.Value, ch.Value) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, ChallengeType("dns-01"), updch.Type) + assert.Equal(t, "zap.internal", updch.Value) + assert.Equal(t, StatusPending, updch.Status) err := NewError(ErrorDNSType, "error looking up TXT records for domain %s: force", ch.Value) - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return nil }, }, @@ -523,19 +532,20 @@ func TestChallenge_Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, ch.Status) - assert.Equals(t, updch.Type, ch.Type) - assert.Equals(t, updch.Value, ch.Value) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, ChallengeType("tls-alpn-01"), updch.Type) + assert.Equal(t, "zap.internal", updch.Value) + assert.Equal(t, StatusPending, updch.Status) err := NewError(ErrorConnectionType, "error doing TLS dial for %v:443: force", ch.Value) - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return errors.New("force") }, }, @@ -552,14 +562,14 @@ func TestChallenge_Validate(t *testing.T) { } jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuth, err := KeyAuthorization(ch.Token, jwk) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuthHash := sha256.Sum256([]byte(expKeyAuth)) cert, err := newTLSALPNValidationCert(expKeyAuthHash[:], false, true, ch.Value) - assert.FatalError(t, err) + require.NoError(t, err) srv, tlsDial := newTestTLSALPNServer(cert) srv.Start() @@ -571,12 +581,12 @@ func TestChallenge_Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, ch.Status) - assert.Equals(t, updch.Type, ch.Type) - assert.Equals(t, updch.Value, ch.Value) - assert.Equals(t, updch.Error, nil) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, ChallengeType("tls-alpn-01"), updch.Type) + assert.Equal(t, "zap.internal", updch.Value) + assert.Equal(t, StatusValid, updch.Status) + assert.Nil(t, updch.Error) return nil }, }, @@ -598,14 +608,14 @@ func TestChallenge_Validate(t *testing.T) { } jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuth, err := KeyAuthorization(ch.Token, jwk) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuthHash := sha256.Sum256([]byte(expKeyAuth)) cert, err := newTLSALPNValidationCert(expKeyAuthHash[:], false, true, ch.Value) - assert.FatalError(t, err) + require.NoError(t, err) l, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { @@ -637,12 +647,12 @@ func TestChallenge_Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, ch.Status) - assert.Equals(t, updch.Type, ch.Type) - assert.Equals(t, updch.Value, ch.Value) - assert.Equals(t, updch.Error, nil) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, ChallengeType("tls-alpn-01"), updch.Type) + assert.Equal(t, "zap.internal", updch.Value) + assert.Equal(t, StatusValid, updch.Status) + assert.Nil(t, updch.Error) return nil }, }, @@ -663,25 +673,24 @@ func TestChallenge_Validate(t *testing.T) { }{ Error: "an error", }) - sassert.NoError(t, err) + assert.NoError(t, err) return test{ ch: ch, payload: payload, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - sassert.Equal(t, "chID", updch.ID) - sassert.Equal(t, "token", updch.Token) - sassert.Equal(t, StatusInvalid, updch.Status) - sassert.Equal(t, ChallengeType("device-attest-01"), updch.Type) - sassert.Equal(t, "12345678", updch.Value) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusInvalid, updch.Status) + assert.Equal(t, ChallengeType("device-attest-01"), updch.Type) + assert.Equal(t, "12345678", updch.Value) err := NewError(ErrorRejectedIdentifierType, "payload contained error: an error") - sassert.EqualError(t, updch.Error.Err, err.Err.Error()) - sassert.Equal(t, err.Type, updch.Error.Type) - sassert.Equal(t, err.Detail, updch.Error.Detail) - sassert.Equal(t, err.Status, updch.Error.Status) - sassert.Equal(t, err.Detail, updch.Error.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) return errors.New("force") }, @@ -692,7 +701,7 @@ func TestChallenge_Validate(t *testing.T) { "ok/device-attest-01": func(t *testing.T) test { ctx := context.Background() ca, err := minica.New() - sassert.NoError(t, err) + assert.NoError(t, err) caRoot := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: ca.Root.Raw}) ctx = NewProvisionerContext(ctx, mustAttestationProvisioner(t, caRoot)) makeLeaf := func(signer crypto.Signer, serialNumber []byte) *x509.Certificate { @@ -710,21 +719,21 @@ func TestChallenge_Validate(t *testing.T) { } signer, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - sassert.NoError(t, err) + assert.NoError(t, err) serialNumber, err := asn1.Marshal(1234) - sassert.NoError(t, err) + assert.NoError(t, err) leaf := makeLeaf(signer, serialNumber) jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - sassert.NoError(t, err) + assert.NoError(t, err) token := "token" keyAuth, err := KeyAuthorization(token, jwk) - sassert.NoError(t, err) + assert.NoError(t, err) keyAuthSum := sha256.Sum256([]byte(keyAuth)) sig, err := signer.Sign(rand.Reader, keyAuthSum[:], crypto.SHA256) - sassert.NoError(t, err) + assert.NoError(t, err) cborSig, err := cbor.Marshal(sig) - sassert.NoError(t, err) + assert.NoError(t, err) ch := &Challenge{ ID: "chID", @@ -744,13 +753,13 @@ func TestChallenge_Validate(t *testing.T) { "sig": cborSig, }, }) - sassert.NoError(t, err) + assert.NoError(t, err) payload, err := json.Marshal(struct { AttObj string `json:"attObj"` }{ AttObj: base64.RawURLEncoding.EncodeToString(attObj), }) - sassert.NoError(t, err) + assert.NoError(t, err) return test{ ch: ch, payload: payload, @@ -758,11 +767,11 @@ func TestChallenge_Validate(t *testing.T) { jwk: jwk, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - sassert.Equal(t, "chID", updch.ID) - sassert.Equal(t, token, updch.Token) - sassert.Equal(t, StatusValid, updch.Status) - sassert.Equal(t, ChallengeType("device-attest-01"), updch.Type) - sassert.Equal(t, "1234", updch.Value) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, token, updch.Token) + assert.Equal(t, StatusValid, updch.Status) + assert.Equal(t, ChallengeType("device-attest-01"), updch.Type) + assert.Equal(t, "1234", updch.Value) return nil }, @@ -784,16 +793,15 @@ func TestChallenge_Validate(t *testing.T) { } ctx = NewClientContext(ctx, tc.vc) if err := tc.ch.Validate(ctx, tc.db, tc.jwk, tc.payload); err != nil { - if assert.NotNil(t, tc.err) { + if assert.Error(t, tc.err) { var k *Error if errors.As(err, &k) { - assert.Equals(t, k.Type, tc.err.Type) - assert.Equals(t, k.Detail, tc.err.Detail) - assert.Equals(t, k.Status, tc.err.Status) - assert.Equals(t, k.Err.Error(), tc.err.Err.Error()) - assert.Equals(t, k.Detail, tc.err.Detail) + assert.Equal(t, tc.err.Type, k.Type) + assert.Equal(t, tc.err.Detail, k.Detail) + assert.Equal(t, tc.err.Status, k.Status) + assert.Equal(t, tc.err.Err.Error(), k.Err.Error()) } else { - assert.FatalError(t, errors.New("unexpected error type")) + assert.Fail(t, "unexpected error type") } } } else { @@ -838,17 +846,18 @@ func TestHTTP01Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Value, ch.Value) - assert.Equals(t, updch.Status, StatusPending) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, "zap.internal", updch.Value) + assert.Equal(t, StatusPending, updch.Status) err := NewError(ErrorConnectionType, "error doing http GET for url http://zap.internal/.well-known/acme-challenge/%s: force", ch.Token) - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return errors.New("force") }, }, @@ -872,17 +881,18 @@ func TestHTTP01Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Value, ch.Value) - assert.Equals(t, updch.Status, StatusPending) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, "zap.internal", updch.Value) + assert.Equal(t, StatusPending, updch.Status) err := NewError(ErrorConnectionType, "error doing http GET for url http://zap.internal/.well-known/acme-challenge/%s: force", ch.Token) - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return nil }, }, @@ -908,17 +918,18 @@ func TestHTTP01Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Value, ch.Value) - assert.Equals(t, updch.Status, StatusPending) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, "zap.internal", updch.Value) + assert.Equal(t, StatusPending, updch.Status) err := NewError(ErrorConnectionType, "error doing http GET for url http://zap.internal/.well-known/acme-challenge/%s with status code 400", ch.Token) - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return errors.New("force") }, }, @@ -945,17 +956,18 @@ func TestHTTP01Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Value, ch.Value) - assert.Equals(t, updch.Status, StatusPending) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, "zap.internal", updch.Value) + assert.Equal(t, StatusPending, updch.Status) err := NewError(ErrorConnectionType, "error doing http GET for url http://zap.internal/.well-known/acme-challenge/%s with status code 400", ch.Token) - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return nil }, }, @@ -990,7 +1002,7 @@ func TestHTTP01Validate(t *testing.T) { } jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - assert.FatalError(t, err) + require.NoError(t, err) jwk.Key = "foo" return test{ ch: ch, @@ -1014,10 +1026,10 @@ func TestHTTP01Validate(t *testing.T) { } jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuth, err := KeyAuthorization(ch.Token, jwk) - assert.FatalError(t, err) + require.NoError(t, err) return test{ ch: ch, vc: &mockClient{ @@ -1030,18 +1042,19 @@ func TestHTTP01Validate(t *testing.T) { jwk: jwk, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Value, ch.Value) - assert.Equals(t, updch.Status, StatusInvalid) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, "zap.internal", updch.Value) + assert.Equal(t, StatusInvalid, updch.Status) err := NewError(ErrorRejectedIdentifierType, "keyAuthorization does not match; expected %s, but got foo", expKeyAuth) - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return nil }, }, @@ -1056,10 +1069,10 @@ func TestHTTP01Validate(t *testing.T) { } jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuth, err := KeyAuthorization(ch.Token, jwk) - assert.FatalError(t, err) + require.NoError(t, err) return test{ ch: ch, vc: &mockClient{ @@ -1072,18 +1085,19 @@ func TestHTTP01Validate(t *testing.T) { jwk: jwk, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Value, ch.Value) - assert.Equals(t, updch.Status, StatusInvalid) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, "zap.internal", updch.Value) + assert.Equal(t, StatusInvalid, updch.Status) err := NewError(ErrorRejectedIdentifierType, "keyAuthorization does not match; expected %s, but got foo", expKeyAuth) - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return errors.New("force") }, }, @@ -1099,10 +1113,10 @@ func TestHTTP01Validate(t *testing.T) { } jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuth, err := KeyAuthorization(ch.Token, jwk) - assert.FatalError(t, err) + require.NoError(t, err) return test{ ch: ch, vc: &mockClient{ @@ -1115,13 +1129,14 @@ func TestHTTP01Validate(t *testing.T) { jwk: jwk, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Value, ch.Value) - assert.Equals(t, updch.Status, StatusValid) - assert.Equals(t, updch.Error, nil) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, "zap.internal", updch.Value) + assert.Equal(t, StatusValid, updch.Status) + assert.Nil(t, updch.Error) + va, err := time.Parse(time.RFC3339, updch.ValidatedAt) - assert.FatalError(t, err) + require.NoError(t, err) now := clock.Now() assert.True(t, va.Add(-time.Minute).Before(now)) assert.True(t, va.Add(time.Minute).After(now)) @@ -1141,10 +1156,10 @@ func TestHTTP01Validate(t *testing.T) { } jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuth, err := KeyAuthorization(ch.Token, jwk) - assert.FatalError(t, err) + require.NoError(t, err) return test{ ch: ch, vc: &mockClient{ @@ -1157,14 +1172,14 @@ func TestHTTP01Validate(t *testing.T) { jwk: jwk, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Value, ch.Value) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, "zap.internal", updch.Value) + assert.Equal(t, StatusValid, updch.Status) + assert.Nil(t, updch.Error) - assert.Equals(t, updch.Status, StatusValid) - assert.Equals(t, updch.Error, nil) va, err := time.Parse(time.RFC3339, updch.ValidatedAt) - assert.FatalError(t, err) + require.NoError(t, err) now := clock.Now() assert.True(t, va.Add(-time.Minute).Before(now)) assert.True(t, va.Add(time.Minute).After(now)) @@ -1179,16 +1194,15 @@ func TestHTTP01Validate(t *testing.T) { tc := run(t) ctx := NewClientContext(context.Background(), tc.vc) if err := http01Validate(ctx, tc.ch, tc.db, tc.jwk); err != nil { - if assert.NotNil(t, tc.err) { + if assert.Error(t, tc.err) { var k *Error if errors.As(err, &k) { - assert.Equals(t, k.Type, tc.err.Type) - assert.Equals(t, k.Detail, tc.err.Detail) - assert.Equals(t, k.Status, tc.err.Status) - assert.Equals(t, k.Err.Error(), tc.err.Err.Error()) - assert.Equals(t, k.Detail, tc.err.Detail) + assert.Equal(t, tc.err.Type, k.Type) + assert.Equal(t, tc.err.Detail, k.Detail) + assert.Equal(t, tc.err.Status, k.Status) + assert.Equal(t, tc.err.Err.Error(), k.Err.Error()) } else { - assert.FatalError(t, errors.New("unexpected error type")) + assert.Fail(t, "unexpected error type") } } } else { @@ -1226,18 +1240,19 @@ func TestDNS01Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Value, ch.Value) - assert.Equals(t, updch.Status, StatusPending) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, fulldomain, updch.Value) + assert.Equal(t, StatusPending, updch.Status) err := NewError(ErrorDNSType, "error looking up TXT records for domain %s: force", domain) - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return errors.New("force") }, }, @@ -1261,18 +1276,19 @@ func TestDNS01Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Value, ch.Value) - assert.Equals(t, updch.Status, StatusPending) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, fulldomain, updch.Value) + assert.Equal(t, StatusPending, updch.Status) err := NewError(ErrorDNSType, "error looking up TXT records for domain %s: force", domain) - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return nil }, }, @@ -1287,7 +1303,7 @@ func TestDNS01Validate(t *testing.T) { } jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - assert.FatalError(t, err) + require.NoError(t, err) jwk.Key = "foo" return test{ @@ -1310,10 +1326,10 @@ func TestDNS01Validate(t *testing.T) { } jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuth, err := KeyAuthorization(ch.Token, jwk) - assert.FatalError(t, err) + require.NoError(t, err) return test{ ch: ch, @@ -1324,18 +1340,19 @@ func TestDNS01Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Value, ch.Value) - assert.Equals(t, updch.Status, StatusPending) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, fulldomain, updch.Value) + assert.Equal(t, StatusPending, updch.Status) err := NewError(ErrorRejectedIdentifierType, "keyAuthorization does not match; expected %s, but got %s", expKeyAuth, []string{"foo", "bar"}) - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return errors.New("force") }, }, @@ -1352,10 +1369,10 @@ func TestDNS01Validate(t *testing.T) { } jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuth, err := KeyAuthorization(ch.Token, jwk) - assert.FatalError(t, err) + require.NoError(t, err) return test{ ch: ch, @@ -1366,18 +1383,19 @@ func TestDNS01Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Value, ch.Value) - assert.Equals(t, updch.Status, StatusPending) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, fulldomain, updch.Value) + assert.Equal(t, StatusPending, updch.Status) err := NewError(ErrorRejectedIdentifierType, "keyAuthorization does not match; expected %s, but got %s", expKeyAuth, []string{"foo", "bar"}) - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return nil }, }, @@ -1393,10 +1411,10 @@ func TestDNS01Validate(t *testing.T) { } jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuth, err := KeyAuthorization(ch.Token, jwk) - assert.FatalError(t, err) + require.NoError(t, err) h := sha256.Sum256([]byte(expKeyAuth)) expected := base64.RawURLEncoding.EncodeToString(h[:]) @@ -1409,15 +1427,14 @@ func TestDNS01Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Value, ch.Value) - assert.Equals(t, updch.Status, StatusValid) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, fulldomain, ch.Value) + assert.Equal(t, StatusValid, updch.Status) + assert.Nil(t, updch.Error) - assert.Equals(t, updch.Status, StatusValid) - assert.Equals(t, updch.Error, nil) va, err := time.Parse(time.RFC3339, updch.ValidatedAt) - assert.FatalError(t, err) + require.NoError(t, err) now := clock.Now() assert.True(t, va.Add(-time.Minute).Before(now)) assert.True(t, va.Add(time.Minute).After(now)) @@ -1438,10 +1455,10 @@ func TestDNS01Validate(t *testing.T) { } jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuth, err := KeyAuthorization(ch.Token, jwk) - assert.FatalError(t, err) + require.NoError(t, err) h := sha256.Sum256([]byte(expKeyAuth)) expected := base64.RawURLEncoding.EncodeToString(h[:]) @@ -1454,15 +1471,14 @@ func TestDNS01Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Value, ch.Value) - assert.Equals(t, updch.Status, StatusValid) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, fulldomain, updch.Value) + assert.Equal(t, StatusValid, updch.Status) + assert.Nil(t, updch.Error) - assert.Equals(t, updch.Status, StatusValid) - assert.Equals(t, updch.Error, nil) va, err := time.Parse(time.RFC3339, updch.ValidatedAt) - assert.FatalError(t, err) + require.NoError(t, err) now := clock.Now() assert.True(t, va.Add(-time.Minute).Before(now)) assert.True(t, va.Add(time.Minute).After(now)) @@ -1479,16 +1495,15 @@ func TestDNS01Validate(t *testing.T) { tc := run(t) ctx := NewClientContext(context.Background(), tc.vc) if err := dns01Validate(ctx, tc.ch, tc.db, tc.jwk); err != nil { - if assert.NotNil(t, tc.err) { + if assert.Error(t, tc.err) { var k *Error if errors.As(err, &k) { - assert.Equals(t, k.Type, tc.err.Type) - assert.Equals(t, k.Detail, tc.err.Detail) - assert.Equals(t, k.Status, tc.err.Status) - assert.Equals(t, k.Err.Error(), tc.err.Err.Error()) - assert.Equals(t, k.Detail, tc.err.Detail) + assert.Equal(t, tc.err.Type, k.Type) + assert.Equal(t, tc.err.Detail, k.Detail) + assert.Equal(t, tc.err.Status, k.Status) + assert.Equal(t, tc.err.Err.Error(), k.Err.Error()) } else { - assert.FatalError(t, errors.New("unexpected error type")) + assert.Fail(t, "unexpected error type") } } } else { @@ -1627,19 +1642,20 @@ func TestTLSALPN01Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, ch.Status) - assert.Equals(t, updch.Type, ch.Type) - assert.Equals(t, updch.Value, ch.Value) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusPending, updch.Status) + assert.Equal(t, ChallengeType("tls-alpn-01"), updch.Type) + assert.Equal(t, "zap.internal", updch.Value) err := NewError(ErrorConnectionType, "error doing TLS dial for %v:443: force", ch.Value) - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return errors.New("force") }, }, @@ -1657,19 +1673,20 @@ func TestTLSALPN01Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, ch.Status) - assert.Equals(t, updch.Type, ch.Type) - assert.Equals(t, updch.Value, ch.Value) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusPending, updch.Status) + assert.Equal(t, ChallengeType("tls-alpn-01"), updch.Type) + assert.Equal(t, "zap.internal", updch.Value) err := NewError(ErrorConnectionType, "error doing TLS dial for %v:443: force", ch.Value) - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return nil }, }, @@ -1688,19 +1705,20 @@ func TestTLSALPN01Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, ch.Status) - assert.Equals(t, updch.Type, ch.Type) - assert.Equals(t, updch.Value, ch.Value) - - err := NewError(ErrorConnectionType, "error doing TLS dial for %v:443:", ch.Value) - - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusPending, updch.Status) + assert.Equal(t, ChallengeType("tls-alpn-01"), updch.Type) + assert.Equal(t, "zap.internal", updch.Value) + + err := NewError(ErrorConnectionType, "error doing TLS dial for %v:443: context deadline exceeded", ch.Value) + + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return nil }, }, @@ -1719,19 +1737,20 @@ func TestTLSALPN01Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, StatusInvalid) - assert.Equals(t, updch.Type, ch.Type) - assert.Equals(t, updch.Value, ch.Value) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusInvalid, updch.Status) + assert.Equal(t, ChallengeType("tls-alpn-01"), updch.Type) + assert.Equal(t, "zap.internal", updch.Value) err := NewError(ErrorRejectedIdentifierType, "tls-alpn-01 challenge for %v resulted in no certificates", ch.Value) - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return nil }, }, @@ -1749,19 +1768,20 @@ func TestTLSALPN01Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, StatusInvalid) - assert.Equals(t, updch.Type, ch.Type) - assert.Equals(t, updch.Value, ch.Value) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusInvalid, updch.Status) + assert.Equal(t, ChallengeType("tls-alpn-01"), updch.Type) + assert.Equal(t, "zap.internal", updch.Value) err := NewError(ErrorRejectedIdentifierType, "tls-alpn-01 challenge for %v resulted in no certificates", ch.Value) - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return errors.New("force") }, }, @@ -1772,7 +1792,7 @@ func TestTLSALPN01Validate(t *testing.T) { ch := makeTLSCh() jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - assert.FatalError(t, err) + require.NoError(t, err) srv := httptest.NewTLSServer(nil) @@ -1785,19 +1805,20 @@ func TestTLSALPN01Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, StatusInvalid) - assert.Equals(t, updch.Type, ch.Type) - assert.Equals(t, updch.Value, ch.Value) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusInvalid, updch.Status) + assert.Equal(t, ChallengeType("tls-alpn-01"), updch.Type) + assert.Equal(t, "zap.internal", updch.Value) err := NewError(ErrorRejectedIdentifierType, "cannot negotiate ALPN acme-tls/1 protocol for tls-alpn-01 challenge") - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return nil }, }, @@ -1809,7 +1830,7 @@ func TestTLSALPN01Validate(t *testing.T) { ch := makeTLSCh() jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - assert.FatalError(t, err) + require.NoError(t, err) srv := httptest.NewTLSServer(nil) @@ -1822,19 +1843,20 @@ func TestTLSALPN01Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, StatusInvalid) - assert.Equals(t, updch.Type, ch.Type) - assert.Equals(t, updch.Value, ch.Value) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusInvalid, updch.Status) + assert.Equal(t, ChallengeType("tls-alpn-01"), updch.Type) + assert.Equal(t, "zap.internal", updch.Value) err := NewError(ErrorRejectedIdentifierType, "cannot negotiate ALPN acme-tls/1 protocol for tls-alpn-01 challenge") - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return errors.New("force") }, }, @@ -1847,14 +1869,14 @@ func TestTLSALPN01Validate(t *testing.T) { ch := makeTLSCh() jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuth, err := KeyAuthorization(ch.Token, jwk) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuthHash := sha256.Sum256([]byte(expKeyAuth)) cert, err := newTLSALPNValidationCert(expKeyAuthHash[:], false, true) - assert.FatalError(t, err) + require.NoError(t, err) srv, tlsDial := newTestTLSALPNServer(cert) srv.Start() @@ -1866,19 +1888,20 @@ func TestTLSALPN01Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, StatusInvalid) - assert.Equals(t, updch.Type, ch.Type) - assert.Equals(t, updch.Value, ch.Value) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusInvalid, updch.Status) + assert.Equal(t, ChallengeType("tls-alpn-01"), updch.Type) + assert.Equal(t, "zap.internal", updch.Value) err := NewError(ErrorRejectedIdentifierType, "incorrect certificate for tls-alpn-01 challenge: leaf certificate must contain a single IP address or DNS name, %v", ch.Value) - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return nil }, }, @@ -1890,14 +1913,14 @@ func TestTLSALPN01Validate(t *testing.T) { ch := makeTLSCh() jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuth, err := KeyAuthorization(ch.Token, jwk) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuthHash := sha256.Sum256([]byte(expKeyAuth)) cert, err := newTLSALPNValidationCert(expKeyAuthHash[:], false, true) - assert.FatalError(t, err) + require.NoError(t, err) srv, tlsDial := newTestTLSALPNServer(cert) srv.Start() @@ -1909,19 +1932,20 @@ func TestTLSALPN01Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, StatusInvalid) - assert.Equals(t, updch.Type, ch.Type) - assert.Equals(t, updch.Value, ch.Value) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusInvalid, updch.Status) + assert.Equal(t, ChallengeType("tls-alpn-01"), updch.Type) + assert.Equal(t, "zap.internal", updch.Value) err := NewError(ErrorRejectedIdentifierType, "incorrect certificate for tls-alpn-01 challenge: leaf certificate must contain a single IP address or DNS name, %v", ch.Value) - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return errors.New("force") }, }, @@ -1934,14 +1958,14 @@ func TestTLSALPN01Validate(t *testing.T) { ch := makeTLSCh() jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuth, err := KeyAuthorization(ch.Token, jwk) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuthHash := sha256.Sum256([]byte(expKeyAuth)) cert, err := newTLSALPNValidationCert(expKeyAuthHash[:], false, true, ch.Value, "other.internal") - assert.FatalError(t, err) + require.NoError(t, err) srv, tlsDial := newTestTLSALPNServer(cert) srv.Start() @@ -1953,19 +1977,20 @@ func TestTLSALPN01Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, StatusInvalid) - assert.Equals(t, updch.Type, ch.Type) - assert.Equals(t, updch.Value, ch.Value) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusInvalid, updch.Status) + assert.Equal(t, ChallengeType("tls-alpn-01"), updch.Type) + assert.Equal(t, "zap.internal", updch.Value) err := NewError(ErrorRejectedIdentifierType, "incorrect certificate for tls-alpn-01 challenge: leaf certificate must contain a single IP address or DNS name, %v", ch.Value) - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return nil }, }, @@ -1977,14 +2002,14 @@ func TestTLSALPN01Validate(t *testing.T) { ch := makeTLSCh() jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuth, err := KeyAuthorization(ch.Token, jwk) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuthHash := sha256.Sum256([]byte(expKeyAuth)) cert, err := newTLSALPNValidationCert(expKeyAuthHash[:], false, true, "other.internal") - assert.FatalError(t, err) + require.NoError(t, err) srv, tlsDial := newTestTLSALPNServer(cert) srv.Start() @@ -1996,19 +2021,20 @@ func TestTLSALPN01Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, StatusInvalid) - assert.Equals(t, updch.Type, ch.Type) - assert.Equals(t, updch.Value, ch.Value) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusInvalid, updch.Status) + assert.Equal(t, ChallengeType("tls-alpn-01"), updch.Type) + assert.Equal(t, "zap.internal", updch.Value) err := NewError(ErrorRejectedIdentifierType, "incorrect certificate for tls-alpn-01 challenge: leaf certificate must contain a single IP address or DNS name, %v", ch.Value) - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return nil }, }, @@ -2020,15 +2046,15 @@ func TestTLSALPN01Validate(t *testing.T) { ch := makeTLSCh() jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuth, err := KeyAuthorization(ch.Token, jwk) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuthHash := sha256.Sum256([]byte(expKeyAuth)) jwk.Key = "foo" cert, err := newTLSALPNValidationCert(expKeyAuthHash[:], false, true, ch.Value) - assert.FatalError(t, err) + require.NoError(t, err) srv, tlsDial := newTestTLSALPNServer(cert) srv.Start() @@ -2047,10 +2073,10 @@ func TestTLSALPN01Validate(t *testing.T) { ch := makeTLSCh() jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - assert.FatalError(t, err) + require.NoError(t, err) cert, err := newTLSALPNValidationCert(nil, false, true, ch.Value) - assert.FatalError(t, err) + require.NoError(t, err) srv, tlsDial := newTestTLSALPNServer(cert) srv.Start() @@ -2062,19 +2088,20 @@ func TestTLSALPN01Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, StatusInvalid) - assert.Equals(t, updch.Type, ch.Type) - assert.Equals(t, updch.Value, ch.Value) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusInvalid, updch.Status) + assert.Equal(t, ChallengeType("tls-alpn-01"), updch.Type) + assert.Equal(t, "zap.internal", updch.Value) err := NewError(ErrorRejectedIdentifierType, "incorrect certificate for tls-alpn-01 challenge: missing acmeValidationV1 extension") - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return nil }, }, @@ -2086,10 +2113,10 @@ func TestTLSALPN01Validate(t *testing.T) { ch := makeTLSCh() jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - assert.FatalError(t, err) + require.NoError(t, err) cert, err := newTLSALPNValidationCert(nil, false, true, ch.Value) - assert.FatalError(t, err) + require.NoError(t, err) srv, tlsDial := newTestTLSALPNServer(cert) srv.Start() @@ -2101,19 +2128,20 @@ func TestTLSALPN01Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, StatusInvalid) - assert.Equals(t, updch.Type, ch.Type) - assert.Equals(t, updch.Value, ch.Value) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusInvalid, updch.Status) + assert.Equal(t, ChallengeType("tls-alpn-01"), updch.Type) + assert.Equal(t, "zap.internal", updch.Value) err := NewError(ErrorRejectedIdentifierType, "incorrect certificate for tls-alpn-01 challenge: missing acmeValidationV1 extension") - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return errors.New("force") }, }, @@ -2126,14 +2154,14 @@ func TestTLSALPN01Validate(t *testing.T) { ch := makeTLSCh() jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuth, err := KeyAuthorization(ch.Token, jwk) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuthHash := sha256.Sum256([]byte(expKeyAuth)) cert, err := newTLSALPNValidationCert(expKeyAuthHash[:], false, false, ch.Value) - assert.FatalError(t, err) + require.NoError(t, err) srv, tlsDial := newTestTLSALPNServer(cert) srv.Start() @@ -2145,19 +2173,20 @@ func TestTLSALPN01Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, StatusInvalid) - assert.Equals(t, updch.Type, ch.Type) - assert.Equals(t, updch.Value, ch.Value) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusInvalid, updch.Status) + assert.Equal(t, ChallengeType("tls-alpn-01"), updch.Type) + assert.Equal(t, "zap.internal", updch.Value) err := NewError(ErrorRejectedIdentifierType, "incorrect certificate for tls-alpn-01 challenge: acmeValidationV1 extension not critical") - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return nil }, }, @@ -2169,14 +2198,14 @@ func TestTLSALPN01Validate(t *testing.T) { ch := makeTLSCh() jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuth, err := KeyAuthorization(ch.Token, jwk) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuthHash := sha256.Sum256([]byte(expKeyAuth)) cert, err := newTLSALPNValidationCert(expKeyAuthHash[:], false, false, ch.Value) - assert.FatalError(t, err) + require.NoError(t, err) srv, tlsDial := newTestTLSALPNServer(cert) srv.Start() @@ -2188,19 +2217,20 @@ func TestTLSALPN01Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, StatusInvalid) - assert.Equals(t, updch.Type, ch.Type) - assert.Equals(t, updch.Value, ch.Value) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusInvalid, updch.Status) + assert.Equal(t, ChallengeType("tls-alpn-01"), updch.Type) + assert.Equal(t, "zap.internal", updch.Value) err := NewError(ErrorRejectedIdentifierType, "incorrect certificate for tls-alpn-01 challenge: acmeValidationV1 extension not critical") - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return errors.New("force") }, }, @@ -2213,10 +2243,10 @@ func TestTLSALPN01Validate(t *testing.T) { ch := makeTLSCh() jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - assert.FatalError(t, err) + require.NoError(t, err) cert, err := newTLSALPNValidationCert([]byte{1, 2, 3}, false, true, ch.Value) - assert.FatalError(t, err) + require.NoError(t, err) srv, tlsDial := newTestTLSALPNServer(cert) srv.Start() @@ -2228,19 +2258,20 @@ func TestTLSALPN01Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, StatusInvalid) - assert.Equals(t, updch.Type, ch.Type) - assert.Equals(t, updch.Value, ch.Value) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusInvalid, updch.Status) + assert.Equal(t, ChallengeType("tls-alpn-01"), updch.Type) + assert.Equal(t, "zap.internal", updch.Value) err := NewError(ErrorRejectedIdentifierType, "incorrect certificate for tls-alpn-01 challenge: malformed acmeValidationV1 extension value") - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return nil }, }, @@ -2252,10 +2283,10 @@ func TestTLSALPN01Validate(t *testing.T) { ch := makeTLSCh() jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - assert.FatalError(t, err) + require.NoError(t, err) cert, err := newTLSALPNValidationCert([]byte{1, 2, 3}, false, true, ch.Value) - assert.FatalError(t, err) + require.NoError(t, err) srv, tlsDial := newTestTLSALPNServer(cert) srv.Start() @@ -2267,19 +2298,20 @@ func TestTLSALPN01Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, StatusInvalid) - assert.Equals(t, updch.Type, ch.Type) - assert.Equals(t, updch.Value, ch.Value) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusInvalid, updch.Status) + assert.Equal(t, ChallengeType("tls-alpn-01"), updch.Type) + assert.Equal(t, "zap.internal", updch.Value) err := NewError(ErrorRejectedIdentifierType, "incorrect certificate for tls-alpn-01 challenge: malformed acmeValidationV1 extension value") - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return errors.New("force") }, }, @@ -2292,15 +2324,15 @@ func TestTLSALPN01Validate(t *testing.T) { ch := makeTLSCh() jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuth, err := KeyAuthorization(ch.Token, jwk) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuthHash := sha256.Sum256([]byte(expKeyAuth)) incorrectTokenHash := sha256.Sum256([]byte("mismatched")) cert, err := newTLSALPNValidationCert(incorrectTokenHash[:], false, true, ch.Value) - assert.FatalError(t, err) + require.NoError(t, err) srv, tlsDial := newTestTLSALPNServer(cert) srv.Start() @@ -2312,21 +2344,22 @@ func TestTLSALPN01Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, StatusInvalid) - assert.Equals(t, updch.Type, ch.Type) - assert.Equals(t, updch.Value, ch.Value) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusInvalid, updch.Status) + assert.Equal(t, ChallengeType("tls-alpn-01"), updch.Type) + assert.Equal(t, "zap.internal", updch.Value) err := NewError(ErrorRejectedIdentifierType, "incorrect certificate for tls-alpn-01 challenge: "+ "expected acmeValidationV1 extension value %s for this challenge but got %s", hex.EncodeToString(expKeyAuthHash[:]), hex.EncodeToString(incorrectTokenHash[:])) - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return nil }, }, @@ -2338,15 +2371,15 @@ func TestTLSALPN01Validate(t *testing.T) { ch := makeTLSCh() jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuth, err := KeyAuthorization(ch.Token, jwk) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuthHash := sha256.Sum256([]byte(expKeyAuth)) incorrectTokenHash := sha256.Sum256([]byte("mismatched")) cert, err := newTLSALPNValidationCert(incorrectTokenHash[:], false, true, ch.Value) - assert.FatalError(t, err) + require.NoError(t, err) srv, tlsDial := newTestTLSALPNServer(cert) srv.Start() @@ -2358,21 +2391,22 @@ func TestTLSALPN01Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, StatusInvalid) - assert.Equals(t, updch.Type, ch.Type) - assert.Equals(t, updch.Value, ch.Value) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusInvalid, updch.Status) + assert.Equal(t, ChallengeType("tls-alpn-01"), updch.Type) + assert.Equal(t, "zap.internal", updch.Value) err := NewError(ErrorRejectedIdentifierType, "incorrect certificate for tls-alpn-01 challenge: "+ "expected acmeValidationV1 extension value %s for this challenge but got %s", hex.EncodeToString(expKeyAuthHash[:]), hex.EncodeToString(incorrectTokenHash[:])) - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return errors.New("force") }, }, @@ -2385,14 +2419,14 @@ func TestTLSALPN01Validate(t *testing.T) { ch := makeTLSCh() jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuth, err := KeyAuthorization(ch.Token, jwk) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuthHash := sha256.Sum256([]byte(expKeyAuth)) cert, err := newTLSALPNValidationCert(expKeyAuthHash[:], true, true, ch.Value) - assert.FatalError(t, err) + require.NoError(t, err) srv, tlsDial := newTestTLSALPNServer(cert) srv.Start() @@ -2404,20 +2438,21 @@ func TestTLSALPN01Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, StatusInvalid) - assert.Equals(t, updch.Type, ch.Type) - assert.Equals(t, updch.Value, ch.Value) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusInvalid, updch.Status) + assert.Equal(t, ChallengeType("tls-alpn-01"), updch.Type) + assert.Equal(t, "zap.internal", updch.Value) err := NewError(ErrorRejectedIdentifierType, "incorrect certificate for tls-alpn-01 challenge: "+ "obsolete id-pe-acmeIdentifier in acmeValidationV1 extension") - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return nil }, }, @@ -2429,14 +2464,14 @@ func TestTLSALPN01Validate(t *testing.T) { ch := makeTLSCh() jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuth, err := KeyAuthorization(ch.Token, jwk) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuthHash := sha256.Sum256([]byte(expKeyAuth)) cert, err := newTLSALPNValidationCert(expKeyAuthHash[:], true, true, ch.Value) - assert.FatalError(t, err) + require.NoError(t, err) srv, tlsDial := newTestTLSALPNServer(cert) srv.Start() @@ -2448,20 +2483,21 @@ func TestTLSALPN01Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, StatusInvalid) - assert.Equals(t, updch.Type, ch.Type) - assert.Equals(t, updch.Value, ch.Value) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusInvalid, updch.Status) + assert.Equal(t, ChallengeType("tls-alpn-01"), updch.Type) + assert.Equal(t, "zap.internal", updch.Value) err := NewError(ErrorRejectedIdentifierType, "incorrect certificate for tls-alpn-01 challenge: "+ "obsolete id-pe-acmeIdentifier in acmeValidationV1 extension") - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return errors.New("force") }, }, @@ -2474,14 +2510,14 @@ func TestTLSALPN01Validate(t *testing.T) { ch := makeTLSCh() jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuth, err := KeyAuthorization(ch.Token, jwk) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuthHash := sha256.Sum256([]byte(expKeyAuth)) cert, err := newTLSALPNValidationCert(expKeyAuthHash[:], false, true, ch.Value) - assert.FatalError(t, err) + require.NoError(t, err) srv, tlsDial := newTestTLSALPNServer(cert) srv.Start() @@ -2493,12 +2529,13 @@ func TestTLSALPN01Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, StatusValid) - assert.Equals(t, updch.Type, ch.Type) - assert.Equals(t, updch.Value, ch.Value) - assert.Equals(t, updch.Error, nil) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusValid, updch.Status) + assert.Equal(t, ChallengeType("tls-alpn-01"), updch.Type) + assert.Equal(t, "zap.internal", updch.Value) + assert.Nil(t, updch.Error) + return nil }, }, @@ -2511,14 +2548,14 @@ func TestTLSALPN01Validate(t *testing.T) { ch.Value = "127.0.0.1" jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuth, err := KeyAuthorization(ch.Token, jwk) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuthHash := sha256.Sum256([]byte(expKeyAuth)) cert, err := newTLSALPNValidationCert(expKeyAuthHash[:], false, true, ch.Value) - assert.FatalError(t, err) + require.NoError(t, err) srv, tlsDial := newTestTLSALPNServer(cert) srv.Start() @@ -2530,12 +2567,13 @@ func TestTLSALPN01Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, StatusValid) - assert.Equals(t, updch.Type, ch.Type) - assert.Equals(t, updch.Value, ch.Value) - assert.Equals(t, updch.Error, nil) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusValid, updch.Status) + assert.Equal(t, ChallengeType("tls-alpn-01"), updch.Type) + assert.Equal(t, "127.0.0.1", updch.Value) + assert.Nil(t, updch.Error) + return nil }, }, @@ -2554,16 +2592,16 @@ func TestTLSALPN01Validate(t *testing.T) { ctx := NewClientContext(context.Background(), tc.vc) if err := tlsalpn01Validate(ctx, tc.ch, tc.db, tc.jwk); err != nil { - if assert.NotNil(t, tc.err) { + if assert.Error(t, tc.err) { var k *Error if errors.As(err, &k) { - assert.Equals(t, k.Type, tc.err.Type) - assert.Equals(t, k.Detail, tc.err.Detail) - assert.Equals(t, k.Status, tc.err.Status) - assert.Equals(t, k.Err.Error(), tc.err.Err.Error()) - assert.Equals(t, k.Detail, tc.err.Detail) + assert.Equal(t, tc.err.Type, k.Type) + assert.Equal(t, tc.err.Detail, k.Detail) + assert.Equal(t, tc.err.Status, k.Status) + assert.Equal(t, tc.err.Err.Error(), k.Err.Error()) + assert.Equal(t, tc.err.Subproblems, k.Subproblems) } else { - assert.FatalError(t, errors.New("unexpected error type")) + assert.Fail(t, "unexpected error type") } } } else { @@ -3133,19 +3171,19 @@ func Test_deviceAttest01Validate(t *testing.T) { }{ Error: "an error", }) - sassert.NoError(t, err) + require.NoError(t, err) errorBase64Payload, err := json.Marshal(struct { AttObj string `json:"attObj"` }{ AttObj: "?!", }) - sassert.NoError(t, err) + require.NoError(t, err) errorCBORPayload, err := json.Marshal(struct { AttObj string `json:"attObj"` }{ AttObj: "AAAA", }) - sassert.NoError(t, err) + require.NoError(t, err) type args struct { ctx context.Context ch *Challenge @@ -3187,19 +3225,19 @@ func Test_deviceAttest01Validate(t *testing.T) { payload: errorPayload, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - sassert.Equal(t, "chID", updch.ID) - sassert.Equal(t, "token", updch.Token) - sassert.Equal(t, StatusInvalid, updch.Status) - sassert.Equal(t, ChallengeType("device-attest-01"), updch.Type) - sassert.Equal(t, "12345678", updch.Value) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusInvalid, updch.Status) + assert.Equal(t, ChallengeType("device-attest-01"), updch.Type) + assert.Equal(t, "12345678", updch.Value) err := NewError(ErrorRejectedIdentifierType, "payload contained error: an error") - sassert.EqualError(t, updch.Error.Err, err.Err.Error()) - sassert.Equal(t, err.Type, updch.Error.Type) - sassert.Equal(t, err.Detail, updch.Error.Detail) - sassert.Equal(t, err.Status, updch.Error.Status) - sassert.Equal(t, err.Detail, updch.Error.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) return errors.New("force") }, @@ -3221,19 +3259,19 @@ func Test_deviceAttest01Validate(t *testing.T) { payload: errorPayload, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - sassert.Equal(t, "chID", updch.ID) - sassert.Equal(t, "token", updch.Token) - sassert.Equal(t, StatusInvalid, updch.Status) - sassert.Equal(t, ChallengeType("device-attest-01"), updch.Type) - sassert.Equal(t, "12345678", updch.Value) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusInvalid, updch.Status) + assert.Equal(t, ChallengeType("device-attest-01"), updch.Type) + assert.Equal(t, "12345678", updch.Value) err := NewError(ErrorRejectedIdentifierType, "payload contained error: an error") - sassert.EqualError(t, updch.Error.Err, err.Err.Error()) - sassert.Equal(t, err.Type, updch.Error.Type) - sassert.Equal(t, err.Detail, updch.Error.Detail) - sassert.Equal(t, err.Status, updch.Error.Status) - sassert.Equal(t, err.Detail, updch.Error.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) return nil }, @@ -3274,7 +3312,7 @@ func Test_deviceAttest01Validate(t *testing.T) { }, "ok/prov.IsAttestationFormatEnabled": func(t *testing.T) test { ca, err := minica.New() - sassert.NoError(t, err) + require.NoError(t, err) makeLeaf := func(signer crypto.Signer, serialNumber []byte) *x509.Certificate { leaf, err := ca.Sign(&x509.Certificate{ Subject: pkix.Name{CommonName: "attestation cert"}, @@ -3289,20 +3327,20 @@ func Test_deviceAttest01Validate(t *testing.T) { return leaf } signer, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - sassert.NoError(t, err) + require.NoError(t, err) serialNumber, err := asn1.Marshal(1234) - sassert.NoError(t, err) + require.NoError(t, err) leaf := makeLeaf(signer, serialNumber) jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - sassert.NoError(t, err) + require.NoError(t, err) token := "token" keyAuth, err := KeyAuthorization(token, jwk) - sassert.NoError(t, err) + require.NoError(t, err) keyAuthSum := sha256.Sum256([]byte(keyAuth)) sig, err := signer.Sign(rand.Reader, keyAuthSum[:], crypto.SHA256) - sassert.NoError(t, err) + require.NoError(t, err) cborSig, err := cbor.Marshal(sig) - sassert.NoError(t, err) + require.NoError(t, err) ctx := NewProvisionerContext(context.Background(), mustNonAttestationProvisioner(t)) attObj, err := cbor.Marshal(struct { Format string `json:"fmt"` @@ -3315,13 +3353,13 @@ func Test_deviceAttest01Validate(t *testing.T) { "sig": cborSig, }, }) - sassert.NoError(t, err) + require.NoError(t, err) payload, err := json.Marshal(struct { AttObj string `json:"attObj"` }{ AttObj: base64.RawURLEncoding.EncodeToString(attObj), }) - sassert.NoError(t, err) + require.NoError(t, err) return test{ args: args{ ctx: ctx, @@ -3335,19 +3373,19 @@ func Test_deviceAttest01Validate(t *testing.T) { payload: payload, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - sassert.Equal(t, "chID", updch.ID) - sassert.Equal(t, "token", updch.Token) - sassert.Equal(t, StatusInvalid, updch.Status) - sassert.Equal(t, ChallengeType("device-attest-01"), updch.Type) - sassert.Equal(t, "12345678", updch.Value) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusInvalid, updch.Status) + assert.Equal(t, ChallengeType("device-attest-01"), updch.Type) + assert.Equal(t, "12345678", updch.Value) err := NewError(ErrorBadAttestationStatementType, "attestation format %q is not enabled", "step") - sassert.EqualError(t, updch.Error.Err, err.Err.Error()) - sassert.Equal(t, err.Type, updch.Error.Type) - sassert.Equal(t, err.Detail, updch.Error.Detail) - sassert.Equal(t, err.Status, updch.Error.Status) - sassert.Equal(t, err.Detail, updch.Error.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) return nil }, @@ -3365,13 +3403,13 @@ func Test_deviceAttest01Validate(t *testing.T) { Format: "apple", AttStatement: map[string]interface{}{}, }) - sassert.NoError(t, err) + require.NoError(t, err) payload, err := json.Marshal(struct { AttObj string `json:"attObj"` }{ AttObj: base64.RawURLEncoding.EncodeToString(attObj), }) - sassert.NoError(t, err) + require.NoError(t, err) return test{ args: args{ ctx: ctx, @@ -3385,19 +3423,19 @@ func Test_deviceAttest01Validate(t *testing.T) { payload: payload, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - sassert.Equal(t, "chID", updch.ID) - sassert.Equal(t, "token", updch.Token) - sassert.Equal(t, StatusInvalid, updch.Status) - sassert.Equal(t, ChallengeType("device-attest-01"), updch.Type) - sassert.Equal(t, "12345678", updch.Value) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusInvalid, updch.Status) + assert.Equal(t, ChallengeType("device-attest-01"), updch.Type) + assert.Equal(t, "12345678", updch.Value) err := NewError(ErrorBadAttestationStatementType, "x5c not present") - sassert.EqualError(t, updch.Error.Err, err.Err.Error()) - sassert.Equal(t, err.Type, updch.Error.Type) - sassert.Equal(t, err.Detail, updch.Error.Detail) - sassert.Equal(t, err.Status, updch.Error.Status) - sassert.Equal(t, err.Detail, updch.Error.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) return nil }, @@ -3408,9 +3446,9 @@ func Test_deviceAttest01Validate(t *testing.T) { }, "ok/doAppleAttestationFormat-non-matching-nonce": func(t *testing.T) test { ca, err := minica.New() - sassert.NoError(t, err) + require.NoError(t, err) signer, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - sassert.NoError(t, err) + require.NoError(t, err) caRoot := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: ca.Root.Raw}) leaf, err := ca.Sign(&x509.Certificate{ Subject: pkix.Name{CommonName: "attestation cert"}, @@ -3422,7 +3460,7 @@ func Test_deviceAttest01Validate(t *testing.T) { {Id: oidAppleNonce, Value: []byte("nonce")}, }, }) - sassert.NoError(t, err) + require.NoError(t, err) ctx := NewProvisionerContext(context.Background(), mustAttestationProvisioner(t, caRoot)) attObj, err := cbor.Marshal(struct { Format string `json:"fmt"` @@ -3433,13 +3471,13 @@ func Test_deviceAttest01Validate(t *testing.T) { "x5c": []interface{}{leaf.Raw, ca.Intermediate.Raw}, }, }) - sassert.NoError(t, err) + require.NoError(t, err) payload, err := json.Marshal(struct { AttObj string `json:"attObj"` }{ AttObj: base64.RawURLEncoding.EncodeToString(attObj), }) - sassert.NoError(t, err) + require.NoError(t, err) return test{ args: args{ ctx: ctx, @@ -3453,19 +3491,19 @@ func Test_deviceAttest01Validate(t *testing.T) { payload: payload, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - sassert.Equal(t, "chID", updch.ID) - sassert.Equal(t, "token", updch.Token) - sassert.Equal(t, StatusInvalid, updch.Status) - sassert.Equal(t, ChallengeType("device-attest-01"), updch.Type) - sassert.Equal(t, "12345678", updch.Value) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusInvalid, updch.Status) + assert.Equal(t, ChallengeType("device-attest-01"), updch.Type) + assert.Equal(t, "12345678", updch.Value) err := NewError(ErrorBadAttestationStatementType, "challenge token does not match") - sassert.EqualError(t, updch.Error.Err, err.Err.Error()) - sassert.Equal(t, err.Type, updch.Error.Type) - sassert.Equal(t, err.Detail, updch.Error.Detail) - sassert.Equal(t, err.Status, updch.Error.Status) - sassert.Equal(t, err.Detail, updch.Error.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) return nil }, @@ -3476,9 +3514,9 @@ func Test_deviceAttest01Validate(t *testing.T) { }, "ok/doAppleAttestationFormat-non-matching-challenge-value": func(t *testing.T) test { ca, err := minica.New() - sassert.NoError(t, err) + require.NoError(t, err) signer, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - sassert.NoError(t, err) + require.NoError(t, err) caRoot := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: ca.Root.Raw}) nonce := sha256.Sum256([]byte("nonce")) leaf, err := ca.Sign(&x509.Certificate{ @@ -3491,7 +3529,7 @@ func Test_deviceAttest01Validate(t *testing.T) { {Id: oidAppleNonce, Value: nonce[:]}, }, }) - sassert.NoError(t, err) + require.NoError(t, err) ctx := NewProvisionerContext(context.Background(), mustAttestationProvisioner(t, caRoot)) attObj, err := cbor.Marshal(struct { Format string `json:"fmt"` @@ -3502,13 +3540,13 @@ func Test_deviceAttest01Validate(t *testing.T) { "x5c": []interface{}{leaf.Raw, ca.Intermediate.Raw}, }, }) - sassert.NoError(t, err) + require.NoError(t, err) payload, err := json.Marshal(struct { AttObj string `json:"attObj"` }{ AttObj: base64.RawURLEncoding.EncodeToString(attObj), }) - sassert.NoError(t, err) + require.NoError(t, err) return test{ args: args{ ctx: ctx, @@ -3522,19 +3560,19 @@ func Test_deviceAttest01Validate(t *testing.T) { payload: payload, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - sassert.Equal(t, "chID", updch.ID) - sassert.Equal(t, "nonce", updch.Token) - sassert.Equal(t, StatusInvalid, updch.Status) - sassert.Equal(t, ChallengeType("device-attest-01"), updch.Type) - sassert.Equal(t, "non-matching-value", updch.Value) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "nonce", updch.Token) + assert.Equal(t, StatusInvalid, updch.Status) + assert.Equal(t, ChallengeType("device-attest-01"), updch.Type) + assert.Equal(t, "non-matching-value", updch.Value) err := NewError(ErrorBadAttestationStatementType, "permanent identifier does not match") - sassert.EqualError(t, updch.Error.Err, err.Err.Error()) - sassert.Equal(t, err.Type, updch.Error.Type) - sassert.Equal(t, err.Detail, updch.Error.Detail) - sassert.Equal(t, err.Status, updch.Error.Status) - sassert.Equal(t, err.Detail, updch.Error.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) return nil }, @@ -3545,20 +3583,20 @@ func Test_deviceAttest01Validate(t *testing.T) { }, "ok/doStepAttestationFormat-storeError": func(t *testing.T) test { ca, err := minica.New() - sassert.NoError(t, err) + require.NoError(t, err) caRoot := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: ca.Root.Raw}) signer, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - sassert.NoError(t, err) + require.NoError(t, err) jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - sassert.NoError(t, err) + require.NoError(t, err) token := "token" keyAuth, err := KeyAuthorization(token, jwk) - sassert.NoError(t, err) + require.NoError(t, err) keyAuthSum := sha256.Sum256([]byte(keyAuth)) sig, err := signer.Sign(rand.Reader, keyAuthSum[:], crypto.SHA256) - sassert.NoError(t, err) + require.NoError(t, err) cborSig, err := cbor.Marshal(sig) - sassert.NoError(t, err) + require.NoError(t, err) ctx := NewProvisionerContext(context.Background(), mustAttestationProvisioner(t, caRoot)) attObj, err := cbor.Marshal(struct { Format string `json:"fmt"` @@ -3570,13 +3608,13 @@ func Test_deviceAttest01Validate(t *testing.T) { "sig": cborSig, }, }) - sassert.NoError(t, err) + require.NoError(t, err) payload, err := json.Marshal(struct { AttObj string `json:"attObj"` }{ AttObj: base64.RawURLEncoding.EncodeToString(attObj), }) - sassert.NoError(t, err) + require.NoError(t, err) return test{ args: args{ ctx: ctx, @@ -3590,19 +3628,19 @@ func Test_deviceAttest01Validate(t *testing.T) { payload: payload, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - sassert.Equal(t, "chID", updch.ID) - sassert.Equal(t, "token", updch.Token) - sassert.Equal(t, StatusInvalid, updch.Status) - sassert.Equal(t, ChallengeType("device-attest-01"), updch.Type) - sassert.Equal(t, "12345678", updch.Value) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusInvalid, updch.Status) + assert.Equal(t, ChallengeType("device-attest-01"), updch.Type) + assert.Equal(t, "12345678", updch.Value) err := NewError(ErrorBadAttestationStatementType, "x5c not present") - sassert.EqualError(t, updch.Error.Err, err.Err.Error()) - sassert.Equal(t, err.Type, updch.Error.Type) - sassert.Equal(t, err.Detail, updch.Error.Detail) - sassert.Equal(t, err.Status, updch.Error.Status) - sassert.Equal(t, err.Detail, updch.Error.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) return nil }, @@ -3613,20 +3651,20 @@ func Test_deviceAttest01Validate(t *testing.T) { }, "ok/doStepAttestationFormat-non-matching-identifier": func(t *testing.T) test { ca, err := minica.New() - sassert.NoError(t, err) + require.NoError(t, err) caRoot := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: ca.Root.Raw}) signer, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - sassert.NoError(t, err) + require.NoError(t, err) jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - sassert.NoError(t, err) + require.NoError(t, err) token := "token" keyAuth, err := KeyAuthorization(token, jwk) - sassert.NoError(t, err) + require.NoError(t, err) keyAuthSum := sha256.Sum256([]byte(keyAuth)) sig, err := signer.Sign(rand.Reader, keyAuthSum[:], crypto.SHA256) - sassert.NoError(t, err) + require.NoError(t, err) cborSig, err := cbor.Marshal(sig) - sassert.NoError(t, err) + require.NoError(t, err) ctx := NewProvisionerContext(context.Background(), mustAttestationProvisioner(t, caRoot)) makeLeaf := func(signer crypto.Signer, serialNumber []byte) *x509.Certificate { leaf, err := ca.Sign(&x509.Certificate{ @@ -3641,9 +3679,9 @@ func Test_deviceAttest01Validate(t *testing.T) { } return leaf } - sassert.NoError(t, err) + require.NoError(t, err) serialNumber, err := asn1.Marshal(87654321) - sassert.NoError(t, err) + require.NoError(t, err) leaf := makeLeaf(signer, serialNumber) attObj, err := cbor.Marshal(struct { Format string `json:"fmt"` @@ -3656,13 +3694,13 @@ func Test_deviceAttest01Validate(t *testing.T) { "sig": cborSig, }, }) - sassert.NoError(t, err) + require.NoError(t, err) payload, err := json.Marshal(struct { AttObj string `json:"attObj"` }{ AttObj: base64.RawURLEncoding.EncodeToString(attObj), }) - sassert.NoError(t, err) + require.NoError(t, err) return test{ args: args{ ctx: ctx, @@ -3676,11 +3714,11 @@ func Test_deviceAttest01Validate(t *testing.T) { payload: payload, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - sassert.Equal(t, "chID", updch.ID) - sassert.Equal(t, "token", updch.Token) - sassert.Equal(t, StatusInvalid, updch.Status) - sassert.Equal(t, ChallengeType("device-attest-01"), updch.Type) - sassert.Equal(t, "12345678", updch.Value) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusInvalid, updch.Status) + assert.Equal(t, ChallengeType("device-attest-01"), updch.Type) + assert.Equal(t, "12345678", updch.Value) err := NewError(ErrorBadAttestationStatementType, "permanent identifier does not match"). AddSubproblems(NewSubproblemWithIdentifier( @@ -3689,12 +3727,11 @@ func Test_deviceAttest01Validate(t *testing.T) { "challenge identifier \"12345678\" doesn't match the attested hardware identifier \"87654321\"", )) - sassert.EqualError(t, updch.Error.Err, err.Err.Error()) - sassert.Equal(t, err.Type, updch.Error.Type) - sassert.Equal(t, err.Detail, updch.Error.Detail) - sassert.Equal(t, err.Status, updch.Error.Status) - sassert.Equal(t, err.Detail, updch.Error.Detail) - sassert.Equal(t, err.Subproblems, updch.Error.Subproblems) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) return nil }, @@ -3706,19 +3743,19 @@ func Test_deviceAttest01Validate(t *testing.T) { }, "ok/unknown-attestation-format": func(t *testing.T) test { ca, err := minica.New() - sassert.NoError(t, err) + require.NoError(t, err) signer, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - sassert.NoError(t, err) + require.NoError(t, err) jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - sassert.NoError(t, err) + require.NoError(t, err) token := "token" keyAuth, err := KeyAuthorization(token, jwk) - sassert.NoError(t, err) + require.NoError(t, err) keyAuthSum := sha256.Sum256([]byte(keyAuth)) sig, err := signer.Sign(rand.Reader, keyAuthSum[:], crypto.SHA256) - sassert.NoError(t, err) + require.NoError(t, err) cborSig, err := cbor.Marshal(sig) - sassert.NoError(t, err) + require.NoError(t, err) ctx := NewProvisionerContext(context.Background(), mustNonAttestationProvisioner(t)) makeLeaf := func(signer crypto.Signer, serialNumber []byte) *x509.Certificate { leaf, err := ca.Sign(&x509.Certificate{ @@ -3733,9 +3770,9 @@ func Test_deviceAttest01Validate(t *testing.T) { } return leaf } - sassert.NoError(t, err) + require.NoError(t, err) serialNumber, err := asn1.Marshal(87654321) - sassert.NoError(t, err) + require.NoError(t, err) leaf := makeLeaf(signer, serialNumber) attObj, err := cbor.Marshal(struct { Format string `json:"fmt"` @@ -3748,13 +3785,13 @@ func Test_deviceAttest01Validate(t *testing.T) { "sig": cborSig, }, }) - sassert.NoError(t, err) + require.NoError(t, err) payload, err := json.Marshal(struct { AttObj string `json:"attObj"` }{ AttObj: base64.RawURLEncoding.EncodeToString(attObj), }) - sassert.NoError(t, err) + require.NoError(t, err) return test{ args: args{ ctx: ctx, @@ -3768,20 +3805,19 @@ func Test_deviceAttest01Validate(t *testing.T) { payload: payload, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - sassert.Equal(t, "chID", updch.ID) - sassert.Equal(t, "token", updch.Token) - sassert.Equal(t, StatusInvalid, updch.Status) - sassert.Equal(t, ChallengeType("device-attest-01"), updch.Type) - sassert.Equal(t, "12345678", updch.Value) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusInvalid, updch.Status) + assert.Equal(t, ChallengeType("device-attest-01"), updch.Type) + assert.Equal(t, "12345678", updch.Value) err := NewError(ErrorBadAttestationStatementType, "unexpected attestation object format") - sassert.EqualError(t, updch.Error.Err, err.Err.Error()) - sassert.Equal(t, err.Type, updch.Error.Type) - sassert.Equal(t, err.Detail, updch.Error.Detail) - sassert.Equal(t, err.Status, updch.Error.Status) - sassert.Equal(t, err.Detail, updch.Error.Detail) - sassert.Equal(t, err.Subproblems, updch.Error.Subproblems) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) return nil }, @@ -3793,20 +3829,20 @@ func Test_deviceAttest01Validate(t *testing.T) { }, "fail/db.UpdateChallenge": func(t *testing.T) test { ca, err := minica.New() - sassert.NoError(t, err) + require.NoError(t, err) caRoot := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: ca.Root.Raw}) signer, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - sassert.NoError(t, err) + require.NoError(t, err) jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - sassert.NoError(t, err) + require.NoError(t, err) token := "token" keyAuth, err := KeyAuthorization(token, jwk) - sassert.NoError(t, err) + require.NoError(t, err) keyAuthSum := sha256.Sum256([]byte(keyAuth)) sig, err := signer.Sign(rand.Reader, keyAuthSum[:], crypto.SHA256) - sassert.NoError(t, err) + require.NoError(t, err) cborSig, err := cbor.Marshal(sig) - sassert.NoError(t, err) + require.NoError(t, err) ctx := NewProvisionerContext(context.Background(), mustAttestationProvisioner(t, caRoot)) makeLeaf := func(signer crypto.Signer, serialNumber []byte) *x509.Certificate { leaf, err := ca.Sign(&x509.Certificate{ @@ -3821,9 +3857,9 @@ func Test_deviceAttest01Validate(t *testing.T) { } return leaf } - sassert.NoError(t, err) + require.NoError(t, err) serialNumber, err := asn1.Marshal(12345678) - sassert.NoError(t, err) + require.NoError(t, err) leaf := makeLeaf(signer, serialNumber) attObj, err := cbor.Marshal(struct { Format string `json:"fmt"` @@ -3836,13 +3872,13 @@ func Test_deviceAttest01Validate(t *testing.T) { "sig": cborSig, }, }) - sassert.NoError(t, err) + require.NoError(t, err) payload, err := json.Marshal(struct { AttObj string `json:"attObj"` }{ AttObj: base64.RawURLEncoding.EncodeToString(attObj), }) - sassert.NoError(t, err) + require.NoError(t, err) return test{ args: args{ ctx: ctx, @@ -3856,11 +3892,11 @@ func Test_deviceAttest01Validate(t *testing.T) { payload: payload, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - sassert.Equal(t, "chID", updch.ID) - sassert.Equal(t, "token", updch.Token) - sassert.Equal(t, StatusValid, updch.Status) - sassert.Equal(t, ChallengeType("device-attest-01"), updch.Type) - sassert.Equal(t, "12345678", updch.Value) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusValid, updch.Status) + assert.Equal(t, ChallengeType("device-attest-01"), updch.Type) + assert.Equal(t, "12345678", updch.Value) return errors.New("force") }, @@ -3870,20 +3906,97 @@ func Test_deviceAttest01Validate(t *testing.T) { wantErr: NewError(ErrorServerInternalType, "error updating challenge: force"), } }, + "ok": func(t *testing.T) test { + ca, err := minica.New() + require.NoError(t, err) + caRoot := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: ca.Root.Raw}) + signer, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + require.NoError(t, err) + jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) + require.NoError(t, err) + token := "token" + keyAuth, err := KeyAuthorization(token, jwk) + require.NoError(t, err) + keyAuthSum := sha256.Sum256([]byte(keyAuth)) + sig, err := signer.Sign(rand.Reader, keyAuthSum[:], crypto.SHA256) + require.NoError(t, err) + cborSig, err := cbor.Marshal(sig) + require.NoError(t, err) + ctx := NewProvisionerContext(context.Background(), mustAttestationProvisioner(t, caRoot)) + makeLeaf := func(signer crypto.Signer, serialNumber []byte) *x509.Certificate { + leaf, err := ca.Sign(&x509.Certificate{ + Subject: pkix.Name{CommonName: "attestation cert"}, + PublicKey: signer.Public(), + ExtraExtensions: []pkix.Extension{ + {Id: oidYubicoSerialNumber, Value: serialNumber}, + }, + }) + if err != nil { + t.Fatal(err) + } + return leaf + } + require.NoError(t, err) + serialNumber, err := asn1.Marshal(12345678) + require.NoError(t, err) + leaf := makeLeaf(signer, serialNumber) + attObj, err := cbor.Marshal(struct { + Format string `json:"fmt"` + AttStatement map[string]interface{} `json:"attStmt,omitempty"` + }{ + Format: "step", + AttStatement: map[string]interface{}{ + "x5c": []interface{}{leaf.Raw, ca.Intermediate.Raw}, + "alg": -7, + "sig": cborSig, + }, + }) + require.NoError(t, err) + payload, err := json.Marshal(struct { + AttObj string `json:"attObj"` + }{ + AttObj: base64.RawURLEncoding.EncodeToString(attObj), + }) + require.NoError(t, err) + return test{ + args: args{ + ctx: ctx, + ch: &Challenge{ + ID: "chID", + Token: "token", + Type: "device-attest-01", + Status: StatusPending, + Value: "12345678", + }, + payload: payload, + db: &MockDB{ + MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusValid, updch.Status) + assert.Equal(t, ChallengeType("device-attest-01"), updch.Type) + assert.Equal(t, "12345678", updch.Value) + + return nil + }, + }, + jwk: jwk, + }, + wantErr: nil, + } + }, } for name, run := range tests { t.Run(name, func(t *testing.T) { tc := run(t) if err := deviceAttest01Validate(tc.args.ctx, tc.args.ch, tc.args.db, tc.args.jwk, tc.args.payload); err != nil { - sassert.NotNil(t, tc.wantErr) - sassert.EqualError(t, tc.wantErr, err.Error()) + assert.Error(t, tc.wantErr) + assert.EqualError(t, err, tc.wantErr.Error()) return } - sassert.Nil(t, tc.wantErr) - - // TODO: more validations? + assert.Nil(t, tc.wantErr) }) } }