diff options
Diffstat (limited to 'common/u2f.c')
-rw-r--r-- | common/u2f.c | 445 |
1 files changed, 0 insertions, 445 deletions
diff --git a/common/u2f.c b/common/u2f.c deleted file mode 100644 index 8cef638d3a..0000000000 --- a/common/u2f.c +++ /dev/null @@ -1,445 +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. - */ - -/* APDU dispatcher and U2F command handlers. */ - -#include "console.h" -#include "cryptoc/p256.h" -#include "cryptoc/sha256.h" -#include "dcrypto.h" -#include "extension.h" -#include "system.h" -#include "u2f_impl.h" -#include "u2f.h" -#include "util.h" - -#define G2F_CERT_NAME "CrOS" - -#define CPRINTF(format, args...) cprintf(CC_EXTENSION, format, ##args) - -/* Crypto parameters */ -#define AES_BLOCK_LEN 16 -#define KH_LEN 64 - -/* De-interleave 64 bytes into two 32 arrays. */ -static void deinterleave64(const uint8_t *in, uint8_t *a, uint8_t *b) -{ - size_t i; - - for (i = 0; i < 32; ++i) { - a[i] = in[2 * i + 0]; - b[i] = in[2 * i + 1]; - } -} - -/* (un)wrap w/ the origin dependent KEK. */ -static int wrap_kh(const uint8_t *origin, const uint8_t *in, - uint8_t *out, enum encrypt_mode mode) -{ - uint8_t kek[SHA256_DIGEST_SIZE]; - uint8_t iv[AES_BLOCK_LEN] = {0}; - int i; - - /* KEK derivation */ - if (u2f_gen_kek(origin, kek, sizeof(kek))) - return EC_ERROR_UNKNOWN; - - DCRYPTO_aes_init(kek, 256, iv, CIPHER_MODE_CBC, mode); - - for (i = 0; i < 4; i++) - DCRYPTO_aes_block(in + i * AES_BLOCK_LEN, - out + i * AES_BLOCK_LEN); - - return EC_SUCCESS; -} - -static int individual_cert(const p256_int *d, const p256_int *pk_x, - const p256_int *pk_y, uint8_t *cert, const int n) -{ - p256_int *serial; - - if (system_get_chip_unique_id((uint8_t **)&serial) != P256_NBYTES) - return 0; - - 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; - - /* 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 */ -static enum vendor_cmd_rc u2f_generate(enum vendor_cmd_cc code, - void *buf, - size_t input_size, - size_t *response_size) -{ - U2F_GENERATE_REQ *req = buf; - U2F_GENERATE_RESP *resp; - - /* Origin keypair */ - uint8_t od_seed[P256_NBYTES]; - p256_int od, opk_x, opk_y; - - /* Key handle */ - uint8_t kh[U2F_FIXED_KH_SIZE]; - - /* Whether keypair generation succeeded */ - int generate_keypair_rc; - - size_t response_buf_size = *response_size; - - *response_size = 0; - - if (input_size != sizeof(U2F_GENERATE_REQ) || - response_buf_size < sizeof(U2F_GENERATE_RESP)) - return VENDOR_RC_BOGUS_ARGS; - - /* Maybe enforce user presence, w/ optional consume */ - if (pop_check_presence(req->flags & G2F_CONSUME) != POP_TOUCH_YES && - (req->flags & U2F_AUTH_FLAG_TUP) != 0) - return VENDOR_RC_NOT_ALLOWED; - - /* Generate origin-specific keypair */ - do { - if (!DCRYPTO_ladder_random(&od_seed)) - return VENDOR_RC_INTERNAL_ERROR; - - if (u2f_origin_user_keyhandle(req->appId, req->userSecret, - od_seed, kh) != EC_SUCCESS) - return VENDOR_RC_INTERNAL_ERROR; - - generate_keypair_rc = - u2f_origin_user_keypair(kh, &od, &opk_x, &opk_y); - } while (generate_keypair_rc == EC_ERROR_TRY_AGAIN); - - if (generate_keypair_rc != EC_SUCCESS) - return VENDOR_RC_INTERNAL_ERROR; - - /* - * From this point: the request 'req' content is invalid as it is - * overridden by the response we are building in the same buffer. - */ - resp = buf; - - *response_size = sizeof(*resp); - - /* 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(kh)); - - return VENDOR_RC_SUCCESS; -} -DECLARE_VENDOR_COMMAND(VENDOR_CC_U2F_GENERATE, u2f_generate); - -static int verify_kh_pubkey(const uint8_t *key_handle, - const U2F_EC_POINT *public_key, int *matches) { - int rc; - U2F_EC_POINT kh_pubkey; - p256_int od, opk_x, opk_y; - - rc = u2f_origin_user_keypair(key_handle, &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(U2F_EC_POINT)) == 0; - - return EC_SUCCESS; -} - -static int verify_kh_owned(const uint8_t *user_secret, const uint8_t *app_id, - const uint8_t *key_handle, int *owned) -{ - int rc; - /* Re-created key handle. */ - uint8_t recreated_kh[KH_LEN]; - - /* - * 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, - recreated_kh); - - if (rc == EC_SUCCESS) - *owned = safe_memcmp(recreated_kh, key_handle, KH_LEN) == 0; - - return rc; -} - -static int verify_legacy_kh_owned(const uint8_t *app_id, - const uint8_t *key_handle, - uint8_t *origin_seed) -{ - uint8_t unwrapped_kh[KH_LEN]; - uint8_t kh_app_id[U2F_APPID_SIZE]; - - p256_int app_id_p256; - p256_int kh_app_id_p256; - - /* Unwrap key handle */ - if (wrap_kh(app_id, key_handle, unwrapped_kh, DECRYPT_MODE)) - return 0; - deinterleave64(unwrapped_kh, kh_app_id, origin_seed); - - /* Return whether appId (i.e. origin) matches. */ - p256_from_bin(app_id, &app_id_p256); - p256_from_bin(kh_app_id, &kh_app_id_p256); - return p256_cmp(&app_id_p256, &kh_app_id_p256) == 0; -} - -/* Below, we depend on the response not being larger than than the request. */ -BUILD_ASSERT(sizeof(U2F_SIGN_RESP) <= sizeof(U2F_SIGN_REQ)); - -/* U2F SIGN command */ -static enum vendor_cmd_rc u2f_sign(enum vendor_cmd_cc code, - void *buf, - size_t input_size, - size_t *response_size) -{ - const U2F_SIGN_REQ *req = buf; - U2F_SIGN_RESP *resp; - - struct drbg_ctx ctx; - - /* Whether the key handle is owned by this device. */ - int kh_owned; - - /* Origin private key. */ - uint8_t legacy_origin_seed[SHA256_DIGEST_SIZE]; - p256_int origin_d; - - /* Hash, and corresponding signature. */ - p256_int h, r, s; - - /* Whether the key handle uses the legacy key derivation scheme. */ - int legacy_kh = 0; - - /* Response is smaller than request, so no need to check this. */ - *response_size = 0; - - if (input_size != sizeof(U2F_SIGN_REQ)) - return VENDOR_RC_BOGUS_ARGS; - - if (verify_kh_owned(req->userSecret, req->appId, req->keyHandle, - &kh_owned) != EC_SUCCESS) - return VENDOR_RC_INTERNAL_ERROR; - - if (!kh_owned) { - if ((req->flags & SIGN_LEGACY_KH) == 0) - return VENDOR_RC_PASSWORD_REQUIRED; - - /* - * We have a key handle which is not valid for the new scheme, - * but may be a valid legacy key handle, and we have been asked - * to sign legacy key handles. - */ - if (verify_legacy_kh_owned(req->appId, req->keyHandle, - legacy_origin_seed)) - legacy_kh = 1; - else - return VENDOR_RC_PASSWORD_REQUIRED; - } - - /* We might not actually need to sign anything. */ - if (req->flags == U2F_AUTH_CHECK_ONLY) - return VENDOR_RC_SUCCESS; - - /* Always enforce user presence, with optional consume. */ - if (pop_check_presence(req->flags & G2F_CONSUME) != POP_TOUCH_YES) - return VENDOR_RC_NOT_ALLOWED; - - /* 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(req->keyHandle, &origin_d, NULL, - NULL) != EC_SUCCESS) - return VENDOR_RC_INTERNAL_ERROR; - } - - /* Prepare hash to sign. */ - p256_from_bin(req->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); - - /* - * 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; - - *response_size = sizeof(*resp); - - 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)]; - 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; - - 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, msg->key_handle, - &verified) != EC_SUCCESS) - return VENDOR_RC_INTERNAL_ERROR; - - if (!verified) - return VENDOR_RC_NOT_ALLOWED; - - if (verify_kh_pubkey(msg->key_handle, &msg->public_key, &verified) != - EC_SUCCESS) - return VENDOR_RC_INTERNAL_ERROR; - - if (!verified) - return VENDOR_RC_NOT_ALLOWED; - - return VENDOR_RC_SUCCESS; -} - -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) -{ - switch (format) { - case U2F_ATTEST_FORMAT_REG_RESP: - return sizeof(struct G2F_REGISTER_MSG); - default: - return 0; - } -} - -/* U2F ATTEST command */ -static enum vendor_cmd_rc u2f_attest(enum vendor_cmd_cc code, - void *buf, - size_t input_size, - size_t *response_size) -{ - const U2F_ATTEST_REQ *req = buf; - U2F_ATTEST_RESP *resp; - - int verify_ret; - - HASH_CTX h_ctx; - struct drbg_ctx dr_ctx; - - /* Data hash, and corresponding signature. */ - p256_int h, r, s; - - /* Attestation key */ - p256_int d, pk_x, pk_y; - - size_t response_buf_size = *response_size; - - *response_size = 0; - - if (input_size < offsetof(U2F_ATTEST_REQ, data) || - input_size < (offsetof(U2F_ATTEST_REQ, data) + req->dataLen) || - input_size > sizeof(U2F_ATTEST_REQ) || - response_buf_size < sizeof(*resp)) - return VENDOR_RC_BOGUS_ARGS; - - verify_ret = u2f_attest_verify(req->userSecret, - req->format, - req->dataLen, - req->data); - - if (verify_ret != VENDOR_RC_SUCCESS) - return verify_ret; - - /* Message signature */ - DCRYPTO_SHA256_init(&h_ctx, 0); - HASH_update(&h_ctx, req->data, u2f_attest_format_size(req->format)); - p256_from_bin(HASH_final(&h_ctx), &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); - - /* - * 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; - - *response_size = sizeof(*resp); - - p256_to_bin(&r, resp->sig_r); - p256_to_bin(&s, resp->sig_s); - - return VENDOR_RC_SUCCESS; -} -DECLARE_VENDOR_COMMAND(VENDOR_CC_U2F_ATTEST, u2f_attest); |