diff --git a/picokeys_sdk_import.cmake b/picokeys_sdk_import.cmake index d884c30..f57b0ad 100644 --- a/picokeys_sdk_import.cmake +++ b/picokeys_sdk_import.cmake @@ -170,6 +170,11 @@ set(MBEDTLS_SOURCES ${CMAKE_CURRENT_LIST_DIR}/third-party/mbedtls/library/pk.c ${CMAKE_CURRENT_LIST_DIR}/third-party/mbedtls/library/pk_wrap.c ${CMAKE_CURRENT_LIST_DIR}/third-party/mbedtls/library/pkwrite.c + ${CMAKE_CURRENT_LIST_DIR}/third-party/mbedtls/library/x509.c + ${CMAKE_CURRENT_LIST_DIR}/third-party/mbedtls/library/x509_crt.c + ${CMAKE_CURRENT_LIST_DIR}/third-party/mbedtls/library/pkparse.c + ${CMAKE_CURRENT_LIST_DIR}/third-party/mbedtls/library/pk_ecc.c + ${CMAKE_CURRENT_LIST_DIR}/third-party/mbedtls/library/pkcs12.c ) if(ENABLE_EDDSA) @@ -338,6 +343,7 @@ list(APPEND PICOKEYS_SOURCES ${CMAKE_CURRENT_LIST_DIR}/src/button.c ${CMAKE_CURRENT_LIST_DIR}/src/led/led.c ${CMAKE_CURRENT_LIST_DIR}/src/signal.c + ${CMAKE_CURRENT_LIST_DIR}/src/vault.c ) if(ESP_PLATFORM) diff --git a/src/fs/vault_container.c b/src/fs/vault_container.c index 67f109b..d7abe3b 100644 --- a/src/fs/vault_container.c +++ b/src/fs/vault_container.c @@ -21,79 +21,13 @@ #include #include "picokeys.h" -#include "button.h" -#include "random.h" -#include "serial.h" #include "flash.h" -#include "led/led.h" -#if defined(ESP_PLATFORM) -#include "compat/esp_compat.h" -#else -#include "compat/board.h" -#endif -#include "mbedtls/chachapoly.h" -#include "mbedtls/ecdh.h" -#include "mbedtls/ecp.h" -#include "mbedtls/gcm.h" -#include "mbedtls/hkdf.h" -#include "mbedtls/md.h" -#include "mbedtls/pk.h" -#include "mbedtls/x509_crt.h" static const uint8_t vault_container_magic[4] = { 'K', 'V', 'W', '1' }; -static const uint8_t vault_id_domain[] = "PicoKeys Vault ID v1"; -static const uint8_t vault_enroll_info[] = "PicoKeys Vault enrollment v1"; -static const uint8_t picokeys_vault_ca_der[] = { - 0x30, 0x82, 0x01, 0xEB, 0x30, 0x82, 0x01, 0x6B, 0xA0, 0x03, 0x02, 0x01, - 0x02, 0x02, 0x14, 0x2B, 0x37, 0x6A, 0xC8, 0x98, 0x74, 0xE5, 0x0E, 0x80, - 0x1F, 0xB9, 0x61, 0xCD, 0x25, 0x80, 0x48, 0x17, 0xD6, 0x33, 0xA1, 0x30, - 0x05, 0x06, 0x03, 0x2B, 0x65, 0x71, 0x30, 0x3C, 0x31, 0x0B, 0x30, 0x09, - 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x45, 0x53, 0x31, 0x11, 0x30, - 0x0F, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x0C, 0x08, 0x50, 0x69, 0x63, 0x6F, - 0x4B, 0x65, 0x79, 0x73, 0x31, 0x1A, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, - 0x03, 0x0C, 0x11, 0x50, 0x69, 0x63, 0x6F, 0x4B, 0x65, 0x79, 0x73, 0x20, - 0x56, 0x61, 0x75, 0x6C, 0x74, 0x20, 0x43, 0x41, 0x30, 0x1E, 0x17, 0x0D, - 0x32, 0x36, 0x30, 0x38, 0x30, 0x35, 0x31, 0x36, 0x34, 0x34, 0x35, 0x36, - 0x5A, 0x17, 0x0D, 0x33, 0x36, 0x30, 0x38, 0x30, 0x32, 0x31, 0x36, 0x34, - 0x34, 0x35, 0x36, 0x5A, 0x30, 0x3C, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, - 0x55, 0x04, 0x06, 0x13, 0x02, 0x45, 0x53, 0x31, 0x11, 0x30, 0x0F, 0x06, - 0x03, 0x55, 0x04, 0x0A, 0x0C, 0x08, 0x50, 0x69, 0x63, 0x6F, 0x4B, 0x65, - 0x79, 0x73, 0x31, 0x1A, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0C, - 0x11, 0x50, 0x69, 0x63, 0x6F, 0x4B, 0x65, 0x79, 0x73, 0x20, 0x56, 0x61, - 0x75, 0x6C, 0x74, 0x20, 0x43, 0x41, 0x30, 0x43, 0x30, 0x05, 0x06, 0x03, - 0x2B, 0x65, 0x71, 0x03, 0x3A, 0x00, 0xE4, 0x0E, 0x8C, 0x62, 0xC6, 0xD3, - 0x6B, 0x06, 0xC4, 0x0A, 0x54, 0x40, 0x7C, 0x82, 0x1F, 0xDD, 0xAE, 0x93, - 0xF2, 0x88, 0x00, 0x9F, 0xDB, 0x10, 0x67, 0x9A, 0x32, 0x47, 0x62, 0xCE, - 0x92, 0x2A, 0xE2, 0x94, 0x2F, 0x40, 0xF4, 0xEC, 0x0A, 0xFE, 0x72, 0xA4, - 0x18, 0x5D, 0x20, 0x4D, 0x55, 0x97, 0x46, 0xE5, 0x94, 0x7D, 0x11, 0xF0, - 0x5C, 0xE6, 0x00, 0xA3, 0x66, 0x30, 0x64, 0x30, 0x1F, 0x06, 0x03, 0x55, - 0x1D, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xAE, 0xFB, 0xD5, 0x12, - 0x05, 0xBA, 0x61, 0xD7, 0x67, 0xE6, 0xAC, 0x78, 0x2E, 0x68, 0xD4, 0x22, - 0xFA, 0xC7, 0xDC, 0x26, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1D, 0x13, 0x01, - 0x01, 0xFF, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xFF, 0x02, 0x01, 0x00, - 0x30, 0x0E, 0x06, 0x03, 0x55, 0x1D, 0x0F, 0x01, 0x01, 0xFF, 0x04, 0x04, - 0x03, 0x02, 0x01, 0x06, 0x30, 0x1D, 0x06, 0x03, 0x55, 0x1D, 0x0E, 0x04, - 0x16, 0x04, 0x14, 0xAE, 0xFB, 0xD5, 0x12, 0x05, 0xBA, 0x61, 0xD7, 0x67, - 0xE6, 0xAC, 0x78, 0x2E, 0x68, 0xD4, 0x22, 0xFA, 0xC7, 0xDC, 0x26, 0x30, - 0x05, 0x06, 0x03, 0x2B, 0x65, 0x71, 0x03, 0x73, 0x00, 0x0B, 0xB4, 0x4F, - 0x45, 0x1C, 0x36, 0x77, 0xC1, 0x58, 0xDE, 0x39, 0xC0, 0x29, 0xA0, 0x7C, - 0x9F, 0x8F, 0x75, 0xC2, 0x9E, 0xAE, 0x12, 0x41, 0x00, 0xC8, 0xC9, 0x45, - 0xD1, 0xC0, 0xA6, 0x9A, 0x1D, 0xFA, 0x75, 0xE9, 0xB8, 0x82, 0x00, 0xE3, - 0x81, 0xCF, 0x74, 0x35, 0x59, 0x7F, 0x70, 0x06, 0x3A, 0xEC, 0xDF, 0x52, - 0x42, 0x53, 0x0D, 0xC3, 0x3B, 0x80, 0xF1, 0x1E, 0x3F, 0xC4, 0xAD, 0xC8, - 0xCA, 0x07, 0x4E, 0xBD, 0x5E, 0x35, 0xB7, 0x54, 0x63, 0x08, 0x43, 0x4B, - 0xB1, 0xCC, 0x7F, 0x1A, 0x45, 0x4C, 0xE1, 0x34, 0x57, 0x89, 0x57, 0xAA, - 0x08, 0xD5, 0xF6, 0x54, 0xC5, 0xE7, 0x49, 0xC7, 0xBA, 0xD7, 0x79, 0xAE, - 0xD6, 0x11, 0x05, 0x7A, 0xEF, 0x38, 0x97, 0x05, 0x96, 0x13, 0xC6, 0x95, - 0x01, 0x3A, 0x00, -}; - -static uint8_t vault_enroll_private[PICOKEYS_VAULT_X448_BYTES]; -static uint8_t vault_enroll_public[PICOKEYS_VAULT_X448_BYTES]; -static uint8_t vault_enroll_challenge[PICOKEYS_VAULT_ENROLL_CHALLENGE_BYTES]; -static bool vault_enroll_active; -static bool vault_enroll_button_accepted; +static bool vault_legacy_record_valid(const uint8_t *data, size_t data_len, size_t record_size) { + return data_len == record_size && (record_size != PICOKEYS_VAULT_RECORD_SIZE || picokeys_vault_record_valid(data, data_len)); +} bool picokeys_vault_container_valid(const uint8_t *data, size_t data_len, size_t record_size) { if (!data || record_size == 0 || data_len < PICOKEYS_VAULT_CONTAINER_HEADER_SIZE || memcmp(data, vault_container_magic, sizeof(vault_container_magic)) != 0 || data[4] != 1) { @@ -129,7 +63,7 @@ const uint8_t *picokeys_vault_find_record(const file_t *file, uint8_t app_id, si const uint8_t *data = file_get_data(file); size_t data_len = file_get_size(file); - if (app_id == 0 && data_len == record_size) { + if (app_id == 0 && vault_legacy_record_valid(data, data_len, record_size)) { return data; } if (!picokeys_vault_container_valid(data, data_len, record_size)) { @@ -174,7 +108,7 @@ int picokeys_vault_store_record(file_t *file, uint8_t app_id, const_byte_array_t else { const uint8_t *data = file_get_data(file); size_t data_len = file_get_size(file); - if (data_len == record.len) { + if (vault_legacy_record_valid(data, data_len, record.len)) { memcpy(scratch, vault_container_magic, sizeof(vault_container_magic)); scratch[4] = 1; scratch[5] = 1; @@ -273,410 +207,23 @@ int picokeys_vault_store_kvault(file_t *file, uint8_t app_id, const uint8_t wrap return ret; } -int picokeys_vault_layer_key(const uint8_t key[PICOKEYS_VAULT_KEY_SIZE], const uint8_t vault_id[PICOKEYS_VAULT_KEY_SIZE], const uint8_t credential_hash[PICOKEYS_VAULT_KEY_SIZE], uint8_t algorithm, uint8_t layer, uint8_t out[PICOKEYS_VAULT_KEY_SIZE]) { - if (!key || !vault_id || !credential_hash || !out) { - return PICOKEYS_ERR_NULL_PARAM; +int picokeys_vault_clear_wrapper(file_t *file, uint8_t app_id) { + if (!file) { + return PICOKEYS_OK; } - - uint8_t info[sizeof(vault_enroll_info) - 1 + PICOKEYS_VAULT_KEY_SIZE + 2]; - memcpy(info, vault_enroll_info, sizeof(vault_enroll_info) - 1); - memcpy(info + sizeof(vault_enroll_info) - 1, credential_hash, PICOKEYS_VAULT_KEY_SIZE); - info[sizeof(vault_enroll_info) - 1 + PICOKEYS_VAULT_KEY_SIZE] = algorithm; - info[sizeof(vault_enroll_info) - 1 + PICOKEYS_VAULT_KEY_SIZE + 1] = layer; - int ret = mbedtls_hkdf(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), vault_id, PICOKEYS_VAULT_KEY_SIZE, key, PICOKEYS_VAULT_KEY_SIZE, info, sizeof(info), out, PICOKEYS_VAULT_KEY_SIZE); - mbedtls_platform_zeroize(info, sizeof(info)); - return ret == 0 ? PICOKEYS_OK : PICOKEYS_EXEC_ERROR; -} - -int picokeys_vault_encrypt_layer(uint8_t algorithm, const uint8_t key[PICOKEYS_VAULT_KEY_SIZE], const uint8_t nonce[PICOKEYS_VAULT_BLOB_NONCE_SIZE], const uint8_t *aad, size_t aad_len, const uint8_t *input, size_t input_len, uint8_t *output, uint8_t tag[PICOKEYS_VAULT_BLOB_TAG_SIZE]) { - if (!key || !nonce || (aad_len > 0 && !aad) || (input_len > 0 && (!input || !output)) || !tag) { - return PICOKEYS_ERR_NULL_PARAM; - } - - int ret = -1; - if (algorithm == PICOKEYS_VAULT_ALGORITHM_CHACHAPOLY) { - mbedtls_chachapoly_context chatx; - mbedtls_chachapoly_init(&chatx); - ret = mbedtls_chachapoly_setkey(&chatx, key); - if (ret == 0) { - ret = mbedtls_chachapoly_encrypt_and_tag(&chatx, input_len, nonce, aad, aad_len, input, output, tag); - } - mbedtls_chachapoly_free(&chatx); - } - else if (algorithm == PICOKEYS_VAULT_ALGORITHM_AESGCM) { - mbedtls_gcm_context gcm; - mbedtls_gcm_init(&gcm); - ret = mbedtls_gcm_setkey(&gcm, MBEDTLS_CIPHER_ID_AES, key, 256); - if (ret == 0) { - ret = mbedtls_gcm_crypt_and_tag(&gcm, MBEDTLS_GCM_ENCRYPT, input_len, nonce, PICOKEYS_VAULT_BLOB_NONCE_SIZE, aad, aad_len, input, output, PICOKEYS_VAULT_BLOB_TAG_SIZE, tag); - } - mbedtls_gcm_free(&gcm); - } - return ret == 0 ? PICOKEYS_OK : PICOKEYS_EXEC_ERROR; -} - -int picokeys_vault_decrypt_layer(uint8_t algorithm, const uint8_t key[PICOKEYS_VAULT_KEY_SIZE], const uint8_t nonce[PICOKEYS_VAULT_BLOB_NONCE_SIZE], const uint8_t *aad, size_t aad_len, const uint8_t *input, size_t input_len, const uint8_t tag[PICOKEYS_VAULT_BLOB_TAG_SIZE], uint8_t *output) { - if (!key || !nonce || (aad_len > 0 && !aad) || (input_len > 0 && (!input || !output)) || !tag) { - return PICOKEYS_ERR_NULL_PARAM; - } - - int ret = -1; - if (algorithm == PICOKEYS_VAULT_ALGORITHM_CHACHAPOLY) { - mbedtls_chachapoly_context chatx; - mbedtls_chachapoly_init(&chatx); - ret = mbedtls_chachapoly_setkey(&chatx, key); - if (ret == 0) { - ret = mbedtls_chachapoly_auth_decrypt(&chatx, input_len, nonce, aad, aad_len, tag, input, output); - } - mbedtls_chachapoly_free(&chatx); - } - else if (algorithm == PICOKEYS_VAULT_ALGORITHM_AESGCM) { - mbedtls_gcm_context gcm; - mbedtls_gcm_init(&gcm); - ret = mbedtls_gcm_setkey(&gcm, MBEDTLS_CIPHER_ID_AES, key, 256); - if (ret == 0) { - ret = mbedtls_gcm_auth_decrypt(&gcm, input_len, nonce, PICOKEYS_VAULT_BLOB_NONCE_SIZE, aad, aad_len, tag, PICOKEYS_VAULT_BLOB_TAG_SIZE, input, output); - } - mbedtls_gcm_free(&gcm); - } - return ret == 0 ? PICOKEYS_OK : PICOKEYS_VERIFICATION_FAILED; -} - -int picokeys_vault_hash_kvault(const uint8_t kvault[PICOKEYS_VAULT_KEY_SIZE], uint8_t vault_id[PICOKEYS_VAULT_KEY_SIZE]) { - if (!kvault || !vault_id) { - return PICOKEYS_ERR_NULL_PARAM; - } - - uint8_t input[sizeof(vault_id_domain) - 1 + PICOKEYS_VAULT_KEY_SIZE] = { 0 }; - memcpy(input, vault_id_domain, sizeof(vault_id_domain) - 1); - memcpy(input + sizeof(vault_id_domain) - 1, kvault, PICOKEYS_VAULT_KEY_SIZE); - hash256(CONST_BYTE_ARRAY(input, sizeof(input)), vault_id); - mbedtls_platform_zeroize(input, sizeof(input)); - return PICOKEYS_OK; -} - -int picokeys_vault_clear_wrappers(file_t *file, uint8_t preserve_app_id) { - if (!file || !file_has_data(file)) { + if (!file_has_data(file)) { + meta_delete_no_commit(file->fid); return PICOKEYS_OK; } const uint8_t *data = file_get_data(file); size_t data_len = file_get_size(file); if (picokeys_vault_record_valid(data, data_len)) { - return PICOKEYS_OK; - } - if (!picokeys_vault_container_valid(data, data_len, PICOKEYS_VAULT_RECORD_SIZE)) { - return PICOKEYS_WRONG_DATA; - } - - const uint8_t *record = picokeys_vault_find_record(file, preserve_app_id, PICOKEYS_VAULT_RECORD_SIZE); - if (!record) { - return flash_clear_file(file); - } - - uint8_t legacy_record[PICOKEYS_VAULT_RECORD_SIZE] = { 0 }; - memcpy(legacy_record, record, sizeof(legacy_record)); - int ret = file_put_data(file, CONST_BYTE_ARRAY(legacy_record, sizeof(legacy_record))); - mbedtls_platform_zeroize(legacy_record, sizeof(legacy_record)); - return ret; -} - -int picokeys_vault_x448_generate(uint8_t private_key[PICOKEYS_VAULT_X448_BYTES], uint8_t public_key[PICOKEYS_VAULT_X448_BYTES]) { - if (!private_key || !public_key) { - return PICOKEYS_ERR_NULL_PARAM; - } - - mbedtls_ecp_keypair key; - size_t private_len = 0; - size_t public_len = 0; - mbedtls_ecp_keypair_init(&key); - int ret = mbedtls_ecp_gen_key(MBEDTLS_ECP_DP_CURVE448, &key, random_fill_iterator, NULL); - if (ret == 0) { - ret = mbedtls_ecp_write_key_ext(&key, &private_len, private_key, PICOKEYS_VAULT_X448_BYTES); - } - if (ret == 0) { - ret = mbedtls_ecp_point_write_binary(&key.MBEDTLS_PRIVATE(grp), &key.MBEDTLS_PRIVATE(Q), MBEDTLS_ECP_PF_UNCOMPRESSED, &public_len, public_key, PICOKEYS_VAULT_X448_BYTES); - } - mbedtls_ecp_keypair_free(&key); - if (ret != 0) { - return PICOKEYS_EXEC_ERROR; - } - return private_len == PICOKEYS_VAULT_X448_BYTES && public_len == PICOKEYS_VAULT_X448_BYTES ? PICOKEYS_OK : PICOKEYS_WRONG_LENGTH; -} - -int picokeys_vault_x448_shared(const uint8_t private_key[PICOKEYS_VAULT_X448_BYTES], const uint8_t peer_public[PICOKEYS_VAULT_X448_BYTES], uint8_t shared[PICOKEYS_VAULT_X448_BYTES]) { - if (!private_key || !peer_public || !shared) { - return PICOKEYS_ERR_NULL_PARAM; - } - - mbedtls_ecdh_context ecdh; - mbedtls_ecp_keypair ours; - mbedtls_ecp_keypair theirs; - size_t shared_len = 0; - mbedtls_ecdh_init(&ecdh); - mbedtls_ecp_keypair_init(&ours); - mbedtls_ecp_keypair_init(&theirs); - int ret = mbedtls_ecp_group_load(&ours.MBEDTLS_PRIVATE(grp), MBEDTLS_ECP_DP_CURVE448); - if (ret == 0) { - ret = mbedtls_ecp_group_load(&theirs.MBEDTLS_PRIVATE(grp), MBEDTLS_ECP_DP_CURVE448); - } - if (ret == 0) { - ret = mbedtls_ecp_read_key(MBEDTLS_ECP_DP_CURVE448, &ours, private_key, PICOKEYS_VAULT_X448_BYTES); - } - if (ret == 0) { - ret = mbedtls_ecp_point_read_binary(&theirs.MBEDTLS_PRIVATE(grp), &theirs.MBEDTLS_PRIVATE(Q), peer_public, PICOKEYS_VAULT_X448_BYTES); - } - if (ret == 0) { - ret = mbedtls_ecdh_setup(&ecdh, MBEDTLS_ECP_DP_CURVE448); - } - if (ret == 0) { - ret = mbedtls_ecdh_get_params(&ecdh, &ours, MBEDTLS_ECDH_OURS); - } - if (ret == 0) { - ret = mbedtls_ecdh_get_params(&ecdh, &theirs, MBEDTLS_ECDH_THEIRS); - } - if (ret == 0) { - ret = mbedtls_ecdh_calc_secret(&ecdh, &shared_len, shared, PICOKEYS_VAULT_X448_BYTES, random_fill_iterator, NULL); - } - mbedtls_ecdh_free(&ecdh); - mbedtls_ecp_keypair_free(&ours); - mbedtls_ecp_keypair_free(&theirs); - if (ret != 0) { - return PICOKEYS_EXEC_ERROR; - } - return shared_len == PICOKEYS_VAULT_X448_BYTES ? PICOKEYS_OK : PICOKEYS_WRONG_LENGTH; -} - -bool picokeys_vault_enrollment_active(void) { - return vault_enroll_active; -} - -#ifndef ENABLE_EMULATION -static bool vault_enrollment_window_open(void) { - return board_millis() < PICOKEYS_VAULT_ENROLL_WINDOW_MS; -} -#endif - -bool picokeys_vault_enrollment_button_ready(void) { -#ifdef ENABLE_EMULATION - vault_enroll_button_accepted = true; - return true; -#else - if (!vault_enrollment_window_open() && !vault_enroll_active) { - vault_enroll_button_accepted = false; - button_pressed_duration = 0; - led_set_mode(MODE_MOUNTED); - return false; - } - if (button_pressed_duration >= PICOKEYS_VAULT_ENROLL_HOLD_MS) { - if (!vault_enroll_button_accepted) { - led_set_mode(MODE_BUTTON); - } - vault_enroll_button_accepted = true; - } - return vault_enroll_button_accepted; -#endif -} - -int picokeys_vault_enrollment_start(uint8_t public_key[PICOKEYS_VAULT_X448_BYTES], uint8_t challenge[PICOKEYS_VAULT_ENROLL_CHALLENGE_BYTES]) { - if (!public_key || !challenge) { - return PICOKEYS_ERR_NULL_PARAM; - } - - picokeys_vault_enrollment_clear(); - int ret = picokeys_vault_x448_generate(vault_enroll_private, public_key); - if (ret == PICOKEYS_OK) { - memcpy(vault_enroll_public, public_key, PICOKEYS_VAULT_X448_BYTES); - random_fill_buffer(BYTE_ARRAY(vault_enroll_challenge, sizeof(vault_enroll_challenge))); - memcpy(challenge, vault_enroll_challenge, sizeof(vault_enroll_challenge)); - vault_enroll_active = true; -#ifndef ENABLE_EMULATION - led_set_mode(MODE_BUTTON); -#endif - } - return ret; -} - -void picokeys_vault_enrollment_clear(void) { - mbedtls_platform_zeroize(vault_enroll_private, sizeof(vault_enroll_private)); - mbedtls_platform_zeroize(vault_enroll_challenge, sizeof(vault_enroll_challenge)); - mbedtls_platform_zeroize(vault_enroll_public, sizeof(vault_enroll_public)); - vault_enroll_active = false; - vault_enroll_button_accepted = false; -} - -void picokeys_vault_enrollment_reset(void) { - picokeys_vault_enrollment_clear(); -#ifndef ENABLE_EMULATION - button_pressed_duration = 0; - led_set_mode(MODE_MOUNTED); -#endif -} - -static int vault_enrollment_validate_certificate(mbedtls_x509_crt *certificate) { - if (!certificate) { - return PICOKEYS_ERR_NULL_PARAM; - } - - mbedtls_x509_crt ca; - uint32_t flags = 0; - mbedtls_x509_crt_init(&ca); - int ret = mbedtls_x509_crt_parse(&ca, picokeys_vault_ca_der, sizeof(picokeys_vault_ca_der)); - if (ret == 0) { - ret = mbedtls_x509_crt_verify(certificate, &ca, NULL, NULL, &flags, NULL, NULL); - } - mbedtls_x509_crt_free(&ca); - return ret != 0 ? PICOKEYS_EXEC_ERROR : (flags == 0 ? PICOKEYS_OK : PICOKEYS_VERIFICATION_FAILED); -} - -static int vault_enrollment_validate_serial(mbedtls_x509_crt *certificate) { - if (!certificate || !certificate->subject_alt_names.next) { - return PICOKEYS_VERIFICATION_FAILED; - } - - size_t serial_len = strlen(pico_serial_str); - for (mbedtls_x509_sequence *entry = certificate->subject_alt_names.next; entry; entry = entry->next) { - mbedtls_x509_subject_alternative_name san = {0}; - int ret = mbedtls_x509_parse_subject_alt_name(&entry->buf, &san); - if (ret == 0 && (san.type == MBEDTLS_X509_SAN_DNS_NAME || san.type == MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER) && san.san.unstructured_name.len == serial_len && memcmp(san.san.unstructured_name.p, pico_serial_str, serial_len) == 0) { - mbedtls_x509_free_subject_alt_name(&san); + if (app_id != 0) { return PICOKEYS_OK; } - mbedtls_x509_free_subject_alt_name(&san); - } - return PICOKEYS_VERIFICATION_FAILED; -} - -static int vault_enrollment_certificate_public(mbedtls_x509_crt *certificate, uint8_t public_key[PICOKEYS_VAULT_X448_BYTES]) { - if (!certificate || !public_key || (mbedtls_pk_get_type(&certificate->pk) != MBEDTLS_PK_ECKEY && mbedtls_pk_get_type(&certificate->pk) != MBEDTLS_PK_ECKEY_DH)) { - return PICOKEYS_WRONG_DATA; - } - - mbedtls_ecp_keypair *key = mbedtls_pk_ec(certificate->pk); - if (!key || key->MBEDTLS_PRIVATE(grp).id != MBEDTLS_ECP_DP_CURVE448) { - return PICOKEYS_WRONG_DATA; - } - - size_t public_len = 0; - int ret = mbedtls_ecp_point_write_binary(&key->MBEDTLS_PRIVATE(grp), &key->MBEDTLS_PRIVATE(Q), MBEDTLS_ECP_PF_UNCOMPRESSED, &public_len, public_key, PICOKEYS_VAULT_X448_BYTES); - return ret == 0 && public_len == PICOKEYS_VAULT_X448_BYTES ? PICOKEYS_OK : PICOKEYS_WRONG_DATA; -} - -int picokeys_vault_enrollment_finish(const uint8_t *packet, size_t packet_len, file_t *file, uint8_t app_id, const uint8_t wrapping_key[PICOKEYS_VAULT_KEY_SIZE], uint8_t *metadata, size_t metadata_capacity, size_t *metadata_len) { - if (!picokeys_vault_enrollment_active() || !packet || !file || !wrapping_key || !metadata_len || (metadata_capacity > 0 && !metadata) || packet_len < PICOKEYS_VAULT_ENROLL_MIN_PACKET_LEN) { - picokeys_vault_enrollment_reset(); - return PICOKEYS_WRONG_LENGTH; - } - - *metadata_len = 0; - uint16_t certificate_len = ((uint16_t)packet[0] << 8) | packet[1]; - size_t certificate_offset = 2; - size_t encrypted_offset = certificate_offset + certificate_len; - if (certificate_len == 0 || certificate_len > PICOKEYS_VAULT_ENROLL_CERT_MAX || encrypted_offset > packet_len || packet_len < encrypted_offset + 12u + 16u + PICOKEYS_VAULT_KEY_SIZE) { - picokeys_vault_enrollment_reset(); - return PICOKEYS_WRONG_LENGTH; - } - - size_t encrypted_len = packet_len - encrypted_offset - 12u; - if (encrypted_len < PICOKEYS_VAULT_KEY_SIZE + 16u || encrypted_len > PICOKEYS_VAULT_ENROLL_PLAIN_MAX + 16u) { - picokeys_vault_enrollment_reset(); - return PICOKEYS_WRONG_LENGTH; - } - - mbedtls_x509_crt certificate; - mbedtls_x509_crt_init(&certificate); - int ret = mbedtls_x509_crt_parse(&certificate, packet + certificate_offset, certificate_len); - if (ret == 0) { - ret = vault_enrollment_validate_certificate(&certificate); - } - if (ret == PICOKEYS_OK) { - ret = vault_enrollment_validate_serial(&certificate); - } - - uint8_t certificate_public[PICOKEYS_VAULT_X448_BYTES] = { 0 }; - if (ret == PICOKEYS_OK) { - ret = vault_enrollment_certificate_public(&certificate, certificate_public); - } - mbedtls_x509_crt_free(&certificate); - if (ret != PICOKEYS_OK) { - mbedtls_platform_zeroize(certificate_public, sizeof(certificate_public)); - picokeys_vault_enrollment_reset(); - return PICOKEYS_VERIFICATION_FAILED; - } - - uint8_t shared[PICOKEYS_VAULT_X448_BYTES] = { 0 }; - uint8_t session_key[PICOKEYS_VAULT_KEY_SIZE] = { 0 }; - uint8_t info[sizeof(vault_enroll_info) - 1 + PICOKEYS_VAULT_ENROLL_CHALLENGE_BYTES + PICOKEYS_VAULT_X448_BYTES * 2] = { 0 }; - memcpy(info, vault_enroll_info, sizeof(vault_enroll_info) - 1); - memcpy(info + sizeof(vault_enroll_info) - 1, vault_enroll_challenge, PICOKEYS_VAULT_ENROLL_CHALLENGE_BYTES); - memcpy(info + sizeof(vault_enroll_info) - 1 + PICOKEYS_VAULT_ENROLL_CHALLENGE_BYTES, certificate_public, PICOKEYS_VAULT_X448_BYTES); - memcpy(info + sizeof(vault_enroll_info) - 1 + PICOKEYS_VAULT_ENROLL_CHALLENGE_BYTES + PICOKEYS_VAULT_X448_BYTES, vault_enroll_public, PICOKEYS_VAULT_X448_BYTES); - - ret = picokeys_vault_x448_shared(vault_enroll_private, certificate_public, shared); - if (ret == PICOKEYS_OK) { - ret = mbedtls_hkdf(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), NULL, 0, shared, sizeof(shared), info, sizeof(info), session_key, sizeof(session_key)); - } - - uint8_t kvault[PICOKEYS_VAULT_KEY_SIZE] = { 0 }; - uint8_t enrollment_plain[PICOKEYS_VAULT_ENROLL_PLAIN_MAX] = { 0 }; - size_t plain_len = encrypted_len - 16u; - if (ret == PICOKEYS_OK) { - mbedtls_gcm_context gcm; - mbedtls_gcm_init(&gcm); - ret = mbedtls_gcm_setkey(&gcm, MBEDTLS_CIPHER_ID_AES, session_key, 256); - if (ret == 0) { - ret = mbedtls_gcm_auth_decrypt(&gcm, plain_len, packet + encrypted_offset + 12u, 12u, info, sizeof(info), packet + encrypted_offset + 12u + plain_len, 16u, packet + encrypted_offset + 12u, enrollment_plain); - } - mbedtls_gcm_free(&gcm); - } - - size_t plain_metadata_len = 0; - if (ret == 0 && plain_len == PICOKEYS_VAULT_KEY_SIZE) { - memcpy(kvault, enrollment_plain, PICOKEYS_VAULT_KEY_SIZE); - } - else if (ret == 0 && plain_len >= PICOKEYS_VAULT_KEY_SIZE + 1u && enrollment_plain[PICOKEYS_VAULT_KEY_SIZE] <= 64u && plain_len == PICOKEYS_VAULT_KEY_SIZE + 1u + enrollment_plain[PICOKEYS_VAULT_KEY_SIZE]) { - memcpy(kvault, enrollment_plain, PICOKEYS_VAULT_KEY_SIZE); - plain_metadata_len = enrollment_plain[PICOKEYS_VAULT_KEY_SIZE]; - if (plain_metadata_len > metadata_capacity) { - ret = PICOKEYS_ERR_NO_MEMORY; - } - else if (plain_metadata_len > 0 && metadata) { - memcpy(metadata, enrollment_plain + PICOKEYS_VAULT_KEY_SIZE + 1u, plain_metadata_len); - } - } - else if (ret == 0) { - ret = PICOKEYS_WRONG_LENGTH; - } - - if (ret == PICOKEYS_OK) { - ret = picokeys_vault_store_kvault(file, app_id, wrapping_key, kvault); - } - if (ret == PICOKEYS_OK && !flash_commit_sync(5000)) { - ret = PICOKEYS_ERR_MEMORY_FATAL; - } - if (ret == PICOKEYS_OK) { - *metadata_len = plain_metadata_len; - } - - mbedtls_platform_zeroize(shared, sizeof(shared)); - mbedtls_platform_zeroize(session_key, sizeof(session_key)); - mbedtls_platform_zeroize(info, sizeof(info)); - mbedtls_platform_zeroize(certificate_public, sizeof(certificate_public)); - mbedtls_platform_zeroize(kvault, sizeof(kvault)); - mbedtls_platform_zeroize(enrollment_plain, sizeof(enrollment_plain)); - picokeys_vault_enrollment_reset(); - return ret; -} - -static int vault_remove_record(file_t *file, uint8_t app_id) { - if (!file || !file_has_data(file)) { - return PICOKEYS_OK; - } - - const uint8_t *data = file_get_data(file); - size_t data_len = file_get_size(file); - if (picokeys_vault_record_valid(data, data_len)) { - return app_id == 0 ? flash_clear_file(file) : PICOKEYS_OK; + meta_delete_no_commit(file->fid); + return flash_clear_file(file); } if (!picokeys_vault_container_valid(data, data_len, PICOKEYS_VAULT_RECORD_SIZE)) { return PICOKEYS_WRONG_DATA; @@ -695,6 +242,7 @@ static int vault_remove_record(file_t *file, uint8_t app_id) { return PICOKEYS_OK; } if (count == 1) { + meta_delete_no_commit(file->fid); return flash_clear_file(file); } @@ -717,45 +265,3 @@ static int vault_remove_record(file_t *file, uint8_t app_id) { free(updated); return ret; } - -int picokeys_vault_clear_file(file_t *file) { - if (!file) { - return PICOKEYS_OK; - } - meta_delete_no_commit(file->fid); - return flash_clear_file(file); -} - -int picokeys_vault_unenroll(file_t *file, file_t *label_file, uint8_t app_id) { - picokeys_vault_enrollment_reset(); - int ret = vault_remove_record(file, app_id); - if (ret == PICOKEYS_OK && !flash_commit_sync(5000)) { - ret = PICOKEYS_ERR_MEMORY_FATAL; - } - if (ret == PICOKEYS_OK) { - ret = picokeys_vault_clear_file(label_file); - } - if (ret == PICOKEYS_OK && !flash_commit_sync(5000)) { - ret = PICOKEYS_ERR_MEMORY_FATAL; - } - picokeys_vault_enrollment_reset(); - return ret; -} - -bool picokeys_vault_algorithm_valid(uint8_t algorithm) { - return algorithm >= PICOKEYS_VAULT_ALGORITHM_CHACHAPOLY && algorithm <= PICOKEYS_VAULT_ALGORITHM_AESGCM_CHACHAPOLY; -} - -size_t picokeys_vault_algorithm_layers(uint8_t algorithm) { - return algorithm >= PICOKEYS_VAULT_ALGORITHM_CHACHAPOLY_AESGCM ? 2u : 1u; -} - -uint8_t picokeys_vault_algorithm_layer(uint8_t algorithm, size_t index) { - if (algorithm == PICOKEYS_VAULT_ALGORITHM_CHACHAPOLY_AESGCM) { - return index == 0 ? PICOKEYS_VAULT_ALGORITHM_CHACHAPOLY : PICOKEYS_VAULT_ALGORITHM_AESGCM; - } - if (algorithm == PICOKEYS_VAULT_ALGORITHM_AESGCM_CHACHAPOLY) { - return index == 0 ? PICOKEYS_VAULT_ALGORITHM_AESGCM : PICOKEYS_VAULT_ALGORITHM_CHACHAPOLY; - } - return algorithm; -} diff --git a/src/fs/vault_container.h b/src/fs/vault_container.h index 9f2adf4..184a070 100644 --- a/src/fs/vault_container.h +++ b/src/fs/vault_container.h @@ -23,54 +23,23 @@ #include #include "byte_array.h" -#include "crypto_utils.h" #include "file.h" +#include "../vault.h" #define PICOKEYS_VAULT_CONTAINER_HEADER_SIZE 6u #define PICOKEYS_VAULT_CONTAINER_RECORD_PREFIX_SIZE 1u -#define PICOKEYS_VAULT_KEY_SIZE 32u -#define PICOKEYS_VAULT_X448_BYTES 56u -#define PICOKEYS_VAULT_BLOB_NONCE_SIZE 12u -#define PICOKEYS_VAULT_BLOB_TAG_SIZE 16u -#define PICOKEYS_VAULT_ALGORITHM_CHACHAPOLY 1u -#define PICOKEYS_VAULT_ALGORITHM_AESGCM 2u -#define PICOKEYS_VAULT_ALGORITHM_CHACHAPOLY_AESGCM 3 -#define PICOKEYS_VAULT_ALGORITHM_AESGCM_CHACHAPOLY 4 -#define PICOKEYS_VAULT_ENROLL_CHALLENGE_BYTES 32u -#define PICOKEYS_VAULT_ENROLL_WINDOW_MS 60000u -#define PICOKEYS_VAULT_ENROLL_HOLD_MS 10000u -#define PICOKEYS_VAULT_ENROLL_CERT_MAX 1900u -#define PICOKEYS_VAULT_ENROLL_PLAIN_MAX (PICOKEYS_VAULT_KEY_SIZE + 1u + 64u) -#define PICOKEYS_VAULT_ENROLL_MIN_PACKET_LEN (2u + 12u + PICOKEYS_VAULT_KEY_SIZE + 16u) #define PICOKEYS_VAULT_RECORD_FORMAT PIN_KDF_V2 #define PICOKEYS_VAULT_RECORD_SIZE (1u + PIN_KDF_SIZE(PICOKEYS_VAULT_KEY_SIZE)) -bool picokeys_vault_container_valid(const uint8_t *data, size_t data_len, size_t record_size); -bool picokeys_vault_record_valid(const uint8_t *record, size_t record_len); -const uint8_t *picokeys_vault_find_record(const file_t *file, uint8_t app_id, size_t record_size); -bool picokeys_vault_record_available(const file_t *file, uint8_t app_id); -int picokeys_vault_store_record(file_t *file, uint8_t app_id, const_byte_array_t record, uint8_t *scratch, size_t scratch_size); -int picokeys_vault_wrap(const uint8_t wrapping_key[PICOKEYS_VAULT_KEY_SIZE], const uint8_t kvault[PICOKEYS_VAULT_KEY_SIZE], uint8_t record[PICOKEYS_VAULT_RECORD_SIZE]); -int picokeys_vault_unwrap(const uint8_t wrapping_key[PICOKEYS_VAULT_KEY_SIZE], const uint8_t record[PICOKEYS_VAULT_RECORD_SIZE], uint8_t kvault[PICOKEYS_VAULT_KEY_SIZE]); -int picokeys_vault_load_kvault(const file_t *file, uint8_t app_id, const uint8_t wrapping_key[PICOKEYS_VAULT_KEY_SIZE], uint8_t kvault[PICOKEYS_VAULT_KEY_SIZE]); -int picokeys_vault_store_kvault(file_t *file, uint8_t app_id, const uint8_t wrapping_key[PICOKEYS_VAULT_KEY_SIZE], const uint8_t kvault[PICOKEYS_VAULT_KEY_SIZE]); -int picokeys_vault_hash_kvault(const uint8_t kvault[PICOKEYS_VAULT_KEY_SIZE], uint8_t vault_id[PICOKEYS_VAULT_KEY_SIZE]); -int picokeys_vault_layer_key(const uint8_t key[PICOKEYS_VAULT_KEY_SIZE], const uint8_t vault_id[PICOKEYS_VAULT_KEY_SIZE], const uint8_t credential_hash[PICOKEYS_VAULT_KEY_SIZE], uint8_t algorithm, uint8_t layer, uint8_t out[PICOKEYS_VAULT_KEY_SIZE]); -int picokeys_vault_encrypt_layer(uint8_t algorithm, const uint8_t key[PICOKEYS_VAULT_KEY_SIZE], const uint8_t nonce[PICOKEYS_VAULT_BLOB_NONCE_SIZE], const uint8_t *aad, size_t aad_len, const uint8_t *input, size_t input_len, uint8_t *output, uint8_t tag[PICOKEYS_VAULT_BLOB_TAG_SIZE]); -int picokeys_vault_decrypt_layer(uint8_t algorithm, const uint8_t key[PICOKEYS_VAULT_KEY_SIZE], const uint8_t nonce[PICOKEYS_VAULT_BLOB_NONCE_SIZE], const uint8_t *aad, size_t aad_len, const uint8_t *input, size_t input_len, const uint8_t tag[PICOKEYS_VAULT_BLOB_TAG_SIZE], uint8_t *output); -int picokeys_vault_clear_wrappers(file_t *file, uint8_t preserve_app_id); -int picokeys_vault_x448_generate(uint8_t private_key[PICOKEYS_VAULT_X448_BYTES], uint8_t public_key[PICOKEYS_VAULT_X448_BYTES]); -int picokeys_vault_x448_shared(const uint8_t private_key[PICOKEYS_VAULT_X448_BYTES], const uint8_t peer_public[PICOKEYS_VAULT_X448_BYTES], uint8_t shared[PICOKEYS_VAULT_X448_BYTES]); -bool picokeys_vault_enrollment_active(void); -bool picokeys_vault_enrollment_button_ready(void); -int picokeys_vault_enrollment_start(uint8_t public_key[PICOKEYS_VAULT_X448_BYTES], uint8_t challenge[PICOKEYS_VAULT_ENROLL_CHALLENGE_BYTES]); -void picokeys_vault_enrollment_clear(void); -void picokeys_vault_enrollment_reset(void); -int picokeys_vault_enrollment_finish(const uint8_t *packet, size_t packet_len, file_t *file, uint8_t app_id, const uint8_t wrapping_key[PICOKEYS_VAULT_KEY_SIZE], uint8_t *metadata, size_t metadata_capacity, size_t *metadata_len); -bool picokeys_vault_algorithm_valid(uint8_t algorithm); -size_t picokeys_vault_algorithm_layers(uint8_t algorithm); -uint8_t picokeys_vault_algorithm_layer(uint8_t algorithm, size_t layer); -int picokeys_vault_clear_file(file_t *file); -int picokeys_vault_unenroll(file_t *file, file_t *label_file, uint8_t app_id); +extern bool picokeys_vault_container_valid(const uint8_t *data, size_t data_len, size_t record_size); +extern bool picokeys_vault_record_valid(const uint8_t *record, size_t record_len); +extern const uint8_t *picokeys_vault_find_record(const file_t *file, uint8_t app_id, size_t record_size); +extern bool picokeys_vault_record_available(const file_t *file, uint8_t app_id); +extern int picokeys_vault_store_record(file_t *file, uint8_t app_id, const_byte_array_t record, uint8_t *scratch, size_t scratch_size); +extern int picokeys_vault_wrap(const uint8_t wrapping_key[PICOKEYS_VAULT_KEY_SIZE], const uint8_t kvault[PICOKEYS_VAULT_KEY_SIZE], uint8_t record[PICOKEYS_VAULT_RECORD_SIZE]); +extern int picokeys_vault_unwrap(const uint8_t wrapping_key[PICOKEYS_VAULT_KEY_SIZE], const uint8_t record[PICOKEYS_VAULT_RECORD_SIZE], uint8_t kvault[PICOKEYS_VAULT_KEY_SIZE]); +extern int picokeys_vault_load_kvault(const file_t *file, uint8_t app_id, const uint8_t wrapping_key[PICOKEYS_VAULT_KEY_SIZE], uint8_t kvault[PICOKEYS_VAULT_KEY_SIZE]); +extern int picokeys_vault_store_kvault(file_t *file, uint8_t app_id, const uint8_t wrapping_key[PICOKEYS_VAULT_KEY_SIZE], const uint8_t kvault[PICOKEYS_VAULT_KEY_SIZE]); +extern int picokeys_vault_clear_wrapper(file_t *file, uint8_t app_id); #endif // _VAULT_CONTAINER_H_ diff --git a/src/vault.c b/src/vault.c new file mode 100644 index 0000000..0f971dd --- /dev/null +++ b/src/vault.c @@ -0,0 +1,501 @@ +/* + * This file is part of the Pico Keys SDK distribution (https://github.com/polhenarejos/pico-keys-sdk). + * Copyright (c) 2022 Pol Henarejos. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include "vault.h" + +#include +#include + +#include "button.h" +#include "flash.h" +#include "led/led.h" +#include "picokeys.h" +#include "random.h" +#include "serial.h" +#include "vault_container.h" +#if defined(ESP_PLATFORM) +#include "compat/esp_compat.h" +#else +#include "compat/board.h" +#endif +#include "mbedtls/chachapoly.h" +#include "mbedtls/ecdh.h" +#include "mbedtls/ecp.h" +#include "mbedtls/gcm.h" +#include "mbedtls/hkdf.h" +#include "mbedtls/md.h" +#include "mbedtls/pk.h" +#include "mbedtls/x509_crt.h" + +static const uint8_t vault_id_domain[] = "PicoKeys Vault ID v1"; +static const uint8_t vault_enroll_info[] = "PicoKeys Vault enrollment v1"; +static const uint8_t picokeys_vault_ca_der[] = { + 0x30, 0x82, 0x01, 0xEB, 0x30, 0x82, 0x01, 0x6B, 0xA0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x14, 0x2B, 0x37, 0x6A, 0xC8, 0x98, 0x74, 0xE5, 0x0E, 0x80, + 0x1F, 0xB9, 0x61, 0xCD, 0x25, 0x80, 0x48, 0x17, 0xD6, 0x33, 0xA1, 0x30, + 0x05, 0x06, 0x03, 0x2B, 0x65, 0x71, 0x30, 0x3C, 0x31, 0x0B, 0x30, 0x09, + 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x45, 0x53, 0x31, 0x11, 0x30, + 0x0F, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x0C, 0x08, 0x50, 0x69, 0x63, 0x6F, + 0x4B, 0x65, 0x79, 0x73, 0x31, 0x1A, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, + 0x03, 0x0C, 0x11, 0x50, 0x69, 0x63, 0x6F, 0x4B, 0x65, 0x79, 0x73, 0x20, + 0x56, 0x61, 0x75, 0x6C, 0x74, 0x20, 0x43, 0x41, 0x30, 0x1E, 0x17, 0x0D, + 0x32, 0x36, 0x30, 0x38, 0x30, 0x35, 0x31, 0x36, 0x34, 0x34, 0x35, 0x36, + 0x5A, 0x17, 0x0D, 0x33, 0x36, 0x30, 0x38, 0x30, 0x32, 0x31, 0x36, 0x34, + 0x34, 0x35, 0x36, 0x5A, 0x30, 0x3C, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, + 0x55, 0x04, 0x06, 0x13, 0x02, 0x45, 0x53, 0x31, 0x11, 0x30, 0x0F, 0x06, + 0x03, 0x55, 0x04, 0x0A, 0x0C, 0x08, 0x50, 0x69, 0x63, 0x6F, 0x4B, 0x65, + 0x79, 0x73, 0x31, 0x1A, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0C, + 0x11, 0x50, 0x69, 0x63, 0x6F, 0x4B, 0x65, 0x79, 0x73, 0x20, 0x56, 0x61, + 0x75, 0x6C, 0x74, 0x20, 0x43, 0x41, 0x30, 0x43, 0x30, 0x05, 0x06, 0x03, + 0x2B, 0x65, 0x71, 0x03, 0x3A, 0x00, 0xE4, 0x0E, 0x8C, 0x62, 0xC6, 0xD3, + 0x6B, 0x06, 0xC4, 0x0A, 0x54, 0x40, 0x7C, 0x82, 0x1F, 0xDD, 0xAE, 0x93, + 0xF2, 0x88, 0x00, 0x9F, 0xDB, 0x10, 0x67, 0x9A, 0x32, 0x47, 0x62, 0xCE, + 0x92, 0x2A, 0xE2, 0x94, 0x2F, 0x40, 0xF4, 0xEC, 0x0A, 0xFE, 0x72, 0xA4, + 0x18, 0x5D, 0x20, 0x4D, 0x55, 0x97, 0x46, 0xE5, 0x94, 0x7D, 0x11, 0xF0, + 0x5C, 0xE6, 0x00, 0xA3, 0x66, 0x30, 0x64, 0x30, 0x1F, 0x06, 0x03, 0x55, + 0x1D, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xAE, 0xFB, 0xD5, 0x12, + 0x05, 0xBA, 0x61, 0xD7, 0x67, 0xE6, 0xAC, 0x78, 0x2E, 0x68, 0xD4, 0x22, + 0xFA, 0xC7, 0xDC, 0x26, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1D, 0x13, 0x01, + 0x01, 0xFF, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xFF, 0x02, 0x01, 0x00, + 0x30, 0x0E, 0x06, 0x03, 0x55, 0x1D, 0x0F, 0x01, 0x01, 0xFF, 0x04, 0x04, + 0x03, 0x02, 0x01, 0x06, 0x30, 0x1D, 0x06, 0x03, 0x55, 0x1D, 0x0E, 0x04, + 0x16, 0x04, 0x14, 0xAE, 0xFB, 0xD5, 0x12, 0x05, 0xBA, 0x61, 0xD7, 0x67, + 0xE6, 0xAC, 0x78, 0x2E, 0x68, 0xD4, 0x22, 0xFA, 0xC7, 0xDC, 0x26, 0x30, + 0x05, 0x06, 0x03, 0x2B, 0x65, 0x71, 0x03, 0x73, 0x00, 0x0B, 0xB4, 0x4F, + 0x45, 0x1C, 0x36, 0x77, 0xC1, 0x58, 0xDE, 0x39, 0xC0, 0x29, 0xA0, 0x7C, + 0x9F, 0x8F, 0x75, 0xC2, 0x9E, 0xAE, 0x12, 0x41, 0x00, 0xC8, 0xC9, 0x45, + 0xD1, 0xC0, 0xA6, 0x9A, 0x1D, 0xFA, 0x75, 0xE9, 0xB8, 0x82, 0x00, 0xE3, + 0x81, 0xCF, 0x74, 0x35, 0x59, 0x7F, 0x70, 0x06, 0x3A, 0xEC, 0xDF, 0x52, + 0x42, 0x53, 0x0D, 0xC3, 0x3B, 0x80, 0xF1, 0x1E, 0x3F, 0xC4, 0xAD, 0xC8, + 0xCA, 0x07, 0x4E, 0xBD, 0x5E, 0x35, 0xB7, 0x54, 0x63, 0x08, 0x43, 0x4B, + 0xB1, 0xCC, 0x7F, 0x1A, 0x45, 0x4C, 0xE1, 0x34, 0x57, 0x89, 0x57, 0xAA, + 0x08, 0xD5, 0xF6, 0x54, 0xC5, 0xE7, 0x49, 0xC7, 0xBA, 0xD7, 0x79, 0xAE, + 0xD6, 0x11, 0x05, 0x7A, 0xEF, 0x38, 0x97, 0x05, 0x96, 0x13, 0xC6, 0x95, + 0x01, 0x3A, 0x00, +}; +static uint8_t vault_enroll_private[PICOKEYS_VAULT_X448_BYTES]; +static uint8_t vault_enroll_public[PICOKEYS_VAULT_X448_BYTES]; +static uint8_t vault_enroll_challenge[PICOKEYS_VAULT_ENROLL_CHALLENGE_BYTES]; +static bool vault_enroll_active; +static bool vault_enroll_button_accepted; + +int picokeys_vault_hash_kvault(const uint8_t kvault[PICOKEYS_VAULT_KEY_SIZE], uint8_t vault_id[PICOKEYS_VAULT_KEY_SIZE]) { + if (!kvault || !vault_id) { + return PICOKEYS_ERR_NULL_PARAM; + } + + uint8_t input[sizeof(vault_id_domain) - 1 + PICOKEYS_VAULT_KEY_SIZE] = { 0 }; + memcpy(input, vault_id_domain, sizeof(vault_id_domain) - 1); + memcpy(input + sizeof(vault_id_domain) - 1, kvault, PICOKEYS_VAULT_KEY_SIZE); + hash256(CONST_BYTE_ARRAY(input, sizeof(input)), vault_id); + mbedtls_platform_zeroize(input, sizeof(input)); + return PICOKEYS_OK; +} + +int picokeys_vault_layer_key(const uint8_t key[PICOKEYS_VAULT_KEY_SIZE], const uint8_t vault_id[PICOKEYS_VAULT_KEY_SIZE], const uint8_t credential_hash[PICOKEYS_VAULT_KEY_SIZE], uint8_t algorithm, uint8_t layer, uint8_t out[PICOKEYS_VAULT_KEY_SIZE]) { + if (!key || !vault_id || !credential_hash || !out) { + return PICOKEYS_ERR_NULL_PARAM; + } + + uint8_t info[sizeof(vault_enroll_info) - 1 + PICOKEYS_VAULT_KEY_SIZE + 2]; + memcpy(info, vault_enroll_info, sizeof(vault_enroll_info) - 1); + memcpy(info + sizeof(vault_enroll_info) - 1, credential_hash, PICOKEYS_VAULT_KEY_SIZE); + info[sizeof(vault_enroll_info) - 1 + PICOKEYS_VAULT_KEY_SIZE] = algorithm; + info[sizeof(vault_enroll_info) - 1 + PICOKEYS_VAULT_KEY_SIZE + 1] = layer; + int ret = mbedtls_hkdf(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), vault_id, PICOKEYS_VAULT_KEY_SIZE, key, PICOKEYS_VAULT_KEY_SIZE, info, sizeof(info), out, PICOKEYS_VAULT_KEY_SIZE); + mbedtls_platform_zeroize(info, sizeof(info)); + return ret == 0 ? PICOKEYS_OK : PICOKEYS_EXEC_ERROR; +} + +int picokeys_vault_encrypt_layer(uint8_t algorithm, const uint8_t key[PICOKEYS_VAULT_KEY_SIZE], const uint8_t nonce[PICOKEYS_VAULT_BLOB_NONCE_SIZE], const uint8_t *aad, size_t aad_len, const uint8_t *input, size_t input_len, uint8_t *output, uint8_t tag[PICOKEYS_VAULT_BLOB_TAG_SIZE]) { + if (!key || !nonce || (aad_len > 0 && !aad) || (input_len > 0 && (!input || !output)) || !tag) { + return PICOKEYS_ERR_NULL_PARAM; + } + + int ret = -1; + if (algorithm == PICOKEYS_VAULT_ALGORITHM_CHACHAPOLY) { + mbedtls_chachapoly_context chatx; + mbedtls_chachapoly_init(&chatx); + ret = mbedtls_chachapoly_setkey(&chatx, key); + if (ret == 0) { + ret = mbedtls_chachapoly_encrypt_and_tag(&chatx, input_len, nonce, aad, aad_len, input, output, tag); + } + mbedtls_chachapoly_free(&chatx); + } + else if (algorithm == PICOKEYS_VAULT_ALGORITHM_AESGCM) { + mbedtls_gcm_context gcm; + mbedtls_gcm_init(&gcm); + ret = mbedtls_gcm_setkey(&gcm, MBEDTLS_CIPHER_ID_AES, key, 256); + if (ret == 0) { + ret = mbedtls_gcm_crypt_and_tag(&gcm, MBEDTLS_GCM_ENCRYPT, input_len, nonce, PICOKEYS_VAULT_BLOB_NONCE_SIZE, aad, aad_len, input, output, PICOKEYS_VAULT_BLOB_TAG_SIZE, tag); + } + mbedtls_gcm_free(&gcm); + } + return ret == 0 ? PICOKEYS_OK : PICOKEYS_EXEC_ERROR; +} + +int picokeys_vault_decrypt_layer(uint8_t algorithm, const uint8_t key[PICOKEYS_VAULT_KEY_SIZE], const uint8_t nonce[PICOKEYS_VAULT_BLOB_NONCE_SIZE], const uint8_t *aad, size_t aad_len, const uint8_t *input, size_t input_len, const uint8_t tag[PICOKEYS_VAULT_BLOB_TAG_SIZE], uint8_t *output) { + if (!key || !nonce || (aad_len > 0 && !aad) || (input_len > 0 && (!input || !output)) || !tag) { + return PICOKEYS_ERR_NULL_PARAM; + } + + int ret = -1; + if (algorithm == PICOKEYS_VAULT_ALGORITHM_CHACHAPOLY) { + mbedtls_chachapoly_context chatx; + mbedtls_chachapoly_init(&chatx); + ret = mbedtls_chachapoly_setkey(&chatx, key); + if (ret == 0) { + ret = mbedtls_chachapoly_auth_decrypt(&chatx, input_len, nonce, aad, aad_len, tag, input, output); + } + mbedtls_chachapoly_free(&chatx); + } + else if (algorithm == PICOKEYS_VAULT_ALGORITHM_AESGCM) { + mbedtls_gcm_context gcm; + mbedtls_gcm_init(&gcm); + ret = mbedtls_gcm_setkey(&gcm, MBEDTLS_CIPHER_ID_AES, key, 256); + if (ret == 0) { + ret = mbedtls_gcm_auth_decrypt(&gcm, input_len, nonce, PICOKEYS_VAULT_BLOB_NONCE_SIZE, aad, aad_len, tag, PICOKEYS_VAULT_BLOB_TAG_SIZE, input, output); + } + mbedtls_gcm_free(&gcm); + } + return ret == 0 ? PICOKEYS_OK : PICOKEYS_VERIFICATION_FAILED; +} + +int picokeys_vault_x448_generate(uint8_t private_key[PICOKEYS_VAULT_X448_BYTES], uint8_t public_key[PICOKEYS_VAULT_X448_BYTES]) { + if (!private_key || !public_key) { + return PICOKEYS_ERR_NULL_PARAM; + } + + mbedtls_ecp_keypair key; + size_t private_len = 0; + size_t public_len = 0; + mbedtls_ecp_keypair_init(&key); + int ret = mbedtls_ecp_gen_key(MBEDTLS_ECP_DP_CURVE448, &key, random_fill_iterator, NULL); + if (ret == 0) { + ret = mbedtls_ecp_write_key_ext(&key, &private_len, private_key, PICOKEYS_VAULT_X448_BYTES); + } + if (ret == 0) { + ret = mbedtls_ecp_point_write_binary(&key.MBEDTLS_PRIVATE(grp), &key.MBEDTLS_PRIVATE(Q), MBEDTLS_ECP_PF_UNCOMPRESSED, &public_len, public_key, PICOKEYS_VAULT_X448_BYTES); + } + mbedtls_ecp_keypair_free(&key); + if (ret != 0) { + return PICOKEYS_EXEC_ERROR; + } + return private_len == PICOKEYS_VAULT_X448_BYTES && public_len == PICOKEYS_VAULT_X448_BYTES ? PICOKEYS_OK : PICOKEYS_WRONG_LENGTH; +} + +int picokeys_vault_x448_shared(const uint8_t private_key[PICOKEYS_VAULT_X448_BYTES], const uint8_t peer_public[PICOKEYS_VAULT_X448_BYTES], uint8_t shared[PICOKEYS_VAULT_X448_BYTES]) { + if (!private_key || !peer_public || !shared) { + return PICOKEYS_ERR_NULL_PARAM; + } + + mbedtls_ecdh_context ecdh; + mbedtls_ecp_keypair ours; + mbedtls_ecp_keypair theirs; + size_t shared_len = 0; + mbedtls_ecdh_init(&ecdh); + mbedtls_ecp_keypair_init(&ours); + mbedtls_ecp_keypair_init(&theirs); + int ret = mbedtls_ecp_group_load(&ours.MBEDTLS_PRIVATE(grp), MBEDTLS_ECP_DP_CURVE448); + if (ret == 0) { + ret = mbedtls_ecp_group_load(&theirs.MBEDTLS_PRIVATE(grp), MBEDTLS_ECP_DP_CURVE448); + } + if (ret == 0) { + ret = mbedtls_ecp_read_key(MBEDTLS_ECP_DP_CURVE448, &ours, private_key, PICOKEYS_VAULT_X448_BYTES); + } + if (ret == 0) { + ret = mbedtls_ecp_point_read_binary(&theirs.MBEDTLS_PRIVATE(grp), &theirs.MBEDTLS_PRIVATE(Q), peer_public, PICOKEYS_VAULT_X448_BYTES); + } + if (ret == 0) { + ret = mbedtls_ecdh_setup(&ecdh, MBEDTLS_ECP_DP_CURVE448); + } + if (ret == 0) { + ret = mbedtls_ecdh_get_params(&ecdh, &ours, MBEDTLS_ECDH_OURS); + } + if (ret == 0) { + ret = mbedtls_ecdh_get_params(&ecdh, &theirs, MBEDTLS_ECDH_THEIRS); + } + if (ret == 0) { + ret = mbedtls_ecdh_calc_secret(&ecdh, &shared_len, shared, PICOKEYS_VAULT_X448_BYTES, random_fill_iterator, NULL); + } + mbedtls_ecdh_free(&ecdh); + mbedtls_ecp_keypair_free(&ours); + mbedtls_ecp_keypair_free(&theirs); + if (ret != 0) { + return PICOKEYS_EXEC_ERROR; + } + return shared_len == PICOKEYS_VAULT_X448_BYTES ? PICOKEYS_OK : PICOKEYS_WRONG_LENGTH; +} + +bool picokeys_vault_enrollment_active(void) { + return vault_enroll_active; +} + +#ifndef ENABLE_EMULATION +static bool vault_enrollment_window_open(void) { + return board_millis() < PICOKEYS_VAULT_ENROLL_WINDOW_MS; +} +#endif + +bool picokeys_vault_enrollment_button_ready(void) { +#ifdef ENABLE_EMULATION + vault_enroll_button_accepted = true; + return true; +#else + if (!vault_enrollment_window_open() && !vault_enroll_active) { + vault_enroll_button_accepted = false; + button_pressed_duration = 0; + led_set_mode(MODE_MOUNTED); + return false; + } + if (button_pressed_duration >= PICOKEYS_VAULT_ENROLL_HOLD_MS) { + if (!vault_enroll_button_accepted) { + led_set_mode(MODE_BUTTON); + } + vault_enroll_button_accepted = true; + } + return vault_enroll_button_accepted; +#endif +} + +int picokeys_vault_enrollment_start(uint8_t public_key[PICOKEYS_VAULT_X448_BYTES], uint8_t challenge[PICOKEYS_VAULT_ENROLL_CHALLENGE_BYTES]) { + if (!public_key || !challenge) { + return PICOKEYS_ERR_NULL_PARAM; + } + + picokeys_vault_enrollment_clear(); + int ret = picokeys_vault_x448_generate(vault_enroll_private, public_key); + if (ret == PICOKEYS_OK) { + memcpy(vault_enroll_public, public_key, PICOKEYS_VAULT_X448_BYTES); + random_fill_buffer(BYTE_ARRAY(vault_enroll_challenge, sizeof(vault_enroll_challenge))); + memcpy(challenge, vault_enroll_challenge, sizeof(vault_enroll_challenge)); + vault_enroll_active = true; +#ifndef ENABLE_EMULATION + led_set_mode(MODE_BUTTON); +#endif + } + return ret; +} + +void picokeys_vault_enrollment_clear(void) { + mbedtls_platform_zeroize(vault_enroll_private, sizeof(vault_enroll_private)); + mbedtls_platform_zeroize(vault_enroll_challenge, sizeof(vault_enroll_challenge)); + mbedtls_platform_zeroize(vault_enroll_public, sizeof(vault_enroll_public)); + vault_enroll_active = false; + vault_enroll_button_accepted = false; +} + +void picokeys_vault_enrollment_reset(void) { + picokeys_vault_enrollment_clear(); +#ifndef ENABLE_EMULATION + button_pressed_duration = 0; + led_set_mode(MODE_MOUNTED); +#endif +} + +static int vault_enrollment_validate_certificate(mbedtls_x509_crt *certificate) { + if (!certificate) { + return PICOKEYS_ERR_NULL_PARAM; + } + + mbedtls_x509_crt ca; + uint32_t flags = 0; + mbedtls_x509_crt_init(&ca); + int ret = mbedtls_x509_crt_parse(&ca, picokeys_vault_ca_der, sizeof(picokeys_vault_ca_der)); + if (ret == 0) { + ret = mbedtls_x509_crt_verify(certificate, &ca, NULL, NULL, &flags, NULL, NULL); + } + mbedtls_x509_crt_free(&ca); + return ret != 0 ? PICOKEYS_EXEC_ERROR : (flags == 0 ? PICOKEYS_OK : PICOKEYS_VERIFICATION_FAILED); +} + +static int vault_enrollment_validate_serial(mbedtls_x509_crt *certificate) { + if (!certificate || !certificate->subject_alt_names.next) { + return PICOKEYS_VERIFICATION_FAILED; + } + + size_t serial_len = strlen(pico_serial_str); + for (mbedtls_x509_sequence *entry = certificate->subject_alt_names.next; entry; entry = entry->next) { + mbedtls_x509_subject_alternative_name san = { 0 }; + int ret = mbedtls_x509_parse_subject_alt_name(&entry->buf, &san); + if (ret == 0 && (san.type == MBEDTLS_X509_SAN_DNS_NAME || san.type == MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER) && san.san.unstructured_name.len == serial_len && memcmp(san.san.unstructured_name.p, pico_serial_str, serial_len) == 0) { + mbedtls_x509_free_subject_alt_name(&san); + return PICOKEYS_OK; + } + mbedtls_x509_free_subject_alt_name(&san); + } + return PICOKEYS_VERIFICATION_FAILED; +} + +static int vault_enrollment_certificate_public(mbedtls_x509_crt *certificate, uint8_t public_key[PICOKEYS_VAULT_X448_BYTES]) { + if (!certificate || !public_key || (mbedtls_pk_get_type(&certificate->pk) != MBEDTLS_PK_ECKEY && mbedtls_pk_get_type(&certificate->pk) != MBEDTLS_PK_ECKEY_DH)) { + return PICOKEYS_WRONG_DATA; + } + + mbedtls_ecp_keypair *key = mbedtls_pk_ec(certificate->pk); + if (!key || key->MBEDTLS_PRIVATE(grp).id != MBEDTLS_ECP_DP_CURVE448) { + return PICOKEYS_WRONG_DATA; + } + + size_t public_len = 0; + int ret = mbedtls_ecp_point_write_binary(&key->MBEDTLS_PRIVATE(grp), &key->MBEDTLS_PRIVATE(Q), MBEDTLS_ECP_PF_UNCOMPRESSED, &public_len, public_key, PICOKEYS_VAULT_X448_BYTES); + return ret == 0 && public_len == PICOKEYS_VAULT_X448_BYTES ? PICOKEYS_OK : PICOKEYS_WRONG_DATA; +} + +int picokeys_vault_enrollment_finish(const uint8_t *packet, size_t packet_len, file_t *file, uint8_t app_id, const uint8_t wrapping_key[PICOKEYS_VAULT_KEY_SIZE], uint8_t *metadata, size_t metadata_capacity, size_t *metadata_len) { + if (!picokeys_vault_enrollment_active() || !packet || !file || !wrapping_key || !metadata_len || (metadata_capacity > 0 && !metadata) || packet_len < PICOKEYS_VAULT_ENROLL_MIN_PACKET_LEN) { + picokeys_vault_enrollment_reset(); + return PICOKEYS_WRONG_LENGTH; + } + + *metadata_len = 0; + uint16_t certificate_len = ((uint16_t)packet[0] << 8) | packet[1]; + size_t certificate_offset = 2; + size_t encrypted_offset = certificate_offset + certificate_len; + if (certificate_len == 0 || certificate_len > PICOKEYS_VAULT_ENROLL_CERT_MAX || encrypted_offset > packet_len || packet_len < encrypted_offset + 12u + 16u + PICOKEYS_VAULT_KEY_SIZE) { + picokeys_vault_enrollment_reset(); + return PICOKEYS_WRONG_LENGTH; + } + + size_t encrypted_len = packet_len - encrypted_offset - 12u; + if (encrypted_len < PICOKEYS_VAULT_KEY_SIZE + 16u || encrypted_len > PICOKEYS_VAULT_ENROLL_PLAIN_MAX + 16u) { + picokeys_vault_enrollment_reset(); + return PICOKEYS_WRONG_LENGTH; + } + + mbedtls_x509_crt certificate; + mbedtls_x509_crt_init(&certificate); + int ret = mbedtls_x509_crt_parse(&certificate, packet + certificate_offset, certificate_len); + if (ret == 0) { + ret = vault_enrollment_validate_certificate(&certificate); + } + if (ret == PICOKEYS_OK) { + ret = vault_enrollment_validate_serial(&certificate); + } + + uint8_t certificate_public[PICOKEYS_VAULT_X448_BYTES] = { 0 }; + if (ret == PICOKEYS_OK) { + ret = vault_enrollment_certificate_public(&certificate, certificate_public); + } + mbedtls_x509_crt_free(&certificate); + if (ret != PICOKEYS_OK) { + mbedtls_platform_zeroize(certificate_public, sizeof(certificate_public)); + picokeys_vault_enrollment_reset(); + return PICOKEYS_VERIFICATION_FAILED; + } + + uint8_t shared[PICOKEYS_VAULT_X448_BYTES] = { 0 }; + uint8_t session_key[PICOKEYS_VAULT_KEY_SIZE] = { 0 }; + uint8_t info[sizeof(vault_enroll_info) - 1 + PICOKEYS_VAULT_ENROLL_CHALLENGE_BYTES + PICOKEYS_VAULT_X448_BYTES * 2] = { 0 }; + memcpy(info, vault_enroll_info, sizeof(vault_enroll_info) - 1); + memcpy(info + sizeof(vault_enroll_info) - 1, vault_enroll_challenge, PICOKEYS_VAULT_ENROLL_CHALLENGE_BYTES); + memcpy(info + sizeof(vault_enroll_info) - 1 + PICOKEYS_VAULT_ENROLL_CHALLENGE_BYTES, certificate_public, PICOKEYS_VAULT_X448_BYTES); + memcpy(info + sizeof(vault_enroll_info) - 1 + PICOKEYS_VAULT_ENROLL_CHALLENGE_BYTES + PICOKEYS_VAULT_X448_BYTES, vault_enroll_public, PICOKEYS_VAULT_X448_BYTES); + + ret = picokeys_vault_x448_shared(vault_enroll_private, certificate_public, shared); + if (ret == PICOKEYS_OK) { + ret = mbedtls_hkdf(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), NULL, 0, shared, sizeof(shared), info, sizeof(info), session_key, sizeof(session_key)); + } + + uint8_t kvault[PICOKEYS_VAULT_KEY_SIZE] = { 0 }; + uint8_t enrollment_plain[PICOKEYS_VAULT_ENROLL_PLAIN_MAX] = { 0 }; + size_t plain_len = encrypted_len - 16u; + if (ret == PICOKEYS_OK) { + mbedtls_gcm_context gcm; + mbedtls_gcm_init(&gcm); + ret = mbedtls_gcm_setkey(&gcm, MBEDTLS_CIPHER_ID_AES, session_key, 256); + if (ret == 0) { + ret = mbedtls_gcm_auth_decrypt(&gcm, plain_len, packet + encrypted_offset + 12u, 12u, info, sizeof(info), packet + encrypted_offset + 12u + plain_len, 16u, packet + encrypted_offset + 12u, enrollment_plain); + } + mbedtls_gcm_free(&gcm); + } + + size_t plain_metadata_len = 0; + if (ret == 0 && plain_len == PICOKEYS_VAULT_KEY_SIZE) { + memcpy(kvault, enrollment_plain, PICOKEYS_VAULT_KEY_SIZE); + } + else if (ret == 0 && plain_len >= PICOKEYS_VAULT_KEY_SIZE + 1u && enrollment_plain[PICOKEYS_VAULT_KEY_SIZE] <= 64u && plain_len == PICOKEYS_VAULT_KEY_SIZE + 1u + enrollment_plain[PICOKEYS_VAULT_KEY_SIZE]) { + memcpy(kvault, enrollment_plain, PICOKEYS_VAULT_KEY_SIZE); + plain_metadata_len = enrollment_plain[PICOKEYS_VAULT_KEY_SIZE]; + if (plain_metadata_len > metadata_capacity) { + ret = PICOKEYS_ERR_NO_MEMORY; + } + else if (plain_metadata_len > 0 && metadata) { + memcpy(metadata, enrollment_plain + PICOKEYS_VAULT_KEY_SIZE + 1u, plain_metadata_len); + } + } + else if (ret == 0) { + ret = PICOKEYS_WRONG_LENGTH; + } + + if (ret == PICOKEYS_OK) { + ret = picokeys_vault_store_kvault(file, app_id, wrapping_key, kvault); + } + if (ret == PICOKEYS_OK && !flash_commit_sync(5000)) { + ret = PICOKEYS_ERR_MEMORY_FATAL; + } + if (ret == PICOKEYS_OK) { + *metadata_len = plain_metadata_len; + } + + mbedtls_platform_zeroize(shared, sizeof(shared)); + mbedtls_platform_zeroize(session_key, sizeof(session_key)); + mbedtls_platform_zeroize(info, sizeof(info)); + mbedtls_platform_zeroize(certificate_public, sizeof(certificate_public)); + mbedtls_platform_zeroize(kvault, sizeof(kvault)); + mbedtls_platform_zeroize(enrollment_plain, sizeof(enrollment_plain)); + picokeys_vault_enrollment_reset(); + return ret; +} + +int picokeys_vault_clear_file(file_t *file) { + if (!file) { + return PICOKEYS_OK; + } + meta_delete_no_commit(file->fid); + return flash_clear_file(file); +} + +int picokeys_vault_unenroll(file_t *file, file_t *label_file, uint8_t app_id) { + int ret = picokeys_vault_clear_wrapper(file, app_id); + if (ret == PICOKEYS_OK) { + ret = picokeys_vault_clear_file(label_file); + } + if (ret == PICOKEYS_OK && !flash_commit_sync(5000)) { + ret = PICOKEYS_ERR_MEMORY_FATAL; + } + picokeys_vault_enrollment_reset(); + return ret; +} + +bool picokeys_vault_algorithm_valid(uint8_t algorithm) { + return algorithm >= PICOKEYS_VAULT_ALGORITHM_CHACHAPOLY && algorithm <= PICOKEYS_VAULT_ALGORITHM_AESGCM_CHACHAPOLY; +} + +size_t picokeys_vault_algorithm_layers(uint8_t algorithm) { + return algorithm >= PICOKEYS_VAULT_ALGORITHM_CHACHAPOLY_AESGCM ? 2u : 1u; +} + +uint8_t picokeys_vault_algorithm_layer(uint8_t algorithm, size_t index) { + if (algorithm == PICOKEYS_VAULT_ALGORITHM_CHACHAPOLY_AESGCM) { + return index == 0 ? PICOKEYS_VAULT_ALGORITHM_CHACHAPOLY : PICOKEYS_VAULT_ALGORITHM_AESGCM; + } + if (algorithm == PICOKEYS_VAULT_ALGORITHM_AESGCM_CHACHAPOLY) { + return index == 0 ? PICOKEYS_VAULT_ALGORITHM_AESGCM : PICOKEYS_VAULT_ALGORITHM_CHACHAPOLY; + } + return algorithm; +} diff --git a/src/vault.h b/src/vault.h new file mode 100644 index 0000000..1be1d1b --- /dev/null +++ b/src/vault.h @@ -0,0 +1,61 @@ +/* + * This file is part of the Pico Keys SDK distribution (https://github.com/polhenarejos/pico-keys-sdk). + * Copyright (c) 2022 Pol Henarejos. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#ifndef _PICOKEYS_VAULT_H_ +#define _PICOKEYS_VAULT_H_ + +#include +#include +#include + +#include "crypto_utils.h" +#include "file.h" + +#define PICOKEYS_VAULT_KEY_SIZE 32u +#define PICOKEYS_VAULT_X448_BYTES 56u +#define PICOKEYS_VAULT_BLOB_NONCE_SIZE 12u +#define PICOKEYS_VAULT_BLOB_TAG_SIZE 16u +#define PICOKEYS_VAULT_ALGORITHM_CHACHAPOLY 1u +#define PICOKEYS_VAULT_ALGORITHM_AESGCM 2u +#define PICOKEYS_VAULT_ALGORITHM_CHACHAPOLY_AESGCM 3u +#define PICOKEYS_VAULT_ALGORITHM_AESGCM_CHACHAPOLY 4u +#define PICOKEYS_VAULT_ENROLL_CHALLENGE_BYTES 32u +#define PICOKEYS_VAULT_ENROLL_WINDOW_MS 60000u +#define PICOKEYS_VAULT_ENROLL_HOLD_MS 10000u +#define PICOKEYS_VAULT_ENROLL_CERT_MAX 1900u +#define PICOKEYS_VAULT_ENROLL_PLAIN_MAX (PICOKEYS_VAULT_KEY_SIZE + 1u + 64u) +#define PICOKEYS_VAULT_ENROLL_MIN_PACKET_LEN (2u + 12u + PICOKEYS_VAULT_KEY_SIZE + 16u) + +extern int picokeys_vault_hash_kvault(const uint8_t kvault[PICOKEYS_VAULT_KEY_SIZE], uint8_t vault_id[PICOKEYS_VAULT_KEY_SIZE]); +extern int picokeys_vault_layer_key(const uint8_t key[PICOKEYS_VAULT_KEY_SIZE], const uint8_t vault_id[PICOKEYS_VAULT_KEY_SIZE], const uint8_t credential_hash[PICOKEYS_VAULT_KEY_SIZE], uint8_t algorithm, uint8_t layer, uint8_t out[PICOKEYS_VAULT_KEY_SIZE]); +extern int picokeys_vault_encrypt_layer(uint8_t algorithm, const uint8_t key[PICOKEYS_VAULT_KEY_SIZE], const uint8_t nonce[PICOKEYS_VAULT_BLOB_NONCE_SIZE], const uint8_t *aad, size_t aad_len, const uint8_t *input, size_t input_len, uint8_t *output, uint8_t tag[PICOKEYS_VAULT_BLOB_TAG_SIZE]); +extern int picokeys_vault_decrypt_layer(uint8_t algorithm, const uint8_t key[PICOKEYS_VAULT_KEY_SIZE], const uint8_t nonce[PICOKEYS_VAULT_BLOB_NONCE_SIZE], const uint8_t *aad, size_t aad_len, const uint8_t *input, size_t input_len, const uint8_t tag[PICOKEYS_VAULT_BLOB_TAG_SIZE], uint8_t *output); +extern int picokeys_vault_x448_generate(uint8_t private_key[PICOKEYS_VAULT_X448_BYTES], uint8_t public_key[PICOKEYS_VAULT_X448_BYTES]); +extern int picokeys_vault_x448_shared(const uint8_t private_key[PICOKEYS_VAULT_X448_BYTES], const uint8_t peer_public[PICOKEYS_VAULT_X448_BYTES], uint8_t shared[PICOKEYS_VAULT_X448_BYTES]); +extern bool picokeys_vault_enrollment_active(void); +extern bool picokeys_vault_enrollment_button_ready(void); +extern int picokeys_vault_enrollment_start(uint8_t public_key[PICOKEYS_VAULT_X448_BYTES], uint8_t challenge[PICOKEYS_VAULT_ENROLL_CHALLENGE_BYTES]); +extern void picokeys_vault_enrollment_clear(void); +extern void picokeys_vault_enrollment_reset(void); +extern int picokeys_vault_enrollment_finish(const uint8_t *packet, size_t packet_len, file_t *file, uint8_t app_id, const uint8_t wrapping_key[PICOKEYS_VAULT_KEY_SIZE], uint8_t *metadata, size_t metadata_capacity, size_t *metadata_len); +extern bool picokeys_vault_algorithm_valid(uint8_t algorithm); +extern size_t picokeys_vault_algorithm_layers(uint8_t algorithm); +extern uint8_t picokeys_vault_algorithm_layer(uint8_t algorithm, size_t layer); +extern int picokeys_vault_clear_file(file_t *file); +extern int picokeys_vault_unenroll(file_t *file, file_t *label_file, uint8_t app_id); + +#endif // _PICOKEYS_VAULT_H_