You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

attestation_androidkey.go 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. package protocol
  2. import (
  3. "bytes"
  4. "crypto/x509"
  5. "encoding/asn1"
  6. "fmt"
  7. "github.com/duo-labs/webauthn/protocol/webauthncose"
  8. )
  9. var androidAttestationKey = "android-key"
  10. func init() {
  11. RegisterAttestationFormat(androidAttestationKey, verifyAndroidKeyFormat)
  12. }
  13. // From §8.4. https://www.w3.org/TR/webauthn/#android-key-attestation
  14. // The android-key attestation statement looks like:
  15. // $$attStmtType //= (
  16. // fmt: "android-key",
  17. // attStmt: androidStmtFormat
  18. // )
  19. // androidStmtFormat = {
  20. // alg: COSEAlgorithmIdentifier,
  21. // sig: bytes,
  22. // x5c: [ credCert: bytes, * (caCert: bytes) ]
  23. // }
  24. func verifyAndroidKeyFormat(att AttestationObject, clientDataHash []byte) (string, []interface{}, error) {
  25. // Given the verification procedure inputs attStmt, authenticatorData and clientDataHash, the verification procedure is as follows:
  26. // §8.4.1. Verify that attStmt is valid CBOR conforming to the syntax defined above and perform CBOR decoding on it to extract
  27. // the contained fields.
  28. // Get the alg value - A COSEAlgorithmIdentifier containing the identifier of the algorithm
  29. // used to generate the attestation signature.
  30. alg, present := att.AttStatement["alg"].(int64)
  31. if !present {
  32. return androidAttestationKey, nil, ErrAttestationFormat.WithDetails("Error retreiving alg value")
  33. }
  34. // Get the sig value - A byte string containing the attestation signature.
  35. sig, present := att.AttStatement["sig"].([]byte)
  36. if !present {
  37. return androidAttestationKey, nil, ErrAttestationFormat.WithDetails("Error retreiving sig value")
  38. }
  39. // If x5c is not present, return an error
  40. x5c, x509present := att.AttStatement["x5c"].([]interface{})
  41. if !x509present {
  42. // Handle Basic Attestation steps for the x509 Certificate
  43. return androidAttestationKey, nil, ErrAttestationFormat.WithDetails("Error retreiving x5c value")
  44. }
  45. // §8.4.2. Verify that sig is a valid signature over the concatenation of authenticatorData and clientDataHash
  46. // using the public key in the first certificate in x5c with the algorithm specified in alg.
  47. attCertBytes, valid := x5c[0].([]byte)
  48. if !valid {
  49. return androidAttestationKey, nil, ErrAttestation.WithDetails("Error getting certificate from x5c cert chain")
  50. }
  51. signatureData := append(att.RawAuthData, clientDataHash...)
  52. attCert, err := x509.ParseCertificate(attCertBytes)
  53. if err != nil {
  54. return androidAttestationKey, nil, ErrAttestationFormat.WithDetails(fmt.Sprintf("Error parsing certificate from ASN.1 data: %+v", err))
  55. }
  56. coseAlg := webauthncose.COSEAlgorithmIdentifier(alg)
  57. sigAlg := webauthncose.SigAlgFromCOSEAlg(coseAlg)
  58. err = attCert.CheckSignature(x509.SignatureAlgorithm(sigAlg), signatureData, sig)
  59. if err != nil {
  60. return androidAttestationKey, nil, ErrInvalidAttestation.WithDetails(fmt.Sprintf("Signature validation error: %+v\n", err))
  61. }
  62. // Verify that the public key in the first certificate in x5c matches the credentialPublicKey in the attestedCredentialData in authenticatorData.
  63. pubKey, err := webauthncose.ParsePublicKey(att.AuthData.AttData.CredentialPublicKey)
  64. if err != nil {
  65. return androidAttestationKey, nil, ErrInvalidAttestation.WithDetails(fmt.Sprintf("Error parsing public key: %+v\n", err))
  66. }
  67. e := pubKey.(webauthncose.EC2PublicKeyData)
  68. valid, err = e.Verify(signatureData, sig)
  69. if err != nil || valid != true {
  70. return androidAttestationKey, nil, ErrInvalidAttestation.WithDetails(fmt.Sprintf("Error parsing public key: %+v\n", err))
  71. }
  72. // §8.4.3. Verify that the attestationChallenge field in the attestation certificate extension data is identical to clientDataHash.
  73. // attCert.Extensions
  74. var attExtBytes []byte
  75. for _, ext := range attCert.Extensions {
  76. if ext.Id.Equal([]int{1, 3, 6, 1, 4, 1, 11129, 2, 1, 17}) {
  77. attExtBytes = ext.Value
  78. }
  79. }
  80. if len(attExtBytes) == 0 {
  81. return androidAttestationKey, nil, ErrAttestationFormat.WithDetails("Attestation certificate extensions missing 1.3.6.1.4.1.11129.2.1.17")
  82. }
  83. // As noted in §8.4.1 (https://w3c.github.io/webauthn/#key-attstn-cert-requirements) the Android Key Attestation attestation certificate's
  84. // android key attestation certificate extension data is identified by the OID "1.3.6.1.4.1.11129.2.1.17".
  85. decoded := keyDescription{}
  86. _, err = asn1.Unmarshal([]byte(attExtBytes), &decoded)
  87. if err != nil {
  88. return androidAttestationKey, nil, ErrAttestationFormat.WithDetails("Unable to parse Android key attestation certificate extensions")
  89. }
  90. // Verify that the attestationChallenge field in the attestation certificate extension data is identical to clientDataHash.
  91. if 0 != bytes.Compare(decoded.AttestationChallenge, clientDataHash) {
  92. return androidAttestationKey, nil, ErrAttestationFormat.WithDetails("Attestation challenge not equal to clientDataHash")
  93. }
  94. // The AuthorizationList.allApplications field is not present on either authorization list (softwareEnforced nor teeEnforced), since PublicKeyCredential MUST be scoped to the RP ID.
  95. if nil != decoded.SoftwareEnforced.AllApplications || nil != decoded.TeeEnforced.AllApplications {
  96. return androidAttestationKey, nil, ErrAttestationFormat.WithDetails("Attestation certificate extensions contains all applications field")
  97. }
  98. // For the following, use only the teeEnforced authorization list if the RP wants to accept only keys from a trusted execution environment, otherwise use the union of teeEnforced and softwareEnforced.
  99. // The value in the AuthorizationList.origin field is equal to KM_ORIGIN_GENERATED. (which == 0)
  100. if KM_ORIGIN_GENERATED != decoded.SoftwareEnforced.Origin || KM_ORIGIN_GENERATED != decoded.TeeEnforced.Origin {
  101. return androidAttestationKey, nil, ErrAttestationFormat.WithDetails("Attestation certificate extensions contains authorization list with origin not equal KM_ORIGIN_GENERATED")
  102. }
  103. // The value in the AuthorizationList.purpose field is equal to KM_PURPOSE_SIGN. (which == 2)
  104. if !contains(decoded.SoftwareEnforced.Purpose, KM_PURPOSE_SIGN) && !contains(decoded.TeeEnforced.Purpose, KM_PURPOSE_SIGN) {
  105. return androidAttestationKey, nil, ErrAttestationFormat.WithDetails("Attestation certificate extensions contains authorization list with purpose not equal KM_PURPOSE_SIGN")
  106. }
  107. return androidAttestationKey, x5c, err
  108. }
  109. func contains(s []int, e int) bool {
  110. for _, a := range s {
  111. if a == e {
  112. return true
  113. }
  114. }
  115. return false
  116. }
  117. type keyDescription struct {
  118. AttestationVersion int
  119. AttestationSecurityLevel asn1.Enumerated
  120. KeymasterVersion int
  121. KeymasterSecurityLevel asn1.Enumerated
  122. AttestationChallenge []byte
  123. UniqueID []byte
  124. SoftwareEnforced authorizationList
  125. TeeEnforced authorizationList
  126. }
  127. type authorizationList struct {
  128. Purpose []int `asn1:"tag:1,explicit,set,optional"`
  129. Algorithm int `asn1:"tag:2,explicit,optional"`
  130. KeySize int `asn1:"tag:3,explicit,optional"`
  131. Digest []int `asn1:"tag:5,explicit,set,optional"`
  132. Padding []int `asn1:"tag:6,explicit,set,optional"`
  133. EcCurve int `asn1:"tag:10,explicit,optional"`
  134. RsaPublicExponent int `asn1:"tag:200,explicit,optional"`
  135. RollbackResistance interface{} `asn1:"tag:303,explicit,optional"`
  136. ActiveDateTime int `asn1:"tag:400,explicit,optional"`
  137. OriginationExpireDateTime int `asn1:"tag:401,explicit,optional"`
  138. UsageExpireDateTime int `asn1:"tag:402,explicit,optional"`
  139. NoAuthRequired interface{} `asn1:"tag:503,explicit,optional"`
  140. UserAuthType int `asn1:"tag:504,explicit,optional"`
  141. AuthTimeout int `asn1:"tag:505,explicit,optional"`
  142. AllowWhileOnBody interface{} `asn1:"tag:506,explicit,optional"`
  143. TrustedUserPresenceRequired interface{} `asn1:"tag:507,explicit,optional"`
  144. TrustedConfirmationRequired interface{} `asn1:"tag:508,explicit,optional"`
  145. UnlockedDeviceRequired interface{} `asn1:"tag:509,explicit,optional"`
  146. AllApplications interface{} `asn1:"tag:600,explicit,optional"`
  147. ApplicationID interface{} `asn1:"tag:601,explicit,optional"`
  148. CreationDateTime int `asn1:"tag:701,explicit,optional"`
  149. Origin int `asn1:"tag:702,explicit,optional"`
  150. RootOfTrust rootOfTrust `asn1:"tag:704,explicit,optional"`
  151. OsVersion int `asn1:"tag:705,explicit,optional"`
  152. OsPatchLevel int `asn1:"tag:706,explicit,optional"`
  153. AttestationApplicationID []byte `asn1:"tag:709,explicit,optional"`
  154. AttestationIDBrand []byte `asn1:"tag:710,explicit,optional"`
  155. AttestationIDDevice []byte `asn1:"tag:711,explicit,optional"`
  156. AttestationIDProduct []byte `asn1:"tag:712,explicit,optional"`
  157. AttestationIDSerial []byte `asn1:"tag:713,explicit,optional"`
  158. AttestationIDImei []byte `asn1:"tag:714,explicit,optional"`
  159. AttestationIDMeid []byte `asn1:"tag:715,explicit,optional"`
  160. AttestationIDManufacturer []byte `asn1:"tag:716,explicit,optional"`
  161. AttestationIDModel []byte `asn1:"tag:717,explicit,optional"`
  162. VendorPatchLevel int `asn1:"tag:718,explicit,optional"`
  163. BootPatchLevel int `asn1:"tag:719,explicit,optional"`
  164. }
  165. type rootOfTrust struct {
  166. verifiedBootKey []byte
  167. deviceLocked bool
  168. verifiedBootState verifiedBootState
  169. verifiedBootHash []byte
  170. }
  171. type verifiedBootState int
  172. const (
  173. Verified verifiedBootState = iota
  174. SelfSigned
  175. Unverified
  176. Failed
  177. )
  178. /**
  179. * The origin of a key (or pair), i.e. where it was generated. Note that KM_TAG_ORIGIN can be found
  180. * in either the hardware-enforced or software-enforced list for a key, indicating whether the key
  181. * is hardware or software-based. Specifically, a key with KM_ORIGIN_GENERATED in the
  182. * hardware-enforced list is guaranteed never to have existed outide the secure hardware.
  183. */
  184. type KM_KEY_ORIGIN int
  185. const (
  186. KM_ORIGIN_GENERATED = iota /* Generated in keymaster. Should not exist outside the TEE. */
  187. KM_ORIGIN_DERIVED /* Derived inside keymaster. Likely exists off-device. */
  188. KM_ORIGIN_IMPORTED /* Imported into keymaster. Existed as cleartext in Android. */
  189. KM_ORIGIN_UNKNOWN /* Keymaster did not record origin. This value can only be seen on
  190. * keys in a keymaster0 implementation. The keymaster0 adapter uses
  191. * this value to document the fact that it is unkown whether the key
  192. * was generated inside or imported into keymaster. */
  193. )
  194. /**
  195. * Possible purposes of a key (or pair).
  196. */
  197. type KM_PURPOSE int
  198. const (
  199. KM_PURPOSE_ENCRYPT = iota /* Usable with RSA, EC and AES keys. */
  200. KM_PURPOSE_DECRYPT /* Usable with RSA, EC and AES keys. */
  201. KM_PURPOSE_SIGN /* Usable with RSA, EC and HMAC keys. */
  202. KM_PURPOSE_VERIFY /* Usable with RSA, EC and HMAC keys. */
  203. KM_PURPOSE_DERIVE_KEY /* Usable with EC keys. */
  204. KM_PURPOSE_WRAP /* Usable with wrapped keys. */
  205. )