diff --git a/acme/api/handler.go b/acme/api/handler.go index 2e3931b1..177e7b66 100644 --- a/acme/api/handler.go +++ b/acme/api/handler.go @@ -291,7 +291,7 @@ func GetChallenge(w http.ResponseWriter, r *http.Request) { } // Just verify that the payload was set, since we're not strictly adhering // to ACME V2 spec for reasons specified below. - _, err = payloadFromContext(ctx) + payload, err := payloadFromContext(ctx) if err != nil { render.Error(w, err) return @@ -320,7 +320,7 @@ func GetChallenge(w http.ResponseWriter, r *http.Request) { render.Error(w, err) return } - if err = ch.Validate(ctx, db, jwk); err != nil { + if err = ch.Validate(ctx, db, jwk, payload.value); err != nil { render.Error(w, acme.WrapErrorISE(err, "error validating challenge")) return } diff --git a/acme/challenge.go b/acme/challenge.go index 8d8466bd..4a143544 100644 --- a/acme/challenge.go +++ b/acme/challenge.go @@ -31,6 +31,8 @@ const ( DNS01 ChallengeType = "dns-01" // TLSALPN01 is the tls-alpn-01 ACME challenge type TLSALPN01 ChallengeType = "tls-alpn-01" + // DEVICEATTEST01 is the device-attest-01 ACME challenge type + DEVICEATTEST01 ChallengeType = "device-attest-01" ) // Challenge represents an ACME response Challenge type. @@ -60,7 +62,7 @@ func (ch *Challenge) ToLog() (interface{}, error) { // type using the DB interface. // satisfactorily validated, the 'status' and 'validated' attributes are // updated. -func (ch *Challenge) Validate(ctx context.Context, db DB, jwk *jose.JSONWebKey) error { +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 { return nil @@ -72,6 +74,8 @@ func (ch *Challenge) Validate(ctx context.Context, db DB, jwk *jose.JSONWebKey) return dns01Validate(ctx, ch, db, jwk) case TLSALPN01: return tlsalpn01Validate(ctx, ch, db, jwk) + case DEVICEATTEST01: + return deviceAttest01Validate(ctx, ch, db, jwk, payload) default: return NewErrorISE("unexpected challenge type '%s'", ch.Type) } @@ -297,6 +301,22 @@ func dns01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSONWebK return nil } +func deviceAttest01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSONWebKey, payload []byte) error { + if string(payload) != "\"fake_device_attestation_statement\"" { + return errors.New("string(payload) != \"fake_device_attestation_statement\"") + } + + // Update and store the challenge. + ch.Status = StatusValid + ch.Error = nil + ch.ValidatedAt = clock.Now().Format(time.RFC3339) + + if err := db.UpdateChallenge(ctx, ch); err != nil { + return WrapErrorISE(err, "error updating challenge") + } + return nil +} + // serverName determines the SNI HostName to set based on an acme.Challenge // for TLS-ALPN-01 challenges RFC8738 states that, if HostName is an IP, it // should be the ARPA address https://datatracker.ietf.org/doc/html/rfc8738#section-6. diff --git a/acme/challenge_test.go b/acme/challenge_test.go index e1b6816a..3f7e214d 100644 --- a/acme/challenge_test.go +++ b/acme/challenge_test.go @@ -506,7 +506,7 @@ func TestChallenge_Validate(t *testing.T) { } ctx := NewClientContext(context.Background(), tc.vc) - if err := tc.ch.Validate(ctx, tc.db, tc.jwk); err != nil { + if err := tc.ch.Validate(ctx, tc.db, tc.jwk, nil); err != nil { if assert.NotNil(t, tc.err) { switch k := err.(type) { case *Error: