mirror of
https://github.com/polhenarejos/pico-keys-sdk.git
synced 2026-08-24 21:46:55 +01:00
Add object_crypto_provider.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
This commit is contained in:
@@ -318,6 +318,7 @@ list(APPEND PICOKEYS_SOURCES
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fs/object_store.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fs/object_store_txn.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fs/object_container.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fs/object_crypto_provider.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fs/flash.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fs/low_flash.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fs/phy.c
|
||||
|
||||
@@ -367,10 +367,27 @@ int file_object_record_header_parse(const uint8_t *record, size_t len, const fil
|
||||
uint32_t generation = get_uint32_be(record + FILE_OBJECT_RECORD_GENERATION_OFFSET);
|
||||
uint64_t nonce_record_id = get_uint64_be(record + FILE_OBJECT_RECORD_NONCE_OFFSET);
|
||||
uint32_t nonce_generation = get_uint32_be(record + FILE_OBJECT_RECORD_NONCE_OFFSET + sizeof(uint64_t));
|
||||
if (memcmp(record, file_object_record_magic, sizeof(file_object_record_magic)) != 0 || record[4] != FILE_OBJECT_RECORD_FORMAT_VERSION || record[FILE_OBJECT_RECORD_PROTECTION_OFFSET] != object->protection || get_uint16_be(record + FILE_OBJECT_RECORD_HEADER_SIZE_OFFSET) != FILE_OBJECT_RECORD_HEADER_SIZE || record_id != object->record_id || stored_size != object->stored_size || logical_size != object->logical_size || generation != object->generation || nonce_record_id != record_id || nonce_generation != generation) {
|
||||
if (memcmp(record, file_object_record_magic, sizeof(file_object_record_magic)) != 0
|
||||
|| record[4] != FILE_OBJECT_RECORD_FORMAT_VERSION
|
||||
|| record[FILE_OBJECT_RECORD_PROTECTION_OFFSET] != object->protection
|
||||
|| get_uint16_be(record + FILE_OBJECT_RECORD_HEADER_SIZE_OFFSET) != FILE_OBJECT_RECORD_HEADER_SIZE
|
||||
|| record_id != object->record_id
|
||||
|| stored_size != object->stored_size
|
||||
|| logical_size != object->logical_size
|
||||
|| generation != object->generation
|
||||
|| nonce_record_id != record_id
|
||||
|| nonce_generation != generation) {
|
||||
return PICOKEYS_WRONG_DATA;
|
||||
}
|
||||
*info = (file_object_record_info_t) { .record_id = record_id, .stored_size = stored_size, .logical_size = logical_size, .generation = generation, .protection = record[FILE_OBJECT_RECORD_PROTECTION_OFFSET], .payload_offset = FILE_OBJECT_RECORD_HEADER_SIZE, .tag_offset = FILE_OBJECT_RECORD_HEADER_SIZE + stored_size };
|
||||
*info = (file_object_record_info_t) {
|
||||
.record_id = record_id,
|
||||
.stored_size = stored_size,
|
||||
.logical_size = logical_size,
|
||||
.generation = generation,
|
||||
.protection = record[FILE_OBJECT_RECORD_PROTECTION_OFFSET],
|
||||
.payload_offset = FILE_OBJECT_RECORD_HEADER_SIZE,
|
||||
.tag_offset = FILE_OBJECT_RECORD_HEADER_SIZE + stored_size
|
||||
};
|
||||
return PICOKEYS_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,343 @@
|
||||
/*
|
||||
* 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 "picokeys.h"
|
||||
#include "object_crypto_provider.h"
|
||||
|
||||
#include <mbedtls/gcm.h>
|
||||
#include <mbedtls/hkdf.h>
|
||||
#include <mbedtls/platform_util.h>
|
||||
|
||||
#define FILE_OBJECT_CRYPTO_HMAC_SIZE 32u
|
||||
|
||||
#define FILE_OBJECT_CRYPTO_AAD_NAMESPACE_OFFSET 5u
|
||||
#define FILE_OBJECT_CRYPTO_AAD_KIND_OFFSET 7u
|
||||
#define FILE_OBJECT_CRYPTO_AAD_CONTAINER_ID_OFFSET 9u
|
||||
#define FILE_OBJECT_CRYPTO_AAD_TYPE_OFFSET 13u
|
||||
#define FILE_OBJECT_CRYPTO_AAD_TAG_OFFSET 15u
|
||||
#define FILE_OBJECT_CRYPTO_AAD_GENERATION_OFFSET 17u
|
||||
#define FILE_OBJECT_CRYPTO_AAD_LOGICAL_SIZE_OFFSET 21u
|
||||
#define FILE_OBJECT_CRYPTO_AAD_POLICY_ID_OFFSET 25u
|
||||
#define FILE_OBJECT_CRYPTO_AAD_POLICY_HASH_OFFSET 27u
|
||||
#define FILE_OBJECT_CRYPTO_AAD_KEY_DOMAIN_OFFSET 43u
|
||||
#define FILE_OBJECT_CRYPTO_AAD_PROTECTION_OFFSET 44u
|
||||
#define FILE_OBJECT_CRYPTO_AAD_FLAGS_OFFSET 45u
|
||||
#define FILE_OBJECT_CRYPTO_AAD_RECORD_ID_OFFSET 47u
|
||||
|
||||
static const uint8_t file_object_crypto_record_magic[4] = { 'P', 'K', 'O', 'R' };
|
||||
static const uint8_t file_object_crypto_domain_label[] = "PKOC/domain/v1";
|
||||
static const uint8_t file_object_crypto_key_label[] = "PKOC/object/v1";
|
||||
static const uint8_t file_object_crypto_manifest_label[] = "PKOC/manifest/v1";
|
||||
|
||||
static const mbedtls_md_info_t *file_object_crypto_sha256(void) {
|
||||
return mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
|
||||
}
|
||||
|
||||
static bool file_object_crypto_provider_valid(const file_object_crypto_provider_t *provider) {
|
||||
return provider && provider->initialized && provider->config.namespace_id != 0 && provider->config.load_root;
|
||||
}
|
||||
|
||||
static int file_object_crypto_load_root(file_object_crypto_provider_t *provider, uint8_t root[FILE_OBJECT_CRYPTO_ROOT_KEY_SIZE]) {
|
||||
int r = provider->config.load_root(provider->config.ctx, root);
|
||||
if (r != PICOKEYS_OK) {
|
||||
mbedtls_platform_zeroize(root, FILE_OBJECT_CRYPTO_ROOT_KEY_SIZE);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
static int file_object_crypto_derive_manifest_key(file_object_crypto_provider_t *provider, uint8_t key[FILE_OBJECT_CRYPTO_ROOT_KEY_SIZE]) {
|
||||
uint8_t root[FILE_OBJECT_CRYPTO_ROOT_KEY_SIZE] = { 0 };
|
||||
uint8_t info[sizeof(uint16_t) + sizeof(file_object_crypto_manifest_label) - 1];
|
||||
put_uint16_be(provider->config.namespace_id, info);
|
||||
memcpy(info + sizeof(uint16_t), file_object_crypto_manifest_label, sizeof(file_object_crypto_manifest_label) - 1);
|
||||
|
||||
int r = file_object_crypto_load_root(provider, root);
|
||||
if (r == PICOKEYS_OK && mbedtls_hkdf(file_object_crypto_sha256(), NULL, 0, root, sizeof(root), info, sizeof(info), key, FILE_OBJECT_CRYPTO_ROOT_KEY_SIZE) != 0) {
|
||||
r = PICOKEYS_EXEC_ERROR;
|
||||
}
|
||||
mbedtls_platform_zeroize(root, sizeof(root));
|
||||
mbedtls_platform_zeroize(info, sizeof(info));
|
||||
return r;
|
||||
}
|
||||
|
||||
static int file_object_crypto_derive_record_key(file_object_crypto_provider_t *provider, const file_object_record_identity_t *identity, const uint8_t aad[FILE_OBJECT_RECORD_AAD_SIZE], uint8_t key[FILE_OBJECT_CRYPTO_ROOT_KEY_SIZE]) {
|
||||
uint8_t root[FILE_OBJECT_CRYPTO_ROOT_KEY_SIZE] = { 0 };
|
||||
uint8_t domain_key[FILE_OBJECT_CRYPTO_ROOT_KEY_SIZE] = { 0 };
|
||||
uint8_t domain_info[sizeof(uint16_t) + sizeof(uint8_t) + sizeof(file_object_crypto_domain_label) - 1];
|
||||
uint8_t object_info[sizeof(file_object_crypto_key_label) - 1 + FILE_OBJECT_RECORD_AAD_SIZE];
|
||||
|
||||
put_uint16_be(identity->namespace_id, domain_info);
|
||||
domain_info[sizeof(uint16_t)] = identity->key_domain;
|
||||
memcpy(domain_info + sizeof(uint16_t) + sizeof(uint8_t), file_object_crypto_domain_label, sizeof(file_object_crypto_domain_label) - 1);
|
||||
memcpy(object_info, file_object_crypto_key_label, sizeof(file_object_crypto_key_label) - 1);
|
||||
memcpy(object_info + sizeof(file_object_crypto_key_label) - 1, aad, FILE_OBJECT_RECORD_AAD_SIZE);
|
||||
|
||||
int r = file_object_crypto_load_root(provider, root);
|
||||
if (r == PICOKEYS_OK && mbedtls_hkdf(file_object_crypto_sha256(), NULL, 0, root, sizeof(root), domain_info, sizeof(domain_info), domain_key, sizeof(domain_key)) != 0) {
|
||||
r = PICOKEYS_EXEC_ERROR;
|
||||
}
|
||||
if (r == PICOKEYS_OK && mbedtls_hkdf(file_object_crypto_sha256(), NULL, 0, domain_key, sizeof(domain_key), object_info, sizeof(object_info), key, FILE_OBJECT_CRYPTO_ROOT_KEY_SIZE) != 0) {
|
||||
r = PICOKEYS_EXEC_ERROR;
|
||||
}
|
||||
mbedtls_platform_zeroize(root, sizeof(root));
|
||||
mbedtls_platform_zeroize(domain_key, sizeof(domain_key));
|
||||
mbedtls_platform_zeroize(domain_info, sizeof(domain_info));
|
||||
mbedtls_platform_zeroize(object_info, sizeof(object_info));
|
||||
return r;
|
||||
}
|
||||
|
||||
static bool file_object_crypto_identity_valid(file_object_crypto_provider_t *provider, const file_object_record_identity_t *identity, const uint8_t nonce[FILE_OBJECT_RECORD_NONCE_SIZE], const uint8_t aad[FILE_OBJECT_RECORD_AAD_SIZE]) {
|
||||
if (!file_object_crypto_provider_valid(provider) || !identity || !nonce || !aad || identity->namespace_id != provider->config.namespace_id) {
|
||||
return false;
|
||||
}
|
||||
if (identity->generation == 0 || identity->record_id == 0 || identity->protection < FILE_OBJECT_PROTECTION_AUTHENTICATED_PUBLIC || identity->protection > FILE_OBJECT_PROTECTION_FIRMWARE_INTERNAL || (identity->flags & ~FILE_OBJECT_FLAG_MASK) != 0 || (identity->flags & FILE_OBJECT_FLAG_INLINE) != 0) {
|
||||
return false;
|
||||
}
|
||||
if (provider->config.identity_valid && !provider->config.identity_valid(provider->config.ctx, identity)) {
|
||||
return false;
|
||||
}
|
||||
if (memcmp(aad, file_object_crypto_record_magic, sizeof(file_object_crypto_record_magic)) != 0 || aad[4] != FILE_OBJECT_RECORD_FORMAT_VERSION) {
|
||||
return false;
|
||||
}
|
||||
if (get_uint16_be(aad + FILE_OBJECT_CRYPTO_AAD_NAMESPACE_OFFSET) != identity->namespace_id
|
||||
|| get_uint16_be(aad + FILE_OBJECT_CRYPTO_AAD_KIND_OFFSET) != identity->container_kind
|
||||
|| get_uint32_be(aad + FILE_OBJECT_CRYPTO_AAD_CONTAINER_ID_OFFSET) != identity->container_id
|
||||
|| get_uint16_be(aad + FILE_OBJECT_CRYPTO_AAD_TYPE_OFFSET) != identity->object_type
|
||||
|| get_uint16_be(aad + FILE_OBJECT_CRYPTO_AAD_TAG_OFFSET) != identity->object_tag
|
||||
|| get_uint32_be(aad + FILE_OBJECT_CRYPTO_AAD_GENERATION_OFFSET) != identity->generation
|
||||
|| get_uint32_be(aad + FILE_OBJECT_CRYPTO_AAD_LOGICAL_SIZE_OFFSET) != identity->logical_size
|
||||
|| get_uint16_be(aad + FILE_OBJECT_CRYPTO_AAD_POLICY_ID_OFFSET) != identity->policy_id
|
||||
|| memcmp(aad + FILE_OBJECT_CRYPTO_AAD_POLICY_HASH_OFFSET, identity->policy_hash, FILE_OBJECT_POLICY_HASH_SIZE) != 0
|
||||
|| aad[FILE_OBJECT_CRYPTO_AAD_KEY_DOMAIN_OFFSET] != identity->key_domain
|
||||
|| aad[FILE_OBJECT_CRYPTO_AAD_PROTECTION_OFFSET] != identity->protection
|
||||
|| get_uint16_be(aad + FILE_OBJECT_CRYPTO_AAD_FLAGS_OFFSET) != identity->flags
|
||||
|| get_uint64_be(aad + FILE_OBJECT_CRYPTO_AAD_RECORD_ID_OFFSET) != identity->record_id) {
|
||||
return false;
|
||||
}
|
||||
return get_uint64_be(nonce) == identity->record_id && get_uint32_be(nonce + sizeof(uint64_t)) == identity->generation;
|
||||
}
|
||||
|
||||
static bool file_object_crypto_tag_equal(const uint8_t *left, const uint8_t *right) {
|
||||
uint8_t difference = 0;
|
||||
for (size_t i = 0; i < FILE_OBJECT_AUTH_TAG_SIZE; i++) {
|
||||
difference |= left[i] ^ right[i];
|
||||
}
|
||||
return difference == 0;
|
||||
}
|
||||
|
||||
static int file_object_crypto_hmac(const uint8_t key[FILE_OBJECT_CRYPTO_ROOT_KEY_SIZE], const uint8_t nonce[FILE_OBJECT_RECORD_NONCE_SIZE], const uint8_t aad[FILE_OBJECT_RECORD_AAD_SIZE], const uint8_t *stored, size_t len, uint8_t tag[FILE_OBJECT_AUTH_TAG_SIZE]) {
|
||||
mbedtls_md_context_t md;
|
||||
mbedtls_md_init(&md);
|
||||
uint8_t full_tag[FILE_OBJECT_CRYPTO_HMAC_SIZE] = { 0 };
|
||||
int r = mbedtls_md_setup(&md, file_object_crypto_sha256(), 1);
|
||||
if (r == 0) {
|
||||
r = mbedtls_md_hmac_starts(&md, key, FILE_OBJECT_CRYPTO_ROOT_KEY_SIZE);
|
||||
}
|
||||
if (r == 0) {
|
||||
r = mbedtls_md_hmac_update(&md, aad, FILE_OBJECT_RECORD_AAD_SIZE);
|
||||
}
|
||||
if (r == 0) {
|
||||
r = mbedtls_md_hmac_update(&md, nonce, FILE_OBJECT_RECORD_NONCE_SIZE);
|
||||
}
|
||||
if (r == 0 && len > 0) {
|
||||
r = mbedtls_md_hmac_update(&md, stored, len);
|
||||
}
|
||||
if (r == 0) {
|
||||
r = mbedtls_md_hmac_finish(&md, full_tag);
|
||||
}
|
||||
if (r == 0) {
|
||||
memcpy(tag, full_tag, FILE_OBJECT_AUTH_TAG_SIZE);
|
||||
}
|
||||
mbedtls_platform_zeroize(full_tag, sizeof(full_tag));
|
||||
mbedtls_md_free(&md);
|
||||
return r == 0 ? PICOKEYS_OK : PICOKEYS_EXEC_ERROR;
|
||||
}
|
||||
|
||||
static int file_object_crypto_record_seal(void *ctx, const file_object_record_identity_t *identity, const uint8_t nonce[FILE_OBJECT_RECORD_NONCE_SIZE], const uint8_t aad[FILE_OBJECT_RECORD_AAD_SIZE], const uint8_t *plaintext, size_t len, uint8_t *stored, uint8_t tag[FILE_OBJECT_AUTH_TAG_SIZE]) {
|
||||
file_object_crypto_provider_t *provider = (file_object_crypto_provider_t *)ctx;
|
||||
if (!file_object_crypto_identity_valid(provider, identity, nonce, aad) || (!plaintext && len > 0) || !stored || !tag) {
|
||||
return PICOKEYS_WRONG_DATA;
|
||||
}
|
||||
|
||||
uint8_t key[FILE_OBJECT_CRYPTO_ROOT_KEY_SIZE] = { 0 };
|
||||
int r = file_object_crypto_derive_record_key(provider, identity, aad, key);
|
||||
if (r == PICOKEYS_OK && identity->protection == FILE_OBJECT_PROTECTION_AUTHENTICATED_PUBLIC) {
|
||||
if (len > 0) {
|
||||
memmove(stored, plaintext, len);
|
||||
}
|
||||
r = file_object_crypto_hmac(key, nonce, aad, stored, len, tag);
|
||||
}
|
||||
else if (r == PICOKEYS_OK) {
|
||||
mbedtls_gcm_context gcm;
|
||||
mbedtls_gcm_init(&gcm);
|
||||
uint8_t empty_input = 0;
|
||||
uint8_t empty_output = 0;
|
||||
const uint8_t *gcm_input = len > 0 ? plaintext : &empty_input;
|
||||
uint8_t *gcm_output = len > 0 ? stored : &empty_output;
|
||||
int crypto_result = mbedtls_gcm_setkey(&gcm, MBEDTLS_CIPHER_ID_AES, key, FILE_OBJECT_CRYPTO_ROOT_KEY_SIZE * 8u);
|
||||
if (crypto_result == 0) {
|
||||
crypto_result = mbedtls_gcm_crypt_and_tag(&gcm, MBEDTLS_GCM_ENCRYPT, len, nonce, FILE_OBJECT_RECORD_NONCE_SIZE, aad, FILE_OBJECT_RECORD_AAD_SIZE, gcm_input, gcm_output, FILE_OBJECT_AUTH_TAG_SIZE, tag);
|
||||
}
|
||||
mbedtls_gcm_free(&gcm);
|
||||
r = crypto_result == 0 ? PICOKEYS_OK : PICOKEYS_EXEC_ERROR;
|
||||
}
|
||||
mbedtls_platform_zeroize(key, sizeof(key));
|
||||
return r;
|
||||
}
|
||||
|
||||
static int file_object_crypto_record_unseal(void *ctx, const file_object_record_identity_t *identity, const uint8_t nonce[FILE_OBJECT_RECORD_NONCE_SIZE], const uint8_t aad[FILE_OBJECT_RECORD_AAD_SIZE], const uint8_t *stored, size_t len, const uint8_t tag[FILE_OBJECT_AUTH_TAG_SIZE], uint8_t *plaintext) {
|
||||
file_object_crypto_provider_t *provider = (file_object_crypto_provider_t *)ctx;
|
||||
if (!file_object_crypto_identity_valid(provider, identity, nonce, aad) || (!stored && len > 0) || !tag || (!plaintext && len > 0)) {
|
||||
return PICOKEYS_WRONG_DATA;
|
||||
}
|
||||
|
||||
uint8_t key[FILE_OBJECT_CRYPTO_ROOT_KEY_SIZE] = { 0 };
|
||||
int r = file_object_crypto_derive_record_key(provider, identity, aad, key);
|
||||
if (r == PICOKEYS_OK && identity->protection == FILE_OBJECT_PROTECTION_AUTHENTICATED_PUBLIC) {
|
||||
uint8_t calculated_tag[FILE_OBJECT_AUTH_TAG_SIZE] = { 0 };
|
||||
r = file_object_crypto_hmac(key, nonce, aad, stored, len, calculated_tag);
|
||||
if (r == PICOKEYS_OK && !file_object_crypto_tag_equal(calculated_tag, tag)) {
|
||||
r = PICOKEYS_WRONG_SIGNATURE;
|
||||
}
|
||||
if (r == PICOKEYS_OK && len > 0) {
|
||||
memmove(plaintext, stored, len);
|
||||
}
|
||||
mbedtls_platform_zeroize(calculated_tag, sizeof(calculated_tag));
|
||||
}
|
||||
else if (r == PICOKEYS_OK) {
|
||||
mbedtls_gcm_context gcm;
|
||||
mbedtls_gcm_init(&gcm);
|
||||
uint8_t empty_input = 0;
|
||||
uint8_t empty_output = 0;
|
||||
const uint8_t *gcm_input = len > 0 ? stored : &empty_input;
|
||||
uint8_t *gcm_output = len > 0 ? plaintext : &empty_output;
|
||||
int crypto_result = mbedtls_gcm_setkey(&gcm, MBEDTLS_CIPHER_ID_AES, key, FILE_OBJECT_CRYPTO_ROOT_KEY_SIZE * 8u);
|
||||
if (crypto_result == 0) {
|
||||
crypto_result = mbedtls_gcm_auth_decrypt(&gcm, len, nonce, FILE_OBJECT_RECORD_NONCE_SIZE, aad, FILE_OBJECT_RECORD_AAD_SIZE, tag, FILE_OBJECT_AUTH_TAG_SIZE, gcm_input, gcm_output);
|
||||
r = crypto_result == 0 ? PICOKEYS_OK : PICOKEYS_WRONG_SIGNATURE;
|
||||
}
|
||||
else {
|
||||
r = PICOKEYS_EXEC_ERROR;
|
||||
}
|
||||
mbedtls_gcm_free(&gcm);
|
||||
}
|
||||
if (r != PICOKEYS_OK && plaintext && len > 0) {
|
||||
mbedtls_platform_zeroize(plaintext, len);
|
||||
}
|
||||
mbedtls_platform_zeroize(key, sizeof(key));
|
||||
return r;
|
||||
}
|
||||
|
||||
static void file_object_crypto_auth_abort(void *ctx) {
|
||||
file_object_crypto_provider_t *provider = (file_object_crypto_provider_t *)ctx;
|
||||
if (!provider || !provider->initialized) {
|
||||
return;
|
||||
}
|
||||
mbedtls_md_free(&provider->manifest_md);
|
||||
mbedtls_md_init(&provider->manifest_md);
|
||||
provider->manifest_active = false;
|
||||
}
|
||||
|
||||
static int file_object_crypto_auth_start(void *ctx) {
|
||||
file_object_crypto_provider_t *provider = (file_object_crypto_provider_t *)ctx;
|
||||
if (!file_object_crypto_provider_valid(provider)) {
|
||||
return PICOKEYS_WRONG_DATA;
|
||||
}
|
||||
file_object_crypto_auth_abort(provider);
|
||||
|
||||
uint8_t key[FILE_OBJECT_CRYPTO_ROOT_KEY_SIZE] = { 0 };
|
||||
int r = file_object_crypto_derive_manifest_key(provider, key);
|
||||
if (r == PICOKEYS_OK && (mbedtls_md_setup(&provider->manifest_md, file_object_crypto_sha256(), 1) != 0 || mbedtls_md_hmac_starts(&provider->manifest_md, key, sizeof(key)) != 0)) {
|
||||
r = PICOKEYS_EXEC_ERROR;
|
||||
}
|
||||
mbedtls_platform_zeroize(key, sizeof(key));
|
||||
if (r != PICOKEYS_OK) {
|
||||
file_object_crypto_auth_abort(provider);
|
||||
return r;
|
||||
}
|
||||
provider->manifest_active = true;
|
||||
return PICOKEYS_OK;
|
||||
}
|
||||
|
||||
static int file_object_crypto_auth_update(void *ctx, const uint8_t *data, size_t len) {
|
||||
file_object_crypto_provider_t *provider = (file_object_crypto_provider_t *)ctx;
|
||||
if (!file_object_crypto_provider_valid(provider) || !provider->manifest_active || (!data && len > 0)) {
|
||||
return PICOKEYS_WRONG_DATA;
|
||||
}
|
||||
if (len == 0) {
|
||||
return PICOKEYS_OK;
|
||||
}
|
||||
return mbedtls_md_hmac_update(&provider->manifest_md, data, len) == 0 ? PICOKEYS_OK : PICOKEYS_EXEC_ERROR;
|
||||
}
|
||||
|
||||
static int file_object_crypto_auth_finish(void *ctx, uint8_t tag[FILE_OBJECT_AUTH_TAG_SIZE]) {
|
||||
file_object_crypto_provider_t *provider = (file_object_crypto_provider_t *)ctx;
|
||||
if (!file_object_crypto_provider_valid(provider) || !provider->manifest_active || !tag) {
|
||||
return PICOKEYS_WRONG_DATA;
|
||||
}
|
||||
|
||||
uint8_t full_tag[FILE_OBJECT_CRYPTO_HMAC_SIZE] = { 0 };
|
||||
int r = mbedtls_md_hmac_finish(&provider->manifest_md, full_tag) == 0 ? PICOKEYS_OK : PICOKEYS_EXEC_ERROR;
|
||||
if (r == PICOKEYS_OK) {
|
||||
memcpy(tag, full_tag, FILE_OBJECT_AUTH_TAG_SIZE);
|
||||
}
|
||||
mbedtls_platform_zeroize(full_tag, sizeof(full_tag));
|
||||
file_object_crypto_auth_abort(provider);
|
||||
return r;
|
||||
}
|
||||
|
||||
int file_object_crypto_provider_init(file_object_crypto_provider_t *provider, const file_object_crypto_provider_config_t *config) {
|
||||
if (!provider || !config || config->namespace_id == 0 || !config->load_root) {
|
||||
return PICOKEYS_ERR_NULL_PARAM;
|
||||
}
|
||||
|
||||
memset(provider, 0, sizeof(*provider));
|
||||
provider->config = *config;
|
||||
provider->manifest_authenticator = (file_object_authenticator_t) {
|
||||
.ctx = provider,
|
||||
.start = file_object_crypto_auth_start,
|
||||
.update = file_object_crypto_auth_update,
|
||||
.finish = file_object_crypto_auth_finish,
|
||||
.abort = file_object_crypto_auth_abort
|
||||
};
|
||||
provider->record_protector = (file_object_record_protector_t) {
|
||||
.ctx = provider,
|
||||
.seal = file_object_crypto_record_seal,
|
||||
.unseal = file_object_crypto_record_unseal
|
||||
};
|
||||
mbedtls_md_init(&provider->manifest_md);
|
||||
provider->initialized = true;
|
||||
return PICOKEYS_OK;
|
||||
}
|
||||
|
||||
void file_object_crypto_provider_deinit(file_object_crypto_provider_t *provider) {
|
||||
if (!provider) {
|
||||
return;
|
||||
}
|
||||
if (provider->initialized) {
|
||||
file_object_crypto_auth_abort(provider);
|
||||
}
|
||||
mbedtls_platform_zeroize(provider, sizeof(*provider));
|
||||
}
|
||||
|
||||
const file_object_authenticator_t *file_object_crypto_manifest_authenticator(file_object_crypto_provider_t *provider) {
|
||||
return file_object_crypto_provider_valid(provider) ? &provider->manifest_authenticator : NULL;
|
||||
}
|
||||
|
||||
const file_object_record_protector_t *file_object_crypto_record_protector(file_object_crypto_provider_t *provider) {
|
||||
return file_object_crypto_provider_valid(provider) ? &provider->record_protector : NULL;
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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 _OBJECT_CRYPTO_PROVIDER_H_
|
||||
#define _OBJECT_CRYPTO_PROVIDER_H_
|
||||
|
||||
#include "object_container.h"
|
||||
|
||||
#include <mbedtls/md.h>
|
||||
|
||||
#define FILE_OBJECT_CRYPTO_ROOT_KEY_SIZE 32u
|
||||
|
||||
/* The provider zeroizes the root buffer after every callback invocation. */
|
||||
typedef int (*file_object_crypto_root_load_t)(void *ctx, uint8_t root[FILE_OBJECT_CRYPTO_ROOT_KEY_SIZE]);
|
||||
/* This optional callback applies application-specific constraints after namespace validation. */
|
||||
typedef bool (*file_object_crypto_identity_valid_t)(void *ctx, const file_object_record_identity_t *identity);
|
||||
|
||||
typedef struct file_object_crypto_provider_config {
|
||||
void *ctx;
|
||||
uint16_t namespace_id;
|
||||
file_object_crypto_root_load_t load_root;
|
||||
file_object_crypto_identity_valid_t identity_valid;
|
||||
} file_object_crypto_provider_config_t;
|
||||
|
||||
/* One manifest authentication may be active per instance. Instances must not be copied after initialization. */
|
||||
typedef struct file_object_crypto_provider {
|
||||
file_object_crypto_provider_config_t config;
|
||||
file_object_authenticator_t manifest_authenticator;
|
||||
file_object_record_protector_t record_protector;
|
||||
mbedtls_md_context_t manifest_md;
|
||||
bool manifest_active;
|
||||
bool initialized;
|
||||
} file_object_crypto_provider_t;
|
||||
|
||||
int file_object_crypto_provider_init(file_object_crypto_provider_t *provider, const file_object_crypto_provider_config_t *config);
|
||||
void file_object_crypto_provider_deinit(file_object_crypto_provider_t *provider);
|
||||
const file_object_authenticator_t *file_object_crypto_manifest_authenticator(file_object_crypto_provider_t *provider);
|
||||
const file_object_record_protector_t *file_object_crypto_record_protector(file_object_crypto_provider_t *provider);
|
||||
|
||||
#endif // _OBJECT_CRYPTO_PROVIDER_H_
|
||||
+15
-3
@@ -69,7 +69,11 @@ static int file_object_parse(file_t *file, const file_object_id_t *object_id, fi
|
||||
return PICOKEYS_WRONG_DATA;
|
||||
}
|
||||
|
||||
*record = (file_object_record_t) { .payload_offset = FILE_OBJECT_HEADER_SIZE, .payload_size = payload_size, .generation = generation };
|
||||
*record = (file_object_record_t) {
|
||||
.payload_offset = FILE_OBJECT_HEADER_SIZE,
|
||||
.payload_size = payload_size,
|
||||
.generation = generation
|
||||
};
|
||||
return PICOKEYS_OK;
|
||||
}
|
||||
|
||||
@@ -136,7 +140,12 @@ int file_object_open(const file_object_id_t *object_id, file_object_handle_t *ha
|
||||
|
||||
for (size_t i = 0; i < FILE_OBJECT_MAX_HANDLES; i++) {
|
||||
if (file_object_handles[i].token == FILE_OBJECT_INVALID_HANDLE) {
|
||||
file_object_handles[i] = (file_object_handle_entry_t) { .object_id = *object_id, .generation = record.generation, .payload_size = record.payload_size, .token = file_object_allocate_token() };
|
||||
file_object_handles[i] = (file_object_handle_entry_t) {
|
||||
.object_id = *object_id,
|
||||
.generation = record.generation,
|
||||
.payload_size = record.payload_size,
|
||||
.token = file_object_allocate_token()
|
||||
};
|
||||
*handle = file_object_handles[i].token;
|
||||
return PICOKEYS_OK;
|
||||
}
|
||||
@@ -154,7 +163,10 @@ int file_object_get_info(file_object_handle_t handle, file_object_info_t *info)
|
||||
if (r != PICOKEYS_OK) {
|
||||
return r;
|
||||
}
|
||||
*info = (file_object_info_t) { .payload_size = record.payload_size, .generation = record.generation };
|
||||
*info = (file_object_info_t) {
|
||||
.payload_size = record.payload_size,
|
||||
.generation = record.generation
|
||||
};
|
||||
return PICOKEYS_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -172,7 +172,19 @@ static int file_object_txn_validate_record(const file_object_txn_layout_t *layou
|
||||
if (r != PICOKEYS_OK) {
|
||||
return r;
|
||||
}
|
||||
if (memcmp(header, file_object_txn_record_magic, sizeof(file_object_txn_record_magic)) != 0 || header[FILE_OBJECT_TXN_VERSION_OFFSET] != FILE_OBJECT_TXN_FORMAT_VERSION || header[FILE_OBJECT_TXN_HEADER_SIZE_OFFSET] != FILE_OBJECT_TXN_RECORD_HEADER_SIZE || header[FILE_OBJECT_TXN_SLOT_OFFSET] != slot || header[FILE_OBJECT_TXN_RECORD_RESERVED_OFFSET] != 0 || get_uint16_be(header + FILE_OBJECT_TXN_NAMESPACE_OFFSET) != layout->namespace_id || get_uint16_be(header + FILE_OBJECT_TXN_TYPE_OFFSET) != layout->object_type || get_uint32_be(header + FILE_OBJECT_TXN_OBJECT_ID_OFFSET) != layout->object_id || get_uint32_be(header + FILE_OBJECT_TXN_GENERATION_OFFSET) != generation || get_uint32_be(header + FILE_OBJECT_TXN_RECORD_ID_OFFSET) != record_id || get_uint32_be(header + FILE_OBJECT_TXN_PAYLOAD_SIZE_OFFSET) != payload_size || get_uint16_be(header + FILE_OBJECT_TXN_RECORD_FID_OFFSET) != layout->record_fid[slot] || get_uint16_be(header + FILE_OBJECT_TXN_RECORD_RESERVED_2_OFFSET) != 0) {
|
||||
if (memcmp(header, file_object_txn_record_magic, sizeof(file_object_txn_record_magic)) != 0
|
||||
|| header[FILE_OBJECT_TXN_VERSION_OFFSET] != FILE_OBJECT_TXN_FORMAT_VERSION
|
||||
|| header[FILE_OBJECT_TXN_HEADER_SIZE_OFFSET] != FILE_OBJECT_TXN_RECORD_HEADER_SIZE
|
||||
|| header[FILE_OBJECT_TXN_SLOT_OFFSET] != slot
|
||||
|| header[FILE_OBJECT_TXN_RECORD_RESERVED_OFFSET] != 0
|
||||
|| get_uint16_be(header + FILE_OBJECT_TXN_NAMESPACE_OFFSET) != layout->namespace_id
|
||||
|| get_uint16_be(header + FILE_OBJECT_TXN_TYPE_OFFSET) != layout->object_type
|
||||
|| get_uint32_be(header + FILE_OBJECT_TXN_OBJECT_ID_OFFSET) != layout->object_id
|
||||
|| get_uint32_be(header + FILE_OBJECT_TXN_GENERATION_OFFSET) != generation
|
||||
|| get_uint32_be(header + FILE_OBJECT_TXN_RECORD_ID_OFFSET) != record_id
|
||||
|| get_uint32_be(header + FILE_OBJECT_TXN_PAYLOAD_SIZE_OFFSET) != payload_size
|
||||
|| get_uint16_be(header + FILE_OBJECT_TXN_RECORD_FID_OFFSET) != layout->record_fid[slot]
|
||||
|| get_uint16_be(header + FILE_OBJECT_TXN_RECORD_RESERVED_2_OFFSET) != 0) {
|
||||
return PICOKEYS_WRONG_DATA;
|
||||
}
|
||||
|
||||
@@ -210,7 +222,16 @@ static int file_object_txn_parse_commit(const file_object_txn_layout_t *layout,
|
||||
uint32_t payload_size = get_uint32_be(commit + FILE_OBJECT_TXN_COMMIT_PAYLOAD_SIZE_OFFSET);
|
||||
uint16_t record_fid = get_uint16_be(commit + FILE_OBJECT_TXN_COMMIT_RECORD_FID_OFFSET);
|
||||
uint8_t record_slot = commit[FILE_OBJECT_TXN_COMMIT_RECORD_SLOT_OFFSET];
|
||||
if (memcmp(commit, file_object_txn_commit_magic, sizeof(file_object_txn_commit_magic)) != 0 || commit[FILE_OBJECT_TXN_VERSION_OFFSET] != FILE_OBJECT_TXN_FORMAT_VERSION || commit[FILE_OBJECT_TXN_HEADER_SIZE_OFFSET] != FILE_OBJECT_TXN_COMMIT_HEADER_SIZE || commit[FILE_OBJECT_TXN_SLOT_OFFSET] != slot || commit[FILE_OBJECT_TXN_COMMIT_MARKER_OFFSET] != FILE_OBJECT_TXN_COMMIT_MARKER || get_uint16_be(commit + FILE_OBJECT_TXN_NAMESPACE_OFFSET) != layout->namespace_id || get_uint16_be(commit + FILE_OBJECT_TXN_TYPE_OFFSET) != layout->object_type || get_uint32_be(commit + FILE_OBJECT_TXN_OBJECT_ID_OFFSET) != layout->object_id || generation == 0 || (flags & ~FILE_OBJECT_TXN_FLAG_DELETED) != 0) {
|
||||
if (memcmp(commit, file_object_txn_commit_magic, sizeof(file_object_txn_commit_magic)) != 0
|
||||
|| commit[FILE_OBJECT_TXN_VERSION_OFFSET] != FILE_OBJECT_TXN_FORMAT_VERSION
|
||||
|| commit[FILE_OBJECT_TXN_HEADER_SIZE_OFFSET] != FILE_OBJECT_TXN_COMMIT_HEADER_SIZE
|
||||
|| commit[FILE_OBJECT_TXN_SLOT_OFFSET] != slot
|
||||
|| commit[FILE_OBJECT_TXN_COMMIT_MARKER_OFFSET] != FILE_OBJECT_TXN_COMMIT_MARKER
|
||||
|| get_uint16_be(commit + FILE_OBJECT_TXN_NAMESPACE_OFFSET) != layout->namespace_id
|
||||
|| get_uint16_be(commit + FILE_OBJECT_TXN_TYPE_OFFSET) != layout->object_type
|
||||
|| get_uint32_be(commit + FILE_OBJECT_TXN_OBJECT_ID_OFFSET) != layout->object_id
|
||||
|| generation == 0
|
||||
|| (flags & ~FILE_OBJECT_TXN_FLAG_DELETED) != 0) {
|
||||
return PICOKEYS_WRONG_DATA;
|
||||
}
|
||||
|
||||
@@ -241,7 +262,14 @@ static int file_object_txn_parse_commit(const file_object_txn_layout_t *layout,
|
||||
}
|
||||
}
|
||||
|
||||
*state = (file_object_txn_state_t) { .generation = generation, .record_id = record_id, .payload_size = payload_size, .commit_slot = slot, .record_slot = record_slot, .deleted = deleted };
|
||||
*state = (file_object_txn_state_t) {
|
||||
.generation = generation,
|
||||
.record_id = record_id,
|
||||
.payload_size = payload_size,
|
||||
.commit_slot = slot,
|
||||
.record_slot = record_slot,
|
||||
.deleted = deleted
|
||||
};
|
||||
return PICOKEYS_OK;
|
||||
}
|
||||
|
||||
@@ -448,7 +476,11 @@ int file_object_txn_open(const file_object_txn_layout_t *layout, const file_obje
|
||||
|
||||
for (size_t i = 0; i < FILE_OBJECT_TXN_MAX_HANDLES; i++) {
|
||||
if (file_object_txn_handles[i].token == FILE_OBJECT_TXN_INVALID_HANDLE) {
|
||||
file_object_txn_handles[i] = (file_object_txn_handle_entry_t) { .layout = *layout, .state = state, .token = file_object_txn_allocate_token() };
|
||||
file_object_txn_handles[i] = (file_object_txn_handle_entry_t) {
|
||||
.layout = *layout,
|
||||
.state = state,
|
||||
.token = file_object_txn_allocate_token()
|
||||
};
|
||||
*handle = file_object_txn_handles[i].token;
|
||||
return PICOKEYS_OK;
|
||||
}
|
||||
@@ -466,7 +498,10 @@ int file_object_txn_get_info(file_object_txn_handle_t handle, const file_object_
|
||||
if (r != PICOKEYS_OK) {
|
||||
return r;
|
||||
}
|
||||
*info = (file_object_info_t) { .payload_size = state.payload_size, .generation = state.generation };
|
||||
*info = (file_object_info_t) {
|
||||
.payload_size = state.payload_size,
|
||||
.generation = state.generation
|
||||
};
|
||||
return PICOKEYS_OK;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user