summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVadim Sukhomlinov <sukhomlinov@google.com>2021-09-30 23:53:39 -0700
committerCommit Bot <commit-bot@chromium.org>2021-10-06 04:01:05 +0000
commit38690405baf21e64dd7ec4f6be329098a2e330ac (patch)
treedbae268e5971c37612d6336e00f7d2b4e0791677
parentcc7679235b5b30083cd74a68890b54c71bb61f7f (diff)
downloadchrome-ec-stabilize-14267.B-cr50_stab.tar.gz
cr50: add support for v2 of U2F key handle for WebAuthnstabilize-14267.B-cr50_stab
Adding v2 of key handle which drops kh_hmac field and use single authorization code for all relevant fields. BUG=b:172971998 TEST=make BOARD=cr50 CRYPTO_TEST=1 U2F_TEST=1; in ccd: u2f_test - unit tests test/tpm_test/tpmtest.py Signed-off-by: Vadim Sukhomlinov <sukhomlinov@google.com> Change-Id: I647ded7a2c157cea91ac48a2ba679def318c1e63 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/3199671 Reviewed-by: Vadim Sukhomlinov <sukhomlinov@chromium.org> Reviewed-by: Andrey Pronin <apronin@chromium.org> Tested-by: Vadim Sukhomlinov <sukhomlinov@chromium.org> Auto-Submit: Vadim Sukhomlinov <sukhomlinov@chromium.org> Commit-Queue: Vadim Sukhomlinov <sukhomlinov@chromium.org>
-rw-r--r--board/cr50/dcrypto/u2f.c272
-rw-r--r--common/u2f.c125
-rw-r--r--include/u2f.h79
-rw-r--r--include/u2f_cmds.h11
-rw-r--r--test/tpm_test/u2f_test.py36
5 files changed, 381 insertions, 142 deletions
diff --git a/board/cr50/dcrypto/u2f.c b/board/cr50/dcrypto/u2f.c
index 76be43285d..4cd267ac61 100644
--- a/board/cr50/dcrypto/u2f.c
+++ b/board/cr50/dcrypto/u2f.c
@@ -53,7 +53,7 @@ static void u2f_origin_user_mac(const struct u2f_state *state,
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)
+ if (kh_version == U2F_KH_VERSION_1)
HMAC_SHA256_update(&ctx, &kh_version, sizeof(kh_version));
#ifdef CR50_DEV_U2F_VERBOSE
ccprintf("origin %ph\n", HEX_BUF(origin, U2F_APPID_SIZE));
@@ -71,7 +71,8 @@ static void u2f_origin_user_mac(const struct u2f_state *state,
static void u2f_authorization_mac(const struct u2f_state *state,
const union u2f_key_handle_variant *kh,
- uint8_t kh_version,
+ uint8_t kh_version, const uint8_t *user,
+ const uint8_t *origin,
const uint8_t *auth_time_secret_hash,
uint8_t *kh_auth_mac)
{
@@ -80,25 +81,39 @@ static void u2f_authorization_mac(const struct u2f_state *state,
const void *kh_header = NULL;
size_t kh_header_size = 0;
- if (kh_version == 0) {
- memset(kh_auth_mac, 0xff, SHA256_DIGEST_SIZE);
- return;
- }
- /* At some point we may have v2 key handle, so prepare for it. */
- if (kh_version == 1) {
+ switch (kh_version) {
+ case U2F_KH_VERSION_2:
+ auth_salt = kh->v2.authorization_salt;
+ kh_header = &kh->v2;
+ kh_header_size = U2F_V2_KH_HEADER_SIZE;
+ break;
+ case U2F_KH_VERSION_1:
auth_salt = kh->v1.authorization_salt;
kh_header = &kh->v1;
+ /* include kh_hmac, which depends on user and origin */
kh_header_size = U2F_V1_KH_HEADER_SIZE;
+ break;
+ default:
+ /**
+ * Version 0 doesn't contain authorization salt, so do
+ * nothing for it as well as for unknown versions.
+ */
+ memset(kh_auth_mac, 0xff, SHA256_DIGEST_SIZE);
+ return;
}
/**
* HMAC(u2f_hmac_key, auth_salt || key_handle_header
- * || authTimeSecret)
+ * [origin || user ] || authTimeSecret)
*/
HMAC_SHA256_hw_init(&ctx, state->hmac_key, SHA256_DIGEST_SIZE);
HMAC_SHA256_update(&ctx, auth_salt, U2F_AUTHORIZATION_SALT_SIZE);
HMAC_SHA256_update(&ctx, kh_header, kh_header_size);
+ if (kh_version == U2F_KH_VERSION_2) {
+ HMAC_SHA256_update(&ctx, origin, U2F_APPID_SIZE);
+ HMAC_SHA256_update(&ctx, user, U2F_USER_SECRET_SIZE);
+ }
HMAC_SHA256_update(&ctx, auth_time_secret_hash,
U2F_AUTH_TIME_SECRET_SIZE);
@@ -124,7 +139,7 @@ static int app_hw_device_id(enum dcrypto_appid appid, const uint32_t input[8],
/**
* Compute HMAC(HMAC(hw_device_id, SHA256(name[appid])), input)
- * It is not used as a key though, and treated as personalization
+ * It is not used as a key though, and treated as additional data
* string for DRBG.
*/
result = DCRYPTO_appkey_derive(appid, input, output);
@@ -141,7 +156,7 @@ static int app_hw_device_id(enum dcrypto_appid appid, const uint32_t input[8],
*
* @param state U2F state parameters
* @param kh key handle
- * @param kh_version key handle version (0 - legacy, 1 - versioned)
+ * @param kh_version key handle version
* @param d pointer to ECDSA private key
* @param pk_x pointer to public key point
* @param pk_y pointer to public key point
@@ -158,15 +173,29 @@ static enum ec_error_list u2f_origin_user_key_pair(
struct drbg_ctx drbg;
size_t key_handle_size = 0;
uint8_t *key_handle = NULL;
- enum dcrypto_result result;
-
- 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)) {
+ enum dcrypto_result result = DCRYPTO_FAIL;
+
+ p256_clear(d);
+ memset(key_seed, 0, sizeof(key_seed));
+
+ switch (kh_version) {
+ case U2F_KH_VERSION_2:
+ if (kh->v2.version != U2F_KH_VERSION_2)
+ return EC_ERROR_INVAL;
+ key_handle_size = U2F_V2_KH_HEADER_SIZE;
+ key_handle = (uint8_t *)&kh->v2;
+ break;
+ case U2F_KH_VERSION_1:
+ if (kh->v1.version != U2F_KH_VERSION_1)
+ return EC_ERROR_INVAL;
key_handle_size = U2F_V1_KH_HEADER_SIZE;
key_handle = (uint8_t *)&kh->v1;
- } else {
+ break;
+ case 0:
+ key_handle_size = sizeof(struct u2f_key_handle_v0);
+ key_handle = (uint8_t *)&kh->v0;
+ break;
+ default:
return EC_ERROR_INVAL;
}
@@ -184,10 +213,8 @@ static enum ec_error_list u2f_origin_user_key_pair(
hmac_drbg_init(&drbg, state->drbg_entropy,
state->drbg_entropy_size, dev_salt, P256_NBYTES,
NULL, 0, HMAC_DRBG_DO_NOT_AUTO_RESEED);
- if (hmac_drbg_generate(&drbg, key_seed, sizeof(key_seed),
- key_handle,
- key_handle_size) != DCRYPTO_OK)
- return EC_ERROR_HW_INTERNAL;
+ result = hmac_drbg_generate(&drbg, key_seed, sizeof(key_seed),
+ key_handle, key_handle_size);
} else {
/**
* FIPS-compliant path.
@@ -206,10 +233,13 @@ static enum ec_error_list u2f_origin_user_key_pair(
/**
* Additional data = Device_ID (constant coming from HW).
*/
- if (hmac_drbg_generate(&drbg, key_seed, sizeof(key_seed),
- dev_salt, P256_NBYTES) != DCRYPTO_OK)
- return EC_ERROR_HW_INTERNAL;
+ result = hmac_drbg_generate(&drbg, key_seed, sizeof(key_seed),
+ dev_salt, P256_NBYTES);
}
+
+ if (result != DCRYPTO_OK)
+ return EC_ERROR_HW_INTERNAL;
+
result = DCRYPTO_p256_key_from_bytes(pk_x, pk_y, d, key_seed);
drbg_exit(&drbg);
@@ -237,28 +267,48 @@ enum ec_error_list u2f_generate(const struct u2f_state *state,
union u2f_key_handle_variant *kh,
uint8_t kh_version, struct u2f_ec_point *pubKey)
{
- uint8_t *kh_hmac, *kh_origin_seed;
- int generate_key_pair_rc;
+ uint8_t *kh_hmac = NULL;
+ uint8_t *kh_origin_seed = NULL;
+ uint8_t *auth_salt = NULL;
+ uint8_t *auth_hmac = NULL;
+ enum ec_error_list generate_key_pair_rc = EC_ERROR_HW_INTERNAL;
+
/* Generated public keys associated with key handle. */
p256_int opk_x, opk_y;
if (!fips_crypto_allowed())
return EC_ERROR_HW_INTERNAL;
- /* 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) {
+ /* Compute constants for requested key handle version. */
+ switch (kh_version) {
+ case U2F_KH_VERSION_2:
+ /**
+ * This may overwrite input parameters if shared
+ * request/response buffer is used by caller.
+ */
+ kh_origin_seed = kh->v2.origin_seed;
+ auth_salt = kh->v2.authorization_salt;
+ auth_hmac = kh->v2.authorization_hmac;
+ kh->v2.version = U2F_KH_VERSION_2;
+ break;
+ case U2F_KH_VERSION_1:
kh_hmac = kh->v1.kh_hmac;
kh_origin_seed = kh->v1.origin_seed;
+ auth_salt = kh->v1.authorization_salt;
+ auth_hmac = kh->v1.authorization_hmac;
/**
* This may overwrite input parameters if shared
* request/response buffer is used by caller.
*/
kh->v1.version = kh_version;
- } else
+ break;
+ case 0:
+ kh_hmac = kh->v0.hmac;
+ kh_origin_seed = kh->v0.origin_seed;
+ break;
+ default:
return EC_ERROR_INVAL;
+ }
/* Generate key handle candidates and origin-specific key pair. */
do {
@@ -267,8 +317,9 @@ enum ec_error_list u2f_generate(const struct u2f_state *state,
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);
+ if (kh_hmac) /* Versions 0 & 1 only. */
+ 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
@@ -284,13 +335,12 @@ enum ec_error_list u2f_generate(const struct u2f_state *state,
if (generate_key_pair_rc != EC_SUCCESS)
return generate_key_pair_rc;
- if (kh_version == 1) {
- if (!fips_rand_bytes(kh->v1.authorization_salt,
- U2F_AUTHORIZATION_SALT_SIZE))
+ if (kh_version) {
+ if (!fips_rand_bytes(auth_salt, U2F_AUTHORIZATION_SALT_SIZE))
return EC_ERROR_HW_INTERNAL;
- u2f_authorization_mac(state, kh, kh_version, authTimeSecretHash,
- kh->v1.authorization_hmac);
+ u2f_authorization_mac(state, kh, kh_version, user, origin,
+ authTimeSecretHash, auth_hmac);
}
pubKey->pointFormat = U2F_POINT_UNCOMPRESSED;
@@ -307,8 +357,8 @@ enum ec_error_list u2f_authorize_keyhandle(
{
/* Re-created key handle. */
uint8_t recreated_hmac[SHA256_DIGEST_SIZE];
- const uint8_t *origin_seed, *kh_hmac;
- int result = 0;
+ const uint8_t *origin_seed = NULL, *kh_hmac = NULL, *auth_hmac = NULL;
+ enum dcrypto_result result = 0;
if (!fips_crypto_allowed())
return EC_ERROR_HW_INTERNAL;
@@ -318,36 +368,54 @@ enum ec_error_list u2f_authorize_keyhandle(
* 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 {
+ switch (kh_version) {
+ case U2F_KH_VERSION_2:
+ if (!authTimeSecretHash || kh->v2.version != U2F_KH_VERSION_2)
+ return EC_ERROR_ACCESS_DENIED;
+
+ origin_seed = kh->v2.origin_seed;
+ auth_hmac = kh->v2.authorization_hmac;
+ break;
+ case U2F_KH_VERSION_1:
+ if (kh->v1.version != U2F_KH_VERSION_1)
+ return EC_ERROR_ACCESS_DENIED;
origin_seed = kh->v1.origin_seed;
+ auth_hmac = kh->v1.authorization_hmac;
kh_hmac = kh->v1.kh_hmac;
+ break;
+ case 0:
+ origin_seed = kh->v0.origin_seed;
+ kh_hmac = kh->v0.hmac;
+ break;
+ default:
+ return EC_ERROR_INVAL;
}
- /* 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)) - DCRYPTO_OK;
+ if (kh_hmac) {
+ /* First, check inner part. */
+ u2f_origin_user_mac(state, user, origin, origin_seed,
+ kh_version, recreated_hmac);
+
+ /**
+ * DCRYPTO_equals return DCRYPTO_OK 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));
- always_memset(recreated_hmac, 0, sizeof(recreated_hmac));
+ 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)) - DCRYPTO_OK;
+ if (auth_hmac && authTimeSecretHash) {
+ u2f_authorization_mac(state, kh, kh_version, user, origin,
+ authTimeSecretHash, recreated_hmac);
+ result |= DCRYPTO_equals(&recreated_hmac, auth_hmac,
+ sizeof(recreated_hmac));
always_memset(recreated_hmac, 0, sizeof(recreated_hmac));
}
- return (result == 0) ? EC_SUCCESS : EC_ERROR_ACCESS_DENIED;
+ return (result == DCRYPTO_OK) ? EC_SUCCESS : EC_ERROR_ACCESS_DENIED;
}
static enum ec_error_list
@@ -411,14 +479,18 @@ enum ec_error_list u2f_sign(const struct u2f_state *state,
result = u2f_authorize_keyhandle(state, kh, kh_version, user, origin,
authTimeSecretHash);
- if (result != EC_SUCCESS)
+ if (result != EC_SUCCESS) {
+ memset(sig, 0, sizeof(*sig));
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)
+ if (result != EC_SUCCESS) {
+ memset(sig, 0, sizeof(*sig));
return result;
+ }
/* Prepare hash to sign. */
p256_from_bin(hash, &h);
@@ -581,6 +653,7 @@ enum ec_error_list u2f_attest(const struct u2f_state *state,
DCRYPTO_OK) ?
EC_SUCCESS :
EC_ERROR_HW_INTERNAL;
+ drbg_exit(&dr_ctx);
p256_clear(&d);
p256_to_bin(&r, sig->sig_r);
@@ -675,41 +748,90 @@ static int cmd_u2f_test(int argc, char **argv)
ccprintf("\nVersion 1 tests\n");
ccprintf("u2f_generate - %s\n",
expect_bool(u2f_generate(&state, user, origin, authTime, &kh,
- 1, &pubKey),
+ U2F_KH_VERSION_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,
+ expect_bool(u2f_authorize_keyhandle(&state, &kh,
+ U2F_KH_VERSION_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,
+ expect_bool(u2f_authorize_keyhandle(&state, &kh,
+ U2F_KH_VERSION_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),
+ expect_bool(u2f_sign(&state, &kh, U2F_KH_VERSION_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),
+ expect_bool(u2f_attest(&state, &kh, U2F_KH_VERSION_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),
+ expect_bool(u2f_sign(&state, &kh, U2F_KH_VERSION_1, user,
+ origin, authTime, authTime, &sig),
+ EC_ERROR_ACCESS_DENIED));
+ ccprintf("sig: %ph\n", HEX_BUF(&sig, sizeof(sig)));
+
+ cflush();
+
+ /* Version 2 key handle. */
+ memset(&kh, 0, sizeof(kh));
+ ccprintf("\nVersion 2 tests\n");
+ ccprintf("u2f_generate - %s\n",
+ expect_bool(u2f_generate(&state, user, origin, authTime, &kh,
+ U2F_KH_VERSION_2, &pubKey),
+ EC_SUCCESS));
+ ccprintf("kh: %ph\n", HEX_BUF(&kh, sizeof(kh.v2)));
+ ccprintf("pubKey: %ph\n", HEX_BUF(&pubKey, sizeof(pubKey)));
+
+ ccprintf("u2f_authorize_keyhandle - %s\n",
+ expect_bool(u2f_authorize_keyhandle(&state, &kh,
+ U2F_KH_VERSION_2, user,
+ origin, authTime),
+ EC_SUCCESS));
+
+ kh.v2.authorization_salt[0] ^= 0x10;
+ ccprintf("u2f_authorize_keyhandle - %s\n",
+ expect_bool(u2f_authorize_keyhandle(&state, &kh,
+ U2F_KH_VERSION_2, user,
+ origin, authTime),
+ EC_ERROR_ACCESS_DENIED));
+
+ kh.v2.authorization_salt[0] ^= 0x10;
+ ccprintf("u2f_sign - %s\n",
+ expect_bool(u2f_sign(&state, &kh, U2F_KH_VERSION_2, 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, U2F_KH_VERSION_2, 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.v2.origin_seed[0] ^= 0x10;
+ ccprintf("u2f_sign - %s\n",
+ expect_bool(u2f_sign(&state, &kh, U2F_KH_VERSION_2, user,
+ origin, authTime, authTime, &sig),
EC_ERROR_ACCESS_DENIED));
ccprintf("sig: %ph\n", HEX_BUF(&sig, sizeof(sig)));
diff --git a/common/u2f.c b/common/u2f.c
index 0795d88359..89565950dd 100644
--- a/common/u2f.c
+++ b/common/u2f.c
@@ -55,20 +55,24 @@ enum vendor_cmd_rc u2f_generate_cmd(enum vendor_cmd_cc code, void *buf,
size_t input_size, size_t *response_size)
{
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;
+ union u2f_generate_response *resp = buf;
+ struct u2f_ec_point *pubKey = NULL;
const struct u2f_state *state = u2f_get_state();
- uint8_t kh_version =
- (req->flags & U2F_UV_ENABLED_KH) ? U2F_KH_VERSION_1 : 0;
+ uint8_t kh_version = 0; /* Fall back version of key handle. */
+
+ static const size_t kh_version_response_size[] = {
+ sizeof(struct u2f_generate_resp),
+ sizeof(struct u2f_generate_versioned_resp),
+ sizeof(struct u2f_generate_versioned_resp_v2)
+ };
/**
* 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;
+ union u2f_key_handle_variant *kh_buf = NULL;
uint8_t authTimeSecretHash[U2F_AUTH_TIME_SECRET_SIZE];
@@ -83,22 +87,32 @@ enum vendor_cmd_rc u2f_generate_cmd(enum vendor_cmd_cc code, void *buf,
if (state == NULL)
return VENDOR_RC_INTERNAL_ERROR;
+ if ((req->flags & U2F_V2_KH_MASK) == U2F_V2_KH_MASK)
+ kh_version = U2F_KH_VERSION_2;
+ else if (req->flags & U2F_UV_ENABLED_KH)
+ kh_version = U2F_KH_VERSION_1;
+
+ /* Check there is enough room for response in response buffer. */
+ if (response_buf_size < kh_version_response_size[kh_version])
+ return VENDOR_RC_BOGUS_ARGS;
+
/* 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;
+ switch (kh_version) {
+ case U2F_KH_VERSION_2:
+ pubKey = &resp->v2.pubKey;
+ kh_buf = (union u2f_key_handle_variant *)&resp->v2.keyHandle;
+ break;
+ case U2F_KH_VERSION_1:
+ pubKey = &resp->v1.pubKey;
+ kh_buf = (union u2f_key_handle_variant *)&resp->v1.keyHandle;
+ break;
+ default: /* Version 0 */
+ pubKey = &resp->v0.pubKey;
+ kh_buf = (union u2f_key_handle_variant *)&resp->v0.keyHandle;
+ break;
}
/* Maybe enforce user presence, w/ optional consume */
@@ -122,12 +136,7 @@ enum vendor_cmd_rc u2f_generate_cmd(enum vendor_cmd_cc code, void *buf,
* From this point: the request 'req' content is invalid as it is
* overridden by the response we are building in the same buffer.
*/
- if (kh_version == 0) {
- *response_size = sizeof(struct u2f_generate_resp);
- } else {
- *response_size = sizeof(struct u2f_generate_versioned_resp);
- }
-
+ *response_size = kh_version_response_size[kh_version];
return VENDOR_RC_SUCCESS;
}
DECLARE_VENDOR_COMMAND(VENDOR_CC_U2F_GENERATE, u2f_generate_cmd);
@@ -135,17 +144,17 @@ DECLARE_VENDOR_COMMAND(VENDOR_CC_U2F_GENERATE, u2f_generate_cmd);
/* 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)
{
- const struct u2f_sign_req *req = buf;
- const struct u2f_sign_versioned_req *req_versioned = buf;
+ const union u2f_sign_request *req = buf;
union u2f_key_handle_variant *kh;
const struct u2f_state *state = u2f_get_state();
- const uint8_t *hash, *user, *origin /* TODO: *authTimeSecret = NULL */;
+ const uint8_t *hash, *user, *origin, *authTimeSecret = NULL;
uint8_t flags;
struct u2f_sign_resp *resp;
@@ -167,26 +176,38 @@ enum vendor_cmd_rc u2f_sign_cmd(enum vendor_cmd_cc code, void *buf,
*/
if (input_size == sizeof(struct u2f_sign_req)) {
kh_version = 0;
- kh = (union u2f_key_handle_variant *)&req->keyHandle;
- hash = req->hash;
- flags = req->flags;
- user = req->userSecret;
- origin = req->appId;
+ kh = (union u2f_key_handle_variant *)&req->v0.keyHandle;
+ hash = req->v0.hash;
+ flags = req->v0.flags;
+ user = req->v0.userSecret;
+ origin = req->v0.appId;
} else if (input_size == sizeof(struct u2f_sign_versioned_req)) {
- kh = (union u2f_key_handle_variant *)&req_versioned->keyHandle;
- kh_version = kh->v1.version;
- hash = req_versioned->hash;
- flags = req_versioned->flags;
- user = req_versioned->userSecret;
- origin = req_versioned->appId;
- /* TODO: authTimeSecret = req_versioned->authTimeSecret; */
- } else {
+ kh = (union u2f_key_handle_variant *)&req->v1.keyHandle;
+ kh_version = U2F_KH_VERSION_1;
+ hash = req->v1.hash;
+ flags = req->v1.flags;
+ user = req->v1.userSecret;
+ origin = req->v1.appId;
+ /**
+ * TODO(b/184393647): Enforce user verification if no user
+ * presence check is requested. Set
+ * authTimeSecret = req->v1.authTimeSecret;
+ * unconditionally or if (flags & U2F_AUTH_FLAG_TUP) == 0
+ */
+ authTimeSecret = NULL;
+ } else if (input_size == sizeof(struct u2f_sign_versioned_req_v2)) {
+ kh = (union u2f_key_handle_variant *)&req->v2.keyHandle;
+ kh_version = U2F_KH_VERSION_2;
+ hash = req->v2.hash;
+ flags = req->v2.flags;
+ user = req->v2.userSecret;
+ origin = req->v2.appId;
+ authTimeSecret = req->v2.authTimeSecret;
+ } else
return VENDOR_RC_BOGUS_ARGS;
- }
- /* TODO(b/184393647): pass authTimeSecret when ready. */
result = u2f_authorize_keyhandle(state, kh, kh_version, user, origin,
- NULL);
+ authTimeSecret);
if (result == EC_ERROR_ACCESS_DENIED)
return VENDOR_RC_PASSWORD_REQUIRED;
if (result != EC_SUCCESS)
@@ -198,6 +219,7 @@ enum vendor_cmd_rc u2f_sign_cmd(enum vendor_cmd_cc code, void *buf,
/*
* Enforce user presence for version 0 KHs, with optional consume.
+ * TODO(b/184393647): update logic for version 2 if needed.
*/
if (pop_check_presence(flags & G2F_CONSUME) != POP_TOUCH_YES) {
if (kh_version != U2F_KH_VERSION_1)
@@ -215,15 +237,8 @@ enum vendor_cmd_rc u2f_sign_cmd(enum vendor_cmd_cc code, void *buf,
*/
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,
- (struct u2f_signature *)resp);
+ result = u2f_sign(state, kh, kh_version, user, origin, authTimeSecret,
+ hash, (struct u2f_signature *)resp);
if (result == EC_ERROR_ACCESS_DENIED)
return VENDOR_RC_PASSWORD_REQUIRED;
@@ -240,7 +255,7 @@ static inline size_t u2f_attest_format_size(uint8_t format)
{
switch (format) {
case U2F_ATTEST_FORMAT_REG_RESP:
- return sizeof(struct g2f_register_msg);
+ return sizeof(struct g2f_register_msg_v0);
default:
return 0;
}
@@ -253,7 +268,7 @@ static enum vendor_cmd_rc u2f_attest_cmd(enum vendor_cmd_cc code, void *buf,
{
const struct u2f_attest_req *req = buf;
struct u2f_attest_resp *resp;
- struct g2f_register_msg *msg = (void *)req->data;
+ struct g2f_register_msg_v0 *msg = (void *)req->data;
enum ec_error_list result;
size_t response_buf_size = *response_size;
@@ -276,7 +291,7 @@ static enum vendor_cmd_rc u2f_attest_cmd(enum vendor_cmd_cc code, void *buf,
if (req->format != U2F_ATTEST_FORMAT_REG_RESP)
return VENDOR_RC_NOT_ALLOWED;
- if (req->dataLen != sizeof(struct g2f_register_msg))
+ if (req->dataLen != sizeof(struct g2f_register_msg_v0))
return VENDOR_RC_NOT_ALLOWED;
if (msg->reserved != 0)
diff --git a/include/u2f.h b/include/u2f.h
index f24eac84a4..cb422db3bf 100644
--- a/include/u2f.h
+++ b/include/u2f.h
@@ -68,7 +68,13 @@ struct u2f_ec_point {
/* The key handle can be used with fingerprint or PIN. */
#define U2F_UV_ENABLED_KH 0x08
+/* Request v2 key handle. Should be used with U2F_UV_ENABLED_KH */
+#define U2F_V2_KH 0x10
+#define U2F_V2_KH_MASK (U2F_V2_KH | U2F_UV_ENABLED_KH)
+
+
#define U2F_KH_VERSION_1 0x01
+#define U2F_KH_VERSION_2 0x02
#define U2F_AUTHORIZATION_SALT_SIZE 16
#define U2F_V0_KH_SIZE 64
@@ -81,6 +87,14 @@ struct u2f_ec_point {
/* Header is composed of version || origin_seed || kh_hmac */
#define U2F_V1_KH_HEADER_SIZE (U2F_ORIGIN_SEED_SIZE + SHA256_DIGEST_SIZE + 1)
+/**
+ * Key handle version = 2 for WebAuthn, bound to device and user.
+ */
+#define U2F_V2_KH_SIZE 81
+
+/* Header is composed of version || origin_seed */
+#define U2F_V2_KH_HEADER_SIZE (U2F_ORIGIN_SEED_SIZE + 1)
+
struct u2f_signature {
uint8_t sig_r[U2F_EC_KEY_SIZE]; /* Signature */
uint8_t sig_s[U2F_EC_KEY_SIZE]; /* Signature */
@@ -122,15 +136,35 @@ struct u2f_key_handle_v0 {
struct u2f_key_handle_v1 {
uint8_t version;
uint8_t origin_seed[U2F_ORIGIN_SEED_SIZE];
+ /* HMAC(u2f_hmac_key, origin || user || origin seed || version) */
uint8_t kh_hmac[SHA256_DIGEST_SIZE];
/* Optionally checked in u2f_sign. */
uint8_t authorization_salt[U2F_AUTHORIZATION_SALT_SIZE];
+ /**
+ * HMAC(u2f_hmac_key,
+ * auth_salt || version || origin_seed || kh_hmac || authTimeSecret)
+ */
+ uint8_t authorization_hmac[SHA256_DIGEST_SIZE];
+};
+
+/* Key handle version = 2, bound to device and user. */
+struct u2f_key_handle_v2 {
+ uint8_t version;
+ uint8_t origin_seed[U2F_ORIGIN_SEED_SIZE];
+ /* Always checked in u2f_sign. */
+ uint8_t authorization_salt[U2F_AUTHORIZATION_SALT_SIZE];
+ /**
+ * HMAC(u2f_hmac_key,
+ * auth_salt || version || origin_seed || origin ||
+ * user || authTimeSecret)
+ */
uint8_t authorization_hmac[SHA256_DIGEST_SIZE];
};
union u2f_key_handle_variant {
struct u2f_key_handle_v0 v0;
struct u2f_key_handle_v1 v1;
+ struct u2f_key_handle_v2 v2;
};
/* TODO(louiscollard): Add Descriptions. */
@@ -156,6 +190,22 @@ struct u2f_generate_versioned_resp {
struct u2f_versioned_key_handle keyHandle;
};
+struct u2f_generate_versioned_resp_v2 {
+ struct u2f_ec_point pubKey; /* Generated public key */
+ struct u2f_key_handle_v2 keyHandle;
+};
+
+/**
+ * Combined type for U2F_GENERATE response. Length of response size
+ * should be used to determine which version of key handle is generated.
+ * Caller may check that response matches request flags.
+ */
+union u2f_generate_response {
+ struct u2f_generate_resp v0;
+ struct u2f_generate_versioned_resp v1;
+ struct u2f_generate_versioned_resp_v2 v2;
+};
+
struct u2f_sign_req {
uint8_t appId[U2F_APPID_SIZE]; /* Application id */
uint8_t userSecret[U2F_USER_SECRET_SIZE];
@@ -173,6 +223,25 @@ struct u2f_sign_versioned_req {
struct u2f_versioned_key_handle keyHandle;
};
+struct u2f_sign_versioned_req_v2 {
+ 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_P256_SIZE];
+ uint8_t flags;
+ struct u2f_key_handle_v2 keyHandle;
+};
+
+/**
+ * Combined type for U2F_SIGN request. Length of request size
+ * is used to determine which version of key handle is provided.
+ */
+union u2f_sign_request {
+ struct u2f_sign_req v0;
+ struct u2f_sign_versioned_req v1;
+ struct u2f_sign_versioned_req_v2 v2;
+};
+
struct u2f_sign_resp {
uint8_t sig_r[U2F_P256_SIZE]; /* Signature */
uint8_t sig_s[U2F_P256_SIZE]; /* Signature */
@@ -182,7 +251,15 @@ struct u2f_attest_req {
uint8_t userSecret[U2F_USER_SECRET_SIZE];
uint8_t format;
uint8_t dataLen;
- uint8_t data[U2F_MAX_ATTEST_SIZE];
+ uint8_t data[U2F_MAX_ATTEST_SIZE]; /* struct g2f_register_msg_vX */
+};
+
+struct g2f_register_msg_v0 {
+ 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;
};
struct u2f_attest_resp {
diff --git a/include/u2f_cmds.h b/include/u2f_cmds.h
index 96c2883e2d..00a12af808 100644
--- a/include/u2f_cmds.h
+++ b/include/u2f_cmds.h
@@ -20,14 +20,11 @@ BUILD_ASSERT(sizeof(struct u2f_key_handle_v1) ==
BUILD_ASSERT(sizeof(struct u2f_signature) == sizeof(struct u2f_sign_resp));
BUILD_ASSERT(sizeof(struct u2f_signature) == sizeof(struct u2f_attest_resp));
-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;
-};
+BUILD_ASSERT(sizeof(struct u2f_key_handle_v0) == U2F_V0_KH_SIZE);
+BUILD_ASSERT(sizeof(struct u2f_key_handle_v1) == U2F_V1_KH_SIZE);
+BUILD_ASSERT(sizeof(struct u2f_key_handle_v2) == U2F_V2_KH_SIZE);
+BUILD_ASSERT(sizeof(struct g2f_register_msg_v0) <= U2F_MAX_ATTEST_SIZE);
/**
* U2F_GENERATE command handler. Generates a key handle according to input
* parameters.
diff --git a/test/tpm_test/u2f_test.py b/test/tpm_test/u2f_test.py
index 6fe470dc8d..156399d740 100644
--- a/test/tpm_test/u2f_test.py
+++ b/test/tpm_test/u2f_test.py
@@ -81,28 +81,56 @@ def u2f_test(tpm):
user = b'2'
auth = b'3'
msg = b'12345'
- public_key1, khv1 = u2f_generate(tpm, origin, user, 0, auth)
+
+ print('U2F_GENERATE v0');
+ public_key0, khv0 = u2f_generate(tpm, origin, user, 0, auth)
+ if tpm.debug_enabled():
+ print('key_handle v0 = ',utils.hex_dump(khv0), len(khv0))
+ print('public_key v0 = ',utils.hex_dump(public_key0), len(public_key0))
+
+ print('U2F_GENERATE v1');
+ public_key1, khv1 = u2f_generate(tpm, origin, user, 8, auth)
if tpm.debug_enabled():
print('key_handle v1 = ',utils.hex_dump(khv1), len(khv1))
- print('public_key v1 = ',utils.hex_dump(public_key1), len(public_key1))
- public_key2, khv2 = u2f_generate(tpm, origin, user, 8, auth)
+ print('U2F_GENERATE v2');
+ public_key2, khv2 = u2f_generate(tpm, origin, user, 24, auth)
if tpm.debug_enabled():
print('key_handle v2 = ',utils.hex_dump(khv2), len(khv2))
+ print('U2F_SIGN v0');
+ sig1 = u2f_sign(tpm, origin, user, auth, khv0, msg, 2)
+ if tpm.debug_enabled():
+ print('sig v0 = ',utils.hex_dump(sig1), len(sig1))
+
+ print('U2F_SIGN v0 to fail');
+ sig1 = u2f_sign(tpm, user, origin, auth, khv0, msg, 2, fail=True)
+ if tpm.debug_enabled():
+ print('sig v0 = ',utils.hex_dump(sig1), len(sig1))
+
+ print('U2F_SIGN v1');
sig1 = u2f_sign(tpm, origin, user, auth, khv1, msg, 2)
if tpm.debug_enabled():
print('sig v1 = ',utils.hex_dump(sig1), len(sig1))
+ print('U2F_SIGN v1 to fail');
+ sig1 = u2f_sign(tpm, user, origin, auth, khv1, msg, 2, fail=True)
+ if tpm.debug_enabled():
+ print('sig v1 = ',utils.hex_dump(sig1), len(sig1))
+
+
+ print('U2F_SIGN v2');
sig1 = u2f_sign(tpm, origin, user, auth, khv2, msg, 2)
if tpm.debug_enabled():
print('sig v2 = ',utils.hex_dump(sig1), len(sig1))
+ print('U2F_SIGN v2 to fail');
sig1 = u2f_sign(tpm, user, origin, auth, khv2, msg, 2, fail=True)
if tpm.debug_enabled():
print('sig v2 = ',utils.hex_dump(sig1), len(sig1))
- sig_attest = u2f_attest(tpm, origin, user, auth, khv1, public_key1)
+ print('U2F_ATTEST v0');
+ sig_attest = u2f_attest(tpm, origin, user, auth, khv0, public_key0)
if tpm.debug_enabled():
print('sig attest = ',utils.hex_dump(sig_attest), len(sig_attest))
print('%sSUCCESS: %s' % (utils.cursor_back(), 'U2F test'))