diff options
author | Yicheng Li <yichengli@chromium.org> | 2020-07-23 17:57:08 -0700 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2020-08-18 05:55:41 +0000 |
commit | 4b4bbc8d761225b260625584dadbc6d4e623ef2d (patch) | |
tree | a540022243de10afb4b677c119611564609f728b | |
parent | 4e9e48219c254654027040a09a181f377784b281 (diff) | |
download | chrome-ec-stabilize-13421.53.B-cr50_stab.tar.gz |
Reland "u2f: Append hmac of auth time secret to versioned KH"stabilize-quickfix-13421.74.B-cr50_stabstabilize-quickfix-13421.100.B-cr50_stabstabilize-13421.99.B-cr50_stabstabilize-13421.96.B-cr50_stabstabilize-13421.89.B-cr50_stabstabilize-13421.80.B-cr50_stabstabilize-13421.73.B-cr50_stabstabilize-13421.53.B-cr50_stabstabilize-13421.42.B-cr50_stabstabilize-13421.103.B-cr50_stabstabilize-13421.102.B-cr50_stabrelease-R86-13421.B-cr50_stabfirmware-zork-13421.B-cr50_stab
This is a reland of d2627d12bb21308f49a72cadaf47a0a86730a960 with one
modification: The versioned key handle header (the old "key handle"
concept) is now used in the derivation of authorization_hmac. This is
to tie the key handle to the authorization secret.
Original change's description:
> u2f: Append hmac of auth time secret to versioned KH
>
> When generating versioned KHs, u2fd should send a public derivative
> (sha256) of the user's auth time secret to cr50. Cr50 derives an
> hmac of it and appends this authorization_hmac to the KH.
>
> When signing versioned KHs, u2fd may supply the unhashed auth time
> secret. Cr50 will check the authorization_hmac if no power button press.
> If the reconstructed hmac matches authorization_hmac, power button press
> is waived.
>
> Currently for v1, we will just prepare the authorization_hmac but not
> enforce it. This is because fingerprint and PIN are unable to unlock
> the same secret.
>
> While we waive power button press for v1, we can enforce
> authorization_hmac whenever auth-time secrets is ready.
>
> BUG=b:144861739
> TEST=- Use a known 32-byte "auth-time secret"
> - Compute the sha256 of the auth-time secret (this is public)
> - u2f_generate with the computed "authTimeSecretHash"
> - Add code to u2f_sign command handler such that cr50 computes
> the sha256 of the supplied auth-time secret at u2f_sign time
> and require power button press if the hmac doesn't match.
> - u2f_sign with the true auth-time secret -> observe in logging
> that hmac matches, and no power button press required.
> - u2f_sign with a wrong auth-time secret -> observe in logging
> that hmac doesn't match, and power button press is required
> for signing.
>
> Cq-Depend: chromium:2321731
> Change-Id: Ib9ae913667f8178ac7a4790f861d7dada972c4a0
> Signed-off-by: Yicheng Li <yichengli@chromium.org>
> Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2317047
> Reviewed-by: Andrey Pronin <apronin@chromium.org>
> Reviewed-by: Vadim Sukhomlinov <sukhomlinov@chromium.org>
BUG=b:144861739
TEST=See original CL's TEST above
Cq-Depend: chromium:2327865
Change-Id: Ia1b0b4a585ec604398cfa730354ae1a91e7bc00b
Signed-off-by: Yicheng Li <yichengli@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2355177
Reviewed-by: Andrey Pronin <apronin@chromium.org>
-rw-r--r-- | board/cr50/u2f.c | 33 | ||||
-rw-r--r-- | common/u2f.c | 77 | ||||
-rw-r--r-- | include/u2f.h | 23 | ||||
-rw-r--r-- | include/u2f_impl.h | 15 |
4 files changed, 113 insertions, 35 deletions
diff --git a/board/cr50/u2f.c b/board/cr50/u2f.c index 7c6273823c..7fe1f6484f 100644 --- a/board/cr50/u2f.c +++ b/board/cr50/u2f.c @@ -201,7 +201,8 @@ int u2f_origin_user_keyhandle(const uint8_t *origin, const uint8_t *user, 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 *key_handle) + uint8_t version, + struct u2f_versioned_key_handle_header *key_handle_header) { LITE_HMAC_CTX ctx; struct u2f_state *state = get_state(); @@ -209,16 +210,17 @@ int u2f_origin_user_versioned_keyhandle( if (!state) return EC_ERROR_UNKNOWN; - key_handle->version = version; - memcpy(key_handle->origin_seed, origin_seed, P256_NBYTES); + key_handle_header->version = version; + memcpy(key_handle_header->origin_seed, origin_seed, P256_NBYTES); DCRYPTO_HMAC_SHA256_init(&ctx, state->salt_kek, SHA256_DIGEST_SIZE); HASH_update(&ctx.hash, origin, P256_NBYTES); HASH_update(&ctx.hash, user, P256_NBYTES); HASH_update(&ctx.hash, origin_seed, P256_NBYTES); - HASH_update(&ctx.hash, &version, sizeof(key_handle->version)); + HASH_update(&ctx.hash, &version, sizeof(key_handle_header->version)); - memcpy(key_handle->hmac, DCRYPTO_HMAC_final(&ctx), SHA256_DIGEST_SIZE); + memcpy(key_handle_header->kh_hmac, DCRYPTO_HMAC_final(&ctx), + SHA256_DIGEST_SIZE); return EC_SUCCESS; } @@ -250,6 +252,27 @@ int u2f_origin_user_keypair(const uint8_t *key_handle, size_t key_handle_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) +{ + LITE_HMAC_CTX ctx; + struct u2f_state *state = get_state(); + + if (!state) + return EC_ERROR_UNKNOWN; + + DCRYPTO_HMAC_SHA256_init(&ctx, state->salt_kek, SHA256_DIGEST_SIZE); + HASH_update(&ctx.hash, authorization_salt, U2F_AUTHORIZATION_SALT_SIZE); + HASH_update(&ctx.hash, (uint8_t *)header, + sizeof(struct u2f_versioned_key_handle_header)); + HASH_update(&ctx.hash, auth_time_secret_hash, SHA256_DIGEST_SIZE); + + memcpy(hmac, DCRYPTO_HMAC_final(&ctx), SHA256_DIGEST_SIZE); + + return EC_SUCCESS; +} + int u2f_gen_kek(const uint8_t *origin, uint8_t *kek, size_t key_len) { uint32_t buf[P256_NDIGITS]; diff --git a/common/u2f.c b/common/u2f.c index eaeb38b08c..91cb4ab121 100644 --- a/common/u2f.c +++ b/common/u2f.c @@ -10,6 +10,7 @@ #include "cryptoc/sha256.h" #include "dcrypto.h" #include "extension.h" +#include "fips_rand.h" #include "system.h" #include "u2f_impl.h" #include "u2f.h" @@ -127,8 +128,10 @@ static enum vendor_cmd_rc u2f_generate(enum vendor_cmd_cc code, void *buf, struct u2f_key_handle kh; struct u2f_versioned_key_handle vkh; } kh_buf; - size_t kh_size = (kh_version == 0) ? sizeof(kh_buf.kh) : - sizeof(kh_buf.vkh); + 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; @@ -168,13 +171,14 @@ static enum vendor_cmd_rc u2f_generate(enum vendor_cmd_cc code, void *buf, else generate_kh_rc = u2f_origin_user_versioned_keyhandle( req->appId, req->userSecret, od_seed, - kh_version, &kh_buf.vkh); + 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, kh_size, &od, &opk_x, &opk_y); + (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) @@ -188,6 +192,17 @@ static enum vendor_cmd_rc u2f_generate(enum vendor_cmd_cc code, void *buf, copy_kh_pubkey_out(&opk_x, &opk_y, &kh_buf.kh, buf); *response_size = sizeof(struct u2f_generate_resp); } else { + if (!fips_rand_bytes(kh_buf.vkh.authorization_salt, + U2F_AUTHORIZATION_SALT_SIZE)) + return VENDOR_RC_INTERNAL_ERROR; + + if (u2f_authorization_hmac(kh_buf.vkh.authorization_salt, + &kh_buf.vkh.header, + req->authTimeSecretHash, + kh_buf.vkh.authorization_hmac) != + EC_SUCCESS) + return VENDOR_RC_INTERNAL_ERROR; + copy_versioned_kh_pubkey_out(&opk_x, &opk_y, &kh_buf.vkh, buf); *response_size = sizeof(struct u2f_generate_versioned_resp); } @@ -242,14 +257,14 @@ static int verify_kh_owned(const uint8_t *user_secret, const uint8_t *app_id, return rc; } -static int -verify_versioned_kh_owned(const uint8_t *user_secret, const uint8_t *app_id, - const struct u2f_versioned_key_handle *key_handle, - int *owned) +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 recreated_kh; + struct u2f_versioned_key_handle_header recreated_kh_header; /* * Re-create the key handle and compare against that which @@ -258,13 +273,13 @@ verify_versioned_kh_owned(const uint8_t *user_secret, const uint8_t *app_id, */ rc = u2f_origin_user_versioned_keyhandle(app_id, user_secret, - key_handle->origin_seed, - key_handle->version, - &recreated_kh); + key_handle_header->origin_seed, + key_handle_header->version, + &recreated_kh_header); if (rc == EC_SUCCESS) - *owned = safe_memcmp(&recreated_kh, key_handle, - sizeof(recreated_kh)) == 0; + *owned = safe_memcmp(&recreated_kh_header, key_handle_header, + sizeof(recreated_kh_header)) == 0; return rc; } @@ -321,8 +336,8 @@ static enum vendor_cmd_rc u2f_sign(enum vendor_cmd_cc code, void *buf, /* Version of KH; 0 if KH is not versioned. */ uint8_t version; - /* Size of KH in bytes. */ - size_t kh_size; + /* Size of the part of KH used to derive keypair, in bytes. */ + size_t keypair_input_size; int verify_owned_rc; @@ -334,18 +349,19 @@ static enum vendor_cmd_rc u2f_sign(enum vendor_cmd_cc code, void *buf, key_handle = (uint8_t *)&req->keyHandle; hash = req->hash; flags = req->flags; - kh_size = sizeof(struct u2f_key_handle); + 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)) { - version = req_versioned->keyHandle.version; + version = req_versioned->keyHandle.header.version; key_handle = (uint8_t *)&req_versioned->keyHandle; hash = req_versioned->hash; flags = req_versioned->flags; - kh_size = sizeof(struct u2f_versioned_key_handle); + 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, &kh_owned); + &req_versioned->keyHandle.header, &kh_owned); } else { return VENDOR_RC_BOGUS_ARGS; } @@ -378,17 +394,28 @@ static enum vendor_cmd_rc u2f_sign(enum vendor_cmd_cc code, void *buf, if ((flags & U2F_AUTH_CHECK_ONLY) == U2F_AUTH_CHECK_ONLY) return VENDOR_RC_SUCCESS; - /* Always enforce user presence, with optional consume. */ - if (pop_check_presence(flags & G2F_CONSUME) != POP_TOUCH_YES) - return VENDOR_RC_NOT_ALLOWED; + /* + * Enforce user presence for version 0 KHs, with optional consume. + */ + if (pop_check_presence(flags & G2F_CONSUME) != POP_TOUCH_YES) { + 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 (legacy_kh) { if (u2f_origin_key(legacy_origin_seed, &origin_d) != EC_SUCCESS) return VENDOR_RC_INTERNAL_ERROR; } else { - if (u2f_origin_user_keypair(key_handle, kh_size, &origin_d, - NULL, NULL) != EC_SUCCESS) + if (u2f_origin_user_keypair(key_handle, keypair_input_size, + &origin_d, NULL, + NULL) != EC_SUCCESS) return VENDOR_RC_INTERNAL_ERROR; } diff --git a/include/u2f.h b/include/u2f.h index 61b9677185..6680ef5300 100644 --- a/include/u2f.h +++ b/include/u2f.h @@ -31,6 +31,8 @@ extern "C" { #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 */ @@ -53,15 +55,24 @@ struct u2f_ec_point { #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[U2F_P256_SIZE]; + uint8_t hmac[SHA256_DIGEST_SIZE]; }; -struct u2f_versioned_key_handle { +struct u2f_versioned_key_handle_header { uint8_t version; uint8_t origin_seed[U2F_P256_SIZE]; - uint8_t hmac[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. */ @@ -70,6 +81,11 @@ 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 { @@ -93,6 +109,7 @@ struct u2f_sign_req { 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; diff --git a/include/u2f_impl.h b/include/u2f_impl.h index 5bd69309c6..2c63d11d87 100644 --- a/include/u2f_impl.h +++ b/include/u2f_impl.h @@ -76,13 +76,14 @@ int u2f_origin_user_keyhandle(const uint8_t *origin, const uint8_t *user, * @param user pointer to user secret * @param seed pointer to origin-specific random seed * @param version the version byte to pack; should be greater than 0. - * @param key_handle buffer to hold the output key handle + * @param key_handle_header buffer to hold the output key handle header * * @return EC_SUCCESS if a valid keypair was created. */ 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 *key_handle); + uint8_t version, + struct u2f_versioned_key_handle_header *key_handle_header); /** * Generate an origin and user-specific ECDSA keypair from the specified @@ -101,6 +102,16 @@ int u2f_origin_user_versioned_keyhandle( 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); +/** + * 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); + /*** * Generate a hardware derived 256b private key. * |