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 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, 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..5f639f43 --- /dev/null +++ b/firmware/src/hal/sgx/test/mock/hsm_t.h @@ -0,0 +1,47 @@ +/** + * 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 + +#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.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..24a05e7c --- /dev/null +++ b/firmware/src/hal/sgx/test/mock/mock_ocall.c @@ -0,0 +1,124 @@ +/** + * 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 "assert_utils.h" +#include "openenclave/common.h" +#include "mock_ocall.h" + +// Trivial key-value store implementation for testing purposes +// This key-value store is only capable of storing a single key-value pair +#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; + +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 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; + 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); + assert(data_size <= sizeof(G_kvstore_data)); + memcpy(G_kvstore_data, data, data_size); + G_kvstore_data_size = data_size; + *_retval = true; + return OE_OK; +} + +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; + } + + *_retval = mock_ocall_kstore_key_exists(key); + return OE_OK; +} + +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; + } + + if (strcmp(key, G_kvstore_key) == 0) { + *_retval = G_kvstore_data_size; + memcpy(data_buf, G_kvstore_data, G_kvstore_data_size); + } else { + *_retval = 0; + } + return OE_OK; +} + +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; + } + 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; +} + +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 new file mode 100644 index 00000000..afefa187 --- /dev/null +++ b/firmware/src/hal/sgx/test/mock/mock_ocall.h @@ -0,0 +1,82 @@ +/** + * 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_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 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 mock_ocall_kvstore_exists(bool* _retval, char* key); + +/** + * @brief Mock implementation of ocall_kvstore_get + */ +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 mock_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); + +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 new file mode 100644 index 00000000..88decc16 --- /dev/null +++ b/firmware/src/hal/sgx/test/mock/mock_seal.c @@ -0,0 +1,167 @@ +/** + * 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 +#include "mock_seal.h" +#include "assert_utils.h" + +// 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[BUFSIZ]; + 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; +// 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_fail_next = false; +} + +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; + 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_fail_next) { + G_fail_next = false; + 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 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; + G_oe_unseal_args.additional_data_size = additional_data_size; + + if (G_fail_next) { + G_fail_next = false; + return OE_FAILURE; + } + + *plaintext_size = blob_size - strlen(SEALED_PREFIX); + *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() { + 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 new file mode 100644 index 00000000..cc968565 --- /dev/null +++ b/firmware/src/hal/sgx/test/mock/mock_seal.h @@ -0,0 +1,98 @@ +/** + * 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" +#include "openenclave/seal.h" + +/** + * @brief Initializes the mock seal implementation + */ +void mock_seal_init(); + +/** + * @brief Mock implementation of oe_seal API function + */ +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 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 + * 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 + */ +void mock_seal_fail_next(); + +#endif // #ifndef __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/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..21e3d657 --- /dev/null +++ b/firmware/src/hal/sgx/test/mock/openenclave/seal.h @@ -0,0 +1,58 @@ +/** + * 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 +#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/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..51e9a919 --- /dev/null +++ b/firmware/src/hal/sgx/test/secret_store/test_secret_store.c @@ -0,0 +1,455 @@ +/** + * 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 +#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) +// 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, + 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 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(); + assert(sest_init()); +} + +// Test cases +void test_secret_exists_after_write() { + setup(); + printf("Test secret exists after write...\n"); + + 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_oe_seal_called_with( + NULL, + (const oe_seal_setting_t[]){OE_SEAL_SET_POLICY(1)}, + 1, + secret, + sizeof(secret), + NULL, + 0); + mock_ocall_kstore_assert_value(key, sealed_secret); + 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"; + 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))); + assert_oe_seal_called_with( + NULL, + (const oe_seal_setting_t[]){OE_SEAL_SET_POLICY(1)}, + 1, + 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)); + assert_oe_unseal_called_with(sealed_secret, sizeof(sealed_secret), 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"; + uint8_t sealed_secret[] = SEALED("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); + 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 to the kvstore and ensure it exists + char* key = "key"; + 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 + 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"); + + // Write a valid secret to the kvstore and ensure it exists + char* key = "key"; + 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(); + 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_called_with(sealed_secret, sizeof(sealed_secret), 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"); + + // Write a valid secret to the kvstore and ensure it exists + char* key = "key"; + uint8_t secret[] = "secret"; + uint8_t sealed_secret[] = SEALED("secret"); + save_to_mock_kvstore(key, sealed_secret, sizeof(sealed_secret)); + assert(sest_exists(key)); + // 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)); + assert_oe_unseal_called_with(sealed_secret, sizeof(sealed_secret), 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)); + 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() { + 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(); + 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)); + assert(!mock_ocall_kstore_key_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)); + assert(!mock_ocall_kstore_key_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)); + assert(!mock_ocall_kstore_key_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 = MAX_BLOB_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)); + 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 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"; + 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"); + + // Write a valid secret to the kvstore and ensure it exists + char* key = "key"; + 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]; + 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"); + + // Pre-load the kvstore with a secret that is larger than the maximum + // allowed blob size + char* key = "key"; + uint8_t secret[MAX_BLOB_SIZE + 1]; + save_to_mock_kvstore(key, secret, sizeof(secret)); + + assert(sest_exists(key)); + 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 remove invalid key fails...\n"); + + char* valid_key = "valid key"; + 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() { + setup(); + printf("Test remove fails when ocall_kvstore_remove fails...\n"); + + char* key = "key"; + 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() { + 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(); +}