summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Stevens <stevensd@chromium.org>2021-08-25 01:22:24 +0000
committerDavid Stevens <stevensd@chromium.org>2021-08-25 01:24:10 +0000
commit1485393c069ed6c4b9419154664e7407e7022c2e (patch)
treeb46e568e7a7d49966adeaacc1d3d7c2633084a85
parent5ae1c684271a117539858cb12252959dfe46803c (diff)
downloadchrome-ec-stabilize-14179.B-cr50_stab.tar.gz
Revert "u2f: refactoring to split command processing and crypto"stabilize-14179.B-cr50_stabstabilize-14178.B-cr50_stab
This reverts commit 5ae1c684271a117539858cb12252959dfe46803c. Reason for revert: breaks chromeos-ec-headers BUG=b:197691499 Original change's description: > u2f: refactoring to split command processing and crypto > > Split U2F crypto from U2F command processing by moving all crypto > code into boards/cr50 (platform hooks). > > U2F state management is part of common code and passed to U2F crypto > as a parameter. > > BUG=b:134594373 > TEST=make BOARD=cr50 CRYPTO_TEST=1 > console: u2f_test > test/tpmtest.py > FAFT U2F tests pass > > Signed-off-by: Vadim Sukhomlinov <sukhomlinov@google.com> > Change-Id: I85442cddb2959bd3102f7f6e6047134ede90951b > Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/3034852 > Reviewed-by: Vadim Sukhomlinov <sukhomlinov@chromium.org> > Reviewed-by: Andrey Pronin <apronin@chromium.org> > Tested-by: Vadim Sukhomlinov <sukhomlinov@chromium.org> > Commit-Queue: Vadim Sukhomlinov <sukhomlinov@chromium.org> Bug: b:134594373 Change-Id: I61a965995fcd53b4e155084f5f351574cb84cd1e Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/3115930 Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com> Owners-Override: David Stevens <stevensd@chromium.org>
-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
-rw-r--r--chip/host/build.mk3
-rw-r--r--common/u2f.c547
-rw-r--r--include/physical_presence.h14
-rw-r--r--include/u2f.h (renamed from include/u2f_cmds.h)125
-rw-r--r--include/u2f_impl.h296
-rw-r--r--test/build.mk2
-rw-r--r--test/u2f.c69
14 files changed, 787 insertions, 1308 deletions
diff --git a/board/cr50/build.mk b/board/cr50/build.mk
index 42a095e54c..e3b2555a8d 100644
--- a/board/cr50/build.mk
+++ b/board/cr50/build.mk
@@ -103,7 +103,6 @@ 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 554b048c25..6642bd3396 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_cmds.h"
+#include "u2f_impl.h"
/**
* Create IRQ handler calling FIPS module's dcrypto_done_interrupt() on
@@ -68,46 +68,47 @@ static void fips_print_status(void)
}
DECLARE_HOOK(HOOK_INIT, fips_print_status, HOOK_PRIO_INIT_PRINT_FIPS_STATUS);
-#if defined(CRYPTO_TEST_SETUP) || defined(CR50_DEV)
+#ifdef CRYPTO_TEST_SETUP
static const uint8_t k_salt = NVMEM_VAR_G2F_SALT;
-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);
+/* Can't include TPM2 headers, so just define constant locally. */
+#define HR_NV_INDEX (1U << 24)
- 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)
+/* Wipe old U2F keys. */
+static void u2f_zeroize_non_fips(void)
{
- CPRINTS("U2F state %x", (uintptr_t)u2f_get_state());
- print_u2f_keys_status();
+ 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);
}
-/* Set U2F keys as old. */
-static void fips_set_old_u2f_keys(void)
+/* Set U2F keys to old or new version. */
+static void fips_set_u2f_keys(bool active)
{
- 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);
+ 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);
}
#endif
@@ -126,20 +127,11 @@ static int cmd_fips_status(int argc, char **argv)
fips_print_test_time();
fips_print_mode();
}
-#if defined(CRYPTO_TEST_SETUP) || defined(CR50_DEV)
+#ifdef CRYPTO_TEST_SETUP
else if (!strncmp(argv[1], "new", 3))
- CPRINTS("u2f update status: %d", u2f_update_keys());
- else if (!strncmp(argv[1], "del", 3))
- CPRINTS("u2f zeroization status: %d",
- u2f_zeroize_keys());
+ fips_set_u2f_keys(true); /* we can reboot here... */
else if (!strncmp(argv[1], "old", 3))
- 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
+ fips_set_u2f_keys(false); /* we can reboot here... */
else if (!strncmp(argv[1], "trng", 4))
fips_break_cmd = FIPS_BREAK_TRNG;
else if (!strncmp(argv[1], "sha", 3))
@@ -152,7 +144,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 | u2f | gen | trng | sha]",
+ "[test | new | old | trng | sha]",
"Report FIPS status, switch U2F key, run tests, simulate errors");
#else
"[test]", "Report FIPS status, run tests");
@@ -189,10 +181,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:
- u2f_update_keys();
+ fips_set_u2f_keys(true); /* we can reboot here... */
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 a13f450031..2d22966273 100644
--- a/board/cr50/power_button.c
+++ b/board/cr50/power_button.c
@@ -15,6 +15,7 @@
#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)
@@ -307,43 +308,6 @@ 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 267ac7cf7a..07851b9a23 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_cmds.h"
+#include "u2f_impl.h"
#include "util.h"
#include "version.h"
diff --git a/board/cr50/tpm2/virtual_nvmem.c b/board/cr50/tpm2/virtual_nvmem.c
index 3ddaed067d..dc65d75f46 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_cmds.h"
+#include "u2f_impl.h"
#include "virtual_nvmem.h"
/*
diff --git a/board/cr50/u2f.c b/board/cr50/u2f.c
index 78bc25c01f..43082d5008 100644
--- a/board/cr50/u2f.c
+++ b/board/cr50/u2f.c
@@ -3,179 +3,181 @@
* found in the LICENSE file.
*/
-#if defined(CRYPTO_TEST_SETUP) || defined(CR50_DEV)
-#include "console.h"
-#endif
+/* Helpers to emulate a U2F HID dongle over the TPM transport */
+#include "console.h"
#include "dcrypto.h"
-#include "fips_rand.h"
-
-#include "u2f_cmds.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 "u2f_impl.h"
#include "util.h"
-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;
-}
+#define CPRINTS(format, args...) cprints(CC_EXTENSION, format, ## args)
-enum ec_error_list u2f_generate_drbg_entropy(struct u2f_state *state)
-{
- 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;
-}
+/* ---- physical presence (using the laptop power button) ---- */
-enum ec_error_list u2f_generate_g2f_secret(struct u2f_state *state)
+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)
{
- /* G2F specific path. */
- if (!fips_rand_bytes(state->salt, sizeof(state->salt)))
- return EC_ERROR_HW_INTERNAL;
- return EC_SUCCESS;
+ if (ap_is_on() && rbox_powerbtn_is_pressed()) {
+ last_press = get_time();
+#ifdef CR50_DEV
+ CPRINTS("record pp");
+#endif
+ }
}
-/* 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)
+enum touch_state pop_check_presence(int consume)
{
- 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();
+#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
- 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();
+ if (consume)
+ last_press.val = 0;
+
+ /* user physical presence on the power button */
+ return recent ? POP_TOUCH_YES : POP_TOUCH_NO;
#endif
}
-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)
+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)
{
- 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;
+ 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;
}
- /* 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;
+
+ 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;
}
- /**
- * 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);
+ return 1;
+}
+
+struct u2f_state *get_state(void)
+{
+ static int state_loaded;
+ static struct u2f_state state;
- HMAC_SHA256_update(&ctx, auth_time_secret_hash,
- U2F_AUTH_TIME_SECRET_SIZE);
+ if (!state_loaded)
+ state_loaded = load_state(&state);
- memcpy(kh_auth_mac, HMAC_SHA256_final(&ctx), SHA256_DIGEST_SIZE);
+ return state_loaded ? &state : NULL;
}
-static int app_hw_device_id(enum dcrypto_appid appid, const uint32_t input[8],
- uint32_t output[8])
+/* ---- chip-specific U2F crypto ---- */
+
+static int _derive_key(enum dcrypto_appid appid, const uint32_t input[8],
+ uint32_t output[8])
{
struct APPKEY_CTX ctx;
int result;
- /**
- * 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.
- */
+ /* Setup USR-based application key. */
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;
}
-/**
- * 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)
+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)
{
uint32_t dev_salt[P256_NDIGITS];
uint8_t key_seed[P256_NBYTES];
struct drbg_ctx drbg;
- 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;
- }
+ struct u2f_state *state = get_state();
- /* TODO(sukhomlinov): implement new FIPS path. */
- if (!app_hw_device_id(U2F_ORIGIN, state->hmac_key, dev_salt))
+ if (!state)
return EC_ERROR_UNKNOWN;
- hmac_drbg_init(&drbg, state->drbg_entropy, P256_NBYTES, dev_salt,
+ if (!_derive_key(U2F_ORIGIN, state->salt_kek, dev_salt))
+ return EC_ERROR_UNKNOWN;
+
+ hmac_drbg_init(&drbg, state->salt_kh, P256_NBYTES, dev_salt,
P256_NBYTES, NULL, 0);
hmac_drbg_generate(&drbg, key_seed, sizeof(key_seed), key_handle,
@@ -184,243 +186,41 @@ static enum ec_error_list u2f_origin_user_key_pair(
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;
}
-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)
+int u2f_gen_kek(const uint8_t *origin, uint8_t *kek, size_t key_len)
{
- 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);
-
- p256_clear(&od);
- } while (generate_key_pair_rc == EC_ERROR_TRY_AGAIN);
+ uint32_t buf[P256_NDIGITS];
- if (generate_key_pair_rc != EC_SUCCESS)
- return generate_key_pair_rc;
+ struct u2f_state *state = get_state();
- 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);
- }
+ if (!state)
+ return EC_ERROR_UNKNOWN;
- pubKey->pointFormat = U2F_POINT_UNCOMPRESSED;
- p256_to_bin(&opk_x, pubKey->x); /* endianness */
- p256_to_bin(&opk_y, pubKey->y); /* endianness */
+ 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);
return EC_SUCCESS;
}
-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)
-{
- /* 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;
-}
-
-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)
+int g2f_individual_keypair(p256_int *d, p256_int *pk_x, p256_int *pk_y)
{
- /* Origin private key. */
- p256_int origin_d;
-
- /* Hash, and corresponding signature. */
- p256_int h, r, s;
-
- 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);
-
- p256_to_bin(&r, sig->sig_r);
- p256_to_bin(&s, sig->sig_s);
+ uint8_t buf[SHA256_DIGEST_SIZE];
- return result;
-}
+ struct u2f_state *state = get_state();
-/**
- * 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];
+ if (!state)
+ return EC_ERROR_UNKNOWN;
- /* Incorporate HIK & diversification constant. */
- if (!app_hw_device_id(U2F_ATTEST, state->salt, (uint32_t *)buf))
- return false;
+ /* Incorporate HIK & diversification constant */
+ if (!_derive_key(U2F_ATTEST, state->salt, (uint32_t *)buf))
+ return EC_ERROR_UNKNOWN;
- /* Generate unbiased private key (non-FIPS path). */
+ /* Generate unbiased private key */
while (!DCRYPTO_p256_key_from_bytes(pk_x, pk_y, d, buf)) {
struct sha256_ctx sha;
@@ -429,208 +229,22 @@ static bool g2f_individual_key_pair(const struct u2f_state *state, p256_int *d,
memcpy(buf, SHA256_final(&sha), sizeof(buf));
}
- return true;
-}
-
-#define G2F_CERT_NAME "CrO2"
-
-size_t g2f_attestation_cert_serial(const struct u2f_state *state,
- const uint8_t *serial, uint8_t *buf)
-{
- p256_int d, pk_x, pk_y;
-
- 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);
+ return EC_SUCCESS;
}
-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)
+int u2f_gen_kek_seed(int commit)
{
- struct sha256_ctx h_ctx;
- struct drbg_ctx dr_ctx;
-
- /* Data hash, and corresponding signature. */
- p256_int h, r, s;
+ struct u2f_state *state = get_state();
- /* 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 (!state)
+ return EC_ERROR_UNKNOWN;
- /* 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
+ if (!DCRYPTO_ladder_random(state->salt_kek))
return EC_ERROR_HW_INTERNAL;
- }
-
- /* Message signature. */
- SHA256_hw_init(&h_ctx);
- SHA256_update(&h_ctx, data, data_size);
- p256_from_bin(SHA256_final(&h_ctx)->b8, &h);
-
- /* 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";
-}
+ if (write_tpm_nvmem_hidden(TPM_HIDDEN_U2F_KEK, sizeof(state->salt_kek),
+ state->salt_kek, commit) == TPM_WRITE_FAIL)
+ return EC_ERROR_UNKNOWN;
-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;
+ return EC_SUCCESS;
}
-
-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
deleted file mode 100644
index a1c8927dab..0000000000
--- a/board/cr50/u2f_state_load.c
+++ /dev/null
@@ -1,198 +0,0 @@
-/* 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 */
diff --git a/chip/host/build.mk b/chip/host/build.mk
index fd7f75f3c0..0ea6027533 100644
--- a/chip/host/build.mk
+++ b/chip/host/build.mk
@@ -37,9 +37,6 @@ chip-$(CONFIG_DCRYPTO)+= ../../board/cr50/dcrypto/hmac_sw.o
chip-$(CONFIG_DCRYPTO)+= ../../board/cr50/dcrypto/sha1.o
chip-$(CONFIG_DCRYPTO)+= ../../board/cr50/dcrypto/sha256.o
chip-$(CONFIG_DCRYPTO)+= ../../board/cr50/dcrypto/hmac_drbg.o
-chip-$(CONFIG_DCRYPTO)+= ../../board/cr50/dcrypto/p256.o
-chip-$(CONFIG_DCRYPTO)+= ../../board/cr50/dcrypto/compare.o
-chip-$(CONFIG_DCRYPTO)+= ../../board/cr50/dcrypto/hkdf.o
# We still want raw SHA & HMAC implementations for mocked dcrypto
chip-$(CONFIG_DCRYPTO_MOCK)+= ../../board/cr50/dcrypto/sha256.o
diff --git a/common/u2f.c b/common/u2f.c
index e03e6819f7..f5f91376d1 100644
--- a/common/u2f.c
+++ b/common/u2f.c
@@ -9,82 +9,118 @@
#include "dcrypto.h"
#include "extension.h"
-#include "nvmem_vars.h"
-#include "physical_presence.h"
#include "system.h"
-#include "tpm_nvmem_ops.h"
-#include "tpm_vendor_cmds.h"
-#include "u2f_cmds.h"
#include "u2f_impl.h"
+#include "u2f.h"
#include "util.h"
+#define G2F_CERT_NAME "CrO2"
+
#define CPRINTF(format, args...) cprintf(CC_EXTENSION, format, ##args)
+/* Crypto parameters */
+#define AES_BLOCK_LEN 16
+#define KH_LEN 64
-size_t g2f_attestation_cert(uint8_t *buf)
+static int individual_cert(const p256_int *d, const p256_int *pk_x,
+ const p256_int *pk_y, uint8_t *cert, const int n)
{
- uint8_t *serial;
-
- const struct u2f_state *state = u2f_get_state();
+ p256_int *serial;
- if (!state)
+ if (system_get_chip_unique_id((uint8_t **)&serial) != P256_NBYTES)
return 0;
- if (system_get_chip_unique_id(&serial) != P256_NBYTES)
+ return DCRYPTO_x509_gen_u2f_cert_name(d, pk_x, pk_y, serial,
+ G2F_CERT_NAME, cert, n);
+}
+
+int g2f_attestation_cert(uint8_t *buf)
+{
+ p256_int d, pk_x, pk_y;
+
+ if (g2f_individual_keypair(&d, &pk_x, &pk_y))
return 0;
- return g2f_attestation_cert_serial(state, serial, buf);
+ /* Note that max length is not currently respected here. */
+ return individual_cert(&d, &pk_x, &pk_y, buf,
+ G2F_ATTESTATION_CERT_MAX_LEN);
}
-/* U2F GENERATE command */
-enum vendor_cmd_rc u2f_generate_cmd(enum vendor_cmd_cc code, void *buf,
- size_t input_size, size_t *response_size)
+static void copy_kh_pubkey_out(p256_int *opk_x, p256_int *opk_y,
+ struct u2f_key_handle *kh, void *buf)
{
- struct u2f_generate_req *req = buf;
struct u2f_generate_resp *resp = buf;
- struct u2f_generate_versioned_resp *resp_versioned = buf;
- struct u2f_ec_point *pubKey;
- const struct u2f_state *state = u2f_get_state();
+ /* Insert origin-specific public keys into the response */
+ p256_to_bin(opk_x, resp->pubKey.x); /* endianness */
+ p256_to_bin(opk_y, resp->pubKey.y); /* endianness */
+
+ resp->pubKey.pointFormat = U2F_POINT_UNCOMPRESSED;
+
+ /* Copy key handle to response. */
+ memcpy(&resp->keyHandle, kh, sizeof(struct u2f_key_handle));
+}
+
+static void copy_versioned_kh_pubkey_out(p256_int *opk_x, p256_int *opk_y,
+ struct u2f_versioned_key_handle *kh,
+ void *buf)
+{
+ struct u2f_generate_versioned_resp *resp = buf;
+
+ /* Insert origin-specific public keys into the response */
+ p256_to_bin(opk_x, resp->pubKey.x); /* endianness */
+ p256_to_bin(opk_y, resp->pubKey.y); /* endianness */
+
+ resp->pubKey.pointFormat = U2F_POINT_UNCOMPRESSED;
+
+ /* Copy key handle to response. */
+ memcpy(&resp->keyHandle, kh, sizeof(struct u2f_versioned_key_handle));
+}
+
+/* U2F GENERATE command */
+enum vendor_cmd_rc u2f_generate(enum vendor_cmd_cc code, void *buf,
+ size_t input_size, size_t *response_size)
+{
+ struct u2f_generate_req *req = buf;
uint8_t kh_version =
(req->flags & U2F_UV_ENABLED_KH) ? U2F_KH_VERSION_1 : 0;
- /**
- * Buffer for generating key handle as part of response. Note, it
- * overlaps with authTimeSecret in response since request and response
- * shares same buffer.
- */
- union u2f_key_handle_variant *kh_buf;
-
- uint8_t authTimeSecretHash[U2F_AUTH_TIME_SECRET_SIZE];
+ /* Origin keypair. Must be word aligned, otherwise TRNG will crash. */
+ uint8_t od_seed[P256_NBYTES] __aligned(4);
+ p256_int od, opk_x, opk_y;
+
+ /* Buffer for generating key handle. */
+ union {
+ struct u2f_key_handle kh;
+ struct u2f_versioned_key_handle vkh;
+ } kh_buf;
+ size_t keypair_input_size =
+ (kh_version == 0) ?
+ sizeof(kh_buf.kh) :
+ sizeof(struct u2f_versioned_key_handle_header);
+
+ /* Whether key handle generation succeeded */
+ int generate_kh_rc;
+ /* Whether keypair generation succeeded */
+ int generate_keypair_rc;
size_t response_buf_size = *response_size;
- enum ec_error_list result;
+
+ /* Authorization salt for versioned KHs */
+ uint8_t *authorization_salt;
*response_size = 0;
if (input_size != sizeof(struct u2f_generate_req))
return VENDOR_RC_BOGUS_ARGS;
- if (state == NULL)
- return VENDOR_RC_INTERNAL_ERROR;
-
- /* Copy to avoid overwriting data before use. */
- memcpy(authTimeSecretHash, req->authTimeSecretHash,
- sizeof(authTimeSecretHash));
-
if (kh_version == 0) {
if (response_buf_size < sizeof(struct u2f_generate_resp))
return VENDOR_RC_BOGUS_ARGS;
- pubKey = &resp->pubKey;
- kh_buf = (union u2f_key_handle_variant *)&resp->keyHandle;
} else {
if (response_buf_size <
sizeof(struct u2f_generate_versioned_resp))
return VENDOR_RC_BOGUS_ARGS;
- pubKey = &resp_versioned->pubKey;
- kh_buf = (union u2f_key_handle_variant *)&resp_versioned
- ->keyHandle;
}
/* Maybe enforce user presence, w/ optional consume */
@@ -92,16 +128,29 @@ enum vendor_cmd_rc u2f_generate_cmd(enum vendor_cmd_cc code, void *buf,
(req->flags & U2F_AUTH_FLAG_TUP) != 0)
return VENDOR_RC_NOT_ALLOWED;
- /**
- * req->userSecret and req->appId are consumed by u2f_generate() before
- * being overwritten.
- */
- result = u2f_generate(state, req->userSecret, req->appId,
- authTimeSecretHash, kh_buf, kh_version, pubKey);
-
- always_memset(authTimeSecretHash, 0, sizeof(authTimeSecretHash));
-
- if (result != EC_SUCCESS)
+ /* Generate origin-specific keypair */
+ do {
+ if (!DCRYPTO_ladder_random(&od_seed))
+ return VENDOR_RC_INTERNAL_ERROR;
+
+ if (kh_version == 0)
+ generate_kh_rc = u2f_origin_user_keyhandle(
+ req->appId, req->userSecret, od_seed,
+ &kh_buf.kh);
+ else
+ generate_kh_rc = u2f_origin_user_versioned_keyhandle(
+ req->appId, req->userSecret, od_seed,
+ kh_version, &kh_buf.vkh.header);
+
+ if (generate_kh_rc != EC_SUCCESS)
+ return VENDOR_RC_INTERNAL_ERROR;
+
+ generate_keypair_rc = u2f_origin_user_keypair(
+ (uint8_t *)&kh_buf, keypair_input_size, &od, &opk_x,
+ &opk_y);
+ } while (generate_keypair_rc == EC_ERROR_TRY_AGAIN);
+
+ if (generate_keypair_rc != EC_SUCCESS)
return VENDOR_RC_INTERNAL_ERROR;
/*
@@ -109,75 +158,166 @@ enum vendor_cmd_rc u2f_generate_cmd(enum vendor_cmd_cc code, void *buf,
* overridden by the response we are building in the same buffer.
*/
if (kh_version == 0) {
+ copy_kh_pubkey_out(&opk_x, &opk_y, &kh_buf.kh, buf);
*response_size = sizeof(struct u2f_generate_resp);
} else {
+ authorization_salt = od_seed;
+ /* Generate in word-aligned array so that TRNG doesn't crash */
+ if (!DCRYPTO_ladder_random(authorization_salt))
+ return VENDOR_RC_INTERNAL_ERROR;
+
+ if (u2f_authorization_hmac(
+ authorization_salt, &kh_buf.vkh.header,
+ req->authTimeSecretHash,
+ kh_buf.vkh.authorization_hmac) != EC_SUCCESS)
+ return VENDOR_RC_INTERNAL_ERROR;
+
+ memcpy(&kh_buf.vkh.authorization_salt, authorization_salt,
+ U2F_AUTHORIZATION_SALT_SIZE);
+ copy_versioned_kh_pubkey_out(&opk_x, &opk_y, &kh_buf.vkh, buf);
*response_size = sizeof(struct u2f_generate_versioned_resp);
}
return VENDOR_RC_SUCCESS;
}
-DECLARE_VENDOR_COMMAND(VENDOR_CC_U2F_GENERATE, u2f_generate_cmd);
+DECLARE_VENDOR_COMMAND(VENDOR_CC_U2F_GENERATE, u2f_generate);
+
+static int verify_kh_pubkey(const uint8_t *key_handle, size_t key_handle_size,
+ const struct u2f_ec_point *public_key, int *matches)
+{
+ int rc;
+ struct u2f_ec_point kh_pubkey;
+ p256_int od, opk_x, opk_y;
+
+ rc = u2f_origin_user_keypair(key_handle, key_handle_size, &od, &opk_x,
+ &opk_y);
+ if (rc != EC_SUCCESS)
+ return rc;
+
+ /* 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;
+
+ *matches = safe_memcmp(&kh_pubkey, public_key,
+ sizeof(struct u2f_ec_point)) == 0;
+
+ return EC_SUCCESS;
+}
+
+static int verify_kh_owned(const uint8_t *user_secret, const uint8_t *app_id,
+ const struct u2f_key_handle *key_handle, int *owned)
+{
+ int rc;
+ /* Re-created key handle. */
+ struct u2f_key_handle recreated_kh;
+
+ /*
+ * 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 app_id.
+ */
+
+ rc = u2f_origin_user_keyhandle(app_id, user_secret,
+ key_handle->origin_seed, &recreated_kh);
+
+ if (rc == EC_SUCCESS)
+ *owned = safe_memcmp(&recreated_kh, key_handle,
+ sizeof(recreated_kh)) == 0;
+
+ return rc;
+}
+
+static int verify_versioned_kh_owned(
+ const uint8_t *user_secret, const uint8_t *app_id,
+ const struct u2f_versioned_key_handle_header *key_handle_header,
+ int *owned)
+{
+ int rc;
+ /* Re-created key handle. */
+ struct u2f_versioned_key_handle_header recreated_kh_header;
+
+ /*
+ * 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 app_id.
+ */
+
+ rc = u2f_origin_user_versioned_keyhandle(app_id, user_secret,
+ key_handle_header->origin_seed,
+ key_handle_header->version,
+ &recreated_kh_header);
+
+ if (rc == EC_SUCCESS)
+ *owned = safe_memcmp(&recreated_kh_header, key_handle_header,
+ sizeof(recreated_kh_header)) == 0;
+
+ return rc;
+}
/* Below, we depend on the response not being larger than than the request. */
BUILD_ASSERT(sizeof(struct u2f_sign_resp) <= sizeof(struct u2f_sign_req));
/* U2F SIGN command */
-enum vendor_cmd_rc u2f_sign_cmd(enum vendor_cmd_cc code, void *buf,
- size_t input_size, size_t *response_size)
+enum vendor_cmd_rc u2f_sign(enum vendor_cmd_cc code, void *buf,
+ size_t input_size, size_t *response_size)
{
const struct u2f_sign_req *req = buf;
const struct u2f_sign_versioned_req *req_versioned = buf;
- union u2f_key_handle_variant *kh;
+ const uint8_t *key_handle, *hash;
+ uint8_t flags;
+ struct u2f_sign_resp *resp;
- const struct u2f_state *state = u2f_get_state();
+ struct drbg_ctx ctx;
- const uint8_t *hash, *user, *origin /* TODO: *authTimeSecret = NULL */;
+ /* Whether the key handle is owned by this device. */
+ int kh_owned = 0;
- uint8_t flags;
- struct u2f_sign_resp *resp;
+ /* Origin private key. */
+ p256_int origin_d;
+
+ /* Hash, and corresponding signature. */
+ p256_int h, r, s;
/* Version of KH; 0 if KH is not versioned. */
- uint8_t kh_version;
+ uint8_t version;
- enum ec_error_list result;
+ /* Size of the part of KH used to derive keypair, in bytes. */
+ size_t keypair_input_size;
+
+ int verify_owned_rc;
/* Response is smaller than request, so no need to check this. */
*response_size = 0;
- if (!state)
- return VENDOR_RC_INTERNAL_ERROR;
-
- /**
- * Request can be in old (non-versioned) and new (versioned) formats,
- * which differs in size. Use request size to distinguish it.
- */
if (input_size == sizeof(struct u2f_sign_req)) {
- kh_version = 0;
- kh = (union u2f_key_handle_variant *)&req->keyHandle;
+ version = 0;
+ key_handle = (uint8_t *)&req->keyHandle;
hash = req->hash;
flags = req->flags;
- user = req->userSecret;
- origin = req->appId;
+ keypair_input_size = sizeof(struct u2f_key_handle);
+ verify_owned_rc = verify_kh_owned(req->userSecret, req->appId,
+ &req->keyHandle, &kh_owned);
} else if (input_size == sizeof(struct u2f_sign_versioned_req)) {
- kh_version = req_versioned->keyHandle.version;
- kh = (union u2f_key_handle_variant *)&req_versioned->keyHandle;
+ version = req_versioned->keyHandle.header.version;
+ key_handle = (uint8_t *)&req_versioned->keyHandle;
hash = req_versioned->hash;
flags = req_versioned->flags;
- user = req_versioned->userSecret;
- origin = req_versioned->appId;
- /* TODO: authTimeSecret = req_versioned->authTimeSecret; */
+ keypair_input_size =
+ sizeof(struct u2f_versioned_key_handle_header);
+ verify_owned_rc = verify_versioned_kh_owned(
+ req_versioned->userSecret, req_versioned->appId,
+ &req_versioned->keyHandle.header, &kh_owned);
} else {
return VENDOR_RC_BOGUS_ARGS;
}
- /* TODO(b/184393647): pass authTimeSecret when ready. */
- result = u2f_authorize_keyhandle(state, kh, kh_version, user, origin,
- NULL);
- if (result == EC_ERROR_ACCESS_DENIED)
- return VENDOR_RC_PASSWORD_REQUIRED;
- if (result != EC_SUCCESS)
+ if (verify_owned_rc != EC_SUCCESS)
return VENDOR_RC_INTERNAL_ERROR;
+ if (!kh_owned)
+ return VENDOR_RC_PASSWORD_REQUIRED;
+
/* We might not actually need to sign anything. */
if ((flags & U2F_AUTH_CHECK_ONLY) == U2F_AUTH_CHECK_ONLY)
return VENDOR_RC_SUCCESS;
@@ -186,40 +326,99 @@ enum vendor_cmd_rc u2f_sign_cmd(enum vendor_cmd_cc code, void *buf,
* Enforce user presence for version 0 KHs, with optional consume.
*/
if (pop_check_presence(flags & G2F_CONSUME) != POP_TOUCH_YES) {
- if (kh_version != U2F_KH_VERSION_1)
+ if (version != U2F_KH_VERSION_1)
return VENDOR_RC_NOT_ALLOWED;
if ((flags & U2F_AUTH_FLAG_TUP) != 0)
return VENDOR_RC_NOT_ALLOWED;
+ /*
+ * TODO(yichengli): When auth-time secrets is ready, enforce
+ * authorization hmac when no power button press.
+ */
}
+ /* Re-create origin-specific key. */
+ if (u2f_origin_user_keypair(key_handle, keypair_input_size, &origin_d,
+ NULL, NULL) != EC_SUCCESS)
+ return VENDOR_RC_INTERNAL_ERROR;
+
+ /* Prepare hash to sign. */
+ p256_from_bin(hash, &h);
+
+ /* Sign. */
+ hmac_drbg_init_rfc6979(&ctx, &origin_d, &h);
+ if (!dcrypto_p256_ecdsa_sign(&ctx, &origin_d, &h, &r, &s)) {
+ p256_clear(&origin_d);
+ return VENDOR_RC_INTERNAL_ERROR;
+ }
+ p256_clear(&origin_d);
+
/*
- * u2f_sign first consume all data from request 'req', and compute
- * result in temporary storage. Once accomplished, it stores it in
- * provided buffer. This allows overlap between input and output
- * parameters.
+ * From this point: the request 'req' content is invalid as it is
+ * overridden by the response we are building in the same buffer.
* The response is smaller than the request, so we have the space.
*/
resp = buf;
- /**
- * TODO(b/184393647): When auth-time secrets is ready, enforce
- * authorization hmac when no power button press.
- * use u2f_authorize_keyhandle_with_secret() which requires
- * correct authorization mac to be provided by the caller.
- */
- result = u2f_sign(state, kh, kh_version, user, origin,
- NULL /* TODO: authTimeSecret */, hash, &resp->sig);
+ *response_size = sizeof(*resp);
- if (result == EC_ERROR_ACCESS_DENIED)
- return VENDOR_RC_PASSWORD_REQUIRED;
- if (result != EC_SUCCESS)
+ p256_to_bin(&r, resp->sig_r);
+ p256_to_bin(&s, resp->sig_s);
+
+ return VENDOR_RC_SUCCESS;
+}
+DECLARE_VENDOR_COMMAND(VENDOR_CC_U2F_SIGN, u2f_sign);
+
+struct g2f_register_msg {
+ uint8_t reserved;
+ uint8_t app_id[U2F_APPID_SIZE];
+ uint8_t challenge[U2F_CHAL_SIZE];
+ uint8_t key_handle[U2F_APPID_SIZE + sizeof(p256_int)];
+ struct u2f_ec_point public_key;
+};
+
+static inline int u2f_attest_verify_reg_resp(const uint8_t *user_secret,
+ uint8_t data_size,
+ const uint8_t *data)
+{
+ struct g2f_register_msg *msg = (void *)data;
+ int verified;
+ /* We only do u2f_attest on non-versioned KHs. */
+ const int key_handle_size = sizeof(struct u2f_key_handle);
+
+ if (data_size != sizeof(struct g2f_register_msg))
+ return VENDOR_RC_NOT_ALLOWED;
+
+ if (msg->reserved != 0)
+ return VENDOR_RC_NOT_ALLOWED;
+
+ if (verify_kh_owned(user_secret, msg->app_id,
+ (struct u2f_key_handle *)&msg->key_handle,
+ &verified) != EC_SUCCESS)
return VENDOR_RC_INTERNAL_ERROR;
- *response_size = sizeof(*resp);
+ if (!verified)
+ return VENDOR_RC_NOT_ALLOWED;
+
+ if (verify_kh_pubkey(msg->key_handle, key_handle_size, &msg->public_key,
+ &verified) != EC_SUCCESS)
+ return VENDOR_RC_INTERNAL_ERROR;
+
+ if (!verified)
+ return VENDOR_RC_NOT_ALLOWED;
return VENDOR_RC_SUCCESS;
}
-DECLARE_VENDOR_COMMAND(VENDOR_CC_U2F_SIGN, u2f_sign_cmd);
+
+static int u2f_attest_verify(const uint8_t *user_secret, uint8_t format,
+ uint8_t data_size, const uint8_t *data)
+{
+ switch (format) {
+ case U2F_ATTEST_FORMAT_REG_RESP:
+ return u2f_attest_verify_reg_resp(user_secret, data_size, data);
+ default:
+ return VENDOR_RC_NOT_ALLOWED;
+ }
+}
static inline size_t u2f_attest_format_size(uint8_t format)
{
@@ -232,23 +431,26 @@ static inline size_t u2f_attest_format_size(uint8_t format)
}
/* U2F ATTEST command */
-static enum vendor_cmd_rc u2f_attest_cmd(enum vendor_cmd_cc code, void *buf,
- size_t input_size,
- size_t *response_size)
+static enum vendor_cmd_rc u2f_attest(enum vendor_cmd_cc code, void *buf,
+ size_t input_size, size_t *response_size)
{
const struct u2f_attest_req *req = buf;
struct u2f_attest_resp *resp;
- struct g2f_register_msg *msg = (void *)req->data;
- enum ec_error_list result;
- size_t response_buf_size = *response_size;
+ int verify_ret;
- const struct u2f_state *state = u2f_get_state();
+ struct sha256_ctx h_ctx;
+ struct drbg_ctx dr_ctx;
- *response_size = 0;
+ /* Data hash, and corresponding signature. */
+ p256_int h, r, s;
- if (!state)
- return VENDOR_RC_INTERNAL_ERROR;
+ /* Attestation key */
+ p256_int d, pk_x, pk_y;
+
+ size_t response_buf_size = *response_size;
+
+ *response_size = 0;
if (input_size < offsetof(struct u2f_attest_req, data) ||
input_size <
@@ -257,41 +459,114 @@ static enum vendor_cmd_rc u2f_attest_cmd(enum vendor_cmd_cc code, void *buf,
response_buf_size < sizeof(*resp))
return VENDOR_RC_BOGUS_ARGS;
- /* Only one format is supported, key handle version is 0. */
- if (req->format != U2F_ATTEST_FORMAT_REG_RESP)
- return VENDOR_RC_NOT_ALLOWED;
+ verify_ret = u2f_attest_verify(req->userSecret, req->format,
+ req->dataLen, req->data);
- if (req->dataLen != sizeof(struct g2f_register_msg))
- return VENDOR_RC_NOT_ALLOWED;
+ if (verify_ret != VENDOR_RC_SUCCESS)
+ return verify_ret;
- if (msg->reserved != 0)
- return VENDOR_RC_NOT_ALLOWED;
+ /* Message signature */
+ SHA256_hw_init(&h_ctx);
+ SHA256_update(&h_ctx, req->data, u2f_attest_format_size(req->format));
+ p256_from_bin(SHA256_final(&h_ctx)->b8, &h);
+
+ /* Derive G2F Attestation Key */
+ if (g2f_individual_keypair(&d, &pk_x, &pk_y)) {
+ CPRINTF("G2F Attestation key generation failed");
+ return VENDOR_RC_INTERNAL_ERROR;
+ }
+
+ /* Sign over the response w/ the attestation key */
+ hmac_drbg_init_rfc6979(&dr_ctx, &d, &h);
+ if (!dcrypto_p256_ecdsa_sign(&dr_ctx, &d, &h, &r, &s)) {
+ CPRINTF("Signing error");
+ return VENDOR_RC_INTERNAL_ERROR;
+ }
+ p256_clear(&d);
/*
- * u2f_attest first consume all data from request 'req', and compute
- * result in temporary storage. Once accomplished, it stores it in
- * provided buffer. This allows overlap between input and output
- * parameters.
+ * From this point: the request 'req' content is invalid as it is
+ * overridden by the response we are building in the same buffer.
* The response is smaller than the request, so we have the space.
*/
resp = buf;
- /* TODO: If WebAuthn support is needed, pass AuthTimeSecret. */
- result = u2f_attest(state,
- (union u2f_key_handle_variant *)&msg->key_handle, 0,
- req->userSecret, msg->app_id, NULL,
- &msg->public_key, req->data,
- u2f_attest_format_size(req->format), &resp->sig);
-
- if (result == EC_ERROR_ACCESS_DENIED)
- return VENDOR_RC_NOT_ALLOWED;
+ *response_size = sizeof(*resp);
- if (result != EC_SUCCESS) {
- CPRINTF("G2F Attestation failed");
- return VENDOR_RC_INTERNAL_ERROR;
- }
+ p256_to_bin(&r, resp->sig_r);
+ p256_to_bin(&s, resp->sig_s);
- *response_size = sizeof(*resp);
return VENDOR_RC_SUCCESS;
}
-DECLARE_VENDOR_COMMAND(VENDOR_CC_U2F_ATTEST, u2f_attest_cmd);
+DECLARE_VENDOR_COMMAND(VENDOR_CC_U2F_ATTEST, u2f_attest);
+
+int u2f_origin_user_keyhandle(const uint8_t *origin, const uint8_t *user,
+ const uint8_t *origin_seed,
+ struct u2f_key_handle *key_handle)
+{
+ struct hmac_sha256_ctx ctx;
+ struct u2f_state *state = get_state();
+
+ if (!state)
+ return EC_ERROR_UNKNOWN;
+
+ memcpy(key_handle->origin_seed, origin_seed, P256_NBYTES);
+
+ HMAC_SHA256_hw_init(&ctx, state->salt_kek, SHA256_DIGEST_SIZE);
+ HMAC_SHA256_update(&ctx, origin, P256_NBYTES);
+ HMAC_SHA256_update(&ctx, user, P256_NBYTES);
+ HMAC_SHA256_update(&ctx, origin_seed, P256_NBYTES);
+
+ memcpy(key_handle->hmac, HMAC_SHA256_hw_final(&ctx),
+ SHA256_DIGEST_SIZE);
+
+ return EC_SUCCESS;
+}
+
+int u2f_origin_user_versioned_keyhandle(
+ const uint8_t *origin, const uint8_t *user, const uint8_t *origin_seed,
+ uint8_t version,
+ struct u2f_versioned_key_handle_header *key_handle_header)
+{
+ struct hmac_sha256_ctx ctx;
+ struct u2f_state *state = get_state();
+
+ if (!state)
+ return EC_ERROR_UNKNOWN;
+
+ key_handle_header->version = version;
+ memcpy(key_handle_header->origin_seed, origin_seed, P256_NBYTES);
+
+ HMAC_SHA256_hw_init(&ctx, state->salt_kek, SHA256_DIGEST_SIZE);
+ HMAC_SHA256_update(&ctx, origin, P256_NBYTES);
+ HMAC_SHA256_update(&ctx, user, P256_NBYTES);
+ HMAC_SHA256_update(&ctx, origin_seed, P256_NBYTES);
+ HMAC_SHA256_update(&ctx, &version, sizeof(key_handle_header->version));
+
+ memcpy(key_handle_header->kh_hmac, HMAC_SHA256_hw_final(&ctx),
+ SHA256_DIGEST_SIZE);
+
+ return EC_SUCCESS;
+}
+
+int u2f_authorization_hmac(const uint8_t *authorization_salt,
+ const struct u2f_versioned_key_handle_header *header,
+ const uint8_t *auth_time_secret_hash, uint8_t *hmac)
+{
+ struct hmac_sha256_ctx ctx;
+ struct u2f_state *state = get_state();
+
+ if (!state)
+ return EC_ERROR_UNKNOWN;
+
+ HMAC_SHA256_hw_init(&ctx, state->salt_kek, SHA256_DIGEST_SIZE);
+ HMAC_SHA256_update(&ctx, authorization_salt,
+ U2F_AUTHORIZATION_SALT_SIZE);
+ HMAC_SHA256_update(&ctx, (uint8_t *)header,
+ sizeof(struct u2f_versioned_key_handle_header));
+ HMAC_SHA256_update(&ctx, auth_time_secret_hash, SHA256_DIGEST_SIZE);
+
+ memcpy(hmac, HMAC_SHA256_hw_final(&ctx), SHA256_DIGEST_SIZE);
+
+ return EC_SUCCESS;
+}
diff --git a/include/physical_presence.h b/include/physical_presence.h
index c71c6af1ea..0acbc65691 100644
--- a/include/physical_presence.h
+++ b/include/physical_presence.h
@@ -73,18 +73,4 @@ enum pp_fsm_state {
};
enum pp_fsm_state physical_presense_fsm_state(void);
-/* ---- Physical presence ---- */
-enum touch_state {
- POP_TOUCH_NO = 0, /* waiting for a user touch */
- POP_TOUCH_YES = 1, /* touch recorded and latched */
-};
-
-/*
- * Check whether the user presence event was latched.
- *
- * @param consume reset the latched touch event and the presence LED.
- * @return POP_TOUCH_NO or POP_TOUCH_YES.
- */
-enum touch_state pop_check_presence(int consume);
-
#endif /* __CROS_EC_PHYSICAL_PRESENCE_H */
diff --git a/include/u2f_cmds.h b/include/u2f.h
index 12435677bb..6680ef5300 100644
--- a/include/u2f_cmds.h
+++ b/include/u2f.h
@@ -1,8 +1,3 @@
-/* Copyright 2017 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.
- */
-
// Common U2F raw message format header - Review Draft
// 2014-10-08
// Editor: Jakob Ehrensvard, Yubico, jakob@yubico.com
@@ -23,104 +18,118 @@ typedef unsigned long int uint64_t;
extern "C" {
#endif
-/* Load platform hooks/definitions */
-#include "tpm_vendor_cmds.h"
-#include "u2f_impl.h"
-
/* General constants */
+#define U2F_EC_KEY_SIZE 32 /* EC key size in bytes */
+#define U2F_EC_POINT_SIZE ((U2F_EC_KEY_SIZE * 2) + 1) /* Size of EC point */
+#define U2F_MAX_KH_SIZE 128 /* Max size of key handle */
#define U2F_MAX_ATT_CERT_SIZE 2048 /* Max size of attestation certificate */
#define U2F_MAX_EC_SIG_SIZE 72 /* Max size of DER coded EC signature */
#define U2F_CTR_SIZE 4 /* Size of counter field */
#define U2F_APPID_SIZE 32 /* Size of application id */
-#define U2F_USER_SECRET_SIZE 32 /* Size of user secret */
-
#define U2F_CHAL_SIZE 32 /* Size of challenge */
#define U2F_MAX_ATTEST_SIZE 256 /* Size of largest blob to sign */
+#define U2F_P256_SIZE 32
-#define SHA256_DIGEST_SIZE 32
+#define SHA256_DIGEST_SIZE 32
-#define U2F_AUTH_TIME_SECRET_SIZE 32
-#define U2F_MESSAGE_DIGEST_SIZE SHA256_DIGEST_SIZE
+#define ENC_SIZE(x) ((x + 7) & 0xfff8)
+/* EC (uncompressed) point */
-#define ENC_SIZE(x) ((x + 7) & 0xfff8)
+#define U2F_POINT_UNCOMPRESSED 0x04 /* Uncompressed point format */
+
+struct u2f_ec_point {
+ uint8_t pointFormat; /* Point type */
+ uint8_t x[U2F_EC_KEY_SIZE]; /* X-value */
+ uint8_t y[U2F_EC_KEY_SIZE]; /* Y-value */
+};
/* Request Flags. */
#define U2F_AUTH_ENFORCE 0x03 /* Enforce user presence and sign */
#define U2F_AUTH_CHECK_ONLY 0x07 /* Check only */
#define U2F_AUTH_FLAG_TUP 0x01 /* Test of user presence set */
-/**
- * The key handle can be used with fingerprint or PIN fro WebAuthn.
- * Implies key handle version = 1.
- */
+/* The key handle can be used with fingerprint or PIN. */
#define U2F_UV_ENABLED_KH 0x08
-#define U2F_KH_VERSION_1 0x01
+#define U2F_KH_VERSION_1 0x01
+
+#define U2F_AUTHORIZATION_SALT_SIZE 16
+
+struct u2f_key_handle {
+ uint8_t origin_seed[U2F_P256_SIZE];
+ uint8_t hmac[SHA256_DIGEST_SIZE];
+};
+
+struct u2f_versioned_key_handle_header {
+ uint8_t version;
+ uint8_t origin_seed[U2F_P256_SIZE];
+ uint8_t kh_hmac[SHA256_DIGEST_SIZE];
+};
+
+struct u2f_versioned_key_handle {
+ struct u2f_versioned_key_handle_header header;
+ /* Optionally checked in u2f_sign. */
+ uint8_t authorization_salt[U2F_AUTHORIZATION_SALT_SIZE];
+ uint8_t authorization_hmac[SHA256_DIGEST_SIZE];
+};
/* TODO(louiscollard): Add Descriptions. */
struct u2f_generate_req {
uint8_t appId[U2F_APPID_SIZE]; /* Application id */
- uint8_t userSecret[U2F_USER_SECRET_SIZE];
+ uint8_t userSecret[U2F_P256_SIZE];
uint8_t flags;
/*
* If generating versioned KH, derive an hmac from it and append to
* the key handle. Otherwise unused.
*/
- uint8_t authTimeSecretHash[U2F_AUTH_TIME_SECRET_SIZE];
+ uint8_t authTimeSecretHash[SHA256_DIGEST_SIZE];
};
struct u2f_generate_resp {
struct u2f_ec_point pubKey; /* Generated public key */
- struct u2f_key_handle_v0 keyHandle;
+ struct u2f_key_handle keyHandle;
};
struct u2f_generate_versioned_resp {
struct u2f_ec_point pubKey; /* Generated public key */
- struct u2f_key_handle_v1 keyHandle;
+ struct u2f_versioned_key_handle keyHandle;
};
struct u2f_sign_req {
uint8_t appId[U2F_APPID_SIZE]; /* Application id */
- uint8_t userSecret[U2F_USER_SECRET_SIZE];
- struct u2f_key_handle_v0 keyHandle;
- uint8_t hash[U2F_MESSAGE_DIGEST_SIZE];
+ uint8_t userSecret[U2F_P256_SIZE];
+ struct u2f_key_handle keyHandle;
+ uint8_t hash[U2F_P256_SIZE];
uint8_t flags;
};
struct u2f_sign_versioned_req {
uint8_t appId[U2F_APPID_SIZE]; /* Application id */
- uint8_t userSecret[U2F_USER_SECRET_SIZE];
- uint8_t authTimeSecret[U2F_AUTH_TIME_SECRET_SIZE];
- uint8_t hash[U2F_MESSAGE_DIGEST_SIZE];
+ uint8_t userSecret[U2F_P256_SIZE];
+ uint8_t authTimeSecret[U2F_P256_SIZE];
+ uint8_t hash[U2F_P256_SIZE];
uint8_t flags;
- struct u2f_key_handle_v1 keyHandle;
+ struct u2f_versioned_key_handle keyHandle;
};
-
struct u2f_sign_resp {
- struct u2f_signature sig; /* Signature */
+ uint8_t sig_r[U2F_P256_SIZE]; /* Signature */
+ uint8_t sig_s[U2F_P256_SIZE]; /* Signature */
};
struct u2f_attest_req {
- uint8_t userSecret[U2F_USER_SECRET_SIZE];
+ uint8_t userSecret[U2F_P256_SIZE];
uint8_t format;
uint8_t dataLen;
uint8_t data[U2F_MAX_ATTEST_SIZE];
};
struct u2f_attest_resp {
- struct u2f_signature sig; /* Signature */
-};
-
-struct g2f_register_msg {
- uint8_t reserved;
- uint8_t app_id[U2F_APPID_SIZE];
- uint8_t challenge[U2F_CHAL_SIZE];
- struct u2f_key_handle_v0 key_handle;
- struct u2f_ec_point public_key;
+ uint8_t sig_r[U2F_P256_SIZE];
+ uint8_t sig_s[U2F_P256_SIZE];
};
/* Command status responses */
@@ -158,36 +167,6 @@ struct g2f_register_msg {
/* Vendor command to enable/disable the extensions */
#define U2F_VENDOR_MODE U2F_VENDOR_LAST
-
-/**
- * U2F_GENERATE command handler. Generates a key handle according to input
- * parameters.
- */
-enum vendor_cmd_rc u2f_generate_cmd(enum vendor_cmd_cc code, void *buf,
- size_t input_size, size_t *response_size);
-
-/**
- * U2F_SIGN command handler. Verifies a key handle is owned and signs data with
- * it.
- */
-enum vendor_cmd_rc u2f_sign_cmd(enum vendor_cmd_cc code, void *buf,
- size_t input_size, size_t *response_size);
-
-
-/* Maximum size in bytes of G2F attestation certificate. */
-#define G2F_ATTESTATION_CERT_MAX_LEN 315
-
-/**
- * Gets the x509 certificate for the attestation key pair returned
- * by g2f_individual_keypair().
- *
- * @param buf pointer to a buffer that must be at least
- * G2F_ATTESTATION_CERT_MAX_LEN bytes.
- * @return size of certificate written to buf, 0 on error.
- */
-size_t g2f_attestation_cert(uint8_t *buf);
-
-
#ifdef __cplusplus
}
#endif
diff --git a/include/u2f_impl.h b/include/u2f_impl.h
index fa59424595..75e50cc6c7 100644
--- a/include/u2f_impl.h
+++ b/include/u2f_impl.h
@@ -9,246 +9,121 @@
#define __CROS_EC_U2F_IMPL_H
#include "common.h"
-#include "dcrypto.h"
-
-/* ---- platform cryptography hooks ---- */
-
-#define U2F_MAX_KH_SIZE 128 /* Max size of key handle */
-
-/* ---- non-volatile U2F state, shared with common code ---- */
-struct u2f_state {
- /* G2F key gen seed. */
- uint32_t salt[8];
- /* HMAC key for U2F key handle authentication. */
- uint32_t hmac_key[SHA256_DIGEST_SIZE / sizeof(uint32_t)];
- /* Stored DRBG entropy. */
- uint32_t drbg_entropy[16];
- size_t drbg_entropy_size;
-};
-
-/* Forward declarations to reduce dependencies. */
-/* EC (uncompressed) point */
-#define U2F_EC_KEY_SIZE P256_NBYTES /* EC key size in bytes */
-#define U2F_EC_POINT_SIZE ((U2F_EC_KEY_SIZE * 2) + 1) /* Size of EC point */
-#define U2F_POINT_UNCOMPRESSED 0x04 /* Uncompressed point format */
-
-struct u2f_ec_point {
- uint8_t pointFormat; /* Point type */
- uint8_t x[U2F_EC_KEY_SIZE]; /* X-value */
- uint8_t y[U2F_EC_KEY_SIZE]; /* Y-value */
-};
-
-BUILD_ASSERT(sizeof(struct u2f_ec_point) == U2F_EC_POINT_SIZE);
-
-struct u2f_signature {
- uint8_t sig_r[U2F_EC_KEY_SIZE]; /* Signature */
- uint8_t sig_s[U2F_EC_KEY_SIZE]; /* Signature */
-};
+#include "dcrypto.h"
-/* Origin seed is a random nonce generated during key handle creation. */
-#define U2F_ORIGIN_SEED_SIZE 32
-#define U2F_AUTHORIZATION_SALT_SIZE 16
+#include "tpm_vendor_cmds.h"
+#include "u2f.h"
-#define U2F_V0_KH_SIZE 64
+/* ---- Physical presence ---- */
-/* Key handle version = 0, only bound to device. */
-struct u2f_key_handle_v0 {
- uint8_t origin_seed[U2F_ORIGIN_SEED_SIZE];
- uint8_t hmac[SHA256_DIGEST_SIZE];
+enum touch_state {
+ POP_TOUCH_NO = 0, /* waiting for a user touch */
+ POP_TOUCH_YES = 1, /* touch recorded and latched */
};
-BUILD_ASSERT(sizeof(struct u2f_key_handle_v0) <= U2F_MAX_KH_SIZE);
-BUILD_ASSERT(sizeof(struct u2f_key_handle_v0) == U2F_V0_KH_SIZE);
-
-/**
- * Key handle version = 1 for WebAuthn, bound to device and user.
+/*
+ * Check whether the user presence event was latched.
+ *
+ * @param consume reset the latched touch event and the presence LED.
+ * @return POP_TOUCH_NO or POP_TOUCH_YES.
*/
-#define U2F_V1_KH_SIZE 113
+enum touch_state pop_check_presence(int consume);
-/* Header is composed of version || origin_seed || kh_hmac */
-#define U2F_V1_KH_HEADER_SIZE (U2F_ORIGIN_SEED_SIZE + SHA256_DIGEST_SIZE + 1)
+/* ---- non-volatile U2F state ---- */
-struct u2f_key_handle_v1 {
- uint8_t version;
- uint8_t origin_seed[U2F_ORIGIN_SEED_SIZE];
- uint8_t kh_hmac[SHA256_DIGEST_SIZE];
- /* Optionally checked in u2f_sign. */
- uint8_t authorization_salt[U2F_AUTHORIZATION_SALT_SIZE];
- uint8_t authorization_hmac[SHA256_DIGEST_SIZE];
-};
-
-BUILD_ASSERT(sizeof(struct u2f_key_handle_v1) <= U2F_MAX_KH_SIZE);
-BUILD_ASSERT(sizeof(struct u2f_key_handle_v1) == U2F_V1_KH_SIZE);
-
-union u2f_key_handle_variant {
- struct u2f_key_handle_v0 v0;
- struct u2f_key_handle_v1 v1;
+struct u2f_state {
+ uint32_t salt[8];
+ uint32_t salt_kek[8];
+ uint32_t salt_kh[8];
};
-BUILD_ASSERT(sizeof(union u2f_key_handle_variant) <= U2F_MAX_KH_SIZE);
-
/**
- * Create or update DRBG entropy in U2F state. Used when changing ownership
- * to cryptographically discard previously generated keys.
- *
- * @param state u2f state to update
- *
- * @return EC_SUCCESS if successful
- */
-enum ec_error_list u2f_generate_drbg_entropy(struct u2f_state *state);
-
-/**
- * Create or update HMAC key in U2F state. Used when changing ownership to
- * cryptographically discard previously generated keys.
- *
- * @param state u2f state to update
- *
- * @return EC_SUCCESS if successful
+ * Get the current u2f state from the board.
*/
-enum ec_error_list u2f_generate_hmac_key(struct u2f_state *state);
+struct u2f_state *get_state(void);
-/**
- * Create or update G2F secret in U2F state.
- *
- * @param state u2f state to update
- *
- * @return EC_SUCCESS if successful
- */
-enum ec_error_list u2f_generate_g2f_secret(struct u2f_state *state);
+/* ---- platform cryptography hooks ---- */
/**
- * Create a randomized key handle for specified origin, user secret.
- * Generate associated signing key.
+ * Pack the specified origin, user secret and origin-specific seed
+ * into a key handle.
*
- * @param state initialized u2f state
* @param origin pointer to origin id
* @param user pointer to user secret
- * @param authTimeSecretHash authentication time secret
- * @param kh output key handle header
- * @param kh_version - key handle version to generate
- * @param pubKey - generated public key
+ * @param seed pointer to origin-specific random seed
+ * @param key_handle buffer to hold the output key handle
*
- * @return EC_SUCCESS if successful
+ * @return EC_SUCCESS if a valid keypair was created.
*/
-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);
+int u2f_origin_user_keyhandle(const uint8_t *origin, const uint8_t *user,
+ const uint8_t *seed,
+ struct u2f_key_handle *key_handle);
/**
- * Create a randomized key handle for specified origin, user secret.
- * Generate associated signing key.
+ * Pack the specified origin, user secret, origin-specific seed and version
+ * byte into a key handle.
*
- * @param state initialized u2f state
- * @param kh output key handle header
- * @param kh_version - key handle version to generate
* @param origin pointer to origin id
* @param user pointer to user secret
- * @param authTimeSecretHash pointer to user's authentication secret.
- * can be set to NULL if authorization_hmac check is not needed.
- * @param r - generated part of signature
- * @param s - generated part of signature
+ * @param seed pointer to origin-specific random seed
+ * @param version the version byte to pack; should be greater than 0.
+ * @param key_handle_header buffer to hold the output key handle header
*
- * @return EC_SUCCESS if a valid key pair was created
- * EC_ACCESS_DENIED if key handle can't authenticated
+ * @return EC_SUCCESS if a valid keypair was created.
*/
-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);
+int u2f_origin_user_versioned_keyhandle(
+ const uint8_t *origin, const uint8_t *user, const uint8_t *seed,
+ uint8_t version,
+ struct u2f_versioned_key_handle_header *key_handle_header);
/**
- * Verify that key handle matches provided origin, user and user's
- * authentication secret and was created on this device (signed with
- * U2F state HMAC key).
- *
- * @param state initialized u2f state
- * @param kh input key handle
- * @param kh_version - key handle version to verify
- * @param user pointer to user secret
- * @param origin pointer to origin id
- * @param authTimeSecretHash pointer to user's authentication secret.
- * can be set to NULL if authorization_hmac check is not needed.
+ * Generate an origin and user-specific ECDSA keypair from the specified
+ * key handle.
*
- * @return EC_SUCCESS if handle can be authenticated
- */
-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);
-
-/**
- * Gets the x509 certificate for the attestation key pair returned
- * by g2f_individual_keypair().
- *
- * @param state U2F state parameters
- * @param serial Device serial number
- * @param buf pointer to a buffer that must be at least
- *
- * G2F_ATTESTATION_CERT_MAX_LEN bytes.
- * @return size of certificate written to buf, 0 on error.
- */
-size_t g2f_attestation_cert_serial(const struct u2f_state *state,
- const uint8_t *serial, uint8_t *buf);
-
-/**
- * Verify that provided key handle and public key match.
- * @param state U2F state parameters
- * @param key_handle key handle
- * @param kh_version key handle version (0 - legacy, 1 - versioned)
- * @param user pointer to user secret
- * @param origin pointer to origin id
- * @param authTimeSecretHash pointer to user's authentication secret.
- * can be set to NULL if authorization_hmac check is not needed.
- * @param public_key pointer to public key point (big endian)
- * @param data data to sign
- * @param data_size data size in bytes
+ * If pk_x and pk_y are NULL, public key generation will be skipped.
*
- * @param r part of generated signature
- * @param s part of generated signature
+ * @param key_handle pointer to the key handle
+ * @param key_handle_size size of the key handle in bytes
+ * @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 public key matches key handle,
- * (r,s) set to valid signature
- * EC_ACCESS_DENIED if key handle can't authenticated
+ * @return EC_SUCCESS if a valid keypair was created.
*/
-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);
-
+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);
/**
- *
- * Board U2F key management part implemented.
- *
+ * Derive an hmac from the given salt, key handle and hash. The salt is to make
+ * sure the hmac is different for different key handles of one user. The key
+ * handle header is encoded into the authorization hmac to protect against
+ * swapping auth time secret.
*/
+int u2f_authorization_hmac(const uint8_t *authorization_salt,
+ const struct u2f_versioned_key_handle_header *header,
+ const uint8_t *auth_time_secret_hash, uint8_t *hmac);
-/**
- * Get the current u2f state from the board.
+/***
+ * Generate a hardware derived 256b private key.
*
- * @return pointer to static state if successful, NULL otherwise
+ * @param kek ptr to store the generated key.
+ * @param key_len size of the storage buffer. Should be 32 bytes.
+ * @return EC_SUCCESS if a valid key was created.
*/
-struct u2f_state *u2f_get_state(void);
+int u2f_gen_kek(const uint8_t *origin, uint8_t *kek, size_t key_len);
/**
- * Try to load U2F keys or create if failed.
+ * Generate a hardware derived ECDSA keypair for individual attestation.
*
- * @param state - buffer for state to load/create
- * @param force_create - if true, always create all keys
+ * @param seed ptr to store 32-byte seed to regenerate this key on this chip
+ * @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 state is properly initialized and will persist in flash.
+ * @return EC_SUCCESS if a valid keypair was created.
*/
-bool u2f_load_or_create_state(struct u2f_state *state, bool force_create);
+int g2f_individual_keypair(p256_int *d, p256_int *pk_x, p256_int *pk_y);
/***
* Generates and persists to nvram a new seed that will be used to
@@ -258,22 +133,33 @@ bool u2f_load_or_create_state(struct u2f_state *state, bool force_create);
* @return EC_SUCCESS if seed was successfully created
* (and persisted if requested).
*/
-enum ec_error_list u2f_gen_kek_seed(int commit);
+int u2f_gen_kek_seed(int commit);
+
+/* Maximum size in bytes of G2F attestation certificate. */
+#define G2F_ATTESTATION_CERT_MAX_LEN 315
/**
- * Zeroize U2F keys. Can be used to switch to FIPS-compliant path by
- * destroying old keys.
+ * Gets the x509 certificate for the attestation keypair returned
+ * by g2f_individual_keypair().
*
- * @return true if state is properly initialized and will persist in flash.
+ * @param buf pointer to a buffer that must be at least
+ * G2F_ATTESTATION_CERT_MAX_LEN bytes.
+ * @return size of certificate written to buf, 0 on error.
*/
-enum ec_error_list u2f_zeroize_keys(void);
+int g2f_attestation_cert(uint8_t *buf);
/**
- * Update keys to a newer (FIPS-compliant) version if needed. Do nothing if
- * keys are already updated.
- *
- * @return EC_SUCCESS or error code.
+ * U2F_GENERATE command handler. Generates a key handle according to input
+ * parameters.
+ */
+enum vendor_cmd_rc u2f_generate(enum vendor_cmd_cc code, void *buf,
+ size_t input_size, size_t *response_size);
+
+/**
+ * U2F_SIGN command handler. Verifies a key handle is owned and signs data with
+ * it.
*/
-enum ec_error_list u2f_update_keys(void);
+enum vendor_cmd_rc u2f_sign(enum vendor_cmd_cc code, void *buf,
+ size_t input_size, size_t *response_size);
#endif /* __CROS_EC_U2F_IMPL_H */
diff --git a/test/build.mk b/test/build.mk
index 8efc82f5f0..7555778e70 100644
--- a/test/build.mk
+++ b/test/build.mk
@@ -95,7 +95,6 @@ thermal-y=thermal.o
timer_calib-y=timer_calib.o
timer_dos-y=timer_dos.o
u2f-y=u2f.o
-u2f-y+=../board/cr50/u2f.o
uptime-y=uptime.o
utils-y=utils.o
utils_str-y=utils_str.o
@@ -108,7 +107,6 @@ TPM2_ROOT := $(CROS_WORKON_SRCROOT)/src/third_party/tpm2
$(out)/RO/common/new_nvmem.o: CFLAGS += -I$(TPM2_ROOT) -I chip/g
$(out)/RO/test/nvmem.o: CFLAGS += -I$(TPM2_ROOT)
$(out)/RO/test/nvmem_tpm2_mock.o: CFLAGS += -I$(TPM2_ROOT)
-$(out)/RO/common/u2f.o: CFLAGS += -DU2F_TEST
host-is_enabled_error: TEST_SCRIPT=is_enabled_error.sh
is_enabled_error-y=is_enabled_error.o.cmd
diff --git a/test/u2f.c b/test/u2f.c
index 047c62b7df..c74bc847a3 100644
--- a/test/u2f.c
+++ b/test/u2f.c
@@ -3,8 +3,6 @@
* found in the LICENSE file.
*/
-#include "u2f_cmds.h"
-#include "physical_presence.h"
#include "test_util.h"
#include "u2f_impl.h"
@@ -26,43 +24,15 @@ int DCRYPTO_ladder_random(void *output)
return 1;
}
-bool fips_rand_bytes(void *buffer, size_t len)
-{
- memset(buffer, 1, len);
- return true;
-}
-
-bool fips_trng_bytes(void *buffer, size_t len)
-{
- memset(buffer, 2, len);
- return true;
-}
-
int DCRYPTO_x509_gen_u2f_cert_name(const p256_int *d, const p256_int *pk_x,
const p256_int *pk_y, const p256_int *serial,
- const char *name, uint8_t *cert, const int n)
+ const char *name, uint8_t *cert,
+ const int n)
{
/* Return the size of certificate, 0 means error. */
return 0;
}
-int DCRYPTO_p256_key_from_bytes(p256_int *x, p256_int *y, p256_int *d,
- const uint8_t key_bytes[P256_NBYTES])
-{
- p256_int key;
-
- p256_from_bin(key_bytes, &key);
-
- if (p256_lt_blinded(&key, &SECP256r1_nMin2) >= 0)
- return 0;
- p256_add_d(&key, 1, d);
- if (x == NULL || y == NULL)
- return 1;
- memset(x, 0, P256_NBYTES);
- memset(y, 0, P256_NBYTES);
- return 1;
-}
-
int dcrypto_p256_ecdsa_sign(struct drbg_ctx *drbg, const p256_int *key,
const p256_int *message, p256_int *r, p256_int *s)
{
@@ -72,7 +42,6 @@ int dcrypto_p256_ecdsa_sign(struct drbg_ctx *drbg, const p256_int *key,
return 1;
}
-
/******************************************************************************/
/* Mock implementations of U2F functionality.
*/
@@ -80,20 +49,32 @@ static int presence;
static struct u2f_state state;
-struct u2f_state *u2f_get_state(void)
+struct u2f_state *get_state(void)
{
return &state;
}
enum touch_state pop_check_presence(int consume)
{
- enum touch_state ret = presence ? POP_TOUCH_YES : POP_TOUCH_NO;
+ enum touch_state ret = presence ?
+ POP_TOUCH_YES : POP_TOUCH_NO;
if (consume)
presence = 0;
return ret;
}
+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)
+{
+ return EC_SUCCESS;
+}
+
+int g2f_individual_keypair(p256_int *d, p256_int *pk_x, p256_int *pk_y)
+{
+ return EC_SUCCESS;
+}
+
/******************************************************************************/
/* Tests begin here.
*/
@@ -108,8 +89,10 @@ test_static int test_u2f_generate_no_require_presence(void)
memset(buffer, 0, sizeof(buffer));
req->flags = 0;
presence = 0;
- ret = u2f_generate_cmd(VENDOR_CC_U2F_GENERATE, &buffer,
- sizeof(struct u2f_generate_req), &response_size);
+ ret = u2f_generate(
+ VENDOR_CC_U2F_GENERATE, &buffer,
+ sizeof(struct u2f_generate_req),
+ &response_size);
TEST_ASSERT(ret == VENDOR_RC_SUCCESS);
return EC_SUCCESS;
@@ -124,16 +107,20 @@ test_static int test_u2f_generate_require_presence(void)
memset(buffer, 0, sizeof(buffer));
req->flags = U2F_AUTH_FLAG_TUP;
presence = 0;
- ret = u2f_generate_cmd(VENDOR_CC_U2F_GENERATE, &buffer,
- sizeof(struct u2f_generate_req), &response_size);
+ ret = u2f_generate(
+ VENDOR_CC_U2F_GENERATE, &buffer,
+ sizeof(struct u2f_generate_req),
+ &response_size);
TEST_ASSERT(ret == VENDOR_RC_NOT_ALLOWED);
memset(buffer, 0, sizeof(buffer));
req->flags = U2F_AUTH_FLAG_TUP;
response_size = sizeof(struct u2f_generate_resp);
presence = 1;
- ret = u2f_generate_cmd(VENDOR_CC_U2F_GENERATE, &buffer,
- sizeof(struct u2f_generate_req), &response_size);
+ ret = u2f_generate(
+ VENDOR_CC_U2F_GENERATE, &buffer,
+ sizeof(struct u2f_generate_req),
+ &response_size);
TEST_ASSERT(ret == VENDOR_RC_SUCCESS);
return EC_SUCCESS;