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.go 7.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. package protocol
  2. import (
  3. "crypto/sha256"
  4. "encoding/json"
  5. "fmt"
  6. "github.com/fxamacker/cbor/v2"
  7. )
  8. // From §5.2.1 (https://www.w3.org/TR/webauthn/#authenticatorattestationresponse)
  9. // "The authenticator's response to a client’s request for the creation
  10. // of a new public key credential. It contains information about the new credential
  11. // that can be used to identify it for later use, and metadata that can be used by
  12. // the WebAuthn Relying Party to assess the characteristics of the credential
  13. // during registration."
  14. // The initial unpacked 'response' object received by the relying party. This
  15. // contains the clientDataJSON object, which will be marshalled into
  16. // CollectedClientData, and the 'attestationObject', which contains
  17. // information about the authenticator, and the newly minted
  18. // public key credential. The information in both objects are used
  19. // to verify the authenticity of the ceremony and new credential
  20. type AuthenticatorAttestationResponse struct {
  21. // The byte slice of clientDataJSON, which becomes CollectedClientData
  22. AuthenticatorResponse
  23. // The byte slice version of AttestationObject
  24. // This attribute contains an attestation object, which is opaque to, and
  25. // cryptographically protected against tampering by, the client. The
  26. // attestation object contains both authenticator data and an attestation
  27. // statement. The former contains the AAGUID, a unique credential ID, and
  28. // the credential public key. The contents of the attestation statement are
  29. // determined by the attestation statement format used by the authenticator.
  30. // It also contains any additional information that the Relying Party's server
  31. // requires to validate the attestation statement, as well as to decode and
  32. // validate the authenticator data along with the JSON-serialized client data.
  33. AttestationObject URLEncodedBase64 `json:"attestationObject"`
  34. }
  35. // The parsed out version of AuthenticatorAttestationResponse.
  36. type ParsedAttestationResponse struct {
  37. CollectedClientData CollectedClientData
  38. AttestationObject AttestationObject
  39. }
  40. // From §6.4. Authenticators MUST also provide some form of attestation. The basic requirement is that the
  41. // authenticator can produce, for each credential public key, an attestation statement verifiable by the
  42. // WebAuthn Relying Party. Typically, this attestation statement contains a signature by an attestation
  43. // private key over the attested credential public key and a challenge, as well as a certificate or similar
  44. // data providing provenance information for the attestation public key, enabling the Relying Party to make
  45. // a trust decision. However, if an attestation key pair is not available, then the authenticator MUST
  46. // perform self attestation of the credential public key with the corresponding credential private key.
  47. // All this information is returned by authenticators any time a new public key credential is generated, in
  48. // the overall form of an attestation object. (https://www.w3.org/TR/webauthn/#attestation-object)
  49. //
  50. type AttestationObject struct {
  51. // The authenticator data, including the newly created public key. See AuthenticatorData for more info
  52. AuthData AuthenticatorData
  53. // The byteform version of the authenticator data, used in part for signature validation
  54. RawAuthData []byte `json:"authData"`
  55. // The format of the Attestation data.
  56. Format string `json:"fmt"`
  57. // The attestation statement data sent back if attestation is requested.
  58. AttStatement map[string]interface{} `json:"attStmt,omitempty"`
  59. }
  60. type attestationFormatValidationHandler func(AttestationObject, []byte) (string, []interface{}, error)
  61. var attestationRegistry = make(map[string]attestationFormatValidationHandler)
  62. // Using one of the locally registered attestation formats, handle validating the attestation
  63. // data provided by the authenticator (and in some cases its manufacturer)
  64. func RegisterAttestationFormat(format string, handler attestationFormatValidationHandler) {
  65. attestationRegistry[format] = handler
  66. }
  67. // Parse the values returned in the authenticator response and perform attestation verification
  68. // Step 8. This returns a fully decoded struct with the data put into a format that can be
  69. // used to verify the user and credential that was created
  70. func (ccr *AuthenticatorAttestationResponse) Parse() (*ParsedAttestationResponse, error) {
  71. var p ParsedAttestationResponse
  72. err := json.Unmarshal(ccr.ClientDataJSON, &p.CollectedClientData)
  73. if err != nil {
  74. return nil, ErrParsingData.WithInfo(err.Error())
  75. }
  76. err = cbor.Unmarshal(ccr.AttestationObject, &p.AttestationObject)
  77. if err != nil {
  78. return nil, ErrParsingData.WithInfo(err.Error())
  79. }
  80. // Step 8. Perform CBOR decoding on the attestationObject field of the AuthenticatorAttestationResponse
  81. // structure to obtain the attestation statement format fmt, the authenticator data authData, and
  82. // the attestation statement attStmt.
  83. err = p.AttestationObject.AuthData.Unmarshal(p.AttestationObject.RawAuthData)
  84. if err != nil {
  85. return nil, fmt.Errorf("error decoding auth data: %v", err)
  86. }
  87. if !p.AttestationObject.AuthData.Flags.HasAttestedCredentialData() {
  88. return nil, ErrAttestationFormat.WithInfo("Attestation missing attested credential data flag")
  89. }
  90. return &p, nil
  91. }
  92. // Verify - Perform Steps 9 through 14 of registration verification, delegating Steps
  93. func (attestationObject *AttestationObject) Verify(relyingPartyID string, clientDataHash []byte, verificationRequired bool) error {
  94. // Steps 9 through 12 are verified against the auth data.
  95. // These steps are identical to 11 through 14 for assertion
  96. // so we handle them with AuthData
  97. // Begin Step 9. Verify that the rpIdHash in authData is
  98. // the SHA-256 hash of the RP ID expected by the RP.
  99. rpIDHash := sha256.Sum256([]byte(relyingPartyID))
  100. // Handle Steps 9 through 12
  101. authDataVerificationError := attestationObject.AuthData.Verify(rpIDHash[:], nil, verificationRequired)
  102. if authDataVerificationError != nil {
  103. return authDataVerificationError
  104. }
  105. // Step 13. Determine the attestation statement format by performing a
  106. // USASCII case-sensitive match on fmt against the set of supported
  107. // WebAuthn Attestation Statement Format Identifier values. The up-to-date
  108. // list of registered WebAuthn Attestation Statement Format Identifier
  109. // values is maintained in the IANA registry of the same name
  110. // [WebAuthn-Registries] (https://www.w3.org/TR/webauthn/#biblio-webauthn-registries).
  111. // Since there is not an active registry yet, we'll check it against our internal
  112. // Supported types.
  113. // But first let's make sure attestation is present. If it isn't, we don't need to handle
  114. // any of the following steps
  115. if attestationObject.Format == "none" {
  116. if len(attestationObject.AttStatement) != 0 {
  117. return ErrAttestationFormat.WithInfo("Attestation format none with attestation present")
  118. }
  119. return nil
  120. }
  121. formatHandler, valid := attestationRegistry[attestationObject.Format]
  122. if !valid {
  123. return ErrAttestationFormat.WithInfo(fmt.Sprintf("Attestation format %s is unsupported", attestationObject.Format))
  124. }
  125. // Step 14. Verify that attStmt is a correct attestation statement, conveying a valid attestation signature, by using
  126. // the attestation statement format fmt’s verification procedure given attStmt, authData and the hash of the serialized
  127. // client data computed in step 7.
  128. attestationType, _, err := formatHandler(*attestationObject, clientDataHash)
  129. if err != nil {
  130. return err.(*Error).WithInfo(attestationType)
  131. }
  132. return nil
  133. }