From 2b3b2c283aaf52f6063cad530d2df821d6829829 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Thu, 14 Jul 2022 17:10:03 -0700 Subject: [PATCH] Add attestation certificate validation for Apple devices --- acme/api/order.go | 2 -- acme/challenge.go | 71 +---------------------------------------------- acme/order.go | 37 +----------------------- 3 files changed, 2 insertions(+), 108 deletions(-) diff --git a/acme/api/order.go b/acme/api/order.go index 2f6cd776..0c81df76 100644 --- a/acme/api/order.go +++ b/acme/api/order.go @@ -399,8 +399,6 @@ func challengeTypes(az *acme.Authorization) []acme.ChallengeType { } case acme.PermanentIdentifier: chTypes = []acme.ChallengeType{acme.DEVICEATTEST01} - case acme.CA: - chTypes = []acme.ChallengeType{acme.APPLEATTEST01} default: chTypes = []acme.ChallengeType{} } diff --git a/acme/challenge.go b/acme/challenge.go index 8a369342..47c46490 100644 --- a/acme/challenge.go +++ b/acme/challenge.go @@ -1,7 +1,6 @@ package acme import ( - "bytes" "context" "crypto" "crypto/ecdsa" @@ -16,13 +15,11 @@ import ( "encoding/base64" "encoding/hex" "encoding/json" - "encoding/pem" "errors" "fmt" "io" "net" "net/url" - "os" "reflect" "strconv" "strings" @@ -45,7 +42,6 @@ const ( TLSALPN01 ChallengeType = "tls-alpn-01" // DEVICEATTEST01 is the device-attest-01 ACME challenge type DEVICEATTEST01 ChallengeType = "device-attest-01" - APPLEATTEST01 ChallengeType = "client-01" ) // Challenge represents an ACME response Challenge type. @@ -89,8 +85,6 @@ func (ch *Challenge) Validate(ctx context.Context, db DB, jwk *jose.JSONWebKey, return tlsalpn01Validate(ctx, ch, db, jwk) case DEVICEATTEST01: return deviceAttest01Validate(ctx, ch, db, jwk, payload) - case APPLEATTEST01: - return appleAttest01Validate(ctx, ch, db, jwk, payload) default: return NewErrorISE("unexpected challenge type '%s'", ch.Type) } @@ -168,7 +162,7 @@ func tlsalpn01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSON // [RFC5246] or higher when connecting to clients for validation. MinVersion: tls.VersionTLS12, ServerName: serverName(ch), - InsecureSkipVerify: true, //nolint:gosec // we expect a self-signed challenge certificate + InsecureSkipVerify: true, // nolint:gosec // we expect a self-signed challenge certificate } hostPort := net.JoinHostPort(ch.Value, "443") @@ -418,7 +412,6 @@ func deviceAttest01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose return nil } -<<<<<<< HEAD // Apple Enterprise Attestation Root CA from // https://www.apple.com/certificateauthority/private/ const appleEnterpriseAttestationRootCA = `-----BEGIN CERTIFICATE----- @@ -659,68 +652,6 @@ func doStepAttestationFormat(ctx context.Context, prov Provisioner, ch *Challeng } return data, nil -======= -type ApplePayload struct { - AttObj string `json:"attObj"` - Error string `json:"error"` -} - -func appleAttest01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSONWebKey, payload []byte) error { - var p ApplePayload - if err := json.Unmarshal(payload, &p); err != nil { - return WrapErrorISE(err, "error unmarshalling JSON") - } - - fmt.Fprintf(os.Stderr, "p.AttObj: %v\n", p.AttObj) - - attObj, err := base64.RawURLEncoding.DecodeString(p.AttObj) - if err != nil { - return WrapErrorISE(err, "error base64 decoding attObj") - } - - att := AttestationObject{} - if err := cbor.Unmarshal(attObj, &att); err != nil { - return WrapErrorISE(err, "error unmarshalling CBOR") - } - - if att.Format != "apple" { - return storeError(ctx, db, ch, true, NewError(ErrorBadAttestationStatement, - "unexpected attestation object format")) - } - - x5c, x509present := att.AttStatement["x5c"].([]interface{}) - if !x509present { - return storeError(ctx, db, ch, true, NewError(ErrorBadAttestationStatement, - "x5c not present")) - } - - attCertBytes, valid := x5c[0].([]byte) - if !valid { - return storeError(ctx, db, ch, true, NewError(ErrorRejectedIdentifierType, - "error getting certificate from x5c cert chain")) - } - - attCert, err := x509.ParseCertificate(attCertBytes) - if err != nil { - return WrapErrorISE(err, "error parsing AK certificate") - } - - b := &pem.Block{ - Type: "CERTIFICATE", - Bytes: attCert.Raw, - } - pem.Encode(os.Stderr, b) - - // 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 ->>>>>>> 26e1b4ba (iOS 16 beta 1 support) } // serverName determines the SNI HostName to set based on an acme.Challenge diff --git a/acme/order.go b/acme/order.go index 127edb74..2eddad53 100644 --- a/acme/order.go +++ b/acme/order.go @@ -5,15 +5,11 @@ import ( "context" "crypto/x509" "encoding/json" - "encoding/pem" "net" - "os" "sort" "strings" "time" - "github.com/google/go-attestation/oid" - attest_x509 "github.com/google/go-attestation/x509" "github.com/smallstep/certificates/authority/provisioner" "go.step.sm/crypto/x509util" ) @@ -28,7 +24,6 @@ const ( // PermanentIdentifier is the ACME permanent-identifier identifier type // defined in https://datatracker.ietf.org/doc/html/draft-bweeks-acme-device-attest-00 PermanentIdentifier IdentifierType = "permanent-identifier" - CA IdentifierType = "ca" ) // Identifier encodes the type that an order pertains to. @@ -155,12 +150,6 @@ func (o *Order) Finalize(ctx context.Context, db DB, csr *x509.CertificateReques return NewErrorISE("unexpected status %s for order %s", o.Status, o.ID) } - b := &pem.Block{ - Type: "CERTIFICATE REQUEST", - Bytes: csr.Raw, - } - pem.Encode(os.Stderr, b) - // canonicalize the CSR to allow for comparison csr = canonicalize(csr) @@ -199,11 +188,6 @@ func (o *Order) Finalize(ctx context.Context, db DB, csr *x509.CertificateReques data.SetSubjectAlternativeNames(sans...) } - deviceIDs, err := o.deviceIDs(csr) - if err != nil { - return err - } - // Get authorizations from the ACME provisioner. ctx = provisioner.NewContextWithMethod(ctx, provisioner.SignMethod) signOps, err := p.AuthorizeSign(ctx, "") @@ -269,7 +253,6 @@ func (o *Order) sans(csr *x509.CertificateRequest) ([]x509util.SubjectAlternativ case PermanentIdentifier: orderPIDs[indexPID] = n.Value indexPID++ - case CA: default: return sans, NewErrorISE("unsupported identifier type in order: %s", n.Type) } @@ -323,25 +306,6 @@ func (o *Order) sans(csr *x509.CertificateRequest) ([]x509util.SubjectAlternativ return sans, nil } -func (o *Order) deviceIDs(csr *x509.CertificateRequest) ([]x509util.PermanentIdentifier, error) { - var permIDs []x509util.PermanentIdentifier - for _, ext := range csr.Extensions { - if ext.Id.Equal(oid.SubjectAltName) { - san, err := attest_x509.ParseSubjectAltName(ext) - if err != nil { - return nil, err - } - for _, pi := range san.PermanentIdentifiers { - permIDs = append(permIDs, x509util.PermanentIdentifier{ - Value: pi.IdentifierValue, - Assigner: pi.Assigner, - }) - } - } - } - return permIDs, nil -} - // numberOfIdentifierType returns the number of Identifiers that // are of type typ. func numberOfIdentifierType(typ IdentifierType, ids []Identifier) int { @@ -360,6 +324,7 @@ func numberOfIdentifierType(typ IdentifierType, ids []Identifier) int { // addresses or DNS names slice, depending on whether it can be parsed as an IP // or not. This might result in an additional SAN in the final certificate. func canonicalize(csr *x509.CertificateRequest) (canonicalized *x509.CertificateRequest) { + // for clarity only; we're operating on the same object by pointer canonicalized = csr