@@ -5,93 +5,101 @@ import (
55 "crypto/x509"
66 "encoding/asn1"
77 "fmt"
8+ "time"
89
910 "github.com/go-webauthn/webauthn/metadata"
1011 "github.com/go-webauthn/webauthn/protocol/webauthncose"
1112)
1213
13- func init () {
14- RegisterAttestationFormat (AttestationFormatAndroidKey , verifyAndroidKeyFormat )
15- }
16-
17- // The android-key attestation statement looks like:
18- // $$attStmtType //= (
14+ // attestationFormatValidationHandlerAndroidKey is the handler for the Android Key Attestation Statement Format.
1915//
20- // fmt: "android- key",
21- // attStmt: androidStmtFormat
16+ // An Android key attestation statement consists simply of the Android attestation statement, which is a series of DER
17+ // encoded X.509 certificates. See the Android developer documentation. Its syntax is defined as follows:
2218//
23- // )
19+ // $$attStmtType //= (
20+ // fmt: "android-key",
21+ // attStmt: androidStmtFormat
22+ // )
23+ //
24+ // androidStmtFormat = {
25+ // alg: COSEAlgorithmIdentifier,
26+ // sig: bytes,
27+ // x5c: [ credCert: bytes, * (caCert: bytes) ]
28+ // }
2429//
25- // androidStmtFormat = {
26- // alg: COSEAlgorithmIdentifier,
27- // sig: bytes,
28- // x5c: [ credCert: bytes, * (caCert: bytes) ]
29- // }
30+ // Specification: §8.4. Android Key Attestation Statement Format
3031//
31- // Specification: §8.4. Android Key Attestation Statement Format (https://www.w3.org/TR/webauthn/#sctn-android-key-attestation)
32- func verifyAndroidKeyFormat (att AttestationObject , clientDataHash []byte , _ metadata.Provider ) (attestationType string , x5cs []any , err error ) {
32+ // See: https://www.w3.org/TR/webauthn/#sctn-android-key-attestation
33+ func attestationFormatValidationHandlerAndroidKey (att AttestationObject , clientDataHash []byte , _ metadata.Provider ) (attestationType string , x5cs []any , err error ) {
34+ var (
35+ alg int64
36+ sig []byte
37+ ok bool
38+ )
39+
3340 // Given the verification procedure inputs attStmt, authenticatorData and clientDataHash, the verification procedure is as follows:
3441 // §8.4.1. Verify that attStmt is valid CBOR conforming to the syntax defined above and perform CBOR decoding on it to extract
3542 // the contained fields.
36-
3743 // Get the alg value - A COSEAlgorithmIdentifier containing the identifier of the algorithm
3844 // used to generate the attestation signature.
39- alg , present := att .AttStatement [stmtAlgorithm ].(int64 )
40- if ! present {
45+ if alg , ok = att .AttStatement [stmtAlgorithm ].(int64 ); ! ok {
4146 return "" , nil , ErrAttestationFormat .WithDetails ("Error retrieving alg value" )
4247 }
4348
4449 // Get the sig value - A byte string containing the attestation signature.
45- sig , present := att .AttStatement [stmtSignature ].([]byte )
46- if ! present {
50+ if sig , ok = att .AttStatement [stmtSignature ].([]byte ); ! ok {
4751 return "" , nil , ErrAttestationFormat .WithDetails ("Error retrieving sig value" )
4852 }
4953
50- // If x5c is not present, return an error.
51- x5c , x509present := att .AttStatement [stmtX5C ].([]any )
52- if ! x509present {
53- // Handle Basic Attestation steps for the x509 Certificate.
54- return "" , nil , ErrAttestationFormat .WithDetails ("Error retrieving x5c value" )
55- }
56-
5754 // §8.4.2. Verify that sig is a valid signature over the concatenation of authenticatorData and clientDataHash
5855 // using the public key in the first certificate in x5c with the algorithm specified in alg.
59- attCertBytes , valid := x5c [0 ].([]byte )
60- if ! valid {
61- return "" , nil , ErrAttestation .WithDetails ("Error getting certificate from x5c cert chain" )
56+ var (
57+ x5c []any
58+ certs []* x509.Certificate
59+ )
60+
61+ if x5c , certs , err = attStatementParseX5CS (att .AttStatement , stmtX5C ); err != nil {
62+ return "" , nil , err
6263 }
6364
64- attCert , err := x509 .ParseCertificate (attCertBytes )
65- if err != nil {
66- return "" , nil , ErrAttestationFormat .WithDetails (fmt .Sprintf ("Error parsing certificate from ASN.1 data: %+v" , err )).WithError (err )
65+ if len (certs ) == 0 {
66+ return "" , nil , ErrInvalidAttestation .WithDetails ("No certificates in x5c" )
67+ }
68+
69+ credCert := certs [0 ]
70+
71+ if _ , err = attStatementCertChainVerify (certs , attAndroidKeyHardwareRootsCertPool , true , time .Now ().Add (time .Hour * 8760 ).UTC ()); err != nil {
72+ return "" , nil , ErrInvalidAttestation .WithDetails ("Error validating x5c cert chain" ).WithError (err )
6773 }
6874
6975 signatureData := append (att .RawAuthData , clientDataHash ... ) //nolint:gocritic // This is intentional.
7076
71- coseAlg := webauthncose .COSEAlgorithmIdentifier (alg )
72- if err = attCert .CheckSignature (webauthncose .SigAlgFromCOSEAlg (coseAlg ), signatureData , sig ); err != nil {
73- return "" , nil , ErrInvalidAttestation .WithDetails (fmt .Sprintf ("Signature validation error: %+v\n " , err )).WithError (err )
77+ if sigAlg := webauthncose .SigAlgFromCOSEAlg (webauthncose .COSEAlgorithmIdentifier (alg )); sigAlg == x509 .UnknownSignatureAlgorithm {
78+ return "" , nil , ErrInvalidAttestation .WithDetails (fmt .Sprintf ("Unsupported COSE alg: %d" , alg ))
79+ } else if err = credCert .CheckSignature (sigAlg , signatureData , sig ); err != nil {
80+ return "" , nil , ErrInvalidAttestation .WithDetails (fmt .Sprintf ("Signature validation error: %+v" , err )).WithError (err )
7481 }
7582
7683 // Verify that the public key in the first certificate in x5c matches the credentialPublicKey in the attestedCredentialData in authenticatorData.
77- pubKey , err := webauthncose .ParsePublicKey ( att . AuthData . AttData . CredentialPublicKey )
78- if err != nil {
79- return "" , nil , ErrInvalidAttestation . WithDetails ( fmt . Sprintf ( "Error parsing public key: %+v \n " , err )). WithError ( err )
84+ var attPublicKeyData webauthncose.EC2PublicKeyData
85+ if attPublicKeyData , err = verifyAttestationECDSAPublicKeyMatch ( att , credCert ); err != nil {
86+ return "" , nil , err
8087 }
8188
82- e := pubKey .(webauthncose.EC2PublicKeyData )
83-
84- valid , err = e .Verify (signatureData , sig )
85- if err != nil || ! valid {
86- return "" , nil , ErrInvalidAttestation .WithDetails (fmt .Sprintf ("Error parsing public key: %+v\n " , err )).WithError (err )
89+ var valid bool
90+ if valid , err = attPublicKeyData .Verify (signatureData , sig ); err != nil || ! valid {
91+ return "" , nil , ErrInvalidAttestation .WithDetails (fmt .Sprintf ("Error parsing public key: %+v" , err )).WithError (err )
8792 }
8893
8994 // §8.4.3. Verify that the attestationChallenge field in the attestation certificate extension data is identical to clientDataHash.
9095 // attCert.Extensions.
96+ // As noted in §8.4.1 (https://www.w3.org/TR/webauthn/#key-attstn-cert-requirements) the Android Key Attestation
97+ // certificate's android key attestation certificate extension data is identified by the OID
98+ // "1.3.6.1.4.1.11129.2.1.17".
9199 var attExtBytes []byte
92100
93- for _ , ext := range attCert .Extensions {
94- if ext .Id .Equal ([] int { 1 , 3 , 6 , 1 , 4 , 1 , 11129 , 2 , 1 , 17 } ) {
101+ for _ , ext := range credCert .Extensions {
102+ if ext .Id .Equal (oidExtensionAndroidKeystore ) {
95103 attExtBytes = ext .Value
96104 }
97105 }
@@ -100,8 +108,6 @@ func verifyAndroidKeyFormat(att AttestationObject, clientDataHash []byte, _ meta
100108 return "" , nil , ErrAttestationFormat .WithDetails ("Attestation certificate extensions missing 1.3.6.1.4.1.11129.2.1.17" )
101109 }
102110
103- // As noted in §8.4.1 (https://www.w3.org/TR/webauthn/#key-attstn-cert-requirements) the Android Key Attestation attestation certificate's
104- // android key attestation certificate extension data is identified by the OID "1.3.6.1.4.1.11129.2.1.17".
105111 decoded := keyDescription {}
106112
107113 if _ , err = asn1 .Unmarshal (attExtBytes , & decoded ); err != nil {
@@ -114,7 +120,7 @@ func verifyAndroidKeyFormat(att AttestationObject, clientDataHash []byte, _ meta
114120 }
115121
116122 // The AuthorizationList.allApplications field is not present on either authorization list (softwareEnforced nor teeEnforced), since PublicKeyCredential MUST be scoped to the RP ID.
117- if nil != decoded .SoftwareEnforced .AllApplications || nil != decoded .TeeEnforced .AllApplications {
123+ if decoded .SoftwareEnforced .AllApplications != nil || decoded .TeeEnforced .AllApplications != nil {
118124 return "" , nil , ErrAttestationFormat .WithDetails ("Attestation certificate extensions contains all applications field" )
119125 }
120126
@@ -208,34 +214,47 @@ const (
208214 Failed
209215)
210216
211- /**
212- * The origin of a key (or pair), i.e. where it was generated. Note that KM_TAG_ORIGIN can be found
213- * in either the hardware-enforced or software-enforced list for a key, indicating whether the key
214- * is hardware or software-based. Specifically, a key with KM_ORIGIN_GENERATED in the
215- * hardware-enforced list is guaranteed never to have existed outide the secure hardware.
216- */
217- type KM_KEY_ORIGIN int
218-
219217const (
220- KM_ORIGIN_GENERATED = iota /* Generated in keymaster. Should not exist outside the TEE. */
221- KM_ORIGIN_DERIVED /* Derived inside keymaster. Likely exists off-device. */
222- KM_ORIGIN_IMPORTED /* Imported into keymaster. Existed as clear text in Android. */
223- KM_ORIGIN_UNKNOWN /* Keymaster did not record origin. This value can only be seen on
224- * keys in a keymaster0 implementation. The keymaster0 adapter uses
225- * this value to document the fact that it is unknown whether the key
226- * was generated inside or imported into keymaster. */
218+ // KM_ORIGIN_GENERATED means generated in keymaster. Should not exist outside the TEE.
219+ KM_ORIGIN_GENERATED = iota
220+
221+ // KM_ORIGIN_DERIVED means derived inside keymaster. Likely exists off-device.
222+ KM_ORIGIN_DERIVED
223+
224+ // KM_ORIGIN_IMPORTED means imported into keymaster. Existed as clear text in Android.
225+ KM_ORIGIN_IMPORTED
226+
227+ // KM_ORIGIN_UNKNOWN means keymaster did not record origin. This value can only be seen on keys in a keymaster0
228+ // implementation. The keymaster0 adapter uses this value to document the fact that it is unknown whether the key
229+ // was generated inside or imported into keymaster.
230+ KM_ORIGIN_UNKNOWN
227231)
228232
229- /**
230- * Possible purposes of a key (or pair).
231- */
232- type KM_PURPOSE int
233233
234234const (
235- KM_PURPOSE_ENCRYPT = iota /* Usable with RSA, EC and AES keys. */
236- KM_PURPOSE_DECRYPT /* Usable with RSA, EC and AES keys. */
237- KM_PURPOSE_SIGN /* Usable with RSA, EC and HMAC keys. */
238- KM_PURPOSE_VERIFY /* Usable with RSA, EC and HMAC keys. */
239- KM_PURPOSE_DERIVE_KEY /* Usable with EC keys. */
240- KM_PURPOSE_WRAP /* Usable with wrapped keys. */
235+ // KM_PURPOSE_ENCRYPT is usable with RSA, EC and AES keys.
236+ KM_PURPOSE_ENCRYPT = iota
237+
238+ // KM_PURPOSE_DECRYPT is usable with RSA, EC and AES keys.
239+ KM_PURPOSE_DECRYPT
240+
241+ // KM_PURPOSE_SIGN is usable with RSA, EC and HMAC keys.
242+ KM_PURPOSE_SIGN
243+
244+ // KM_PURPOSE_VERIFY is usable with RSA, EC and HMAC keys.
245+ KM_PURPOSE_VERIFY
246+
247+ // KM_PURPOSE_DERIVE_KEY is usable with EC keys.
248+ KM_PURPOSE_DERIVE_KEY
249+
250+ // KM_PURPOSE_WRAP is usable with wrapped keys.
251+ KM_PURPOSE_WRAP
252+ )
253+
254+ var (
255+ attAndroidKeyHardwareRootsCertPool * x509.CertPool
241256)
257+
258+ func init () {
259+ RegisterAttestationFormat (AttestationFormatAndroidKey , attestationFormatValidationHandlerAndroidKey )
260+ }
0 commit comments