From 898c88dc6d9852667afc7a2f1837ed6ad1c43050 Mon Sep 17 00:00:00 2001 From: Pol Henarejos Date: Wed, 8 Oct 2025 00:33:23 +0200 Subject: [PATCH] Migration to the new system of secure functions to derive keys based on OTP, if available, and pico_serial as a fallback. PIN is also an input vector, which defines a separated domain. PIN is used to derive encryption key, derive session key and derive verifier. From session key is derived encryption key. As a consequence, MKEK functionalities are not necessary anymore, since key device is handled by this new set directly. Some MKEK functions are left for compatibility purposes and for the silent migration to new format. It also applies for double_hash_pin and hash_multi, which are deprecated. Signed-off-by: Pol Henarejos --- pico-keys-sdk | 2 +- src/fido/cbor_client_pin.c | 135 ++++++++++++++++++++----------------- src/fido/fido.c | 96 +++++++++++++++----------- src/fido/fido.h | 2 +- src/fido/kek.c | 40 ----------- 5 files changed, 134 insertions(+), 141 deletions(-) diff --git a/pico-keys-sdk b/pico-keys-sdk index b3b2b67..c165ae4 160000 --- a/pico-keys-sdk +++ b/pico-keys-sdk @@ -1 +1 @@ -Subproject commit b3b2b67034334dfbd1030ed8dc6ac4d463faacd7 +Subproject commit c165ae4838bd2edcbac260cca3247979d9910edc diff --git a/src/fido/cbor_client_pin.c b/src/fido/cbor_client_pin.c index 703b156..78579a8 100644 --- a/src/fido/cbor_client_pin.c +++ b/src/fido/cbor_client_pin.c @@ -44,6 +44,7 @@ uint32_t max_usage_time_period = 600 * 1000; bool needs_power_cycle = false; static mbedtls_ecdh_context hkey; static bool hkey_init = false; +extern int encrypt_keydev_f1(const uint8_t keydev[32]); int beginUsingPinUvAuthToken(bool userIsPresent) { paut.user_present = userIsPresent; @@ -199,11 +200,7 @@ int decrypt(uint8_t protocol, const uint8_t *key, const uint8_t *in, uint16_t in return -1; } -int authenticate(uint8_t protocol, - const uint8_t *key, - const uint8_t *data, - size_t len, - uint8_t *sign) { +int authenticate(uint8_t protocol, const uint8_t *key, const uint8_t *data, size_t len, uint8_t *sign) { uint8_t hmac[32]; int ret = mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), key, 32, data, len, hmac); @@ -231,10 +228,10 @@ int verify(uint8_t protocol, const uint8_t *key, const uint8_t *data, uint16_t l return ret; } if (protocol == 1) { - return memcmp(sign, hmac, 16); + return ct_memcmp(sign, hmac, 16); } else if (protocol == 2) { - return memcmp(sign, hmac, 32); + return ct_memcmp(sign, hmac, 32); } return -1; } @@ -269,17 +266,15 @@ int pinUvAuthTokenUsageTimerObserver() { return 0; } -int check_mkek_encrypted(const uint8_t *dhash) { - if (file_get_size(ef_mkek) == MKEK_IV_SIZE + MKEK_KEY_SIZE) { - hash_multi(dhash, 16, session_pin); // Only for storing MKEK - uint8_t mkek[MKEK_SIZE] = {0}; - memcpy(mkek, file_get_data(ef_mkek), MKEK_IV_SIZE + MKEK_KEY_SIZE); - int ret = store_mkek(mkek); - mbedtls_platform_zeroize(mkek, sizeof(mkek)); - mbedtls_platform_zeroize(session_pin, sizeof(session_pin)); - if (ret != PICOKEY_OK) { - return CTAP2_ERR_PIN_AUTH_INVALID; - } +int check_keydev_encrypted(const uint8_t pin_token[32]) { + if (file_get_data(ef_keydev) && *file_get_data(ef_keydev) == 0x01) { + uint8_t tmp_keydev[61]; + tmp_keydev[0] = 0x02; // Change format to encrypted + encrypt_with_aad(pin_token, file_get_data(ef_keydev) + 1, 32, tmp_keydev + 1); + DEBUG_DATA(tmp_keydev, sizeof(tmp_keydev)); + file_put_data(ef_keydev, tmp_keydev, sizeof(tmp_keydev)); + mbedtls_platform_zeroize(tmp_keydev, sizeof(tmp_keydev)); + low_flash_available(); } return PICOKEY_OK; } @@ -294,11 +289,11 @@ int cbor_client_pin(const uint8_t *data, size_t len) { CborEncoder encoder, mapEncoder; CborValue map; CborError error = CborNoError; - CborByteString pinUvAuthParam = { 0 }, newPinEnc = { 0 }, pinHashEnc = { 0 }, kax = { 0 }, - kay = { 0 }; + CborByteString pinUvAuthParam = { 0 }, newPinEnc = { 0 }, pinHashEnc = { 0 }, kax = { 0 }, kay = { 0 }; CborCharString rpId = { 0 }; CBOR_CHECK(cbor_parser_init(data, len, 0, &parser, &map)); uint64_t val_c = 1; + uint8_t keydev[32] = {0}; if (hkey_init == false) { initialize(); } @@ -425,11 +420,12 @@ int cbor_client_pin(const uint8_t *data, size_t len) { hsh[1] = pin_len; hsh[2] = 1; // New format indicator mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), paddedNewPin, pin_len, dhash); - double_hash_pin_otp(dhash, 16, hsh + 3); + pin_derive_verifier(dhash, 16, hsh + 3); file_put_data(ef_pin, hsh, sizeof(hsh)); low_flash_available(); - ret = check_mkek_encrypted(dhash); + pin_derive_session(dhash, 16, session_pin); + ret = check_keydev_encrypted(session_pin); if (ret != PICOKEY_OK) { CBOR_ERROR(ret); } @@ -494,10 +490,10 @@ int cbor_client_pin(const uint8_t *data, size_t len) { double_hash_pin(paddedNewPin, 16, dhash); } else { - double_hash_pin_otp(paddedNewPin, 16, dhash); + pin_derive_verifier(paddedNewPin, 16, dhash); } - if (memcmp(dhash, file_get_data(ef_pin) + off, 32) != 0) { + if (ct_memcmp(dhash, file_get_data(ef_pin) + off, 32) != 0) { regenerate(); mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret)); if (retries == 0) { @@ -514,12 +510,25 @@ int cbor_client_pin(const uint8_t *data, size_t len) { if (off == 2) { // Upgrade pin file to new format pin_data[2] = 1; // New format indicator - double_hash_pin_otp(paddedNewPin, 16, pin_data + 3); + pin_derive_verifier(paddedNewPin, 16, pin_data + 3); + + hash_multi(paddedNewPin, 16, session_pin); + ret = load_keydev(keydev); + if (ret != PICOKEY_OK) { + CBOR_ERROR(CTAP2_ERR_PIN_INVALID); + } + encrypt_keydev_f1(keydev); } - hash_multi(paddedNewPin, 16, session_pin); + pin_derive_session(paddedNewPin, 16, session_pin); pin_data[0] = MAX_PIN_RETRIES; file_put_data(ef_pin, pin_data, sizeof(pin_data)); low_flash_available(); + + ret = check_keydev_encrypted(session_pin); + if (ret != PICOKEY_OK) { + CBOR_ERROR(ret); + } + new_pin_mismatches = 0; ret = decrypt((uint8_t)pinUvAuthProtocol, sharedSecret, newPinEnc.data, (uint16_t)newPinEnc.len, paddedNewPin); mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret)); @@ -541,35 +550,32 @@ int cbor_client_pin(const uint8_t *data, size_t len) { if (pin_len < minPin) { CBOR_ERROR(CTAP2_ERR_PIN_POLICY_VIOLATION); } + + // New PIN is valid and verified + ret = load_keydev(keydev); + if (ret != PICOKEY_OK) { + CBOR_ERROR(CTAP2_ERR_PIN_INVALID); + } + encrypt_keydev_f1(keydev); + + mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), paddedNewPin, pin_len, dhash); + pin_derive_session(dhash, 16, session_pin); + ret = check_keydev_encrypted(session_pin); + if (ret != PICOKEY_OK) { + CBOR_ERROR(ret); + } + low_flash_available(); + pin_data[0] = MAX_PIN_RETRIES; pin_data[1] = pin_len; pin_data[2] = 1; // New format indicator - mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), paddedNewPin, pin_len, dhash); - double_hash_pin_otp(dhash, 16, pin_data + 3); + pin_derive_verifier(dhash, 16, pin_data + 3); - if (file_has_data(ef_minpin) && file_get_data(ef_minpin)[1] == 1 && - memcmp(pin_data + 3, file_get_data(ef_pin) + 3, 32) == 0) { + if (file_has_data(ef_minpin) && file_get_data(ef_minpin)[1] == 1 && ct_memcmp(pin_data + 3, file_get_data(ef_pin) + 3, 32) == 0) { CBOR_ERROR(CTAP2_ERR_PIN_POLICY_VIOLATION); } - - uint8_t mkek[MKEK_SIZE] = {0}; - ret = load_mkek(mkek); - if (ret != PICOKEY_OK) { - CBOR_ERROR(ret); - } file_put_data(ef_pin, pin_data, sizeof(pin_data)); - ret = check_mkek_encrypted(dhash); - if (ret != PICOKEY_OK) { - CBOR_ERROR(ret); - } - - hash_multi(dhash, 16, session_pin); - ret = store_mkek(mkek); - mbedtls_platform_zeroize(mkek, sizeof(mkek)); - if (ret != PICOKEY_OK) { - CBOR_ERROR(ret); - } mbedtls_platform_zeroize(pin_data, sizeof(pin_data)); mbedtls_platform_zeroize(dhash, sizeof(dhash)); if (file_has_data(ef_minpin) && file_get_data(ef_minpin)[1] == 1) { @@ -642,11 +648,12 @@ int cbor_client_pin(const uint8_t *data, size_t len) { double_hash_pin(paddedNewPin, 16, dhash); } else { - double_hash_pin_otp(paddedNewPin, 16, dhash); + pin_derive_verifier(paddedNewPin, 16, dhash); } - if (memcmp(dhash, file_get_data(ef_pin) + off, 32) != 0) { + if (ct_memcmp(dhash, file_get_data(ef_pin) + off, 32) != 0) { regenerate(); mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret)); + mbedtls_platform_zeroize(dhash, sizeof(dhash)); if (retries == 0) { CBOR_ERROR(CTAP2_ERR_PIN_BLOCKED); } @@ -658,25 +665,31 @@ int cbor_client_pin(const uint8_t *data, size_t len) { CBOR_ERROR(CTAP2_ERR_PIN_INVALID); } } - - ret = check_mkek_encrypted(paddedNewPin); - if (ret != PICOKEY_OK) { - CBOR_ERROR(ret); - } - - hash_multi(paddedNewPin, 16, session_pin); - pin_data[0] = MAX_PIN_RETRIES; - new_pin_mismatches = 0; + mbedtls_platform_zeroize(dhash, sizeof(dhash)); if (off == 2) { // Upgrade pin file to new format pin_data[2] = 1; // New format indicator - double_hash_pin_otp(paddedNewPin, 16, pin_data + 3); + pin_derive_verifier(paddedNewPin, 16, pin_data + 3); + hash_multi(paddedNewPin, 16, session_pin); + ret = load_keydev(keydev); + if (ret != PICOKEY_OK) { + CBOR_ERROR(CTAP2_ERR_PIN_INVALID); + } + encrypt_keydev_f1(keydev); } + pin_derive_session(paddedNewPin, 16, session_pin); + ret = check_keydev_encrypted(session_pin); + if (ret != PICOKEY_OK) { + CBOR_ERROR(ret); + } + + pin_data[0] = MAX_PIN_RETRIES; + new_pin_mismatches = 0; + file_put_data(ef_pin, pin_data, sizeof(pin_data)); mbedtls_platform_zeroize(pin_data, sizeof(pin_data)); - mbedtls_platform_zeroize(dhash, sizeof(dhash)); low_flash_available(); file_t *ef_minpin = search_by_fid(EF_MINPINLEN, NULL, SPECIFY_EF); @@ -684,7 +697,6 @@ int cbor_client_pin(const uint8_t *data, size_t len) { CBOR_ERROR(CTAP2_ERR_PIN_INVALID); } uint8_t pinUvAuthToken_enc[32 + IV_SIZE], *pdata = NULL; - ; if (permissions & CTAP_PERMISSION_PCMR) { ppaut.permissions = CTAP_PERMISSION_PCMR; pdata = ppaut.data; @@ -722,6 +734,7 @@ err: CBOR_FREE_BYTE_STRING(kax); CBOR_FREE_BYTE_STRING(kay); CBOR_FREE_BYTE_STRING(rpId); + mbedtls_platform_zeroize(keydev, sizeof(keydev)); if (error != CborNoError) { if (error == CborErrorImproperValue) { return CTAP2_ERR_CBOR_UNEXPECTED_TYPE; diff --git a/src/fido/fido.c b/src/fido/fido.c index b9a0ef8..b05e1fb 100644 --- a/src/fido/fido.c +++ b/src/fido/fido.c @@ -204,7 +204,7 @@ int x509_create_cert(mbedtls_ecdsa_context *ecdsa, uint8_t *buffer, size_t buffe return ret; } -int load_keydev(uint8_t *key) { +int load_keydev(uint8_t key[32]) { if (has_keydev_dec == false && !file_has_data(ef_keydev)) { return PICOKEY_ERR_MEMORY_FATAL; } @@ -213,13 +213,39 @@ int load_keydev(uint8_t *key) { memcpy(key, keydev_dec, sizeof(keydev_dec)); } else { - memcpy(key, file_get_data(ef_keydev), file_get_size(ef_keydev)); - - if (mkek_decrypt(key, 32) != PICOKEY_OK) { - return PICOKEY_EXEC_ERROR; + uint16_t fid_size = file_get_size(ef_keydev); + if (fid_size == 32) { + memcpy(key, file_get_data(ef_keydev), 32); + if (mkek_decrypt(key, 32) != PICOKEY_OK) { + return PICOKEY_EXEC_ERROR; + } + if (otp_key_1 && aes_decrypt(otp_key_1, NULL, 32 * 8, PICO_KEYS_AES_MODE_CBC, key, 32) != PICOKEY_OK) { + return PICOKEY_EXEC_ERROR; + } } - if (otp_key_1 && aes_decrypt(otp_key_1, NULL, 32 * 8, PICO_KEYS_AES_MODE_CBC, key, 32) != PICOKEY_OK) { - return PICOKEY_EXEC_ERROR; + else if (fid_size == 33 || fid_size == 61) { + uint8_t format = *file_get_data(ef_keydev); + if (format == 0x01 || format == 0x02) { // Format indicator + if (format == 0x02) { + uint8_t tmp_key[61]; + memcpy(tmp_key, file_get_data(ef_keydev), sizeof(tmp_key)); + int ret = decrypt_with_aad(session_pin, tmp_key + 1, 60, key); + if (ret != PICOKEY_OK) { + return PICOKEY_EXEC_ERROR; + } + } + else { + memcpy(key, file_get_data(ef_keydev) + 1, 32); + } + uint8_t kbase[32]; + derive_kbase(kbase); + int ret = aes_decrypt(kbase, pico_serial_hash, 32 * 8, PICO_KEYS_AES_MODE_CBC, key, 32); + if (ret != PICOKEY_OK) { + mbedtls_platform_zeroize(kbase, sizeof(kbase)); + return PICOKEY_EXEC_ERROR; + } + mbedtls_platform_zeroize(kbase, sizeof(kbase)); + } } } @@ -315,6 +341,23 @@ int derive_key(const uint8_t *app_id, bool new_key, uint8_t *key_handle, int cur return r; } +int encrypt_keydev_f1(const uint8_t keydev[32]) { + uint8_t kdata[33] = {0}; + kdata[0] = 0x01; // Format indicator + memcpy(kdata + 1, keydev, 32); + uint8_t kbase[32]; + derive_kbase(kbase); + int ret = aes_encrypt(kbase, pico_serial_hash, 32 * 8, PICO_KEYS_AES_MODE_CBC, kdata + 1, 32); + mbedtls_platform_zeroize(kbase, sizeof(kbase)); + if (ret != PICOKEY_OK) { + return ret; + } + ret = file_put_data(ef_keydev, kdata, 33); + mbedtls_platform_zeroize(kdata, sizeof(kdata)); + low_flash_available(); + return ret; +} + int scan_files_fido() { ef_keydev = search_by_fid(EF_KEY_DEV, NULL, SPECIFY_EF); ef_keydev_enc = search_by_fid(EF_KEY_DEV_ENC, NULL, SPECIFY_EF); @@ -330,17 +373,16 @@ int scan_files_fido() { mbedtls_ecdsa_free(&ecdsa); return ret; } - uint8_t kdata[64]; + uint8_t keydev[32] = {0}; size_t key_size = 0; - ret = mbedtls_ecp_write_key_ext(&ecdsa, &key_size, kdata, sizeof(kdata)); - if (ret != PICOKEY_OK) { - return ret; + ret = mbedtls_ecp_write_key_ext(&ecdsa, &key_size, keydev, sizeof(keydev)); + if (ret != 0 || key_size != 32) { + mbedtls_platform_zeroize(keydev, sizeof(keydev)); + mbedtls_ecdsa_free(&ecdsa); + return ret != 0 ? ret : PICOKEY_EXEC_ERROR; } - if (otp_key_1) { - ret = aes_encrypt(otp_key_1, NULL, 32 * 8, PICO_KEYS_AES_MODE_CBC, kdata, 32); - } - ret = file_put_data(ef_keydev, kdata, (uint16_t)key_size); - mbedtls_platform_zeroize(kdata, sizeof(kdata)); + encrypt_keydev_f1(keydev); + mbedtls_platform_zeroize(keydev, sizeof(keydev)); mbedtls_ecdsa_free(&ecdsa); if (ret != PICOKEY_OK) { return ret; @@ -351,21 +393,6 @@ int scan_files_fido() { else { printf("FATAL ERROR: KEY DEV not found in memory!\r\n"); } - if (ef_mkek) { // No encrypted MKEK - if (!file_has_data(ef_mkek)) { - uint8_t mkek[MKEK_IV_SIZE + MKEK_KEY_SIZE]; - random_gen(NULL, mkek, sizeof(mkek)); - file_put_data(ef_mkek, mkek, sizeof(mkek)); - int ret = aes_encrypt_cfb_256(MKEK_KEY(mkek), MKEK_IV(mkek), file_get_data(ef_keydev), 32); - mbedtls_platform_zeroize(mkek, sizeof(mkek)); - if (ret != 0) { - printf("FATAL ERROR: MKEK encryption failed!\r\n"); - } - } - } - else { - printf("FATAL ERROR: MKEK not found in memory!\r\n"); - } ef_certdev = search_by_fid(EF_EE_DEV, NULL, SPECIFY_EF); if (ef_certdev) { if (!file_has_data(ef_certdev)) { @@ -409,13 +436,6 @@ int scan_files_fido() { printf("FATAL ERROR: Global counter not found in memory!\r\n"); } ef_pin = search_by_fid(EF_PIN, NULL, SPECIFY_EF); - if (file_get_size(ef_pin) == 18) { // Upgrade PIN storage - uint8_t pin_data[34] = { 0 }, dhash[32]; - memcpy(pin_data, file_get_data(ef_pin), 18); - double_hash_pin(pin_data + 2, 16, dhash); - memcpy(pin_data + 2, dhash, 32); - file_put_data(ef_pin, pin_data, 34); - } ef_authtoken = search_by_fid(EF_AUTHTOKEN, NULL, SPECIFY_EF); if (ef_authtoken) { if (!file_has_data(ef_authtoken)) { diff --git a/src/fido/fido.h b/src/fido/fido.h index 75e5928..bc42f8e 100644 --- a/src/fido/fido.h +++ b/src/fido/fido.h @@ -51,7 +51,7 @@ extern void init_fido(); extern mbedtls_ecp_group_id fido_curve_to_mbedtls(int curve); extern int mbedtls_curve_to_fido(mbedtls_ecp_group_id id); extern int fido_load_key(int curve, const uint8_t *cred_id, mbedtls_ecp_keypair *key); -extern int load_keydev(uint8_t *key); +extern int load_keydev(uint8_t key[32]); extern int encrypt(uint8_t protocol, const uint8_t *key, const uint8_t *in, uint16_t in_len, uint8_t *out); extern int decrypt(uint8_t protocol, const uint8_t *key, const uint8_t *in, uint16_t in_len, uint8_t *out); extern int ecdh(uint8_t protocol, const mbedtls_ecp_point *Q, uint8_t *sharedSecret); diff --git a/src/fido/kek.c b/src/fido/kek.c index 943217a..325dcca 100644 --- a/src/fido/kek.c +++ b/src/fido/kek.c @@ -85,46 +85,6 @@ void release_mkek(uint8_t *mkek) { mbedtls_platform_zeroize(mkek, MKEK_SIZE); } -int store_mkek(const uint8_t *mkek) { - uint8_t tmp_mkek[MKEK_SIZE]; - if (mkek == NULL) { - const uint8_t *rd = random_bytes_get(MKEK_IV_SIZE + MKEK_KEY_SIZE); - memcpy(tmp_mkek, rd, MKEK_IV_SIZE + MKEK_KEY_SIZE); - } - else { - memcpy(tmp_mkek, mkek, MKEK_SIZE); - } - if (otp_key_1) { - mkek_masked(tmp_mkek, otp_key_1); - } - *(uint32_t *) MKEK_CHECKSUM(tmp_mkek) = crc32c(MKEK_KEY(tmp_mkek), MKEK_KEY_SIZE); - uint8_t tmp_mkek_pin[MKEK_SIZE]; - memcpy(tmp_mkek_pin, tmp_mkek, MKEK_SIZE); - file_t *tf = search_file(EF_MKEK); - if (!tf) { - release_mkek(tmp_mkek); - release_mkek(tmp_mkek_pin); - return PICOKEY_ERR_FILE_NOT_FOUND; - } - aes_encrypt_cfb_256(session_pin, MKEK_IV(tmp_mkek_pin), MKEK_KEY(tmp_mkek_pin), MKEK_KEY_SIZE + MKEK_KEY_CS_SIZE); - file_put_data(tf, tmp_mkek_pin, MKEK_SIZE); - release_mkek(tmp_mkek_pin); - low_flash_available(); - release_mkek(tmp_mkek); - return PICOKEY_OK; -} - -int mkek_encrypt(uint8_t *data, uint16_t len) { - int r; - uint8_t mkek[MKEK_SIZE + 4]; - if ((r = load_mkek(mkek)) != PICOKEY_OK) { - return r; - } - r = aes_encrypt_cfb_256(MKEK_KEY(mkek), MKEK_IV(mkek), data, len); - release_mkek(mkek); - return r; -} - int mkek_decrypt(uint8_t *data, uint16_t len) { int r; uint8_t mkek[MKEK_SIZE + 4];