blob: bca71aedf46485a09fdcc4610889ebc5a9be84e6 [file] [log] [blame]
/* 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 "nvcounter.h"
#include "system.h"
#include "u2f_impl.h"
#include "u2f.h"
#include "util.h"
#define CPRINTF(format, args...) cprintf(CC_EXTENSION, format, ##args)
/* Crypto parameters */
#define AES_BLOCK_LEN 16
#define KH_LEN 64
/* Interleave bytes of two 32 byte arrays */
static void interleave32(const uint8_t *a, const uint8_t *b, uint8_t *out)
{
size_t i;
for (i = 0; i < 32; ++i) {
out[2 * i + 0] = a[i];
out[2 * i + 1] = b[i];
}
}
/* 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 anonymous_cert(const p256_int *d, const p256_int *pk_x,
const p256_int *pk_y, uint8_t *cert, const int n)
{
return DCRYPTO_x509_gen_u2f_cert(d, pk_x, pk_y, NULL, cert, n);
}
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(d, pk_x, pk_y, serial, cert, n);
}
static unsigned u2f_version(struct apdu apdu, void *buf, unsigned *ret_len,
unsigned max_len)
{
static const char version[] = "U2F_V2";
if (apdu.len || max_len < sizeof(version) - 1)
return U2F_SW_WRONG_LENGTH;
memcpy(buf, version, sizeof(version) - 1 /* not ending zero */);
*ret_len = sizeof(version) - 1;
return U2F_SW_NO_ERROR;
}
/* U2F REGISTER command */
static unsigned u2f_register(struct apdu apdu, void *buf,
unsigned *ret_len, unsigned max_len)
{
const U2F_REGISTER_REQ *req = (const U2F_REGISTER_REQ *)apdu.data;
U2F_REGISTER_RESP *resp;
int l, m_off; /* msg length and interior offset */
p256_int r, s; /* ecdsa signature */
struct drbg_ctx ctx;
/* Origin keypair */
uint8_t od_seed[SHA256_DIGEST_SIZE];
p256_int od, opk_x, opk_y;
/* KDF, Key handle */
HASH_CTX sha;
uint8_t kh[U2F_APPID_SIZE + sizeof(p256_int)];
uint8_t tmp[U2F_APPID_SIZE + sizeof(p256_int)];
/* sha256({RFU, app ID, nonce, keyhandle, public key}) */
p256_int h;
const uint8_t rfu = U2F_REGISTER_HASH_ID;
const uint8_t pk_start = U2F_POINT_UNCOMPRESSED;
p256_int att_d;
int cert_len;
const int cert_max_len = max_len - sizeof(kh)
- offsetof(U2F_REGISTER_RESP, keyHandleCertSig);
if (apdu.len != sizeof(U2F_REGISTER_REQ)) {
CPRINTF("#ERR REGISTER wrong length");
return U2F_SW_WRONG_LENGTH;
}
/* Check user presence, w/ optional consume */
if (pop_check_presence(apdu.p1 & G2F_CONSUME) != POP_TOUCH_YES &&
(apdu.p1 & U2F_AUTH_FLAG_TUP) != 0) {
return U2F_SW_CONDITIONS_NOT_SATISFIED;
}
/* Generate origin-specific keypair */
if (u2f_origin_keypair(od_seed, &od, &opk_x, &opk_y) !=
EC_SUCCESS) {
CPRINTF("#ERR Origin-specific keypair generation failed");
return U2F_SW_WTF + 1;
}
/* Generate key handle */
/* Interleave origin ID, origin priv key, wrap and export. */
interleave32(req->appId, od_seed, tmp);
if (wrap_kh(req->appId, tmp, kh, ENCRYPT_MODE) != EC_SUCCESS)
return U2F_SW_WTF + 2;
/* Response message hash for signing */
DCRYPTO_SHA256_init(&sha, 0);
HASH_update(&sha, &rfu, sizeof(rfu));
HASH_update(&sha, req->appId, U2F_APPID_SIZE);
HASH_update(&sha, req->chal, U2F_CHAL_SIZE);
HASH_update(&sha, kh, sizeof(kh));
HASH_update(&sha, &pk_start, sizeof(pk_start));
/*
* 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;
/* 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 */
HASH_update(&sha, resp->pubKey.x, sizeof(p256_int));
HASH_update(&sha, resp->pubKey.y, sizeof(p256_int));
p256_from_bin(HASH_final(&sha), &h);
/* Construct remainder of the response */
resp->registerId = U2F_REGISTER_ID;
l = sizeof(resp->registerId);
resp->pubKey.pointFormat = U2F_POINT_UNCOMPRESSED;
l += sizeof(resp->pubKey);
resp->keyHandleLen = sizeof(kh);
l += sizeof(resp->keyHandleLen);
memcpy(resp->keyHandleCertSig, kh, sizeof(kh));
l += sizeof(kh);
m_off = sizeof(kh);
if (use_g2f() && apdu.p1 & G2F_ATTEST) {
/* Use a hw-derived keypair for Individual attestation */
if (g2f_individual_keypair(&att_d, &opk_x, &opk_y)) {
CPRINTF("#ERR Attestation key generation failed");
return U2F_SW_WTF + 3;
}
cert_len = individual_cert(&att_d, &opk_x, &opk_y,
resp->keyHandleCertSig + m_off, cert_max_len);
} else {
/* Anon attestation keypair; use origin key to self-sign */
cert_len = anonymous_cert(&od, &opk_x, &opk_y,
resp->keyHandleCertSig + m_off, cert_max_len);
att_d = od;
}
if (cert_len == 0)
return U2F_SW_WTF + 4;
l += cert_len;
m_off += cert_len;
/* Sign over the response w/ the attestation key */
drbg_rfc6979_init(&ctx, &att_d, &h);
if (!dcrypto_p256_ecdsa_sign(&ctx, &att_d, &h, &r, &s)) {
p256_clear(&att_d);
p256_clear(&od);
CPRINTF("#ERR signing error");
return U2F_SW_WTF + 5;
}
p256_clear(&att_d);
p256_clear(&od);
/* Signature -> ASN.1 DER encoded bytes */
l += DCRYPTO_asn1_sigp(resp->keyHandleCertSig + m_off, &r, &s);
*ret_len = l;
return U2F_SW_NO_ERROR; /* APDU success */
}
static unsigned u2f_authenticate(struct apdu apdu, void *buf,
unsigned *ret_len, unsigned max_len)
{
const U2F_AUTHENTICATE_REQ *req = (const void *)apdu.data;
U2F_AUTHENTICATE_RESP *resp;
uint8_t unwrapped_kh[KH_LEN];
uint8_t od_seed[SHA256_DIGEST_SIZE];
struct drbg_ctx ctx;
p256_int origin_d;
uint8_t origin[U2F_APPID_SIZE];
HASH_CTX sha;
p256_int h, r, s;
unsigned sig_len;
uint8_t flags;
uint8_t ctr[U2F_CTR_SIZE];
uint32_t count = 0;
if (apdu.len != U2F_APPID_SIZE + U2F_CHAL_SIZE + 1 + KH_LEN) {
CPRINTF("#ERR AUTHENTICATE wrong length %d", apdu.len);
return U2F_SW_WRONG_LENGTH;
}
/* Unwrap key handle */
if (wrap_kh(req->appId, req->keyHandle, unwrapped_kh, DECRYPT_MODE))
return U2F_SW_WTF + 1;
deinterleave64(unwrapped_kh, origin, od_seed);
/* Check whether appId (i.e. origin) matches.
* Constant time.
*/
p256_from_bin(origin, &r);
p256_from_bin(req->appId, &s);
if (p256_cmp(&r, &s) != 0)
return U2F_SW_WRONG_DATA;
/* Origin check only? */
if (apdu.p1 == U2F_AUTH_CHECK_ONLY)
return U2F_SW_CONDITIONS_NOT_SATISFIED;
/* Sense user presence, with optional consume */
flags = pop_check_presence(apdu.p1 & G2F_CONSUME) == POP_TOUCH_YES;
/* Mandatory user presence? */
if ((apdu.p1 & U2F_AUTH_ENFORCE) != 0 && flags == 0)
return U2F_SW_CONDITIONS_NOT_SATISFIED;
/* Increment-only counter in flash. OK to share between origins. */
count = nvcounter_incr();
ctr[0] = (count >> 24) & 0xFF;
ctr[1] = (count >> 16) & 0xFF;
ctr[2] = (count >> 8) & 0xFF;
ctr[3] = count & 0xFF;
/* Message signature */
DCRYPTO_SHA256_init(&sha, 0);
HASH_update(&sha, req->appId, U2F_APPID_SIZE);
HASH_update(&sha, &flags, sizeof(uint8_t));
HASH_update(&sha, ctr, U2F_CTR_SIZE);
HASH_update(&sha, req->chal, U2F_CHAL_SIZE);
p256_from_bin(HASH_final(&sha), &h);
if (u2f_origin_key(od_seed, &origin_d))
return U2F_SW_WTF + 2;
drbg_rfc6979_init(&ctx, &origin_d, &h);
if (!dcrypto_p256_ecdsa_sign(&ctx, &origin_d, &h, &r, &s)) {
p256_clear(&origin_d);
return U2F_SW_WTF + 3;
}
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;
resp->flags = flags;
memcpy(resp->ctr, ctr, U2F_CTR_SIZE);
sig_len = DCRYPTO_asn1_sigp(resp->sig, &r, &s);
*ret_len = sizeof(resp->flags) + U2F_CTR_SIZE + sig_len;
return U2F_SW_NO_ERROR;
}
unsigned u2f_apdu_rcv(uint8_t *buf, unsigned in_len, unsigned max_len)
{
unsigned ret_len = 0;
uint16_t sw = U2F_SW_CLA_NOT_SUPPORTED;
/*
* APDU structure:
* [CLA INS P1 P2 [LC1 [LC2 LC3 <request-data>]]]
*/
uint8_t cla = buf[0];
uint8_t ins = buf[1];
struct apdu apdu = {
.p1 = buf[2],
.p2 = buf[3],
.len = 0,
.data = buf + 5
};
/* ISO7618 LC decoding */
if (in_len >= 5)
apdu.len = buf[4];
if (apdu.len == 0 && in_len >= 7) {
apdu.len = (buf[5] << 8) | buf[6];
apdu.data += 2;
}
CPRINTF("%T/%d U2F APDU ", apdu.len);
/* Is the APDU well-formed including its payload ? */
if (in_len < 4 || (apdu.len > in_len - (apdu.data - buf))) {
sw = U2F_SW_WRONG_LENGTH;
goto ret_status;
}
if (cla == 0x00) { /* Always 0x00 */
sw = U2F_SW_INS_NOT_SUPPORTED;
max_len -= 2; /* reserve space for the status */
switch (ins) {
case (U2F_REGISTER):
CPRINTF("REGISTER");
sw = u2f_register(apdu, buf, &ret_len, max_len);
break;
case (U2F_AUTHENTICATE):
CPRINTF("AUTHENTICATE");
sw = u2f_authenticate(apdu, buf, &ret_len, max_len);
break;
case (U2F_VERSION):
CPRINTF("VERSION");
sw = u2f_version(apdu, buf, &ret_len, max_len);
break;
}
/* Not a U2F INS. Try internal extensions next. */
if (sw == U2F_SW_INS_NOT_SUPPORTED && u2f_custom_dispatch &&
(use_g2f() || ins == U2F_VENDOR_MODE))
sw = u2f_custom_dispatch(ins, apdu, buf, &ret_len);
}
ret_status:
/* append SW status word */
buf[ret_len++] = sw >> 8;
buf[ret_len++] = sw;
CPRINTF(" resp %04x len %d\n", sw, ret_len);
return ret_len;
}