Import Mbed OS hard-float snapshot
This commit is contained in:
306
storage/kvstore/securestore/include/securestore/SecureStore.h
Normal file
306
storage/kvstore/securestore/include/securestore/SecureStore.h
Normal 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
|
||||
6
storage/kvstore/securestore/mbed_lib.json
Normal file
6
storage/kvstore/securestore/mbed_lib.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "SecureStore",
|
||||
"macros": ["MBEDTLS_CIPHER_MODE_CTR"],
|
||||
"config": {
|
||||
}
|
||||
}
|
||||
894
storage/kvstore/securestore/source/SecureStore.cpp
Normal file
894
storage/kvstore/securestore/source/SecureStore.cpp
Normal 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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user