To start building with Keycard, here are some core concepts that you need to understand:
Keycard is a BIP-32 compatible hardware wallet. You can ask it to sign a hash using the key at any BIP-32 path, given in the command itself.
The seed or any other private key (with the exception of those under the subtree defined by EIP-1581) cannot be exported from the card.
Keycard has no concept of transaction. It signs hashes using a supported signature scheme: ECDSA over secp256k1, and since 4.0 BIP-340 Schnorr over the same curve. The hash must be 256-bit (32 bytes) long.
Keycard allows loading seeds as defined by BIP-32. You can also load an extended keypair (keypair + chaining code).
You can use Keycard to generate entropy (in the form of BIP39 mnemonic) for a new keypair. The generated entropy will be returned but will not be persisted. You need to load the final seed/keypair yourself.
You can ask Keycard to generate and persist a keypair internally. Using this kind of setup will however prevent any form of backup so it must be used with caution, and likely not in everyday use.
Any operation involving the wallet or modifying card state (except factory reset) requires user authentication through a PIN. Since 4.0 there are no exceptions to this.
Every command except selecting the applet, opening the secure channel and factory reset travels inside a secure channel, which guarantees confidentiality, integrity and authenticity.
Since 4.0 there is no pairing step: the card authenticates itself with a certificate installed at the factory, so a client can talk to a card it has never seen.
All communication is initiated by the host, which sends a command and waits for the Keycard to respond. The card cannot initiate communication.
The communication protocol is as per ISO7816-4, using either the NFC or the contacted interface.
Keycard has a few well-defined lifecycle state. The card functionality depends on what state it is in. The states are
UNINITALIZED: In this state the card holds no secret and no authentication credential. The only possible operation is initialization, which is done through a single command (INIT). Initialization only setups user credentials but not wallet keys.
INITALIZED: In this state the card is able to setup secure communication with the host and is able to authenticate the user but is still missing a seed so it is not functional as a hardware wallet and as such this state is just an intermediate step. You can load the seed at this stage.
OPERATIONAL: In this state the card is fully operational and all of its functionality (exporting public keys, signing transactions, etc) is available. You can replace the loaded seed at any time. You can also remove the current seed, reverting to the INITIALIZED state
LOCKED: If user authentication fails for a specific number of times (3 by default) the card goes into the blocked state. In this state no operation requiring user authentication is available. From this state, you can either go back to OPERATIONAL by using a PUK (if setup by the user) or to UNINITALIZED by performing a factory reset
FROZEN: This state is similar to LOCKED, but from this state you can only transition back to UNITIALIZED by performing a factory reset. You get into this state by failing PUK authentication for a specific number of times (5 by default)
Since 4.0 the current state is reported by the status byte in the SELECT response, so a client can tell initialized from uninitialized without opening a secure channel.
Keycard uses several credentials as part of its security architecture. It is important to understand their role and usage.
User PIN: A 6-digit code that authenticates the user. This is the most sensitive piece of authentication since it enables performing priviledged operations like loading keys, signing, etc. It is first setup during initialization and can be changed at any time by the authenticated user. After a number of consecutive failed authentication attempts, the PIN is blocked. The number of attempts can only be set during card initialization, with the default being 3 (recommended value)
Duress PIN: Same as User PIN, but when the user authenticates using this PIN key derivation is altered to generate different keys than when using the regular PIN. This is done deterministically (no separate seed), so the same BIP32 paths yield a different set of keys/addresses while still being usable for transactions. The API does not explicitly indicate which PIN was used, so the authenticated session determines which wallet view is active. This PIN needs to be set during card initialization and can only be changed by authenticating with it and following the regular PIN change procedure.
PUK: A 12-digit code that can be used to reset a blocked user PIN. This is as sensitive as the PIN. It is first setup during initialization and can be changed at any time by the authenticated user. After a number of consecutive failed authentication attempts, the PUK is blocked permanently. The number of attempts can only be set during card initialization, with the default being 5 (recommended value). For UX purpose this value can be set to a random value during setup without showing it to the user or keeping any track of it. This effectively disables the PUK until the user explicitly sets it up (if they want).
On applet 3.x there is one more:
Pairing password: An alphanumeric string, processed with 50000 iterations of PBKDF2-HMAC-SHA256 with salt "Keycard Pairing Password Salt", needed during the pairing process. Pairing is how a 3.x card authenticates to a host and vice versa; the resulting pairing secret (a 1-byte index and a 32-byte secret) has to be stored by the host and the card has a limited number of pairing slots. Since pairing no longer exists on 4.0 — the factory certificate took its place — neither the password nor the secret exists either. Code that still supports 3.x cards keeps using the default password "KeycardDefaultPairing", which all SDKs and compatible wallets assume.
Card identity: every card is programmed with a persistent secp256k1 key pair, the authentikey, together with a 98-byte certificate signed by a certification authority. On 4.0 the certificate comes back with the SELECT response and is what lets a client prove the card is genuine before it sends any secret. On 3.x the same key is used through the IDENT command. See Card identity.
Instance UID: an identifier assigned randomly to the applet instance, present on 3.x cards and used to keep track of paired cards. Removed in 4.0 — the certificate public key is a stable card identifier and survives reinstalls.
Key UID: identifies the keypair loaded on Keycard. Multiple cards will share the same Key UID if the same seed is loaded. This ID is useful to check if the card has the expected keypair. Unlike the others, it identifies a wallet rather than a piece of hardware, so it changes whenever the seed changes.
Besides the BIP-32 wallet, a card can hold a couple of independent things: