From 1485393c069ed6c4b9419154664e7407e7022c2e Mon Sep 17 00:00:00 2001 From: David Stevens Date: Wed, 25 Aug 2021 01:22:24 +0000 Subject: Revert "u2f: refactoring to split command processing and crypto" 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 > Change-Id: I85442cddb2959bd3102f7f6e6047134ede90951b > Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/3034852 > Reviewed-by: Vadim Sukhomlinov > Reviewed-by: Andrey Pronin > Tested-by: Vadim Sukhomlinov > Commit-Queue: Vadim Sukhomlinov Bug: b:134594373 Change-Id: I61a965995fcd53b4e155084f5f351574cb84cd1e Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/3115930 Bot-Commit: Rubber Stamper Owners-Override: David Stevens --- board/cr50/build.mk | 1 - board/cr50/fips_cmd.c | 90 +++-- board/cr50/power_button.c | 38 +-- board/cr50/tpm2/platform.c | 2 +- board/cr50/tpm2/virtual_nvmem.c | 2 +- board/cr50/u2f.c | 708 +++++++++------------------------------- board/cr50/u2f_state_load.c | 198 ----------- chip/host/build.mk | 3 - common/u2f.c | 547 +++++++++++++++++++++++-------- include/physical_presence.h | 14 - include/u2f.h | 174 ++++++++++ include/u2f_cmds.h | 195 ----------- include/u2f_impl.h | 296 ++++++----------- test/build.mk | 2 - test/u2f.c | 69 ++-- 15 files changed, 909 insertions(+), 1430 deletions(-) delete mode 100644 board/cr50/u2f_state_load.c create mode 100644 include/u2f.h delete mode 100644 include/u2f_cmds.h 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.h b/include/u2f.h new file mode 100644 index 0000000000..6680ef5300 --- /dev/null +++ b/include/u2f.h @@ -0,0 +1,174 @@ +// Common U2F raw message format header - Review Draft +// 2014-10-08 +// Editor: Jakob Ehrensvard, Yubico, jakob@yubico.com + +#ifndef __U2F_H_INCLUDED__ +#define __U2F_H_INCLUDED__ + +#ifdef _MSC_VER /* Windows */ +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long int uint64_t; +#else +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* 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_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 ENC_SIZE(x) ((x + 7) & 0xfff8) + +/* EC (uncompressed) 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 */ +}; + +/* 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. */ +#define U2F_UV_ENABLED_KH 0x08 + +#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_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[SHA256_DIGEST_SIZE]; +}; + +struct u2f_generate_resp { + struct u2f_ec_point pubKey; /* Generated public key */ + struct u2f_key_handle keyHandle; +}; + +struct u2f_generate_versioned_resp { + struct u2f_ec_point pubKey; /* Generated public key */ + struct u2f_versioned_key_handle keyHandle; +}; + +struct u2f_sign_req { + uint8_t appId[U2F_APPID_SIZE]; /* Application id */ + 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_P256_SIZE]; + uint8_t authTimeSecret[U2F_P256_SIZE]; + uint8_t hash[U2F_P256_SIZE]; + uint8_t flags; + struct u2f_versioned_key_handle keyHandle; +}; + +struct u2f_sign_resp { + uint8_t sig_r[U2F_P256_SIZE]; /* Signature */ + uint8_t sig_s[U2F_P256_SIZE]; /* Signature */ +}; + +struct u2f_attest_req { + uint8_t userSecret[U2F_P256_SIZE]; + uint8_t format; + uint8_t dataLen; + uint8_t data[U2F_MAX_ATTEST_SIZE]; +}; + +struct u2f_attest_resp { + uint8_t sig_r[U2F_P256_SIZE]; + uint8_t sig_s[U2F_P256_SIZE]; +}; + +/* Command status responses */ + +#define U2F_SW_NO_ERROR 0x9000 /* SW_NO_ERROR */ +#define U2F_SW_WRONG_DATA 0x6A80 /* SW_WRONG_DATA */ +#define U2F_SW_CONDITIONS_NOT_SATISFIED \ + 0x6985 /* SW_CONDITIONS_NOT_SATISFIED \ + */ +#define U2F_SW_COMMAND_NOT_ALLOWED 0x6986 /* SW_COMMAND_NOT_ALLOWED */ +#define U2F_SW_INS_NOT_SUPPORTED 0x6D00 /* SW_INS_NOT_SUPPORTED */ + +/* Protocol extensions */ + +/* Non-standardized command status responses */ +#define U2F_SW_CLA_NOT_SUPPORTED 0x6E00 +#define U2F_SW_WRONG_LENGTH 0x6700 +#define U2F_SW_WTF 0x6f00 + +/* Additional flags for P1 field */ +#define G2F_ATTEST 0x80 /* Fixed attestation key */ +#define G2F_CONSUME 0x02 /* Consume presence */ + +/* + * The key handle format was changed when support for user secrets was added. + * U2F_SIGN requests that specify this flag will first try to validate the key + * handle as a new format key handle, and if that fails, will fall back to + * treating it as a legacy key handle (without user secrets). + */ +#define SIGN_LEGACY_KH 0x40 + +/* U2F Attest format for U2F Register Response. */ +#define U2F_ATTEST_FORMAT_REG_RESP 0 + +/* Vendor command to enable/disable the extensions */ +#define U2F_VENDOR_MODE U2F_VENDOR_LAST + +#ifdef __cplusplus +} +#endif + +#endif /* __U2F_H_INCLUDED__ */ diff --git a/include/u2f_cmds.h b/include/u2f_cmds.h deleted file mode 100644 index 12435677bb..0000000000 --- a/include/u2f_cmds.h +++ /dev/null @@ -1,195 +0,0 @@ -/* 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 - -#ifndef __U2F_H_INCLUDED__ -#define __U2F_H_INCLUDED__ - -#ifdef _MSC_VER /* Windows */ -typedef unsigned char uint8_t; -typedef unsigned short uint16_t; -typedef unsigned int uint32_t; -typedef unsigned long int uint64_t; -#else -#include -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -/* Load platform hooks/definitions */ -#include "tpm_vendor_cmds.h" -#include "u2f_impl.h" - -/* General constants */ - -#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 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) - -/* 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. - */ -#define U2F_UV_ENABLED_KH 0x08 - -#define U2F_KH_VERSION_1 0x01 - -/* 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 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]; -}; - -struct u2f_generate_resp { - struct u2f_ec_point pubKey; /* Generated public key */ - struct u2f_key_handle_v0 keyHandle; -}; - -struct u2f_generate_versioned_resp { - struct u2f_ec_point pubKey; /* Generated public key */ - struct u2f_key_handle_v1 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 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 flags; - struct u2f_key_handle_v1 keyHandle; -}; - - -struct u2f_sign_resp { - struct u2f_signature sig; /* Signature */ -}; - -struct u2f_attest_req { - uint8_t userSecret[U2F_USER_SECRET_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; -}; - -/* Command status responses */ - -#define U2F_SW_NO_ERROR 0x9000 /* SW_NO_ERROR */ -#define U2F_SW_WRONG_DATA 0x6A80 /* SW_WRONG_DATA */ -#define U2F_SW_CONDITIONS_NOT_SATISFIED \ - 0x6985 /* SW_CONDITIONS_NOT_SATISFIED \ - */ -#define U2F_SW_COMMAND_NOT_ALLOWED 0x6986 /* SW_COMMAND_NOT_ALLOWED */ -#define U2F_SW_INS_NOT_SUPPORTED 0x6D00 /* SW_INS_NOT_SUPPORTED */ - -/* Protocol extensions */ - -/* Non-standardized command status responses */ -#define U2F_SW_CLA_NOT_SUPPORTED 0x6E00 -#define U2F_SW_WRONG_LENGTH 0x6700 -#define U2F_SW_WTF 0x6f00 - -/* Additional flags for P1 field */ -#define G2F_ATTEST 0x80 /* Fixed attestation key */ -#define G2F_CONSUME 0x02 /* Consume presence */ - -/* - * The key handle format was changed when support for user secrets was added. - * U2F_SIGN requests that specify this flag will first try to validate the key - * handle as a new format key handle, and if that fails, will fall back to - * treating it as a legacy key handle (without user secrets). - */ -#define SIGN_LEGACY_KH 0x40 - -/* U2F Attest format for U2F Register Response. */ -#define U2F_ATTEST_FORMAT_REG_RESP 0 - -/* 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 - -#endif /* __U2F_H_INCLUDED__ */ 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; -- cgit v1.2.1