mirror of
https://github.com/polhenarejos/pico-hsm.git
synced 2026-08-23 05:06:53 +01:00
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 <pol.henarejos@cttc.es>
This commit is contained in:
+16
-10
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
+92
-4
@@ -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;
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#define _DKEK_H_
|
||||
|
||||
#include "crypto_utils.h"
|
||||
#include "files.h"
|
||||
#if defined(ENABLE_EMULATION) || defined(ESP_PLATFORM)
|
||||
#include <stdbool.h>
|
||||
#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;
|
||||
|
||||
+7
-19
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user