Skip to content

Commit

Permalink
Added secret store unit tests
Browse files Browse the repository at this point in the history
- 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
  • Loading branch information
italo-sampaio committed Oct 8, 2024
1 parent 1adf629 commit 8fd993b
Show file tree
Hide file tree
Showing 13 changed files with 982 additions and 2 deletions.
2 changes: 1 addition & 1 deletion firmware/src/hal/sgx/test/common/common.mk
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
6 changes: 6 additions & 0 deletions firmware/src/hal/sgx/test/mock/hsm_t.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#ifndef __MOCK_HSM_T_H
#define __MOCK_HSM_T_H

#include "mock_ocall.h"

#endif // __MOCK_HSM_T_H
2 changes: 2 additions & 0 deletions firmware/src/hal/sgx/test/mock/mock.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,7 @@
#define __MOCK_H

#include "mock_secret_store.h"
#include "mock_seal.h"
#include "mock_ocall.h"

#endif // #ifndef __MOCK_H
95 changes: 95 additions & 0 deletions firmware/src/hal/sgx/test/mock/mock_ocall.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
#include <string.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];
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;
}
79 changes: 79 additions & 0 deletions firmware/src/hal/sgx/test/mock/mock_ocall.h
Original file line number Diff line number Diff line change
@@ -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 <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#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
159 changes: 159 additions & 0 deletions firmware/src/hal/sgx/test/mock/mock_seal.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
#include <assert.h>
#include <string.h>
#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);
}
Loading

0 comments on commit 8fd993b

Please sign in to comment.