mirror of
https://github.com/polhenarejos/pico-keys-sdk.git
synced 2026-08-26 22:48:44 +01:00
Move vault routines to SDK
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
This commit is contained in:
@@ -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)
|
||||
|
||||
+14
-508
@@ -21,79 +21,13 @@
|
||||
#include <string.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
+11
-42
@@ -23,54 +23,23 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#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_
|
||||
|
||||
+501
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "vault.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
+61
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _PICOKEYS_VAULT_H_
|
||||
#define _PICOKEYS_VAULT_H_
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#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_
|
||||
Reference in New Issue
Block a user