diff options
author | Allen Webb <allenwebb@google.com> | 2018-02-28 15:42:47 -0800 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2018-04-27 12:22:26 -0700 |
commit | 1820ecce31e6a23e5ab79f708f66a8655da6a161 (patch) | |
tree | 9c4f09621ec2282208cfed21de76f8172fc9c888 | |
parent | 826a3876b4f3ecd5f73d2320ee1e853a789e6e30 (diff) | |
download | chrome-ec-1820ecce31e6a23e5ab79f708f66a8655da6a161.tar.gz |
Cr50: Add logging functionality to PinWeaver.
In order to be able to recover from the AP and Cr50 getting out of
sync, this logging functionality gives Cr50 a way to track the
state changes of the merkle tree so that the AP can be updated to
the current state as long as it has a recent enough copy.
This involves packing the important information so it can be stored
efficiently on flash, and adding the necessary messages for the
replay.
CQ-DEPEND=CL:895395,CL:929430
BRANCH=none
BUG=chromium:809729, chromium:809745
TEST=cd ~/src/platform/ec && V=1 make run-weaver_ng -j
Change-Id: I40f98de2c8e9706cccb5b922215699f2132fa121
Signed-off-by: Allen Webb <allenwebb@google.com>
Reviewed-on: https://chromium-review.googlesource.com/963773
Reviewed-by: Vadim Bendebury <vbendeb@chromium.org>
-rw-r--r-- | board/cr50/pinweaver_tpm_imports.c | 5 | ||||
-rw-r--r-- | board/cr50/tpm2/platform.c | 2 | ||||
-rw-r--r-- | board/host/dcrypto.h | 7 | ||||
-rw-r--r-- | common/pinweaver.c | 464 | ||||
-rw-r--r-- | include/pinweaver.h | 54 | ||||
-rw-r--r-- | include/pinweaver_tpm_imports.h | 2 | ||||
-rw-r--r-- | include/pinweaver_types.h | 60 | ||||
-rw-r--r-- | test/pinweaver.c | 778 | ||||
-rw-r--r-- | test/test_config.h | 2 |
9 files changed, 1338 insertions, 36 deletions
diff --git a/board/cr50/pinweaver_tpm_imports.c b/board/cr50/pinweaver_tpm_imports.c index eec2284dfd..abc1bed872 100644 --- a/board/cr50/pinweaver_tpm_imports.c +++ b/board/cr50/pinweaver_tpm_imports.c @@ -8,11 +8,6 @@ #include <Global.h> #include <util.h> -uint32_t get_restart_count(void) -{ - return gp.resetCount; -} - void get_storage_seed(void *buf, size_t *len) { *len = MIN(*len, sizeof(gp.SPSeed)); diff --git a/board/cr50/tpm2/platform.c b/board/cr50/tpm2/platform.c index e380d8afe1..3068c903e3 100644 --- a/board/cr50/tpm2/platform.c +++ b/board/cr50/tpm2/platform.c @@ -7,6 +7,7 @@ #include "TPM_Types.h" #include "ccd_config.h" +#include "pinweaver.h" #include "trng.h" #include "util.h" #include "version.h" @@ -64,5 +65,6 @@ void _plat__GetFwVersion(uint32_t *firmwareV1, uint32_t *firmwareV2) void _plat__ResetCallback(void) { + pinweaver_init(); ccd_tpm_reset_callback(); } diff --git a/board/host/dcrypto.h b/board/host/dcrypto.h index f949ef4352..48bde62592 100644 --- a/board/host/dcrypto.h +++ b/board/host/dcrypto.h @@ -4,7 +4,9 @@ */ /* Provides the minimal declarations needed by pinweaver to build on - * CHIP_HOST. + * CHIP_HOST. While it might be preferable to simply use the original dcrypto.h, + * That would require incorporating additional headers / dependencies such as + * cryptoc. */ #ifndef __CROS_EC_DCRYPTO_HOST_H @@ -30,6 +32,9 @@ enum dcrypto_appid { /* This enum value should not exceed 7. */ }; +/* Used as a replacement for declarations in cryptoc that are used by Cr50, but + * add unnecessary complexity to the test code. + */ struct dcrypto_mock_ctx_t { struct HASH_CTX hash; }; diff --git a/common/pinweaver.c b/common/pinweaver.c index a1743d8fdc..64bdb26de2 100644 --- a/common/pinweaver.c +++ b/common/pinweaver.c @@ -4,10 +4,12 @@ */ #include <common.h> +#include <compile_time_macros.h> #include <console.h> #include <dcrypto.h> #include <extension.h> #include <hooks.h> +#include <nvmem_vars.h> #include <pinweaver.h> #include <pinweaver_tpm_imports.h> #include <pinweaver_types.h> @@ -27,13 +29,19 @@ BUILD_ASSERT(sizeof(struct leaf_sensitive_data_t) % PW_WRAP_BLOCK_SIZE == 0); BUILD_ASSERT(sizeof(((struct merkle_tree_t *)0)->wrap_key) == AES256_BLOCK_CIPHER_KEY_SIZE); +/* Verify that the nvmem_vars log entries have the correct sizes. */ +BUILD_ASSERT(sizeof(struct pw_long_term_storage_t) + + sizeof(struct pw_log_storage_t) <= PW_MAX_VAR_USAGE); + /* Verify that the request structs will fit into the message. */ BUILD_ASSERT(PW_MAX_MESSAGE_SIZE >= sizeof(struct pw_request_header_t) + sizeof(union {struct pw_request_insert_leaf_t insert_leaf; struct pw_request_remove_leaf_t remove_leaf; struct pw_request_try_auth_t try_auth; - struct pw_request_reset_auth_t reset_auth; }) + + struct pw_request_reset_auth_t reset_auth; + struct pw_request_get_log_t get_log; + struct pw_request_log_replay_t log_replay; }) + sizeof(struct leaf_public_data_t) + sizeof(struct leaf_sensitive_data_t) + PW_MAX_PATH_SIZE); @@ -41,7 +49,8 @@ BUILD_ASSERT(PW_MAX_MESSAGE_SIZE >= #define PW_MAX_RESPONSE_SIZE (sizeof(struct pw_response_header_t) + \ sizeof(union {struct pw_response_insert_leaf_t insert_leaf; \ struct pw_response_try_auth_t try_auth; \ - struct pw_response_reset_auth_t reset_auth; }) + \ + struct pw_response_reset_auth_t reset_auth; \ + struct pw_response_log_replay_t log_replay; }) + \ PW_LEAF_PAYLOAD_SIZE) /* Verify that the request structs will fit into the message. */ BUILD_ASSERT(PW_MAX_MESSAGE_SIZE >= PW_MAX_RESPONSE_SIZE); @@ -72,6 +81,13 @@ BUILD_ASSERT(PW_MAX_PATH_SIZE == 1536); */ BUILD_ASSERT(sizeof(struct leaf_sensitive_data_t) == 3 * PW_SECRET_SIZE); +#define RESTART_TIMER_THRESHOLD (10 * SECOND) + +/* This var caches the restart count so the nvram log structure doesn't need to + * be walked every time try_auth request is made. + */ +uint32_t pw_restart_count; + /******************************************************************************/ /* Struct helper functions. */ @@ -470,7 +486,7 @@ static int validate_request_with_wrapped_leaf( static void update_timestamp(struct pw_timestamp_t *ts) { ts->timer_value = get_time().val / SECOND; - ts->boot_count = get_restart_count(); + ts->boot_count = pw_restart_count; } /* Checks if an auth attempt can be made or not based on the delay schedule. @@ -533,6 +549,260 @@ static int test_rate_limit(struct leaf_data_t *leaf_data, } /******************************************************************************/ +/* Logging implementation. + */ + +/* Once the storage version is incremented, the update code needs to be written + * to handle differences in the structs. + * + * See the two comments "Add storage format updates here." below. + */ +BUILD_ASSERT(PW_STORAGE_VERSION == 0); + +void force_restart_count(uint32_t mock_value) +{ + pw_restart_count = mock_value; +} + +/* Returns EC_SUCCESS if the root hash was found. Sets *index to the first index + * of the log entry with a matching root hash, or the index of the last valid + * entry. + */ +static int find_relevant_entry(const struct pw_log_storage_t *log, + const uint8_t root[PW_HASH_SIZE], int *index) +{ + /* Find the relevant log entry. */ + for (*index = 0; *index < PW_LOG_ENTRY_COUNT; ++*index) { + if (log->entries[*index].type.v == PW_MT_INVALID) + break; + if (memcmp(root, log->entries[*index].root, PW_HASH_SIZE) == 0) + return EC_SUCCESS; + } + --*index; + return PW_ERR_ROOT_NOT_FOUND; +} + +static int load_log_data(struct pw_log_storage_t *log) +{ + const struct tuple *ptr; + const struct pw_log_storage_t *view; + + ptr = getvar(PW_LOG_VAR0, sizeof(PW_LOG_VAR0) - 1); + if (ptr == NULL) + return PW_ERR_NV_EMPTY; + + view = (void *)tuple_val(ptr); + if (ptr->val_len != sizeof(struct pw_log_storage_t)) + return PW_ERR_NV_LENGTH_MISMATCH; + if (view->storage_version != PW_STORAGE_VERSION) + return PW_ERR_NV_VERSION_MISMATCH; + + memcpy(log, view, ptr->val_len); + return EC_SUCCESS; +} + +int store_log_data(const struct pw_log_storage_t *log) +{ + int ret; + + ret = setvar(PW_LOG_VAR0, sizeof(PW_LOG_VAR0) - 1, (uint8_t *)log, + sizeof(struct pw_log_storage_t)); + if (ret != EC_SUCCESS) + return ret; + + return writevars(); +} + +static int load_merkle_tree(struct merkle_tree_t *merkle_tree) +{ + int ret; + const struct tuple *ptr; + + cprints(CC_TASK, "PinWeaver: Loading Tree!"); + + /* Handle the immutable data. */ + { + const struct pw_long_term_storage_t *tree; + + ptr = getvar(PW_TREE_VAR, sizeof(PW_TREE_VAR) - 1); + if (ptr == NULL) + return PW_ERR_NV_EMPTY; + + tree = (void *)tuple_val(ptr); + /* Add storage format updates here. */ + if (ptr->val_len != sizeof(*tree)) + return PW_ERR_NV_LENGTH_MISMATCH; + if (tree->storage_version != PW_STORAGE_VERSION) + return PW_ERR_NV_VERSION_MISMATCH; + + merkle_tree->bits_per_level = tree->bits_per_level; + merkle_tree->height = tree->height; + memcpy(merkle_tree->key_derivation_nonce, + tree->key_derivation_nonce, + sizeof(tree->key_derivation_nonce)); + ret = derive_keys(merkle_tree); + if (ret != EC_SUCCESS) + return ret; + } + + /* Handle the root hash. */ + { + struct pw_log_storage_t *log; + + ptr = getvar(PW_LOG_VAR0, sizeof(PW_LOG_VAR0) - 1); + if (ptr == NULL) + return PW_ERR_NV_EMPTY; + + log = (void *)tuple_val(ptr); + /* Add storage format updates here. */ + if (ptr->val_len != sizeof(struct pw_log_storage_t)) + return PW_ERR_NV_LENGTH_MISMATCH; + if (log->storage_version != PW_STORAGE_VERSION) + return PW_ERR_NV_VERSION_MISMATCH; + + memcpy(merkle_tree->root, log->entries[0].root, + sizeof(merkle_tree->root)); + + /* This forces an NVRAM write for hard reboots for which the + * timer value gets reset. The TPM restart and reset counters + * were not used because they do not track the state of the + * counter. + * + * Pinweaver uses the restart_count to know when the time since + * boot can be used as the elapsed time for the delay schedule, + * versus when the elapsed time starts from a timestamp. + */ + if (get_time().val < RESTART_TIMER_THRESHOLD) { + ++log->restart_count; + ret = setvar(PW_LOG_VAR0, sizeof(PW_LOG_VAR0) - 1, + (uint8_t *)log, + sizeof(struct pw_log_storage_t)); + if (ret != EC_SUCCESS) + return ret; + ret = writevars(); + if (ret != EC_SUCCESS) + return ret; + } + pw_restart_count = log->restart_count; + } + + cprints(CC_TASK, "PinWeaver: Loaded Tree. restart_count = %d", + pw_restart_count); + + return EC_SUCCESS; +} + +/* This should only be called when a new tree is created. */ +int store_merkle_tree(const struct merkle_tree_t *merkle_tree) +{ + int ret; + + /* Handle the immutable data. */ + { + struct pw_long_term_storage_t data; + + data.storage_version = PW_STORAGE_VERSION; + data.bits_per_level = merkle_tree->bits_per_level; + data.height = merkle_tree->height; + memcpy(data.key_derivation_nonce, + merkle_tree->key_derivation_nonce, + sizeof(data.key_derivation_nonce)); + + ret = setvar(PW_TREE_VAR, sizeof(PW_TREE_VAR) - 1, + (uint8_t *)&data, sizeof(data)); + if (ret != EC_SUCCESS) + return ret; + } + + /* Handle the root hash. */ + { + struct pw_log_storage_t log = {}; + struct pw_get_log_entry_t *entry = log.entries; + + log.storage_version = PW_STORAGE_VERSION; + entry->type.v = PW_RESET_TREE; + memcpy(entry->root, merkle_tree->root, + sizeof(merkle_tree->root)); + + ret = store_log_data(&log); + if (ret == EC_SUCCESS) + pw_restart_count = 0; + return ret; + } + +} + +static int log_roll_for_append(struct pw_log_storage_t *log) +{ + int ret; + + ret = load_log_data(log); + if (ret != EC_SUCCESS) + return ret; + + memmove(&log->entries[1], &log->entries[0], + sizeof(log->entries[0]) * (PW_LOG_ENTRY_COUNT - 1)); + memset(&log->entries[0], 0, sizeof(log->entries[0])); + return EC_SUCCESS; +} + +int log_insert_leaf(struct label_t label, const uint8_t root[PW_HASH_SIZE], + const uint8_t hmac[PW_HASH_SIZE]) +{ + int ret; + struct pw_log_storage_t log; + struct pw_get_log_entry_t *entry = log.entries; + + ret = log_roll_for_append(&log); + if (ret != EC_SUCCESS) + return ret; + + entry->type.v = PW_INSERT_LEAF; + entry->label.v = label.v; + memcpy(entry->root, root, sizeof(entry->root)); + memcpy(entry->leaf_hmac, hmac, sizeof(entry->leaf_hmac)); + + return store_log_data(&log); +} + +int log_remove_leaf(struct label_t label, const uint8_t root[PW_HASH_SIZE]) +{ + int ret; + struct pw_log_storage_t log; + struct pw_get_log_entry_t *entry = log.entries; + + ret = log_roll_for_append(&log); + if (ret != EC_SUCCESS) + return ret; + + entry->type.v = PW_REMOVE_LEAF; + entry->label.v = label.v; + memcpy(entry->root, root, sizeof(entry->root)); + + return store_log_data(&log); +} + +int log_auth(struct label_t label, const uint8_t root[PW_HASH_SIZE], int code, + struct pw_timestamp_t timestamp) +{ + int ret; + struct pw_log_storage_t log; + struct pw_get_log_entry_t *entry = log.entries; + + ret = log_roll_for_append(&log); + if (ret != EC_SUCCESS) + return ret; + + entry->type.v = PW_TRY_AUTH; + entry->label.v = label.v; + memcpy(entry->root, root, sizeof(entry->root)); + entry->return_code = code; + memcpy(&entry->timestamp, ×tamp, sizeof(entry->timestamp)); + + return store_log_data(&log); +} + +/******************************************************************************/ /* Per-request-type handler implementations. */ @@ -556,6 +826,10 @@ static int pw_handle_reset_tree(struct merkle_tree_t *merkle_tree, if (ret != EC_SUCCESS) return ret; + ret = store_merkle_tree(&new_tree); + if (ret != EC_SUCCESS) + return ret; + memcpy(merkle_tree, &new_tree, sizeof(new_tree)); return EC_SUCCESS; } @@ -603,6 +877,11 @@ static int pw_handle_insert_leaf(struct merkle_tree_t *merkle_tree, if (ret != EC_SUCCESS) return ret; + ret = log_insert_leaf(request->label, new_root, + wrapped_leaf_data.hmac); + if (ret != EC_SUCCESS) + return ret; + memcpy(merkle_tree->root, new_root, sizeof(new_root)); memcpy(&response->unimported_leaf_data, &wrapped_leaf_data, @@ -635,8 +914,11 @@ static int pw_handle_remove_leaf(struct merkle_tree_t *merkle_tree, compute_root_hash(merkle_tree, request->leaf_location, request->path_hashes, empty_hash, new_root); - memcpy(merkle_tree->root, new_root, sizeof(new_root)); + ret = log_remove_leaf(request->leaf_location, new_root); + if (ret != EC_SUCCESS) + return ret; + memcpy(merkle_tree->root, new_root, sizeof(new_root)); return ret; } @@ -721,6 +1003,17 @@ static int pw_handle_try_auth(struct merkle_tree_t *merkle_tree, if (ret != EC_SUCCESS) return ret; + ret = log_auth(wrapped_leaf_data.pub.label, new_root, + results_table[auth_result].ret, leaf_data.pub.timestamp); + if (ret != EC_SUCCESS) { + memcpy(new_root, merkle_tree->root, sizeof(merkle_tree->root)); + return ret; + } + /**********************************************************************/ + /* At this point the log should be written so it should be safe for the + * runtime of the code paths to diverge. + */ + memcpy(merkle_tree->root, new_root, sizeof(new_root)); *data_length = sizeof(*response) + PW_LEAF_PAYLOAD_SIZE; @@ -776,6 +1069,11 @@ static int pw_handle_reset_auth(struct merkle_tree_t *merkle_tree, if (ret != EC_SUCCESS) return ret; + ret = log_auth(leaf_data.pub.label, new_root, ret, + leaf_data.pub.timestamp); + if (ret != EC_SUCCESS) + return ret; + memcpy(merkle_tree->root, new_root, sizeof(new_root)); memcpy(&response->unimported_leaf_data, &wrapped_leaf_data, @@ -790,6 +1088,141 @@ static int pw_handle_reset_auth(struct merkle_tree_t *merkle_tree, return ret; } +static int pw_handle_get_log(const struct merkle_tree_t *merkle_tree, + const struct pw_request_get_log_t *request, + uint16_t req_size, + struct pw_get_log_entry_t response[], + uint16_t *response_size) +{ + int ret; + int x; + struct pw_log_storage_t log; + + if (req_size != sizeof(*request)) + return PW_ERR_LENGTH_INVALID; + + ret = validate_tree(merkle_tree); + if (ret != EC_SUCCESS) + return ret; + + ret = load_log_data(&log); + if (ret != EC_SUCCESS) + return ret; + + /* Find the relevant log entry. The return value isn't used because if + * the entry isn't found the entire log is returned. This makes it + * easier to recover when the log is too short. + * + * Here is an example: + * 50 attempts have been made against a leaf that becomes out of sync + * because of a disk flush failing. The copy of the leaf on disk is + * behind by 50 and the log contains less than 50 entries. The CrOS + * implementation can check the public parameters of the local copy with + * the log entry to determine that leaf is out of sync. It can then send + * any valid copy of that leaf with a log replay request that will only + * succeed if the HMAC of the resulting leaf matches the log entry. + */ + find_relevant_entry(&log, request->root, &x); + /* If there are no valid entries, return. */ + if (x < 0) + return EC_SUCCESS; + + /* Copy the entries in reverse order. */ + while (1) { + memcpy(&response[x], &log.entries[x], sizeof(log.entries[x])); + *response_size += sizeof(log.entries[x]); + if (x == 0) + break; + --x; + } + + return EC_SUCCESS; +} + +static int pw_handle_log_replay(const struct merkle_tree_t *merkle_tree, + const struct pw_request_log_replay_t *request, + uint16_t req_size, + struct pw_response_log_replay_t *response, + uint16_t *response_size) +{ + int ret; + int x; + struct pw_log_storage_t log; + struct leaf_data_t leaf_data = {}; + struct imported_leaf_data_t imported_leaf_data; + struct wrapped_leaf_data_t wrapped_leaf_data; + uint8_t hmac[PW_HASH_SIZE]; + uint8_t root[PW_HASH_SIZE]; + + if (req_size < sizeof(*request)) + return PW_ERR_LENGTH_INVALID; + + ret = validate_tree(merkle_tree); + if (ret != EC_SUCCESS) + return ret; + + /* validate_request_with_wrapped_leaf() isn't used here because the + * path validation is delayed to allow any valid copy of the same leaf + * to be used in the replay operation as long as the result passes path + * validation. + */ + ret = validate_leaf_header(&request->unimported_leaf_data.head, + req_size - sizeof(*request), + get_path_auxiliary_hash_count(merkle_tree)); + if (ret != EC_SUCCESS) + return ret; + + import_leaf(&request->unimported_leaf_data, &imported_leaf_data); + + ret = load_log_data(&log); + if (ret != EC_SUCCESS) + return ret; + + /* Find the relevant log entry. */ + ret = find_relevant_entry(&log, request->log_root, &x); + if (ret != EC_SUCCESS) + return ret; + + /* The other message types don't need to be handled by Cr50. */ + if (log.entries[x].type.v != PW_TRY_AUTH) + return PW_ERR_TYPE_INVALID; + + compute_hmac(merkle_tree, &imported_leaf_data, hmac); + if (safe_memcmp(hmac, request->unimported_leaf_data.hmac, sizeof(hmac))) + return PW_ERR_HMAC_AUTH_FAILED; + + ret = decrypt_leaf_data(merkle_tree, &imported_leaf_data, &leaf_data); + if (ret != EC_SUCCESS) + return ret; + + if (leaf_data.pub.label.v != log.entries[x].label.v) + return PW_ERR_LABEL_INVALID; + + /* Update the metadata to match the log. */ + if (log.entries[x].return_code == EC_SUCCESS) + leaf_data.pub.attempt_count.v = 0; + else + ++leaf_data.pub.attempt_count.v; + memcpy(&leaf_data.pub.timestamp, &log.entries[x].timestamp, + sizeof(leaf_data.pub.timestamp)); + + ret = handle_leaf_update(merkle_tree, &leaf_data, + imported_leaf_data.hashes, &wrapped_leaf_data, + root, &imported_leaf_data); + if (ret != EC_SUCCESS) + return ret; + + if (memcmp(root, log.entries[x].root, PW_HASH_SIZE)) + return PW_ERR_PATH_AUTH_FAILED; + + memcpy(&response->unimported_leaf_data, &wrapped_leaf_data, + sizeof(wrapped_leaf_data)); + + *response_size = sizeof(*response) + PW_LEAF_PAYLOAD_SIZE; + + return EC_SUCCESS; +} + struct merkle_tree_t pw_merkle_tree; /* @@ -833,16 +1266,15 @@ static enum vendor_cmd_rc pw_vendor_specific_command(enum vendor_cmd_cc code, DECLARE_VENDOR_COMMAND(VENDOR_CC_PINWEAVER, pw_vendor_specific_command); -static void pinweaver_init(void) -{ - /* TODO(allenwebb) load merkle_tree from flash here. */ -} -DECLARE_HOOK(HOOK_INIT, pinweaver_init, HOOK_PRIO_LAST); - /******************************************************************************/ /* Non-static functions. */ +void pinweaver_init(void) +{ + load_merkle_tree(&pw_merkle_tree); +} + int get_path_auxiliary_hash_count(const struct merkle_tree_t *merkle_tree) { return ((1 << merkle_tree->bits_per_level.v) - 1) * @@ -932,6 +1364,18 @@ int pw_handle_request(struct merkle_tree_t *merkle_tree, &response->data.reset_auth, &resp_length); break; + case PW_GET_LOG: + ret = pw_handle_get_log(merkle_tree, &request->data.get_log, + request->header.data_length, + (void *)&response->data, &resp_length); + break; + case PW_LOG_REPLAY: + ret = pw_handle_log_replay(merkle_tree, + &request->data.log_replay, + request->header.data_length, + &response->data.log_replay, + &resp_length); + break; default: ret = PW_ERR_TYPE_INVALID; break; diff --git a/include/pinweaver.h b/include/pinweaver.h index 58210fa037..7c7a8fb1cb 100644 --- a/include/pinweaver.h +++ b/include/pinweaver.h @@ -23,6 +23,8 @@ */ #define HEIGHT_MAX(logk) ((sizeof(struct label_t) * 8) / logk) +#define PW_LOG_ENTRY_COUNT 2 + /* Persistent information used by this feature. */ struct merkle_tree_t { /* log2(Fan out). */ @@ -43,8 +45,28 @@ struct merkle_tree_t { uint8_t PW_ALIGN_TO_WRD wrap_key[32]; }; +/* Long term flash storage for tree metadata. */ +struct PW_PACKED pw_long_term_storage_t { + uint16_t storage_version; + + /* log2(Fan out). */ + struct bits_per_level_t bits_per_level; + /* Height of the tree or param_l / bits_per_level. */ + struct height_t height; + + /* Random bits used as part of the key derivation process. */ + uint8_t key_derivation_nonce[16]; +}; + +struct PW_PACKED pw_log_storage_t { + uint16_t storage_version; + uint32_t restart_count; + struct pw_get_log_entry_t entries[PW_LOG_ENTRY_COUNT]; +}; + /* Do not remove fields within the same PW_LEAF_MAJOR_VERSION. */ -/* Unencrypted part of the leaf data. */ +/* Unencrypted part of the leaf data. + */ struct PW_PACKED leaf_public_data_t { struct label_t label; struct delay_schedule_entry_t delay_schedule[PW_SCHED_COUNT]; @@ -55,7 +77,8 @@ struct PW_PACKED leaf_public_data_t { }; /* Do not remove fields within the same PW_LEAF_MAJOR_VERSION. */ -/* Encrypted part of the leaf data. */ +/* Encrypted part of the leaf data. + */ struct PW_PACKED PW_ALIGN_TO_BLK leaf_sensitive_data_t { uint8_t low_entropy_secret[PW_SECRET_SIZE]; uint8_t high_entropy_secret[PW_SECRET_SIZE]; @@ -99,6 +122,21 @@ struct leaf_data_t { struct leaf_sensitive_data_t sec; }; +/* Key names for nvmem_vars */ +#define PW_TREE_VAR "pwT0" +#define PW_LOG_VAR0 "pwL0" +/* The maximum key-value pair space allowed for the values of PinWeaver until + * the Cr50 NVRAM implementation is updated to use a separate object per + * key value pair. + */ +#define PW_MAX_VAR_USAGE 192 + +/* Initializes the PinWeaver feature. + * + * This needs to be called prior to handling any messages. + */ +void pinweaver_init(void); + /* Handler for incoming messages after they have been reconstructed. * * merkle_tree->root needs to be updated with new_root outside of this function. @@ -139,4 +177,16 @@ void compute_hash(const uint8_t hashes[][PW_HASH_SIZE], uint16_t num_hashes, const uint8_t child_hash[PW_HASH_SIZE], uint8_t result[PW_HASH_SIZE]); +/* This should only be used in tests. */ +void force_restart_count(uint32_t mock_value); + +/* NV RAM log functions exported for use in test code. */ +int store_log_data(const struct pw_log_storage_t *log); +int store_merkle_tree(const struct merkle_tree_t *merkle_tree); +int log_insert_leaf(struct label_t label, const uint8_t root[PW_HASH_SIZE], + const uint8_t hmac[PW_HASH_SIZE]); +int log_remove_leaf(struct label_t label, const uint8_t root[PW_HASH_SIZE]); +int log_auth(struct label_t label, const uint8_t root[PW_HASH_SIZE], int code, + struct pw_timestamp_t timestamp); + #endif /* __CROS_EC_INCLUDE_PINWEAVER_H */ diff --git a/include/pinweaver_tpm_imports.h b/include/pinweaver_tpm_imports.h index be882593d6..7dad418910 100644 --- a/include/pinweaver_tpm_imports.h +++ b/include/pinweaver_tpm_imports.h @@ -16,8 +16,6 @@ #include <stddef.h> #include <stdint.h> -uint32_t get_restart_count(void); - /* This is used to get the storage seed from the TPM implementation so * TPM_Clear() will break the keys used by PinWeaver so that any metadata * that persists on the machine storage is unusable by attackers. diff --git a/include/pinweaver_types.h b/include/pinweaver_types.h index 9fbb801e81..4200082696 100644 --- a/include/pinweaver_types.h +++ b/include/pinweaver_types.h @@ -41,6 +41,10 @@ enum pw_error_codes_enum { PW_ERR_RESET_AUTH_FAILED, PW_ERR_CRYPTO_FAILURE, PW_ERR_RATE_LIMIT_REACHED, + PW_ERR_ROOT_NOT_FOUND, + PW_ERR_NV_EMPTY, + PW_ERR_NV_LENGTH_MISMATCH, + PW_ERR_NV_VERSION_MISMATCH, }; /* Represents the log2(fan out) of a tree. */ @@ -179,6 +183,8 @@ enum pw_message_type_enum { PW_REMOVE_LEAF, PW_TRY_AUTH, PW_RESET_AUTH, + PW_GET_LOG, + PW_LOG_REPLAY, }; struct PW_PACKED pw_message_type_t { @@ -255,6 +261,53 @@ struct PW_PACKED pw_response_reset_auth_t { struct unimported_leaf_data_t unimported_leaf_data; }; +struct PW_PACKED pw_request_get_log_t { + /* The root on the CrOS side that needs to be brought back in sync with + * the root on Cr50. If this doesn't match a log entry, the entire log + * is returned. + */ + uint8_t root[PW_HASH_SIZE]; +}; + +struct PW_PACKED pw_request_log_replay_t { + /* The root hash after the desired log event. + * The log entry that matches this hash contains all the necessary + * data to update wrapped_leaf_data + */ + uint8_t log_root[PW_HASH_SIZE]; + struct unimported_leaf_data_t unimported_leaf_data; +}; + +struct PW_PACKED pw_response_log_replay_t { + struct unimported_leaf_data_t unimported_leaf_data; +}; + +struct PW_PACKED pw_get_log_entry_t { + /* The root hash after this operation. */ + uint8_t root[PW_HASH_SIZE]; + /* The label of the leaf that was operated on. */ + struct label_t label; + /* The type of operation. This should be one of + * PW_INSERT_LEAF, + * PW_REMOVE_LEAF, + * PW_TRY_AUTH. + * + * Successful PW_RESET_AUTH events are included + */ + struct pw_message_type_t type; + /* Type specific fields. */ + union { + /* PW_INSERT_LEAF */ + uint8_t leaf_hmac[PW_HASH_SIZE]; + /* PW_REMOVE_LEAF */ + /* PW_TRY_AUTH */ + struct PW_PACKED { + struct pw_timestamp_t timestamp; + int32_t return_code; + }; + }; +}; + struct PW_PACKED pw_request_t { struct pw_request_header_t header; union { @@ -263,6 +316,8 @@ struct PW_PACKED pw_request_t { struct pw_request_remove_leaf_t remove_leaf; struct pw_request_try_auth_t try_auth; struct pw_request_reset_auth_t reset_auth; + struct pw_request_get_log_t get_log; + struct pw_request_log_replay_t log_replay; } data; }; @@ -273,6 +328,11 @@ struct PW_PACKED pw_response_t { struct pw_response_insert_leaf_t insert_leaf; struct pw_response_try_auth_t try_auth; struct pw_response_reset_auth_t reset_auth; + /* An array with as many entries as are present in the log up to + * the present time or will fit in the message. + */ + uint8_t get_log[0]; + struct pw_response_log_replay_t log_replay; } data; }; diff --git a/test/pinweaver.c b/test/pinweaver.c index 16c40ec9aa..2f105c7b0a 100644 --- a/test/pinweaver.c +++ b/test/pinweaver.c @@ -6,6 +6,7 @@ #include <pinweaver.h> #include <dcrypto.h> +#include <nvmem_vars.h> #include <sha256.h> #include <stdint.h> #include <string.h> @@ -123,11 +124,32 @@ const uint8_t ROOT_WITH_DEFAULT_HMAC[] = { 0x37, 0xc2, 0xf2, 0x72, 0x31, 0xdd, 0xc4, 0xaf, }; +/* This is not the actual hmac. */ +const uint8_t OTHER_HMAC[] = { + 0xec, 0x64, 0x73, 0x39, 0xcf, 0x53, 0xb7, 0x08, + 0x85, 0x8f, 0xb6, 0x20, 0x25, 0x98, 0x59, 0x97, + 0x58, 0x8c, 0x7a, 0x80, 0x10, 0xb4, 0xc1, 0xc8, + 0x8a, 0xdf, 0xe3, 0x69, 0x07, 0xd1, 0xc4, 0xdc, +}; + +const uint8_t ROOT_WITH_OTHER_HMAC[] = { + 0xdf, 0xce, 0xf4, 0xba, 0x18, 0xe8, 0xd0, 0x1d, + 0xcb, 0x3b, 0x29, 0x41, 0x44, 0x01, 0x6e, 0x72, + 0xe3, 0x19, 0x9a, 0x44, 0x62, 0x44, 0x2a, 0xf1, + 0xaf, 0x66, 0xb6, 0xf0, 0x61, 0x05, 0x9d, 0xc0, +}; + /******************************************************************************/ /* Config Variables and defines for Mocks. */ -uint32_t MOCK_restart_count; +struct tuple MOCK_pw_tuple; +struct pw_long_term_storage_t MOCK_pw_long_term_storage; +struct pw_log_storage_t MOCK_pw_log_storage; +int MOCK_getvar_ret = EC_SUCCESS; +int MOCK_setvar_ret = EC_SUCCESS; +int MOCK_writevars_ret = EC_SUCCESS; +void *MOCK_tuple_val_ret; const uint8_t *MOCK_rand_bytes_src; size_t MOCK_rand_bytes_offset; @@ -198,6 +220,14 @@ static const char *pw_error_str(int code) return "PW_ERR_CRYPTO_FAILURE"; case PW_ERR_RATE_LIMIT_REACHED: return "PW_ERR_RATE_LIMIT_REACHED"; + case PW_ERR_ROOT_NOT_FOUND: + return "PW_ERR_ROOT_NOT_FOUND"; + case PW_ERR_NV_EMPTY: + return "PW_ERR_NV_EMPTY"; + case PW_ERR_NV_LENGTH_MISMATCH: + return "PW_ERR_NV_LENGTH_MISMATCH"; + case PW_ERR_NV_VERSION_MISMATCH: + return "PW_ERR_NV_VERSION_MISMATCH"; default: return "?"; } @@ -262,6 +292,64 @@ void print_hex(const uint8_t *data, size_t n) ccprintf("%02x ", data[x]); } +/* Initialize the log. + * For num_operations: + * < 0 only zero out the storage. + * == 0 only initialize the tree + * > 0 cyclically applies operations in the following order: + * insert + * auth failed + * auth success + * remove + * So for num_operations == 4 the complete set of operations will be written to + * the log. + */ +static void setup_storage(int num_operations) +{ + MOCK_getvar_ret = EC_SUCCESS; + MOCK_setvar_ret = EC_SUCCESS; + MOCK_writevars_ret = EC_SUCCESS; + + memset(&MOCK_pw_long_term_storage, 0, + sizeof(MOCK_pw_long_term_storage)); + memset(&MOCK_pw_log_storage, 0, sizeof(MOCK_pw_log_storage)); + + if (num_operations < 0) + return; + --num_operations; + + store_merkle_tree(&EMPTY_TREE); + + while (num_operations > 0) { + --num_operations; + + log_insert_leaf(DEFAULT_LEAF.pub.label, ROOT_WITH_DEFAULT_HMAC, + DEFAULT_HMAC); + + if (num_operations < 0) + return; + --num_operations; + + log_auth(DEFAULT_LEAF.pub.label, ROOT_WITH_OTHER_HMAC, + PW_ERR_LOWENT_AUTH_FAILED, + (struct pw_timestamp_t) {7, 99}); + + if (num_operations < 0) + return; + --num_operations; + + log_auth(DEFAULT_LEAF.pub.label, ROOT_WITH_DEFAULT_HMAC, + EC_SUCCESS, + (struct pw_timestamp_t) {10, 100}); + + if (num_operations < 0) + return; + --num_operations; + + log_remove_leaf(DEFAULT_LEAF.pub.label, EMPTY_TREE.root); + } +} + static void setup_default_empty_path(uint8_t hashes[][PW_HASH_SIZE]) { uint8_t num_siblings = (1 << EMPTY_TREE.bits_per_level.v) - 1; @@ -331,6 +419,9 @@ static void setup_reset_tree_defaults(struct merkle_tree_t *merkle_tree, MOCK_DECRYPTO_release_counter = 0; memset(merkle_tree, 0, sizeof(*merkle_tree)); + memset(&MOCK_pw_long_term_storage, 0, + sizeof(MOCK_pw_long_term_storage)); + memset(&MOCK_pw_log_storage, 0, sizeof(MOCK_pw_log_storage)); request->header.version = PW_PROTOCOL_VERSION; request->header.type.v = PW_RESET_TREE; @@ -343,6 +434,8 @@ static void setup_reset_tree_defaults(struct merkle_tree_t *merkle_tree, MOCK_rand_bytes_offset = 0; MOCK_rand_bytes_len = sizeof(EMPTY_TREE.key_derivation_nonce); MOCK_appkey_derive_fail = EC_SUCCESS; + MOCK_setvar_ret = EC_SUCCESS; + MOCK_writevars_ret = EC_SUCCESS; } static void setup_insert_leaf_defaults(struct merkle_tree_t *merkle_tree, @@ -352,6 +445,7 @@ static void setup_insert_leaf_defaults(struct merkle_tree_t *merkle_tree, MOCK_DECRYPTO_release_counter = 0; memcpy(merkle_tree, &EMPTY_TREE, sizeof(EMPTY_TREE)); + memset(&MOCK_pw_log_storage, 0, sizeof(MOCK_pw_log_storage)); request->header.version = PW_PROTOCOL_VERSION; request->header.type.v = PW_INSERT_LEAF; @@ -380,6 +474,8 @@ static void setup_insert_leaf_defaults(struct merkle_tree_t *merkle_tree, MOCK_hash_update_cb = 0; MOCK_hmac = DEFAULT_HMAC; MOCK_aes_fail = 0; + MOCK_setvar_ret = EC_SUCCESS; + MOCK_writevars_ret = EC_SUCCESS; } static void setup_remove_leaf_defaults(struct merkle_tree_t *merkle_tree, @@ -391,6 +487,7 @@ static void setup_remove_leaf_defaults(struct merkle_tree_t *merkle_tree, memcpy(merkle_tree, &EMPTY_TREE, sizeof(EMPTY_TREE)); memcpy(merkle_tree->root, ROOT_WITH_DEFAULT_HMAC, sizeof(ROOT_WITH_DEFAULT_HMAC)); + memset(&MOCK_pw_log_storage, 0, sizeof(MOCK_pw_log_storage)); request->header.version = PW_PROTOCOL_VERSION; request->header.type.v = PW_REMOVE_LEAF; @@ -403,6 +500,9 @@ static void setup_remove_leaf_defaults(struct merkle_tree_t *merkle_tree, memcpy(request->data.remove_leaf.leaf_hmac, DEFAULT_HMAC, sizeof(request->data.remove_leaf.leaf_hmac)); setup_default_empty_path(request->data.remove_leaf.path_hashes); + + MOCK_setvar_ret = EC_SUCCESS; + MOCK_writevars_ret = EC_SUCCESS; } static void setup_try_auth_defaults_with_leaf( @@ -425,6 +525,8 @@ static void setup_try_auth_defaults_with_leaf( /* Gets overwritten by auth_hash_update_cb. */ MOCK_hmac = EMPTY_HMAC; + memset(&MOCK_pw_log_storage, 0, sizeof(MOCK_pw_log_storage)); + request->header.version = PW_PROTOCOL_VERSION; request->header.type.v = PW_TRY_AUTH; request->header.data_length = @@ -440,13 +542,15 @@ static void setup_try_auth_defaults_with_leaf( leaf_data, MOCK_hmac, &request->data.try_auth.unimported_leaf_data); - MOCK_restart_count = 0; + force_restart_count(0); force_time((timestamp_t){.val = 0}); MOCK_rand_bytes_src = DEFAULT_IV; MOCK_rand_bytes_offset = 0; MOCK_rand_bytes_len = sizeof(DEFAULT_IV); MOCK_hash_update_cb = auth_hash_update_cb; MOCK_aes_fail = 0; + MOCK_setvar_ret = EC_SUCCESS; + MOCK_writevars_ret = EC_SUCCESS; } static void setup_try_auth_defaults(struct merkle_tree_t *merkle_tree, @@ -465,6 +569,7 @@ static void setup_reset_auth_defaults(struct merkle_tree_t *merkle_tree, MOCK_DECRYPTO_init_counter = 0; MOCK_DECRYPTO_release_counter = 0; memcpy(merkle_tree, &EMPTY_TREE, sizeof(EMPTY_TREE)); + memset(&MOCK_pw_log_storage, 0, sizeof(MOCK_pw_log_storage)); request->header.version = PW_PROTOCOL_VERSION; request->header.type.v = PW_RESET_AUTH; @@ -489,6 +594,71 @@ static void setup_reset_auth_defaults(struct merkle_tree_t *merkle_tree, MOCK_hash_update_cb = auth_hash_update_cb; MOCK_hmac = EMPTY_HMAC; /* Gets overwritten by auth_hash_update_cb. */ MOCK_aes_fail = 0; + MOCK_setvar_ret = EC_SUCCESS; + MOCK_writevars_ret = EC_SUCCESS; +} + +static void setup_get_log_defaults(struct merkle_tree_t *merkle_tree, + struct pw_request_t *request) +{ + MOCK_DECRYPTO_init_counter = 0; + MOCK_DECRYPTO_release_counter = 0; + + memcpy(merkle_tree, &EMPTY_TREE, sizeof(*merkle_tree)); + + request->header.version = PW_PROTOCOL_VERSION; + request->header.type.v = PW_GET_LOG; + request->header.data_length = sizeof(struct pw_request_get_log_t); + + /* Chosen not to match any of the root hashes in the log. */ + memcpy(request->data.get_log.root, OTHER_HMAC, + sizeof(OTHER_HMAC)); + + setup_storage(1); +} + +static void setup_log_replay_defaults_with_leaf( + const struct leaf_data_t *leaf_data, + struct merkle_tree_t *merkle_tree, + struct pw_request_t *request) +{ + MOCK_DECRYPTO_init_counter = 0; + MOCK_DECRYPTO_release_counter = 0; + + memcpy(merkle_tree, &EMPTY_TREE, sizeof(*merkle_tree)); + if (leaf_data->pub.attempt_count.v != 6 && + leaf_data->pub.attempt_count.v != 10) + /* Gets overwritten by auth_hash_update_cb. */ + MOCK_hmac = DEFAULT_HMAC; + else + /* Gets overwritten by auth_hash_update_cb. */ + MOCK_hmac = EMPTY_HMAC; + + request->header.version = PW_PROTOCOL_VERSION; + request->header.type.v = PW_LOG_REPLAY; + request->header.data_length = + sizeof(struct pw_request_log_replay_t) + + PW_LEAF_PAYLOAD_SIZE + + get_path_auxiliary_hash_count(&EMPTY_TREE) * + PW_HASH_SIZE; + + memcpy(request->data.log_replay.log_root, ROOT_WITH_DEFAULT_HMAC, + sizeof(ROOT_WITH_DEFAULT_HMAC)); + + setup_default_unimported_leaf_data_and_hashes( + leaf_data, MOCK_hmac, + &request->data.try_auth.unimported_leaf_data); + + MOCK_hash_update_cb = auth_hash_update_cb; + + setup_storage(4); +} + +static void setup_log_replay_defaults(struct merkle_tree_t *merkle_tree, + struct pw_request_t *request) +{ + setup_log_replay_defaults_with_leaf(&DEFAULT_LEAF, merkle_tree, + request); } /* Increases the length of the pub and cipher_text by 4 each. */ @@ -550,6 +720,9 @@ static void auth_hash_update_cb(const void *data, size_t len) case 6: MOCK_hmac = EMPTY_HMAC; break; + case 16: + MOCK_hmac = OTHER_HMAC; + break; default: MOCK_hmac = DEFAULT_HMAC; break; @@ -557,14 +730,9 @@ static void auth_hash_update_cb(const void *data, size_t len) } /******************************************************************************/ -/* Mock implementations of TPM, TRNG, and Dcrypto functionality. +/* Mock implementations of TPM functionality. */ -uint32_t get_restart_count(void) -{ - return MOCK_restart_count; -} - void get_storage_seed(void *buf, size_t *len) { *len = *len < sizeof(DEFAULT_STORAGE_SEED) ? *len : @@ -572,6 +740,65 @@ void get_storage_seed(void *buf, size_t *len) memcpy(buf, DEFAULT_STORAGE_SEED, *len); } +/******************************************************************************/ +/* Mock implementations of nvmem_vars functionality. + */ +const struct tuple *getvar(const uint8_t *key, uint8_t key_len) +{ + if (MOCK_getvar_ret != EC_SUCCESS) + return NULL; + + MOCK_pw_tuple.flags = 0; + MOCK_pw_tuple.key_len = key_len; + + if (key_len == (sizeof(PW_TREE_VAR) - 1) && + memcmp(key, PW_TREE_VAR, (sizeof(PW_TREE_VAR) - 1)) == 0) { + MOCK_pw_tuple.val_len = sizeof(MOCK_pw_long_term_storage); + MOCK_tuple_val_ret = &MOCK_pw_long_term_storage; + return &MOCK_pw_tuple; + } else if (key_len == (sizeof(PW_LOG_VAR0) - 1) && + memcmp(key, PW_LOG_VAR0, (sizeof(PW_LOG_VAR0) - 1)) == 0) { + MOCK_pw_tuple.val_len = sizeof(struct pw_log_storage_t); + MOCK_tuple_val_ret = &MOCK_pw_log_storage; + return &MOCK_pw_tuple; + } else + return NULL; +} + +const uint8_t *tuple_val(const struct tuple *tpl) +{ + return MOCK_tuple_val_ret; +} + +int setvar(const uint8_t *key, uint8_t key_len, + const uint8_t *val, uint8_t val_len) +{ + if (MOCK_setvar_ret != EC_SUCCESS) + return MOCK_setvar_ret; + + if (key_len == (sizeof(PW_TREE_VAR) - 1) && + memcmp(key, PW_TREE_VAR, (sizeof(PW_TREE_VAR) - 1)) == 0) { + TEST_ASSERT(val_len == sizeof(MOCK_pw_long_term_storage)); + memcpy(&MOCK_pw_long_term_storage, val, val_len); + return EC_SUCCESS; + } else if (key_len == (sizeof(PW_LOG_VAR0) - 1) && + memcmp(key, PW_LOG_VAR0, (sizeof(PW_LOG_VAR0) - 1)) == 0) { + TEST_ASSERT(val_len == sizeof(struct pw_log_storage_t)); + memcpy(&MOCK_pw_log_storage, val, val_len); + return EC_SUCCESS; + } else + return EC_ERROR_UNKNOWN; +} + +int writevars(void) +{ + return MOCK_writevars_ret; +} + +/******************************************************************************/ +/* Mock implementations of TRNG functionality. + */ + void rand_bytes(void *buffer, size_t len) { if (!MOCK_rand_bytes_src) @@ -585,6 +812,10 @@ void rand_bytes(void *buffer, size_t len) MOCK_rand_bytes_offset = 0; } +/******************************************************************************/ +/* Mock implementations of Dcrypto functionality. + */ + void HASH_update(struct HASH_CTX *ctx, const void *data, size_t len) { if (MOCK_hash_update_cb) @@ -919,6 +1150,20 @@ static int handle_reset_tree_crypto_failure(void) return check_dcrypto_mutex_usage(); } +static int handle_reset_tree_nv_fail(void) +{ + struct merkle_tree_t merkle_tree; + struct pw_test_data_t buf; + + setup_reset_tree_defaults(&merkle_tree, &buf.request); + + MOCK_writevars_ret = PW_ERR_NV_LENGTH_MISMATCH; + + TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, merkle_tree.root), + PW_ERR_NV_LENGTH_MISMATCH); + return EC_SUCCESS; +} + static int handle_reset_tree_success(void) { struct merkle_tree_t merkle_tree; @@ -932,6 +1177,21 @@ static int handle_reset_tree_success(void) TEST_ASSERT_ARRAY_EQ((uint8_t *)&merkle_tree, (uint8_t *)&EMPTY_TREE, sizeof(EMPTY_TREE)); + TEST_ASSERT(MOCK_pw_long_term_storage.storage_version == + PW_STORAGE_VERSION); + TEST_ASSERT(MOCK_pw_long_term_storage.bits_per_level.v == + EMPTY_TREE.bits_per_level.v); + TEST_ASSERT(MOCK_pw_long_term_storage.height.v == + EMPTY_TREE.height.v); + TEST_ASSERT_ARRAY_EQ(MOCK_pw_long_term_storage.key_derivation_nonce, + EMPTY_TREE.key_derivation_nonce, + sizeof(EMPTY_TREE.key_derivation_nonce)); + + TEST_ASSERT(MOCK_pw_log_storage.storage_version == PW_STORAGE_VERSION); + TEST_ASSERT(MOCK_pw_log_storage.entries[0].type.v == PW_RESET_TREE); + TEST_ASSERT_ARRAY_EQ(MOCK_pw_log_storage.entries[0].root, + EMPTY_TREE.root, sizeof(EMPTY_TREE.root)); + return check_dcrypto_mutex_usage(); } @@ -1040,6 +1300,20 @@ static int handle_insert_leaf_crypto_failure(void) return check_dcrypto_mutex_usage(); } +static int handle_insert_leaf_nv_fail(void) +{ + struct merkle_tree_t merkle_tree; + struct pw_test_data_t buf; + + setup_insert_leaf_defaults(&merkle_tree, &buf.request); + + MOCK_writevars_ret = PW_ERR_NV_LENGTH_MISMATCH; + + TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, merkle_tree.root), + PW_ERR_NV_LENGTH_MISMATCH); + return EC_SUCCESS; +} + static int handle_insert_leaf_success(void) { struct merkle_tree_t merkle_tree; @@ -1075,6 +1349,16 @@ static int handle_insert_leaf_success(void) (wrapped_leaf_data->cipher_text[x] ^ MOCK_AES_XOR_BYTE(x))); + TEST_ASSERT(MOCK_pw_log_storage.entries[0].type.v == PW_INSERT_LEAF); + TEST_ASSERT(MOCK_pw_log_storage.entries[0].label.v == + DEFAULT_LEAF.pub.label.v); + TEST_ASSERT_ARRAY_EQ(MOCK_pw_log_storage.entries[0].root, + ROOT_WITH_DEFAULT_HMAC, + sizeof(ROOT_WITH_DEFAULT_HMAC)); + TEST_ASSERT_ARRAY_EQ(MOCK_pw_log_storage.entries[0].leaf_hmac, + DEFAULT_HMAC, + sizeof(DEFAULT_HMAC)); + return check_dcrypto_mutex_usage(); } @@ -1127,6 +1411,20 @@ static int handle_remove_leaf_path_auth_failed(void) return check_dcrypto_mutex_usage(); } +static int handle_remove_leaf_nv_fail(void) +{ + struct merkle_tree_t merkle_tree; + struct pw_test_data_t buf; + + setup_remove_leaf_defaults(&merkle_tree, &buf.request); + + MOCK_writevars_ret = PW_ERR_NV_LENGTH_MISMATCH; + + TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, merkle_tree.root), + PW_ERR_NV_LENGTH_MISMATCH); + return EC_SUCCESS; +} + static int handle_remove_leaf_success(void) { struct merkle_tree_t merkle_tree; @@ -1137,6 +1435,12 @@ static int handle_remove_leaf_success(void) TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, EMPTY_TREE.root), EC_SUCCESS); + TEST_ASSERT(MOCK_pw_log_storage.entries[0].type.v == + PW_REMOVE_LEAF); + TEST_ASSERT(MOCK_pw_log_storage.entries[0].label.v == + DEFAULT_LEAF.pub.label.v); + TEST_ASSERT_ARRAY_EQ(MOCK_pw_log_storage.entries[0].root, + EMPTY_TREE.root, sizeof(EMPTY_TREE.root)); return check_dcrypto_mutex_usage(); } @@ -1279,7 +1583,7 @@ static int handle_try_auth_rate_limit_reached(void) /* Test PW_BLOCK_ATTEMPTS. */ memcpy(&leaf_data, &DEFAULT_LEAF, sizeof(DEFAULT_LEAF)); leaf_data.pub.attempt_count.v = 51; - MOCK_restart_count = 1; + force_restart_count(1); force_time((timestamp_t){.val = 7200llu * SECOND}); setup_try_auth_defaults_with_leaf(&leaf_data, &merkle_tree, &buf.request); @@ -1295,7 +1599,23 @@ static int handle_try_auth_rate_limit_reached(void) leaf_data.pub.delay_schedule[0].attempt_count.v = 5; leaf_data.pub.delay_schedule[0].time_diff.v = PW_BLOCK_ATTEMPTS; leaf_data.pub.attempt_count.v = 6; - MOCK_restart_count = 1; + force_restart_count(1); + force_time((timestamp_t){.val = 7200llu * SECOND}); + setup_try_auth_defaults_with_leaf(&leaf_data, &merkle_tree, + &buf.request); + + TEST_RET_EQ(check_try_auth_rate_limit_reached_response( + &merkle_tree, &buf, + (const struct time_diff_t){PW_BLOCK_ATTEMPTS}), + EC_SUCCESS); + + memcpy(&leaf_data, &DEFAULT_LEAF, sizeof(DEFAULT_LEAF)); + memset(leaf_data.pub.delay_schedule, 0, + sizeof(leaf_data.pub.delay_schedule)); + leaf_data.pub.delay_schedule[0].attempt_count.v = 5; + leaf_data.pub.delay_schedule[0].time_diff.v = PW_BLOCK_ATTEMPTS; + leaf_data.pub.attempt_count.v = 6; + force_restart_count(1); force_time((timestamp_t){.val = 7200llu * SECOND}); setup_try_auth_defaults_with_leaf(&leaf_data, &merkle_tree, &buf.request); @@ -1312,7 +1632,7 @@ static int handle_try_auth_rate_limit_reached(void) leaf_data.pub.timestamp.timer_value = 7200llu; setup_try_auth_defaults_with_leaf(&leaf_data, &merkle_tree, &buf.request); - MOCK_restart_count = 0; + force_restart_count(0); force_time((timestamp_t){.val = (leaf_data.pub.timestamp.timer_value + 3599llu) * SECOND}); @@ -1327,7 +1647,7 @@ static int handle_try_auth_rate_limit_reached(void) leaf_data.pub.timestamp.timer_value = 7200llu; setup_try_auth_defaults_with_leaf(&leaf_data, &merkle_tree, &buf.request); - MOCK_restart_count = 1; + force_restart_count(1); force_time((timestamp_t){.val = 3599llu * SECOND}); TEST_RET_EQ(check_try_auth_rate_limit_reached_response( @@ -1337,6 +1657,22 @@ static int handle_try_auth_rate_limit_reached(void) return check_dcrypto_mutex_usage(); } +static int handle_try_auth_nv_fail(void) +{ + struct merkle_tree_t merkle_tree; + struct pw_test_data_t buf; + + setup_try_auth_defaults(&merkle_tree, &buf.request); + force_restart_count(0); + force_time((timestamp_t){.val = 65 * SECOND}); + + MOCK_writevars_ret = PW_ERR_NV_LENGTH_MISMATCH; + + TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, merkle_tree.root), + PW_ERR_NV_LENGTH_MISMATCH); + return EC_SUCCESS; +} + static int handle_try_auth_lowent_auth_failed(void) { struct merkle_tree_t merkle_tree; @@ -1356,7 +1692,7 @@ static int handle_try_auth_lowent_auth_failed(void) sizeof(leaf_data.sec.low_entropy_secret) - 1]; setup_try_auth_defaults_with_leaf(&leaf_data, &merkle_tree, &buf.request); - MOCK_restart_count = 1; + force_restart_count(1); force_time((timestamp_t){.val = (65ull * SECOND)}); TEST_RET_EQ(do_request(&merkle_tree, &buf), PW_ERR_LOWENT_AUTH_FAILED); @@ -1397,6 +1733,20 @@ static int handle_try_auth_lowent_auth_failed(void) * force_time() is called. */ TEST_ASSERT(pub->timestamp.timer_value - 65ull < 100); + + /* Validate the log entry for a failed auth attempt. */ + TEST_ASSERT(MOCK_pw_log_storage.entries[0].type.v == PW_TRY_AUTH); + TEST_ASSERT(MOCK_pw_log_storage.entries[0].label.v == + DEFAULT_LEAF.pub.label.v); + TEST_ASSERT(MOCK_pw_log_storage.entries[0].return_code == + PW_ERR_LOWENT_AUTH_FAILED); + TEST_ASSERT(MOCK_pw_log_storage.entries[0].timestamp.boot_count == + pub->timestamp.boot_count); + TEST_ASSERT(MOCK_pw_log_storage.entries[0].timestamp.timer_value == + pub->timestamp.timer_value); + TEST_ASSERT_ARRAY_EQ(MOCK_pw_log_storage.entries[0].root, + EMPTY_TREE.root, + sizeof(EMPTY_TREE.root)); return check_dcrypto_mutex_usage(); } @@ -1416,7 +1766,7 @@ static int handle_try_auth_success(void) leaf_data.pub.attempt_count.v = 6; setup_try_auth_defaults_with_leaf(&leaf_data, &merkle_tree, &buf.request); - MOCK_restart_count = 0; + force_restart_count(0); force_time((timestamp_t){.val = 65 * SECOND}); TEST_RET_EQ(do_request(&merkle_tree, &buf), EC_SUCCESS); @@ -1453,13 +1803,26 @@ static int handle_try_auth_success(void) DEFAULT_LEAF.sec.high_entropy_secret, sizeof(DEFAULT_LEAF.sec.high_entropy_secret)); + /* Validate the log entry on success. */ + TEST_ASSERT(MOCK_pw_log_storage.entries[0].type.v == PW_TRY_AUTH); + TEST_ASSERT(MOCK_pw_log_storage.entries[0].label.v == + DEFAULT_LEAF.pub.label.v); + TEST_ASSERT(MOCK_pw_log_storage.entries[0].return_code == EC_SUCCESS); + TEST_ASSERT(MOCK_pw_log_storage.entries[0].timestamp.boot_count == + pub->timestamp.boot_count); + TEST_ASSERT(MOCK_pw_log_storage.entries[0].timestamp.timer_value == + pub->timestamp.timer_value); + TEST_ASSERT_ARRAY_EQ(MOCK_pw_log_storage.entries[0].root, + ROOT_WITH_DEFAULT_HMAC, + sizeof(ROOT_WITH_DEFAULT_HMAC)); + /* Test boot_count + 1 case. */ leaf_data.pub.attempt_count.v = 6; leaf_data.pub.timestamp.boot_count = 0; leaf_data.pub.timestamp.timer_value = 7200llu; setup_try_auth_defaults_with_leaf(&leaf_data, &merkle_tree, &buf.request); - MOCK_restart_count = 1; + force_restart_count(1); force_time((timestamp_t){.val = 65llu * SECOND}); TEST_RET_EQ(do_request(&merkle_tree, &buf), EC_SUCCESS); @@ -1588,6 +1951,20 @@ static int handle_reset_auth_reset_auth_failed(void) return check_dcrypto_mutex_usage(); } +static int handle_reset_auth_nv_fail(void) +{ + struct merkle_tree_t merkle_tree; + struct pw_test_data_t buf; + + setup_reset_auth_defaults(&merkle_tree, &buf.request); + + MOCK_writevars_ret = PW_ERR_NV_LENGTH_MISMATCH; + + TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, merkle_tree.root), + PW_ERR_NV_LENGTH_MISMATCH); + return EC_SUCCESS; +} + static int handle_reset_auth_success(void) { struct merkle_tree_t merkle_tree; @@ -1635,6 +2012,19 @@ static int handle_reset_auth_success(void) sizeof(DEFAULT_LEAF.sec)); TEST_ASSERT(pub->attempt_count.v == 0); + /* Validate the log entry on success. */ + TEST_ASSERT(MOCK_pw_log_storage.entries[0].type.v == PW_TRY_AUTH); + TEST_ASSERT(MOCK_pw_log_storage.entries[0].label.v == + DEFAULT_LEAF.pub.label.v); + TEST_ASSERT(MOCK_pw_log_storage.entries[0].return_code == EC_SUCCESS); + TEST_ASSERT(MOCK_pw_log_storage.entries[0].timestamp.boot_count == + pub->timestamp.boot_count); + TEST_ASSERT(MOCK_pw_log_storage.entries[0].timestamp.timer_value == + pub->timestamp.timer_value); + TEST_ASSERT_ARRAY_EQ(MOCK_pw_log_storage.entries[0].root, + ROOT_WITH_DEFAULT_HMAC, + sizeof(ROOT_WITH_DEFAULT_HMAC)); + /* Test with different minor version and struct lengths. */ setup_reset_auth_defaults(&merkle_tree, &buf.request); setup_mock_future_version( @@ -1675,6 +2065,343 @@ static int handle_reset_auth_success(void) TEST_ASSERT_ARRAY_EQ((uint8_t *)&sec, (uint8_t *)&DEFAULT_LEAF.sec, sizeof(DEFAULT_LEAF.sec)); TEST_ASSERT(pub->attempt_count.v == 0); + + /* Validate the log entry on success. */ + TEST_ASSERT(MOCK_pw_log_storage.entries[0].type.v == PW_TRY_AUTH); + TEST_ASSERT(MOCK_pw_log_storage.entries[0].label.v == + DEFAULT_LEAF.pub.label.v); + TEST_ASSERT(MOCK_pw_log_storage.entries[0].return_code == EC_SUCCESS); + TEST_ASSERT(MOCK_pw_log_storage.entries[0].timestamp.boot_count == + pub->timestamp.boot_count); + TEST_ASSERT(MOCK_pw_log_storage.entries[0].timestamp.timer_value == + pub->timestamp.timer_value); + TEST_ASSERT_ARRAY_EQ(MOCK_pw_log_storage.entries[0].root, + ROOT_WITH_DEFAULT_HMAC, + sizeof(ROOT_WITH_DEFAULT_HMAC)); + return check_dcrypto_mutex_usage(); +} + +/******************************************************************************/ +/* Get log test cases. + */ + +static int handle_get_log_invalid_length(void) +{ + struct merkle_tree_t merkle_tree; + struct pw_test_data_t buf; + + setup_get_log_defaults(&merkle_tree, &buf.request); + + ++buf.request.header.data_length; + + TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, EMPTY_TREE.root), + PW_ERR_LENGTH_INVALID); + return check_dcrypto_mutex_usage(); +} + +static int handle_get_log_nv_fail(void) +{ + struct merkle_tree_t merkle_tree; + struct pw_test_data_t buf; + + setup_get_log_defaults(&merkle_tree, &buf.request); + + MOCK_getvar_ret = PW_ERR_NV_EMPTY; + + TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, EMPTY_TREE.root), + PW_ERR_NV_EMPTY); + return check_dcrypto_mutex_usage(); +} + +static int handle_get_log_success(void) +{ + struct merkle_tree_t merkle_tree; + struct pw_test_data_t buf; + const struct pw_get_log_entry_t (*view)[PW_LOG_ENTRY_COUNT] = + (void *)buf.response.data.get_log; + + setup_get_log_defaults(&merkle_tree, &buf.request); + setup_storage(4); + + TEST_RET_EQ(do_request(&merkle_tree, &buf), EC_SUCCESS); + + TEST_ASSERT_ARRAY_EQ(buf.response.header.root, EMPTY_TREE.root, + sizeof(EMPTY_TREE.root)); + TEST_ASSERT_ARRAY_EQ(buf.response.header.root, merkle_tree.root, + sizeof(merkle_tree.root)); + + TEST_ASSERT(buf.response.header.version == PW_PROTOCOL_VERSION); + TEST_ASSERT(buf.response.header.data_length == + sizeof(struct pw_get_log_entry_t) * PW_LOG_ENTRY_COUNT); + TEST_RET_EQ(buf.response.header.result_code, EC_SUCCESS); + + TEST_ASSERT(buf.response.header.version == PW_PROTOCOL_VERSION); + + TEST_ASSERT((*view)[0].type.v == PW_REMOVE_LEAF); + TEST_ASSERT((*view)[0].label.v == DEFAULT_LEAF.pub.label.v); + TEST_ASSERT_ARRAY_EQ((*view)[0].root, EMPTY_TREE.root, + sizeof(EMPTY_TREE.root)); + + TEST_ASSERT((*view)[1].type.v == PW_TRY_AUTH); + TEST_ASSERT((*view)[1].label.v == DEFAULT_LEAF.pub.label.v); + TEST_ASSERT((*view)[1].return_code == EC_SUCCESS); + TEST_ASSERT((*view)[1].timestamp.boot_count == 10); + TEST_ASSERT((*view)[1].timestamp.timer_value == 100); + TEST_ASSERT_ARRAY_EQ((*view)[1].root, ROOT_WITH_DEFAULT_HMAC, + sizeof(ROOT_WITH_DEFAULT_HMAC)); + + setup_get_log_defaults(&merkle_tree, &buf.request); + setup_storage(2); + + TEST_RET_EQ(do_request(&merkle_tree, &buf), EC_SUCCESS); + + TEST_ASSERT((*view)[0].type.v == PW_TRY_AUTH); + TEST_ASSERT((*view)[0].label.v == DEFAULT_LEAF.pub.label.v); + TEST_ASSERT((*view)[0].return_code == PW_ERR_LOWENT_AUTH_FAILED); + TEST_ASSERT((*view)[0].timestamp.boot_count == 7); + TEST_ASSERT((*view)[0].timestamp.timer_value == 99); + TEST_ASSERT_ARRAY_EQ((*view)[0].root, ROOT_WITH_OTHER_HMAC, + sizeof(ROOT_WITH_OTHER_HMAC)); + + TEST_ASSERT((*view)[1].type.v == PW_INSERT_LEAF); + TEST_ASSERT((*view)[1].label.v == DEFAULT_LEAF.pub.label.v); + TEST_ASSERT_ARRAY_EQ((*view)[1].root, ROOT_WITH_DEFAULT_HMAC, + sizeof(ROOT_WITH_DEFAULT_HMAC)); + TEST_ASSERT_ARRAY_EQ((*view)[1].leaf_hmac, DEFAULT_HMAC, + sizeof(DEFAULT_HMAC)); + + setup_get_log_defaults(&merkle_tree, &buf.request); + setup_storage(0); + + TEST_RET_EQ(do_request(&merkle_tree, &buf), EC_SUCCESS); + + TEST_ASSERT((*view)[0].type.v == PW_RESET_TREE); + TEST_ASSERT_ARRAY_EQ((*view)[0].root, EMPTY_TREE.root, + sizeof(EMPTY_TREE.root)); + + return check_dcrypto_mutex_usage(); +} + +/******************************************************************************/ +/* Log replay test cases. + */ + +static int handle_log_replay_invalid_length(void) +{ + return invalid_length_with_leaf_head( + (size_t)&((struct pw_request_t *)0)->data.log_replay + .unimported_leaf_data.head, + setup_log_replay_defaults); +} + +static int handle_log_replay_nv_fail(void) +{ + struct merkle_tree_t merkle_tree; + struct pw_test_data_t buf; + + setup_log_replay_defaults(&merkle_tree, &buf.request); + + MOCK_getvar_ret = PW_ERR_NV_EMPTY; + + TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, EMPTY_TREE.root), + PW_ERR_NV_EMPTY); + return check_dcrypto_mutex_usage(); +} + +static int handle_log_replay_root_not_found(void) +{ + struct merkle_tree_t merkle_tree; + struct pw_test_data_t buf; + + setup_log_replay_defaults(&merkle_tree, &buf.request); + + memcpy(buf.request.data.log_replay.log_root, DEFAULT_HMAC, + sizeof(DEFAULT_HMAC)); + + TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, EMPTY_TREE.root), + PW_ERR_ROOT_NOT_FOUND); + return check_dcrypto_mutex_usage(); +} + +static int handle_log_replay_type_invalid(void) +{ + struct merkle_tree_t merkle_tree; + struct pw_test_data_t buf; + + setup_log_replay_defaults(&merkle_tree, &buf.request); + + memcpy(buf.request.data.log_replay.log_root, EMPTY_TREE.root, + sizeof(EMPTY_TREE.root)); + + TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, EMPTY_TREE.root), + PW_ERR_TYPE_INVALID); + return check_dcrypto_mutex_usage(); +} + +static int handle_log_replay_hmac_auth_failed(void) +{ + struct merkle_tree_t merkle_tree; + struct pw_test_data_t buf; + struct leaf_data_t leaf_data = {}; + + memcpy(&leaf_data, &DEFAULT_LEAF, sizeof(leaf_data)); + leaf_data.pub.attempt_count.v = 7; + setup_log_replay_defaults_with_leaf(&leaf_data, &merkle_tree, + &buf.request); + + memcpy(buf.request.data.log_replay.unimported_leaf_data.hmac, + EMPTY_HMAC, sizeof(EMPTY_HMAC)); + + TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, EMPTY_TREE.root), + PW_ERR_HMAC_AUTH_FAILED); + return check_dcrypto_mutex_usage(); +} + +static int handle_log_replay_crypto_failure(void) +{ + struct merkle_tree_t merkle_tree; + struct pw_test_data_t buf; + struct leaf_data_t leaf_data = {}; + + memcpy(&leaf_data, &DEFAULT_LEAF, sizeof(leaf_data)); + leaf_data.pub.attempt_count.v = 7; + setup_log_replay_defaults_with_leaf(&leaf_data, &merkle_tree, + &buf.request); + + MOCK_aes_fail = 1; + + TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, EMPTY_TREE.root), + PW_ERR_CRYPTO_FAILURE); + return check_dcrypto_mutex_usage(); +} + +static int handle_log_replay_label_invalid(void) +{ + struct merkle_tree_t merkle_tree; + struct pw_test_data_t buf; + struct leaf_data_t leaf_data = {}; + + memcpy(&leaf_data, &DEFAULT_LEAF, sizeof(leaf_data)); + leaf_data.pub.label.v = 0; + setup_log_replay_defaults_with_leaf(&leaf_data, &merkle_tree, + &buf.request); + + TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, EMPTY_TREE.root), + PW_ERR_LABEL_INVALID); + return check_dcrypto_mutex_usage(); +} + +static int handle_log_replay_path_auth_failed(void) +{ + struct merkle_tree_t merkle_tree; + struct pw_test_data_t buf; + uint8_t (*path_hashes)[32] = + (void *)buf.request.data.log_replay.unimported_leaf_data + .payload + + sizeof(struct leaf_public_data_t) + + sizeof(struct leaf_sensitive_data_t); + + setup_log_replay_defaults(&merkle_tree, &buf.request); + + (*path_hashes)[0] ^= 0xff; + + TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, EMPTY_TREE.root), + PW_ERR_PATH_AUTH_FAILED); + return check_dcrypto_mutex_usage(); +} + +static int handle_log_replay_success(void) +{ + struct merkle_tree_t merkle_tree; + struct pw_test_data_t buf; + struct leaf_data_t leaf_data = {}; + struct leaf_public_data_t *pub = + (void *)buf.response.data.log_replay + .unimported_leaf_data.payload; + struct leaf_sensitive_data_t sec = {}; + uint8_t *resp_cipher_text = (void *)pub + sizeof(*pub); + + /* + * Test for auth success. + */ + setup_log_replay_defaults(&merkle_tree, &buf.request); + + TEST_RET_EQ(do_request(&merkle_tree, &buf), EC_SUCCESS); + + TEST_ASSERT(buf.response.header.version == PW_PROTOCOL_VERSION); + TEST_ASSERT(buf.response.header.data_length == + sizeof(struct pw_response_log_replay_t) + + PW_LEAF_PAYLOAD_SIZE); + TEST_RET_EQ(buf.response.header.result_code, EC_SUCCESS); + + TEST_ASSERT_ARRAY_EQ(buf.response.header.root, EMPTY_TREE.root, + sizeof(EMPTY_TREE.root)); + TEST_ASSERT_ARRAY_EQ(buf.response.header.root, merkle_tree.root, + sizeof(merkle_tree.root)); + + TEST_ASSERT_ARRAY_EQ( + buf.response.data.log_replay.unimported_leaf_data.hmac, + DEFAULT_HMAC, sizeof(DEFAULT_HMAC)); + TEST_ASSERT_ARRAY_EQ( + buf.response.data.log_replay.unimported_leaf_data.iv, + DEFAULT_IV, sizeof(DEFAULT_IV)); + DCRYPTO_aes_ctr((uint8_t *)&sec, EMPTY_TREE.wrap_key, + sizeof(EMPTY_TREE.wrap_key) * 8, DEFAULT_IV, + resp_cipher_text, sizeof(sec)); + TEST_ASSERT(pub->label.v == DEFAULT_LEAF.pub.label.v); + TEST_ASSERT_ARRAY_EQ( + (const uint8_t *)&pub->delay_schedule, + (const uint8_t *)&DEFAULT_LEAF.pub.delay_schedule, + sizeof(DEFAULT_LEAF.pub.delay_schedule)); + TEST_ASSERT_ARRAY_EQ((uint8_t *)&sec, (uint8_t *)&DEFAULT_LEAF.sec, + sizeof(DEFAULT_LEAF.sec)); + TEST_ASSERT(pub->attempt_count.v == 0); + TEST_ASSERT(pub->timestamp.boot_count == 10); + TEST_ASSERT(pub->timestamp.timer_value == 100); + + /* + * Test for auth failed. + */ + memcpy(&leaf_data, &DEFAULT_LEAF, sizeof(leaf_data)); + leaf_data.pub.attempt_count.v = 15; + setup_log_replay_defaults_with_leaf(&leaf_data, &merkle_tree, + &buf.request); + memcpy(buf.request.data.log_replay.log_root, ROOT_WITH_OTHER_HMAC, + sizeof(ROOT_WITH_OTHER_HMAC)); + setup_storage(2); + + TEST_RET_EQ(do_request(&merkle_tree, &buf), EC_SUCCESS); + + TEST_ASSERT(buf.response.header.version == PW_PROTOCOL_VERSION); + TEST_ASSERT(buf.response.header.data_length == + sizeof(struct pw_response_log_replay_t) + + PW_LEAF_PAYLOAD_SIZE); + TEST_RET_EQ(buf.response.header.result_code, EC_SUCCESS); + + TEST_ASSERT_ARRAY_EQ(buf.response.header.root, EMPTY_TREE.root, + sizeof(EMPTY_TREE.root)); + TEST_ASSERT_ARRAY_EQ(buf.response.header.root, merkle_tree.root, + sizeof(merkle_tree.root)); + + TEST_ASSERT_ARRAY_EQ( + buf.response.data.log_replay.unimported_leaf_data.hmac, + OTHER_HMAC, sizeof(OTHER_HMAC)); + TEST_ASSERT_ARRAY_EQ( + buf.response.data.log_replay.unimported_leaf_data.iv, + DEFAULT_IV, sizeof(DEFAULT_IV)); + DCRYPTO_aes_ctr((uint8_t *)&sec, EMPTY_TREE.wrap_key, + sizeof(EMPTY_TREE.wrap_key) * 8, DEFAULT_IV, + resp_cipher_text, sizeof(sec)); + TEST_ASSERT(pub->label.v == DEFAULT_LEAF.pub.label.v); + TEST_ASSERT_ARRAY_EQ( + (const uint8_t *)&pub->delay_schedule, + (const uint8_t *)&DEFAULT_LEAF.pub.delay_schedule, + sizeof(DEFAULT_LEAF.pub.delay_schedule)); + TEST_ASSERT_ARRAY_EQ((uint8_t *)&sec, (uint8_t *)&DEFAULT_LEAF.sec, + sizeof(DEFAULT_LEAF.sec)); + TEST_ASSERT(pub->attempt_count.v == 16); + TEST_ASSERT(pub->timestamp.boot_count == 7); + TEST_ASSERT(pub->timestamp.timer_value == 99); return check_dcrypto_mutex_usage(); } @@ -1699,6 +2426,7 @@ void run_test(void) RUN_TEST(handle_reset_tree_bits_per_level_invalid); RUN_TEST(handle_reset_tree_height_invalid); RUN_TEST(handle_reset_tree_crypto_failure); + RUN_TEST(handle_reset_tree_nv_fail); RUN_TEST(handle_reset_tree_success); /* Test insert leaf. */ @@ -1707,12 +2435,14 @@ void run_test(void) RUN_TEST(handle_insert_leaf_delay_schedule_invalid); RUN_TEST(handle_insert_leaf_path_auth_failed); RUN_TEST(handle_insert_leaf_crypto_failure); + RUN_TEST(handle_insert_leaf_nv_fail); RUN_TEST(handle_insert_leaf_success); /* Test remove leaf. */ RUN_TEST(handle_remove_leaf_invalid_length); RUN_TEST(handle_remove_leaf_label_invalid); RUN_TEST(handle_remove_leaf_path_auth_failed); + RUN_TEST(handle_remove_leaf_nv_fail); RUN_TEST(handle_remove_leaf_success); /* Test try auth. */ @@ -1723,6 +2453,7 @@ void run_test(void) RUN_TEST(handle_try_auth_hmac_auth_failed); RUN_TEST(handle_try_auth_crypto_failure); RUN_TEST(handle_try_auth_rate_limit_reached); + RUN_TEST(handle_try_auth_nv_fail); RUN_TEST(handle_try_auth_lowent_auth_failed); RUN_TEST(handle_try_auth_success); @@ -1733,7 +2464,24 @@ void run_test(void) RUN_TEST(handle_reset_auth_hmac_auth_failed); RUN_TEST(handle_reset_auth_crypto_failure); RUN_TEST(handle_reset_auth_reset_auth_failed); + RUN_TEST(handle_reset_auth_nv_fail); RUN_TEST(handle_reset_auth_success); + /* Test get log. */ + RUN_TEST(handle_get_log_invalid_length); + RUN_TEST(handle_get_log_nv_fail); + RUN_TEST(handle_get_log_success); + + /* Test log replay. */ + RUN_TEST(handle_log_replay_invalid_length); + RUN_TEST(handle_log_replay_nv_fail); + RUN_TEST(handle_log_replay_root_not_found); + RUN_TEST(handle_log_replay_type_invalid); + RUN_TEST(handle_log_replay_hmac_auth_failed); + RUN_TEST(handle_log_replay_crypto_failure); + RUN_TEST(handle_log_replay_label_invalid); + RUN_TEST(handle_log_replay_path_auth_failed); + RUN_TEST(handle_log_replay_success); + test_print_result(); } diff --git a/test/test_config.h b/test/test_config.h index dd14aad6b2..8542a17e0a 100644 --- a/test/test_config.h +++ b/test/test_config.h @@ -262,7 +262,7 @@ enum nvmem_vars { #ifdef TEST_PINWEAVER #define CONFIG_PINWEAVER #define CONFIG_SHA256 -#endif +#endif /* TEST_PINWEAVER */ #ifdef TEST_RTC #define CONFIG_HOSTCMD_RTC |