Number: SLIP-0022
Title: FIDO2 credential ID format for HD wallets
Type: Standard
Status: Final
Authors: Andrew R. Kozlik <andrew.kozlik@satoshilabs.com>
Pavol Rusnak <stick@satoshilabs.com>
Ondrej Vejpustek <ondrej.vejpustek@satoshilabs.com>
Created: 2019-07-19
This document describes an interoperable format for FIDO2 credential IDs for use in hierarchical deterministic wallets.
A FIDO2 credential ID is a probabilistically-unique byte sequence identifying a public key credential. It is generated by the authenticator during registration and stored by the relying party and optionally by the authenticator itself. One way to generate a credential ID is to encrypt all credential data so that only its managing authenticator can decrypt it. This kind of credential ID allows the authenticator to be nearly stateless, by having the relying party store any necessary state. Currently there is no standardized way of formatting the credential data into this kind of credential ID. This specification defines a format for credential IDs designed for use in hierarchical deterministic wallets, which are distinctive in that they derive a hierarchy of cryptographic keys from a single master secret, such as that defined in SLIP-0039 or the binary seed defined in BIP-0039. This format may also be used for U2F key handles.
A SLIP-0022 credential ID is a byte string between 33 and 65535 bytes in length of the following form:
Version | Initialization vector | Encrypted credential data | Authentication tag |
---|---|---|---|
4 bytes | 12 bytes | variable length | 16 bytes |
The version is the byte string "\xf1\xd0\x02\x00
" in case of FIDO2 credential IDs and "\xf1\xd0\x01\x01
" in case of U2F key handles. The initialization vector, which is used as input to the Chacha20Poly1305 cipher, is generated randomly for each new credential ID. The Poly1305 authentication tag is used to verify that the credential ID belongs to the authenticator.
Credential data members are encoded using a CBOR map (CBOR major type 5) with keys of unsigned integer type, similar to how CTAP2 command parameters and response members are encoded. The CBOR map must be encoded using the definite length variant. Some members are optional, therefore the length of the credential data map may vary.
The map keys and value types are specified below:
Member name | Key | Value type | Required | Definition |
---|---|---|---|---|
rpId | 1 | Text string (CBOR major type 3). | Required for FIDO2. | Relying party identifier. The "id" member of the rp parameter from the authenticatorMakeCredential request. |
rpName | 2 | Text string (CBOR major type 3). | Optional. | Relying party name. The "name" member of the rp parameter from the authenticatorMakeCredential request. |
userId | 3 | Byte string (CBOR major type 2). | Required for FIDO2. | User account ID. The "id" member of the user parameter from the authenticatorMakeCredential request. |
userName | 4 | Text string (CBOR major type 3). | Optional. | User account name. The "name" member of the user parameter from the authenticatorMakeCredential request. |
userDisplayName | 5 | Text string (CBOR major type 3). | Optional. | User account display name. The "displayName" member of the user parameter from the authenticatorMakeCredential request. In case of U2F the user may be prompted to enter a custom display name during registration. |
creationTime | 6 | Unsigned integer (CBOR major type 0). | Required for FIDO2. | Any value which allows credentials to be sorted by the time of their creation, such as the UNIX timestamp or the value of an incremental counter at the moment of creation. |
hmacSecret | 7 | Boolean (CBOR simple value 20 or 21). | Optional. False by default. | Indicates whether the credential was created with the hmac-secret extension set to true. |
useSignCount | 8 | Boolean (CBOR simple value 20 or 21). | Optional. False by default for FIDO2. Absent for U2F. | If false, all operations with the credential must use zero as the signature counter value. If true, the credential must use a signature counter which is incremented for each successful authenticatorGetAssertion operation. |
algorithm | 9 | Integer (CBOR major type 0 or 1) | Required if the "curve" field is present, otherwise optional. | The COSE identifier of the algorithm to be used for generating assertion signatures, as specified in the IANA COSE Algorithms Registry IANA-COSE-ALGS-REG. |
curve | 10 | Integer (CBOR major type 0 or 1) | Required if the "algorithm" field specifies an elliptic curve signature algorithm. | The COSE identifier of the elliptic curve to be used for generating assertion signatures, as specified in the IANA COSE Elliptic Curves Registry IANA-COSE-EC-REG. |
If the "algorithm" field is not present, then the algorithm defaults to ES256 (-7) and the curve defaults to P-256 (1).
Credential data MUST be encoded using the CTAP2 canonical CBOR encoding form as specified in Section 6 of the FIDO Client to Authenticator Protocol (CTAP) v2.0.
A credential data object in CBOR diagnostic notation:
credentialData = {
1: "example.com",
3: h'3082019330820138A0030201023082019330820138A003020102308201933082',
4: "johnpsmith@example.com",
6: 2,
7: true
};
would be CBOR encoded as follows:
a5 # map(5)
01 # unsigned(1) - rpId
6b # text(11)
6578616d706c652e636f6d # "example.com"
03 # unsigned(3) - userId
58 20 # bytes(32)
3082019330820138a003020102 # userid
3082019330820138a003020102 # ...
308201933082 # ...
04 # unsigned(4) - userName
76 # text(22)
6a6f686e70736d697468406578616d # "johnpsmith@example.com"
706c652e636f6d # ...
06 # unsigned(6) - creationTime
02 # unsigned(2) - the second credential created
# with this authenticator
07 # unsigned(7) - hmacSecret
f5 # primitive(21) - true
The CBOR encoded credential data is encrypted using Chacha20Poly1305 as defined in RFC 8439. In case of FIDO2 the SHA-256 hash of the rpId is used as the AAD input to the cipher. In case of U2F the application parameter (SHA-256 hash of the UTF-8 encoding of the application identity) is used as the AAD input to the cipher.
The encryption key k is the same for all credential IDs of the same version which are generated by an authenticator with a given master secret. The key is derived from the master secret using the SLIP-0021 method for hierarchical derivation of symmetric keys as:
k = Key(m/"SLIP-0022"/version/"Encryption key")
where version is the first four bytes of the credential ID, for example "\xf1\xd0\x02\x00
" in case of FIDO2 credential IDs.
The credential key pair used for generating assertion signatures is derived from a master secret and from the version and authentication tag of the credential ID using the SLIP-0010 key derivation scheme. The key path is computed from the authentication tag by splitting it into four 4-byte values A, B, C and D which are interpreted as 32-bit integers in big-endian byte order. The highest bit in each integer is set and the key path is:
m/10022'/version'/A'/B'/C'/D'
where version is the first four bytes of the credential ID interpreted as a 32-bit integer in big-endian byte order, for example "0xf1d00200" in case of FIDO2 credential IDs.
The CredRandom value used in the hmac-secret extension is derived from the master secret and the credential ID using the SLIP-0021 method for hierarchical derivation of symmetric keys as:
CredRandom = Key(m/"SLIP-0022"/version/"hmac-secret"/credentialId)
where version is the first four bytes of the credential ID.
The purpose of the signature counter is to aid relying parties in detecting cloned authenticators. Hierarchical deterministic wallets use a master secret, which can be backed-up and used for device recovery or legitimately used to create a clone of the device. Implementation of a signature counter impedes these use cases. Fortunately, FIDO2 allows authenticators to choose whether a credential will or will not use a signature counter. In the latter case the value of the signature counter remains constant at zero in all authenticatorMakeCredential responses and authenticatorGetAssertion responses. It is therefore recommended that authenticators do not create FIDO2 credentials with the "useSignCount" field set to true, unless required by the relying party.
The above does not apply to U2F key handles, because the U2F protocol requires the implementation of a signature counter. In case of U2F key handles the "useSignCount" field MUST NOT be present.
Unless stated otherwise, the values given below are encoded as strings containing two hexadecimal digits for each byte.
The following is an example of a credential ID and the corresponding keys belonging to an authenticator with the master secret S
. The credential data stored in the ID is the same as that given in the previous example:
credentialId = "f1d0020013e65c865634ad8abddf7a66df56ae7d8c3afd356f76426801508b2e579bcb3496fe6396a6002e3cd6d80f6359dfa9961e24c544bfc2f26acec1b8d878ba56727e1f6a7b5176c607552aea63a5abe5d826d69fab3063edfa0201d9a51013d69eddb2eff37acdd5963f"
S = "c76c4ac4f4e4a00d6b274d5c39c700bb4a7ddc04fbc6f78e85ca75007b5b495f74a9043eeb77bdd53aa6fc3a0e31462270316fa04b8c19114c8798706cd02ac8"
Note that S
is the binary seed obtained from the BIP-0039 mnemonic "all all all all all all all all all all all all" with an empty passphrase.
Credential data encryption key:
k = "5b60f6c30e5ef87a5f6756242c98f487da0ca7c173282737660e7bc320fad6cf"
Credential key pair, the private key is encoded as an integer in base 10:
privateKey = 17028406872725666093318073001284158176462610154049610120643103153631976435873
publicKey = "0451f0d4c307bc737c90ac605c6279f7d01e451798aa7b74df550fdb43a7760c7c02b5107fef42094d00f52a9b1e90afb90e1b9decbf15a6f13d4f882de857e2f4"
CredRandom value used in the hmac-secret extension:
CredRandom = "36a9b5d71c13ed54594474b54073af1fb03ea91cd056588909dae43ae2f35dbf"
CBOR encoding is used for serialization throughout the CTAP2 protocol. Therefore any authenticator supporting FIDO2 must support CBOR, making it the natural choice for encoding credential data. Encoding the data as a map allows easy handling of optional members, deprecating old members and introducing new members.
Generally the data members contained in the credential ID are stored alongside the credential ID or are transmitted on the same channel. Therefore, there does not appear to be a strong reason to encrypt the credential ID content. However, encrypting it allows the credential ID to be separated from the account information so that it is meaningless in isolation. Furthermore, in the future new data members requiring confidentiality may need to be added. In that case it is easier to manage encryption of the entire credential data map rather than its individual members.
The random 96-bit initialization vector ensures that even if a user were to generate 232 credentials, then the likelihood of a collision occurring would be below 2−32. Since every credential generation requires user consent, this length provides a sufficient guarantee of IV uniqueness.
The authentication tag is required to prevent an attacker from tampering with credential data and to avoid DOS attacks.
According to the Web Authentication specification the RP ID is provided by the client to the authenticator for all operations, and the authenticator ensures that credentials created by a relying party can only be used in operations requested by the same RP ID. Using the RP ID as AAD input to the cipher enforces this requirement.
The rationale behind using the authentication tag for the SLIP-0010 key path is as follows:
- The key path is not a confidential piece of information so it does not need to be encrypted or derived with the knowledge of a secret key.
- The key path should depend on the credential information and on the relying party identifier, which is always provided as AAD when computing the authentication tag.
- There should be an element of randomness so that different keys are generated even if the credential information is the same. Consider for example a relying party which insists on rotating the authentication key every three months and keeps track of old keys. The randomness is ensured by the fact that the IV is generated randomly.
- Key path collisions should be near impossible to ensure unlinkability of various online identities of the same user. The likelihood of a collision occurring between authentication tags is even smaller than in the case of initialization vectors.
- Web Authentication: An API for accessing Public Key Credentials Level 1, W3C Recommendation, 4 March 2019
- FIDO Client to Authenticator Protocol (CTAP) v2.0, Proposed Standard, January 30, 2019
- IANA-COSE-REG: IANA CBOR Object Signing and Encryption (COSE) Registries.
- RFC 7049: Concise Binary Object Representation (CBOR)
- SLIP-0010: Universal private key derivation from master private key
- SLIP-0021: Hierarchical derivation of symmetric keys