From 1adf6294c795b890d48f23087013d3f46dcd96f3 Mon Sep 17 00:00:00 2001 From: Italo Sampaio Date: Mon, 7 Oct 2024 14:39:56 -0300 Subject: [PATCH 1/6] Minor changes to secret_store.c to allow for easier unit testing - Removed unneeded include - Removed redundant check for blob_size --- firmware/src/hal/sgx/src/trusted/secret_store.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/firmware/src/hal/sgx/src/trusted/secret_store.c b/firmware/src/hal/sgx/src/trusted/secret_store.c index 427b3e29..ebea7f77 100644 --- a/firmware/src/hal/sgx/src/trusted/secret_store.c +++ b/firmware/src/hal/sgx/src/trusted/secret_store.c @@ -26,7 +26,6 @@ #include #include -#include #include #include #include @@ -75,11 +74,6 @@ static uint8_t unseal_data(const sealed_secret_t* sealed_secret, uint8_t* dest, size_t dest_length) { #ifndef SIM_BUILD - if (sealed_secret->blob_size > MAX_BLOB_SIZE) { - LOG("Sealed blob size is too large\n"); - goto unseal_data_error; - } - uint8_t* plaintext = NULL; size_t plaintext_size = 0; oe_result_t result = oe_unseal(sealed_secret->blob, From 8fd993b89e3ebc10c0b073e4e763087297e2e9c7 Mon Sep 17 00:00:00 2001 From: Italo Sampaio Date: Tue, 8 Oct 2024 09:07:57 -0300 Subject: [PATCH 2/6] Added secret store unit tests - Added unit tests for secret_store - Added mock implementations for the ocalls and seal API - Added trivial definitions of some open enclave types needed by the secret_store module --- firmware/src/hal/sgx/test/common/common.mk | 2 +- firmware/src/hal/sgx/test/mock/hsm_t.h | 6 + firmware/src/hal/sgx/test/mock/mock.h | 2 + firmware/src/hal/sgx/test/mock/mock_ocall.c | 95 +++++ firmware/src/hal/sgx/test/mock/mock_ocall.h | 79 ++++ firmware/src/hal/sgx/test/mock/mock_seal.c | 159 ++++++++ firmware/src/hal/sgx/test/mock/mock_seal.h | 121 ++++++ .../hal/sgx/test/mock/openenclave/common.h | 35 ++ .../test/mock/openenclave/corelibc/stdlib.h | 30 ++ .../src/hal/sgx/test/mock/openenclave/seal.h | 30 ++ firmware/src/hal/sgx/test/run-all.sh | 2 +- .../src/hal/sgx/test/secret_store/Makefile | 38 ++ .../sgx/test/secret_store/test_secret_store.c | 385 ++++++++++++++++++ 13 files changed, 982 insertions(+), 2 deletions(-) create mode 100644 firmware/src/hal/sgx/test/mock/hsm_t.h create mode 100644 firmware/src/hal/sgx/test/mock/mock_ocall.c create mode 100644 firmware/src/hal/sgx/test/mock/mock_ocall.h create mode 100644 firmware/src/hal/sgx/test/mock/mock_seal.c create mode 100644 firmware/src/hal/sgx/test/mock/mock_seal.h create mode 100644 firmware/src/hal/sgx/test/mock/openenclave/common.h create mode 100644 firmware/src/hal/sgx/test/mock/openenclave/corelibc/stdlib.h create mode 100644 firmware/src/hal/sgx/test/mock/openenclave/seal.h create mode 100644 firmware/src/hal/sgx/test/secret_store/Makefile create mode 100644 firmware/src/hal/sgx/test/secret_store/test_secret_store.c diff --git a/firmware/src/hal/sgx/test/common/common.mk b/firmware/src/hal/sgx/test/common/common.mk index 71522536..eb2f23f7 100644 --- a/firmware/src/hal/sgx/test/common/common.mk +++ b/firmware/src/hal/sgx/test/common/common.mk @@ -27,7 +27,7 @@ TESTCOMMONDIR = ../common CFLAGS = -Wall -Wextra -Werror -Wno-unused-parameter -Wno-unused-function CFLAGS += -iquote $(SRCDIR) -iquote $(HALINCDIR) CFLAGS += -iquote $(TESTCOMMONDIR) -CFLAGS += -iquote $(MOCKDIR) +CFLAGS += -I$(MOCKDIR) CFLAGS += -DHSM_PLATFORM_SGX VPATH += $(MOCKDIR):$(SRCDIR) diff --git a/firmware/src/hal/sgx/test/mock/hsm_t.h b/firmware/src/hal/sgx/test/mock/hsm_t.h new file mode 100644 index 00000000..a9e577a0 --- /dev/null +++ b/firmware/src/hal/sgx/test/mock/hsm_t.h @@ -0,0 +1,6 @@ +#ifndef __MOCK_HSM_T_H +#define __MOCK_HSM_T_H + +#include "mock_ocall.h" + +#endif // __MOCK_HSM_T_H \ No newline at end of file diff --git a/firmware/src/hal/sgx/test/mock/mock.h b/firmware/src/hal/sgx/test/mock/mock.h index c263ce1b..1cd47890 100644 --- a/firmware/src/hal/sgx/test/mock/mock.h +++ b/firmware/src/hal/sgx/test/mock/mock.h @@ -26,5 +26,7 @@ #define __MOCK_H #include "mock_secret_store.h" +#include "mock_seal.h" +#include "mock_ocall.h" #endif // #ifndef __MOCK_H diff --git a/firmware/src/hal/sgx/test/mock/mock_ocall.c b/firmware/src/hal/sgx/test/mock/mock_ocall.c new file mode 100644 index 00000000..8c555810 --- /dev/null +++ b/firmware/src/hal/sgx/test/mock/mock_ocall.c @@ -0,0 +1,95 @@ +#include +#include "openenclave/common.h" +#include "mock_ocall.h" + +// Maximum size of a sealed blob, as defined in secret_store.c +#define MAX_BLOB_SIZE (1024 * 1024) + +// Trivial key-value store implementation for testing purposes +// This key-value store is only capable of storing a single key-value pair +static char G_kvstore_key[256]; +static uint8_t G_kvstore_data[256]; +static size_t G_kvstore_data_size; +// The type of failure to simulate on the next call to the mock implementation +static mock_kvstore_failure_type_t G_next_failure; + +void mock_ocall_init() { + memset(G_kvstore_key, 0, sizeof(G_kvstore_key)); + memset(G_kvstore_data, 0, sizeof(G_kvstore_data)); + G_kvstore_data_size = 0; + G_next_failure = KVSTORE_FAILURE_NONE; +} + +oe_result_t ocall_kvstore_save(bool* _retval, + char* key, + uint8_t* data, + size_t data_size) { + if (G_next_failure == KVSTORE_FAILURE_SAVE) { + G_next_failure = KVSTORE_FAILURE_NONE; + *_retval = false; + return OE_OK; + } else if (G_next_failure == KVSTORE_FAILURE_OE_FAILURE) { + G_next_failure = KVSTORE_FAILURE_NONE; + return OE_FAILURE; + } + + strcpy(G_kvstore_key, key); + memcpy(G_kvstore_data, data, data_size); + G_kvstore_data_size = data_size; + *_retval = true; + return OE_OK; +} + +oe_result_t ocall_kvstore_exists(bool* _retval, char* key) { + if (G_next_failure == KVSTORE_FAILURE_OE_FAILURE) { + G_next_failure = KVSTORE_FAILURE_NONE; + return OE_FAILURE; + } + + *_retval = (strcmp(key, G_kvstore_key) == 0); + return OE_OK; +} + +oe_result_t ocall_kvstore_get(size_t* _retval, + char* key, + uint8_t* data_buf, + size_t buffer_size) { + if (G_next_failure == KVSTORE_FAILURE_OE_FAILURE) { + G_next_failure = KVSTORE_FAILURE_NONE; + return OE_FAILURE; + } + + if (strcmp(key, G_kvstore_key) == 0) { + if (G_next_failure == KVSTORE_FAILURE_GET_SEALED_BLOB_TOO_LARGE) { + // Return a blob size that exceeds the limit allowed by the caller + G_next_failure = KVSTORE_FAILURE_NONE; + *_retval = MAX_BLOB_SIZE + 1; + } else { + *_retval = G_kvstore_data_size; + } + memcpy(data_buf, G_kvstore_data, G_kvstore_data_size); + } else { + *_retval = 0; + } + return OE_OK; +} + +oe_result_t ocall_kvstore_remove(bool* _retval, char* key) { + if (G_next_failure == KVSTORE_FAILURE_OE_FAILURE) { + G_next_failure = KVSTORE_FAILURE_NONE; + return OE_FAILURE; + } + if (strcmp(key, G_kvstore_key) == 0) { + memset(G_kvstore_key, 0, sizeof(G_kvstore_key)); + memset(G_kvstore_data, 0, sizeof(G_kvstore_data)); + G_kvstore_data_size = 0; + *_retval = true; + } else { + *_retval = false; + } + return OE_OK; +} + +void mock_ocall_kvstore_fail_next(mock_kvstore_failure_type_t failure) { + G_next_failure = failure; +} diff --git a/firmware/src/hal/sgx/test/mock/mock_ocall.h b/firmware/src/hal/sgx/test/mock/mock_ocall.h new file mode 100644 index 00000000..6a905f9e --- /dev/null +++ b/firmware/src/hal/sgx/test/mock/mock_ocall.h @@ -0,0 +1,79 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2021 RSK Labs Ltd + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef __MOCK_OCALL_H +#define __MOCK_OCALL_H + +#include +#include +#include +#include "openenclave/common.h" + +// Types of failures that can be simulated in this mock implementation +typedef enum mock_kvstore_failure_type { + KVSTORE_FAILURE_NONE, + KVSTORE_FAILURE_SAVE, + KVSTORE_FAILURE_GET_SEALED_BLOB_TOO_LARGE, + KVSTORE_FAILURE_OE_FAILURE, +} mock_kvstore_failure_type_t; + +/** + * @brief Initializes the mock ocall implementation + */ +void mock_ocall_init(); + +/** + * @brief Mock implementation of ocall_kvstore_save + */ +oe_result_t ocall_kvstore_save(bool* _retval, + char* key, + uint8_t* data, + size_t data_size); + +/** + * @brief Mock implementation of ocall_kvstore_exists + */ +oe_result_t ocall_kvstore_exists(bool* _retval, char* key); + +/** + * @brief Mock implementation of ocall_kvstore_get + */ +oe_result_t ocall_kvstore_get(size_t* _retval, + char* key, + uint8_t* data_buf, + size_t buffer_size); + +/** + * @brief Mock implementation of ocall_kvstore_remove + */ +oe_result_t ocall_kvstore_remove(bool* _retval, char* key); + +/** + * @brief Simulates a failure on the next call to this mock implementation + * + * @param failure the type of failure to simulate + */ +void mock_ocall_kvstore_fail_next(mock_kvstore_failure_type_t failure); + +#endif // __MOCK_OCALL_H \ No newline at end of file diff --git a/firmware/src/hal/sgx/test/mock/mock_seal.c b/firmware/src/hal/sgx/test/mock/mock_seal.c new file mode 100644 index 00000000..3629f555 --- /dev/null +++ b/firmware/src/hal/sgx/test/mock/mock_seal.c @@ -0,0 +1,159 @@ +#include +#include +#include "mock_seal.h" +#include "assert_utils.h" + +// The maximum allowed blob size, as defined in secret_store.c +#define MAX_BLOB_SIZE (1024 * 1024) + +// A prefix added to sealed blobs in this mock implementation. +// This is just to keep things simple and easily distinguishable. +#define SEALED_PREFIX "SEALED - " + +// Captures the arguments passed to oe_seal +typedef struct oe_seal_args { + const void* plugin_id; + oe_seal_setting_t settings; + size_t settings_count; + const uint8_t* plaintext; + size_t plaintext_size; + const uint8_t* additional_data; + size_t additional_data_size; +} oe_seal_args_t; + +// Captures the arguments passed to oe_unseal +typedef struct oe_unseal_args { + uint8_t blob[MAX_BLOB_SIZE]; + size_t blob_size; + const uint8_t* additional_data; + size_t additional_data_size; +} oe_unseal_args_t; + +// Global variables to capture the arguments passed to oe_seal and oe_unseal +static oe_seal_args_t G_oe_seal_args; +static oe_unseal_args_t G_oe_unseal_args; +// The next failure type to simulate +mock_seal_failure_type_t G_next_failure = SEAL_FAILURE_NONE; + +void mock_seal_init() { + memset(&G_oe_seal_args, 0, sizeof(G_oe_seal_args)); + memset(&G_oe_unseal_args, 0, sizeof(G_oe_unseal_args)); + G_next_failure = SEAL_FAILURE_NONE; +} + +oe_result_t oe_seal(const void* plugin_id, + const oe_seal_setting_t* settings, + size_t settings_count, + const uint8_t* plaintext, + size_t plaintext_size, + const uint8_t* additional_data, + size_t additional_data_size, + uint8_t** blob, + size_t* blob_size) { + G_oe_seal_args.plugin_id = plugin_id; + memcpy(&G_oe_seal_args.settings, settings, sizeof(oe_seal_setting_t)); + G_oe_seal_args.settings_count = settings_count; + G_oe_seal_args.plaintext = plaintext; + G_oe_seal_args.plaintext_size = plaintext_size; + G_oe_seal_args.additional_data = additional_data; + G_oe_seal_args.additional_data_size = additional_data_size; + + if (G_next_failure == SEAL_FAILURE_OE_FAILURE) { + G_next_failure = SEAL_FAILURE_NONE; + return OE_FAILURE; + } + + size_t prefix_length = strlen(SEALED_PREFIX); + *blob_size = plaintext_size + prefix_length; + *blob = malloc(*blob_size); + assert(*blob != NULL); + memcpy(*blob, SEALED_PREFIX, prefix_length); + memcpy(*blob + prefix_length, plaintext, plaintext_size); + + return OE_OK; +} + +oe_result_t oe_unseal(const uint8_t* blob, + size_t blob_size, + const uint8_t* additional_data, + size_t additional_data_size, + uint8_t** plaintext, + size_t* plaintext_size) { + memcpy(G_oe_unseal_args.blob, blob, blob_size); + G_oe_unseal_args.blob_size = blob_size; + G_oe_unseal_args.additional_data = additional_data; + G_oe_unseal_args.additional_data_size = additional_data_size; + + switch (G_next_failure) { + case SEAL_FAILURE_OE_FAILURE: + G_next_failure = SEAL_FAILURE_NONE; + return OE_FAILURE; + case SEAL_FAILURE_OE_UNSEAL_PLAINTEXT_TOO_LARGE: + *plaintext_size = MAX_BLOB_SIZE + 1; + G_next_failure = SEAL_FAILURE_NONE; + break; + case SEAL_FAILURE_NONE: + *plaintext_size = blob_size - strlen(SEALED_PREFIX); + break; + default: + assert(false); + break; + } + + *plaintext = malloc(*plaintext_size); + assert(*plaintext != NULL); + memcpy(*plaintext, blob + strlen(SEALED_PREFIX), *plaintext_size); + + return OE_OK; +} + +void assert_oe_seal_called_with(const void* plugin_id, + const oe_seal_setting_t* settings, + size_t settings_count, + const uint8_t* plaintext, + size_t plaintext_size, + const uint8_t* additional_data, + size_t additional_data_size) { + assert(G_oe_seal_args.plugin_id == plugin_id && + memcmp(&G_oe_seal_args.settings, settings, sizeof(*settings)) == 0 && + G_oe_seal_args.settings_count == settings_count && + G_oe_seal_args.plaintext == plaintext && + G_oe_seal_args.plaintext_size == plaintext_size && + G_oe_seal_args.additional_data == additional_data && + G_oe_seal_args.additional_data_size == additional_data_size); +} + +void assert_oe_unseal_called_with(const uint8_t* blob, + size_t blob_size, + const uint8_t* additional_data, + size_t additional_data_size) { + assert((memcmp(blob, G_oe_unseal_args.blob, blob_size) == 0) && + G_oe_unseal_args.blob_size == blob_size && + G_oe_unseal_args.additional_data == additional_data && + G_oe_unseal_args.additional_data_size == additional_data_size); +} + +void assert_oe_unseal_not_called() { + ASSERT_ARRAY_CLEARED(G_oe_unseal_args.blob); + assert(G_oe_unseal_args.blob_size == 0); + assert(G_oe_unseal_args.additional_data == NULL); + assert(G_oe_unseal_args.additional_data_size == 0); +} + +void assert_oe_seal_not_called() { + assert(G_oe_seal_args.plugin_id == NULL); + assert(G_oe_seal_args.settings.policy == 0); + assert(G_oe_seal_args.settings_count == 0); + assert(G_oe_seal_args.plaintext == NULL); + assert(G_oe_seal_args.plaintext_size == 0); + assert(G_oe_seal_args.additional_data == NULL); + assert(G_oe_seal_args.additional_data_size == 0); +} + +void mock_seal_fail_next(mock_seal_failure_type_t failure) { + G_next_failure = failure; +} + +size_t mock_seal_get_max_plaintext_size() { + return MAX_BLOB_SIZE - strlen(SEALED_PREFIX); +} diff --git a/firmware/src/hal/sgx/test/mock/mock_seal.h b/firmware/src/hal/sgx/test/mock/mock_seal.h new file mode 100644 index 00000000..dd6af69f --- /dev/null +++ b/firmware/src/hal/sgx/test/mock/mock_seal.h @@ -0,0 +1,121 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2021 RSK Labs Ltd + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef __MOCK_SEAL_H +#define __MOCK_SEAL_H + +#include +#include +#include +#include "openenclave/common.h" + +// Types of failures that can be simulated in this mock implementation +typedef enum mock_seal_failure_type { + SEAL_FAILURE_NONE, + SEAL_FAILURE_OE_UNSEAL_PLAINTEXT_TOO_LARGE, + SEAL_FAILURE_OE_FAILURE, +} mock_seal_failure_type_t; + +// Simplified version of the seal settings type. This is only used to ensure +// that the API was called with the expected parameters. +typedef struct { + int policy; +} oe_seal_setting_t; + +#define OE_SEAL_SET_POLICY(policy) \ + { (int)(policy) } + +/** + * @brief Initializes the mock seal implementation + */ +void mock_seal_init(); + +/** + * @brief Mock implementation of oe_seal API function + */ +oe_result_t oe_seal(const void* plugin_id, + const oe_seal_setting_t* settings, + size_t settings_count, + const uint8_t* plaintext, + size_t plaintext_size, + const uint8_t* additional_data, + size_t additional_data_size, + uint8_t** blob, + size_t* blob_size); + +/** + * @brief Mock implementation of oe_unseal API function + */ +oe_result_t oe_unseal(const uint8_t* blob, + size_t blob_size, + const uint8_t* additional_data, + size_t additional_data_size, + uint8_t** plaintext, + size_t* plaintext_size); + +/** + * @brief Asserts that the last call to oe_seal was made with the expected + * parameters + */ +void assert_oe_seal_called_with(const void* plugin_id, + const oe_seal_setting_t* settings, + size_t settings_count, + const uint8_t* plaintext, + size_t plaintext_size, + const uint8_t* additional_data, + size_t additional_data_size); + +/** + * @brief Asserts that the last call to oe_unseal was made with the expected + * parameters + */ +void assert_oe_unseal_called_with(const uint8_t* blob, + size_t blob_size, + const uint8_t* additional_data, + size_t additional_data_size); + +/** + * @brief Asserts that oe_unseal was not called since the mock was initialized + */ +void assert_oe_unseal_not_called(); + +/** + * @brief Asserts that oe_seal was not called since the mock was initialized + */ +void assert_oe_seal_not_called(); + +/** + * @brief Simulates a failure on the next call to this mock implementation + * + * @param failure the type of failure to simulate + */ +void mock_seal_fail_next(mock_seal_failure_type_t failure); + +/** + * @brief Returns the maximum plaintext size allowed, based on the maximum blob + * size. + */ +size_t mock_seal_get_max_plaintext_size(); + +#endif // #ifndef __MOCK_SEAL_H diff --git a/firmware/src/hal/sgx/test/mock/openenclave/common.h b/firmware/src/hal/sgx/test/mock/openenclave/common.h new file mode 100644 index 00000000..54a3fc35 --- /dev/null +++ b/firmware/src/hal/sgx/test/mock/openenclave/common.h @@ -0,0 +1,35 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2021 RSK Labs Ltd + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef __MOCK_OE_COMMON_H +#define __MOCK_OE_COMMON_H + +typedef enum oe_result { + OE_OK, + OE_FAILURE, +} oe_result_t; + +#define oe_result_str(result) ((result) == OE_OK ? "OE_OK" : "OE_FAILURE") + +#endif // #ifndef __MOCK_OE_COMMON_H \ No newline at end of file diff --git a/firmware/src/hal/sgx/test/mock/openenclave/corelibc/stdlib.h b/firmware/src/hal/sgx/test/mock/openenclave/corelibc/stdlib.h new file mode 100644 index 00000000..482a7661 --- /dev/null +++ b/firmware/src/hal/sgx/test/mock/openenclave/corelibc/stdlib.h @@ -0,0 +1,30 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2021 RSK Labs Ltd + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef __MOCK_OE_STDLIB_H +#define __MOCK_OE_STDLIB_H + +#define oe_free(ptr) free(ptr) + +#endif // #ifndef __MOCK_OE_STDLIB_H \ No newline at end of file diff --git a/firmware/src/hal/sgx/test/mock/openenclave/seal.h b/firmware/src/hal/sgx/test/mock/openenclave/seal.h new file mode 100644 index 00000000..9a0d367d --- /dev/null +++ b/firmware/src/hal/sgx/test/mock/openenclave/seal.h @@ -0,0 +1,30 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2021 RSK Labs Ltd + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef __MOCK_OE_SEAL_H +#define __MOCK_OE_SEAL_H + +#include "mock_seal.h" + +#endif // #ifndef __MOCK_OE_SEAL_H \ No newline at end of file diff --git a/firmware/src/hal/sgx/test/run-all.sh b/firmware/src/hal/sgx/test/run-all.sh index 2a4217fe..8a7c782d 100755 --- a/firmware/src/hal/sgx/test/run-all.sh +++ b/firmware/src/hal/sgx/test/run-all.sh @@ -1,6 +1,6 @@ #!/bin/bash BASEDIR=$(dirname $0) -TESTDIRS="nvmem" +TESTDIRS="nvmem secret_store" TESTDIRS=${1:-"$TESTDIRS"} for d in $TESTDIRS; do diff --git a/firmware/src/hal/sgx/test/secret_store/Makefile b/firmware/src/hal/sgx/test/secret_store/Makefile new file mode 100644 index 00000000..4ed0fb95 --- /dev/null +++ b/firmware/src/hal/sgx/test/secret_store/Makefile @@ -0,0 +1,38 @@ +# The MIT License (MIT) +# +# Copyright (c) 2021 RSK Labs Ltd +# +# Permission is hereby granted, free of charge, to any person obtaining a copy of +# this software and associated documentation files (the "Software"), to deal in +# the Software without restriction, including without limitation the rights to +# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +# of the Software, and to permit persons to whom the Software is furnished to do +# so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +include ../common/common.mk + +PROG = test.out +OBJS = secret_store.o test_secret_store.o platform.o mock_seal.o mock_ocall.o + +all: $(PROG) + +$(PROG): $(OBJS) + $(CC) $(COVFLAGS) -o $@ $^ + +.PHONY: clean test +clean: + rm -f $(PROG) *.o $(COVFILES) + +test: all + ./$(PROG) diff --git a/firmware/src/hal/sgx/test/secret_store/test_secret_store.c b/firmware/src/hal/sgx/test/secret_store/test_secret_store.c new file mode 100644 index 00000000..896dd275 --- /dev/null +++ b/firmware/src/hal/sgx/test/secret_store/test_secret_store.c @@ -0,0 +1,385 @@ +#include +#include +#include +#include + +#include "assert_utils.h" +#include "secret_store.h" +#include "mock_seal.h" +#include "mock_ocall.h" + +// Error code for the sest API as defined in secret_store.c +#define SEST_ERROR (0) +// The maximum value that can be returned by sest_read +#define MAX_SEST_READ_SIZE (255) + +void setup() { + mock_seal_init(); + mock_ocall_init(); + assert(sest_init()); +} + +void test_secret_exists_after_write() { + setup(); + printf("Test secret exists after write...\n"); + + char* key = "key"; + uint8_t secret[] = "secret"; + // Ensure the secret doesn't exist before the write + assert(!sest_exists(key)); + // Write the secret and ensure it now exists + assert(sest_write("key", secret, sizeof(secret))); + assert_oe_seal_called_with( + NULL, + (const oe_seal_setting_t[]){OE_SEAL_SET_POLICY(1)}, + 1, + secret, + sizeof(secret), + NULL, + 0); + assert(sest_exists("key")); +} + +void test_write_and_retrieve_secret() { + setup(); + printf("Test write and retrieve secret...\n"); + + char* key = "key"; + uint8_t secret[] = "secret"; + // Write the secret and make sure the seal API is called with the correct + // arguments + assert(sest_write(key, secret, sizeof(secret))); + assert_oe_seal_called_with( + NULL, + (const oe_seal_setting_t[]){OE_SEAL_SET_POLICY(1)}, + 1, + secret, + sizeof(secret), + NULL, + 0); + // Retrieve the secret and make sure the unseal API is called with the + // correct arguments + uint8_t retrieved[MAX_SEST_READ_SIZE]; + uint8_t retrieved_length = sest_read(key, retrieved, sizeof(retrieved)); + uint8_t expected_sealed_blob[] = "SEALED - secret"; + assert_oe_unseal_called_with( + expected_sealed_blob, sizeof(expected_sealed_blob), NULL, 0); + assert(retrieved_length == sizeof(secret)); + ASSERT_MEMCMP(retrieved, secret, retrieved_length); +} + +void test_write_and_remove_secret() { + setup(); + printf("Test write and remove secret...\n"); + + char* key = "key"; + uint8_t secret[] = "secret"; + assert(sest_write(key, secret, sizeof(secret))); + assert_oe_seal_called_with( + NULL, + (const oe_seal_setting_t[]){OE_SEAL_SET_POLICY(1)}, + 1, + secret, + sizeof(secret), + NULL, + 0); + assert(sest_exists(key)); + assert(sest_remove(key)); + assert(!sest_exists(key)); +} + +void test_exists_fails_when_kvstore_exists_fails() { + setup(); + printf("Test sest_exists fails when ocall_kvstore_exists fails...\n"); + + // Write a valid secret and ensure it exists + char* key = "key"; + uint8_t secret[] = "secret"; + assert(!sest_exists(key)); + assert(sest_write(key, secret, sizeof(secret))); + assert_oe_seal_called_with( + NULL, + (const oe_seal_setting_t[]){OE_SEAL_SET_POLICY(1)}, + 1, + secret, + sizeof(secret), + NULL, + 0); + assert(sest_exists(key)); + + // Force the next call to ocall_kvstore_exists to fail + mock_ocall_kvstore_fail_next(KVSTORE_FAILURE_OE_FAILURE); + assert(!sest_exists(key)); +} + +void test_read_fails_when_oe_unseal_fails() { + setup(); + printf("Test read fails when oe_unseal fails (OE_FAILURE)...\n"); + + char* key = "key"; + uint8_t secret[] = "secret"; + assert(sest_write(key, secret, sizeof(secret))); + assert(sest_exists(key)); + // Force the next call to oe_unseal to fail with OE_FAILURE + mock_seal_fail_next(SEAL_FAILURE_OE_FAILURE); + uint8_t retrieved[MAX_SEST_READ_SIZE]; + memset(retrieved, 0, sizeof(retrieved)); + uint8_t retrieved_length = sest_read(key, retrieved, sizeof(retrieved)); + uint8_t expected_sealed_blob[] = "SEALED - secret"; + assert_oe_unseal_called_with( + expected_sealed_blob, sizeof(expected_sealed_blob), NULL, 0); + assert(retrieved_length == SEST_ERROR); + ASSERT_ARRAY_CLEARED(retrieved); +} + +void test_read_fails_when_plaintext_is_too_large() { + setup(); + printf("Test read fails when unsealed secret is too large...\n"); + + char* key = "key"; + uint8_t secret[] = "secret"; + assert(sest_write(key, secret, sizeof(secret))); + assert(sest_exists(key)); + // Force the next call to oe_unseal to fail by returning a plaintext that is + // too large + mock_seal_fail_next(SEAL_FAILURE_OE_UNSEAL_PLAINTEXT_TOO_LARGE); + uint8_t retrieved[MAX_SEST_READ_SIZE]; + memset(retrieved, 0, sizeof(retrieved)); + uint8_t retrieved_length = sest_read(key, retrieved, sizeof(retrieved)); + uint8_t expected_sealed_blob[] = "SEALED - secret"; + assert_oe_unseal_called_with( + expected_sealed_blob, sizeof(expected_sealed_blob), NULL, 0); + assert(retrieved_length == SEST_ERROR); + ASSERT_ARRAY_CLEARED(retrieved); +} + +void test_write_zero_length_secret_fails() { + setup(); + printf("Test write zero length secret fails...\n"); + + char* key = "key"; + // Ensure the secret doesn't exist before the write + assert(!sest_exists(key)); + // Write the secret and ensure it fails + assert(!sest_write(key, NULL, 0)); + // Make sure the seal API was never reached + assert_oe_seal_not_called(); + assert(!sest_exists(key)); +} + +void test_write_fails_when_oe_seal_fails() { + setup(); + printf("Test write fails when oe_seal fails (OE_FAILURE)...\n"); + + // Force the next call to oe_seal to fail + mock_seal_fail_next(SEAL_FAILURE_OE_FAILURE); + char* key = "key"; + uint8_t secret[] = "secret"; + assert(!sest_exists(key)); + assert(!sest_write(key, secret, sizeof(secret))); + assert_oe_seal_called_with( + NULL, + (const oe_seal_setting_t[]){OE_SEAL_SET_POLICY(1)}, + 1, + secret, + sizeof(secret), + NULL, + 0); + assert(!sest_exists(key)); +} + +void test_write_fails_when_kvstore_save_fails() { + setup(); + printf("Test write fails when ocall_kvstore_save fails...\n"); + + char* key = "key"; + uint8_t secret[] = "secret"; + assert(!sest_exists(key)); + // Force the next call to ocall_kvstore_save to fail + mock_ocall_kvstore_fail_next(KVSTORE_FAILURE_SAVE); + assert(!sest_write(key, secret, sizeof(secret))); + assert_oe_seal_called_with( + NULL, + (const oe_seal_setting_t[]){OE_SEAL_SET_POLICY(1)}, + 1, + secret, + sizeof(secret), + NULL, + 0); + assert(!sest_exists(key)); +} + +void test_write_fails_when_kvstore_save_fails_oe_failure() { + setup(); + printf("Test write fails when ocall_kvstore_save fails (OE_FAILURE)...\n"); + + char* key = "key"; + uint8_t secret[] = "secret"; + assert(!sest_exists(key)); + // Force the next call to ocall_kvstore_save to fail with OE_FAILURE + mock_ocall_kvstore_fail_next(KVSTORE_FAILURE_OE_FAILURE); + assert(!sest_write(key, secret, sizeof(secret))); + assert_oe_seal_called_with( + NULL, + (const oe_seal_setting_t[]){OE_SEAL_SET_POLICY(1)}, + 1, + secret, + sizeof(secret), + NULL, + 0); + assert(!sest_exists(key)); +} + +void test_write_fails_when_secret_too_large() { + setup(); + printf("Test write fails when secret is too large...\n"); + + // Attempt to write a secret that is too large + char* key = "key"; + size_t secret_size = mock_seal_get_max_plaintext_size() + 1; + uint8_t secret[secret_size]; + assert(!sest_exists(key)); + assert(!sest_write(key, secret, secret_size)); + assert_oe_seal_called_with( + NULL, + (const oe_seal_setting_t[]){OE_SEAL_SET_POLICY(1)}, + 1, + secret, + secret_size, + NULL, + 0); + assert(!sest_exists(key)); +} + +void test_read_with_invalid_key_fails() { + setup(); + printf("Test read with invalid key fails...\n"); + + char* valid_key = "valid key"; + uint8_t secret[] = "secret"; + assert(sest_write(valid_key, secret, sizeof(secret))); + assert_oe_seal_called_with( + NULL, + (const oe_seal_setting_t[]){OE_SEAL_SET_POLICY(1)}, + 1, + secret, + sizeof(secret), + NULL, + 0); + assert(sest_exists(valid_key)); + + char* invalid_key = "invalid key"; + uint8_t retrieved[MAX_SEST_READ_SIZE]; + uint8_t retrieved_length = + sest_read(invalid_key, retrieved, sizeof(retrieved)); + assert_oe_unseal_not_called(); + assert(retrieved_length == SEST_ERROR); +} + +void test_read_fails_when_kvstore_get_fails() { + setup(); + printf("Test read fails when ocall_kvstore_get fails (OE_FAILURE)...\n"); + + char* key = "key"; + uint8_t secret[] = "secret"; + // Write the secret + assert(sest_write(key, secret, sizeof(secret))); + assert_oe_seal_called_with( + NULL, + (const oe_seal_setting_t[]){OE_SEAL_SET_POLICY(1)}, + 1, + secret, + sizeof(secret), + NULL, + 0); + assert(sest_exists(key)); + // Force OE_FAILURE on the next call to ocall_kvstore_get + mock_ocall_kvstore_fail_next(KVSTORE_FAILURE_OE_FAILURE); + uint8_t retrieved[MAX_SEST_READ_SIZE]; + memset(retrieved, 0, sizeof(retrieved)); + uint8_t retrieved_length = sest_read(key, retrieved, sizeof(retrieved)); + assert_oe_unseal_not_called(); + assert(retrieved_length == SEST_ERROR); + ASSERT_ARRAY_CLEARED(retrieved); +} + +void test_read_fails_when_blob_is_too_large() { + setup(); + printf("Test read fails sealed blob is too large...\n"); + + char* key = "key"; + uint8_t secret[] = "secret"; + // Write the secret + assert(sest_write(key, secret, sizeof(secret))); + assert_oe_seal_called_with( + NULL, + (const oe_seal_setting_t[]){OE_SEAL_SET_POLICY(1)}, + 1, + secret, + sizeof(secret), + NULL, + 0); + assert(sest_exists(key)); + // Force the next call to ocall_kvstore_get to return a blob that is too + // large + mock_ocall_kvstore_fail_next(KVSTORE_FAILURE_GET_SEALED_BLOB_TOO_LARGE); + uint8_t retrieved[MAX_SEST_READ_SIZE]; + memset(retrieved, 0, sizeof(retrieved)); + uint8_t retrieved_length = sest_read(key, retrieved, sizeof(retrieved)); + assert_oe_unseal_not_called(); + assert(retrieved_length == SEST_ERROR); + ASSERT_ARRAY_CLEARED(retrieved); +} + +void test_remove_with_invalid_key_fails() { + setup(); + printf("Test write and remove invalid key fails...\n"); + + char* valid_key = "valid key"; + uint8_t secret[] = "secret"; + assert(sest_write(valid_key, secret, sizeof(secret))); + assert(sest_exists(valid_key)); + char* invalid_key = "invalid key"; + assert(!sest_remove(invalid_key)); +} + +void test_remove_fails_when_kvstore_remove_fails() { + setup(); + printf("Test remove fails when ocall_kvstore_remove fails...\n"); + + char* key = "key"; + uint8_t secret[] = "secret"; + assert(sest_write(key, secret, sizeof(secret))); + assert_oe_seal_called_with( + NULL, + (const oe_seal_setting_t[]){OE_SEAL_SET_POLICY(1)}, + 1, + secret, + sizeof(secret), + NULL, + 0); + assert(sest_exists(key)); + // Force the next call to ocall_kvstore_remove to fail + mock_ocall_kvstore_fail_next(KVSTORE_FAILURE_OE_FAILURE); + assert(!sest_remove(key)); + assert(sest_exists(key)); +} + +int main() { + test_secret_exists_after_write(); + test_write_and_retrieve_secret(); + test_write_and_remove_secret(); + test_write_zero_length_secret_fails(); + test_write_fails_when_oe_seal_fails(); + test_write_fails_when_secret_too_large(); + test_write_fails_when_kvstore_save_fails(); + test_write_fails_when_kvstore_save_fails_oe_failure(); + test_read_with_invalid_key_fails(); + test_read_fails_when_plaintext_is_too_large(); + test_read_fails_when_kvstore_get_fails(); + test_read_fails_when_blob_is_too_large(); + test_read_fails_when_oe_unseal_fails(); + test_exists_fails_when_kvstore_exists_fails(); + test_remove_with_invalid_key_fails(); + test_remove_fails_when_kvstore_remove_fails(); +} From ba6eb8c9756024db35a9d0a21487eeb465ce9d37 Mon Sep 17 00:00:00 2001 From: Italo Sampaio Date: Tue, 8 Oct 2024 17:54:35 -0300 Subject: [PATCH 3/6] Added missing license headers --- firmware/src/hal/sgx/test/mock/hsm_t.h | 24 +++++++++++++++++++ firmware/src/hal/sgx/test/mock/mock_ocall.c | 24 +++++++++++++++++++ firmware/src/hal/sgx/test/mock/mock_seal.c | 24 +++++++++++++++++++ .../src/hal/sgx/test/mock/mock_secret_store.c | 24 +++++++++++++++++++ .../sgx/test/secret_store/test_secret_store.c | 24 +++++++++++++++++++ 5 files changed, 120 insertions(+) diff --git a/firmware/src/hal/sgx/test/mock/hsm_t.h b/firmware/src/hal/sgx/test/mock/hsm_t.h index a9e577a0..e7194db5 100644 --- a/firmware/src/hal/sgx/test/mock/hsm_t.h +++ b/firmware/src/hal/sgx/test/mock/hsm_t.h @@ -1,3 +1,27 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2021 RSK Labs Ltd + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + #ifndef __MOCK_HSM_T_H #define __MOCK_HSM_T_H diff --git a/firmware/src/hal/sgx/test/mock/mock_ocall.c b/firmware/src/hal/sgx/test/mock/mock_ocall.c index 8c555810..f77a756c 100644 --- a/firmware/src/hal/sgx/test/mock/mock_ocall.c +++ b/firmware/src/hal/sgx/test/mock/mock_ocall.c @@ -1,3 +1,27 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2021 RSK Labs Ltd + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + #include #include "openenclave/common.h" #include "mock_ocall.h" diff --git a/firmware/src/hal/sgx/test/mock/mock_seal.c b/firmware/src/hal/sgx/test/mock/mock_seal.c index 3629f555..6f095ca6 100644 --- a/firmware/src/hal/sgx/test/mock/mock_seal.c +++ b/firmware/src/hal/sgx/test/mock/mock_seal.c @@ -1,3 +1,27 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2021 RSK Labs Ltd + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + #include #include #include "mock_seal.h" diff --git a/firmware/src/hal/sgx/test/mock/mock_secret_store.c b/firmware/src/hal/sgx/test/mock/mock_secret_store.c index 6fa2fd7d..0239b873 100644 --- a/firmware/src/hal/sgx/test/mock/mock_secret_store.c +++ b/firmware/src/hal/sgx/test/mock/mock_secret_store.c @@ -1,3 +1,27 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2021 RSK Labs Ltd + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + #include #include #include diff --git a/firmware/src/hal/sgx/test/secret_store/test_secret_store.c b/firmware/src/hal/sgx/test/secret_store/test_secret_store.c index 896dd275..ae93c3e8 100644 --- a/firmware/src/hal/sgx/test/secret_store/test_secret_store.c +++ b/firmware/src/hal/sgx/test/secret_store/test_secret_store.c @@ -1,3 +1,27 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2021 RSK Labs Ltd + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + #include #include #include From e391d27786337dafbca72de9026978d8582297a1 Mon Sep 17 00:00:00 2001 From: Italo Sampaio Date: Tue, 8 Oct 2024 18:21:43 -0300 Subject: [PATCH 4/6] Fixed inconsistency on mock approach - Mock functions are now prefixed with `mock_` to follow the general pattern used for other HAL tests - Mock implementation of functions needed by the tested unit are now part of the test suite, and just call the corresponding mock functions --- firmware/src/hal/sgx/test/mock/hsm_t.h | 19 +++++- firmware/src/hal/sgx/test/mock/mock_ocall.c | 20 +++---- firmware/src/hal/sgx/test/mock/mock_ocall.h | 20 +++---- firmware/src/hal/sgx/test/mock/mock_seal.c | 30 +++++----- firmware/src/hal/sgx/test/mock/mock_seal.h | 40 +++++-------- .../src/hal/sgx/test/mock/openenclave/seal.h | 30 +++++++++- .../sgx/test/secret_store/test_secret_store.c | 60 +++++++++++++++++++ 7 files changed, 158 insertions(+), 61 deletions(-) diff --git a/firmware/src/hal/sgx/test/mock/hsm_t.h b/firmware/src/hal/sgx/test/mock/hsm_t.h index e7194db5..5f639f43 100644 --- a/firmware/src/hal/sgx/test/mock/hsm_t.h +++ b/firmware/src/hal/sgx/test/mock/hsm_t.h @@ -25,6 +25,23 @@ #ifndef __MOCK_HSM_T_H #define __MOCK_HSM_T_H -#include "mock_ocall.h" +#include +#include +#include +#include "openenclave/common.h" + +oe_result_t ocall_kvstore_save(bool* _retval, + char* key, + uint8_t* data, + size_t data_size); + +oe_result_t ocall_kvstore_exists(bool* _retval, char* key); + +oe_result_t ocall_kvstore_get(size_t* _retval, + char* key, + uint8_t* data_buf, + size_t buffer_size); + +oe_result_t ocall_kvstore_remove(bool* _retval, char* key); #endif // __MOCK_HSM_T_H \ No newline at end of file diff --git a/firmware/src/hal/sgx/test/mock/mock_ocall.c b/firmware/src/hal/sgx/test/mock/mock_ocall.c index f77a756c..8a5fcfbd 100644 --- a/firmware/src/hal/sgx/test/mock/mock_ocall.c +++ b/firmware/src/hal/sgx/test/mock/mock_ocall.c @@ -44,10 +44,10 @@ void mock_ocall_init() { G_next_failure = KVSTORE_FAILURE_NONE; } -oe_result_t ocall_kvstore_save(bool* _retval, - char* key, - uint8_t* data, - size_t data_size) { +oe_result_t mock_ocall_kvstore_save(bool* _retval, + char* key, + uint8_t* data, + size_t data_size) { if (G_next_failure == KVSTORE_FAILURE_SAVE) { G_next_failure = KVSTORE_FAILURE_NONE; *_retval = false; @@ -64,7 +64,7 @@ oe_result_t ocall_kvstore_save(bool* _retval, return OE_OK; } -oe_result_t ocall_kvstore_exists(bool* _retval, char* key) { +oe_result_t mock_ocall_kvstore_exists(bool* _retval, char* key) { if (G_next_failure == KVSTORE_FAILURE_OE_FAILURE) { G_next_failure = KVSTORE_FAILURE_NONE; return OE_FAILURE; @@ -74,10 +74,10 @@ oe_result_t ocall_kvstore_exists(bool* _retval, char* key) { return OE_OK; } -oe_result_t ocall_kvstore_get(size_t* _retval, - char* key, - uint8_t* data_buf, - size_t buffer_size) { +oe_result_t mock_ocall_kvstore_get(size_t* _retval, + char* key, + uint8_t* data_buf, + size_t buffer_size) { if (G_next_failure == KVSTORE_FAILURE_OE_FAILURE) { G_next_failure = KVSTORE_FAILURE_NONE; return OE_FAILURE; @@ -98,7 +98,7 @@ oe_result_t ocall_kvstore_get(size_t* _retval, return OE_OK; } -oe_result_t ocall_kvstore_remove(bool* _retval, char* key) { +oe_result_t mock_ocall_kvstore_remove(bool* _retval, char* key) { if (G_next_failure == KVSTORE_FAILURE_OE_FAILURE) { G_next_failure = KVSTORE_FAILURE_NONE; return OE_FAILURE; diff --git a/firmware/src/hal/sgx/test/mock/mock_ocall.h b/firmware/src/hal/sgx/test/mock/mock_ocall.h index 6a905f9e..ec8dcb29 100644 --- a/firmware/src/hal/sgx/test/mock/mock_ocall.h +++ b/firmware/src/hal/sgx/test/mock/mock_ocall.h @@ -46,28 +46,28 @@ void mock_ocall_init(); /** * @brief Mock implementation of ocall_kvstore_save */ -oe_result_t ocall_kvstore_save(bool* _retval, - char* key, - uint8_t* data, - size_t data_size); +oe_result_t mock_ocall_kvstore_save(bool* _retval, + char* key, + uint8_t* data, + size_t data_size); /** * @brief Mock implementation of ocall_kvstore_exists */ -oe_result_t ocall_kvstore_exists(bool* _retval, char* key); +oe_result_t mock_ocall_kvstore_exists(bool* _retval, char* key); /** * @brief Mock implementation of ocall_kvstore_get */ -oe_result_t ocall_kvstore_get(size_t* _retval, - char* key, - uint8_t* data_buf, - size_t buffer_size); +oe_result_t mock_ocall_kvstore_get(size_t* _retval, + char* key, + uint8_t* data_buf, + size_t buffer_size); /** * @brief Mock implementation of ocall_kvstore_remove */ -oe_result_t ocall_kvstore_remove(bool* _retval, char* key); +oe_result_t mock_ocall_kvstore_remove(bool* _retval, char* key); /** * @brief Simulates a failure on the next call to this mock implementation diff --git a/firmware/src/hal/sgx/test/mock/mock_seal.c b/firmware/src/hal/sgx/test/mock/mock_seal.c index 6f095ca6..a1247c04 100644 --- a/firmware/src/hal/sgx/test/mock/mock_seal.c +++ b/firmware/src/hal/sgx/test/mock/mock_seal.c @@ -65,15 +65,15 @@ void mock_seal_init() { G_next_failure = SEAL_FAILURE_NONE; } -oe_result_t oe_seal(const void* plugin_id, - const oe_seal_setting_t* settings, - size_t settings_count, - const uint8_t* plaintext, - size_t plaintext_size, - const uint8_t* additional_data, - size_t additional_data_size, - uint8_t** blob, - size_t* blob_size) { +oe_result_t mock_oe_seal(const void* plugin_id, + const oe_seal_setting_t* settings, + size_t settings_count, + const uint8_t* plaintext, + size_t plaintext_size, + const uint8_t* additional_data, + size_t additional_data_size, + uint8_t** blob, + size_t* blob_size) { G_oe_seal_args.plugin_id = plugin_id; memcpy(&G_oe_seal_args.settings, settings, sizeof(oe_seal_setting_t)); G_oe_seal_args.settings_count = settings_count; @@ -97,12 +97,12 @@ oe_result_t oe_seal(const void* plugin_id, return OE_OK; } -oe_result_t oe_unseal(const uint8_t* blob, - size_t blob_size, - const uint8_t* additional_data, - size_t additional_data_size, - uint8_t** plaintext, - size_t* plaintext_size) { +oe_result_t mock_oe_unseal(const uint8_t* blob, + size_t blob_size, + const uint8_t* additional_data, + size_t additional_data_size, + uint8_t** plaintext, + size_t* plaintext_size) { memcpy(G_oe_unseal_args.blob, blob, blob_size); G_oe_unseal_args.blob_size = blob_size; G_oe_unseal_args.additional_data = additional_data; diff --git a/firmware/src/hal/sgx/test/mock/mock_seal.h b/firmware/src/hal/sgx/test/mock/mock_seal.h index dd6af69f..e7406533 100644 --- a/firmware/src/hal/sgx/test/mock/mock_seal.h +++ b/firmware/src/hal/sgx/test/mock/mock_seal.h @@ -29,6 +29,7 @@ #include #include #include "openenclave/common.h" +#include "openenclave/seal.h" // Types of failures that can be simulated in this mock implementation typedef enum mock_seal_failure_type { @@ -37,15 +38,6 @@ typedef enum mock_seal_failure_type { SEAL_FAILURE_OE_FAILURE, } mock_seal_failure_type_t; -// Simplified version of the seal settings type. This is only used to ensure -// that the API was called with the expected parameters. -typedef struct { - int policy; -} oe_seal_setting_t; - -#define OE_SEAL_SET_POLICY(policy) \ - { (int)(policy) } - /** * @brief Initializes the mock seal implementation */ @@ -54,25 +46,25 @@ void mock_seal_init(); /** * @brief Mock implementation of oe_seal API function */ -oe_result_t oe_seal(const void* plugin_id, - const oe_seal_setting_t* settings, - size_t settings_count, - const uint8_t* plaintext, - size_t plaintext_size, - const uint8_t* additional_data, - size_t additional_data_size, - uint8_t** blob, - size_t* blob_size); +oe_result_t mock_oe_seal(const void* plugin_id, + const oe_seal_setting_t* settings, + size_t settings_count, + const uint8_t* plaintext, + size_t plaintext_size, + const uint8_t* additional_data, + size_t additional_data_size, + uint8_t** blob, + size_t* blob_size); /** * @brief Mock implementation of oe_unseal API function */ -oe_result_t oe_unseal(const uint8_t* blob, - size_t blob_size, - const uint8_t* additional_data, - size_t additional_data_size, - uint8_t** plaintext, - size_t* plaintext_size); +oe_result_t mock_oe_unseal(const uint8_t* blob, + size_t blob_size, + const uint8_t* additional_data, + size_t additional_data_size, + uint8_t** plaintext, + size_t* plaintext_size); /** * @brief Asserts that the last call to oe_seal was made with the expected diff --git a/firmware/src/hal/sgx/test/mock/openenclave/seal.h b/firmware/src/hal/sgx/test/mock/openenclave/seal.h index 9a0d367d..21e3d657 100644 --- a/firmware/src/hal/sgx/test/mock/openenclave/seal.h +++ b/firmware/src/hal/sgx/test/mock/openenclave/seal.h @@ -25,6 +25,34 @@ #ifndef __MOCK_OE_SEAL_H #define __MOCK_OE_SEAL_H -#include "mock_seal.h" +#include +#include +#include "common.h" + +// Simplified version of the seal settings type. This is only used to ensure +// that the API was called with the expected parameters. +typedef struct { + int policy; +} oe_seal_setting_t; + +#define OE_SEAL_SET_POLICY(policy) \ + { (int)(policy) } + +oe_result_t oe_seal(const void* plugin_id, + const oe_seal_setting_t* settings, + size_t settings_count, + const uint8_t* plaintext, + size_t plaintext_size, + const uint8_t* additional_data, + size_t additional_data_size, + uint8_t** blob, + size_t* blob_size); + +oe_result_t oe_unseal(const uint8_t* blob, + size_t blob_size, + const uint8_t* additional_data, + size_t additional_data_size, + uint8_t** plaintext, + size_t* plaintext_size); #endif // #ifndef __MOCK_OE_SEAL_H \ No newline at end of file diff --git a/firmware/src/hal/sgx/test/secret_store/test_secret_store.c b/firmware/src/hal/sgx/test/secret_store/test_secret_store.c index ae93c3e8..ea8f3b97 100644 --- a/firmware/src/hal/sgx/test/secret_store/test_secret_store.c +++ b/firmware/src/hal/sgx/test/secret_store/test_secret_store.c @@ -37,12 +37,72 @@ // The maximum value that can be returned by sest_read #define MAX_SEST_READ_SIZE (255) +// Hand over the seal API calls to the mock implementation +oe_result_t oe_seal(const void* plugin_id, + const oe_seal_setting_t* settings, + size_t settings_count, + const uint8_t* plaintext, + size_t plaintext_size, + const uint8_t* additional_data, + size_t additional_data_size, + uint8_t** blob, + size_t* blob_size) { + return mock_oe_seal(plugin_id, + settings, + settings_count, + plaintext, + plaintext_size, + additional_data, + additional_data_size, + blob, + blob_size); +} + +oe_result_t oe_unseal(const uint8_t* blob, + size_t blob_size, + const uint8_t* additional_data, + size_t additional_data_size, + uint8_t** plaintext, + size_t* plaintext_size) { + return mock_oe_unseal(blob, + blob_size, + additional_data, + additional_data_size, + plaintext, + plaintext_size); +} + +// Hand over the kvstore calls to the mock implementation +oe_result_t ocall_kvstore_save(bool* _retval, + char* key, + uint8_t* data, + size_t data_size) { + return mock_ocall_kvstore_save(_retval, key, data, data_size); +} + +oe_result_t ocall_kvstore_exists(bool* _retval, char* key) { + return mock_ocall_kvstore_exists(_retval, key); +} + +oe_result_t ocall_kvstore_get(size_t* _retval, + char* key, + uint8_t* data_buf, + size_t buffer_size) { + return mock_ocall_kvstore_get(_retval, key, data_buf, buffer_size); +} + +oe_result_t ocall_kvstore_remove(bool* _retval, char* key) { + return mock_ocall_kvstore_remove(_retval, key); +} + +// Helper functions void setup() { mock_seal_init(); mock_ocall_init(); assert(sest_init()); } +// Test cases void test_secret_exists_after_write() { setup(); printf("Test secret exists after write...\n"); From 03da1bd87871fcea07275f10d3ca53a89e558a6a Mon Sep 17 00:00:00 2001 From: Italo Sampaio Date: Tue, 8 Oct 2024 18:56:32 -0300 Subject: [PATCH 5/6] Fixed tightly coupled mocks - mocks no longer depend on specific definitions from secret_store - now asserting that the expected values are written to the kvstore - read/remove/exist tests now just write data interact directly to the kvstore and only test the read/remove/exist logic --- firmware/src/hal/sgx/test/mock/mock_ocall.c | 31 ++-- firmware/src/hal/sgx/test/mock/mock_ocall.h | 5 +- firmware/src/hal/sgx/test/mock/mock_seal.c | 40 ++--- firmware/src/hal/sgx/test/mock/mock_seal.h | 17 +- .../sgx/test/secret_store/test_secret_store.c | 148 ++++++++---------- 5 files changed, 102 insertions(+), 139 deletions(-) diff --git a/firmware/src/hal/sgx/test/mock/mock_ocall.c b/firmware/src/hal/sgx/test/mock/mock_ocall.c index 8a5fcfbd..24a05e7c 100644 --- a/firmware/src/hal/sgx/test/mock/mock_ocall.c +++ b/firmware/src/hal/sgx/test/mock/mock_ocall.c @@ -22,17 +22,18 @@ * IN THE SOFTWARE. */ +#include #include +#include "assert_utils.h" #include "openenclave/common.h" #include "mock_ocall.h" -// Maximum size of a sealed blob, as defined in secret_store.c -#define MAX_BLOB_SIZE (1024 * 1024) - // Trivial key-value store implementation for testing purposes // This key-value store is only capable of storing a single key-value pair -static char G_kvstore_key[256]; -static uint8_t G_kvstore_data[256]; +#define MOCK_KVSTORE_MAX_KEY_SIZE (256) +#define MOCK_KVSTORE_MAX_DATA_SIZE (2 * 1024 * 1024) +static char G_kvstore_key[MOCK_KVSTORE_MAX_KEY_SIZE]; +static uint8_t G_kvstore_data[MOCK_KVSTORE_MAX_DATA_SIZE]; static size_t G_kvstore_data_size; // The type of failure to simulate on the next call to the mock implementation static mock_kvstore_failure_type_t G_next_failure; @@ -58,6 +59,7 @@ oe_result_t mock_ocall_kvstore_save(bool* _retval, } strcpy(G_kvstore_key, key); + assert(data_size <= sizeof(G_kvstore_data)); memcpy(G_kvstore_data, data, data_size); G_kvstore_data_size = data_size; *_retval = true; @@ -70,7 +72,7 @@ oe_result_t mock_ocall_kvstore_exists(bool* _retval, char* key) { return OE_FAILURE; } - *_retval = (strcmp(key, G_kvstore_key) == 0); + *_retval = mock_ocall_kstore_key_exists(key); return OE_OK; } @@ -84,13 +86,7 @@ oe_result_t mock_ocall_kvstore_get(size_t* _retval, } if (strcmp(key, G_kvstore_key) == 0) { - if (G_next_failure == KVSTORE_FAILURE_GET_SEALED_BLOB_TOO_LARGE) { - // Return a blob size that exceeds the limit allowed by the caller - G_next_failure = KVSTORE_FAILURE_NONE; - *_retval = MAX_BLOB_SIZE + 1; - } else { - *_retval = G_kvstore_data_size; - } + *_retval = G_kvstore_data_size; memcpy(data_buf, G_kvstore_data, G_kvstore_data_size); } else { *_retval = 0; @@ -117,3 +113,12 @@ oe_result_t mock_ocall_kvstore_remove(bool* _retval, char* key) { void mock_ocall_kvstore_fail_next(mock_kvstore_failure_type_t failure) { G_next_failure = failure; } + +void mock_ocall_kstore_assert_value(char* key, const uint8_t* value) { + ASSERT_STR_EQUALS(key, G_kvstore_key); + ASSERT_STR_EQUALS(value, G_kvstore_data); +} + +bool mock_ocall_kstore_key_exists(char* key) { + return (strcmp(key, G_kvstore_key) == 0); +} diff --git a/firmware/src/hal/sgx/test/mock/mock_ocall.h b/firmware/src/hal/sgx/test/mock/mock_ocall.h index ec8dcb29..afefa187 100644 --- a/firmware/src/hal/sgx/test/mock/mock_ocall.h +++ b/firmware/src/hal/sgx/test/mock/mock_ocall.h @@ -34,7 +34,6 @@ typedef enum mock_kvstore_failure_type { KVSTORE_FAILURE_NONE, KVSTORE_FAILURE_SAVE, - KVSTORE_FAILURE_GET_SEALED_BLOB_TOO_LARGE, KVSTORE_FAILURE_OE_FAILURE, } mock_kvstore_failure_type_t; @@ -76,4 +75,8 @@ oe_result_t mock_ocall_kvstore_remove(bool* _retval, char* key); */ void mock_ocall_kvstore_fail_next(mock_kvstore_failure_type_t failure); +void mock_ocall_kstore_assert_value(char* key, const uint8_t* value); + +bool mock_ocall_kstore_key_exists(char* key); + #endif // __MOCK_OCALL_H \ No newline at end of file diff --git a/firmware/src/hal/sgx/test/mock/mock_seal.c b/firmware/src/hal/sgx/test/mock/mock_seal.c index a1247c04..88decc16 100644 --- a/firmware/src/hal/sgx/test/mock/mock_seal.c +++ b/firmware/src/hal/sgx/test/mock/mock_seal.c @@ -24,12 +24,10 @@ #include #include +#include #include "mock_seal.h" #include "assert_utils.h" -// The maximum allowed blob size, as defined in secret_store.c -#define MAX_BLOB_SIZE (1024 * 1024) - // A prefix added to sealed blobs in this mock implementation. // This is just to keep things simple and easily distinguishable. #define SEALED_PREFIX "SEALED - " @@ -47,7 +45,7 @@ typedef struct oe_seal_args { // Captures the arguments passed to oe_unseal typedef struct oe_unseal_args { - uint8_t blob[MAX_BLOB_SIZE]; + uint8_t blob[BUFSIZ]; size_t blob_size; const uint8_t* additional_data; size_t additional_data_size; @@ -56,13 +54,13 @@ typedef struct oe_unseal_args { // Global variables to capture the arguments passed to oe_seal and oe_unseal static oe_seal_args_t G_oe_seal_args; static oe_unseal_args_t G_oe_unseal_args; -// The next failure type to simulate -mock_seal_failure_type_t G_next_failure = SEAL_FAILURE_NONE; +// Simulates a OE_FAILURE in the next call to this mock implementation +static bool G_fail_next = false; void mock_seal_init() { memset(&G_oe_seal_args, 0, sizeof(G_oe_seal_args)); memset(&G_oe_unseal_args, 0, sizeof(G_oe_unseal_args)); - G_next_failure = SEAL_FAILURE_NONE; + G_fail_next = false; } oe_result_t mock_oe_seal(const void* plugin_id, @@ -82,8 +80,8 @@ oe_result_t mock_oe_seal(const void* plugin_id, G_oe_seal_args.additional_data = additional_data; G_oe_seal_args.additional_data_size = additional_data_size; - if (G_next_failure == SEAL_FAILURE_OE_FAILURE) { - G_next_failure = SEAL_FAILURE_NONE; + if (G_fail_next) { + G_fail_next = false; return OE_FAILURE; } @@ -108,22 +106,12 @@ oe_result_t mock_oe_unseal(const uint8_t* blob, G_oe_unseal_args.additional_data = additional_data; G_oe_unseal_args.additional_data_size = additional_data_size; - switch (G_next_failure) { - case SEAL_FAILURE_OE_FAILURE: - G_next_failure = SEAL_FAILURE_NONE; + if (G_fail_next) { + G_fail_next = false; return OE_FAILURE; - case SEAL_FAILURE_OE_UNSEAL_PLAINTEXT_TOO_LARGE: - *plaintext_size = MAX_BLOB_SIZE + 1; - G_next_failure = SEAL_FAILURE_NONE; - break; - case SEAL_FAILURE_NONE: - *plaintext_size = blob_size - strlen(SEALED_PREFIX); - break; - default: - assert(false); - break; } + *plaintext_size = blob_size - strlen(SEALED_PREFIX); *plaintext = malloc(*plaintext_size); assert(*plaintext != NULL); memcpy(*plaintext, blob + strlen(SEALED_PREFIX), *plaintext_size); @@ -174,10 +162,6 @@ void assert_oe_seal_not_called() { assert(G_oe_seal_args.additional_data_size == 0); } -void mock_seal_fail_next(mock_seal_failure_type_t failure) { - G_next_failure = failure; -} - -size_t mock_seal_get_max_plaintext_size() { - return MAX_BLOB_SIZE - strlen(SEALED_PREFIX); +void mock_seal_fail_next() { + G_fail_next = true; } diff --git a/firmware/src/hal/sgx/test/mock/mock_seal.h b/firmware/src/hal/sgx/test/mock/mock_seal.h index e7406533..cc968565 100644 --- a/firmware/src/hal/sgx/test/mock/mock_seal.h +++ b/firmware/src/hal/sgx/test/mock/mock_seal.h @@ -31,13 +31,6 @@ #include "openenclave/common.h" #include "openenclave/seal.h" -// Types of failures that can be simulated in this mock implementation -typedef enum mock_seal_failure_type { - SEAL_FAILURE_NONE, - SEAL_FAILURE_OE_UNSEAL_PLAINTEXT_TOO_LARGE, - SEAL_FAILURE_OE_FAILURE, -} mock_seal_failure_type_t; - /** * @brief Initializes the mock seal implementation */ @@ -99,15 +92,7 @@ void assert_oe_seal_not_called(); /** * @brief Simulates a failure on the next call to this mock implementation - * - * @param failure the type of failure to simulate - */ -void mock_seal_fail_next(mock_seal_failure_type_t failure); - -/** - * @brief Returns the maximum plaintext size allowed, based on the maximum blob - * size. */ -size_t mock_seal_get_max_plaintext_size(); +void mock_seal_fail_next(); #endif // #ifndef __MOCK_SEAL_H diff --git a/firmware/src/hal/sgx/test/secret_store/test_secret_store.c b/firmware/src/hal/sgx/test/secret_store/test_secret_store.c index ea8f3b97..51e9a919 100644 --- a/firmware/src/hal/sgx/test/secret_store/test_secret_store.c +++ b/firmware/src/hal/sgx/test/secret_store/test_secret_store.c @@ -36,6 +36,10 @@ #define SEST_ERROR (0) // The maximum value that can be returned by sest_read #define MAX_SEST_READ_SIZE (255) +// The maximum blob_size for a sealed secret, as defined in secret_store.c +#define MAX_BLOB_SIZE (1024 * 1024) +// Utility macro that converts a plaintext secret into the sealed version +#define SEALED(str) ("SEALED - " str) // Hand over the seal API calls to the mock implementation oe_result_t oe_seal(const void* plugin_id, @@ -96,6 +100,13 @@ oe_result_t ocall_kvstore_remove(bool* _retval, char* key) { } // Helper functions +void save_to_mock_kvstore(char* key, uint8_t* value, size_t value_size) { + bool save_success = false; + mock_ocall_kvstore_save(&save_success, key, value, value_size); + mock_ocall_kstore_assert_value(key, value); + assert(save_success); +} + void setup() { mock_seal_init(); mock_ocall_init(); @@ -109,10 +120,12 @@ void test_secret_exists_after_write() { char* key = "key"; uint8_t secret[] = "secret"; + uint8_t sealed_secret[] = SEALED("secret"); // Ensure the secret doesn't exist before the write assert(!sest_exists(key)); + assert(!mock_ocall_kstore_key_exists(key)); // Write the secret and ensure it now exists - assert(sest_write("key", secret, sizeof(secret))); + assert(sest_write(key, secret, sizeof(secret))); assert_oe_seal_called_with( NULL, (const oe_seal_setting_t[]){OE_SEAL_SET_POLICY(1)}, @@ -121,7 +134,8 @@ void test_secret_exists_after_write() { sizeof(secret), NULL, 0); - assert(sest_exists("key")); + mock_ocall_kstore_assert_value(key, sealed_secret); + assert(sest_exists(key)); } void test_write_and_retrieve_secret() { @@ -130,6 +144,7 @@ void test_write_and_retrieve_secret() { char* key = "key"; uint8_t secret[] = "secret"; + uint8_t sealed_secret[] = SEALED("secret"); // Write the secret and make sure the seal API is called with the correct // arguments assert(sest_write(key, secret, sizeof(secret))); @@ -141,13 +156,12 @@ void test_write_and_retrieve_secret() { sizeof(secret), NULL, 0); + mock_ocall_kstore_assert_value(key, sealed_secret); // Retrieve the secret and make sure the unseal API is called with the // correct arguments uint8_t retrieved[MAX_SEST_READ_SIZE]; uint8_t retrieved_length = sest_read(key, retrieved, sizeof(retrieved)); - uint8_t expected_sealed_blob[] = "SEALED - secret"; - assert_oe_unseal_called_with( - expected_sealed_blob, sizeof(expected_sealed_blob), NULL, 0); + assert_oe_unseal_called_with(sealed_secret, sizeof(sealed_secret), NULL, 0); assert(retrieved_length == sizeof(secret)); ASSERT_MEMCMP(retrieved, secret, retrieved_length); } @@ -158,6 +172,7 @@ void test_write_and_remove_secret() { char* key = "key"; uint8_t secret[] = "secret"; + uint8_t sealed_secret[] = SEALED("secret"); assert(sest_write(key, secret, sizeof(secret))); assert_oe_seal_called_with( NULL, @@ -167,28 +182,21 @@ void test_write_and_remove_secret() { sizeof(secret), NULL, 0); + mock_ocall_kstore_assert_value(key, sealed_secret); assert(sest_exists(key)); assert(sest_remove(key)); assert(!sest_exists(key)); + assert(!mock_ocall_kstore_key_exists(key)); } void test_exists_fails_when_kvstore_exists_fails() { setup(); printf("Test sest_exists fails when ocall_kvstore_exists fails...\n"); - // Write a valid secret and ensure it exists + // Write a valid secret to the kvstore and ensure it exists char* key = "key"; - uint8_t secret[] = "secret"; - assert(!sest_exists(key)); - assert(sest_write(key, secret, sizeof(secret))); - assert_oe_seal_called_with( - NULL, - (const oe_seal_setting_t[]){OE_SEAL_SET_POLICY(1)}, - 1, - secret, - sizeof(secret), - NULL, - 0); + uint8_t sealed_secret[] = SEALED("secret"); + save_to_mock_kvstore(key, sealed_secret, sizeof(sealed_secret)); assert(sest_exists(key)); // Force the next call to ocall_kvstore_exists to fail @@ -200,18 +208,18 @@ void test_read_fails_when_oe_unseal_fails() { setup(); printf("Test read fails when oe_unseal fails (OE_FAILURE)...\n"); + // Write a valid secret to the kvstore and ensure it exists char* key = "key"; - uint8_t secret[] = "secret"; - assert(sest_write(key, secret, sizeof(secret))); + uint8_t sealed_secret[] = SEALED("secret"); + save_to_mock_kvstore(key, sealed_secret, sizeof(sealed_secret)); assert(sest_exists(key)); + // Force the next call to oe_unseal to fail with OE_FAILURE - mock_seal_fail_next(SEAL_FAILURE_OE_FAILURE); + mock_seal_fail_next(); uint8_t retrieved[MAX_SEST_READ_SIZE]; memset(retrieved, 0, sizeof(retrieved)); uint8_t retrieved_length = sest_read(key, retrieved, sizeof(retrieved)); - uint8_t expected_sealed_blob[] = "SEALED - secret"; - assert_oe_unseal_called_with( - expected_sealed_blob, sizeof(expected_sealed_blob), NULL, 0); + assert_oe_unseal_called_with(sealed_secret, sizeof(sealed_secret), NULL, 0); assert(retrieved_length == SEST_ERROR); ASSERT_ARRAY_CLEARED(retrieved); } @@ -220,19 +228,17 @@ void test_read_fails_when_plaintext_is_too_large() { setup(); printf("Test read fails when unsealed secret is too large...\n"); + // Write a valid secret to the kvstore and ensure it exists char* key = "key"; uint8_t secret[] = "secret"; - assert(sest_write(key, secret, sizeof(secret))); + uint8_t sealed_secret[] = SEALED("secret"); + save_to_mock_kvstore(key, sealed_secret, sizeof(sealed_secret)); assert(sest_exists(key)); - // Force the next call to oe_unseal to fail by returning a plaintext that is - // too large - mock_seal_fail_next(SEAL_FAILURE_OE_UNSEAL_PLAINTEXT_TOO_LARGE); - uint8_t retrieved[MAX_SEST_READ_SIZE]; + // The retrieved buffer is one byte too short to fit the original secret + uint8_t retrieved[sizeof(secret) - 1]; memset(retrieved, 0, sizeof(retrieved)); uint8_t retrieved_length = sest_read(key, retrieved, sizeof(retrieved)); - uint8_t expected_sealed_blob[] = "SEALED - secret"; - assert_oe_unseal_called_with( - expected_sealed_blob, sizeof(expected_sealed_blob), NULL, 0); + assert_oe_unseal_called_with(sealed_secret, sizeof(sealed_secret), NULL, 0); assert(retrieved_length == SEST_ERROR); ASSERT_ARRAY_CLEARED(retrieved); } @@ -244,11 +250,13 @@ void test_write_zero_length_secret_fails() { char* key = "key"; // Ensure the secret doesn't exist before the write assert(!sest_exists(key)); + assert(!mock_ocall_kstore_key_exists(key)); // Write the secret and ensure it fails assert(!sest_write(key, NULL, 0)); // Make sure the seal API was never reached assert_oe_seal_not_called(); assert(!sest_exists(key)); + assert(!mock_ocall_kstore_key_exists(key)); } void test_write_fails_when_oe_seal_fails() { @@ -256,7 +264,7 @@ void test_write_fails_when_oe_seal_fails() { printf("Test write fails when oe_seal fails (OE_FAILURE)...\n"); // Force the next call to oe_seal to fail - mock_seal_fail_next(SEAL_FAILURE_OE_FAILURE); + mock_seal_fail_next(); char* key = "key"; uint8_t secret[] = "secret"; assert(!sest_exists(key)); @@ -270,6 +278,7 @@ void test_write_fails_when_oe_seal_fails() { NULL, 0); assert(!sest_exists(key)); + assert(!mock_ocall_kstore_key_exists(key)); } void test_write_fails_when_kvstore_save_fails() { @@ -291,6 +300,7 @@ void test_write_fails_when_kvstore_save_fails() { NULL, 0); assert(!sest_exists(key)); + assert(!mock_ocall_kstore_key_exists(key)); } void test_write_fails_when_kvstore_save_fails_oe_failure() { @@ -312,6 +322,7 @@ void test_write_fails_when_kvstore_save_fails_oe_failure() { NULL, 0); assert(!sest_exists(key)); + assert(!mock_ocall_kstore_key_exists(key)); } void test_write_fails_when_secret_too_large() { @@ -320,7 +331,7 @@ void test_write_fails_when_secret_too_large() { // Attempt to write a secret that is too large char* key = "key"; - size_t secret_size = mock_seal_get_max_plaintext_size() + 1; + size_t secret_size = MAX_BLOB_SIZE + 1; uint8_t secret[secret_size]; assert(!sest_exists(key)); assert(!sest_write(key, secret, secret_size)); @@ -333,23 +344,17 @@ void test_write_fails_when_secret_too_large() { NULL, 0); assert(!sest_exists(key)); + assert(!mock_ocall_kstore_key_exists(key)); } void test_read_with_invalid_key_fails() { setup(); printf("Test read with invalid key fails...\n"); + // Write a valid secret to the kvstore and ensure it exists char* valid_key = "valid key"; - uint8_t secret[] = "secret"; - assert(sest_write(valid_key, secret, sizeof(secret))); - assert_oe_seal_called_with( - NULL, - (const oe_seal_setting_t[]){OE_SEAL_SET_POLICY(1)}, - 1, - secret, - sizeof(secret), - NULL, - 0); + uint8_t sealed_secret[] = SEALED("secret"); + save_to_mock_kvstore(valid_key, sealed_secret, sizeof(sealed_secret)); assert(sest_exists(valid_key)); char* invalid_key = "invalid key"; @@ -364,19 +369,12 @@ void test_read_fails_when_kvstore_get_fails() { setup(); printf("Test read fails when ocall_kvstore_get fails (OE_FAILURE)...\n"); + // Write a valid secret to the kvstore and ensure it exists char* key = "key"; - uint8_t secret[] = "secret"; - // Write the secret - assert(sest_write(key, secret, sizeof(secret))); - assert_oe_seal_called_with( - NULL, - (const oe_seal_setting_t[]){OE_SEAL_SET_POLICY(1)}, - 1, - secret, - sizeof(secret), - NULL, - 0); + uint8_t sealed_secret[] = SEALED("secret"); + save_to_mock_kvstore(key, sealed_secret, sizeof(sealed_secret)); assert(sest_exists(key)); + // Force OE_FAILURE on the next call to ocall_kvstore_get mock_ocall_kvstore_fail_next(KVSTORE_FAILURE_OE_FAILURE); uint8_t retrieved[MAX_SEST_READ_SIZE]; @@ -391,22 +389,13 @@ void test_read_fails_when_blob_is_too_large() { setup(); printf("Test read fails sealed blob is too large...\n"); + // Pre-load the kvstore with a secret that is larger than the maximum + // allowed blob size char* key = "key"; - uint8_t secret[] = "secret"; - // Write the secret - assert(sest_write(key, secret, sizeof(secret))); - assert_oe_seal_called_with( - NULL, - (const oe_seal_setting_t[]){OE_SEAL_SET_POLICY(1)}, - 1, - secret, - sizeof(secret), - NULL, - 0); + uint8_t secret[MAX_BLOB_SIZE + 1]; + save_to_mock_kvstore(key, secret, sizeof(secret)); + assert(sest_exists(key)); - // Force the next call to ocall_kvstore_get to return a blob that is too - // large - mock_ocall_kvstore_fail_next(KVSTORE_FAILURE_GET_SEALED_BLOB_TOO_LARGE); uint8_t retrieved[MAX_SEST_READ_SIZE]; memset(retrieved, 0, sizeof(retrieved)); uint8_t retrieved_length = sest_read(key, retrieved, sizeof(retrieved)); @@ -417,14 +406,18 @@ void test_read_fails_when_blob_is_too_large() { void test_remove_with_invalid_key_fails() { setup(); - printf("Test write and remove invalid key fails...\n"); + printf("Test remove invalid key fails...\n"); char* valid_key = "valid key"; - uint8_t secret[] = "secret"; - assert(sest_write(valid_key, secret, sizeof(secret))); + uint8_t sealed_secret[] = SEALED("secret"); + save_to_mock_kvstore(valid_key, sealed_secret, sizeof(sealed_secret)); assert(sest_exists(valid_key)); + char* invalid_key = "invalid key"; assert(!sest_remove(invalid_key)); + // Make sure the valid key still exists + assert(sest_exists(valid_key)); + mock_ocall_kstore_assert_value(valid_key, sealed_secret); } void test_remove_fails_when_kvstore_remove_fails() { @@ -432,21 +425,14 @@ void test_remove_fails_when_kvstore_remove_fails() { printf("Test remove fails when ocall_kvstore_remove fails...\n"); char* key = "key"; - uint8_t secret[] = "secret"; - assert(sest_write(key, secret, sizeof(secret))); - assert_oe_seal_called_with( - NULL, - (const oe_seal_setting_t[]){OE_SEAL_SET_POLICY(1)}, - 1, - secret, - sizeof(secret), - NULL, - 0); + uint8_t sealed_secret[] = SEALED("secret"); + save_to_mock_kvstore(key, sealed_secret, sizeof(sealed_secret)); assert(sest_exists(key)); // Force the next call to ocall_kvstore_remove to fail mock_ocall_kvstore_fail_next(KVSTORE_FAILURE_OE_FAILURE); assert(!sest_remove(key)); assert(sest_exists(key)); + mock_ocall_kstore_assert_value(key, sealed_secret); } int main() { From 1a568c43dc2cefdb2bf384fdd88c0f51c84f34e9 Mon Sep 17 00:00:00 2001 From: Italo Sampaio Date: Wed, 9 Oct 2024 10:32:20 -0300 Subject: [PATCH 6/6] Removed mock files from coverage report --- firmware/coverage/gen-coverage | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/firmware/coverage/gen-coverage b/firmware/coverage/gen-coverage index 4fd85d76..d4a647b1 100755 --- a/firmware/coverage/gen-coverage +++ b/firmware/coverage/gen-coverage @@ -33,10 +33,11 @@ if [[ $1 == "exec" ]]; then # Capture coverage data lcov --capture --directory $SRCDIR --list-full-path --output-file $BASEDIR/coverage.info - # Remove unwanted coverage info (test files, tcpsigner, x86 HAL implementation) + # Remove unwanted coverage info (test files, tcpsigner, x86 HAL implementation, mock files) lcov --remove $BASEDIR/coverage.info "*/test_*.c" --output-file $BASEDIR/coverage.info lcov --remove $BASEDIR/coverage.info "*/tcpsigner/src/*" --output-file $BASEDIR/coverage.info lcov --remove $BASEDIR/coverage.info "*/hal/src/x86/*" --output-file $BASEDIR/coverage.info + lcov --remove $BASEDIR/coverage.info "*/mock_*.c" --output-file $BASEDIR/coverage.info # Generate report and summary genhtml $BASEDIR/coverage.info --output $BASEDIR/output -p $SRCDIR -t "powHSM firmware" lcov --summary $BASEDIR/coverage.info | grep lines | sed -e "s/.\+lines.\+: \([[:digit:].]\+\).\+/\1/g" > $BASEDIR/output/total