/* 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 ]]] */ 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 < 5 || (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; }