diff --git a/board/cr50/board.h b/board/cr50/board.h index 75f25cc533..92c4230620 100644 --- a/board/cr50/board.h +++ b/board/cr50/board.h @@ -358,4 +358,7 @@ enum nvmem_users { #define CONFIG_ENABLE_H1_ALERTS +/* Enable hardware backed brute force resistance feature */ +#define CONFIG_PINWEAVER + #endif /* __CROS_EC_BOARD_H */ diff --git a/board/cr50/build.mk b/board/cr50/build.mk index c245920bcd..9d717572d4 100644 --- a/board/cr50/build.mk +++ b/board/cr50/build.mk @@ -99,3 +99,5 @@ $(out)/tpm2/libtpm2.a: $(MAKE) obj=$(realpath $(out))/tpm2 EMBEDDED_MODE=1 OBJ_PREFIX=Tpm2_ -C $(EXTLIB) endif # BOARD_MK_INCLUDED_ONCE is nonempty + +board-$(CONFIG_PINWEAVER)+=pinweaver_tpm_imports.o diff --git a/board/cr50/pinweaver_tpm_imports.c b/board/cr50/pinweaver_tpm_imports.c new file mode 100644 index 0000000000..eec2284dfd --- /dev/null +++ b/board/cr50/pinweaver_tpm_imports.c @@ -0,0 +1,20 @@ +/* Copyright 2018 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include + +#include +#include + +uint32_t get_restart_count(void) +{ + return gp.resetCount; +} + +void get_storage_seed(void *buf, size_t *len) +{ + *len = MIN(*len, sizeof(gp.SPSeed)); + memcpy(buf, &gp.SPSeed, *len); +} diff --git a/board/host/dcrypto.h b/board/host/dcrypto.h new file mode 100644 index 0000000000..f949ef4352 --- /dev/null +++ b/board/host/dcrypto.h @@ -0,0 +1,61 @@ +/* Copyright 2018 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* Provides the minimal declarations needed by pinweaver to build on + * CHIP_HOST. + */ + +#ifndef __CROS_EC_DCRYPTO_HOST_H +#define __CROS_EC_DCRYPTO_HOST_H + +#include +#include +#include + +#define AES256_BLOCK_CIPHER_KEY_SIZE 32 +#define SHA256_DIGEST_SIZE 32 + +#define HASH_CTX sha256_ctx + +enum dcrypto_appid { + RESERVED = 0, + NVMEM = 1, + U2F_ATTEST = 2, + U2F_ORIGIN = 3, + U2F_WRAP = 4, + PERSO_AUTH = 5, + PINWEAVER = 6, + /* This enum value should not exceed 7. */ +}; + +struct dcrypto_mock_ctx_t { + struct HASH_CTX hash; +}; +#define LITE_HMAC_CTX struct dcrypto_mock_ctx_t +#define LITE_SHA256_CTX struct HASH_CTX + +void HASH_update(struct HASH_CTX *ctx, const void *data, size_t len); + +uint8_t *HASH_final(struct HASH_CTX *ctx); + +void DCRYPTO_SHA256_init(LITE_SHA256_CTX *ctx, uint32_t sw_required); + +void DCRYPTO_HMAC_SHA256_init(LITE_HMAC_CTX *ctx, const void *key, + unsigned int len); +const uint8_t *DCRYPTO_HMAC_final(LITE_HMAC_CTX *ctx); + +int DCRYPTO_aes_ctr(uint8_t *out, const uint8_t *key, uint32_t key_bits, + const uint8_t *iv, const uint8_t *in, size_t in_len); + +struct APPKEY_CTX {}; + +int DCRYPTO_appkey_init(enum dcrypto_appid appid, struct APPKEY_CTX *ctx); + +void DCRYPTO_appkey_finish(struct APPKEY_CTX *ctx); + +int DCRYPTO_appkey_derive(enum dcrypto_appid appid, const uint32_t input[8], + uint32_t output[8]); + +#endif /* __CROS_EC_HOST_DCRYPTO_H */ diff --git a/chip/g/dcrypto/app_key.c b/chip/g/dcrypto/app_key.c index 173bc9d214..d10dd46014 100644 --- a/chip/g/dcrypto/app_key.c +++ b/chip/g/dcrypto/app_key.c @@ -49,7 +49,6 @@ const struct { 0xcd375bcd, 0x8065e8cc, 0xc892ed69, 0x72436c7d } }, -#ifdef CONFIG_STREAM_SIGNATURE { /* This key signs data from H1's configured by mn50/scribe. */ "PERSO_AUTH", @@ -58,7 +57,13 @@ const struct { 0x5ecb7690, 0x09f732c9, 0xe540bf14, 0xcc46799a } }, -#endif + { + "PINWEAVER", + { + 0x51cd9166, 0x911a7460, 0x96aeaf06, 0xa9d0371c, + 0xfa08a500, 0xfe4e04a1, 0xe0a36b57, 0x0418c429 + } + }, }; int DCRYPTO_appkey_init(enum dcrypto_appid appid, struct APPKEY_CTX *ctx) diff --git a/chip/g/dcrypto/dcrypto.h b/chip/g/dcrypto/dcrypto.h index 2d050ff6d7..bbe09821e8 100644 --- a/chip/g/dcrypto/dcrypto.h +++ b/chip/g/dcrypto/dcrypto.h @@ -274,6 +274,7 @@ enum dcrypto_appid { U2F_ORIGIN = 3, U2F_WRAP = 4, PERSO_AUTH = 5, + PINWEAVER = 6, /* This enum value should not exceed 7. */ }; diff --git a/common/build.mk b/common/build.mk index 37e8a322c1..a48880bafc 100644 --- a/common/build.mk +++ b/common/build.mk @@ -81,6 +81,7 @@ common-$(CONFIG_MAG_CALIBRATE)+= mag_cal.o math_util.o vec3.o mat33.o mat44.o common-$(CONFIG_MKBP_EVENT)+=mkbp_event.o common-$(CONFIG_ONEWIRE)+=onewire.o common-$(CONFIG_PHYSICAL_PRESENCE)+=physical_presence.o +common-$(CONFIG_PINWEAVER)+=pinweaver.o common-$(CONFIG_POWER_BUTTON)+=power_button.o common-$(CONFIG_POWER_BUTTON_X86)+=power_button_x86.o common-$(CONFIG_PSTORE)+=pstore_commands.o diff --git a/common/pinweaver.c b/common/pinweaver.c new file mode 100644 index 0000000000..63e1a4522e --- /dev/null +++ b/common/pinweaver.c @@ -0,0 +1,894 @@ +/* Copyright 2018 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Compile time sanity checks. */ +/* Make sure the hash size is consistent with dcrypto. */ +BUILD_ASSERT(PW_HASH_SIZE >= SHA256_DIGEST_SIZE); + +/* sizeof(struct leaf_data_t) % 16 should be zero */ +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 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; }) + + sizeof(struct leaf_public_data_t) + + sizeof(struct leaf_sensitive_data_t) + + PW_MAX_PATH_SIZE); + +/* Verify that the request structs will fit into the message. */ +BUILD_ASSERT(PW_MAX_MESSAGE_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; }) + + PW_LEAF_PAYLOAD_SIZE); +/* Make sure the largest possible message would fit in + * (struct tpm_register_file).data_fifo. + */ +BUILD_ASSERT(PW_MAX_MESSAGE_SIZE + sizeof(struct tpm_cmd_header) <= 2048); + +/* PW_MAX_PATH_SIZE should not change unless PW_LEAF_MAJOR_VERSION changes too. + * Update these statements whenever these constants are changed to remind future + * maintainers about this requirement. + * + * This requirement helps guarantee that forward compatibility across the same + * PW_LEAF_MAJOR_VERSION doesn't break because of a path length becoming too + * long after new fields are added to struct wrapped_leaf_data_t or its sub + * fields. + */ +BUILD_ASSERT(PW_LEAF_MAJOR_VERSION == 0); +BUILD_ASSERT(PW_MAX_PATH_SIZE == 1536); + +/* If fields are appended to struct leaf_sensitive_data_t, an encryption + * operation should be performed on them reusing the same IV since the prefix + * won't change. + * + * If any data in the original struct leaf_sensitive_data_t changes, a new IV + * should be generated and stored as part of the log for a replay to be + * possible. + */ +BUILD_ASSERT(sizeof(struct leaf_sensitive_data_t) == 3 * PW_SECRET_SIZE); + +/******************************************************************************/ +/* Struct helper functions. + */ + +void import_leaf(const struct unimported_leaf_data_t *unimported, + struct imported_leaf_data_t *imported) +{ + imported->head = &unimported->head; + imported->hmac = unimported->hmac; + imported->iv = unimported->iv; + imported->pub = (const struct leaf_public_data_t *)unimported->payload; + imported->cipher_text = unimported->payload + unimported->head.pub_len; + imported->hashes = (const uint8_t (*)[PW_HASH_SIZE])( + imported->cipher_text + unimported->head.sec_len); +} + +/******************************************************************************/ +/* Basic operations required by the Merkle tree. + */ + +static int derive_keys(struct merkle_tree_t *merkle_tree) +{ + struct APPKEY_CTX ctx; + int ret = EC_SUCCESS; + const uint32_t KEY_TYPE_AES = 0x0; + const uint32_t KEY_TYPE_HMAC = 0xffffffff; + union { + uint32_t v[8]; + uint8_t bytes[sizeof(uint32_t) * 8]; + } input; + uint32_t type_field; + size_t seed_size = sizeof(input); + size_t x; + + get_storage_seed(input.v, &seed_size); + for (x = 0; x < ARRAY_SIZE(input.bytes) && + x < ARRAY_SIZE(merkle_tree->key_derivation_nonce); ++x) + input.bytes[x] ^= merkle_tree->key_derivation_nonce[x]; + type_field = input.v[6]; + + if (!DCRYPTO_appkey_init(PINWEAVER, &ctx)) + return PW_ERR_CRYPTO_FAILURE; + + input.v[6] = type_field ^ KEY_TYPE_AES; + if (!DCRYPTO_appkey_derive(PINWEAVER, input.v, + (uint32_t *)merkle_tree->wrap_key)) { + ret = PW_ERR_CRYPTO_FAILURE; + goto cleanup; + } + + input.v[6] = type_field ^ KEY_TYPE_HMAC; + if (!DCRYPTO_appkey_derive(PINWEAVER, input.v, + (uint32_t *)merkle_tree->hmac_key)) { + ret = PW_ERR_CRYPTO_FAILURE; + } +cleanup: + DCRYPTO_appkey_finish(&ctx); + return ret; +} + +/* Creates an empty merkle_tree with the given parameters. */ +static int create_merkle_tree(struct bits_per_level_t bits_per_level, + struct height_t height, + struct merkle_tree_t *merkle_tree) +{ + uint16_t fan_out = 1 << bits_per_level.v; + uint8_t temp_hash[PW_HASH_SIZE] = {}; + uint8_t hx; + uint16_t kx; + LITE_SHA256_CTX ctx; + + merkle_tree->bits_per_level = bits_per_level; + merkle_tree->height = height; + + /* Initialize the root hash. */ + for (hx = 0; hx < height.v; ++hx) { + DCRYPTO_SHA256_init(&ctx, 0); + for (kx = 0; kx < fan_out; ++kx) + HASH_update(&ctx, temp_hash, PW_HASH_SIZE); + memcpy(temp_hash, HASH_final(&ctx), PW_HASH_SIZE); + } + memcpy(merkle_tree->root, temp_hash, PW_HASH_SIZE); + + rand_bytes(merkle_tree->key_derivation_nonce, + sizeof(merkle_tree->key_derivation_nonce)); + return derive_keys(merkle_tree); +} + +/* Computes the HMAC for an encrypted leaf using the key in the merkle_tree. */ +static void compute_hmac( + const struct merkle_tree_t *merkle_tree, + const struct imported_leaf_data_t *imported_leaf_data, + uint8_t result[PW_HASH_SIZE]) +{ + LITE_HMAC_CTX hmac; + + DCRYPTO_HMAC_SHA256_init(&hmac, merkle_tree->hmac_key, + sizeof(merkle_tree->hmac_key)); + HASH_update(&hmac.hash, imported_leaf_data->head, + sizeof(*imported_leaf_data->head)); + HASH_update(&hmac.hash, imported_leaf_data->iv, + sizeof(PW_WRAP_BLOCK_SIZE)); + HASH_update(&hmac.hash, imported_leaf_data->pub, + imported_leaf_data->head->pub_len); + HASH_update(&hmac.hash, imported_leaf_data->cipher_text, + imported_leaf_data->head->sec_len); + memcpy(result, DCRYPTO_HMAC_final(&hmac), PW_HASH_SIZE); +} + +/* Computes the root hash for the specified path and child hash. */ +static void compute_root_hash(const struct merkle_tree_t *merkle_tree, + struct label_t path, + const uint8_t hashes[][PW_HASH_SIZE], + const uint8_t child_hash[PW_HASH_SIZE], + uint8_t new_root[PW_HASH_SIZE]) +{ + /* This is one less than the fan out, the number of sibling hashes. */ + const uint16_t num_aux = (1 << merkle_tree->bits_per_level.v) - 1; + const uint16_t path_suffix_mask = num_aux; + uint8_t temp_hash[PW_HASH_SIZE]; + uint8_t hx = 0; + uint64_t index = path.v; + + compute_hash(hashes, num_aux, + (struct index_t){index & path_suffix_mask}, + child_hash, temp_hash); + for (hx = 1; hx < merkle_tree->height.v; ++hx) { + hashes += num_aux; + index = index >> merkle_tree->bits_per_level.v; + compute_hash(hashes, num_aux, + (struct index_t){index & path_suffix_mask}, + temp_hash, temp_hash); + } + memcpy(new_root, temp_hash, sizeof(temp_hash)); +} + +/* Checks to see the specified path is valid. The length of the path should be + * validated prior to calling this function. + * + * Returns 0 on success or an error code otherwise. + */ +static int authenticate_path(const struct merkle_tree_t *merkle_tree, + struct label_t path, + const uint8_t hashes[][PW_HASH_SIZE], + const uint8_t child_hash[PW_HASH_SIZE]) +{ + uint8_t parent[PW_HASH_SIZE]; + + compute_root_hash(merkle_tree, path, hashes, child_hash, parent); + if (memcmp(parent, merkle_tree->root, sizeof(parent)) != 0) + return PW_ERR_PATH_AUTH_FAILED; + return EC_SUCCESS; +} + +static void init_wrapped_leaf_data( + struct wrapped_leaf_data_t *wrapped_leaf_data) +{ + wrapped_leaf_data->head.leaf_version.major = PW_LEAF_MAJOR_VERSION; + wrapped_leaf_data->head.leaf_version.minor = PW_LEAF_MINOR_VERSION; + wrapped_leaf_data->head.pub_len = sizeof(wrapped_leaf_data->pub); + wrapped_leaf_data->head.sec_len = + sizeof(wrapped_leaf_data->cipher_text); +} + +/* Encrypts the leaf meta data. */ +static int encrypt_leaf_data(const struct merkle_tree_t *merkle_tree, + const struct leaf_data_t *leaf_data, + struct wrapped_leaf_data_t *wrapped_leaf_data) +{ + /* Generate a random IV. + * + * If fields are appended to struct leaf_sensitive_data_t, an encryption + * operation should be performed on them reusing the same IV since the + * prefix won't change. + * + * If any data of in the original struct leaf_sensitive_data_t changes, + * a new IV should be generated and stored as part of the log for a + * replay to be possible. + */ + rand_bytes(wrapped_leaf_data->iv, sizeof(wrapped_leaf_data->iv)); + memcpy(&wrapped_leaf_data->pub, &leaf_data->pub, + sizeof(leaf_data->pub)); + if (!DCRYPTO_aes_ctr(wrapped_leaf_data->cipher_text, + merkle_tree->wrap_key, + sizeof(merkle_tree->wrap_key) << 3, + wrapped_leaf_data->iv, (uint8_t *)&leaf_data->sec, + sizeof(leaf_data->sec))) { + return PW_ERR_CRYPTO_FAILURE; + } + return EC_SUCCESS; +} + +/* Decrypts the leaf meta data. */ +static int decrypt_leaf_data( + const struct merkle_tree_t *merkle_tree, + const struct imported_leaf_data_t *imported_leaf_data, + struct leaf_data_t *leaf_data) +{ + memcpy(&leaf_data->pub, imported_leaf_data->pub, + sizeof(leaf_data->pub)); + if (!DCRYPTO_aes_ctr((uint8_t *)&leaf_data->sec, merkle_tree->wrap_key, + sizeof(merkle_tree->wrap_key) << 3, + imported_leaf_data->iv, + imported_leaf_data->cipher_text, + sizeof(leaf_data->sec))) { + return PW_ERR_CRYPTO_FAILURE; + } + return EC_SUCCESS; +} + +static int handle_leaf_update( + const struct merkle_tree_t *merkle_tree, + const struct leaf_data_t *leaf_data, + const uint8_t hashes[][PW_HASH_SIZE], + struct wrapped_leaf_data_t *wrapped_leaf_data, + uint8_t new_root[PW_HASH_SIZE], + const struct imported_leaf_data_t *optional_old_wrapped_data) +{ + int ret; + struct imported_leaf_data_t ptrs; + + init_wrapped_leaf_data(wrapped_leaf_data); + if (optional_old_wrapped_data == NULL) { + ret = encrypt_leaf_data(merkle_tree, leaf_data, + wrapped_leaf_data); + if (ret != EC_SUCCESS) + return ret; + } else { + memcpy(wrapped_leaf_data->iv, optional_old_wrapped_data->iv, + sizeof(wrapped_leaf_data->iv)); + memcpy(&wrapped_leaf_data->pub, &leaf_data->pub, + sizeof(leaf_data->pub)); + memcpy(wrapped_leaf_data->cipher_text, + optional_old_wrapped_data->cipher_text, + sizeof(wrapped_leaf_data->cipher_text)); + } + + import_leaf((const struct unimported_leaf_data_t *)wrapped_leaf_data, + &ptrs); + compute_hmac(merkle_tree, &ptrs, wrapped_leaf_data->hmac); + + compute_root_hash(merkle_tree, leaf_data->pub.label, + hashes, wrapped_leaf_data->hmac, + new_root); + + return EC_SUCCESS; +} + +/******************************************************************************/ +/* Parameter and state validation functions. + */ + +static int validate_tree_parameters(struct bits_per_level_t bits_per_level, + struct height_t height) +{ + uint8_t fan_out = 1 << bits_per_level.v; + + if (bits_per_level.v < BITS_PER_LEVEL_MIN || + bits_per_level.v > BITS_PER_LEVEL_MAX) + return PW_ERR_BITS_PER_LEVEL_INVALID; + + if (height.v < HEIGHT_MIN || + height.v > HEIGHT_MAX(bits_per_level.v) || + ((fan_out - 1) * height.v) * PW_HASH_SIZE > PW_MAX_PATH_SIZE) + return PW_ERR_HEIGHT_INVALID; + + return EC_SUCCESS; +} + +/* Verifies that merkle_tree has been initialized. */ +static int validate_tree(const struct merkle_tree_t *merkle_tree) +{ + if (validate_tree_parameters(merkle_tree->bits_per_level, + merkle_tree->height) != EC_SUCCESS) + return PW_ERR_TREE_INVALID; + return EC_SUCCESS; +} + +/* Checks the following conditions: + * Extra index fields should be all zero. + */ +static int validate_label(const struct merkle_tree_t *merkle_tree, + struct label_t path) +{ + uint8_t shift_by = merkle_tree->bits_per_level.v * + merkle_tree->height.v; + + if ((path.v >> shift_by) == 0) + return EC_SUCCESS; + return PW_ERR_LABEL_INVALID; +} + +/* Checks the following conditions: + * Columns should be strictly increasing. + * Zeroes for filler at the end of the delay_schedule are permitted. + */ +static int validate_delay_schedule(const struct delay_schedule_entry_t + delay_schedule[PW_SCHED_COUNT]) +{ + size_t x; + + /* The first entry should not be useless. */ + if (delay_schedule[0].time_diff.v == 0) + return PW_ERR_DELAY_SCHEDULE_INVALID; + + for (x = PW_SCHED_COUNT - 1; x > 0; --x) { + if (delay_schedule[x].attempt_count.v == 0) { + if (delay_schedule[x].time_diff.v != 0) + return PW_ERR_DELAY_SCHEDULE_INVALID; + } else if (delay_schedule[x].attempt_count.v <= + delay_schedule[x - 1].attempt_count.v || + delay_schedule[x].time_diff.v <= + delay_schedule[x - 1].time_diff.v) { + return PW_ERR_DELAY_SCHEDULE_INVALID; + } + } + return EC_SUCCESS; +} + +static int validate_leaf_header(const struct leaf_header_t *head, + uint16_t payload_len, uint16_t aux_hash_len) +{ + uint32_t leaf_payload_len = head->pub_len + head->sec_len; + + if (head->leaf_version.major != PW_LEAF_MAJOR_VERSION) + return PW_ERR_LEAF_VERSION_MISMATCH; + + if (head->leaf_version.minor == PW_LEAF_MINOR_VERSION) { + if (leaf_payload_len != PW_LEAF_PAYLOAD_SIZE) + return PW_ERR_LENGTH_INVALID; + } else if (leaf_payload_len < PW_LEAF_PAYLOAD_SIZE) + return PW_ERR_LENGTH_INVALID; + + if (payload_len != leaf_payload_len + aux_hash_len * PW_HASH_SIZE) + return PW_ERR_LENGTH_INVALID; + + return EC_SUCCESS; +} + +/* Common validation for requests that include a path to authenticate. */ +static int validate_request_with_path(const struct merkle_tree_t *merkle_tree, + struct label_t path, + const uint8_t hashes[][PW_HASH_SIZE], + const uint8_t hmac[PW_HASH_SIZE]) +{ + int ret; + + ret = validate_tree(merkle_tree); + if (ret != EC_SUCCESS) + return ret; + + ret = validate_label(merkle_tree, path); + if (ret != EC_SUCCESS) + return ret; + + return authenticate_path(merkle_tree, path, hashes, hmac); +} + +/* Common validation for requests that import a leaf. */ +static int validate_request_with_wrapped_leaf( + const struct merkle_tree_t *merkle_tree, + uint16_t payload_len, + const struct unimported_leaf_data_t *unimported_leaf_data, + struct imported_leaf_data_t *imported_leaf_data, + struct leaf_data_t *leaf_data) +{ + int ret; + uint8_t hmac[PW_HASH_SIZE]; + + ret = validate_leaf_header(&unimported_leaf_data->head, payload_len, + get_path_auxiliary_hash_count(merkle_tree)); + if (ret != EC_SUCCESS) + return ret; + + import_leaf(unimported_leaf_data, imported_leaf_data); + ret = validate_request_with_path(merkle_tree, + imported_leaf_data->pub->label, + imported_leaf_data->hashes, + imported_leaf_data->hmac); + if (ret != EC_SUCCESS) + return ret; + + compute_hmac(merkle_tree, imported_leaf_data, hmac); + /* Safe memcmp is used here to prevent an attacker from being able to + * brute force a valid HMAC for a crafted wrapped_leaf_data. + * memcmp provides an attacker a timing side-channel they can use to + * determine how much of a prefix is correct. + */ + if (safe_memcmp(hmac, unimported_leaf_data->hmac, sizeof(hmac))) + return PW_ERR_HMAC_AUTH_FAILED; + + return decrypt_leaf_data(merkle_tree, imported_leaf_data, leaf_data); +} + +/* Sets the value of ts to the current notion of time. */ +static void update_timestamp(struct pw_timestamp_t *ts) +{ + ts->timer_value = get_time().val / SECOND; + ts->boot_count = get_restart_count(); +} + +/* Checks if an auth attempt can be made or not based on the delay schedule. + * EC_SUCCESS is returned when a new attempt can be made otherwise + * seconds_to_wait will be updated with the remaining wait time required. + */ +static int test_rate_limit(struct leaf_data_t *leaf_data, + struct time_diff_t *seconds_to_wait) +{ + uint64_t ready_time; + uint8_t x; + struct pw_timestamp_t current_time; + struct time_diff_t delay = {0}; + + /* This loop ends when x is one greater than the index that applies. */ + for (x = 0; x < ARRAY_SIZE(leaf_data->pub.delay_schedule); ++x) { + /* Stop if a null entry is reached. The first part of the delay + * schedule has a list of increasing (attempt_count, time_diff) + * pairs with any unused entries zeroed out at the end. + */ + if (leaf_data->pub.delay_schedule[x].attempt_count.v == 0) + break; + + /* Stop once a delay schedule entry is reached whose + * threshold is greater than the current number of + * attempts. + */ + if (leaf_data->pub.attempt_count.v < + leaf_data->pub.delay_schedule[x].attempt_count.v) + break; + } + + /* If the first threshold was greater than the current number of + * attempts, there is no delay. Otherwise, grab the delay from the + * entry prior to the one that was too big. + */ + if (x > 0) + delay = leaf_data->pub.delay_schedule[x - 1].time_diff; + + if (delay.v == 0) + return EC_SUCCESS; + + if (delay.v == PW_BLOCK_ATTEMPTS) { + seconds_to_wait->v = PW_BLOCK_ATTEMPTS; + return PW_ERR_RATE_LIMIT_REACHED; + } + + update_timestamp(¤t_time); + + if (leaf_data->pub.timestamp.boot_count == current_time.boot_count) + ready_time = delay.v + leaf_data->pub.timestamp.timer_value; + else + ready_time = delay.v; + + if (current_time.timer_value >= ready_time) + return EC_SUCCESS; + + seconds_to_wait->v = ready_time - current_time.timer_value; + return PW_ERR_RATE_LIMIT_REACHED; +} + +/******************************************************************************/ +/* Per-request-type handler implementations. + */ + +static int pw_handle_reset_tree(struct merkle_tree_t *merkle_tree, + const struct pw_request_reset_tree_t *request, + uint16_t req_size) +{ + struct merkle_tree_t new_tree = {}; + int ret; + + if (req_size != sizeof(*request)) + return PW_ERR_LENGTH_INVALID; + + ret = validate_tree_parameters(request->bits_per_level, + request->height); + if (ret != EC_SUCCESS) + return ret; + + ret = create_merkle_tree(request->bits_per_level, request->height, + &new_tree); + if (ret != EC_SUCCESS) + return ret; + + memcpy(merkle_tree, &new_tree, sizeof(new_tree)); + return EC_SUCCESS; +} + +static int pw_handle_insert_leaf(struct merkle_tree_t *merkle_tree, + const struct pw_request_insert_leaf_t *request, + uint16_t req_size, + struct pw_response_insert_leaf_t *response, + uint16_t *response_size) +{ + int ret = EC_SUCCESS; + struct leaf_data_t leaf_data = {}; + struct wrapped_leaf_data_t wrapped_leaf_data; + const uint8_t empty_hash[PW_HASH_SIZE] = {}; + uint8_t new_root[PW_HASH_SIZE]; + + if (req_size != sizeof(*request) + + get_path_auxiliary_hash_count(merkle_tree) * + PW_HASH_SIZE) + return PW_ERR_LENGTH_INVALID; + + ret = validate_request_with_path(merkle_tree, request->label, + request->path_hashes, empty_hash); + if (ret != EC_SUCCESS) + return ret; + + ret = validate_delay_schedule(request->delay_schedule); + if (ret != EC_SUCCESS) + return ret; + + memset(&leaf_data, 0, sizeof(leaf_data)); + leaf_data.pub.label.v = request->label.v; + memcpy(&leaf_data.pub.delay_schedule, &request->delay_schedule, + sizeof(request->delay_schedule)); + memcpy(&leaf_data.sec.low_entropy_secret, &request->low_entropy_secret, + sizeof(request->low_entropy_secret)); + memcpy(&leaf_data.sec.high_entropy_secret, + &request->high_entropy_secret, + sizeof(request->high_entropy_secret)); + memcpy(&leaf_data.sec.reset_secret, &request->reset_secret, + sizeof(request->reset_secret)); + + ret = handle_leaf_update(merkle_tree, &leaf_data, request->path_hashes, + &wrapped_leaf_data, new_root, NULL); + if (ret != EC_SUCCESS) + return ret; + + memcpy(merkle_tree->root, new_root, sizeof(new_root)); + + memcpy(&response->unimported_leaf_data, &wrapped_leaf_data, + sizeof(wrapped_leaf_data)); + + *response_size = sizeof(*response) + PW_LEAF_PAYLOAD_SIZE; + + return ret; +} + +static int pw_handle_remove_leaf(struct merkle_tree_t *merkle_tree, + const struct pw_request_remove_leaf_t *request, + uint16_t req_size) +{ + int ret = EC_SUCCESS; + const uint8_t empty_hash[PW_HASH_SIZE] = {}; + uint8_t new_root[PW_HASH_SIZE]; + + if (req_size != sizeof(*request) + + get_path_auxiliary_hash_count(merkle_tree) * + PW_HASH_SIZE) + return PW_ERR_LENGTH_INVALID; + + ret = validate_request_with_path(merkle_tree, request->leaf_location, + request->path_hashes, + request->leaf_hmac); + if (ret != EC_SUCCESS) + return ret; + + compute_root_hash(merkle_tree, request->leaf_location, + request->path_hashes, empty_hash, new_root); + + memcpy(merkle_tree->root, new_root, sizeof(new_root)); + + return ret; +} + +/* Processes a try_auth request. + * + * The valid fields in response based on return code are: + * EC_SUCCESS -> unimported_leaf_data and high_entropy_secret + * PW_ERR_RATE_LIMIT_REACHED -> seconds_to_wait + * PW_ERR_LOWENT_AUTH_FAILED -> unimported_leaf_data + */ +static int pw_handle_try_auth(struct merkle_tree_t *merkle_tree, + const struct pw_request_try_auth_t *request, + uint16_t req_size, + struct pw_response_try_auth_t *response, + uint16_t *data_length) +{ + int ret = EC_SUCCESS; + struct leaf_data_t leaf_data = {}; + struct imported_leaf_data_t imported_leaf_data; + struct wrapped_leaf_data_t wrapped_leaf_data; + struct time_diff_t seconds_to_wait; + uint8_t zeros[PW_SECRET_SIZE] = {}; + uint8_t new_root[PW_HASH_SIZE]; + + /* These variables help eliminate the possibility of a timing side + * channel that would allow an attacker to prevent the log write. + */ + volatile int auth_result; + + volatile struct { + uint32_t attempts; + int ret; + uint8_t *secret; + } results_table[2] = { + { 0, PW_ERR_LOWENT_AUTH_FAILED, zeros }, + { 0, EC_SUCCESS, leaf_data.sec.high_entropy_secret }, + }; + + if (req_size < sizeof(*request)) + return PW_ERR_LENGTH_INVALID; + + ret = validate_request_with_wrapped_leaf( + merkle_tree, req_size - sizeof(*request), + &request->unimported_leaf_data, &imported_leaf_data, + &leaf_data); + if (ret != EC_SUCCESS) + return ret; + + ret = test_rate_limit(&leaf_data, &seconds_to_wait); + if (ret != EC_SUCCESS) { + *data_length = sizeof(*response) + PW_LEAF_PAYLOAD_SIZE; + memset(response, 0, *data_length); + memcpy(&response->seconds_to_wait, &seconds_to_wait, + sizeof(seconds_to_wait)); + return ret; + } + + update_timestamp(&leaf_data.pub.timestamp); + + /* Precompute the failed attempts. */ + results_table[0].attempts = leaf_data.pub.attempt_count.v; + if (results_table[0].attempts != UINT32_MAX) + ++results_table[0].attempts; + + /**********************************************************************/ + /* After this: + * 1) results_table should not be changed; + * 2) the runtime of the code paths for failed and successful + * authentication attempts should not diverge. + */ + auth_result = safe_memcmp(request->low_entropy_secret, + leaf_data.sec.low_entropy_secret, + sizeof(request->low_entropy_secret)) == 0; + leaf_data.pub.attempt_count.v = results_table[auth_result].attempts; + + /* This has a non-constant time path, but it doesn't convey information + * about whether a PW_ERR_LOWENT_AUTH_FAILED happened or not. + */ + ret = handle_leaf_update(merkle_tree, &leaf_data, + imported_leaf_data.hashes, &wrapped_leaf_data, + new_root, &imported_leaf_data); + if (ret != EC_SUCCESS) + return ret; + + memcpy(merkle_tree->root, new_root, sizeof(new_root)); + + *data_length = sizeof(*response) + PW_LEAF_PAYLOAD_SIZE; + memset(response, 0, *data_length); + + memcpy(&response->unimported_leaf_data, &wrapped_leaf_data, + sizeof(wrapped_leaf_data)); + + memcpy(&response->high_entropy_secret, + results_table[auth_result].secret, + sizeof(response->high_entropy_secret)); + + return results_table[auth_result].ret; +} + +static int pw_handle_reset_auth(struct merkle_tree_t *merkle_tree, + const struct pw_request_reset_auth_t *request, + uint16_t req_size, + struct pw_response_reset_auth_t *response, + uint16_t *response_size) +{ + int ret = EC_SUCCESS; + struct leaf_data_t leaf_data = {}; + struct imported_leaf_data_t imported_leaf_data; + struct wrapped_leaf_data_t wrapped_leaf_data; + uint8_t new_root[PW_HASH_SIZE]; + + if (req_size < sizeof(*request)) + return PW_ERR_LENGTH_INVALID; + + ret = validate_request_with_wrapped_leaf( + merkle_tree, req_size - sizeof(*request), + &request->unimported_leaf_data, &imported_leaf_data, + &leaf_data); + if (ret != EC_SUCCESS) + return ret; + + /* Safe memcmp is used here to prevent an attacker from being able to + * brute force the reset secret and use it to unlock the leaf. + * memcmp provides an attacker a timing side-channel they can use to + * determine how much of a prefix is correct. + */ + if (safe_memcmp(request->reset_secret, + leaf_data.sec.reset_secret, + sizeof(request->reset_secret)) != 0) + return PW_ERR_RESET_AUTH_FAILED; + + leaf_data.pub.attempt_count.v = 0; + + ret = handle_leaf_update(merkle_tree, &leaf_data, + imported_leaf_data.hashes, &wrapped_leaf_data, + new_root, &imported_leaf_data); + if (ret != EC_SUCCESS) + return ret; + + memcpy(merkle_tree->root, new_root, sizeof(new_root)); + + memcpy(&response->unimported_leaf_data, &wrapped_leaf_data, + sizeof(wrapped_leaf_data)); + + memcpy(response->high_entropy_secret, + leaf_data.sec.high_entropy_secret, + sizeof(response->high_entropy_secret)); + + *response_size = sizeof(*response) + PW_LEAF_PAYLOAD_SIZE; + + return ret; +} + +/******************************************************************************/ +/* Non-static functions. + */ + +int get_path_auxiliary_hash_count(const struct merkle_tree_t *merkle_tree) +{ + return ((1 << merkle_tree->bits_per_level.v) - 1) * + merkle_tree->height.v; +} + +/* Computes the SHA256 parent hash of a set of child hashes given num_hashes + * sibling hashes in hashes[] and the index of child_hash. + * + * Assumptions: + * num_hashes == fan_out - 1 + * ARRAY_SIZE(hashes) == num_hashes + * 0 <= location <= num_hashes + */ +void compute_hash(const uint8_t hashes[][PW_HASH_SIZE], uint16_t num_hashes, + struct index_t location, + const uint8_t child_hash[PW_HASH_SIZE], + uint8_t result[PW_HASH_SIZE]) +{ + LITE_SHA256_CTX ctx; + + DCRYPTO_SHA256_init(&ctx, 0); + if (location.v > 0) + HASH_update(&ctx, hashes[0], PW_HASH_SIZE * location.v); + HASH_update(&ctx, child_hash, PW_HASH_SIZE); + if (location.v < num_hashes) + HASH_update(&ctx, hashes[location.v], + PW_HASH_SIZE * (num_hashes - location.v)); + memcpy(result, HASH_final(&ctx), PW_HASH_SIZE); +} + +/* Handles the message in request using the context in merkle_tree and writes + * the results to response. The return value captures any error conditions that + * occurred or EC_SUCCESS if there were no errors. + * + * This implementation is written to handle the case where request and response + * exist at the same memory location---are backed by the same buffer. This means + * the implementation requires that no reads are made to request after response + * has been written to. + */ +int pw_handle_request(struct merkle_tree_t *merkle_tree, + const struct pw_request_t *request, + struct pw_response_t *response) +{ + int32_t ret; + uint16_t resp_length; + /* Store the message type of the request since it may be overwritten + * inside the switch whenever response and request overlap in memory. + */ + struct pw_message_type_t type = request->header.type; + + resp_length = 0; + + if (request->header.version != PW_PROTOCOL_VERSION) { + ret = PW_ERR_VERSION_MISMATCH; + goto cleanup; + } + + switch (type.v) { + case PW_RESET_TREE: + ret = pw_handle_reset_tree(merkle_tree, + &request->data.reset_tree, + request->header.data_length); + break; + case PW_INSERT_LEAF: + ret = pw_handle_insert_leaf(merkle_tree, + &request->data.insert_leaf, + request->header.data_length, + &response->data.insert_leaf, + &resp_length); + break; + case PW_REMOVE_LEAF: + ret = pw_handle_remove_leaf(merkle_tree, + &request->data.remove_leaf, + request->header.data_length); + break; + case PW_TRY_AUTH: + ret = pw_handle_try_auth(merkle_tree, &request->data.try_auth, + request->header.data_length, + &response->data.try_auth, + &resp_length); + break; + case PW_RESET_AUTH: + ret = pw_handle_reset_auth(merkle_tree, + &request->data.reset_auth, + request->header.data_length, + &response->data.reset_auth, + &resp_length); + break; + default: + ret = PW_ERR_TYPE_INVALID; + break; + } +cleanup: + response->header.version = PW_PROTOCOL_VERSION; + response->header.data_length = resp_length; + response->header.result_code = ret; + memcpy(&response->header.root, merkle_tree->root, + sizeof(merkle_tree->root)); + return ret; +}; diff --git a/include/config.h b/include/config.h index 0dc58f6f98..03d03b1799 100644 --- a/include/config.h +++ b/include/config.h @@ -2148,6 +2148,13 @@ /* Enable (unsafe!) developer debug features for physical presence */ #undef CONFIG_PHYSICAL_PRESENCE_DEBUG_UNSAFE +/*****************************************************************************/ +/* PinWeaver config + * A feature which exchanges a low entropy secret with rate limits for a high + * entropy secret. This enables a set of vendor specific commands for Cr50. + */ +#undef CONFIG_PINWEAVER + /*****************************************************************************/ /* PMU config */ diff --git a/include/pinweaver.h b/include/pinweaver.h new file mode 100644 index 0000000000..58210fa037 --- /dev/null +++ b/include/pinweaver.h @@ -0,0 +1,142 @@ +/* Copyright 2018 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef __CROS_EC_INCLUDE_PINWEAVER_H +#define __CROS_EC_INCLUDE_PINWEAVER_H + +/* This is required before pinweaver_types.h to provide __packed and __aligned + * while preserving the ability of pinweaver_types.h to be used in code outside + * of src/platform/ec. + */ +#include +#include + +#define PW_STORAGE_VERSION 0 + +#define BITS_PER_LEVEL_MIN 1 +#define BITS_PER_LEVEL_MAX 5 +#define HEIGHT_MIN 1 +/* This will crash for logk == 0 so that condition must not be allowed when + * using this. + */ +#define HEIGHT_MAX(logk) ((sizeof(struct label_t) * 8) / logk) + +/* Persistent information used by this feature. */ +struct merkle_tree_t { + /* 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; + + /* Root hash of the Merkle tree. */ + uint8_t root[PW_HASH_SIZE]; + + /* Random bits used as part of the key derivation process. */ + uint8_t key_derivation_nonce[16]; + + /* Key used to compute the HMACs of the metadata of the leaves. */ + uint8_t PW_ALIGN_TO_WRD hmac_key[32]; + + /* Key used to encrypt and decrypt the metadata of the leaves. */ + uint8_t PW_ALIGN_TO_WRD wrap_key[32]; +}; + +/* Do not remove fields within the same PW_LEAF_MAJOR_VERSION. */ +/* 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]; + + /* State used to rate limit. */ + struct pw_timestamp_t timestamp; + struct attempt_count_t attempt_count; +}; + +/* Do not remove fields within the same PW_LEAF_MAJOR_VERSION. */ +/* 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]; + uint8_t reset_secret[PW_SECRET_SIZE]; +}; + +/* Represents leaf data in a form that can be exported for storage. */ +struct PW_PACKED wrapped_leaf_data_t { + /* This is first so that head.leaf_version will be the first field + * in the struct to keep the meaning of the struct from becoming + * ambiguous across versions. + */ + struct leaf_header_t head; + /* Covers .head, .pub, and .cipher_text. */ + uint8_t hmac[PW_HASH_SIZE]; + uint8_t iv[PW_WRAP_BLOCK_SIZE]; + struct leaf_public_data_t pub; + uint8_t cipher_text[sizeof(struct leaf_sensitive_data_t)]; +}; + +/* Represents encrypted leaf data after the lengths and version in the header + * have been validated. + */ +struct imported_leaf_data_t { + /* This is first so that head.leaf_version will be the first field + * in the struct to keep the meaning of the struct from becoming + * ambiguous across versions. + */ + const struct leaf_header_t *head; + /* Covers .head, .pub, and .cipher_text. */ + const uint8_t *hmac; + const uint8_t *iv; + const struct leaf_public_data_t *pub; + const uint8_t *cipher_text; + const uint8_t (*hashes)[PW_HASH_SIZE]; +}; + +/* The leaf data in a clear text working format. */ +struct leaf_data_t { + struct leaf_public_data_t pub; + struct leaf_sensitive_data_t sec; +}; + +/* Handler for incoming messages after they have been reconstructed. + * + * merkle_tree->root needs to be updated with new_root outside of this function. + */ +int pw_handle_request(struct merkle_tree_t *merkle_tree, + const struct pw_request_t *request, + struct pw_response_t *response); + +/******************************************************************************/ +/* Struct helper functions. + */ + +/* Sets up pointers to the relevant fields inside an wrapped leaf based on the + * length fields in the header. These fields should be validated prior to + * calling this function. + */ +void import_leaf(const struct unimported_leaf_data_t *unimported, + struct imported_leaf_data_t *imported); + +/* Calculate how much is needed to add to the size of structs containing + * an struct unimported_leaf_data_t because the variable length fields at the + * end of the struct are not included by sizeof(). + */ +#define PW_LEAF_PAYLOAD_SIZE (sizeof(struct wrapped_leaf_data_t) - \ + sizeof(struct unimported_leaf_data_t)) + + +/******************************************************************************/ +/* Utility functions exported for better test coverage. + */ + +/* Computes the total number of the sibling hashes along a path. */ +int get_path_auxiliary_hash_count(const struct merkle_tree_t *merkle_tree); + +/* Computes the parent hash for an array of child hashes. */ +void compute_hash(const uint8_t hashes[][PW_HASH_SIZE], uint16_t num_hashes, + struct index_t location, + const uint8_t child_hash[PW_HASH_SIZE], + uint8_t result[PW_HASH_SIZE]); + +#endif /* __CROS_EC_INCLUDE_PINWEAVER_H */ diff --git a/include/pinweaver_tpm_imports.h b/include/pinweaver_tpm_imports.h new file mode 100644 index 0000000000..be882593d6 --- /dev/null +++ b/include/pinweaver_tpm_imports.h @@ -0,0 +1,27 @@ +/* Copyright 2018 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* Compatibility layer between the TPM code and PinWeaver. + * + * This is needed because the headers for the TPM are not compatible with the + * headers used by pinweaver.c. It also makes it easier to mock the + * functionality derived from the TPM code. + */ + +#ifndef __CROS_EC_INCLUDE_PINWEAVER_TPM_IMPORTS_H +#define __CROS_EC_INCLUDE_PINWEAVER_TPM_IMPORTS_H + +#include +#include + +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. + */ +void get_storage_seed(void *buf, size_t *len); + +#endif /* __CROS_EC_INCLUDE_PINWEAVER_TPM_IMPORTS_H */ diff --git a/include/pinweaver_types.h b/include/pinweaver_types.h new file mode 100644 index 0000000000..9fbb801e81 --- /dev/null +++ b/include/pinweaver_types.h @@ -0,0 +1,289 @@ +/* Copyright 2018 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* Shared types between Cr50 and the AP side code. */ + +#ifndef __CROS_EC_INCLUDE_PINWEAVER_TYPES_H +#define __CROS_EC_INCLUDE_PINWEAVER_TYPES_H + +#include + +#define PW_PACKED __packed + +#define PW_PROTOCOL_VERSION 0 +#define PW_LEAF_MAJOR_VERSION 0 +#define PW_LEAF_MINOR_VERSION 0 + +#define PW_MAX_MESSAGE_SIZE (2048 - 12 /* sizeof(struct tpm_cmd_header) */) + +/* The block size of encryption used for wrapped_leaf_data_t. */ +#define PW_WRAP_BLOCK_SIZE 16 + +#define PW_ALIGN_TO_WRD __aligned(4) + +#define PW_ALIGN_TO_BLK __aligned(PW_WRAP_BLOCK_SIZE) + +enum pw_error_codes_enum { + PW_ERR_VERSION_MISMATCH = 0x10000, /* EC_ERROR_INTERNAL_FIRST */ + PW_ERR_TREE_INVALID, + PW_ERR_LENGTH_INVALID, + PW_ERR_TYPE_INVALID, + PW_ERR_BITS_PER_LEVEL_INVALID, + PW_ERR_HEIGHT_INVALID, + PW_ERR_LABEL_INVALID, + PW_ERR_DELAY_SCHEDULE_INVALID, + PW_ERR_PATH_AUTH_FAILED, + PW_ERR_LEAF_VERSION_MISMATCH, + PW_ERR_HMAC_AUTH_FAILED, + PW_ERR_LOWENT_AUTH_FAILED, + PW_ERR_RESET_AUTH_FAILED, + PW_ERR_CRYPTO_FAILURE, + PW_ERR_RATE_LIMIT_REACHED, +}; + +/* Represents the log2(fan out) of a tree. */ +struct PW_PACKED bits_per_level_t { + uint8_t v; +}; + + /* Represent the height of a tree. */ +struct PW_PACKED height_t { + uint8_t v; +}; + +/* Represents a child index of a node in a tree. */ +struct PW_PACKED index_t { + uint8_t v; +}; + +/* Represents the child index for each level of a tree along a path to a leaf. + * It is a Little-endian unsigned integer with the following value (MSB->LSB) + * | Zero padding | 1st level index | ... | leaf index |, + * where each index is represented by bits_per_level bits. + */ +struct PW_PACKED label_t { + uint64_t v; +}; + +/* Represents a count of failed login attempts. This is capped at UINT32_MAX. */ +struct PW_PACKED attempt_count_t { + uint32_t v; +}; + +/* Represents a notion of time. */ +struct PW_PACKED pw_timestamp_t { + /* Number of boots. This is used to track if Cr50 has rebooted since + * timer_value was recorded. + */ + uint32_t boot_count; + /* Seconds since boot. */ + uint64_t timer_value; +}; + +/* Represents a time interval in seconds. + * + * This only needs to be sufficiently large to represent the longest time + * between allowed attempts. + */ +struct PW_PACKED time_diff_t { + uint32_t v; +}; +#define PW_BLOCK_ATTEMPTS UINT32_MAX + +/* Number of bytes required for a hash or hmac value in the merkle tree. */ +#define PW_HASH_SIZE 32 + +/* Represents a single entry in a delay schedule table. */ +struct PW_PACKED delay_schedule_entry_t { + struct attempt_count_t attempt_count; + struct time_diff_t time_diff; +}; + +/* Represents the number of entries in the delay schedule table which can be + * used to determine the next time an authentication attempt can be made. + */ +#define PW_SCHED_COUNT 16 + +/* Number of bytes required to store a secret. + */ +#define PW_SECRET_SIZE 32 + +struct PW_PACKED leaf_version_t { + /* minor comes first so this struct will be compatibile with uint32_t + * comparisons for little endian to make version comparisons easier. + * + * Changes to minor versions are allowed to add new fields, but not + * remove existing fields, and they are allowed to be interpreted by + * previous versions---any extra fields are truncated. + * + * Leafs will reject future major versions assuming they are + * incompatible, so fields in struct leaf_public_data_t and + * struct leaf_sensitive_data_t may be removed for new major versions. + * Upgrades across major versions will require explicit logic to + * map the old struct to the new struct or vice versa. + */ + uint16_t minor; + uint16_t major; +}; + +/* Do not change this within the same PW_LEAF_MAJOR_VERSION. */ +struct PW_PACKED leaf_header_t { + /* Always have leaf_version at the beginning of + * struct wrapped_leaf_data_t to maintain preditable behavior across + * versions. + */ + struct leaf_version_t leaf_version; + uint16_t pub_len; + uint16_t sec_len; +}; + +/* Represents a struct of unknown length to be imported to process a request. */ +struct PW_PACKED unimported_leaf_data_t { + /* This is first so that head.leaf_version will be the first field + * in the struct to make handling different struct versions easier. + */ + struct leaf_header_t head; + /* Covers .head, .iv, and .payload (excluding path_hashes) */ + uint8_t hmac[PW_HASH_SIZE]; + uint8_t iv[PW_WRAP_BLOCK_SIZE]; + /* This field is treated as having a zero size by the compiler so the + * actual size needs to be added to the size of this struct. This allows + * for forward compatibility using the pub_len and sec_len fields in the + * header. + * + * Has following layout: + * Required: + * uint8_t pub_data[head.pub_len]; + * uint8_t ciphter_text[head.sec_len]; + * + * For Requests only: + * uint8_t path_hashes[get_path_auxiliary_hash_count(.)][PW_HASH_SIZE]; + */ + uint8_t payload[]; +}; + +/******************************************************************************/ +/* Message structs + * + * The message format is a pw_request_header_t followed by the data + */ + +enum pw_message_type_enum { + PW_MT_INVALID = 0, + + /* Request / "Question" types. */ + PW_RESET_TREE = 1, + PW_INSERT_LEAF, + PW_REMOVE_LEAF, + PW_TRY_AUTH, + PW_RESET_AUTH, +}; + +struct PW_PACKED pw_message_type_t { + uint8_t v; +}; + +struct PW_PACKED pw_request_header_t { + uint8_t version; + struct pw_message_type_t type; + uint16_t data_length; +}; + +struct PW_PACKED pw_response_header_t { + uint8_t version; + uint16_t data_length; /* Does not include the header. */ + uint32_t result_code; + uint8_t root[PW_HASH_SIZE]; +}; + +struct PW_PACKED pw_request_reset_tree_t { + struct bits_per_level_t bits_per_level; + struct height_t height; +}; + +struct PW_PACKED pw_request_insert_leaf_t { + struct label_t label; + struct delay_schedule_entry_t delay_schedule[PW_SCHED_COUNT]; + uint8_t low_entropy_secret[PW_SECRET_SIZE]; + uint8_t high_entropy_secret[PW_SECRET_SIZE]; + uint8_t reset_secret[PW_SECRET_SIZE]; + /* This is a variable length field because it size is determined at + * runtime based on the chosen tree parameters. Its size is treated as + * zero by the compiler so the computed size needs to be added to the + * size of this struct in order to determine the actual size. This field + * has the form: + * uint8_t path_hashes[get_path_auxiliary_hash_count(.)][PW_HASH_SIZE]; + */ + uint8_t path_hashes[][PW_HASH_SIZE]; +}; + +struct PW_PACKED pw_response_insert_leaf_t { + struct unimported_leaf_data_t unimported_leaf_data; +}; + +struct PW_PACKED pw_request_remove_leaf_t { + struct label_t leaf_location; + uint8_t leaf_hmac[PW_HASH_SIZE]; + /* See (struct pw_request_insert_leaf_t).path_hashes. */ + uint8_t path_hashes[][PW_HASH_SIZE]; +}; + +struct PW_PACKED pw_request_try_auth_t { + uint8_t low_entropy_secret[PW_SECRET_SIZE]; + struct unimported_leaf_data_t unimported_leaf_data; +}; + +struct PW_PACKED pw_response_try_auth_t { + /* Valid for the PW_ERR_RATE_LIMIT_REACHED return code only. */ + struct time_diff_t seconds_to_wait; + /* Valid for the EC_SUCCESS return code only. */ + uint8_t high_entropy_secret[PW_SECRET_SIZE]; + /* Valid for the PW_ERR_LOWENT_AUTH_FAILED and EC_SUCCESS return codes. + */ + struct unimported_leaf_data_t unimported_leaf_data; +}; + +struct PW_PACKED pw_request_reset_auth_t { + uint8_t reset_secret[PW_SECRET_SIZE]; + struct unimported_leaf_data_t unimported_leaf_data; +}; + +struct PW_PACKED pw_response_reset_auth_t { + uint8_t high_entropy_secret[PW_SECRET_SIZE]; + struct unimported_leaf_data_t unimported_leaf_data; +}; + +struct PW_PACKED pw_request_t { + struct pw_request_header_t header; + union { + struct pw_request_reset_tree_t reset_tree; + 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; + } data; +}; + +struct PW_PACKED pw_response_t { + struct pw_response_header_t header; + 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; + } data; +}; + +/* An explicit limit is set because struct unimported_leaf_data_t can have more + * than one variable length field so the max length for these fields needs to be + * defined so that meaningful parameter limits can be set to validate the tree + * parameters. + * + * 1536 was chosen because it is 3/4 of 2048 and allows for a maximum tree + * height of 16 for the default fan-out of 4. + */ +#define PW_MAX_PATH_SIZE 1536 + +#endif /* __CROS_EC_INCLUDE_PINWEAVER_TYPES_H */ diff --git a/test/build.mk b/test/build.mk index 2bcd163655..449c0773a1 100644 --- a/test/build.mk +++ b/test/build.mk @@ -59,6 +59,7 @@ test-list-host += mutex test-list-host += nvmem test-list-host += nvmem_vars test-list-host += pingpong +test-list-host += pinweaver test-list-host += power_button test-list-host += queue test-list-host += rma_auth @@ -111,6 +112,7 @@ mutex-y=mutex.o nvmem-y=nvmem.o nvmem_vars-y=nvmem_vars.o pingpong-y=pingpong.o +pinweaver-y=pinweaver.o power_button-y=power_button.o powerdemo-y=powerdemo.o queue-y=queue.o diff --git a/test/pinweaver.c b/test/pinweaver.c new file mode 100644 index 0000000000..16c40ec9aa --- /dev/null +++ b/test/pinweaver.c @@ -0,0 +1,1739 @@ +/* Copyright 2018 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "test_util.h" + +struct pw_test_data_t { + union { + struct pw_request_t request; + struct pw_response_t response; + /* Reserve space for the variable length fields. */ + uint8_t tpm_buffer_size[PW_MAX_MESSAGE_SIZE]; + }; +}; + +/******************************************************************************/ +/* Test data + */ +const int EMPTY_TREE_PATH_LENGTH = 18; +const struct merkle_tree_t EMPTY_TREE = { + {2} /* bits_per_level */, + {6} /* height */, + /* root */ + {0x81, 0xaa, 0xe9, 0xde, 0x93, 0xf4, 0xdf, 0x88, + 0x18, 0xfa, 0xff, 0xbd, 0xb7, 0x09, 0xc0, 0x86, + 0x48, 0xdd, 0xcd, 0x35, 0x00, 0xf2, 0x88, 0xd6, + 0x3f, 0xa6, 0x5e, 0x80, 0x10, 0x19, 0x41, 0x17}, + /* key derivation nonce. */ + {0x75, 0xf8, 0x43, 0xf7, 0x23, 0xbd, 0x2a, 0x0f, + 0x8d, 0x34, 0xbf, 0xa6, 0x6d, 0xf9, 0x44, 0x38}, + /* hmac_key */ + {0x96, 0xc6, 0xb1, 0x64, 0xb6, 0xa7, 0xa8, 0x01, + 0xd5, 0x1d, 0x8e, 0x97, 0x24, 0x86, 0xf8, 0x6f, + 0xd4, 0x84, 0x0f, 0x95, 0x52, 0x93, 0x8d, 0x7d, + 0x00, 0xbb, 0xba, 0xc8, 0xed, 0x7f, 0xa4, 0x7a}, + /* wrap_key */ + {0x95, 0xc9, 0x0a, 0xd4, 0xb3, 0x61, 0x1b, 0xcf, + 0x1b, 0x49, 0x2b, 0xd6, 0x5d, 0xbc, 0x80, 0xa9, + 0xf4, 0x83, 0xf2, 0x84, 0xd4, 0x04, 0x57, 0x7f, + 0x02, 0xae, 0x37, 0x64, 0xae, 0xda, 0x71, 0x2a}, +}; + +const struct leaf_data_t DEFAULT_LEAF = { + /*pub*/ + { + /* label = {0, 1, 2, 3, 0, 1} */ + {0x1b1llu}, + /* delay_schedule */ + {{{5}, {20} }, {{6}, {60} }, {{7}, {300} }, {{8}, {600} }, + {{9}, {1800} }, {{10}, {3600} }, {{50}, {PW_BLOCK_ATTEMPTS} }, + {{0}, {0} }, + {{0}, {0} }, {{0}, {0} }, {{0}, {0} }, {{0}, {0} }, + {{0}, {0} }, {{0}, {0} }, {{0}, {0} }, {{0}, {0} }, }, + /*timestamp*/ + {0, 0}, + /* attempt_count */ + {0}, + }, + /*sec*/ + { + /* low_entropy_secret */ + {0xba, 0xbc, 0x98, 0x9d, 0x97, 0x20, 0xcf, 0xea, + 0xaa, 0xbd, 0xb2, 0xe3, 0xe0, 0x2c, 0x5c, 0x55, + 0x06, 0x60, 0x93, 0xbd, 0x07, 0xe2, 0xba, 0x92, + 0x10, 0x19, 0x24, 0xb1, 0x29, 0x33, 0x5a, 0xe2}, + /* high_entropy_secret */ + {0xe3, 0x46, 0xe3, 0x62, 0x01, 0x5d, 0xfe, 0x0a, + 0xd3, 0x67, 0xd7, 0xef, 0xab, 0x01, 0xad, 0x0e, + 0x3a, 0xed, 0xe8, 0x2f, 0x99, 0xd1, 0x2d, 0x13, + 0x4d, 0x4e, 0xe4, 0x02, 0xbe, 0x71, 0x8e, 0x40}, + /* reset_secret */ + {0x8c, 0x33, 0x8c, 0xa7, 0x0f, 0x81, 0xa4, 0xee, + 0x24, 0xcd, 0x04, 0x84, 0x9c, 0xa8, 0xfd, 0xdd, + 0x14, 0xb0, 0xad, 0xe6, 0xb7, 0x6a, 0x10, 0xfc, + 0x03, 0x22, 0xcb, 0x71, 0x31, 0xd3, 0x74, 0xd6}, + }, +}; + +const struct leaf_header_t DEFAULT_HEAD = { + { + .minor = PW_LEAF_MINOR_VERSION, + .major = PW_LEAF_MAJOR_VERSION, + }, + sizeof(DEFAULT_LEAF.pub), + sizeof(DEFAULT_LEAF.sec), +}; + +const uint8_t DEFAULT_IV[] = { + 0xaa, 0x65, 0x97, 0xc7, 0x02, 0x23, 0xb8, 0xdc, + 0xb3, 0x55, 0xca, 0x3a, 0xab, 0xd0, 0x03, 0x90, +}; + +const uint8_t EMPTY_HMAC[32] = {}; + +const uint32_t DEFAULT_STORAGE_SEED[8] = { + 0xe9e9880b, 0xb2a9fa0e, 0x9dcf22af, 0xc40156d0, + 0xca8535dc, 0x748606ee, 0x68f0f627, 0x7df7558a, +}; + +/* This is not the actual hmac. */ +const uint8_t DEFAULT_HMAC[] = { + 0x87, 0x7e, 0xe2, 0xb2, 0x60, 0xeb, 0xf3, 0x4b, + 0x80, 0x3e, 0xca, 0xcb, 0xe6, 0x24, 0x21, 0x86, + 0xd9, 0xe3, 0x91, 0xf7, 0x2d, 0x16, 0x59, 0xd8, + 0x0f, 0x37, 0x0a, 0xf4, 0x64, 0x19, 0x44, 0xe7, +}; + +const uint8_t ROOT_WITH_DEFAULT_HMAC[] = { + 0x24, 0xad, 0xe4, 0xad, 0xf2, 0xdc, 0x40, 0x26, + 0x15, 0x03, 0x16, 0x6f, 0x3c, 0x32, 0x05, 0x99, + 0xf8, 0x25, 0x22, 0x92, 0xb9, 0xc7, 0xcd, 0x18, + 0x37, 0xc2, 0xf2, 0x72, 0x31, 0xdd, 0xc4, 0xaf, +}; + +/******************************************************************************/ +/* Config Variables and defines for Mocks. + */ + +uint32_t MOCK_restart_count; + +const uint8_t *MOCK_rand_bytes_src; +size_t MOCK_rand_bytes_offset; +size_t MOCK_rand_bytes_len; + +void (*MOCK_hash_update_cb)(const void *data, size_t len); +static void auth_hash_update_cb(const void *data, size_t len); + +const uint8_t *MOCK_hmac; +size_t MOCK_DECRYPTO_init_counter; +size_t MOCK_DECRYPTO_release_counter; + +#define MOCK_AES_XOR_BYTE(b) ((uint8_t)(0x77 + (b & 15))) +int MOCK_aes_fail; +int MOCK_appkey_derive_fail; +enum dcrypto_appid MOCK_hwctx_appkey; + +/******************************************************************************/ +/* Helper functions + */ +static int do_request(struct merkle_tree_t *merkle_tree, + struct pw_test_data_t *buf) +{ + int ret = pw_handle_request(merkle_tree, &buf->request, &buf->response); + size_t offset = buf->response.header.data_length + + sizeof(buf->response.header); + + /* Zero out bytes that won't be sent for testing.*/ + memset(buf->tpm_buffer_size + offset, 0, + sizeof(buf->tpm_buffer_size) - offset); + return ret; +} + +static const char *pw_error_str(int code) +{ + switch (code) { + case EC_SUCCESS: + return "EC_SUCCESS"; + case EC_ERROR_UNKNOWN: + return "EC_ERROR_UNKNOWN"; + case EC_ERROR_UNIMPLEMENTED: + return "EC_ERROR_UNIMPLEMENTED"; + case PW_ERR_VERSION_MISMATCH: + return "PW_ERR_VERSION_MISMATCH"; + case PW_ERR_LENGTH_INVALID: + return "PW_ERR_LENGTH_INVALID"; + case PW_ERR_TYPE_INVALID: + return "PW_ERR_TYPE_INVALID"; + case PW_ERR_BITS_PER_LEVEL_INVALID: + return "PW_ERR_BITS_PER_LEVEL_INVALID"; + case PW_ERR_HEIGHT_INVALID: + return "PW_ERR_HEIGHT_INVALID"; + case PW_ERR_LABEL_INVALID: + return "PW_ERR_LABEL_INVALID"; + case PW_ERR_DELAY_SCHEDULE_INVALID: + return "PW_ERR_DELAY_SCHEDULE_INVALID"; + case PW_ERR_PATH_AUTH_FAILED: + return "PW_ERR_PATH_AUTH_FAILED"; + case PW_ERR_LEAF_VERSION_MISMATCH: + return "PW_ERR_LEAF_VERSION_MISMATCH"; + case PW_ERR_HMAC_AUTH_FAILED: + return "PW_ERR_HMAC_AUTH_FAILED"; + case PW_ERR_LOWENT_AUTH_FAILED: + return "PW_ERR_LOWENT_AUTH_FAILED"; + case PW_ERR_RESET_AUTH_FAILED: + return "PW_ERR_RESET_AUTH_FAILED"; + case PW_ERR_CRYPTO_FAILURE: + return "PW_ERR_CRYPTO_FAILURE"; + case PW_ERR_RATE_LIMIT_REACHED: + return "PW_ERR_RATE_LIMIT_REACHED"; + default: + return "?"; + } +} + +/* Pinweaver specific return code check. This prints the string representation + * of the return code instead of just the number. + */ +#define TEST_RET_EQ(n, m) \ + do { \ + int val1 = n; \ + int val2 = m; \ + if (val1 != val2) { \ + ccprintf("%d: ASSERTION failed: %s (%d) != %s (%d)\n", \ + __LINE__, pw_error_str(val1), val1, \ + pw_error_str(val2), val2); \ + task_dump_trace(); \ + return EC_ERROR_UNKNOWN; \ + } \ + } while (0) + +/* Allows mock functions when that don't return success / failure to have + * assertions. + */ +#define TEST_ASRT_NORET(n) \ + do { \ + if (!(n)) { \ + int x = 0;\ + ccprintf("%d: ASSERTION failed: %s\n", __LINE__, #n); \ + task_dump_trace(); \ + x = 1 / x; \ + } \ + } while (0) + +/* For debugging and generating test data. */ +void print_array(const uint8_t *data, size_t n) __attribute__ ((unused)); +void print_array(const uint8_t *data, size_t n) +{ + size_t x; + + if (n > 0) { + ccprintf("uint8_t data[] = {"); + for (x = 0; x < n - 1; ++x) { + if ((x & 7) != 7) + ccprintf("0x%02x, ", data[x]); + else + ccprintf("0x%02x,\n", data[x]); + } + ccprintf("0x%02x};\n", data[x]); + } +} + +/* For exporting structs. This is useful for validating the results of crypto + * operations. + */ +void print_hex(const uint8_t *data, size_t n) __attribute__ ((unused)); +void print_hex(const uint8_t *data, size_t n) +{ + size_t x; + + for (x = 0; x < n; ++x) + ccprintf("%02x ", data[x]); +} + +static void setup_default_empty_path(uint8_t hashes[][PW_HASH_SIZE]) +{ + uint8_t num_siblings = (1 << EMPTY_TREE.bits_per_level.v) - 1; + const uint8_t level_hashes[5][PW_HASH_SIZE] = { + /* Values for level 5 are all 0 for empty. */ + /* SHA256 for level 5, values for level 4. */ + {0x38, 0x72, 0x3a, 0x2e, 0x5e, 0x8a, 0x17, 0xaa, + 0x79, 0x50, 0xdc, 0x00, 0x82, 0x09, 0x94, 0x4e, + 0x89, 0x8f, 0x69, 0xa7, 0xbd, 0x10, 0xa2, 0x3c, + 0x83, 0x9d, 0x34, 0x1e, 0x93, 0x5f, 0xd5, 0xca}, + /* SHA256 for level 4, values for level 3. */ + {0xfe, 0xc1, 0x2b, 0x09, 0x33, 0x31, 0x28, 0x34, + 0x79, 0x1f, 0x07, 0x64, 0x1a, 0xed, 0x30, 0x53, + 0x11, 0x1f, 0x15, 0x3e, 0x1e, 0x3e, 0xd1, 0xf0, + 0xcd, 0x16, 0xcb, 0x39, 0x25, 0xfd, 0x5f, 0x84}, + /* SHA256 for level 3, values for level 2. */ + {0xb6, 0xd4, 0x9c, 0x89, 0x76, 0x45, 0x9c, 0xe9, + 0x9c, 0x0b, 0xad, 0x5d, 0x71, 0xdf, 0x92, 0x77, + 0xf6, 0x82, 0x62, 0x63, 0x81, 0x9f, 0xc9, 0x2f, + 0x61, 0x9c, 0x29, 0x67, 0x52, 0x37, 0x01, 0x51}, + /* SHA256 for level 2, values for level 1. */ + {0x87, 0xeb, 0x61, 0x6b, 0x2c, 0x42, 0x07, 0x5e, + 0x70, 0x2d, 0x48, 0x49, 0xf2, 0xe0, 0x13, 0x11, + 0xc4, 0xe6, 0x98, 0xfa, 0x22, 0x7e, 0x65, 0xc6, + 0x66, 0x33, 0x6b, 0xb6, 0xd7, 0xb9, 0x45, 0xfa}, + /* SHA256 for level 1, values for level 0. */ + {0x80, 0x91, 0x04, 0x3f, 0x6c, 0x29, 0x06, 0x35, + 0x86, 0x99, 0x21, 0x88, 0x1f, 0xd9, 0xae, 0xb8, + 0x35, 0x94, 0x26, 0x19, 0x64, 0x68, 0x4f, 0x4f, + 0x4c, 0x66, 0x13, 0xa9, 0x66, 0x69, 0x25, 0x0e},}; + uint8_t hx; + uint8_t kx; + + /* Empty first level. */ + memset(hashes, 0, num_siblings * PW_HASH_SIZE); + hashes += num_siblings; + + for (hx = 1; hx < EMPTY_TREE.height.v; ++hx) { + for (kx = 0; kx < num_siblings; ++kx) { + memcpy(hashes, level_hashes[hx - 1], PW_HASH_SIZE); + ++hashes; + } + } +} + +static void setup_default_unimported_leaf_data_and_hashes( + const struct leaf_data_t *leaf_data, + const uint8_t hmac[PW_HASH_SIZE], + struct unimported_leaf_data_t *data) +{ + memcpy(&data->head, &DEFAULT_HEAD, sizeof(DEFAULT_HEAD)); + memcpy(data->hmac, hmac, sizeof(data->hmac)); + memcpy(data->iv, DEFAULT_IV, sizeof(DEFAULT_IV)); + memcpy(data->payload, &leaf_data->pub, sizeof(leaf_data->pub)); + DCRYPTO_aes_ctr(data->payload + sizeof(leaf_data->pub), + EMPTY_TREE.wrap_key, sizeof(EMPTY_TREE.wrap_key) * 8, + DEFAULT_IV, (const uint8_t *)&leaf_data->sec, + sizeof(leaf_data->sec)); + setup_default_empty_path((void *)(data->payload + DEFAULT_HEAD.pub_len + + DEFAULT_HEAD.sec_len)); +} + +static void setup_reset_tree_defaults(struct merkle_tree_t *merkle_tree, + struct pw_request_t *request) +{ + MOCK_DECRYPTO_init_counter = 0; + MOCK_DECRYPTO_release_counter = 0; + + memset(merkle_tree, 0, sizeof(*merkle_tree)); + + request->header.version = PW_PROTOCOL_VERSION; + request->header.type.v = PW_RESET_TREE; + request->header.data_length = sizeof(struct pw_request_reset_tree_t); + + request->data.reset_tree.bits_per_level.v = 2; /* k = 4 */ + request->data.reset_tree.height.v = 6; /* L = 12 */ + + MOCK_rand_bytes_src = (uint8_t *)EMPTY_TREE.key_derivation_nonce; + MOCK_rand_bytes_offset = 0; + MOCK_rand_bytes_len = sizeof(EMPTY_TREE.key_derivation_nonce); + MOCK_appkey_derive_fail = EC_SUCCESS; +} + +static void setup_insert_leaf_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(EMPTY_TREE)); + + request->header.version = PW_PROTOCOL_VERSION; + request->header.type.v = PW_INSERT_LEAF; + request->header.data_length = sizeof(struct pw_request_insert_leaf_t) + + get_path_auxiliary_hash_count(&EMPTY_TREE) * + PW_HASH_SIZE; + + request->data.insert_leaf.label.v = DEFAULT_LEAF.pub.label.v; + memcpy(&request->data.insert_leaf.delay_schedule, + &DEFAULT_LEAF.pub.delay_schedule, + sizeof(DEFAULT_LEAF.pub.delay_schedule)); + memcpy(&request->data.insert_leaf.low_entropy_secret, + &DEFAULT_LEAF.sec.low_entropy_secret, + sizeof(DEFAULT_LEAF.sec.low_entropy_secret)); + memcpy(&request->data.insert_leaf.high_entropy_secret, + &DEFAULT_LEAF.sec.high_entropy_secret, + sizeof(DEFAULT_LEAF.sec.high_entropy_secret)); + memcpy(&request->data.insert_leaf.reset_secret, + &DEFAULT_LEAF.sec.reset_secret, + sizeof(DEFAULT_LEAF.sec.reset_secret)); + setup_default_empty_path(request->data.insert_leaf.path_hashes); + + MOCK_rand_bytes_src = DEFAULT_IV; + MOCK_rand_bytes_offset = 0; + MOCK_rand_bytes_len = sizeof(DEFAULT_IV); + MOCK_hash_update_cb = 0; + MOCK_hmac = DEFAULT_HMAC; + MOCK_aes_fail = 0; +} + +static void setup_remove_leaf_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(EMPTY_TREE)); + memcpy(merkle_tree->root, ROOT_WITH_DEFAULT_HMAC, + sizeof(ROOT_WITH_DEFAULT_HMAC)); + + request->header.version = PW_PROTOCOL_VERSION; + request->header.type.v = PW_REMOVE_LEAF; + request->header.data_length = + sizeof(struct pw_request_remove_leaf_t) + + get_path_auxiliary_hash_count(&EMPTY_TREE) * + PW_HASH_SIZE; + + request->data.remove_leaf.leaf_location = DEFAULT_LEAF.pub.label; + 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); +} + +static void setup_try_auth_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(EMPTY_TREE)); + if (leaf_data->pub.attempt_count.v != 6 && + leaf_data->pub.attempt_count.v != 10) { + memcpy(merkle_tree->root, ROOT_WITH_DEFAULT_HMAC, + sizeof(ROOT_WITH_DEFAULT_HMAC)); + + /* 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_TRY_AUTH; + request->header.data_length = + sizeof(struct pw_request_try_auth_t) + + PW_LEAF_PAYLOAD_SIZE + + get_path_auxiliary_hash_count(&EMPTY_TREE) * + PW_HASH_SIZE; + + memcpy(request->data.try_auth.low_entropy_secret, + DEFAULT_LEAF.sec.low_entropy_secret, + sizeof(request->data.try_auth.low_entropy_secret)); + setup_default_unimported_leaf_data_and_hashes( + leaf_data, MOCK_hmac, + &request->data.try_auth.unimported_leaf_data); + + MOCK_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; +} + +static void setup_try_auth_defaults(struct merkle_tree_t *merkle_tree, + struct pw_request_t *request) +{ + setup_try_auth_defaults_with_leaf(&DEFAULT_LEAF, merkle_tree, request); +} + +static void setup_reset_auth_defaults(struct merkle_tree_t *merkle_tree, + struct pw_request_t *request) +{ + struct leaf_public_data_t *pub = + (void *)request->data.reset_auth.unimported_leaf_data + .payload; + + MOCK_DECRYPTO_init_counter = 0; + MOCK_DECRYPTO_release_counter = 0; + memcpy(merkle_tree, &EMPTY_TREE, sizeof(EMPTY_TREE)); + + request->header.version = PW_PROTOCOL_VERSION; + request->header.type.v = PW_RESET_AUTH; + request->header.data_length = + sizeof(struct pw_request_reset_auth_t) + + PW_LEAF_PAYLOAD_SIZE + + get_path_auxiliary_hash_count(&EMPTY_TREE) * + PW_HASH_SIZE; + + memcpy(request->data.reset_auth.reset_secret, + DEFAULT_LEAF.sec.reset_secret, + sizeof(request->data.reset_auth.reset_secret)); + + setup_default_unimported_leaf_data_and_hashes( + &DEFAULT_LEAF, EMPTY_HMAC, + &request->data.try_auth.unimported_leaf_data); + pub->attempt_count.v = 6; + + 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_hmac = EMPTY_HMAC; /* Gets overwritten by auth_hash_update_cb. */ + MOCK_aes_fail = 0; +} + +/* Increases the length of the pub and cipher_text by 4 each. */ +static void setup_mock_future_version( + struct unimported_leaf_data_t *unimported_leaf_data, + uint16_t *req_length) +{ + uint8_t *start = unimported_leaf_data->payload; + const uint8_t size_increase = 4; + const uint16_t cipher_text_offset = unimported_leaf_data->head.pub_len; + const uint16_t hashes_offset = cipher_text_offset + + unimported_leaf_data->head.sec_len; + + /* Shift hashes by 8*/ + memmove(start + hashes_offset + size_increase * 2, + start + hashes_offset, + get_path_auxiliary_hash_count(&EMPTY_TREE) * + PW_HASH_SIZE); + + /* Shift cipher_text by 4*/ + memmove(start + cipher_text_offset + size_increase, + start + cipher_text_offset, + unimported_leaf_data->head.sec_len); + + ++unimported_leaf_data->head.leaf_version.minor; + unimported_leaf_data->head.pub_len += size_increase; + unimported_leaf_data->head.sec_len += size_increase; + *req_length += size_increase * 2; +} + +static int test_handle_short_msg(struct merkle_tree_t *merkle_tree, + struct pw_test_data_t *buf, + const uint8_t root[PW_HASH_SIZE]) +{ + int ret = do_request(merkle_tree, buf); + + TEST_RET_EQ(buf->response.header.result_code, ret); + TEST_ASSERT(buf->response.header.version == PW_PROTOCOL_VERSION); + TEST_ASSERT(buf->response.header.data_length == 0); + TEST_ASSERT_ARRAY_EQ(buf->response.header.root, root, PW_HASH_SIZE); + TEST_ASSERT_ARRAY_EQ(buf->response.header.root, merkle_tree->root, + PW_HASH_SIZE); + return ret; +} + +/* Changes MOCK_hmac in a deterministic way based on the contents of the data + * with the goal of making it easier to catch bugs in the handling of try_auth + * and reset_auth requests. + */ +static void auth_hash_update_cb(const void *data, size_t len) +{ + const struct leaf_data_t *leaf_data = data; + + if (len != sizeof(leaf_data->pub) && len != sizeof(leaf_data->pub) + 4) + return; + + switch (leaf_data->pub.attempt_count.v) { + case 10: + case 6: + MOCK_hmac = EMPTY_HMAC; + break; + default: + MOCK_hmac = DEFAULT_HMAC; + break; + } +} + +/******************************************************************************/ +/* Mock implementations of TPM, TRNG, and Dcrypto 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 : + sizeof(DEFAULT_STORAGE_SEED); + memcpy(buf, DEFAULT_STORAGE_SEED, *len); +} + +void rand_bytes(void *buffer, size_t len) +{ + if (!MOCK_rand_bytes_src) + return; + + TEST_ASRT_NORET(len <= MOCK_rand_bytes_len - MOCK_rand_bytes_offset); + + memcpy(buffer, MOCK_rand_bytes_src + MOCK_rand_bytes_offset, len); + MOCK_rand_bytes_offset += len; + if (MOCK_rand_bytes_len == MOCK_rand_bytes_offset) + MOCK_rand_bytes_offset = 0; +} + +void HASH_update(struct HASH_CTX *ctx, const void *data, size_t len) +{ + if (MOCK_hash_update_cb) + MOCK_hash_update_cb(data, len); + if (ctx) + SHA256_update(ctx, data, len); +} + +uint8_t *HASH_final(struct HASH_CTX *ctx) +{ + ++MOCK_DECRYPTO_release_counter; + return SHA256_final(ctx); +} + +void DCRYPTO_SHA256_init(LITE_SHA256_CTX *ctx, uint32_t sw_required) +{ + SHA256_init(ctx); + ++MOCK_DECRYPTO_init_counter; +} + +void DCRYPTO_HMAC_SHA256_init(LITE_HMAC_CTX *ctx, const void *key, + unsigned int len) +{ + TEST_ASRT_NORET(len == sizeof(EMPTY_TREE.hmac_key)); + TEST_ASRT_NORET(memcmp(key, EMPTY_TREE.hmac_key, + sizeof(EMPTY_TREE.hmac_key)) == 0); + SHA256_init(&ctx->hash); + ++MOCK_DECRYPTO_init_counter; +} + +const uint8_t *DCRYPTO_HMAC_final(LITE_HMAC_CTX *ctx) +{ + ++MOCK_DECRYPTO_release_counter; + return MOCK_hmac; +} + +/* Perform a symmetric transformation of the data to simulate AES without + * requiring a full AES-CTR implementation. + * + * 1 for success 0 for fail + */ +int DCRYPTO_aes_ctr(uint8_t *out, const uint8_t *key, uint32_t key_bits, + const uint8_t *iv, const uint8_t *in, size_t in_len) +{ + size_t x; + + if (MOCK_aes_fail) { + --MOCK_aes_fail; + return 0; + } + + TEST_ASSERT(key_bits == 256); + TEST_ASSERT_ARRAY_EQ(key, EMPTY_TREE.wrap_key, + sizeof(EMPTY_TREE.wrap_key)); + TEST_ASSERT_ARRAY_EQ(iv, DEFAULT_IV, sizeof(DEFAULT_IV)); + TEST_ASSERT(in_len == sizeof(struct leaf_sensitive_data_t)); + + for (x = 0; x < in_len; ++x) + out[x] = MOCK_AES_XOR_BYTE(x) ^ in[x]; + return 1; +} + +/* 1 for success 0 for fail*/ +int DCRYPTO_appkey_init(enum dcrypto_appid appid, struct APPKEY_CTX *ctx) +{ + MOCK_hwctx_appkey = appid; + return 1; +} + +void DCRYPTO_appkey_finish(struct APPKEY_CTX *ctx) +{ + MOCK_hwctx_appkey = 0; +} + +/* 1 for success 0 for fail*/ +int DCRYPTO_appkey_derive(enum dcrypto_appid appid, const uint32_t input[8], + uint32_t output[8]) +{ + TEST_ASSERT(appid == PINWEAVER); + TEST_ASSERT(MOCK_hwctx_appkey == appid); + + if (MOCK_appkey_derive_fail != EC_SUCCESS) + return 0; + + if (input[6] ^ DEFAULT_STORAGE_SEED[6]) + memcpy(output, EMPTY_TREE.hmac_key, + sizeof(EMPTY_TREE.hmac_key)); + else + memcpy(output, EMPTY_TREE.wrap_key, + sizeof(EMPTY_TREE.wrap_key)); + return 1; +} + +/******************************************************************************/ +/* Reusable test cases. + */ + +static int check_dcrypto_mutex_usage(void) +{ + if (MOCK_DECRYPTO_init_counter == MOCK_DECRYPTO_release_counter) + return EC_SUCCESS; + ccprintf("ASSERTION failed: DCRYPTO init(%d) != DCRYPTO release(%d)\n", + MOCK_DECRYPTO_init_counter, MOCK_DECRYPTO_release_counter); + return EC_ERROR_UNKNOWN; +} + +static int invalid_length_with_leaf_head( + size_t head_offset, + void (*defaults)(struct merkle_tree_t *, struct pw_request_t *)) +{ + struct merkle_tree_t merkle_tree; + struct pw_test_data_t buf; + struct leaf_header_t *req_head = (void *)&buf + head_offset; + uint8_t old_root[PW_HASH_SIZE]; + + defaults(&merkle_tree, &buf.request); + memcpy(old_root, merkle_tree.root, sizeof(old_root)); + + buf.request.header.data_length = 0; + + TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, old_root), + PW_ERR_LENGTH_INVALID); + + defaults(&merkle_tree, &buf.request); + + ++buf.request.header.data_length; + + TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, old_root), + PW_ERR_LENGTH_INVALID); + + defaults(&merkle_tree, &buf.request); + + ++req_head->pub_len; + + TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, old_root), + PW_ERR_LENGTH_INVALID); + + defaults(&merkle_tree, &buf.request); + + ++req_head->leaf_version.minor; + --req_head->pub_len; + + TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, old_root), + PW_ERR_LENGTH_INVALID); + return check_dcrypto_mutex_usage(); + +} + +/******************************************************************************/ +/* Basic operation test cases. + */ + +static int get_path_auxiliary_hash_count_test(void) +{ + struct merkle_tree_t merkle_tree; + + memcpy(&merkle_tree, &EMPTY_TREE, sizeof(merkle_tree)); + + TEST_ASSERT(get_path_auxiliary_hash_count(&merkle_tree) == + EMPTY_TREE_PATH_LENGTH); + return EC_SUCCESS; +} + +static int compute_hash_test(void) +{ + const uint8_t hashes[4][PW_HASH_SIZE] = { + {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + }; + const struct { + struct index_t index; + uint8_t result[PW_HASH_SIZE]; + } test_cases[] = { + {{0}, + {0xd5, 0xd9, 0x25, 0xb6, 0xa9, 0x90, 0x24, 0x12, + 0x39, 0x0e, 0xfa, 0xd4, 0x8d, 0x55, 0x45, 0xf3, + 0x23, 0x6c, 0x6d, 0xff, 0xcc, 0xc8, 0xe1, 0x39, + 0xc7, 0xc3, 0x25, 0xf0, 0xd2, 0xa8, 0xf2, 0x0c} + }, + {{1}, + {0x64, 0x3e, 0x56, 0xbc, 0xb9, 0xda, 0x18, 0xaf, + 0xa0, 0x8c, 0x1f, 0xf8, 0x5e, 0xba, 0x58, 0xd0, + 0xe1, 0x99, 0x61, 0xe0, 0xe2, 0x12, 0xe9, 0x14, + 0xb5, 0x33, 0x46, 0x35, 0x52, 0x1e, 0xaf, 0x91} + }, + {{3}, + {0xd0, 0x90, 0xc7, 0x3d, 0x12, 0xfb, 0xbc, 0xbc, + 0x78, 0xcc, 0xbe, 0x58, 0x21, 0x14, 0xcf, 0x38, + 0x68, 0x49, 0x20, 0xe9, 0x61, 0xcb, 0x35, 0xc4, + 0x95, 0xb0, 0x14, 0x5a, 0x35, 0x43, 0x3e, 0x73} + }, + }; + uint8_t result[PW_HASH_SIZE]; + size_t x; + + for (x = 0; x < ARRAY_SIZE(test_cases); ++x) { + compute_hash(hashes, 3, test_cases[x].index, hashes[3], result); + TEST_ASSERT_ARRAY_EQ(result, test_cases[x].result, + sizeof(result)); + } + + return EC_SUCCESS; +} + +/******************************************************************************/ +/* Header validation test cases. + */ + +static int handle_request_version_mismatch(void) +{ + struct merkle_tree_t merkle_tree; + struct pw_test_data_t buf; + + setup_reset_tree_defaults(&merkle_tree, &buf.request); + + buf.request.header.version = PW_PROTOCOL_VERSION + 1; + + TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, EMPTY_HMAC), + PW_ERR_VERSION_MISMATCH); + return EC_SUCCESS; +} + +static int handle_request_invalid_type(void) +{ + struct merkle_tree_t merkle_tree; + struct pw_test_data_t buf; + + memcpy(&merkle_tree, &EMPTY_TREE, sizeof(merkle_tree)); + memset(&buf.response, 0x77, sizeof(buf.response)); + + buf.request.header.version = PW_PROTOCOL_VERSION; + buf.request.header.type.v = PW_MT_INVALID; + buf.request.header.data_length = 0; + + TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, EMPTY_TREE.root), + PW_ERR_TYPE_INVALID); + return EC_SUCCESS; +} + +/******************************************************************************/ +/* Reset Tree test cases. + */ + +static int handle_reset_tree_invalid_length(void) +{ + struct merkle_tree_t merkle_tree; + struct pw_test_data_t buf; + + setup_reset_tree_defaults(&merkle_tree, &buf.request); + + ++buf.request.header.data_length; + + TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, EMPTY_HMAC), + PW_ERR_LENGTH_INVALID); + return check_dcrypto_mutex_usage(); +} + +static int handle_reset_tree_bits_per_level_invalid(void) +{ + struct merkle_tree_t merkle_tree; + struct pw_test_data_t buf; + + setup_reset_tree_defaults(&merkle_tree, &buf.request); + + /* Test lower bound. */ + buf.request.data.reset_tree.bits_per_level.v = BITS_PER_LEVEL_MIN - 1; + + TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, merkle_tree.root), + PW_ERR_BITS_PER_LEVEL_INVALID); + + setup_reset_tree_defaults(&merkle_tree, &buf.request); + + /* Test upper bound. */ + buf.request.data.reset_tree.bits_per_level.v = BITS_PER_LEVEL_MAX + 1; + + TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, EMPTY_HMAC), + PW_ERR_BITS_PER_LEVEL_INVALID); + return check_dcrypto_mutex_usage(); +} + +static int handle_reset_tree_height_invalid(void) +{ + struct merkle_tree_t merkle_tree; + struct pw_test_data_t buf; + + setup_reset_tree_defaults(&merkle_tree, &buf.request); + + /* Test lower bound. */ + buf.request.data.reset_tree.height.v = HEIGHT_MIN - 1; + + TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, merkle_tree.root), + PW_ERR_HEIGHT_INVALID); + + setup_reset_tree_defaults(&merkle_tree, &buf.request); + + /* Test upper bound. */ + buf.request.data.reset_tree.height.v = + HEIGHT_MAX(buf.request.data.reset_tree + .bits_per_level.v) + 1; + + TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, EMPTY_HMAC), + PW_ERR_HEIGHT_INVALID); + return check_dcrypto_mutex_usage(); +} + +static int handle_reset_tree_crypto_failure(void) +{ + struct merkle_tree_t merkle_tree; + struct pw_test_data_t buf; + + setup_reset_tree_defaults(&merkle_tree, &buf.request); + + /* Test lower bound. */ + MOCK_appkey_derive_fail = PW_ERR_CRYPTO_FAILURE; + + TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, EMPTY_HMAC), + PW_ERR_CRYPTO_FAILURE); + return check_dcrypto_mutex_usage(); +} + +static int handle_reset_tree_success(void) +{ + struct merkle_tree_t merkle_tree; + struct pw_test_data_t buf; + + setup_reset_tree_defaults(&merkle_tree, &buf.request); + + TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, EMPTY_TREE.root), + EC_SUCCESS); + + TEST_ASSERT_ARRAY_EQ((uint8_t *)&merkle_tree, (uint8_t *)&EMPTY_TREE, + sizeof(EMPTY_TREE)); + + return check_dcrypto_mutex_usage(); +} + +/******************************************************************************/ +/* Insert leaf test cases. + */ + +static int handle_insert_leaf_invalid_length(void) +{ + struct merkle_tree_t merkle_tree; + struct pw_test_data_t buf; + + setup_insert_leaf_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_insert_leaf_label_invalid(void) +{ + struct merkle_tree_t merkle_tree; + struct pw_test_data_t buf; + + setup_insert_leaf_defaults(&merkle_tree, &buf.request); + + buf.request.data.insert_leaf.label.v |= 0x030000; + + 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_insert_leaf_delay_schedule_invalid(void) +{ + struct merkle_tree_t merkle_tree; + struct pw_test_data_t buf; + struct delay_schedule_entry_t (*ds)[PW_SCHED_COUNT] = + &buf.request.data.insert_leaf.delay_schedule; + + setup_insert_leaf_defaults(&merkle_tree, &buf.request); + + /* Non-increasing attempt_count. */ + (*ds)[1].attempt_count.v = 0; + + TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, EMPTY_TREE.root), + PW_ERR_DELAY_SCHEDULE_INVALID); + + setup_insert_leaf_defaults(&merkle_tree, &buf.request); + + /* Non-increasing time_diff. */ + (*ds)[1].time_diff.v = 0; + TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, EMPTY_TREE.root), + PW_ERR_DELAY_SCHEDULE_INVALID); + + setup_insert_leaf_defaults(&merkle_tree, &buf.request); + + /* attempt_count noise. */ + (*ds)[14].attempt_count.v = 99; + TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, EMPTY_TREE.root), + PW_ERR_DELAY_SCHEDULE_INVALID); + + setup_insert_leaf_defaults(&merkle_tree, &buf.request); + + /* time_diff noise. */ + (*ds)[14].time_diff.v = 99; + TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, EMPTY_TREE.root), + PW_ERR_DELAY_SCHEDULE_INVALID); + + setup_insert_leaf_defaults(&merkle_tree, &buf.request); + + /* Empty delay_schedule. */ + memset(&(*ds)[0], 0, sizeof(*ds)); + TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, EMPTY_TREE.root), + PW_ERR_DELAY_SCHEDULE_INVALID); + return check_dcrypto_mutex_usage(); +} + +static int handle_insert_leaf_path_auth_failed(void) +{ + struct merkle_tree_t merkle_tree; + struct pw_test_data_t buf; + + setup_insert_leaf_defaults(&merkle_tree, &buf.request); + + buf.request.data.insert_leaf.path_hashes[0][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_insert_leaf_crypto_failure(void) +{ + struct merkle_tree_t merkle_tree; + struct pw_test_data_t buf; + + setup_insert_leaf_defaults(&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_insert_leaf_success(void) +{ + struct merkle_tree_t merkle_tree; + struct pw_test_data_t buf; + size_t x; + const uint8_t *plain_text = (const uint8_t *)&DEFAULT_LEAF.sec; + struct wrapped_leaf_data_t *wrapped_leaf_data = + (void *)&buf.response.data.insert_leaf + .unimported_leaf_data; + + setup_insert_leaf_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(buf.response.data.insert_leaf) + + PW_LEAF_PAYLOAD_SIZE); + TEST_RET_EQ(buf.response.header.result_code, EC_SUCCESS); + + TEST_ASSERT_ARRAY_EQ(buf.response.header.root, ROOT_WITH_DEFAULT_HMAC, + sizeof(ROOT_WITH_DEFAULT_HMAC)); + TEST_ASSERT_ARRAY_EQ(buf.response.header.root, merkle_tree.root, + PW_HASH_SIZE); + TEST_ASSERT_ARRAY_EQ( + buf.response.data.insert_leaf.unimported_leaf_data.hmac, + DEFAULT_HMAC, sizeof(DEFAULT_HMAC)); + TEST_ASSERT_ARRAY_EQ( + (uint8_t *)&wrapped_leaf_data->pub, + (uint8_t *)&DEFAULT_LEAF.pub, sizeof(DEFAULT_LEAF.pub)); + for (x = 0; x < sizeof(DEFAULT_LEAF.sec); ++x) + TEST_ASSERT(plain_text[x] == + (wrapped_leaf_data->cipher_text[x] ^ + MOCK_AES_XOR_BYTE(x))); + + return check_dcrypto_mutex_usage(); +} + +/******************************************************************************/ +/* Remove leaf test cases. + */ + +static int handle_remove_leaf_invalid_length(void) +{ + struct merkle_tree_t merkle_tree; + struct pw_test_data_t buf; + + setup_remove_leaf_defaults(&merkle_tree, &buf.request); + + ++buf.request.header.data_length; + + TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, + ROOT_WITH_DEFAULT_HMAC), + PW_ERR_LENGTH_INVALID); + return check_dcrypto_mutex_usage(); +} + +static int handle_remove_leaf_label_invalid(void) +{ + struct merkle_tree_t merkle_tree; + struct pw_test_data_t buf; + + setup_remove_leaf_defaults(&merkle_tree, &buf.request); + + buf.request.data.remove_leaf.leaf_location.v |= 0x030000; + + TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, + ROOT_WITH_DEFAULT_HMAC), + PW_ERR_LABEL_INVALID); + return check_dcrypto_mutex_usage(); +} + +static int handle_remove_leaf_path_auth_failed(void) +{ + struct merkle_tree_t merkle_tree; + struct pw_test_data_t buf; + + setup_remove_leaf_defaults(&merkle_tree, &buf.request); + + buf.request.data.remove_leaf.path_hashes[0][0] ^= 0xff; + + TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, + ROOT_WITH_DEFAULT_HMAC), + PW_ERR_PATH_AUTH_FAILED); + return check_dcrypto_mutex_usage(); +} + +static int handle_remove_leaf_success(void) +{ + struct merkle_tree_t merkle_tree; + struct pw_test_data_t buf; + + setup_remove_leaf_defaults(&merkle_tree, &buf.request); + + TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, EMPTY_TREE.root), + EC_SUCCESS); + + return check_dcrypto_mutex_usage(); +} + +/******************************************************************************/ +/* Try auth test cases. + */ + +static int handle_try_auth_invalid_length(void) +{ + return invalid_length_with_leaf_head( + (size_t)&((struct pw_request_t *)0)->data.try_auth + .unimported_leaf_data.head, + setup_try_auth_defaults); +} + +static int handle_try_auth_leaf_version_mismatch(void) +{ + struct merkle_tree_t merkle_tree; + struct pw_test_data_t buf; + struct leaf_header_t *req_head = + &buf.request.data.try_auth.unimported_leaf_data.head; + + setup_try_auth_defaults(&merkle_tree, &buf.request); + + ++req_head->leaf_version.major; + + TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, + ROOT_WITH_DEFAULT_HMAC), + PW_ERR_LEAF_VERSION_MISMATCH); + return check_dcrypto_mutex_usage(); +} + +static int handle_try_auth_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(DEFAULT_LEAF)); + leaf_data.pub.label.v |= 0x030000; + setup_try_auth_defaults_with_leaf(&leaf_data, &merkle_tree, + &buf.request); + + TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, + ROOT_WITH_DEFAULT_HMAC), + PW_ERR_LABEL_INVALID); + return check_dcrypto_mutex_usage(); +} + +static int handle_try_auth_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.try_auth.unimported_leaf_data + .payload + + sizeof(struct leaf_public_data_t) + + sizeof(struct leaf_sensitive_data_t); + + setup_try_auth_defaults(&merkle_tree, &buf.request); + + (*path_hashes)[0] ^= 0xff; + + TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, + ROOT_WITH_DEFAULT_HMAC), + PW_ERR_PATH_AUTH_FAILED); + return check_dcrypto_mutex_usage(); +} + +static int handle_try_auth_hmac_auth_failed(void) +{ + struct merkle_tree_t merkle_tree; + struct pw_test_data_t buf; + + setup_try_auth_defaults(&merkle_tree, &buf.request); + + MOCK_hash_update_cb = 0; + MOCK_hmac = EMPTY_TREE.root; + + TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, + ROOT_WITH_DEFAULT_HMAC), + PW_ERR_HMAC_AUTH_FAILED); + return check_dcrypto_mutex_usage(); +} + +static int handle_try_auth_crypto_failure(void) +{ + struct merkle_tree_t merkle_tree; + struct pw_test_data_t buf; + + setup_try_auth_defaults(&merkle_tree, &buf.request); + + MOCK_aes_fail = 1; + + TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, + ROOT_WITH_DEFAULT_HMAC), + PW_ERR_CRYPTO_FAILURE); + return check_dcrypto_mutex_usage(); +} + +static int check_try_auth_rate_limit_reached_response( + struct merkle_tree_t *merkle_tree, + struct pw_test_data_t *buf, + const struct time_diff_t seconds_to_wait) +{ + uint8_t old_root[PW_HASH_SIZE]; + + memcpy(old_root, merkle_tree->root, sizeof(old_root)); + + TEST_RET_EQ(do_request(merkle_tree, buf), PW_ERR_RATE_LIMIT_REACHED); + + TEST_ASSERT(buf->response.header.version == PW_PROTOCOL_VERSION); + TEST_ASSERT(buf->response.header.data_length == + sizeof(struct pw_response_try_auth_t) + + PW_LEAF_PAYLOAD_SIZE); + TEST_RET_EQ(buf->response.header.result_code, + PW_ERR_RATE_LIMIT_REACHED); + TEST_ASSERT_ARRAY_EQ(buf->response.header.root, old_root, + sizeof(old_root)); + TEST_ASSERT_ARRAY_EQ(buf->response.header.root, merkle_tree->root, + sizeof(merkle_tree->root)); + TEST_ASSERT(buf->response.data.try_auth.seconds_to_wait.v == + seconds_to_wait.v); + TEST_ASSERT_MEMSET(buf->response.data.try_auth.high_entropy_secret, + 0, PW_SECRET_SIZE); + TEST_ASSERT_MEMSET((uint8_t *)&buf->response.data.try_auth + .unimported_leaf_data, 0, + sizeof(buf->response.data.try_auth + .unimported_leaf_data) + PW_LEAF_PAYLOAD_SIZE); + + return check_dcrypto_mutex_usage(); +} + +static int handle_try_auth_rate_limit_reached(void) +{ + struct merkle_tree_t merkle_tree; + struct pw_test_data_t buf; + struct leaf_data_t leaf_data = {}; + + /* Test PW_BLOCK_ATTEMPTS. */ + memcpy(&leaf_data, &DEFAULT_LEAF, sizeof(DEFAULT_LEAF)); + leaf_data.pub.attempt_count.v = 51; + MOCK_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; + MOCK_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); + + /* Test same boot_count case. */ + memcpy(&leaf_data, &DEFAULT_LEAF, sizeof(DEFAULT_LEAF)); + leaf_data.pub.attempt_count.v = 10; + 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 = 0; + force_time((timestamp_t){.val = (leaf_data.pub.timestamp.timer_value + + 3599llu) * SECOND}); + + TEST_RET_EQ(check_try_auth_rate_limit_reached_response( + &merkle_tree, &buf, (const struct time_diff_t){1}), + EC_SUCCESS); + + /* Test boot_count + 1 case. */ + memcpy(&leaf_data, &DEFAULT_LEAF, sizeof(DEFAULT_LEAF)); + leaf_data.pub.attempt_count.v = 10; + 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_time((timestamp_t){.val = 3599llu * SECOND}); + + TEST_RET_EQ(check_try_auth_rate_limit_reached_response( + &merkle_tree, &buf, (const struct time_diff_t){1}), + EC_SUCCESS); + + return check_dcrypto_mutex_usage(); +} + +static int handle_try_auth_lowent_auth_failed(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.try_auth.unimported_leaf_data + .payload; + struct leaf_sensitive_data_t sec = {}; + uint8_t *resp_cipher_text = (void *)pub + sizeof(*pub); + + memcpy(&leaf_data, &DEFAULT_LEAF, sizeof(DEFAULT_LEAF)); + leaf_data.pub.attempt_count.v = 5; + leaf_data.sec.low_entropy_secret[ + sizeof(leaf_data.sec.low_entropy_secret) - 1] = + ~leaf_data.sec.low_entropy_secret[ + 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_time((timestamp_t){.val = (65ull * SECOND)}); + + TEST_RET_EQ(do_request(&merkle_tree, &buf), PW_ERR_LOWENT_AUTH_FAILED); + + TEST_ASSERT(buf.response.header.version == PW_PROTOCOL_VERSION); + TEST_ASSERT(buf.response.header.data_length == + sizeof(struct pw_response_try_auth_t) + + PW_LEAF_PAYLOAD_SIZE); + TEST_RET_EQ(buf.response.header.result_code, PW_ERR_LOWENT_AUTH_FAILED); + + 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.try_auth.unimported_leaf_data.hmac, + EMPTY_HMAC, sizeof(EMPTY_HMAC)); + TEST_ASSERT_ARRAY_EQ(buf.response.data.try_auth.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 == leaf_data.pub.label.v); + TEST_ASSERT_ARRAY_EQ( + (uint8_t *)&pub->delay_schedule, + (uint8_t *)&leaf_data.pub.delay_schedule, + sizeof(leaf_data.pub.delay_schedule)); + TEST_ASSERT_ARRAY_EQ((uint8_t *)&sec, (uint8_t *)&leaf_data.sec, + sizeof(leaf_data.sec)); + TEST_ASSERT(pub->attempt_count.v == leaf_data.pub.attempt_count.v + 1); + TEST_ASSERT(pub->timestamp.boot_count == 1); + + TEST_ASSERT_MEMSET(buf.response.data.try_auth.high_entropy_secret, + 0, PW_SECRET_SIZE); + + /* A threshold of 100 is used since some time will pass after + * force_time() is called. + */ + TEST_ASSERT(pub->timestamp.timer_value - 65ull < 100); + return check_dcrypto_mutex_usage(); +} + +static int handle_try_auth_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.try_auth.unimported_leaf_data + .payload; + struct leaf_sensitive_data_t sec = {}; + uint8_t *resp_cipher_text = (void *)pub + sizeof(*pub); + + /* Test same boot_count case. */ + memcpy(&leaf_data, &DEFAULT_LEAF, sizeof(leaf_data)); + leaf_data.pub.attempt_count.v = 6; + setup_try_auth_defaults_with_leaf(&leaf_data, &merkle_tree, + &buf.request); + MOCK_restart_count = 0; + force_time((timestamp_t){.val = 65 * SECOND}); + + 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_try_auth_t) + + PW_LEAF_PAYLOAD_SIZE); + TEST_RET_EQ(buf.response.header.result_code, EC_SUCCESS); + + TEST_ASSERT_ARRAY_EQ(buf.response.header.root, ROOT_WITH_DEFAULT_HMAC, + sizeof(ROOT_WITH_DEFAULT_HMAC)); + TEST_ASSERT_ARRAY_EQ(buf.response.header.root, merkle_tree.root, + sizeof(merkle_tree.root)); + + TEST_ASSERT_ARRAY_EQ( + buf.response.data.try_auth.unimported_leaf_data.hmac, + DEFAULT_HMAC, sizeof(DEFAULT_HMAC)); + TEST_ASSERT_ARRAY_EQ(buf.response.data.try_auth.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 == leaf_data.pub.label.v); + TEST_ASSERT_ARRAY_EQ( + (uint8_t *)&pub->delay_schedule, + (uint8_t *)&leaf_data.pub.delay_schedule, + sizeof(leaf_data.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_ARRAY_EQ(buf.response.data.try_auth.high_entropy_secret, + DEFAULT_LEAF.sec.high_entropy_secret, + sizeof(DEFAULT_LEAF.sec.high_entropy_secret)); + + /* 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_time((timestamp_t){.val = 65llu * SECOND}); + + 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_try_auth_t) + + PW_LEAF_PAYLOAD_SIZE); + TEST_RET_EQ(buf.response.header.result_code, EC_SUCCESS); + + TEST_ASSERT_ARRAY_EQ(buf.response.header.root, ROOT_WITH_DEFAULT_HMAC, + sizeof(ROOT_WITH_DEFAULT_HMAC)); + TEST_ASSERT_ARRAY_EQ(buf.response.header.root, merkle_tree.root, + sizeof(merkle_tree.root)); + + TEST_ASSERT_ARRAY_EQ( + buf.response.data.try_auth.unimported_leaf_data.hmac, + DEFAULT_HMAC, sizeof(DEFAULT_HMAC)); + TEST_ASSERT_ARRAY_EQ(buf.response.data.try_auth.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 == leaf_data.pub.label.v); + TEST_ASSERT_ARRAY_EQ( + (uint8_t *)&pub->delay_schedule, + (uint8_t *)&leaf_data.pub.delay_schedule, + sizeof(leaf_data.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_ARRAY_EQ(buf.response.data.try_auth.high_entropy_secret, + DEFAULT_LEAF.sec.high_entropy_secret, + sizeof(DEFAULT_LEAF.sec.high_entropy_secret)); + return check_dcrypto_mutex_usage(); +} + +/******************************************************************************/ +/* Reset auth test cases. + */ + +static int handle_reset_auth_invalid_length(void) +{ + return invalid_length_with_leaf_head( + (size_t)&((struct pw_request_t *)0)->data.reset_auth + .unimported_leaf_data.head, + setup_reset_auth_defaults); +} + +static int handle_reset_auth_label_invalid(void) +{ + struct merkle_tree_t merkle_tree; + struct pw_test_data_t buf; + struct leaf_public_data_t *pub = + (void *)buf.request.data.reset_auth.unimported_leaf_data + .payload; + + setup_reset_auth_defaults(&merkle_tree, &buf.request); + pub->label.v |= 0x030000; + + TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, merkle_tree.root), + PW_ERR_LABEL_INVALID); + return check_dcrypto_mutex_usage(); +} + +static int handle_reset_auth_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.reset_auth.unimported_leaf_data + .payload + + sizeof(struct leaf_public_data_t) + + sizeof(struct leaf_sensitive_data_t); + + setup_reset_auth_defaults(&merkle_tree, &buf.request); + + (*path_hashes)[0] ^= 0xff; + + TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, merkle_tree.root), + PW_ERR_PATH_AUTH_FAILED); + return check_dcrypto_mutex_usage(); +} + +static int handle_reset_auth_hmac_auth_failed(void) +{ + struct merkle_tree_t merkle_tree; + struct pw_test_data_t buf; + + setup_reset_auth_defaults(&merkle_tree, &buf.request); + + MOCK_hash_update_cb = 0; + MOCK_hmac = EMPTY_TREE.root; + + TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, merkle_tree.root), + PW_ERR_HMAC_AUTH_FAILED); + return check_dcrypto_mutex_usage(); +} + +static int handle_reset_auth_crypto_failure(void) +{ + struct merkle_tree_t merkle_tree; + struct pw_test_data_t buf; + + setup_reset_auth_defaults(&merkle_tree, &buf.request); + + MOCK_aes_fail = 1; + + TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, merkle_tree.root), + PW_ERR_CRYPTO_FAILURE); + return check_dcrypto_mutex_usage(); +} + +static int handle_reset_auth_reset_auth_failed(void) +{ + struct merkle_tree_t merkle_tree; + struct pw_test_data_t buf; + + setup_reset_auth_defaults(&merkle_tree, &buf.request); + + buf.request.data.reset_auth.reset_secret[0] ^= 0xff; + + TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, merkle_tree.root), + PW_ERR_RESET_AUTH_FAILED); + return check_dcrypto_mutex_usage(); +} + +static int handle_reset_auth_success(void) +{ + struct merkle_tree_t merkle_tree; + struct pw_test_data_t buf; + struct leaf_public_data_t *pub = + (void *)buf.response.data.reset_auth + .unimported_leaf_data.payload; + struct leaf_sensitive_data_t sec = {}; + uint8_t *resp_cipher_text = (void *)pub + sizeof(*pub); + + setup_reset_auth_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_reset_auth_t) + + PW_LEAF_PAYLOAD_SIZE); + TEST_RET_EQ(buf.response.header.result_code, EC_SUCCESS); + + TEST_ASSERT_ARRAY_EQ(buf.response.header.root, ROOT_WITH_DEFAULT_HMAC, + sizeof(ROOT_WITH_DEFAULT_HMAC)); + TEST_ASSERT_ARRAY_EQ(buf.response.header.root, merkle_tree.root, + sizeof(merkle_tree.root)); + + TEST_ASSERT_ARRAY_EQ( + buf.response.data.reset_auth.high_entropy_secret, + DEFAULT_LEAF.sec.high_entropy_secret, + sizeof(DEFAULT_LEAF.sec.high_entropy_secret)); + TEST_ASSERT_ARRAY_EQ( + buf.response.data.reset_auth.unimported_leaf_data.hmac, + DEFAULT_HMAC, sizeof(DEFAULT_HMAC)); + TEST_ASSERT_ARRAY_EQ( + buf.response.data.reset_auth.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 with different minor version and struct lengths. */ + setup_reset_auth_defaults(&merkle_tree, &buf.request); + setup_mock_future_version( + &buf.request.data.reset_auth.unimported_leaf_data, + &buf.request.header.data_length); + + 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_reset_auth_t) + + PW_LEAF_PAYLOAD_SIZE); + TEST_RET_EQ(buf.response.header.result_code, EC_SUCCESS); + + TEST_ASSERT_ARRAY_EQ(buf.response.header.root, ROOT_WITH_DEFAULT_HMAC, + sizeof(ROOT_WITH_DEFAULT_HMAC)); + TEST_ASSERT_ARRAY_EQ(buf.response.header.root, merkle_tree.root, + sizeof(merkle_tree.root)); + + TEST_ASSERT_ARRAY_EQ( + buf.response.data.reset_auth.high_entropy_secret, + DEFAULT_LEAF.sec.high_entropy_secret, + sizeof(DEFAULT_LEAF.sec.high_entropy_secret)); + TEST_ASSERT_ARRAY_EQ( + buf.response.data.reset_auth.unimported_leaf_data.hmac, + DEFAULT_HMAC, sizeof(DEFAULT_HMAC)); + TEST_ASSERT_ARRAY_EQ( + buf.response.data.reset_auth.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); + return check_dcrypto_mutex_usage(); +} + +/******************************************************************************/ +/* Main test function. Encapsulates the test cases.. + */ + +void run_test(void) +{ + test_reset(); + + /* Test basic operations. */ + RUN_TEST(get_path_auxiliary_hash_count_test); + RUN_TEST(compute_hash_test); + + /* Test header validation. */ + RUN_TEST(handle_request_version_mismatch); + RUN_TEST(handle_request_invalid_type); + + /* Test reset tree. */ + RUN_TEST(handle_reset_tree_invalid_length); + 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_success); + + /* Test insert leaf. */ + RUN_TEST(handle_insert_leaf_invalid_length); + RUN_TEST(handle_insert_leaf_label_invalid); + 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_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_success); + + /* Test try auth. */ + RUN_TEST(handle_try_auth_invalid_length); + RUN_TEST(handle_try_auth_leaf_version_mismatch); + RUN_TEST(handle_try_auth_label_invalid); + RUN_TEST(handle_try_auth_path_auth_failed); + 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_lowent_auth_failed); + RUN_TEST(handle_try_auth_success); + + /* Test reset auth. */ + RUN_TEST(handle_reset_auth_invalid_length); + RUN_TEST(handle_reset_auth_label_invalid); + RUN_TEST(handle_reset_auth_path_auth_failed); + 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_success); + + test_print_result(); +} diff --git a/test/pinweaver.tasklist b/test/pinweaver.tasklist new file mode 100644 index 0000000000..de4df33e13 --- /dev/null +++ b/test/pinweaver.tasklist @@ -0,0 +1,17 @@ +/* Copyright 2018 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/** + * List of enabled tasks in the priority order + * + * The first one has the lowest priority. + * + * For each task, use the macro TASK_TEST(n, r, d, s) where : + * 'n' in the name of the task + * 'r' in the main routine of the task + * 'd' in an opaque parameter passed to the routine at startup + * 's' is the stack size in bytes; must be a multiple of 8 + */ +#define CONFIG_TEST_TASK_LIST diff --git a/test/test_config.h b/test/test_config.h index 57b2b413ce..dd14aad6b2 100644 --- a/test/test_config.h +++ b/test/test_config.h @@ -259,6 +259,11 @@ enum nvmem_vars { #define CONFIG_FLASH_NVMEM_VARS_USER_SIZE 600 #endif /* TEST_NVMEM_VARS */ +#ifdef TEST_PINWEAVER +#define CONFIG_PINWEAVER +#define CONFIG_SHA256 +#endif + #ifdef TEST_RTC #define CONFIG_HOSTCMD_RTC #endif