From 38690405baf21e64dd7ec4f6be329098a2e330ac Mon Sep 17 00:00:00 2001 From: Vadim Sukhomlinov Date: Thu, 30 Sep 2021 23:53:39 -0700 Subject: cr50: add support for v2 of U2F key handle for WebAuthn 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 Change-Id: I647ded7a2c157cea91ac48a2ba679def318c1e63 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/3199671 Reviewed-by: Vadim Sukhomlinov Reviewed-by: Andrey Pronin Tested-by: Vadim Sukhomlinov Auto-Submit: Vadim Sukhomlinov Commit-Queue: Vadim Sukhomlinov --- board/cr50/dcrypto/u2f.c | 272 +++++++++++++++++++++++++++++++++------------- common/u2f.c | 125 +++++++++++---------- include/u2f.h | 79 +++++++++++++- include/u2f_cmds.h | 11 +- test/tpm_test/u2f_test.py | 36 +++++- 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')) -- cgit v1.2.1