From 7da850f91afc202ee9a078d970ff2354bc4a0f07 Mon Sep 17 00:00:00 2001 From: Pol Henarejos Date: Thu, 16 Jul 2026 01:46:33 +0200 Subject: [PATCH] New key objects use a self-identifying AEAD record. Keep accepting the legacy CFB layout so firmware upgrades do not invalidate deployed keys. Signed-off-by: Pol Henarejos --- src/hsm/cmd_bip_slip.c | 26 +++++++---- src/hsm/cmd_cipher_sym.c | 11 ++--- src/hsm/cmd_decrypt_asym.c | 12 +++-- src/hsm/cmd_initialize.c | 9 +++- src/hsm/cmd_key_wrap.c | 5 +- src/hsm/cmd_signature.c | 1 + src/hsm/kek.c | 96 ++++++++++++++++++++++++++++++++++++-- src/hsm/kek.h | 6 +++ src/hsm/sc_hsm.c | 26 +++-------- 9 files changed, 144 insertions(+), 48 deletions(-) diff --git a/src/hsm/cmd_bip_slip.c b/src/hsm/cmd_bip_slip.c index 4329822..4629551 100644 --- a/src/hsm/cmd_bip_slip.c +++ b/src/hsm/cmd_bip_slip.c @@ -96,11 +96,21 @@ static int load_master_bip(uint16_t mid, mbedtls_ecp_keypair *ctx, uint8_t chain if (!file_has_data(ef)) { return PICOKEYS_ERR_FILE_NOT_FOUND; } - memcpy(mkey, file_get_data(ef), sizeof(mkey)); - int r = mkek_decrypt(mkey + 1, - sizeof(mkey) - 1); - if (r != PICOKEYS_OK) { - return PICOKEYS_EXEC_ERROR; + if (file_get_size(ef) == sizeof(mkey)) { + memcpy(mkey, file_get_data(ef), sizeof(mkey)); + if (mkek_decrypt(mkey + 1, sizeof(mkey) - 1) != PICOKEYS_OK) { + return PICOKEYS_EXEC_ERROR; + } + if (mkek_store_file(ef, mkey, sizeof(mkey)) != PICOKEYS_OK) { + return PICOKEYS_EXEC_ERROR; + } + flash_commit(); + } + else { + uint16_t mkey_len = sizeof(mkey); + if (mkek_load_file(ef, mkey, &mkey_len) != PICOKEYS_OK || mkey_len != sizeof(mkey)) { + return PICOKEYS_EXEC_ERROR; + } } if (mkey[0] == 0x1 || mkey[0] == 0x2) { if (mkey[0] == 0x1) { @@ -220,11 +230,7 @@ int cmd_bip_slip(void) { } mkey[0] = p1; file_t *ef = file_new(EF_MASTER_SEED | p2); - int r = mkek_encrypt(mkey + 1, sizeof(mkey) - 1); - if (r != PICOKEYS_OK) { - return SW_EXEC_ERROR(); - } - r = file_put_data(ef, mkey, sizeof(mkey)); + int r = mkek_store_file(ef, mkey, sizeof(mkey)); if (r != PICOKEYS_OK) { return SW_EXEC_ERROR(); } diff --git a/src/hsm/cmd_cipher_sym.c b/src/hsm/cmd_cipher_sym.c index 26b5557..8c41912 100644 --- a/src/hsm/cmd_cipher_sym.c +++ b/src/hsm/cmd_cipher_sym.c @@ -189,15 +189,14 @@ int cmd_cipher_sym(void) { uint16_t key_size = 0; uint8_t kdata[64] = {0}; //maximum AES key size if (!using_hd) { - key_size = file_get_size(ef); - if (key_size != 16 && key_size != 24 && key_size != 32 && key_size != 64) { - return SW_WRONG_DATA(); - } - memcpy(kdata, file_get_data(ef), key_size); - if (mkek_decrypt(kdata, key_size) != 0) { + key_size = sizeof(kdata); + if (mkek_load_file(ef, kdata, &key_size) != PICOKEYS_OK) { mbedtls_platform_zeroize(kdata, sizeof(kdata)); return SW_EXEC_ERROR(); } + if (key_size != 16 && key_size != 24 && key_size != 32 && key_size != 64) { + return SW_WRONG_DATA(); + } } if (algo == ALGO_AES_CBC_ENCRYPT || algo == ALGO_AES_CBC_DECRYPT) { if ((apdu.nc % 16) != 0) { diff --git a/src/hsm/cmd_decrypt_asym.c b/src/hsm/cmd_decrypt_asym.c index d61b911..47a1865 100644 --- a/src/hsm/cmd_decrypt_asym.c +++ b/src/hsm/cmd_decrypt_asym.c @@ -55,7 +55,7 @@ int cmd_decrypt_asym(void) { } return SW_EXEC_ERROR(); } - uint16_t key_size = file_get_size(ef); + uint16_t key_size = (uint16_t)mbedtls_rsa_get_len(&ctx); if (apdu.nc < key_size) { //needs padding memset(apdu.data + apdu.nc, 0, key_size - apdu.nc); } @@ -83,11 +83,13 @@ int cmd_decrypt_asym(void) { if (wait_button_pressed() == true) { //timeout return SW_SECURE_MESSAGE_EXEC_ERROR(); } - uint16_t key_size = file_get_size(ef); + uint16_t key_size = 67; uint8_t *kdata = (uint8_t *) calloc(1, key_size); - memcpy(kdata, file_get_data(ef), key_size); - if (mkek_decrypt(kdata, key_size) != 0) { - mbedtls_platform_zeroize(kdata, key_size); + if (!kdata) { + return SW_EXEC_ERROR(); + } + if (mkek_load_file(ef, kdata, &key_size) != PICOKEYS_OK) { + mbedtls_platform_zeroize(kdata, 67); free(kdata); return SW_EXEC_ERROR(); } diff --git a/src/hsm/cmd_initialize.c b/src/hsm/cmd_initialize.c index 4a9f2f4..4e07b63 100644 --- a/src/hsm/cmd_initialize.c +++ b/src/hsm/cmd_initialize.c @@ -183,7 +183,14 @@ int cmd_initialize(void) { return SW_EXEC_ERROR(); } int ret = 0; - if (ret_mkek != PICOKEYS_OK || !file_has_data(fdkey)) { + bool recreate_dev_key = ret_mkek != PICOKEYS_OK || !file_has_data(fdkey); + if (!recreate_dev_key) { + mbedtls_ecp_keypair existing_key; + mbedtls_ecp_keypair_init(&existing_key); + recreate_dev_key = load_private_key_ec(&existing_key, fdkey) != PICOKEYS_OK; + mbedtls_ecp_keypair_free(&existing_key); + } + if (recreate_dev_key) { mbedtls_ecdsa_context ecdsa; mbedtls_ecdsa_init(&ecdsa); mbedtls_ecp_group_id ec_id = MBEDTLS_ECP_DP_SECP256R1; diff --git a/src/hsm/cmd_key_wrap.c b/src/hsm/cmd_key_wrap.c index ad1a277..be01c17 100644 --- a/src/hsm/cmd_key_wrap.c +++ b/src/hsm/cmd_key_wrap.c @@ -87,9 +87,8 @@ int cmd_key_wrap(void) { return SW_SECURE_MESSAGE_EXEC_ERROR(); } - uint16_t key_size = file_get_size(ef), aes_type = PICOKEYS_KEY_AES; - memcpy(kdata_aes, file_get_data(ef), key_size); - if (mkek_decrypt(kdata_aes, key_size) != 0) { + uint16_t key_size = sizeof(kdata_aes), aes_type = PICOKEYS_KEY_AES; + if (mkek_load_file(ef, kdata_aes, &key_size) != PICOKEYS_OK) { return SW_EXEC_ERROR(); } if (key_size == 64) { diff --git a/src/hsm/cmd_signature.c b/src/hsm/cmd_signature.c index f850c40..7eaf60e 100644 --- a/src/hsm/cmd_signature.c +++ b/src/hsm/cmd_signature.c @@ -152,6 +152,7 @@ int cmd_signature(void) { } return SW_EXEC_ERROR(); } + key_size = (uint16_t)mbedtls_rsa_get_len(&ctx); tlv_ctx_t hash = {.len = (uint16_t)apdu.nc, .data = apdu.data}; if (p2 == ALGO_RSA_PKCS1) { //DigestInfo attached uint16_t nc = (uint16_t)apdu.nc; diff --git a/src/hsm/kek.c b/src/hsm/kek.c index c73b60f..cc88553 100644 --- a/src/hsm/kek.c +++ b/src/hsm/kek.c @@ -35,6 +35,8 @@ uint8_t mkek_mask[MKEK_KEY_SIZE]; bool has_mkek_mask = false; uint8_t pending_save_dkek = 0xff; +static const uint8_t mkek_object_magic[MKEK_OBJECT_MAGIC_SIZE] = { 0xF0, 'M', 'K', '4' }; + static void mkek_masked(uint8_t *mkek, const uint8_t *mask) { if (mask) { for (int i = 0; i < MKEK_KEY_SIZE; i++) { @@ -68,6 +70,7 @@ int load_mkek(uint8_t *mkek) { } uint16_t fid_size = file_get_size(ef); + bool legacy_mkek = fid_size == MKEK_SIZE_OLD; if (fid_size == MKEK_SIZE_OLD) { memcpy(mkek, file_get_data(ef), MKEK_SIZE_OLD); if (has_mkek_mask) { @@ -104,6 +107,12 @@ int load_mkek(uint8_t *mkek) { else { return PICOKEYS_ERR_FILE_NOT_FOUND; } + if (legacy_mkek) { + int ret = store_mkek(mkek); + if (ret != PICOKEYS_OK) { + return ret; + } + } return PICOKEYS_OK; } @@ -121,13 +130,31 @@ int mse_decrypt_ct(uint8_t *data, size_t len) { return ret; } +static bool dkek_is_complete(uint8_t id) { + file_t *domain = file_search(EF_KEY_DOMAIN); + if (!file_has_data(domain) || file_get_size(domain) < (uint16_t)(2 * id + 2)) { + return false; + } + const uint8_t *state = file_get_data(domain) + 2 * id; + return state[0] != 0 && state[0] != 0xff && state[1] >= state[0]; +} + static int load_dkek(uint8_t id, uint8_t *dkek) { file_t *tf = file_search(EF_DKEK + id); if (!file_has_data(tf)) { return PICOKEYS_ERR_FILE_NOT_FOUND; } - memcpy(dkek, file_get_data(tf), DKEK_KEY_SIZE); - return mkek_decrypt(dkek, DKEK_KEY_SIZE); + if (file_get_size(tf) == DKEK_KEY_SIZE) { + memcpy(dkek, file_get_data(tf), DKEK_KEY_SIZE); + int r = mkek_decrypt(dkek, DKEK_KEY_SIZE); + if (r == PICOKEYS_OK && dkek_is_complete(id)) { + r = store_dkek_key(id, dkek); + } + return r; + } + uint16_t dkek_len = DKEK_KEY_SIZE; + int r = mkek_load_file(tf, dkek, &dkek_len); + return r == PICOKEYS_OK && dkek_len == DKEK_KEY_SIZE ? PICOKEYS_OK : PICOKEYS_WRONG_DATA; } void release_mkek(uint8_t *mkek) { @@ -170,11 +197,10 @@ int store_dkek_key(uint8_t id, uint8_t *dkek) { if (!tf) { return PICOKEYS_ERR_FILE_NOT_FOUND; } - int r = mkek_encrypt(dkek, DKEK_KEY_SIZE); + int r = mkek_store_file(tf, dkek, DKEK_KEY_SIZE); if (r != PICOKEYS_OK) { return r; } - file_put_data(tf, dkek, DKEK_KEY_SIZE); flash_commit(); return PICOKEYS_OK; } @@ -274,6 +300,68 @@ int mkek_decrypt(uint8_t *data, uint16_t len) { return r; } +int mkek_store_file(file_t *file, const uint8_t *data, uint16_t len) { + if (!file || !data) { + return PICOKEYS_WRONG_DATA; + } + uint8_t *record = calloc(1, MKEK_OBJECT_HEADER_SIZE + 12 + len + 16); + if (!record) { + return PICOKEYS_ERR_MEMORY_FATAL; + } + uint8_t mkek[MKEK_SIZE]; + int r = load_mkek(mkek); + if (r == PICOKEYS_OK) { + record[0] = MKEK_OBJECT_FORMAT_VERSION; + memcpy(record + 1, mkek_object_magic, sizeof(mkek_object_magic)); + r = encrypt_with_aad(MKEK_KEY(mkek), data, len, PIN_KDF_V2, record + MKEK_OBJECT_HEADER_SIZE); + } + release_mkek(mkek); + if (r == PICOKEYS_OK) { + r = file_put_data(file, record, (uint16_t)(MKEK_OBJECT_HEADER_SIZE + 12 + len + 16)); + } + mbedtls_platform_zeroize(record, MKEK_OBJECT_HEADER_SIZE + 12 + len + 16); + free(record); + return r; +} + +int mkek_load_file(file_t *file, uint8_t *data, uint16_t *len) { + if (!file || !data || !len || !file_has_data(file)) { + return PICOKEYS_WRONG_DATA; + } + uint16_t record_len = file_get_size(file); + const uint8_t *record = file_get_data(file); + uint16_t plaintext_len = record_len; + bool aead = record_len > MKEK_OBJECT_HEADER_SIZE + 12 + 16 && record[0] == MKEK_OBJECT_FORMAT_VERSION && memcmp(record + 1, mkek_object_magic, sizeof(mkek_object_magic)) == 0; + if (aead) { + plaintext_len = (uint16_t)(record_len - MKEK_OBJECT_HEADER_SIZE - 12 - 16); + } + if (*len < plaintext_len) { + return PICOKEYS_WRONG_LENGTH; + } + uint8_t mkek[MKEK_SIZE]; + int r = load_mkek(mkek); + if (r == PICOKEYS_OK) { + if (aead) { + r = decrypt_with_aad(MKEK_KEY(mkek), record + MKEK_OBJECT_HEADER_SIZE, record_len - MKEK_OBJECT_HEADER_SIZE, PIN_KDF_V2, data); + } + else { + memcpy(data, record, plaintext_len); + r = aes_decrypt_cfb_256(MKEK_KEY(mkek), MKEK_IV(mkek), data, plaintext_len); + } + } + release_mkek(mkek); + if (r == PICOKEYS_OK) { + *len = plaintext_len; + if (!aead) { + r = mkek_store_file(file, data, plaintext_len); + if (r == PICOKEYS_OK) { + flash_commit(); + } + } + } + return r; +} + int dkek_encode_key(uint8_t id, void *key_ctx, int key_type, uint8_t *out, uint16_t *out_len, const uint8_t *allowed, uint16_t allowed_len) { if (!(key_type & PICOKEYS_KEY_RSA) && !(key_type & PICOKEYS_KEY_EC) && !(key_type & PICOKEYS_KEY_AES)) { return PICOKEYS_WRONG_DATA; diff --git a/src/hsm/kek.h b/src/hsm/kek.h index 8467877..dca9ff0 100644 --- a/src/hsm/kek.h +++ b/src/hsm/kek.h @@ -19,6 +19,7 @@ #define _DKEK_H_ #include "crypto_utils.h" +#include "files.h" #if defined(ENABLE_EMULATION) || defined(ESP_PLATFORM) #include #endif @@ -34,6 +35,8 @@ extern int import_dkek_share(uint8_t, const uint8_t *share); extern int dkek_kcv(uint8_t, uint8_t *kcv); extern int mkek_encrypt(uint8_t *data, uint16_t len); extern int mkek_decrypt(uint8_t *data, uint16_t len); +extern int mkek_store_file(file_t *file, const uint8_t *data, uint16_t len); +extern int mkek_load_file(file_t *file, uint8_t *data, uint16_t *len); extern int dkek_encode_key(uint8_t, void *key_ctx, int key_type, uint8_t *out, uint16_t *out_len, const uint8_t *, uint16_t); extern int dkek_type_key(const uint8_t *in); extern int dkek_decode_key(uint8_t, void *key_ctx, const uint8_t *in, uint16_t in_len, int *key_size_out, uint8_t **, uint16_t *); @@ -53,6 +56,9 @@ extern int dkek_decode_key(uint8_t, void *key_ctx, const uint8_t *in, uint16_t i #define MKEK_SIZE (MKEK_IV_SIZE + MKEK_KEY_SIZE) #define MKEK_FILE_SIZE (1 + (12 + MKEK_SIZE + 16)) +#define MKEK_OBJECT_FORMAT_VERSION 0x01 +#define MKEK_OBJECT_MAGIC_SIZE 4 +#define MKEK_OBJECT_HEADER_SIZE (1 + MKEK_OBJECT_MAGIC_SIZE) extern uint8_t mkek_mask[MKEK_KEY_SIZE]; extern bool has_mkek_mask; diff --git a/src/hsm/sc_hsm.c b/src/hsm/sc_hsm.c index 92e5344..8cd9e5c 100644 --- a/src/hsm/sc_hsm.c +++ b/src/hsm/sc_hsm.c @@ -592,11 +592,7 @@ int store_keys(void *key_ctx, int type, uint8_t key_id) { if (!fpk) { return PICOKEYS_ERR_MEMORY_FATAL; } - r = mkek_encrypt(kdata, key_size); - if (r != PICOKEYS_OK) { - return r; - } - r = file_put_data(fpk, kdata, (uint16_t)key_size); + r = mkek_store_file(fpk, kdata, key_size); if (r != PICOKEYS_OK) { return r; } @@ -660,15 +656,11 @@ int load_private_key_rsa(mbedtls_rsa_context *ctx, file_t *fkey) { } uint8_t kdata[4096 / 8]; - uint16_t key_size = file_get_size(fkey); - if (key_size == 0 || key_size > sizeof(kdata) || (key_size & 1)) { + uint16_t key_size = sizeof(kdata); + if (mkek_load_file(fkey, kdata, &key_size) != PICOKEYS_OK || + key_size == 0 || key_size > sizeof(kdata) || (key_size & 1)) { return PICOKEYS_WRONG_DATA; } - memcpy(kdata, file_get_data(fkey), key_size); - if (mkek_decrypt(kdata, key_size) != 0) { - mbedtls_platform_zeroize(kdata, sizeof(kdata)); - return PICOKEYS_EXEC_ERROR; - } if (mbedtls_mpi_read_binary(&ctx->P, kdata, key_size / 2) != 0) { mbedtls_platform_zeroize(kdata, sizeof(kdata)); mbedtls_rsa_free(ctx); @@ -709,15 +701,11 @@ int load_private_key_ec(mbedtls_ecp_keypair *ctx, file_t *fkey) { } uint8_t kdata[67]; // Worst case, 521 bit + 1byte - uint16_t key_size = file_get_size(fkey); - if (key_size < 2 || key_size > sizeof(kdata)) { + uint16_t key_size = sizeof(kdata); + if (mkek_load_file(fkey, kdata, &key_size) != PICOKEYS_OK || + key_size < 2 || key_size > sizeof(kdata)) { return PICOKEYS_WRONG_DATA; } - memcpy(kdata, file_get_data(fkey), key_size); - if (mkek_decrypt(kdata, key_size) != 0) { - mbedtls_platform_zeroize(kdata, sizeof(kdata)); - return PICOKEYS_EXEC_ERROR; - } mbedtls_ecp_group_id gid = kdata[0]; int r = mbedtls_ecp_read_key(gid, ctx, kdata + 1, key_size - 1); if (r != 0) {