summaryrefslogtreecommitdiff
path: root/board
diff options
context:
space:
mode:
Diffstat (limited to 'board')
-rw-r--r--board/cr50/build.mk1
-rw-r--r--board/cr50/fips_cmd.c90
-rw-r--r--board/cr50/power_button.c38
-rw-r--r--board/cr50/tpm2/platform.c2
-rw-r--r--board/cr50/tpm2/virtual_nvmem.c2
-rw-r--r--board/cr50/u2f.c708
-rw-r--r--board/cr50/u2f_state_load.c198
7 files changed, 834 insertions, 205 deletions
diff --git a/board/cr50/build.mk b/board/cr50/build.mk
index e3b2555a8d..42a095e54c 100644
--- a/board/cr50/build.mk
+++ b/board/cr50/build.mk
@@ -103,6 +103,7 @@ custom-board-ro_objs-${CONFIG_FIPS_UTIL} = $(BDIR)/dcrypto/util.o
# FIPS console and TPM2 commands are outside FIPS module
board-y += fips_cmd.o
+board-y += u2f_state_load.o
board-y += tpm2/NVMem.o
board-y += tpm2/aes.o
diff --git a/board/cr50/fips_cmd.c b/board/cr50/fips_cmd.c
index 6642bd3396..554b048c25 100644
--- a/board/cr50/fips_cmd.c
+++ b/board/cr50/fips_cmd.c
@@ -21,7 +21,7 @@
#include "system.h"
#include "task.h"
#include "tpm_nvmem_ops.h"
-#include "u2f_impl.h"
+#include "u2f_cmds.h"
/**
* Create IRQ handler calling FIPS module's dcrypto_done_interrupt() on
@@ -68,47 +68,46 @@ static void fips_print_status(void)
}
DECLARE_HOOK(HOOK_INIT, fips_print_status, HOOK_PRIO_INIT_PRINT_FIPS_STATUS);
-#ifdef CRYPTO_TEST_SETUP
+#if defined(CRYPTO_TEST_SETUP) || defined(CR50_DEV)
static const uint8_t k_salt = NVMEM_VAR_G2F_SALT;
-/* Can't include TPM2 headers, so just define constant locally. */
-#define HR_NV_INDEX (1U << 24)
+static void print_u2f_keys_status(void)
+{
+ struct u2f_state state;
+ bool load_result;
+ size_t hmac_len, drbg_len;
+
+ hmac_len = read_tpm_nvmem_size(TPM_HIDDEN_U2F_KEK);
+ drbg_len = read_tpm_nvmem_size(TPM_HIDDEN_U2F_KH_SALT);
+ load_result = u2f_load_or_create_state(&state, false);
-/* Wipe old U2F keys. */
-static void u2f_zeroize_non_fips(void)
+ CPRINTS("U2F HMAC len: %u, U2F Entropy len: %u, U2F load:%u, "
+ "State DRBG len:%u", hmac_len,
+ drbg_len, load_result, state.drbg_entropy_size);
+}
+
+static void u2f_keys(void)
{
- const uint32_t u2fobjs[] = { TPM_HIDDEN_U2F_KEK | HR_NV_INDEX,
- TPM_HIDDEN_U2F_KH_SALT | HR_NV_INDEX, 0 };
- /* Delete NVMEM_VAR_G2F_SALT. */
- setvar(&k_salt, sizeof(k_salt), NULL, 0);
- /* Remove U2F keys and wipe all deleted objects. */
- nvmem_erase_tpm_data_selective(u2fobjs);
+ CPRINTS("U2F state %x", (uintptr_t)u2f_get_state());
+ print_u2f_keys_status();
}
-/* Set U2F keys to old or new version. */
-static void fips_set_u2f_keys(bool active)
+/* Set U2F keys as old. */
+static void fips_set_old_u2f_keys(void)
{
- if (!active) {
- /* Old version. */
- uint8_t random[32];
- /* Create fake u2f keys old style */
- fips_trng_bytes(random, sizeof(random));
- setvar(&k_salt, sizeof(k_salt), random, sizeof(random));
-
- fips_trng_bytes(random, sizeof(random));
- write_tpm_nvmem_hidden(TPM_HIDDEN_U2F_KEK, sizeof(random),
- random, 1);
- fips_trng_bytes(random, sizeof(random));
- write_tpm_nvmem_hidden(TPM_HIDDEN_U2F_KH_SALT, sizeof(random),
- random, 1);
- } else {
- /**
- * TODO(sukhomlinov): Implement new key generation after merging
- * https://crrev.com/c/3034852 and adding FIPS key gen.
- */
- u2f_zeroize_non_fips();
- }
- system_reset(EC_RESET_FLAG_SECURITY);
+ uint8_t random[32];
+
+ u2f_zeroize_keys();
+
+ /* Create fake u2f keys old style */
+ fips_trng_bytes(random, sizeof(random));
+ setvar(&k_salt, sizeof(k_salt), random, sizeof(random));
+
+ fips_trng_bytes(random, sizeof(random));
+ write_tpm_nvmem_hidden(TPM_HIDDEN_U2F_KEK, sizeof(random), random, 1);
+ fips_trng_bytes(random, sizeof(random));
+ write_tpm_nvmem_hidden(TPM_HIDDEN_U2F_KH_SALT, sizeof(random), random,
+ 1);
}
#endif
@@ -127,11 +126,20 @@ static int cmd_fips_status(int argc, char **argv)
fips_print_test_time();
fips_print_mode();
}
-#ifdef CRYPTO_TEST_SETUP
+#if defined(CRYPTO_TEST_SETUP) || defined(CR50_DEV)
else if (!strncmp(argv[1], "new", 3))
- fips_set_u2f_keys(true); /* we can reboot here... */
+ CPRINTS("u2f update status: %d", u2f_update_keys());
+ else if (!strncmp(argv[1], "del", 3))
+ CPRINTS("u2f zeroization status: %d",
+ u2f_zeroize_keys());
else if (!strncmp(argv[1], "old", 3))
- fips_set_u2f_keys(false); /* we can reboot here... */
+ fips_set_old_u2f_keys();
+ else if (!strncmp(argv[1], "u2f", 3))
+ print_u2f_keys_status();
+ else if (!strncmp(argv[1], "gen", 3))
+ u2f_keys();
+#endif
+#ifdef CRYPTO_TEST_SETUP
else if (!strncmp(argv[1], "trng", 4))
fips_break_cmd = FIPS_BREAK_TRNG;
else if (!strncmp(argv[1], "sha", 3))
@@ -144,7 +152,7 @@ static int cmd_fips_status(int argc, char **argv)
DECLARE_SAFE_CONSOLE_COMMAND(
fips, cmd_fips_status,
#ifdef CRYPTO_TEST_SETUP
- "[test | new | old | trng | sha]",
+ "[test | new | old | u2f | gen | trng | sha]",
"Report FIPS status, switch U2F key, run tests, simulate errors");
#else
"[test]", "Report FIPS status, run tests");
@@ -181,10 +189,10 @@ static enum vendor_cmd_rc fips_cmd(enum vendor_cmd_cc code, void *buf,
memcpy(buf, &fips_reverse, sizeof(fips_reverse));
*response_size = sizeof(fips_reverse);
break;
-#ifdef CRYPTO_TEST_SETUP
case FIPS_CMD_ON:
- fips_set_u2f_keys(true); /* we can reboot here... */
+ u2f_update_keys();
break;
+#ifdef CRYPTO_TEST_SETUP
case FIPS_CMD_BREAK_TRNG:
fips_break_cmd = FIPS_BREAK_TRNG;
break;
diff --git a/board/cr50/power_button.c b/board/cr50/power_button.c
index 2d22966273..a13f450031 100644
--- a/board/cr50/power_button.c
+++ b/board/cr50/power_button.c
@@ -15,7 +15,6 @@
#include "system_chip.h"
#include "task.h"
#include "timer.h"
-#include "u2f_impl.h"
#define CPRINTS(format, args...) cprints(CC_RBOX, format, ## args)
#define CPRINTF(format, args...) cprintf(CC_RBOX, format, ## args)
@@ -308,6 +307,43 @@ static void power_button_init(void)
DECLARE_HOOK(HOOK_INIT, power_button_init, HOOK_PRIO_DEFAULT);
#endif /* CONFIG_U2F */
+/* ---- physical presence (using the laptop power button) ---- */
+
+static timestamp_t last_press;
+
+/* how long do we keep the last button press as valid presence */
+#define PRESENCE_TIMEOUT (10 * SECOND)
+
+void power_button_record(void)
+{
+ if (ap_is_on() && rbox_powerbtn_is_pressed()) {
+ last_press = get_time();
+#ifdef CR50_DEV
+ CPRINTS("record pp");
+#endif
+ }
+}
+
+enum touch_state pop_check_presence(int consume)
+{
+#ifdef CRYPTO_TEST_SETUP
+ return POP_TOUCH_YES;
+#else
+ int recent = ((last_press.val > 0) &&
+ ((get_time().val - last_press.val) < PRESENCE_TIMEOUT));
+
+#ifdef CR50_DEV
+ if (recent)
+ CPRINTS("User presence: consumed %d", consume);
+#endif
+ if (consume)
+ last_press.val = 0;
+
+ /* user physical presence on the power button */
+ return recent ? POP_TOUCH_YES : POP_TOUCH_NO;
+#endif
+}
+
void board_physical_presence_enable(int enable)
{
#ifndef CONFIG_U2F
diff --git a/board/cr50/tpm2/platform.c b/board/cr50/tpm2/platform.c
index 07851b9a23..267ac7cf7a 100644
--- a/board/cr50/tpm2/platform.c
+++ b/board/cr50/tpm2/platform.c
@@ -10,7 +10,7 @@
#include "pinweaver.h"
#include "tpm_nvmem.h"
#include "trng.h"
-#include "u2f_impl.h"
+#include "u2f_cmds.h"
#include "util.h"
#include "version.h"
diff --git a/board/cr50/tpm2/virtual_nvmem.c b/board/cr50/tpm2/virtual_nvmem.c
index dc65d75f46..3ddaed067d 100644
--- a/board/cr50/tpm2/virtual_nvmem.c
+++ b/board/cr50/tpm2/virtual_nvmem.c
@@ -13,7 +13,7 @@
#include "link_defs.h"
#include "rma_auth.h"
#include "sn_bits.h"
-#include "u2f_impl.h"
+#include "u2f_cmds.h"
#include "virtual_nvmem.h"
/*
diff --git a/board/cr50/u2f.c b/board/cr50/u2f.c
index 43082d5008..78bc25c01f 100644
--- a/board/cr50/u2f.c
+++ b/board/cr50/u2f.c
@@ -3,181 +3,179 @@
* found in the LICENSE file.
*/
-/* Helpers to emulate a U2F HID dongle over the TPM transport */
-
+#if defined(CRYPTO_TEST_SETUP) || defined(CR50_DEV)
#include "console.h"
+#endif
+
#include "dcrypto.h"
-#include "extension.h"
-#include "nvmem_vars.h"
-#include "rbox.h"
-#include "registers.h"
-#include "signed_header.h"
-#include "system.h"
-#include "tpm_nvmem_ops.h"
-#include "tpm_vendor_cmds.h"
-#include "u2f.h"
+#include "fips_rand.h"
+
+#include "u2f_cmds.h"
#include "u2f_impl.h"
#include "util.h"
-#define CPRINTS(format, args...) cprints(CC_EXTENSION, format, ## args)
-
-/* ---- physical presence (using the laptop power button) ---- */
-
-static timestamp_t last_press;
-
-/* how long do we keep the last button press as valid presence */
-#define PRESENCE_TIMEOUT (10 * SECOND)
+enum ec_error_list u2f_generate_hmac_key(struct u2f_state *state)
+{
+ /* HMAC key for key handle. */
+ if (!fips_rand_bytes(state->hmac_key, sizeof(state->hmac_key)))
+ return EC_ERROR_HW_INTERNAL;
+ return EC_SUCCESS;
+}
-void power_button_record(void)
+enum ec_error_list u2f_generate_drbg_entropy(struct u2f_state *state)
{
- if (ap_is_on() && rbox_powerbtn_is_pressed()) {
- last_press = get_time();
-#ifdef CR50_DEV
- CPRINTS("record pp");
-#endif
- }
+ state->drbg_entropy_size = 0;
+ /* Get U2F entropy from health-checked TRNG. */
+ if (!fips_trng_bytes(state->drbg_entropy, sizeof(state->drbg_entropy)))
+ return EC_ERROR_HW_INTERNAL;
+ state->drbg_entropy_size = sizeof(state->drbg_entropy);
+ return EC_SUCCESS;
}
-enum touch_state pop_check_presence(int consume)
+enum ec_error_list u2f_generate_g2f_secret(struct u2f_state *state)
{
-#ifdef CRYPTO_TEST_SETUP
- return POP_TOUCH_YES;
-#else
- int recent = ((last_press.val > 0) &&
- ((get_time().val - last_press.val) < PRESENCE_TIMEOUT));
+ /* G2F specific path. */
+ if (!fips_rand_bytes(state->salt, sizeof(state->salt)))
+ return EC_ERROR_HW_INTERNAL;
+ return EC_SUCCESS;
+}
-#ifdef CR50_DEV
- if (recent)
- CPRINTS("User presence: consumed %d", consume);
+/* Compute Key handle's HMAC. */
+static void u2f_origin_user_mac(const struct u2f_state *state,
+ const uint8_t *user, const uint8_t *origin,
+ const uint8_t *origin_seed, uint8_t kh_version,
+ uint8_t *kh_hmac)
+{
+ struct hmac_sha256_ctx ctx;
+
+ /* HMAC(u2f_hmac_key, origin || user || origin seed || version) */
+
+ HMAC_SHA256_hw_init(&ctx, state->hmac_key, SHA256_DIGEST_SIZE);
+ HMAC_SHA256_update(&ctx, origin, U2F_APPID_SIZE);
+ HMAC_SHA256_update(&ctx, user, U2F_USER_SECRET_SIZE);
+ HMAC_SHA256_update(&ctx, origin_seed, U2F_ORIGIN_SEED_SIZE);
+ if (kh_version != 0)
+ HMAC_SHA256_update(&ctx, &kh_version, sizeof(kh_version));
+#ifdef CR50_DEV_U2F_VERBOSE
+ ccprintf("origin %ph\n", HEX_BUF(origin, U2F_APPID_SIZE));
+ ccprintf("user %ph\n", HEX_BUF(user, U2F_USER_SECRET_SIZE));
+ ccprintf("origin_seed %ph\n",
+ HEX_BUF(origin_seed, U2F_ORIGIN_SEED_SIZE));
+ cflush();
#endif
- if (consume)
- last_press.val = 0;
-
- /* user physical presence on the power button */
- return recent ? POP_TOUCH_YES : POP_TOUCH_NO;
+ memcpy(kh_hmac, HMAC_SHA256_final(&ctx), SHA256_DIGEST_SIZE);
+#ifdef CR50_DEV_U2F_VERBOSE
+ ccprintf("kh_hmac %ph\n", HEX_BUF(kh_hmac, SHA256_DIGEST_SIZE));
+ cflush();
#endif
}
-static const uint8_t k_salt = NVMEM_VAR_G2F_SALT;
-static const uint8_t k_salt_deprecated = NVMEM_VAR_U2F_SALT;
-
-static int load_state(struct u2f_state *state)
+static void u2f_authorization_mac(const struct u2f_state *state,
+ const union u2f_key_handle_variant *kh,
+ uint8_t kh_version,
+ const uint8_t *auth_time_secret_hash,
+ uint8_t *kh_auth_mac)
{
- const struct tuple *t_salt = getvar(&k_salt, sizeof(k_salt));
-
- if (!t_salt) {
- /* Delete the old salt if present, no-op if not. */
- if (setvar(&k_salt_deprecated, sizeof(k_salt_deprecated),
- NULL, 0))
- return 0;
-
- /* create random salt */
- if (!DCRYPTO_ladder_random(state->salt))
- return 0;
- if (setvar(&k_salt, sizeof(k_salt),
- (const uint8_t *)state->salt, sizeof(state->salt)))
- return 0;
- } else {
- memcpy(state->salt, tuple_val(t_salt), sizeof(state->salt));
- freevar(t_salt);
- }
-
- if (read_tpm_nvmem_hidden(TPM_HIDDEN_U2F_KEK, sizeof(state->salt_kek),
- state->salt_kek) == TPM_READ_NOT_FOUND) {
- /*
- * Not found means that we have not used u2f before,
- * or not used it with updated fw that resets kek seed
- * on TPM clear.
- */
- if (t_salt) { /* Note that memory has been freed already!. */
- /*
- * We have previously used u2f, and may have
- * existing registrations; we don't want to
- * invalidate these, so preserve the existing
- * seed as a one-off. It will be changed on
- * next TPM clear.
- */
- memcpy(state->salt_kek, state->salt,
- sizeof(state->salt_kek));
- } else {
- /*
- * We have never used u2f before - generate
- * new seed.
- */
- if (!DCRYPTO_ladder_random(state->salt_kek))
- return 0;
- }
- if (write_tpm_nvmem_hidden(TPM_HIDDEN_U2F_KEK,
- sizeof(state->salt_kek),
- state->salt_kek,
- 1 /* commit */) != TPM_WRITE_CREATED)
- return 0;
+ struct hmac_sha256_ctx ctx;
+ const uint8_t *auth_salt = NULL;
+ const void *kh_header = NULL;
+ size_t kh_header_size = 0;
+
+ if (kh_version == 0) {
+ memset(kh_auth_mac, 0xff, SHA256_DIGEST_SIZE);
+ return;
}
-
- if (read_tpm_nvmem_hidden(TPM_HIDDEN_U2F_KH_SALT,
- sizeof(state->salt_kh),
- state->salt_kh) == TPM_READ_NOT_FOUND) {
- /*
- * We have never used u2f before - generate
- * new seed.
- */
- if (!DCRYPTO_ladder_random(state->salt_kh))
- return 0;
-
- if (write_tpm_nvmem_hidden(TPM_HIDDEN_U2F_KH_SALT,
- sizeof(state->salt_kh),
- state->salt_kh,
- 1 /* commit */) != TPM_WRITE_CREATED)
- return 0;
+ /* At some point we may have v2 key handle, so prepare for it. */
+ if (kh_version == 1) {
+ auth_salt = kh->v1.authorization_salt;
+ kh_header = &kh->v1;
+ kh_header_size = U2F_V1_KH_HEADER_SIZE;
}
- return 1;
-}
-
-struct u2f_state *get_state(void)
-{
- static int state_loaded;
- static struct u2f_state state;
+ /**
+ * HMAC(u2f_hmac_key, auth_salt || key_handle_header
+ * || authTimeSecret)
+ */
+ HMAC_SHA256_hw_init(&ctx, state->hmac_key, SHA256_DIGEST_SIZE);
+ HMAC_SHA256_update(&ctx, auth_salt, U2F_AUTHORIZATION_SALT_SIZE);
+ HMAC_SHA256_update(&ctx, kh_header, kh_header_size);
- if (!state_loaded)
- state_loaded = load_state(&state);
+ HMAC_SHA256_update(&ctx, auth_time_secret_hash,
+ U2F_AUTH_TIME_SECRET_SIZE);
- return state_loaded ? &state : NULL;
+ memcpy(kh_auth_mac, HMAC_SHA256_final(&ctx), SHA256_DIGEST_SIZE);
}
-/* ---- chip-specific U2F crypto ---- */
-
-static int _derive_key(enum dcrypto_appid appid, const uint32_t input[8],
- uint32_t output[8])
+static int app_hw_device_id(enum dcrypto_appid appid, const uint32_t input[8],
+ uint32_t output[8])
{
struct APPKEY_CTX ctx;
int result;
- /* Setup USR-based application key. */
+ /**
+ * Setup USR-based application key. This loads (if not already done)
+ * application-specific DeviceID.
+ * Internally it computes:
+ * HMAC(hw_device_id, SHA256(name[appid])), but we don't care about
+ * process.
+ * Important property:
+ * For same appid it will load same value.
+ */
if (!DCRYPTO_appkey_init(appid, &ctx))
return 0;
+
+ /**
+ * Compute HMAC(HMAC(hw_device_id, SHA256(name[appid])), input)
+ * It is not used as a key though, and treated as personalization
+ * string for DRBG.
+ */
result = DCRYPTO_appkey_derive(appid, input, output);
DCRYPTO_appkey_finish(&ctx);
return result;
}
-int u2f_origin_user_keypair(const uint8_t *key_handle, size_t key_handle_size,
- p256_int *d, p256_int *pk_x, p256_int *pk_y)
+/**
+ * Generate an origin and user-specific ECDSA key pair from the specified
+ * key handle.
+ *
+ * If pk_x and pk_y are NULL, public key generation will be skipped.
+ *
+ * @param state U2F state parameters
+ * @param kh key handle
+ * @param kh_version key handle version (0 - legacy, 1 - versioned)
+ * @param d pointer to ECDSA private key
+ * @param pk_x pointer to public key point
+ * @param pk_y pointer to public key point
+ *
+ * @return EC_SUCCESS if a valid key pair was created.
+ */
+static enum ec_error_list u2f_origin_user_key_pair(
+ const struct u2f_state *state, const union u2f_key_handle_variant *kh,
+ uint8_t kh_version, p256_int *d, p256_int *pk_x, p256_int *pk_y)
{
uint32_t dev_salt[P256_NDIGITS];
uint8_t key_seed[P256_NBYTES];
struct drbg_ctx drbg;
- struct u2f_state *state = get_state();
-
- if (!state)
- return EC_ERROR_UNKNOWN;
+ size_t key_handle_size = 0;
+ uint8_t *key_handle = NULL;
+
+ if (kh_version == 0) {
+ key_handle_size = sizeof(struct u2f_key_handle_v0);
+ key_handle = (uint8_t *)&kh->v0;
+ } else if ((kh_version == 1) && (kh->v1.version == kh_version)) {
+ key_handle_size = U2F_V1_KH_HEADER_SIZE;
+ key_handle = (uint8_t *)&kh->v1;
+ } else {
+ return EC_ERROR_INVAL;
+ }
- if (!_derive_key(U2F_ORIGIN, state->salt_kek, dev_salt))
+ /* TODO(sukhomlinov): implement new FIPS path. */
+ if (!app_hw_device_id(U2F_ORIGIN, state->hmac_key, dev_salt))
return EC_ERROR_UNKNOWN;
- hmac_drbg_init(&drbg, state->salt_kh, P256_NBYTES, dev_salt,
+ hmac_drbg_init(&drbg, state->drbg_entropy, P256_NBYTES, dev_salt,
P256_NBYTES, NULL, 0);
hmac_drbg_generate(&drbg, key_seed, sizeof(key_seed), key_handle,
@@ -186,41 +184,243 @@ int u2f_origin_user_keypair(const uint8_t *key_handle, size_t key_handle_size,
if (!DCRYPTO_p256_key_from_bytes(pk_x, pk_y, d, key_seed))
return EC_ERROR_TRY_AGAIN;
+#ifdef CR50_DEV_U2F_VERBOSE
+ ccprintf("user private key %ph\n", HEX_BUF(d, sizeof(*d)));
+ cflush();
+ if (pk_x)
+ ccprintf("user public x %ph\n", HEX_BUF(pk_x, sizeof(*pk_x)));
+ if (pk_y)
+ ccprintf("user public y %ph\n", HEX_BUF(pk_y, sizeof(*pk_y)));
+ cflush();
+#endif
+
return EC_SUCCESS;
}
-int u2f_gen_kek(const uint8_t *origin, uint8_t *kek, size_t key_len)
+enum ec_error_list u2f_generate(const struct u2f_state *state,
+ const uint8_t *user, const uint8_t *origin,
+ const uint8_t *authTimeSecretHash,
+ union u2f_key_handle_variant *kh,
+ uint8_t kh_version, struct u2f_ec_point *pubKey)
{
- uint32_t buf[P256_NDIGITS];
+ uint8_t *kh_hmac, *kh_origin_seed;
+ int generate_key_pair_rc;
+ /* Generated public keys associated with key handle. */
+ p256_int opk_x, opk_y;
+
+ /* Compute constants for request key handler version. */
+ if (kh_version == 0) {
+ kh_hmac = kh->v0.hmac;
+ kh_origin_seed = kh->v0.origin_seed;
+ } else if (kh_version == 1) {
+ kh_hmac = kh->v1.kh_hmac;
+ kh_origin_seed = kh->v1.origin_seed;
+ /**
+ * This may overwrite input parameters if shared
+ * request/response buffer is used by caller.
+ */
+ kh->v1.version = kh_version;
+ } else
+ return EC_ERROR_INVAL;
+
+ /* Generate key handle candidates and origin-specific key pair. */
+ do {
+ p256_int od;
+ /* Generate random origin seed for key handle candidate. */
+ if (!fips_rand_bytes(kh_origin_seed, U2F_ORIGIN_SEED_SIZE))
+ return EC_ERROR_HW_INTERNAL;
+
+ u2f_origin_user_mac(state, user, origin, kh_origin_seed,
+ kh_version, kh_hmac);
+
+ /**
+ * Try to generate key pair using key handle. This may fail if
+ * key handle results in private key which is out of allowed
+ * range. If this is the case, repeat with another origin seed.
+ */
+ generate_key_pair_rc = u2f_origin_user_key_pair(
+ state, kh, kh_version, &od, &opk_x, &opk_y);
- struct u2f_state *state = get_state();
+ p256_clear(&od);
+ } while (generate_key_pair_rc == EC_ERROR_TRY_AGAIN);
- if (!state)
- return EC_ERROR_UNKNOWN;
+ if (generate_key_pair_rc != EC_SUCCESS)
+ return generate_key_pair_rc;
- if (key_len != sizeof(buf))
- return EC_ERROR_UNKNOWN;
- if (!_derive_key(U2F_WRAP, state->salt_kek, buf))
- return EC_ERROR_UNKNOWN;
- memcpy(kek, buf, key_len);
+ if (kh_version == 1) {
+ if (!fips_rand_bytes(kh->v1.authorization_salt,
+ U2F_AUTHORIZATION_SALT_SIZE))
+ return EC_ERROR_HW_INTERNAL;
+
+ u2f_authorization_mac(state, kh, kh_version, authTimeSecretHash,
+ kh->v1.authorization_hmac);
+ }
+
+ pubKey->pointFormat = U2F_POINT_UNCOMPRESSED;
+ p256_to_bin(&opk_x, pubKey->x); /* endianness */
+ p256_to_bin(&opk_y, pubKey->y); /* endianness */
return EC_SUCCESS;
}
-int g2f_individual_keypair(p256_int *d, p256_int *pk_x, p256_int *pk_y)
+enum ec_error_list u2f_authorize_keyhandle(
+ const struct u2f_state *state, const union u2f_key_handle_variant *kh,
+ uint8_t kh_version, const uint8_t *user, const uint8_t *origin,
+ const uint8_t *authTimeSecretHash)
{
- uint8_t buf[SHA256_DIGEST_SIZE];
+ /* Re-created key handle. */
+ uint8_t recreated_hmac[SHA256_DIGEST_SIZE];
+ const uint8_t *origin_seed, *kh_hmac;
+ int result = 0;
+
+ /*
+ * Re-create the key handle and compare against that which
+ * was provided. This allows us to verify that the key handle
+ * is owned by this combination of device, current user and origin.
+ */
+ if (kh_version == 0) {
+ origin_seed = kh->v0.origin_seed;
+ kh_hmac = kh->v0.hmac;
+ } else {
+ origin_seed = kh->v1.origin_seed;
+ kh_hmac = kh->v1.kh_hmac;
+ }
+ /* First, check inner part. */
+ u2f_origin_user_mac(state, user, origin, origin_seed, kh_version,
+ recreated_hmac);
+
+ /**
+ * DCRYPTO_equals return 1 if success, by subtracting 1 we make it
+ * zero, and other results - zero or non-zero will be detected.
+ */
+ result |= DCRYPTO_equals(&recreated_hmac, kh_hmac,
+ sizeof(recreated_hmac)) -
+ 1;
+
+ always_memset(recreated_hmac, 0, sizeof(recreated_hmac));
+
+ if ((kh_version != 0) && (authTimeSecretHash != NULL)) {
+ u2f_authorization_mac(state, kh, kh_version, authTimeSecretHash,
+ recreated_hmac);
+ result |= DCRYPTO_equals(&recreated_hmac,
+ kh->v1.authorization_hmac,
+ sizeof(recreated_hmac)) -
+ 1;
+ always_memset(recreated_hmac, 0, sizeof(recreated_hmac));
+ }
+
+ return (result == 0) ? EC_SUCCESS : EC_ERROR_ACCESS_DENIED;
+}
+
+static enum ec_error_list
+u2f_attest_keyhandle_pubkey(const struct u2f_state *state,
+ const union u2f_key_handle_variant *key_handle,
+ uint8_t kh_version, const uint8_t *user,
+ const uint8_t *origin,
+ const uint8_t *authTimeSecretHash,
+ const struct u2f_ec_point *public_key)
+{
+ struct u2f_ec_point kh_pubkey;
+ p256_int od, opk_x, opk_y;
+ enum ec_error_list result;
+
+ /* Check this is a correct key handle for provided user/origin. */
+ result = u2f_authorize_keyhandle(state, key_handle, kh_version, user,
+ origin, authTimeSecretHash);
+
+ if (result != EC_SUCCESS)
+ return result;
+
+ /* Recreate public key from key handle. */
+ result = u2f_origin_user_key_pair(state, key_handle, kh_version, &od,
+ &opk_x, &opk_y);
+ if (result != EC_SUCCESS)
+ return result;
+
+ p256_clear(&od);
+ /* Reconstruct the public key. */
+ p256_to_bin(&opk_x, kh_pubkey.x);
+ p256_to_bin(&opk_y, kh_pubkey.y);
+ kh_pubkey.pointFormat = U2F_POINT_UNCOMPRESSED;
+
+#ifdef CR50_DEV_U2F_VERBOSE
+ ccprintf("recreated key %ph\n", HEX_BUF(&kh_pubkey, sizeof(kh_pubkey)));
+ ccprintf("provided key %ph\n", HEX_BUF(public_key, sizeof(kh_pubkey)));
+#endif
+ return (DCRYPTO_equals(&kh_pubkey, public_key,
+ sizeof(struct u2f_ec_point)) == 1) ?
+ EC_SUCCESS :
+ EC_ERROR_ACCESS_DENIED;
+}
- struct u2f_state *state = get_state();
+enum ec_error_list u2f_sign(const struct u2f_state *state,
+ const union u2f_key_handle_variant *kh,
+ uint8_t kh_version, const uint8_t *user,
+ const uint8_t *origin,
+ const uint8_t *authTimeSecretHash,
+ const uint8_t *hash, struct u2f_signature *sig)
+{
+ /* Origin private key. */
+ p256_int origin_d;
- if (!state)
- return EC_ERROR_UNKNOWN;
+ /* Hash, and corresponding signature. */
+ p256_int h, r, s;
- /* Incorporate HIK & diversification constant */
- if (!_derive_key(U2F_ATTEST, state->salt, (uint32_t *)buf))
- return EC_ERROR_UNKNOWN;
+ struct drbg_ctx ctx;
+ enum ec_error_list result;
+
+ result = u2f_authorize_keyhandle(state, kh, kh_version, user, origin,
+ authTimeSecretHash);
+
+ if (result != EC_SUCCESS)
+ return result;
+
+ /* Re-create origin-specific key. */
+ result = u2f_origin_user_key_pair(state, kh, kh_version, &origin_d,
+ NULL, NULL);
+ if (result != EC_SUCCESS)
+ return result;
+
+ /* Prepare hash to sign. */
+ p256_from_bin(hash, &h);
+
+ /* Now, we processed input parameters, so clean-up output. */
+ memset(sig, 0, sizeof(*sig));
+
+ /* Sign. */
+ hmac_drbg_init_rfc6979(&ctx, &origin_d, &h);
+ result = (dcrypto_p256_ecdsa_sign(&ctx, &origin_d, &h, &r, &s) != 0) ?
+ EC_SUCCESS :
+ EC_ERROR_HW_INTERNAL;
+
+ p256_clear(&origin_d);
- /* Generate unbiased private key */
+ p256_to_bin(&r, sig->sig_r);
+ p256_to_bin(&s, sig->sig_s);
+
+ return result;
+}
+
+/**
+ * Generate a hardware derived ECDSA key pair for individual attestation.
+ *
+ * @param state U2F state parameters
+ * @param d pointer to ECDSA private key
+ * @param pk_x pointer to public key point
+ * @param pk_y pointer to public key point
+ *
+ * @return true if a valid key pair was created.
+ */
+static bool g2f_individual_key_pair(const struct u2f_state *state, p256_int *d,
+ p256_int *pk_x, p256_int *pk_y)
+{
+ uint8_t buf[SHA256_DIGEST_SIZE];
+
+ /* Incorporate HIK & diversification constant. */
+ if (!app_hw_device_id(U2F_ATTEST, state->salt, (uint32_t *)buf))
+ return false;
+
+ /* Generate unbiased private key (non-FIPS path). */
while (!DCRYPTO_p256_key_from_bytes(pk_x, pk_y, d, buf)) {
struct sha256_ctx sha;
@@ -229,22 +429,208 @@ int g2f_individual_keypair(p256_int *d, p256_int *pk_x, p256_int *pk_y)
memcpy(buf, SHA256_final(&sha), sizeof(buf));
}
- return EC_SUCCESS;
+ return true;
}
-int u2f_gen_kek_seed(int commit)
+#define G2F_CERT_NAME "CrO2"
+
+size_t g2f_attestation_cert_serial(const struct u2f_state *state,
+ const uint8_t *serial, uint8_t *buf)
{
- struct u2f_state *state = get_state();
+ p256_int d, pk_x, pk_y;
- if (!state)
- return EC_ERROR_UNKNOWN;
+ if (g2f_individual_key_pair(state, &d, &pk_x, &pk_y))
+ return 0;
+
+ /* Note that max length is not currently respected here. */
+ return DCRYPTO_x509_gen_u2f_cert_name(&d, &pk_x, &pk_y,
+ (p256_int *)serial, G2F_CERT_NAME,
+ buf,
+ G2F_ATTESTATION_CERT_MAX_LEN);
+}
+
+enum ec_error_list u2f_attest(const struct u2f_state *state,
+ const union u2f_key_handle_variant *kh,
+ uint8_t kh_version, const uint8_t *user,
+ const uint8_t *origin,
+ const uint8_t *authTimeSecretHash,
+ const struct u2f_ec_point *public_key,
+ const uint8_t *data, size_t data_size,
+ struct u2f_signature *sig)
+{
+ struct sha256_ctx h_ctx;
+ struct drbg_ctx dr_ctx;
+
+ /* Data hash, and corresponding signature. */
+ p256_int h, r, s;
+
+ /* Attestation key. */
+ p256_int d, pk_x, pk_y;
+
+ enum ec_error_list result;
+
+ result = u2f_attest_keyhandle_pubkey(state, kh, kh_version, user,
+ origin, authTimeSecretHash,
+ public_key);
+
+ if (result != EC_SUCCESS)
+ return result;
- if (!DCRYPTO_ladder_random(state->salt_kek))
+ /* Derive G2F Attestation Key. */
+ if (!g2f_individual_key_pair(state, &d, &pk_x, &pk_y)) {
+#ifdef CR50_DEV
+ ccprintf("G2F Attestation key generation failed\n");
+#endif
return EC_ERROR_HW_INTERNAL;
+ }
- if (write_tpm_nvmem_hidden(TPM_HIDDEN_U2F_KEK, sizeof(state->salt_kek),
- state->salt_kek, commit) == TPM_WRITE_FAIL)
- return EC_ERROR_UNKNOWN;
+ /* Message signature. */
+ SHA256_hw_init(&h_ctx);
+ SHA256_update(&h_ctx, data, data_size);
+ p256_from_bin(SHA256_final(&h_ctx)->b8, &h);
- return EC_SUCCESS;
+ /* Now, we processed input parameters, so clean-up output. */
+ memset(sig, 0, sizeof(*sig));
+
+ /* Sign over the response w/ the attestation key. */
+ hmac_drbg_init_rfc6979(&dr_ctx, &d, &h);
+
+ result = (dcrypto_p256_ecdsa_sign(&dr_ctx, &d, &h, &r, &s) != 0) ?
+ EC_SUCCESS :
+ EC_ERROR_HW_INTERNAL;
+ p256_clear(&d);
+
+ p256_to_bin(&r, sig->sig_r);
+ p256_to_bin(&s, sig->sig_s);
+
+ return result;
+}
+
+#ifdef CRYPTO_TEST_SETUP
+static const char *expect_bool(enum ec_error_list value,
+ enum ec_error_list expect)
+{
+ if (value == expect)
+ return "PASSED";
+ return "NOT PASSED";
}
+
+static int cmd_u2f_test(int argc, char **argv)
+{
+ static struct u2f_state state;
+ static union u2f_key_handle_variant kh;
+ static const uint8_t origin[32] = { 0xff, 0xfe, 0xfd, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8 };
+ static const uint8_t user[32] = { 0x88, 0x8e, 0x8d, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7 };
+ static const uint8_t authTime[32] = { 0x99, 0x91, 2, 3, 4, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5 };
+ static struct u2f_ec_point pubKey;
+ static struct u2f_signature sig;
+
+ ccprintf("u2f_generate_hmac_key - %s\n",
+ expect_bool(u2f_generate_hmac_key(&state), EC_SUCCESS));
+
+ ccprintf("u2f_generate_g2f_secret - %s\n",
+ expect_bool(u2f_generate_g2f_secret(&state), EC_SUCCESS));
+
+ ccprintf("u2f_generate_drbg_entropy - %s\n",
+ expect_bool(u2f_generate_drbg_entropy(&state), EC_SUCCESS));
+
+ /* Version 0 key handle. */
+ ccprintf("u2f_generate - %s\n",
+ expect_bool(u2f_generate(&state, user, origin, authTime, &kh,
+ 0, &pubKey),
+ EC_SUCCESS));
+ ccprintf("kh: %ph\n", HEX_BUF(&kh, sizeof(kh.v0)));
+ ccprintf("pubKey: %ph\n", HEX_BUF(&pubKey, sizeof(pubKey)));
+
+ ccprintf("u2f_authorize_keyhandle - %s\n",
+ expect_bool(u2f_authorize_keyhandle(&state, &kh, 0, user,
+ origin, authTime),
+ EC_SUCCESS));
+
+ kh.v0.origin_seed[0] ^= 0x10;
+ ccprintf("u2f_authorize_keyhandle - %s\n",
+ expect_bool(u2f_authorize_keyhandle(&state, &kh, 0, user,
+ origin, authTime),
+ EC_ERROR_ACCESS_DENIED));
+
+ kh.v0.origin_seed[0] ^= 0x10;
+ ccprintf("u2f_sign - %s\n",
+ expect_bool(u2f_sign(&state, &kh, 0, user, origin, authTime,
+ authTime, &sig),
+ EC_SUCCESS));
+ ccprintf("sig: %ph\n", HEX_BUF(&sig, sizeof(sig)));
+
+ ccprintf("u2f_attest - %s\n",
+ expect_bool(u2f_attest(&state, &kh, 0, user, origin, authTime,
+ &pubKey, authTime, sizeof(authTime),
+ &sig),
+ EC_SUCCESS));
+ ccprintf("sig: %ph\n", HEX_BUF(&sig, sizeof(sig)));
+
+ /* Should fail with incorrect key handle. */
+ kh.v0.origin_seed[0] ^= 0x10;
+ ccprintf("u2f_sign - %s\n",
+ expect_bool(u2f_sign(&state, &kh, 0, user, origin, authTime,
+ authTime, &sig),
+ EC_ERROR_ACCESS_DENIED));
+ ccprintf("sig: %ph\n", HEX_BUF(&sig, sizeof(sig)));
+
+ /* Version 1 key handle. */
+ ccprintf("\nVersion 1 tests\n");
+ ccprintf("u2f_generate - %s\n",
+ expect_bool(u2f_generate(&state, user, origin, authTime, &kh,
+ 1, &pubKey),
+ EC_SUCCESS));
+ ccprintf("kh: %ph\n", HEX_BUF(&kh, sizeof(kh.v1)));
+ ccprintf("pubKey: %ph\n", HEX_BUF(&pubKey, sizeof(pubKey)));
+
+ ccprintf("u2f_authorize_keyhandle - %s\n",
+ expect_bool(u2f_authorize_keyhandle(&state, &kh, 1, user,
+ origin, authTime),
+ EC_SUCCESS));
+
+ kh.v1.authorization_salt[0] ^= 0x10;
+ ccprintf("u2f_authorize_keyhandle - %s\n",
+ expect_bool(u2f_authorize_keyhandle(&state, &kh, 1, user,
+ origin, authTime),
+ EC_ERROR_ACCESS_DENIED));
+
+ kh.v1.authorization_salt[0] ^= 0x10;
+ ccprintf("u2f_sign - %s\n",
+ expect_bool(u2f_sign(&state, &kh, 1, user, origin, authTime,
+ authTime, &sig),
+ EC_SUCCESS));
+ ccprintf("sig: %ph\n", HEX_BUF(&sig, sizeof(sig)));
+
+ ccprintf("u2f_attest - %s\n",
+ expect_bool(u2f_attest(&state, &kh, 1, user, origin, authTime,
+ &pubKey, authTime, sizeof(authTime),
+ &sig),
+ EC_SUCCESS));
+ ccprintf("sig: %ph\n", HEX_BUF(&sig, sizeof(sig)));
+
+ /* Should fail with incorrect key handle. */
+ kh.v1.origin_seed[0] ^= 0x10;
+ ccprintf("u2f_sign - %s\n",
+ expect_bool(u2f_sign(&state, &kh, 1, user, origin, authTime,
+ authTime, &sig),
+ EC_ERROR_ACCESS_DENIED));
+ ccprintf("sig: %ph\n", HEX_BUF(&sig, sizeof(sig)));
+
+ cflush();
+
+ return 0;
+}
+
+DECLARE_SAFE_CONSOLE_COMMAND(u2f_test, cmd_u2f_test, NULL,
+ "Test U2F functionality");
+
+#endif
diff --git a/board/cr50/u2f_state_load.c b/board/cr50/u2f_state_load.c
new file mode 100644
index 0000000000..a1c8927dab
--- /dev/null
+++ b/board/cr50/u2f_state_load.c
@@ -0,0 +1,198 @@
+/* Copyright 2021 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 "console.h"
+#include "new_nvmem.h"
+#include "nvmem.h"
+#include "nvmem_vars.h"
+#include "tpm_nvmem_ops.h"
+#include "tpm_vendor_cmds.h"
+#include "u2f_impl.h"
+#include "util.h"
+
+/* For test/u2f.c we provide a mock-up implementation of u2f_get_state(). */
+#ifndef U2F_TEST
+static const uint8_t k_salt = NVMEM_VAR_G2F_SALT;
+static const uint8_t k_salt_deprecated = NVMEM_VAR_U2F_SALT;
+
+#define CPRINTF(format, args...) cprintf(CC_EXTENSION, format, ##args)
+
+bool u2f_load_or_create_state(struct u2f_state *state, bool force_create)
+{
+ bool g2f_secret_was_created = false;
+
+ const struct tuple *t_salt = NULL;
+
+ t_salt = getvar(&k_salt, sizeof(k_salt));
+
+ if (force_create && t_salt) {
+ /* Remove k_salt variable first. */
+ freevar(t_salt);
+ setvar(&k_salt, sizeof(k_salt), NULL, 0);
+ t_salt = NULL;
+ }
+
+ /* Load or create G2F secret. */
+ if (!t_salt) {
+ g2f_secret_was_created = true;
+ if (u2f_generate_g2f_secret(state) != EC_SUCCESS)
+ return false;
+
+ /* Delete the old salt if present, no-op if not. */
+ if (setvar(&k_salt_deprecated, sizeof(k_salt_deprecated), NULL,
+ 0) != EC_SUCCESS)
+ return false;
+ if (setvar(&k_salt, sizeof(k_salt),
+ (const uint8_t *)state->salt,
+ sizeof(state->salt)) != EC_SUCCESS)
+ return false;
+ } else {
+ memcpy(state->salt, tuple_val(t_salt), sizeof(state->salt));
+ freevar(t_salt);
+ }
+
+ /* Load or create HMAC key. Force creation if G2F wasn't loaded. */
+ if (g2f_secret_was_created ||
+ read_tpm_nvmem_hidden(TPM_HIDDEN_U2F_KEK, sizeof(state->hmac_key),
+ state->hmac_key) != TPM_READ_SUCCESS) {
+ if (u2f_generate_hmac_key(state) != EC_SUCCESS)
+ return false;
+
+ if (write_tpm_nvmem_hidden(
+ TPM_HIDDEN_U2F_KEK, sizeof(state->hmac_key),
+ state->hmac_key, 1 /* commit */) == TPM_WRITE_FAIL)
+ return false;
+ }
+
+ /* Load or create DRBG entropy. Force creation if G2F wasn't loaded. */
+ state->drbg_entropy_size = read_tpm_nvmem_size(TPM_HIDDEN_U2F_KH_SALT);
+
+ if (g2f_secret_was_created ||
+ ((state->drbg_entropy_size != sizeof(state->drbg_entropy)) &&
+ (state->drbg_entropy_size != 32)) ||
+ (read_tpm_nvmem_hidden(TPM_HIDDEN_U2F_KH_SALT,
+ state->drbg_entropy_size,
+ state->drbg_entropy) != TPM_READ_SUCCESS)) {
+
+ if (u2f_generate_drbg_entropy(state) != EC_SUCCESS)
+ return false;
+
+ /**
+ * We are in the inconsistent state with only G2F valid.
+ * This could be a result of very old platform being updated.
+ * In such case continue to use old, non FIPS path which is
+ * indicated by 'old' DRBG entropy size.
+ *
+ * Note, that if keys weren't properly created all at once it
+ * will continue in non-FIPS mode until keys are deleted and
+ * properly created again.
+ */
+ if (!g2f_secret_was_created)
+ state->drbg_entropy_size = 32;
+
+ if (write_tpm_nvmem_hidden(TPM_HIDDEN_U2F_KH_SALT,
+ state->drbg_entropy_size,
+ state->drbg_entropy,
+ 1 /* commit */) == TPM_WRITE_FAIL) {
+ state->drbg_entropy_size = 0;
+ return false;
+ }
+ }
+
+ /**
+ * If we loaded G2F secrets, but failed to load U2F secrets, it means
+ * we should continue in non FIPS mode until all keys will be recreated
+ * properly.
+ *
+ * On first run after update:
+ * 1. Load G2F key
+ * 2. Failed or succeeded to load HMAC. Failing at this point means
+ * DRBG load will also fail.
+ * 3. Failed to load DRBG, created DRBG with size = 32 as
+ * g2f_secret_was_created == false
+ *
+ * On subsequent runs it will load DRBG size == 32 until keys would be
+ * removed and recreated.
+ */
+
+ return true;
+}
+
+/**
+ * Get the current u2f state from the board.
+ */
+static bool u2f_state_loaded;
+static struct u2f_state u2f_state;
+
+struct u2f_state *u2f_get_state(void)
+{
+ if (!u2f_state_loaded)
+ u2f_state_loaded = u2f_load_or_create_state(&u2f_state, false);
+
+ return u2f_state_loaded ? &u2f_state : NULL;
+}
+
+enum ec_error_list u2f_gen_kek_seed(int commit)
+{
+ struct u2f_state *state = u2f_get_state();
+
+ if (!state)
+ return EC_ERROR_UNKNOWN;
+
+ if (!u2f_generate_hmac_key(state))
+ return EC_ERROR_HW_INTERNAL;
+
+ if (write_tpm_nvmem_hidden(TPM_HIDDEN_U2F_KEK, sizeof(state->hmac_key),
+ state->hmac_key, commit) == TPM_WRITE_FAIL)
+ return EC_ERROR_UNKNOWN;
+
+ return EC_SUCCESS;
+}
+
+/* Can't include TPM2 headers, so just define constant locally. */
+#define HR_NV_INDEX (1U << 24)
+
+enum ec_error_list u2f_zeroize_keys(void)
+{
+ const uint32_t u2fobjs[] = { TPM_HIDDEN_U2F_KEK | HR_NV_INDEX,
+ TPM_HIDDEN_U2F_KH_SALT | HR_NV_INDEX, 0 };
+
+ enum ec_error_list result1, result2;
+
+ /* Delete NVMEM_VAR_G2F_SALT. */
+ result1 = setvar(&k_salt, sizeof(k_salt), NULL, 0);
+
+ /* Remove U2F keys and wipe all deleted objects. */
+ result2 = nvmem_erase_tpm_data_selective(u2fobjs);
+
+ always_memset(&u2f_state, 0, sizeof(u2f_state));
+ u2f_state_loaded = false;
+ if ((result1 == EC_SUCCESS) && (result2 != EC_SUCCESS))
+ result1 = result2;
+
+ return result1;
+}
+
+enum ec_error_list u2f_update_keys(void)
+{
+ struct u2f_state *state = u2f_get_state();
+ enum ec_error_list result = EC_SUCCESS;
+
+ /* if we couldn't load state or state is not representing new keys */
+ if (!state || state->drbg_entropy_size != sizeof(state->drbg_entropy)) {
+ result = u2f_zeroize_keys();
+ /* Force creation of new keys. */
+ u2f_state_loaded = u2f_load_or_create_state(&u2f_state, true);
+
+ /* try to load again */
+ state = u2f_get_state();
+ }
+ if (!state || state->drbg_entropy_size != sizeof(state->drbg_entropy))
+ result = EC_ERROR_HW_INTERNAL;
+
+ return result;
+}
+
+#endif /* U2F_TEST */