Import Mbed OS hard-float snapshot

This commit is contained in:
Beslan
2026-06-01 20:15:04 +03:00
commit d3738e2f89
16278 changed files with 10628036 additions and 0 deletions

View File

@@ -0,0 +1,306 @@
/*
* Copyright (c) 2018 ARM Limited. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
* Licensed under the Apache License, Version 2.0 (the License); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an AS IS BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef MBED_SECURESTORE_H
#define MBED_SECURESTORE_H
#if !defined(MBEDTLS_CONFIG_FILE)
#include "mbedtls/config.h"
#else
#include MBEDTLS_CONFIG_FILE
#endif
#include "features/device_key/source/DeviceKey.h"
#define SECURESTORE_ENABLED 1
// Whole class is not supported if entropy, device key or required mbed TLS features are not enabled
#if !defined(MBEDTLS_ENTROPY_C) || !defined(MBEDTLS_CIPHER_MODE_CTR) || !defined(MBEDTLS_CMAC_C) || !DEVICEKEY_ENABLED
#undef SECURESTORE_ENABLED
#define SECURESTORE_ENABLED 0
#endif
#if SECURESTORE_ENABLED || defined(DOXYGEN_ONLY)
#include <stdint.h>
#include <stdio.h>
#include "KVStore.h"
#include "PlatformMutex.h"
// Forward declarations
struct mbedtls_entropy_context;
namespace mbed {
/** TDBStore class
*
* Lightweight Key Value storage over a block device
*/
class SecureStore : public KVStore {
public:
/**
* @brief Class constructor
*
* @param[in] underlying_kv KVStore that will hold the data.
* @param[in] rbp_kv Additional KVStore used for rollback protection.
*
* @returns none
*/
SecureStore(KVStore *underlying_kv, KVStore *rbp_kv = 0);
/**
* @brief Class destructor
*
* @returns none
*/
virtual ~SecureStore();
/**
* @brief Initialize SecureStore class. It will also initialize
* the underlying KVStore and the rollback protection KVStore.
*
* @returns MBED_SUCCESS Success.
* or any other error from underlying KVStore instances.
*/
virtual int init();
/**
* @brief Deinitialize SecureStore class, free handles and memory allocations.
*
* @returns MBED_SUCCESS Success.
* or any other error from underlying KVStore instances.
*/
virtual int deinit();
/**
* @brief Reset KVStore contents (clear all keys)
* Warning: This function is not thread safe.
*
* @returns MBED_SUCCESS Success.
* MBED_ERROR_NOT_READY Not initialized.
* or any other error from underlying KVStore instances.
*/
virtual int reset();
/**
* @brief Set one KVStore item, given key and value.
*
* @param[in] key Key - must not include '*' '/' '?' ':' ';' '\' '"' '|' ' ' '<' '>' '\'.
* @param[in] buffer Value data buffer.
* @param[in] size Value data size.
* @param[in] create_flags Flag mask - WRITE_ONCE_FLAG|REQUIRE_CONFIDENTIALITY_FLAG|
* REQUIRE_INTEGRITY_FLAG|REQUIRE_REPLAY_PROTECTION_FLAG
*
* @returns MBED_SUCCESS Success.
* MBED_ERROR_NOT_READY Not initialized.
* MBED_ERROR_READ_FAILED Unable to read from media.
* MBED_ERROR_INVALID_ARGUMENT Invalid argument given in function arguments.
* MBED_ERROR_INVALID_SIZE Invalid size given in function arguments.
* MBED_ERROR_WRITE_PROTECTED Already stored with "write once" flag.
* MBED_ERROR_FAILED_OPERATION Internal error.
* or any other error from underlying KVStore instances.
*/
virtual int set(const char *key, const void *buffer, size_t size, uint32_t create_flags);
/**
* @brief Get one KVStore item, given key.
*
* @param[in] key Key - must not include '*' '/' '?' ':' ';' '\' '"' '|' ' ' '<' '>' '\'.
* @param[in] buffer Value data buffer.
* @param[in] buffer_size Value data buffer size.
* @param[out] actual_size Actual read size.
* @param[in] offset Offset to read from in data.
*
* @returns MBED_SUCCESS Success.
* MBED_ERROR_NOT_READY Not initialized.
* MBED_ERROR_READ_FAILED Unable to read from media.
* MBED_ERROR_INVALID_ARGUMENT Invalid argument given in function arguments.
* MBED_ERROR_INVALID_SIZE Invalid size given in function arguments.
* MBED_ERROR_FAILED_OPERATION Internal error.
* MBED_ERROR_ITEM_NOT_FOUND No such key.
* MBED_ERROR_AUTHENTICATION_FAILED Data authentication failed.
* MBED_ERROR_AUTHENTICATION_RBP_FAILED
* Rollback protection data authentication failed.
* or any other error from underlying KVStore instances.
*/
virtual int get(const char *key, void *buffer, size_t buffer_size, size_t *actual_size = NULL,
size_t offset = 0);
/**
* @brief Get information of a given key.
*
* @param[in] key Key - must not include '*' '/' '?' ':' ';' '\' '"' '|' ' ' '<' '>' '\'.
* @param[out] info Returned information structure containing size and flags.
*
* @returns MBED_SUCCESS Success.
* MBED_ERROR_NOT_READY Not initialized.
* MBED_ERROR_READ_FAILED Unable to read from media.
* MBED_ERROR_INVALID_ARGUMENT Invalid argument given in function arguments.
* MBED_ERROR_FAILED_OPERATION Internal error.
* MBED_ERROR_ITEM_NOT_FOUND No such key.
* MBED_ERROR_AUTHENTICATION_FAILED Data authentication failed.
* MBED_ERROR_AUTHENTICATION_RBP_FAILED
* Rollback protection data authentication failed.
* or any other error from underlying KVStore instances.
*/
virtual int get_info(const char *key, info_t *info);
/**
* @brief Remove a KVStore item, given key.
*
* @param[in] key Key - must not include '*' '/' '?' ':' ';' '\' '"' '|' ' ' '<' '>' '\'.
*
* @returns MBED_SUCCESS Success.
* MBED_ERROR_NOT_READY Not initialized.
* MBED_ERROR_READ_FAILED Unable to read from media.
* MBED_ERROR_INVALID_ARGUMENT Invalid argument given in function arguments.
* MBED_ERROR_WRITE_PROTECTED Already stored with "write once" flag.
* MBED_ERROR_FAILED_OPERATION Internal error.
* or any other error from underlying KVStore instances.
*/
virtual int remove(const char *key);
/**
* @brief Start an incremental KVStore set sequence. This operation is blocking other operations.
* Any get/set/remove/iterator operation will be blocked until set_finalize is called.
*
* @param[out] handle Returned incremental set handle.
* @param[in] key Key - must not include '*' '/' '?' ':' ';' '\' '"' '|' ' ' '<' '>' '\'.
* @param[in] final_data_size Final value data size.
* @param[in] create_flags Flag mask - WRITE_ONCE_FLAG|REQUIRE_CONFIDENTIALITY_FLAG|
* REQUIRE_INTEGRITY_FLAG|REQUIRE_REPLAY_PROTECTION_FLAG
*
* @returns MBED_SUCCESS Success.
* MBED_ERROR_NOT_READY Not initialized.
* MBED_ERROR_READ_FAILED Unable to read from media.
* MBED_ERROR_INVALID_ARGUMENT Invalid argument given in function arguments.
* MBED_ERROR_INVALID_SIZE Invalid size given in function arguments.
* MBED_ERROR_WRITE_PROTECTED Already stored with "write once" flag.
* MBED_ERROR_FAILED_OPERATION Internal error.
* or any other error from underlying KVStore instances.
*/
virtual int set_start(set_handle_t *handle, const char *key, size_t final_data_size, uint32_t create_flags);
/**
* @brief Add data to incremental KVStore set sequence. This operation is blocking other operations.
* Any get/set/remove operation will be blocked until set_finalize is called.
*
* @param[in] handle Incremental set handle.
* @param[in] value_data value data to add.
* @param[in] data_size value data size.
*
* @returns MBED_SUCCESS Success.
* MBED_ERROR_NOT_READY Not initialized.
* MBED_ERROR_INVALID_ARGUMENT Invalid argument given in function arguments.
* MBED_ERROR_INVALID_SIZE Invalid size given in function arguments.
* MBED_ERROR_FAILED_OPERATION Internal error.
* or any other error from underlying KVStore instances.
*/
virtual int set_add_data(set_handle_t handle, const void *value_data, size_t data_size);
/**
* @brief Finalize an incremental KVStore set sequence.
*
* @param[in] handle Incremental set handle.
*
* @returns MBED_SUCCESS Success.
* MBED_ERROR_NOT_READY Not initialized.
* MBED_ERROR_INVALID_ARGUMENT Invalid argument given in function arguments.
* MBED_ERROR_INVALID_SIZE Invalid size given in function arguments.
* MBED_ERROR_FAILED_OPERATION Internal error.
* or any other error from underlying KVStore instances.
*/
virtual int set_finalize(set_handle_t handle);
/**
* @brief Start an iteration over KVStore keys.
* There are no issue with any other operation while iterator is open.
*
* @param[out] it Returned iterator handle.
* @param[in] prefix Key prefix (null for all keys).
*
* @returns MBED_SUCCESS Success.
* MBED_ERROR_NOT_READY Not initialized.
* MBED_ERROR_INVALID_ARGUMENT Invalid argument given in function arguments.
* or any other error from underlying KVStore instances.
*/
virtual int iterator_open(iterator_t *it, const char *prefix = NULL);
/**
* @brief Get next key in iteration.
* There are no issue with any other operation while iterator is open.
*
* @param[in] it Iterator handle.
* @param[in] key Buffer for returned key.
* @param[in] key_size Key buffer size.
*
* @returns MBED_SUCCESS Success.
* MBED_ERROR_NOT_READY Not initialized.
* MBED_ERROR_INVALID_ARGUMENT Invalid argument given in function arguments.
* or any other error from underlying KVStore instances.
*/
virtual int iterator_next(iterator_t it, char *key, size_t key_size);
/**
* @brief Close iteration.
*
* @returns MBED_SUCCESS Success.
* MBED_ERROR_NOT_READY Not initialized.
* MBED_ERROR_INVALID_ARGUMENT Invalid argument given in function arguments.
* or any other error from underlying KVStore instances.
*
* @returns 0 on success or a negative error code on failure
*/
virtual int iterator_close(iterator_t it);
#if !defined(DOXYGEN_ONLY)
private:
// Forward declaration
struct inc_set_handle_t;
PlatformMutex _mutex;
bool _is_initialized;
KVStore *_underlying_kv, *_rbp_kv;
mbedtls_entropy_context *_entropy;
inc_set_handle_t *_ih;
uint8_t *_scratch_buf;
/**
* @brief Actual get function, serving get and get_info APIs.
*
* @param[in] key Key - must not include '*' '/' '?' ':' ';' '\' '"' '|' ' ' '<' '>' '\'.
* @param[in] buffer Value data buffer.
* @param[in] buffer_size Value data buffer size.
* @param[out] actual_size Actual read size.
* @param[in] offset Offset to read from in data.
* @param[out] info Returned information structure.
*
* @returns 0 on success or a negative error code on failure
*/
int do_get(const char *key, void *buffer, size_t buffer_size, size_t *actual_size = NULL,
size_t offset = 0, info_t *info = 0);
#endif
};
/** @}*/
} // namespace mbed
#endif
#endif

View File

@@ -0,0 +1,6 @@
{
"name": "SecureStore",
"macros": ["MBEDTLS_CIPHER_MODE_CTR"],
"config": {
}
}

View File

@@ -0,0 +1,894 @@
/*
* Copyright (c) 2018 ARM Limited. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
* Licensed under the Apache License, Version 2.0 (the License); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an AS IS BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// ----------------------------------------------------------- Includes -----------------------------------------------------------
#include "securestore/SecureStore.h"
#if SECURESTORE_ENABLED
#include "aes.h"
#include "cmac.h"
#include "mbedtls/platform.h"
#include "entropy.h"
#include "DeviceKey.h"
#include "mbed_assert.h"
#include "mbed_wait_api.h"
#include "mbed_error.h"
#include <algorithm>
#include <string.h>
#include <stdio.h>
using namespace mbed;
// --------------------------------------------------------- Definitions ----------------------------------------------------------
static const uint32_t securestore_revision = 1;
static const uint32_t enc_block_size = 16;
static const uint32_t cmac_size = 16;
static const uint32_t iv_size = 8;
static const uint32_t scratch_buf_size = 256;
static const uint32_t derived_key_size = 16;
static const char *const enc_prefix = "ENC";
static const char *const auth_prefix = "AUTH";
static const uint32_t security_flags = KVStore::REQUIRE_CONFIDENTIALITY_FLAG | KVStore::REQUIRE_REPLAY_PROTECTION_FLAG;
namespace {
typedef struct {
uint16_t metadata_size = 0u;
uint16_t revision = 0u;
uint32_t data_size = 0u;
uint32_t create_flags = 0u;
uint8_t iv[iv_size] = { 0u };
} record_metadata_t;
// iterator handle
typedef struct {
KVStore::iterator_t underlying_it;
} key_iterator_handle_t;
}
// incremental set handle
struct SecureStore::inc_set_handle_t {
record_metadata_t metadata;
char *key = nullptr;
uint32_t offset_in_data = 0u;
uint8_t ctr_buf[enc_block_size] = { 0u };
mbedtls_aes_context enc_ctx;
mbedtls_cipher_context_t auth_ctx;
KVStore::set_handle_t underlying_handle;
};
// -------------------------------------------------- Local Functions Declaration ----------------------------------------------------
// -------------------------------------------------- Functions Implementation ----------------------------------------------------
int encrypt_decrypt_start(mbedtls_aes_context &enc_aes_ctx, uint8_t *iv, const char *key,
uint8_t *ctr_buf, uint8_t *salt_buf, int salt_buf_size)
{
DeviceKey &devkey = DeviceKey::get_instance();
char *salt = reinterpret_cast<char *>(salt_buf);
uint8_t encrypt_key[derived_key_size];
strcpy(salt, enc_prefix);
int pos = strlen(enc_prefix);
strncpy(salt + pos, key, salt_buf_size - pos - 1);
salt_buf[salt_buf_size - 1] = 0;
int os_ret = devkey.generate_derived_key(salt_buf, strlen(salt), encrypt_key, DEVICE_KEY_16BYTE);
if (os_ret) {
return os_ret;
}
mbedtls_aes_init(&enc_aes_ctx);
mbedtls_aes_setkey_enc(&enc_aes_ctx, encrypt_key, enc_block_size * 8);
memcpy(ctr_buf, iv, iv_size);
memset(ctr_buf + iv_size, 0, iv_size);
return 0;
}
int encrypt_decrypt_data(mbedtls_aes_context &enc_aes_ctx, const uint8_t *in_buf,
uint8_t *out_buf, uint32_t chunk_size, uint8_t *ctr_buf, size_t &aes_offs)
{
uint8_t stream_block[enc_block_size] = { 0 };
return mbedtls_aes_crypt_ctr(&enc_aes_ctx, chunk_size, &aes_offs, ctr_buf,
stream_block, in_buf, out_buf);
}
int cmac_calc_start(mbedtls_cipher_context_t &auth_ctx, const char *key, uint8_t *salt_buf, int salt_buf_size)
{
DeviceKey &devkey = DeviceKey::get_instance();
char *salt = reinterpret_cast<char *>(salt_buf);
uint8_t auth_key[derived_key_size];
strcpy(salt, auth_prefix);
int pos = strlen(auth_prefix);
strncpy(salt + pos, key, salt_buf_size - pos - 1);
salt_buf[salt_buf_size - 1] = 0;
int os_ret = devkey.generate_derived_key(salt_buf, strlen(salt), auth_key, DEVICE_KEY_16BYTE);
if (os_ret) {
return os_ret;
}
const mbedtls_cipher_info_t *cipher_info = mbedtls_cipher_info_from_type(MBEDTLS_CIPHER_AES_128_ECB);
mbedtls_cipher_init(&auth_ctx);
if ((os_ret = mbedtls_cipher_setup(&auth_ctx, cipher_info)) != 0) {
return os_ret;
}
os_ret = mbedtls_cipher_cmac_starts(&auth_ctx, auth_key, cmac_size * 8);
if (os_ret != 0) {
return os_ret;
}
return 0;
}
int cmac_calc_data(mbedtls_cipher_context_t &auth_ctx, const void *input, size_t ilen)
{
int os_ret;
os_ret = mbedtls_cipher_cmac_update(&auth_ctx, static_cast<const uint8_t *>(input), ilen);
return os_ret;
}
int cmac_calc_finish(mbedtls_cipher_context_t &auth_ctx, uint8_t *output)
{
int os_ret;
os_ret = mbedtls_cipher_cmac_finish(&auth_ctx, output);
return os_ret;
}
// Class member functions
SecureStore::SecureStore(KVStore *underlying_kv, KVStore *rbp_kv) :
_is_initialized(false), _underlying_kv(underlying_kv), _rbp_kv(rbp_kv), _entropy(0),
_ih(0), _scratch_buf(0)
{
}
SecureStore::~SecureStore()
{
deinit();
}
int SecureStore::set_start(set_handle_t *handle, const char *key, size_t final_data_size,
uint32_t create_flags)
{
int ret, os_ret;
info_t info;
bool enc_started = false, auth_started = false;
if (!_is_initialized) {
return MBED_ERROR_NOT_READY;
}
if (!is_valid_key(key)) {
return MBED_ERROR_INVALID_ARGUMENT;
}
_mutex.lock();
*handle = reinterpret_cast<set_handle_t>(_ih);
// Validate internal RBP data
if (_rbp_kv) {
ret = _rbp_kv->get_info(key, &info);
if (ret == MBED_SUCCESS) {
if (info.flags & WRITE_ONCE_FLAG) {
// Trying to re-write a key that is write protected
ret = MBED_ERROR_WRITE_PROTECTED;
goto fail;
}
if (!(create_flags & REQUIRE_REPLAY_PROTECTION_FLAG)) {
// Trying to re-write a key that that has REPLAY_PROTECTION
// with a new key that has this flag not set.
ret = MBED_ERROR_INVALID_ARGUMENT;
goto fail;
}
} else if (ret != MBED_ERROR_ITEM_NOT_FOUND) {
ret = MBED_ERROR_READ_FAILED;
goto fail;
}
} else {
// Only trust external flags, if internal RBP is not in use
ret = _underlying_kv->get(key, &_ih->metadata, sizeof(record_metadata_t));
if (ret == MBED_SUCCESS) {
// Must not remove RP flag, even though internal RBP KV is not in use.
if (!(create_flags & REQUIRE_REPLAY_PROTECTION_FLAG) && (_ih->metadata.create_flags & REQUIRE_REPLAY_PROTECTION_FLAG)) {
ret = MBED_ERROR_INVALID_ARGUMENT;
goto fail;
}
// Existing key is write protected
if (_ih->metadata.create_flags & WRITE_ONCE_FLAG) {
ret = MBED_ERROR_WRITE_PROTECTED;
goto fail;
}
}
}
// Fill metadata
_ih->metadata.create_flags = create_flags;
_ih->metadata.data_size = final_data_size;
_ih->metadata.metadata_size = sizeof(record_metadata_t);
_ih->metadata.revision = securestore_revision;
if (create_flags & REQUIRE_CONFIDENTIALITY_FLAG) {
// generate a new random iv
os_ret = mbedtls_entropy_func(_entropy, _ih->metadata.iv, iv_size);
if (os_ret) {
ret = MBED_ERROR_FAILED_OPERATION;
goto fail;
}
os_ret = encrypt_decrypt_start(_ih->enc_ctx, _ih->metadata.iv, key, _ih->ctr_buf, _scratch_buf,
scratch_buf_size);
if (os_ret) {
ret = MBED_ERROR_FAILED_OPERATION;
goto fail;
}
enc_started = true;
} else {
memset(_ih->metadata.iv, 0, iv_size);
}
os_ret = cmac_calc_start(_ih->auth_ctx, key, _scratch_buf, scratch_buf_size);
if (os_ret) {
ret = MBED_ERROR_FAILED_OPERATION;
goto fail;
}
auth_started = true;
// Although name is not part of the data, we calculate CMAC on it as well
os_ret = cmac_calc_data(_ih->auth_ctx, key, strlen(key));
if (os_ret) {
ret = MBED_ERROR_FAILED_OPERATION;
goto fail;
}
os_ret = cmac_calc_data(_ih->auth_ctx, &_ih->metadata, sizeof(record_metadata_t));
if (os_ret) {
ret = MBED_ERROR_FAILED_OPERATION;
goto fail;
}
_ih->offset_in_data = 0;
_ih->key = 0;
// Should strip security flags from underlying storage
ret = _underlying_kv->set_start(&_ih->underlying_handle, key,
sizeof(record_metadata_t) + final_data_size + cmac_size,
create_flags & ~security_flags);
if (ret) {
goto fail;
}
ret = _underlying_kv->set_add_data(_ih->underlying_handle, &_ih->metadata,
sizeof(record_metadata_t));
if (ret) {
goto fail;
}
if (create_flags & (REQUIRE_REPLAY_PROTECTION_FLAG | WRITE_ONCE_FLAG)) {
_ih->key = new char[strlen(key) + 1];
strcpy(_ih->key, key);
}
goto end;
fail:
if (enc_started) {
mbedtls_aes_free(&_ih->enc_ctx);
}
if (auth_started) {
mbedtls_cipher_free(&_ih->auth_ctx);
}
// mark handle as invalid by clearing metadata size field in header
_ih->metadata.metadata_size = 0;
_mutex.unlock();
end:
return ret;
}
int SecureStore::set_add_data(set_handle_t handle, const void *value_data, size_t data_size)
{
size_t aes_offs = 0;
int os_ret, ret = MBED_SUCCESS;
const uint8_t *src_ptr;
if (reinterpret_cast<inc_set_handle_t *>(handle) != _ih) {
return MBED_ERROR_INVALID_ARGUMENT;
}
if (!value_data && data_size) {
return MBED_ERROR_INVALID_ARGUMENT;
}
if (!_ih->metadata.metadata_size) {
return MBED_ERROR_INVALID_ARGUMENT;
}
if (_ih->offset_in_data + data_size > _ih->metadata.data_size) {
ret = MBED_ERROR_INVALID_SIZE;
goto end;
}
src_ptr = static_cast<const uint8_t *>(value_data);
while (data_size) {
uint32_t chunk_size;
const uint8_t *dst_ptr;
if (_ih->metadata.create_flags & REQUIRE_CONFIDENTIALITY_FLAG) {
// In encrypt mode we don't want to allocate a buffer in the size given by the user -
// Encrypt the data chunk by chunk
chunk_size = std::min((uint32_t) data_size, scratch_buf_size);
dst_ptr = _scratch_buf;
os_ret = encrypt_decrypt_data(_ih->enc_ctx, src_ptr, _scratch_buf,
chunk_size, _ih->ctr_buf, aes_offs);
if (os_ret) {
ret = MBED_ERROR_FAILED_OPERATION;
goto fail;
}
} else {
chunk_size = data_size;
dst_ptr = static_cast <const uint8_t *>(value_data);
}
os_ret = cmac_calc_data(_ih->auth_ctx, dst_ptr, chunk_size);
if (os_ret) {
ret = MBED_ERROR_FAILED_OPERATION;
goto fail;
}
ret = _underlying_kv->set_add_data(_ih->underlying_handle, dst_ptr, chunk_size);
if (ret) {
goto fail;
}
data_size -= chunk_size;
src_ptr += chunk_size;
_ih->offset_in_data += chunk_size;
}
goto end;
fail:
if (_ih->key) {
delete[] _ih->key;
}
if (_ih->metadata.create_flags & REQUIRE_CONFIDENTIALITY_FLAG) {
mbedtls_aes_free(&_ih->enc_ctx);
}
mbedtls_cipher_free(&_ih->auth_ctx);
// mark handle as invalid by clearing metadata size field in header
_ih->metadata.metadata_size = 0;
_mutex.unlock();
end:
return ret;
}
int SecureStore::set_finalize(set_handle_t handle)
{
int os_ret, ret = MBED_SUCCESS;
uint8_t cmac[cmac_size] = {0};
if (reinterpret_cast<inc_set_handle_t *>(handle) != _ih) {
return MBED_ERROR_INVALID_ARGUMENT;
}
if (!_ih->metadata.metadata_size) {
return MBED_ERROR_INVALID_ARGUMENT;
}
if (_ih->offset_in_data != _ih->metadata.data_size) {
ret = MBED_ERROR_INVALID_SIZE;
goto end;
}
os_ret = cmac_calc_finish(_ih->auth_ctx, cmac);
if (os_ret) {
ret = MBED_ERROR_FAILED_OPERATION;
goto end;
}
ret = _underlying_kv->set_add_data(_ih->underlying_handle, cmac, cmac_size);
if (ret) {
goto end;
}
ret = _underlying_kv->set_finalize(_ih->underlying_handle);
if (ret) {
goto end;
}
if (_rbp_kv && (_ih->metadata.create_flags & (REQUIRE_REPLAY_PROTECTION_FLAG | WRITE_ONCE_FLAG))) {
// In rollback protect case, we need to store CMAC in RBP store.
// If it's also write once case, set write once flag in the RBP key as well.
// Use RBP storage also in write once case only - in order to prevent attacks removing
// a written once value from underlying KV.
ret = _rbp_kv->set(_ih->key, cmac, cmac_size, _ih->metadata.create_flags & WRITE_ONCE_FLAG);
delete[] _ih->key;
if (ret) {
goto end;
}
}
end:
// mark handle as invalid by clearing metadata size field in header
_ih->metadata.metadata_size = 0;
if (_ih->metadata.create_flags & REQUIRE_CONFIDENTIALITY_FLAG) {
mbedtls_aes_free(&_ih->enc_ctx);
}
mbedtls_cipher_free(&_ih->auth_ctx);
_mutex.unlock();
return ret;
}
int SecureStore::set(const char *key, const void *buffer, size_t size, uint32_t create_flags)
{
int ret;
set_handle_t handle;
// Don't wait till we get to set_add_data to catch this
if (!buffer && size) {
return MBED_ERROR_INVALID_ARGUMENT;
}
ret = set_start(&handle, key, size, create_flags);
if (ret) {
return ret;
}
ret = set_add_data(handle, buffer, size);
if (ret) {
return ret;
}
ret = set_finalize(handle);
return ret;
}
int SecureStore::remove(const char *key)
{
info_t info;
_mutex.lock();
int ret = do_get(key, 0, 0, 0, 0, &info);
// Allow deleting key if read error is of our own errors
if ((ret != MBED_SUCCESS) && (ret != MBED_ERROR_AUTHENTICATION_FAILED) &&
(ret != MBED_ERROR_RBP_AUTHENTICATION_FAILED)) {
goto end;
}
if (ret == 0 && info.flags & WRITE_ONCE_FLAG) {
ret = MBED_ERROR_WRITE_PROTECTED;
goto end;
}
ret = _underlying_kv->remove(key);
if (ret) {
goto end;
}
if (_rbp_kv && (info.flags & REQUIRE_REPLAY_PROTECTION_FLAG)) {
ret = _rbp_kv->remove(key);
if ((ret != MBED_SUCCESS) && (ret != MBED_ERROR_ITEM_NOT_FOUND)) {
goto end;
}
}
ret = MBED_SUCCESS;
end:
_mutex.unlock();
return ret;
}
int SecureStore::do_get(const char *key, void *buffer, size_t buffer_size, size_t *actual_size,
size_t offset, info_t *info)
{
int os_ret, ret;
bool rbp_key_exists = false;
uint8_t rbp_cmac[cmac_size];
size_t aes_offs = 0;
uint32_t data_size;
uint32_t actual_data_size;
uint32_t current_offset;
uint32_t chunk_size;
uint32_t enc_lead_size;
uint8_t *dest_buf;
bool enc_started = false, auth_started = false;
uint32_t create_flags;
size_t read_len;
info_t rbp_info;
if (!is_valid_key(key)) {
return MBED_ERROR_INVALID_ARGUMENT;
}
if (_rbp_kv) {
ret = _rbp_kv->get_info(key, &rbp_info);
if (ret == MBED_SUCCESS) {
rbp_key_exists = true;
ret = _rbp_kv->get(key, rbp_cmac, cmac_size, &read_len);
if (ret) {
goto end;
}
if ((read_len != cmac_size) || (rbp_info.size != cmac_size)) {
ret = MBED_ERROR_RBP_AUTHENTICATION_FAILED;
}
} else if (ret != MBED_ERROR_ITEM_NOT_FOUND) {
goto end;
}
}
ret = _underlying_kv->get(key, &_ih->metadata, sizeof(record_metadata_t), &read_len);
if (ret) {
// In case we have the key in the RBP KV, then even if the key wasn't found in
// the underlying KV, we may have been exposed to an attack. Return an RBP authentication error.
if (rbp_key_exists) {
ret = MBED_ERROR_RBP_AUTHENTICATION_FAILED;
}
goto end;
}
// Validate header size
if ((read_len != sizeof(record_metadata_t)) || (_ih->metadata.metadata_size != sizeof(record_metadata_t))) {
ret = MBED_ERROR_RBP_AUTHENTICATION_FAILED;
goto end;
}
create_flags = _ih->metadata.create_flags;
if (!_rbp_kv) {
create_flags &= ~REQUIRE_REPLAY_PROTECTION_FLAG;
}
// Another potential attack case - key hasn't got the RP flag set, but exists in the RBP KV
if (rbp_key_exists && !(create_flags & (REQUIRE_REPLAY_PROTECTION_FLAG | WRITE_ONCE_FLAG))) {
ret = MBED_ERROR_RBP_AUTHENTICATION_FAILED;
goto end;
}
os_ret = cmac_calc_start(_ih->auth_ctx, key, _scratch_buf, scratch_buf_size);
if (os_ret) {
ret = MBED_ERROR_FAILED_OPERATION;
goto end;
}
auth_started = true;
// Although name is not part of the data, we calculate CMAC on it as well
os_ret = cmac_calc_data(_ih->auth_ctx, key, strlen(key));
if (os_ret) {
ret = MBED_ERROR_FAILED_OPERATION;
goto end;
}
os_ret = cmac_calc_data(_ih->auth_ctx, &_ih->metadata, sizeof(record_metadata_t));
if (os_ret) {
ret = MBED_ERROR_FAILED_OPERATION;
goto end;
}
if (create_flags & REQUIRE_CONFIDENTIALITY_FLAG) {
os_ret = encrypt_decrypt_start(_ih->enc_ctx, _ih->metadata.iv, key, _ih->ctr_buf, _scratch_buf,
scratch_buf_size);
if (os_ret) {
ret = MBED_ERROR_FAILED_OPERATION;
goto end;
}
enc_started = true;
}
data_size = _ih->metadata.data_size;
actual_data_size = std::min((uint32_t) buffer_size, data_size - offset);
current_offset = 0;
enc_lead_size = 0;
while (data_size) {
// Make sure we read to the user buffer only between offset and offset + actual_data_size
if ((current_offset >= offset) && (current_offset < offset + actual_data_size)) {
dest_buf = (static_cast <uint8_t *>(buffer)) + enc_lead_size;
chunk_size = actual_data_size - enc_lead_size;
enc_lead_size = 0;
} else {
dest_buf = _scratch_buf;
if (current_offset < offset) {
chunk_size = std::min(scratch_buf_size, offset - current_offset);
// A special case: encrypted user data starts at a middle of an encryption block.
// In this case, we need to read entire block into our scratch buffer, and copy
// the encrypted lead size to the user buffer start
if ((create_flags & REQUIRE_CONFIDENTIALITY_FLAG) &&
(chunk_size % enc_block_size)) {
enc_lead_size = std::min(enc_block_size - chunk_size % enc_block_size, actual_data_size);
chunk_size += enc_lead_size;
}
} else {
chunk_size = std::min(scratch_buf_size, data_size);
enc_lead_size = 0;
}
}
ret = _underlying_kv->get(key, dest_buf, chunk_size, 0,
_ih->metadata.metadata_size + current_offset);
if (ret != MBED_SUCCESS) {
goto end;
}
os_ret = cmac_calc_data(_ih->auth_ctx, dest_buf, chunk_size);
if (os_ret) {
ret = MBED_ERROR_FAILED_OPERATION;
goto end;
}
if (create_flags & REQUIRE_CONFIDENTIALITY_FLAG) {
// Decrypt data in place
os_ret = encrypt_decrypt_data(_ih->enc_ctx, dest_buf, dest_buf, chunk_size, _ih->ctr_buf,
aes_offs);
if (os_ret) {
ret = MBED_ERROR_FAILED_OPERATION;
goto end;
}
if (enc_lead_size) {
// Now copy decrypted lead size to user buffer start
memcpy(buffer, dest_buf + chunk_size - enc_lead_size, enc_lead_size);
}
}
current_offset += chunk_size;
data_size -= chunk_size;
}
if (actual_size) {
*actual_size = actual_data_size;
}
uint8_t calc_cmac[cmac_size], read_cmac[cmac_size];
os_ret = cmac_calc_finish(_ih->auth_ctx, calc_cmac);
if (os_ret) {
ret = MBED_ERROR_FAILED_OPERATION;
goto end;
}
// Check with record CMAC
ret = _underlying_kv->get(key, read_cmac, cmac_size, 0,
_ih->metadata.metadata_size + _ih->metadata.data_size);
if (ret) {
goto end;
}
if (memcmp(calc_cmac, read_cmac, cmac_size) != 0) {
ret = MBED_ERROR_AUTHENTICATION_FAILED;
goto end;
}
// If rollback protect, check also CMAC stored in RBP store
if (_rbp_kv && (create_flags & (REQUIRE_REPLAY_PROTECTION_FLAG | WRITE_ONCE_FLAG))) {
if (!rbp_key_exists) {
ret = MBED_ERROR_RBP_AUTHENTICATION_FAILED;
goto end;
}
if (memcmp(calc_cmac, rbp_cmac, cmac_size) != 0) {
ret = MBED_ERROR_RBP_AUTHENTICATION_FAILED;
goto end;
}
}
if (info) {
info->flags = _ih->metadata.create_flags;
info->size = _ih->metadata.data_size;
}
end:
_ih->metadata.metadata_size = 0;
if (enc_started) {
mbedtls_aes_free(&_ih->enc_ctx);
}
if (auth_started) {
mbedtls_cipher_free(&_ih->auth_ctx);
}
return ret;
}
int SecureStore::get(const char *key, void *buffer, size_t buffer_size, size_t *actual_size,
size_t offset)
{
_mutex.lock();
int ret = do_get(key, buffer, buffer_size, actual_size, offset);
_mutex.unlock();
return ret;
}
int SecureStore::get_info(const char *key, info_t *info)
{
_mutex.lock();
int ret = do_get(key, 0, 0, 0, 0, info);
_mutex.unlock();
return ret;
}
int SecureStore::init()
{
int ret = MBED_SUCCESS;
MBED_ASSERT(!(scratch_buf_size % enc_block_size));
if (scratch_buf_size % enc_block_size) {
return MBED_SYSTEM_ERROR_BASE;
}
_mutex.lock();
#if defined(MBEDTLS_PLATFORM_C)
ret = mbedtls_platform_setup(NULL);
if (ret) {
goto fail;
}
#endif /* MBEDTLS_PLATFORM_C */
_entropy = new mbedtls_entropy_context;
mbedtls_entropy_init(_entropy);
_scratch_buf = new uint8_t[scratch_buf_size];
_ih = new inc_set_handle_t;
ret = _underlying_kv->init();
if (ret) {
goto fail;
}
if (_rbp_kv) {
ret = _rbp_kv->init();
if (ret) {
goto fail;
}
}
_is_initialized = true;
fail:
_mutex.unlock();
return ret;
}
int SecureStore::deinit()
{
_mutex.lock();
int ret;
if (_is_initialized) {
if (_entropy) {
mbedtls_entropy_free(_entropy);
delete _entropy;
delete _ih;
delete _scratch_buf;
_entropy = nullptr;
}
ret = _underlying_kv->deinit();
if (ret) {
goto END;
}
if (_rbp_kv) {
ret = _rbp_kv->deinit();
if (ret) {
goto END;
}
}
}
_is_initialized = false;
#if defined(MBEDTLS_PLATFORM_C)
mbedtls_platform_teardown(NULL);
#endif /* MBEDTLS_PLATFORM_C */
ret = MBED_SUCCESS;
END:
_mutex.unlock();
return ret;
}
int SecureStore::reset()
{
int ret;
if (!_is_initialized) {
return MBED_ERROR_NOT_READY;
}
_mutex.lock();
ret = _underlying_kv->reset();
if (ret) {
goto end;
}
if (_rbp_kv) {
ret = _rbp_kv->reset();
if (ret) {
goto end;
}
}
end:
_mutex.unlock();
return ret;
}
int SecureStore::iterator_open(iterator_t *it, const char *prefix)
{
key_iterator_handle_t *handle;
if (!_is_initialized) {
return MBED_ERROR_NOT_READY;
}
if (!it) {
return MBED_ERROR_INVALID_ARGUMENT;
}
handle = new key_iterator_handle_t;
*it = reinterpret_cast<iterator_t>(handle);
return _underlying_kv->iterator_open(&handle->underlying_it, prefix);
}
int SecureStore::iterator_next(iterator_t it, char *key, size_t key_size)
{
key_iterator_handle_t *handle;
if (!_is_initialized) {
return MBED_ERROR_NOT_READY;
}
handle = reinterpret_cast<key_iterator_handle_t *>(it);
return _underlying_kv->iterator_next(handle->underlying_it, key, key_size);
}
int SecureStore::iterator_close(iterator_t it)
{
key_iterator_handle_t *handle;
int ret;
if (!_is_initialized) {
return MBED_ERROR_NOT_READY;
}
handle = reinterpret_cast<key_iterator_handle_t *>(it);
ret = _underlying_kv->iterator_close(handle->underlying_it);
delete handle;
return ret;
}
#endif

View File

@@ -0,0 +1,538 @@
/*
* Copyright (c) 2018 ARM Limited. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the License); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an AS IS BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "securestore/SecureStore.h"
#include "kvstore/TDBStore.h"
#ifdef MBED_CONF_RTOS_PRESENT
#include "Thread.h"
#endif
#include "mbed_error.h"
#include "Timer.h"
#include "HeapBlockDevice.h"
#include "FlashSimBlockDevice.h"
#include "SlicingBlockDevice.h"
#include "greentea-client/test_env.h"
#include "unity/unity.h"
#include "utest/utest.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <algorithm>
#include "DeviceKey.h"
#if !SECURESTORE_ENABLED
#error [NOT_SUPPORTED] SecureStore need to be enabled for this test
#else
using namespace mbed;
const size_t ul_bd_size = 8 * 4096;
const size_t rbp_bd_size = 4 * 4096;
static const int heap_alloc_threshold_size = 4096;
#undef TEST_SPIF
#undef TEST_FSSTORE_UL
#undef NO_RBP_MODE
#ifdef TEST_SPIF
#include "SPIFBlockDevice.h"
SPIFBlockDevice flash_bd(MBED_CONF_SPIF_DRIVER_SPI_MOSI, MBED_CONF_SPIF_DRIVER_SPI_MISO,
MBED_CONF_SPIF_DRIVER_SPI_CLK, MBED_CONF_SPIF_DRIVER_SPI_CS);
SlicingBlockDevice ul_bd(&flash_bd, 0, ul_bd_size);
SlicingBlockDevice rbp_bd(&flash_bd, ul_bd_size, ul_bd_size + rbp_bd_size);
#else
HeapBlockDevice bd(ul_bd_size + rbp_bd_size, 1, 1, 4096);
FlashSimBlockDevice flash_bd(&bd);
SlicingBlockDevice ul_bd(&flash_bd, 0, ul_bd_size);
SlicingBlockDevice rbp_bd(&flash_bd, ul_bd_size, ul_bd_size + rbp_bd_size);
#endif
#ifdef TEST_FSSTORE_UL
#include "LittleFileSystem.h"
#include "FileSystemStore.h"
#endif
using namespace utest::v1;
static const char *const key1 = "key1";
static const char *const key1_val1 = "val1";
static const char *const key2 = "name_of_key2";
static const char *const key2_val1 = "val3";
static const char *const key2_val2 = "val2 of key 2";
static const char *const key2_val3 = "Val1 value of key 2 ";
static const char *const key3 = "This_is_the_name_of_key3";
static const char *const key3_val1 = "Data value of key 3 is the following";
static const char *const key3_val2 = "Unfollow";
static const char *const key4 = "This_is_the_name_of_key4";
static const char *const key4_val1 = "Is this the value of key 4?";
static const char *const key4_val2 = "What the hell is the value of key 4, god damn it!";
static const char *const key5 = "This_is_the_real_name_of_Key5";
static const char *const key5_val1 = "Key 5 value that should definitely be written";
static const char *const key5_val2 = "Key 5 value that should definitely not be written";
static const char *const key6 = "Key6_name";
static const char *const key6_val1 = "Value 1 of key6";
static const char *const key6_val2 = "Value 2 of key6. That's it.";
static const char *const key7 = "Key7";
static const char *const key7_val1 = "7 is a lucky number";
static void white_box_test()
{
uint8_t get_buf[256];
size_t actual_data_size;
int result;
mbed::Timer timer;
int elapsed;
KVStore::info_t info;
uint8_t *dummy = new (std::nothrow) uint8_t[heap_alloc_threshold_size];
TEST_SKIP_UNLESS_MESSAGE(dummy, "Not enough heap to run test");
// We need to skip the test if we don't have enough memory for the heap block device.
// However, this device allocates the erase units on the fly, so "erase" it via the flash
// simulator. A failure here means we haven't got enough memory.
flash_bd.init();
result = flash_bd.erase(0, flash_bd.size());
TEST_SKIP_UNLESS_MESSAGE(!result, "Not enough heap to run test");
flash_bd.deinit();
delete[] dummy;
#ifdef TEST_FSSTORE_UL
LittleFileSystem *fs = new LittleFileSystem("fs", &ul_bd);
result = fs->mount(&ul_bd);
if (result) {
result = fs->reformat(&ul_bd);
TEST_ASSERT_EQUAL(0, result);
}
FileSystemStore *ul_kv = new FileSystemStore(fs);
#else
TDBStore *ul_kv = new TDBStore(&ul_bd);
#endif
#ifdef NO_RBP_MODE
TDBStore *rbp_kv = 0;
#else
TDBStore *rbp_kv = new TDBStore(&rbp_bd);
#endif
SecureStore *sec_kv = new SecureStore(ul_kv, rbp_kv);
for (int i = 0; i < 2; i++) {
timer.reset();
result = sec_kv->init();
TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result);
elapsed = timer.read_ms();
printf("Elapsed time for init %d ms\n", elapsed);
timer.reset();
result = sec_kv->reset();
TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result);
#if DEVICEKEY_ENABLED
DeviceKey::get_instance().generate_root_of_trust();
#endif
elapsed = timer.read_ms();
printf("Elapsed time for reset is %d ms\n", elapsed);
result = sec_kv->set(key1, key1_val1, strlen(key1_val1), KVStore::REQUIRE_CONFIDENTIALITY_FLAG);
TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result);
result = sec_kv->set(key2, key2_val1, strlen(key2_val1), 0);
TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result);
result = sec_kv->set(key2, key2_val2, strlen(key2_val2), KVStore::REQUIRE_CONFIDENTIALITY_FLAG);
TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result);
result = sec_kv->get(key2, get_buf, sizeof(get_buf), &actual_data_size);
TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result);
TEST_ASSERT_EQUAL(strlen(key2_val2), actual_data_size);
TEST_ASSERT_EQUAL_STRING_LEN(key2_val2, get_buf, strlen(key2_val2));
timer.reset();
result = sec_kv->set(key2, key2_val3, strlen(key2_val3), KVStore::REQUIRE_REPLAY_PROTECTION_FLAG);
elapsed = timer.read_ms();
printf("Elapsed time for set is %d ms\n", elapsed);
TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result);
result = sec_kv->set(key3, key3_val1, strlen(key3_val1),
KVStore::REQUIRE_CONFIDENTIALITY_FLAG | KVStore::REQUIRE_REPLAY_PROTECTION_FLAG);
TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result);
result = sec_kv->set(key3, key3_val2, strlen(key3_val2), KVStore::REQUIRE_CONFIDENTIALITY_FLAG);
TEST_ASSERT_EQUAL_ERROR_CODE(MBED_ERROR_INVALID_ARGUMENT, result);
result = sec_kv->get(key3, get_buf, sizeof(get_buf), &actual_data_size);
TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result);
TEST_ASSERT_EQUAL(strlen(key3_val1), actual_data_size);
TEST_ASSERT_EQUAL_STRING_LEN(key3_val1, get_buf, strlen(key3_val1));
for (int j = 0; j < 2; j++) {
result = sec_kv->set(key4, key4_val1, strlen(key4_val1), KVStore::REQUIRE_REPLAY_PROTECTION_FLAG);
TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result);
result = sec_kv->set(key4, key4_val2, strlen(key4_val2), KVStore::REQUIRE_CONFIDENTIALITY_FLAG | KVStore::REQUIRE_REPLAY_PROTECTION_FLAG);
TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result);
}
result = sec_kv->get_info(key3, &info);
TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result);
TEST_ASSERT_EQUAL(KVStore::REQUIRE_CONFIDENTIALITY_FLAG | KVStore::REQUIRE_REPLAY_PROTECTION_FLAG, info.flags);
TEST_ASSERT_EQUAL(strlen(key3_val1), info.size);
result = ul_kv->get_info(key3, &info);
TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result);
TEST_ASSERT_EQUAL(0, info.flags);
#ifndef NO_RBP_MODE
result = rbp_kv->get_info(key3, &info);
TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result);
#endif
result = sec_kv->remove(key3);
TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result);
result = sec_kv->remove(key3);
TEST_ASSERT_EQUAL_ERROR_CODE(MBED_ERROR_ITEM_NOT_FOUND, result);
result = ul_kv->get_info(key3, &info);
TEST_ASSERT_EQUAL_ERROR_CODE(MBED_ERROR_ITEM_NOT_FOUND, result);
#ifndef NO_RBP_MODE
result = rbp_kv->get_info(key3, &info);
TEST_ASSERT_EQUAL_ERROR_CODE(MBED_ERROR_ITEM_NOT_FOUND, result);
#endif
result = sec_kv->get_info(key5, &info);
TEST_ASSERT_EQUAL_ERROR_CODE(MBED_ERROR_ITEM_NOT_FOUND, result);
result = sec_kv->set(key5, key5_val1, strlen(key5_val1),
KVStore::REQUIRE_REPLAY_PROTECTION_FLAG | KVStore::WRITE_ONCE_FLAG);
TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result);
#ifndef NO_RBP_MODE
result = rbp_kv->get_info(key5, &info);
TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result);
TEST_ASSERT_EQUAL(KVStore::WRITE_ONCE_FLAG, info.flags);
#endif
result = sec_kv->set(key5, key5_val2, strlen(key5_val2),
KVStore::REQUIRE_REPLAY_PROTECTION_FLAG | KVStore::WRITE_ONCE_FLAG);
TEST_ASSERT_EQUAL_ERROR_CODE(MBED_ERROR_WRITE_PROTECTED, result);
result = sec_kv->remove(key5);
TEST_ASSERT_EQUAL_ERROR_CODE(MBED_ERROR_WRITE_PROTECTED, result);
result = sec_kv->get(key5, get_buf, sizeof(get_buf), &actual_data_size);
TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result);
TEST_ASSERT_EQUAL(strlen(key5_val1), actual_data_size);
TEST_ASSERT_EQUAL_STRING_LEN(key5_val1, get_buf, strlen(key5_val1));
result = sec_kv->get(key1, get_buf, sizeof(get_buf), &actual_data_size);
TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result);
TEST_ASSERT_EQUAL(strlen(key1_val1), actual_data_size);
TEST_ASSERT_EQUAL_STRING_LEN(key1_val1, get_buf, strlen(key1_val1));
timer.reset();
result = sec_kv->get(key2, get_buf, sizeof(get_buf), &actual_data_size);
elapsed = timer.read_ms();
printf("Elapsed time for get is %d ms\n", elapsed);
TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result);
TEST_ASSERT_EQUAL(strlen(key2_val3), actual_data_size);
TEST_ASSERT_EQUAL_STRING_LEN(key2_val3, get_buf, strlen(key2_val3));
result = sec_kv->get(key4, get_buf, sizeof(get_buf), &actual_data_size);
TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result);
TEST_ASSERT_EQUAL(strlen(key4_val2), actual_data_size);
TEST_ASSERT_EQUAL_STRING_LEN(key4_val2, get_buf, strlen(key4_val2));
result = sec_kv->get(key4, get_buf, 7, &actual_data_size, 30);
TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result);
TEST_ASSERT_EQUAL(7, actual_data_size);
TEST_ASSERT_EQUAL_STRING_LEN(key4_val2 + 30, get_buf, 7);
result = sec_kv->get(key5, get_buf, sizeof(get_buf), &actual_data_size);
TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result);
TEST_ASSERT_EQUAL(strlen(key5_val1), actual_data_size);
TEST_ASSERT_EQUAL_STRING_LEN(key5_val1, get_buf, strlen(key5_val1));
KVStore::iterator_t it;
char *char_get_buf = reinterpret_cast <char *>(get_buf);
result = sec_kv->iterator_open(&it, "This");
TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result);
result = sec_kv->iterator_next(it, char_get_buf, sizeof(get_buf));
TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result);
bool got_key4 = !strcmp(key4, char_get_buf);
bool got_key5 = !strcmp(key5, char_get_buf);
TEST_ASSERT_EQUAL(true, got_key4 || got_key5);
result = sec_kv->iterator_next(it, char_get_buf, sizeof(get_buf));
TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result);
if (got_key4) {
TEST_ASSERT_EQUAL_STRING(key5, char_get_buf);
} else {
TEST_ASSERT_EQUAL_STRING(key4, char_get_buf);
}
result = sec_kv->iterator_next(it, (char *)get_buf, sizeof(get_buf));
TEST_ASSERT_EQUAL_ERROR_CODE(MBED_ERROR_ITEM_NOT_FOUND, result);
result = sec_kv->iterator_close(it);
TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result);
result = sec_kv->deinit();
TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result);
timer.reset();
result = sec_kv->init();
elapsed = timer.read_ms();
printf("Elapsed time for init is %d ms\n", elapsed);
TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result);
result = sec_kv->get(key4, get_buf, sizeof(get_buf), &actual_data_size);
TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result);
TEST_ASSERT_EQUAL(strlen(key4_val2), actual_data_size);
TEST_ASSERT_EQUAL_STRING_LEN(key4_val2, get_buf, strlen(key4_val2));
result = sec_kv->set(key6, key6_val1, strlen(key6_val1),
KVStore::REQUIRE_CONFIDENTIALITY_FLAG | KVStore::REQUIRE_REPLAY_PROTECTION_FLAG);
TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result);
#ifndef NO_RBP_MODE
// Simulate a rollback attack
char attack_buf[sizeof(get_buf)];
size_t attack_size;
result = ul_kv->get(key6, attack_buf, sizeof(attack_buf), &attack_size);
TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result);
result = sec_kv->set(key6, key6_val2, strlen(key6_val2),
KVStore::REQUIRE_CONFIDENTIALITY_FLAG | KVStore::REQUIRE_REPLAY_PROTECTION_FLAG);
TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result);
result = ul_kv->set(key6, attack_buf, attack_size, 0);
TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result);
result = sec_kv->get_info(key6, 0);
TEST_ASSERT_EQUAL_ERROR_CODE(MBED_ERROR_RBP_AUTHENTICATION_FAILED, result);
// Make sure encrypted data is truly encrypted
result = rbp_kv->get_info(key6, &info);
TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result);
int cmac_size = info.size;
uint8_t *cmac = new uint8_t[cmac_size];
result = sec_kv->set(key7, key7_val1, strlen(key7_val1), 0);
TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result);
result = ul_kv->get(key7, attack_buf, sizeof(attack_buf), &attack_size);
TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result);
int data_offset = attack_size - cmac_size - strlen(key7_val1);
TEST_ASSERT_EQUAL(0, strncmp(key7_val1, attack_buf + data_offset, strlen(key7_val1)));
result = sec_kv->set(key7, key7_val1, strlen(key7_val1), KVStore::REQUIRE_CONFIDENTIALITY_FLAG);
TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result);
result = ul_kv->get(key7, attack_buf, sizeof(attack_buf), &attack_size);
TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result);
TEST_ASSERT_NOT_EQUAL(0, strncmp(key7_val1, attack_buf + data_offset, strlen(key7_val1)));
// Simulate a wrong CMAC
result = ul_kv->get(key7, attack_buf, attack_size - cmac_size);
TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result);
result = ul_kv->get(key7, cmac, cmac_size, &actual_data_size, attack_size - cmac_size);
TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result);
cmac[0]++;
KVStore::set_handle_t handle;
result = ul_kv->set_start(&handle, key7, attack_size, 0);
TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result);
result = ul_kv->set_add_data(handle, attack_buf, attack_size - cmac_size);
TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result);
result = ul_kv->set_add_data(handle, cmac, cmac_size);
TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result);
result = ul_kv->set_finalize(handle);
TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result);
result = ul_kv->get(key7, attack_buf, sizeof(attack_buf), &attack_size);
TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result);
result = sec_kv->get_info(key7, 0);
TEST_ASSERT_EQUAL_ERROR_CODE(MBED_ERROR_AUTHENTICATION_FAILED, result);
delete[] cmac;
#endif
result = sec_kv->deinit();
TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result);
}
delete sec_kv;
delete ul_kv;
delete rbp_kv;
#ifdef TEST_FSSTORE_UL
delete fs;
#endif
}
#if 0
static void multi_set_test()
{
char *key;
uint8_t *get_buf, *set_buf;
size_t key_size = 32;
size_t data_size = 512;
size_t num_keys = 16;
size_t set_iters = 3;
size_t actual_data_size;
int result;
mbed::Timer timer;
int elapsed;
size_t i;
uint8_t key_ind;
timer.start();
#if !defined(TEST_SPIF) && !defined(TEST_SD)
HeapBlockDevice heap_bd(4096 * 64, 1, 1, 4096);
FlashSimBlockDevice flash_bd(&heap_bd);
#endif
// TODO: Fix
KVStore *kvs = new TDBStore(&flash_bd);
timer.reset();
result = kvs->init();
elapsed = timer.read_ms();
printf("Elapsed time for initial init is %d ms\n", elapsed);
TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result);
key = new char[key_size + 1];
get_buf = new uint8_t[data_size];
set_buf = new uint8_t[data_size];
srand(1);
for (i = 0; i < key_size; i++) {
// printable characters only
key[i] = rand() % 223 + 32;
}
key[key_size] = '\0';
for (i = 0; i < data_size; i++) {
set_buf[i] = rand() % 256;
}
int max_set_time = 0, total_set_time = 0;
int max_get_time = 0, total_get_time = 0;
timer.reset();
result = kvs->reset();
elapsed = timer.read_ms();
TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result);
for (i = 0; i < set_iters; i++) {
for (key_ind = 0; key_ind < num_keys; key_ind++) {
key[0] = '0' + key_ind;
set_buf[0] = key_ind * (i + 1);
timer.reset();
result = kvs->set(key, set_buf, data_size, 0);
elapsed = timer.read_ms();
TEST_ASSERT_EQUAL(0, result);
if (elapsed > max_set_time) {
max_set_time = elapsed;
}
total_set_time += elapsed;
}
}
for (key_ind = 0; key_ind < num_keys; key_ind++) {
key[0] = '0' + key_ind;
set_buf[0] = key_ind * set_iters;
timer.reset();
result = kvs->get(key, get_buf, data_size, &actual_data_size);
elapsed = timer.read_ms();
TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result);
TEST_ASSERT_EQUAL(data_size, actual_data_size);
TEST_ASSERT_EQUAL_STRING_LEN(set_buf, get_buf, data_size);
if (elapsed > max_get_time) {
max_get_time = elapsed;
}
total_get_time += elapsed;
}
printf("set time: Total (%d * %d times) - %d ms, Average - %d ms, Max - %d ms\n",
set_iters, num_keys, total_set_time,
total_set_time / (set_iters * num_keys), max_set_time);
printf("get time: Total (%d times) - %d ms, Average - %d ms, Max - %d ms\n",
num_keys, total_get_time,
total_get_time / num_keys, max_get_time);
result = kvs->deinit();
TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result);
timer.reset();
result = kvs->init();
elapsed = timer.read_ms();
printf("Elapsed time for init is %d ms\n", elapsed);
TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result);
result = kvs->deinit();
TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result);
delete[] key;
delete[] get_buf;
delete[] set_buf;
delete kvs;
}
#endif
utest::v1::status_t greentea_failure_handler(const Case *const source, const failure_t reason)
{
greentea_case_failure_abort_handler(source, reason);
return STATUS_CONTINUE;
}
Case cases[] = {
Case("SecureStore: White box test", white_box_test, greentea_failure_handler),
};
utest::v1::status_t greentea_test_setup(const size_t number_of_cases)
{
GREENTEA_SETUP(120, "default_auto");
return greentea_test_setup_handler(number_of_cases);
}
Specification specification(greentea_test_setup, cases, greentea_test_teardown_handler);
int main()
{
return !Harness::run(specification);
}
#endif // !SECURESTORE_ENABLED