// Copyright 2019 SoloKeys Developers // // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be // copied, modified, or distributed except according to those terms. #ifndef _CTAP_H #define _CTAP_H #include "cbor.h" #define CTAP_MAKE_CREDENTIAL 0x01 #define CTAP_GET_ASSERTION 0x02 #define CTAP_CANCEL 0x03 #define CTAP_GET_INFO 0x04 #define CTAP_CLIENT_PIN 0x06 #define CTAP_RESET 0x07 #define GET_NEXT_ASSERTION 0x08 #define CTAP_CBOR_CRED_MGMT 0x0A #define CTAP_VENDOR_FIRST 0x40 #define CTAP_CBOR_CRED_MGMT_PRE 0x41 #define CTAP_VENDOR_LAST 0xBF #define MC_clientDataHash 0x01 #define MC_rp 0x02 #define MC_user 0x03 #define MC_pubKeyCredParams 0x04 #define MC_excludeList 0x05 #define MC_extensions 0x06 #define MC_options 0x07 #define MC_pinAuth 0x08 #define MC_pinProtocol 0x09 #define GA_rpId 0x01 #define GA_clientDataHash 0x02 #define GA_allowList 0x03 #define GA_extensions 0x04 #define GA_options 0x05 #define GA_pinAuth 0x06 #define GA_pinProtocol 0x07 #define CM_cmd 0x01 #define CM_cmdMetadata 0x01 #define CM_cmdRPBegin 0x02 #define CM_cmdRPNext 0x03 #define CM_cmdRKBegin 0x04 #define CM_cmdRKNext 0x05 #define CM_cmdRKDelete 0x06 #define CM_subCommandParams 0x02 #define CM_subCommandRpId 0x01 #define CM_subCommandCred 0x02 #define CM_pinProtocol 0x03 #define CM_pinAuth 0x04 #define CP_pinProtocol 0x01 #define CP_subCommand 0x02 #define CP_cmdGetRetries 0x01 #define CP_cmdGetKeyAgreement 0x02 #define CP_cmdSetPin 0x03 #define CP_cmdChangePin 0x04 #define CP_cmdGetPinToken 0x05 #define CP_keyAgreement 0x03 #define CP_pinAuth 0x04 #define CP_newPinEnc 0x05 #define CP_pinHashEnc 0x06 #define CP_getKeyAgreement 0x07 #define CP_getRetries 0x08 #define EXT_HMAC_SECRET_COSE_KEY 0x01 #define EXT_HMAC_SECRET_SALT_ENC 0x02 #define EXT_HMAC_SECRET_SALT_AUTH 0x03 #define EXT_HMAC_SECRET_REQUESTED 0x01 #define EXT_HMAC_SECRET_PARSED 0x02 #define EXT_CRED_PROTECT_INVALID 0x00 #define EXT_CRED_PROTECT_OPTIONAL 0x01 #define EXT_CRED_PROTECT_OPTIONAL_WITH_CREDID 0x02 #define EXT_CRED_PROTECT_REQUIRED 0x03 #define CREDID_ALG_ES256 0x0 #define CREDID_ALG_EDDSA 0x1 #define RESP_versions 0x1 #define RESP_extensions 0x2 #define RESP_aaguid 0x3 #define RESP_options 0x4 #define RESP_maxMsgSize 0x5 #define RESP_pinProtocols 0x6 #define RESP_fmt 0x01 #define RESP_authData 0x02 #define RESP_attStmt 0x03 #define RESP_credential 0x01 #define RESP_signature 0x03 #define RESP_publicKeyCredentialUserEntity 0x04 #define RESP_numberOfCredentials 0x05 #define RESP_keyAgreement 0x01 #define RESP_pinToken 0x02 #define RESP_retries 0x03 #define PARAM_clientDataHash (1 << 0) #define PARAM_rp (1 << 1) #define PARAM_user (1 << 2) #define PARAM_pubKeyCredParams (1 << 3) #define PARAM_excludeList (1 << 4) #define PARAM_extensions (1 << 5) #define PARAM_options (1 << 6) #define PARAM_pinAuth (1 << 7) #define PARAM_pinProtocol (1 << 8) #define PARAM_rpId (1 << 9) #define PARAM_allowList (1 << 10) #define MC_requiredMask (0x0f) #define CLIENT_DATA_HASH_SIZE 32 //sha256 hash #define DOMAIN_NAME_MAX_SIZE 253 #define RP_NAME_LIMIT 32 // application limit, name parameter isn't needed. #define USER_ID_MAX_SIZE 64 #define USER_NAME_LIMIT 65 // Must be minimum of 64 bytes but can be more. #define DISPLAY_NAME_LIMIT 32 // Must be minimum of 64 bytes but can be more. #define ICON_LIMIT 128 // Must be minimum of 64 bytes but can be more. #define CTAP_MAX_MESSAGE_SIZE 1200 #define CREDENTIAL_RK_FLASH_PAD 2 // size of RK should be 8-byte aligned to store in flash easily. #define CREDENTIAL_TAG_SIZE 16 #define CREDENTIAL_NONCE_SIZE (16 + CREDENTIAL_RK_FLASH_PAD) #define CREDENTIAL_COUNTER_SIZE (4) #define CREDENTIAL_ENC_SIZE 176 // pad to multiple of 16 bytes #define PUB_KEY_CRED_PUB_KEY 0x01 #define PUB_KEY_CRED_CTAP1 0x41 #define PUB_KEY_CRED_CUSTOM 0x42 #define PUB_KEY_CRED_UNKNOWN 0x3F #define CREDENTIAL_IS_SUPPORTED 1 #define CREDENTIAL_NOT_SUPPORTED 0 #define ALLOW_LIST_MAX_SIZE 20 #define NEW_PIN_ENC_MAX_SIZE 256 // includes NULL terminator #define NEW_PIN_ENC_MIN_SIZE 64 #define NEW_PIN_MAX_SIZE 64 #define NEW_PIN_MIN_SIZE 4 #define CTAP_RESPONSE_BUFFER_SIZE 4096 #define PIN_LOCKOUT_ATTEMPTS 8 // Number of attempts total #define PIN_BOOT_ATTEMPTS 3 // number of attempts per boot #define CTAP2_UP_DELAY_MS 29000 typedef struct { uint8_t id[USER_ID_MAX_SIZE]; uint8_t id_size; uint8_t name[USER_NAME_LIMIT]; uint8_t displayName[DISPLAY_NAME_LIMIT]; uint8_t icon[ICON_LIMIT]; }__attribute__((packed)) CTAP_userEntity; typedef struct { uint8_t tag[CREDENTIAL_TAG_SIZE]; union { uint8_t nonce[CREDENTIAL_NONCE_SIZE]; struct { uint8_t _pad[CREDENTIAL_NONCE_SIZE - 4]; uint32_t value; }__attribute__((packed)) metadata; }__attribute__((packed)) entropy; uint8_t rpIdHash[32]; uint32_t count; }__attribute__((packed)) CredentialId; struct __attribute__((packed)) Credential { CredentialId id; CTAP_userEntity user; }; typedef struct { CredentialId id; CTAP_userEntity user; // Maximum amount of "extra" space in resident key. uint8_t rpId[48]; uint8_t rpIdSize; } __attribute__((packed)) CTAP_residentKey; typedef struct { uint8_t type; struct Credential credential; } CTAP_credentialDescriptor; typedef struct { uint8_t aaguid[16]; uint8_t credLenH; uint8_t credLenL; CredentialId id; } __attribute__((packed)) CTAP_attestHeader; typedef struct { uint8_t rpIdHash[32]; uint8_t flags; uint32_t signCount; } __attribute__((packed)) CTAP_authDataHeader; typedef struct { CTAP_authDataHeader head; CTAP_attestHeader attest; } __attribute__((packed)) CTAP_authData; typedef struct { uint8_t data[CTAP_RESPONSE_BUFFER_SIZE]; uint16_t data_size; uint16_t length; } CTAP_RESPONSE; struct rpId { uint8_t id[DOMAIN_NAME_MAX_SIZE + 1]; // extra for NULL termination size_t size; uint8_t name[RP_NAME_LIMIT]; }; typedef struct { struct{ uint8_t x[32]; uint8_t y[32]; } pubkey; int kty; int crv; } COSE_key; typedef struct { uint8_t saltLen; uint8_t saltEnc[64]; uint8_t saltAuth[32]; COSE_key keyAgreement; struct Credential * credential; } CTAP_hmac_secret; typedef struct { uint8_t hmac_secret_present; CTAP_hmac_secret hmac_secret; uint32_t cred_protect; } CTAP_extensions; typedef struct { CTAP_userEntity user; uint8_t publicKeyCredentialType; int32_t COSEAlgorithmIdentifier; uint8_t rk; } CTAP_credInfo; typedef struct { uint32_t paramsParsed; uint8_t clientDataHash[CLIENT_DATA_HASH_SIZE]; struct rpId rp; CTAP_credInfo credInfo; CborValue excludeList; size_t excludeListSize; uint8_t uv; uint8_t up; uint8_t pinAuth[16]; uint8_t pinAuthPresent; // pinAuthEmpty is true iff an empty bytestring was provided as pinAuth. // This is exclusive with |pinAuthPresent|. It exists because an empty // pinAuth is a special signal to block for touch. See // https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html#using-pinToken-in-authenticatorMakeCredential uint8_t pinAuthEmpty; int pinProtocol; CTAP_extensions extensions; } CTAP_makeCredential; typedef struct { uint32_t paramsParsed; uint8_t clientDataHash[CLIENT_DATA_HASH_SIZE]; uint8_t clientDataHashPresent; struct rpId rp; int credLen; uint8_t rk; uint8_t uv; uint8_t up; uint8_t pinAuth[16]; uint8_t pinAuthPresent; // pinAuthEmpty is true iff an empty bytestring was provided as pinAuth. // This is exclusive with |pinAuthPresent|. It exists because an empty // pinAuth is a special signal to block for touch. See // https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html#using-pinToken-in-authenticatorGetAssertion uint8_t pinAuthEmpty; int pinProtocol; CTAP_credentialDescriptor * creds; uint8_t allowListPresent; CTAP_extensions extensions; } CTAP_getAssertion; typedef struct { int cmd; struct { uint8_t rpIdHash[32]; CTAP_credentialDescriptor credentialDescriptor; } subCommandParams; struct { uint8_t cmd; uint8_t subCommandParamsCborCopy[sizeof(CTAP_credentialDescriptor) + 16]; } hashed; uint32_t subCommandParamsCborSize; uint8_t pinAuth[16]; uint8_t pinAuthPresent; int pinProtocol; } CTAP_credMgmt; typedef struct { int pinProtocol; int subCommand; COSE_key keyAgreement; uint8_t keyAgreementPresent; uint8_t pinAuth[16]; uint8_t pinAuthPresent; uint8_t newPinEnc[NEW_PIN_ENC_MAX_SIZE]; int newPinEncSize; uint8_t pinHashEnc[16]; uint8_t pinHashEncPresent; _Bool getKeyAgreement; _Bool getRetries; } CTAP_clientPin; struct _getAssertionState { // Room for both authData struct and extensions struct { CTAP_authDataHeader authData; uint8_t extensions[80]; } __attribute__((packed)) buf; CTAP_extensions extensions; uint8_t clientDataHash[CLIENT_DATA_HASH_SIZE]; CTAP_credentialDescriptor creds[ALLOW_LIST_MAX_SIZE]; uint8_t lastcmd; uint32_t count; uint32_t index; uint32_t time; uint8_t user_verified; uint8_t customCredId[256]; uint8_t customCredIdSize; }; void ctap_response_init(CTAP_RESPONSE * resp); uint8_t ctap_request(uint8_t * pkt_raw, int length, CTAP_RESPONSE * resp); // Encodes R,S signature to 2 der sequence of two integers. Sigder must be at least 72 bytes. // @return length of der signature int ctap_encode_der_sig(uint8_t const * const in_sigbuf, uint8_t * const out_sigder); // Run ctap related power-up procedures (init pinToken, generate shared secret) void ctap_init(); // Resets state between different accesses of different applications void ctap_reset_state(); uint8_t ctap_add_pin_if_verified(uint8_t * pinTokenEnc, uint8_t * platform_pubkey, uint8_t * pinHashEnc); uint8_t ctap_update_pin_if_verified(uint8_t * pinEnc, int len, uint8_t * platform_pubkey, uint8_t * pinAuth, uint8_t * pinHashEnc); void ctap_update_pin(uint8_t * pin, int len); uint8_t ctap_decrement_pin_attempts(); int8_t ctap_leftover_pin_attempts(); void ctap_reset_pin_attempts(); uint8_t ctap_is_pin_set(); uint8_t ctap_pin_matches(uint8_t * pin, int len); void ctap_reset(); int8_t ctap_device_locked(); int8_t ctap_device_boot_locked(); // Key storage API // Return length of key at index. 0 if not exist. uint16_t ctap_key_len(uint8_t index); // See error codes in storage.h int8_t ctap_store_key(uint8_t index, uint8_t * key, uint16_t len); int8_t ctap_load_key(uint8_t index, uint8_t * key); uint16_t ctap_key_len(uint8_t index); #define PIN_TOKEN_SIZE 16 extern uint8_t PIN_TOKEN[PIN_TOKEN_SIZE]; extern uint8_t KEY_AGREEMENT_PUB[64]; void lock_device_permanently(); void ctap_load_external_keys(uint8_t * keybytes); #endif