summaryrefslogtreecommitdiff
path: root/common/u2f.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/u2f.c')
-rw-r--r--common/u2f.c445
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);