diff options
author | Louis Collard <louiscollard@chromium.org> | 2019-01-21 19:29:47 +0800 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2019-01-31 18:18:49 -0800 |
commit | c758f2f435a54615507319200615a2a35a088100 (patch) | |
tree | b4e38c892328b8e2f6e4e66a8d8527e302a4e052 | |
parent | 846eb6500388e81b74730b6a02d44c4ede4b9279 (diff) | |
download | chrome-ec-c758f2f435a54615507319200615a2a35a088100.tar.gz |
cr50: Add new U2F generate, sign and attest commands.
These new commands expose a more generic API, which will
allow a refactoring that removes most U2F-specific logic
from cr50, and moves it into u2fd.
CQ-DEPEND=CL:1371584
BRANCH=none
BUG=b:123161715
TEST=local testing using g2ftool
Signed-off-by: Louis Collard <louiscollard@chromium.org>
Change-Id: I32067ce01e4bb31a331994b4e91d5b56d125cbb1
Reviewed-on: https://chromium-review.googlesource.com/1425137
Commit-Ready: ChromeOS CL Exonerator Bot <chromiumos-cl-exonerator@appspot.gserviceaccount.com>
Reviewed-by: Andrey Pronin <apronin@chromium.org>
-rw-r--r-- | common/u2f.c | 240 | ||||
-rw-r--r-- | include/tpm_vendor_cmds.h | 7 | ||||
-rw-r--r-- | include/u2f.h | 41 |
3 files changed, 288 insertions, 0 deletions
diff --git a/common/u2f.c b/common/u2f.c index b6c8a799d3..de12608ba0 100644 --- a/common/u2f.c +++ b/common/u2f.c @@ -9,6 +9,7 @@ #include "cryptoc/p256.h" #include "cryptoc/sha256.h" #include "dcrypto.h" +#include "extension.h" #include "nvcounter.h" #include "system.h" #include "u2f_impl.h" @@ -389,3 +390,242 @@ ret_status: return ret_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[SHA256_DIGEST_SIZE]; + p256_int od, opk_x, opk_y; + + /* Key handle */ + uint8_t kh[U2F_FIXED_KH_SIZE]; + uint8_t tmp[U2F_FIXED_KH_SIZE]; + + if (input_size != sizeof(U2F_GENERATE_REQ) || + *response_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 */ + if (u2f_origin_keypair(od_seed, &od, &opk_x, &opk_y) != + EC_SUCCESS) { + CPRINTF("Origin keypair gen failed"); + return VENDOR_RC_INTERNAL_ERROR; + } + + /* Generate key handle */ + /* Interleave origin ID and origin priv key, wrap and export. */ + interleave32(req->appId, od_seed, tmp); + if (wrap_kh(NULL, tmp, kh, ENCRYPT_MODE) != 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; + + /* 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)); + + *response_size = sizeof(resp->pubKey) + + sizeof(kh); + + return VENDOR_RC_SUCCESS; +} +DECLARE_VENDOR_COMMAND(VENDOR_CC_U2F_GENERATE, u2f_generate); + +/* 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; + + /* Decrypted key handle. */ + uint8_t unwrapped_kh[KH_LEN]; + + /* Contents of key handle after de-interleaving. */ + uint8_t od_seed[SHA256_DIGEST_SIZE]; + uint8_t origin[U2F_APPID_SIZE]; + + struct drbg_ctx ctx; + + /* Origin private key. */ + p256_int origin_d; + + /* Hash, and corresponding signature. */ + p256_int h, r, s; + + if (input_size != sizeof(U2F_SIGN_REQ)) + return VENDOR_RC_BOGUS_ARGS; + + /* Decrypt and unwrap key handle. */ + if (wrap_kh(NULL, req->keyHandle, unwrapped_kh, DECRYPT_MODE)) + return VENDOR_RC_NOT_ALLOWED; + deinterleave64(unwrapped_kh, origin, od_seed); + + /* Check origin matches. */ + if (memcmp(origin, req->appId, U2F_APPID_SIZE) != 0) + return VENDOR_RC_NOT_ALLOWED; + + /* 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 (u2f_origin_key(od_seed, &origin_d)) + 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; + + p256_to_bin(&r, resp->sig_r); + p256_to_bin(&s, resp->sig_s); + + *response_size = sizeof(U2F_SIGN_RESP); + return 0; +} +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(uint8_t data_size, + const uint8_t *data) +{ + struct G2F_REGISTER_MSG *msg = (void *) data; + uint8_t unwrapped_kh[KH_LEN]; + + if (data_size != sizeof(struct G2F_REGISTER_MSG)) + return VENDOR_RC_NOT_ALLOWED; + + /* Check if we can decrypt keyhandle. */ + if (wrap_kh(NULL, msg->key_handle, unwrapped_kh, DECRYPT_MODE)) + return VENDOR_RC_NOT_ALLOWED; + + return VENDOR_RC_SUCCESS; +} + +static int u2f_attest_verify(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(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; + + if (input_size != sizeof(U2F_ATTEST_REQ)) + return VENDOR_RC_BOGUS_ARGS; + + verify_ret = u2f_attest_verify(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; + + 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); diff --git a/include/tpm_vendor_cmds.h b/include/tpm_vendor_cmds.h index a2e2b6920e..af91eaa2c3 100644 --- a/include/tpm_vendor_cmds.h +++ b/include/tpm_vendor_cmds.h @@ -123,6 +123,13 @@ enum vendor_cmd_cc { */ VENDOR_CC_GET_PWR_BTN = 43, + /* + * U2F commands. + */ + VENDOR_CC_U2F_GENERATE = 44, + VENDOR_CC_U2F_SIGN = 45, + VENDOR_CC_U2F_ATTEST = 46, + LAST_VENDOR_COMMAND = 65535, }; diff --git a/include/u2f.h b/include/u2f.h index e0b70d9751..e059f98e32 100644 --- a/include/u2f.h +++ b/include/u2f.h @@ -28,6 +28,9 @@ extern "C" { #define U2F_CTR_SIZE 4 // Size of counter field #define U2F_APPID_SIZE 32 // Size of application id #define U2F_CHAL_SIZE 32 // Size of challenge +#define U2F_MAX_ATTEST_SIZE 256 // Size of largest blob to sign +#define U2F_P256_SIZE 32 +#define U2F_FIXED_KH_SIZE 64 // Size of fixed size key handles #define ENC_SIZE(x) ((x + 7) & 0xfff8) @@ -91,6 +94,41 @@ typedef struct { uint8_t sig[U2F_MAX_EC_SIG_SIZE]; // Signature } U2F_AUTHENTICATE_RESP; +// TODO(louiscollard): Add Descriptions. + +typedef struct { + uint8_t appId[U2F_APPID_SIZE]; // Application id + uint8_t flags; +} U2F_GENERATE_REQ; + +typedef struct { + U2F_EC_POINT pubKey; // Generated public key + uint8_t keyHandle[U2F_FIXED_KH_SIZE]; // Key handle +} U2F_GENERATE_RESP; + +typedef struct { + uint8_t appId[U2F_APPID_SIZE]; // Application id + uint8_t keyHandle[U2F_FIXED_KH_SIZE]; // Key handle + uint8_t hash[U2F_P256_SIZE]; + uint8_t flags; +} U2F_SIGN_REQ; + +typedef struct { + uint8_t sig_r[U2F_P256_SIZE]; // Signature + uint8_t sig_s[U2F_P256_SIZE]; // Signature +} U2F_SIGN_RESP; + +typedef struct { + uint8_t format; + uint8_t dataLen; + uint8_t data[U2F_MAX_ATTEST_SIZE]; +} U2F_ATTEST_REQ; + +typedef struct { + uint8_t sig_r[U2F_P256_SIZE]; + uint8_t sig_s[U2F_P256_SIZE]; +} U2F_ATTEST_RESP; + // Command status responses #define U2F_SW_NO_ERROR 0x9000 // SW_NO_ERROR @@ -110,6 +148,9 @@ typedef struct { #define G2F_ATTEST 0x80 // Fixed attestation key #define G2F_CONSUME 0x02 // Consume presence +// U2F Attest format for U2F Register Response. +#define U2F_ATTEST_FORMAT_REG_RESP 0 + // Vendor command to enable/disable the extensions #define U2F_VENDOR_MODE U2F_VENDOR_LAST |