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.

authenticator.go 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. package protocol
  2. import (
  3. "bytes"
  4. "encoding/binary"
  5. "fmt"
  6. "github.com/fxamacker/cbor/v2"
  7. )
  8. var minAuthDataLength = 37
  9. // Authenticators respond to Relying Party requests by returning an object derived from the
  10. // AuthenticatorResponse interface. See §5.2. Authenticator Responses
  11. // https://www.w3.org/TR/webauthn/#iface-authenticatorresponse
  12. type AuthenticatorResponse struct {
  13. // From the spec https://www.w3.org/TR/webauthn/#dom-authenticatorresponse-clientdatajson
  14. // This attribute contains a JSON serialization of the client data passed to the authenticator
  15. // by the client in its call to either create() or get().
  16. ClientDataJSON URLEncodedBase64 `json:"clientDataJSON"`
  17. }
  18. // AuthenticatorData From §6.1 of the spec.
  19. // The authenticator data structure encodes contextual bindings made by the authenticator. These bindings
  20. // are controlled by the authenticator itself, and derive their trust from the WebAuthn Relying Party's
  21. // assessment of the security properties of the authenticator. In one extreme case, the authenticator
  22. // may be embedded in the client, and its bindings may be no more trustworthy than the client data.
  23. // At the other extreme, the authenticator may be a discrete entity with high-security hardware and
  24. // software, connected to the client over a secure channel. In both cases, the Relying Party receives
  25. // the authenticator data in the same format, and uses its knowledge of the authenticator to make
  26. // trust decisions.
  27. //
  28. // The authenticator data, at least during attestation, contains the Public Key that the RP stores
  29. // and will associate with the user attempting to register.
  30. type AuthenticatorData struct {
  31. RPIDHash []byte `json:"rpid"`
  32. Flags AuthenticatorFlags `json:"flags"`
  33. Counter uint32 `json:"sign_count"`
  34. AttData AttestedCredentialData `json:"att_data"`
  35. ExtData []byte `json:"ext_data"`
  36. }
  37. type AttestedCredentialData struct {
  38. AAGUID []byte `json:"aaguid"`
  39. CredentialID []byte `json:"credential_id"`
  40. // The raw credential public key bytes received from the attestation data
  41. CredentialPublicKey []byte `json:"public_key"`
  42. }
  43. // AuthenticatorAttachment https://www.w3.org/TR/webauthn/#platform-attachment
  44. type AuthenticatorAttachment string
  45. const (
  46. // Platform - A platform authenticator is attached using a client device-specific transport, called
  47. // platform attachment, and is usually not removable from the client device. A public key credential
  48. // bound to a platform authenticator is called a platform credential.
  49. Platform AuthenticatorAttachment = "platform"
  50. // CrossPlatform A roaming authenticator is attached using cross-platform transports, called
  51. // cross-platform attachment. Authenticators of this class are removable from, and can "roam"
  52. // among, client devices. A public key credential bound to a roaming authenticator is called a
  53. // roaming credential.
  54. CrossPlatform AuthenticatorAttachment = "cross-platform"
  55. )
  56. // Authenticators may implement various transports for communicating with clients. This enumeration defines
  57. // hints as to how clients might communicate with a particular authenticator in order to obtain an assertion
  58. // for a specific credential. Note that these hints represent the WebAuthn Relying Party's best belief as to
  59. // how an authenticator may be reached. A Relying Party may obtain a list of transports hints from some
  60. // attestation statement formats or via some out-of-band mechanism; it is outside the scope of this
  61. // specification to define that mechanism.
  62. // See §5.10.4. Authenticator Transport https://www.w3.org/TR/webauthn/#transport
  63. type AuthenticatorTransport string
  64. const (
  65. // USB The authenticator should transport information over USB
  66. USB AuthenticatorTransport = "usb"
  67. // NFC The authenticator should transport information over Near Field Communication Protocol
  68. NFC AuthenticatorTransport = "nfc"
  69. // BLE The authenticator should transport information over Bluetooth
  70. BLE AuthenticatorTransport = "ble"
  71. // Internal the client should use an internal source like a TPM or SE
  72. Internal AuthenticatorTransport = "internal"
  73. )
  74. // A WebAuthn Relying Party may require user verification for some of its operations but not for others,
  75. // and may use this type to express its needs.
  76. // See §5.10.6. User Verification Requirement Enumeration https://www.w3.org/TR/webauthn/#userVerificationRequirement
  77. type UserVerificationRequirement string
  78. const (
  79. // VerificationRequired User verification is required to create/release a credential
  80. VerificationRequired UserVerificationRequirement = "required"
  81. // VerificationPreferred User verification is preferred to create/release a credential
  82. VerificationPreferred UserVerificationRequirement = "preferred" // This is the default
  83. // VerificationDiscouraged The authenticator should not verify the user for the credential
  84. VerificationDiscouraged UserVerificationRequirement = "discouraged"
  85. )
  86. // AuthenticatorFlags A byte of information returned during during ceremonies in the
  87. // authenticatorData that contains bits that give us information about the
  88. // whether the user was present and/or verified during authentication, and whether
  89. // there is attestation or extension data present. Bit 0 is the least significant bit.
  90. type AuthenticatorFlags byte
  91. // The bits that do not have flags are reserved for future use.
  92. const (
  93. // FlagUserPresent Bit 00000001 in the byte sequence. Tells us if user is present
  94. FlagUserPresent AuthenticatorFlags = 1 << iota // Referred to as UP
  95. _ // Reserved
  96. // FlagUserVerified Bit 00000100 in the byte sequence. Tells us if user is verified
  97. // by the authenticator using a biometric or PIN
  98. FlagUserVerified // Referred to as UV
  99. _ // Reserved
  100. _ // Reserved
  101. _ // Reserved
  102. // FlagAttestedCredentialData Bit 01000000 in the byte sequence. Indicates whether
  103. // the authenticator added attested credential data.
  104. FlagAttestedCredentialData // Referred to as AT
  105. // FlagHasExtension Bit 10000000 in the byte sequence. Indicates if the authenticator data has extensions.
  106. FlagHasExtensions // Referred to as ED
  107. )
  108. // UserPresent returns if the UP flag was set
  109. func (flag AuthenticatorFlags) UserPresent() bool {
  110. return (flag & FlagUserPresent) == FlagUserPresent
  111. }
  112. // UserVerified returns if the UV flag was set
  113. func (flag AuthenticatorFlags) UserVerified() bool {
  114. return (flag & FlagUserVerified) == FlagUserVerified
  115. }
  116. // HasAttestedCredentialData returns if the AT flag was set
  117. func (flag AuthenticatorFlags) HasAttestedCredentialData() bool {
  118. return (flag & FlagAttestedCredentialData) == FlagAttestedCredentialData
  119. }
  120. // HasExtensions returns if the ED flag was set
  121. func (flag AuthenticatorFlags) HasExtensions() bool {
  122. return (flag & FlagHasExtensions) == FlagHasExtensions
  123. }
  124. // Unmarshal will take the raw Authenticator Data and marshalls it into AuthenticatorData for further validation.
  125. // The authenticator data has a compact but extensible encoding. This is desired since authenticators can be
  126. // devices with limited capabilities and low power requirements, with much simpler software stacks than the client platform.
  127. // The authenticator data structure is a byte array of 37 bytes or more, and is laid out in this table:
  128. // https://www.w3.org/TR/webauthn/#table-authData
  129. func (a *AuthenticatorData) Unmarshal(rawAuthData []byte) error {
  130. if minAuthDataLength > len(rawAuthData) {
  131. err := ErrBadRequest.WithDetails("Authenticator data length too short")
  132. info := fmt.Sprintf("Expected data greater than %d bytes. Got %d bytes\n", minAuthDataLength, len(rawAuthData))
  133. return err.WithInfo(info)
  134. }
  135. a.RPIDHash = rawAuthData[:32]
  136. a.Flags = AuthenticatorFlags(rawAuthData[32])
  137. a.Counter = binary.BigEndian.Uint32(rawAuthData[33:37])
  138. remaining := len(rawAuthData) - minAuthDataLength
  139. if a.Flags.HasAttestedCredentialData() {
  140. if len(rawAuthData) > minAuthDataLength {
  141. a.unmarshalAttestedData(rawAuthData)
  142. attDataLen := len(a.AttData.AAGUID) + 2 + len(a.AttData.CredentialID) + len(a.AttData.CredentialPublicKey)
  143. remaining = remaining - attDataLen
  144. } else {
  145. return ErrBadRequest.WithDetails("Attested credential flag set but data is missing")
  146. }
  147. } else {
  148. if !a.Flags.HasExtensions() && len(rawAuthData) != 37 {
  149. return ErrBadRequest.WithDetails("Attested credential flag not set")
  150. }
  151. }
  152. if a.Flags.HasExtensions() {
  153. if remaining != 0 {
  154. a.ExtData = rawAuthData[len(rawAuthData)-remaining:]
  155. remaining -= len(a.ExtData)
  156. } else {
  157. return ErrBadRequest.WithDetails("Extensions flag set but extensions data is missing")
  158. }
  159. }
  160. if remaining != 0 {
  161. return ErrBadRequest.WithDetails("Leftover bytes decoding AuthenticatorData")
  162. }
  163. return nil
  164. }
  165. // If Attestation Data is present, unmarshall that into the appropriate public key structure
  166. func (a *AuthenticatorData) unmarshalAttestedData(rawAuthData []byte) {
  167. a.AttData.AAGUID = rawAuthData[37:53]
  168. idLength := binary.BigEndian.Uint16(rawAuthData[53:55])
  169. a.AttData.CredentialID = rawAuthData[55 : 55+idLength]
  170. a.AttData.CredentialPublicKey = unmarshalCredentialPublicKey(rawAuthData[55+idLength:])
  171. }
  172. // Unmarshall the credential's Public Key into CBOR encoding
  173. func unmarshalCredentialPublicKey(keyBytes []byte) []byte {
  174. var m interface{}
  175. cbor.Unmarshal(keyBytes, &m)
  176. rawBytes, _ := cbor.Marshal(m)
  177. return rawBytes
  178. }
  179. // ResidentKeyRequired - Require that the key be private key resident to the client device
  180. func ResidentKeyRequired() *bool {
  181. required := true
  182. return &required
  183. }
  184. // ResidentKeyUnrequired - Do not require that the private key be resident to the client device.
  185. func ResidentKeyUnrequired() *bool {
  186. required := false
  187. return &required
  188. }
  189. // Verify on AuthenticatorData handles Steps 9 through 12 for Registration
  190. // and Steps 11 through 14 for Assertion.
  191. func (a *AuthenticatorData) Verify(rpIdHash, appIDHash []byte, userVerificationRequired bool) error {
  192. // Registration Step 9 & Assertion Step 11
  193. // Verify that the RP ID hash in authData is indeed the SHA-256
  194. // hash of the RP ID expected by the RP.
  195. if !bytes.Equal(a.RPIDHash[:], rpIdHash) && !bytes.Equal(a.RPIDHash[:], appIDHash) {
  196. return ErrVerification.WithInfo(fmt.Sprintf("RP Hash mismatch. Expected %s and Received %s\n", a.RPIDHash, rpIdHash))
  197. }
  198. // Registration Step 10 & Assertion Step 12
  199. // Verify that the User Present bit of the flags in authData is set.
  200. if !a.Flags.UserPresent() {
  201. return ErrVerification.WithInfo(fmt.Sprintln("User presence flag not set by authenticator"))
  202. }
  203. // Registration Step 11 & Assertion Step 13
  204. // If user verification is required for this assertion, verify that
  205. // the User Verified bit of the flags in authData is set.
  206. if userVerificationRequired && !a.Flags.UserVerified() {
  207. return ErrVerification.WithInfo(fmt.Sprintln("User verification required but flag not set by authenticator"))
  208. }
  209. // Registration Step 12 & Assertion Step 14
  210. // Verify that the values of the client extension outputs in clientExtensionResults
  211. // and the authenticator extension outputs in the extensions in authData are as
  212. // expected, considering the client extension input values that were given as the
  213. // extensions option in the create() call. In particular, any extension identifier
  214. // values in the clientExtensionResults and the extensions in authData MUST be also be
  215. // present as extension identifier values in the extensions member of options, i.e., no
  216. // extensions are present that were not requested. In the general case, the meaning
  217. // of "are as expected" is specific to the Relying Party and which extensions are in use.
  218. // This is not yet fully implemented by the spec or by browsers
  219. return nil
  220. }