diff options
Diffstat (limited to 'common/u2f.c')
-rw-r--r-- | common/u2f.c | 203 |
1 files changed, 159 insertions, 44 deletions
diff --git a/common/u2f.c b/common/u2f.c index 79ad69c01a..27e1685696 100644 --- a/common/u2f.c +++ b/common/u2f.c @@ -79,20 +79,59 @@ int g2f_attestation_cert(uint8_t *buf) G2F_ATTESTATION_CERT_MAX_LEN); } +static void copy_kh_pubkey_out(p256_int *opk_x, p256_int *opk_y, + struct u2f_key_handle *kh, void *buf) +{ + struct u2f_generate_resp *resp = buf; + + /* 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(struct u2f_key_handle)); +} + +static void copy_versioned_kh_pubkey_out(p256_int *opk_x, p256_int *opk_y, + struct u2f_versioned_key_handle *kh, + void *buf) +{ + struct u2f_generate_versioned_resp *resp = buf; + + /* 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(struct u2f_versioned_key_handle)); +} + /* 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) { struct u2f_generate_req *req = buf; - struct u2f_generate_resp *resp; + uint8_t kh_version = + (req->flags & U2F_UV_ENABLED_KH) ? U2F_KH_VERSION_1 : 0; - /* Origin keypair */ - uint8_t od_seed[P256_NBYTES]; + /* Origin keypair. Must be word aligned, otherwise TRNG will crash. */ + uint8_t od_seed[P256_NBYTES] __aligned(4); p256_int od, opk_x, opk_y; - /* Key handle */ - uint8_t kh[U2F_FIXED_KH_SIZE]; + /* Buffer for generating key handle. */ + union { + 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); + /* Whether key handle generation succeeded */ + int generate_kh_rc; /* Whether keypair generation succeeded */ int generate_keypair_rc; @@ -100,10 +139,18 @@ static enum vendor_cmd_rc u2f_generate(enum vendor_cmd_cc code, void *buf, *response_size = 0; - if (input_size != sizeof(struct u2f_generate_req) || - response_buf_size < sizeof(struct u2f_generate_resp)) + if (input_size != sizeof(struct u2f_generate_req)) return VENDOR_RC_BOGUS_ARGS; + if (kh_version == 0) { + if (response_buf_size < sizeof(struct u2f_generate_resp)) + return VENDOR_RC_BOGUS_ARGS; + } else { + if (response_buf_size < + sizeof(struct u2f_generate_versioned_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) @@ -114,12 +161,20 @@ static enum vendor_cmd_rc u2f_generate(enum vendor_cmd_cc code, void *buf, 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) + if (kh_version == 0) + generate_kh_rc = u2f_origin_user_keyhandle( + req->appId, req->userSecret, od_seed, + &kh_buf.kh); + else + generate_kh_rc = u2f_origin_user_versioned_keyhandle( + req->appId, req->userSecret, od_seed, + kh_version, &kh_buf.vkh); + + if (generate_kh_rc != EC_SUCCESS) return VENDOR_RC_INTERNAL_ERROR; - generate_keypair_rc = - u2f_origin_user_keypair(kh, &od, &opk_x, &opk_y); + generate_keypair_rc = u2f_origin_user_keypair( + (uint8_t *)&kh_buf, kh_size, &od, &opk_x, &opk_y); } while (generate_keypair_rc == EC_ERROR_TRY_AGAIN); if (generate_keypair_rc != EC_SUCCESS) @@ -129,31 +184,27 @@ static enum vendor_cmd_rc u2f_generate(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. */ - 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)); + if (kh_version == 0) { + copy_kh_pubkey_out(&opk_x, &opk_y, &kh_buf.kh, buf); + *response_size = sizeof(struct u2f_generate_resp); + } else { + copy_versioned_kh_pubkey_out(&opk_x, &opk_y, &kh_buf.vkh, buf); + *response_size = sizeof(struct u2f_generate_versioned_resp); + } return VENDOR_RC_SUCCESS; } DECLARE_VENDOR_COMMAND(VENDOR_CC_U2F_GENERATE, u2f_generate); -static int verify_kh_pubkey(const uint8_t *key_handle, +static int verify_kh_pubkey(const uint8_t *key_handle, size_t key_handle_size, const struct u2f_ec_point *public_key, int *matches) { int rc; struct u2f_ec_point kh_pubkey; p256_int od, opk_x, opk_y; - rc = u2f_origin_user_keypair(key_handle, &od, &opk_x, &opk_y); + rc = u2f_origin_user_keypair(key_handle, key_handle_size, &od, &opk_x, + &opk_y); if (rc != EC_SUCCESS) return rc; @@ -169,11 +220,36 @@ static int verify_kh_pubkey(const uint8_t *key_handle, } static int verify_kh_owned(const uint8_t *user_secret, const uint8_t *app_id, - const uint8_t *key_handle, int *owned) + const struct u2f_key_handle *key_handle, int *owned) +{ + int rc; + /* Re-created key handle. */ + struct u2f_key_handle recreated_kh; + + /* + * 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->origin_seed, &recreated_kh); + + if (rc == EC_SUCCESS) + *owned = safe_memcmp(&recreated_kh, key_handle, + sizeof(recreated_kh)) == 0; + + 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) { int rc; /* Re-created key handle. */ - uint8_t recreated_kh[KH_LEN]; + struct u2f_versioned_key_handle recreated_kh; /* * Re-create the key handle and compare against that which @@ -181,11 +257,14 @@ static int verify_kh_owned(const uint8_t *user_secret, const uint8_t *app_id, * 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); + rc = u2f_origin_user_versioned_keyhandle(app_id, user_secret, + key_handle->origin_seed, + key_handle->version, + &recreated_kh); if (rc == EC_SUCCESS) - *owned = safe_memcmp(recreated_kh, key_handle, KH_LEN) == 0; + *owned = safe_memcmp(&recreated_kh, key_handle, + sizeof(recreated_kh)) == 0; return rc; } @@ -219,12 +298,15 @@ static enum vendor_cmd_rc u2f_sign(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 uint8_t *key_handle, *hash; + uint8_t flags; struct u2f_sign_resp *resp; struct drbg_ctx ctx; /* Whether the key handle is owned by this device. */ - int kh_owned; + int kh_owned = 0; /* Origin private key. */ uint8_t legacy_origin_seed[SHA256_DIGEST_SIZE]; @@ -236,18 +318,47 @@ static enum vendor_cmd_rc u2f_sign(enum vendor_cmd_cc code, void *buf, /* Whether the key handle uses the legacy key derivation scheme. */ int legacy_kh = 0; + /* Version of KH; 0 if KH is not versioned. */ + uint8_t version; + + /* Size of KH in bytes. */ + size_t kh_size; + + int verify_owned_rc; + /* Response is smaller than request, so no need to check this. */ *response_size = 0; - if (input_size != sizeof(struct u2f_sign_req)) + if (input_size == sizeof(struct u2f_sign_req)) { + version = 0; + key_handle = (uint8_t *)&req->keyHandle; + hash = req->hash; + flags = req->flags; + kh_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; + key_handle = (uint8_t *)&req->keyHandle; + hash = req_versioned->hash; + flags = req_versioned->flags; + kh_size = sizeof(struct u2f_versioned_key_handle); + verify_owned_rc = verify_versioned_kh_owned( + req_versioned->userSecret, req_versioned->appId, + &req_versioned->keyHandle, &kh_owned); + } else { return VENDOR_RC_BOGUS_ARGS; + } - if (verify_kh_owned(req->userSecret, req->appId, req->keyHandle, - &kh_owned) != EC_SUCCESS) + if (verify_owned_rc != EC_SUCCESS) return VENDOR_RC_INTERNAL_ERROR; if (!kh_owned) { - if ((req->flags & SIGN_LEGACY_KH) == 0) + if ((flags & SIGN_LEGACY_KH) == 0) + return VENDOR_RC_PASSWORD_REQUIRED; + + /* Legacy KH must be version 0. */ + if (version != 0) return VENDOR_RC_PASSWORD_REQUIRED; /* @@ -255,7 +366,8 @@ static enum vendor_cmd_rc u2f_sign(enum vendor_cmd_cc code, void *buf, * 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, + if (verify_legacy_kh_owned(req->appId, + (uint8_t *)&req->keyHandle, legacy_origin_seed)) legacy_kh = 1; else @@ -263,11 +375,11 @@ static enum vendor_cmd_rc u2f_sign(enum vendor_cmd_cc code, void *buf, } /* We might not actually need to sign anything. */ - if (req->flags == U2F_AUTH_CHECK_ONLY) + 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(req->flags & G2F_CONSUME) != POP_TOUCH_YES) + if (pop_check_presence(flags & G2F_CONSUME) != POP_TOUCH_YES) return VENDOR_RC_NOT_ALLOWED; /* Re-create origin-specific key. */ @@ -275,13 +387,13 @@ static enum vendor_cmd_rc u2f_sign(enum vendor_cmd_cc code, void *buf, 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) + if (u2f_origin_user_keypair(key_handle, kh_size, &origin_d, + NULL, NULL) != EC_SUCCESS) return VENDOR_RC_INTERNAL_ERROR; } /* Prepare hash to sign. */ - p256_from_bin(req->hash, &h); + p256_from_bin(hash, &h); /* Sign. */ hmac_drbg_init_rfc6979(&ctx, &origin_d, &h); @@ -321,6 +433,8 @@ static inline int u2f_attest_verify_reg_resp(const uint8_t *user_secret, { struct g2f_register_msg *msg = (void *)data; int verified; + /* We only do u2f_attest on non-versioned KHs. */ + const int key_handle_size = sizeof(struct u2f_key_handle); if (data_size != sizeof(struct g2f_register_msg)) return VENDOR_RC_NOT_ALLOWED; @@ -328,15 +442,16 @@ static inline int u2f_attest_verify_reg_resp(const uint8_t *user_secret, if (msg->reserved != 0) return VENDOR_RC_NOT_ALLOWED; - if (verify_kh_owned(user_secret, msg->app_id, msg->key_handle, + if (verify_kh_owned(user_secret, msg->app_id, + (struct u2f_key_handle *)&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) + if (verify_kh_pubkey(msg->key_handle, key_handle_size, &msg->public_key, + &verified) != EC_SUCCESS) return VENDOR_RC_INTERNAL_ERROR; if (!verified) |