summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAllen Webb <allenwebb@google.com>2018-02-28 15:42:47 -0800
committerchrome-bot <chrome-bot@chromium.org>2018-04-27 12:22:26 -0700
commit1820ecce31e6a23e5ab79f708f66a8655da6a161 (patch)
tree9c4f09621ec2282208cfed21de76f8172fc9c888
parent826a3876b4f3ecd5f73d2320ee1e853a789e6e30 (diff)
downloadchrome-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.c5
-rw-r--r--board/cr50/tpm2/platform.c2
-rw-r--r--board/host/dcrypto.h7
-rw-r--r--common/pinweaver.c464
-rw-r--r--include/pinweaver.h54
-rw-r--r--include/pinweaver_tpm_imports.h2
-rw-r--r--include/pinweaver_types.h60
-rw-r--r--test/pinweaver.c778
-rw-r--r--test/test_config.h2
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, &timestamp, 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