diff options
-rw-r--r-- | board/cr50/build.mk | 6 | ||||
-rw-r--r-- | board/cr50/dcrypto/internal.h | 10 | ||||
-rw-r--r-- | board/cr50/dcrypto/p256.c | 95 | ||||
-rw-r--r-- | board/cr50/dcrypto/u2f.c | 7 | ||||
-rw-r--r-- | board/cr50/dcrypto/u2f_impl.h | 2 | ||||
-rw-r--r-- | board/cr50/tpm2/ecc.c | 208 | ||||
-rw-r--r-- | common/u2f.c | 17 |
7 files changed, 186 insertions, 159 deletions
diff --git a/board/cr50/build.mk b/board/cr50/build.mk index 4576e30c0e..b221c4f050 100644 --- a/board/cr50/build.mk +++ b/board/cr50/build.mk @@ -19,7 +19,7 @@ ifeq ($(BOARD_MK_INCLUDED_ONCE),) # List of variables which can be defined in the environment or set in the make # command line. ENV_VARS := CR50_DEV CRYPTO_TEST H1_RED_BOARD U2F_TEST RND_TEST DRBG_TEST\ - ECDSA_TEST DCRYPTO_TEST + ECDSA_TEST DCRYPTO_TEST P256_BIN_TEST ifneq ($(CRYPTO_TEST),) CPPFLAGS += -DCRYPTO_TEST_SETUP @@ -44,6 +44,10 @@ ifneq ($(DCRYPTO_TEST),) CPPFLAGS += -DCRYPTO_TEST_CMD_DCRYPTO_TEST=1 endif +ifneq ($(P256_BIN_TEST),) +CPPFLAGS += -DP256_BIN_TEST=1 +endif + endif diff --git a/board/cr50/dcrypto/internal.h b/board/cr50/dcrypto/internal.h index 6df2df3ef9..b88d0a1d25 100644 --- a/board/cr50/dcrypto/internal.h +++ b/board/cr50/dcrypto/internal.h @@ -180,7 +180,7 @@ typedef struct p256_int { p256_digit a[P256_NDIGITS]; uint8_t b8[P256_NBYTES]; }; -} __packed p256_int; +} p256_int; extern const p256_int SECP256r1_nMin2; @@ -211,6 +211,14 @@ void p256_to_bin(const p256_int *src, uint8_t dst[P256_NBYTES]); */ void p256_from_bin(const uint8_t src[P256_NBYTES], p256_int *dst); +/** + * Reads from big-endian binary form of given size, add padding with + * zeros if short. Check that leading digits beyond P256_NBYTES are zeroes. + * + * @return true if provided big-endian fits into p256. + */ +bool p256_from_be_bin_size(const uint8_t *src, size_t len, p256_int *dst); + int dcrypto_p256_ecdsa_sign(struct drbg_ctx *drbg, const p256_int *key, const p256_int *message, p256_int *r, p256_int *s) __attribute__((warn_unused_result)); diff --git a/board/cr50/dcrypto/p256.c b/board/cr50/dcrypto/p256.c index 6c923b4699..49c2fe4b2b 100644 --- a/board/cr50/dcrypto/p256.c +++ b/board/cr50/dcrypto/p256.c @@ -4,6 +4,7 @@ */ #include "dcrypto.h" +#include "endian.h" const p256_int SECP256r1_nMin2 = /* P-256 curve order - 2 */ { .a = { 0xfc632551 - 2, 0xf3b9cac2, 0xa7179e84, 0xbce6faad, -1, -1, 0, @@ -135,6 +136,34 @@ void p256_to_bin(const p256_int *src, uint8_t dst[P256_NBYTES]) #endif } +bool p256_from_be_bin_size(const uint8_t *src, size_t len, p256_int *dst) +{ + size_t i; + + /** + * Skip zero padding if input length is larger than P-256 size. + * This may happen with TPM2 commands receiving big-endian number + * with leading zeroes from external sources. + */ + while (len > P256_NBYTES) { + if (*src != 0) + return false; + len--; + src++; + } + + i = len; + /* Now add zero padding little-endian p256 if length is smaller. */ + while (i < P256_NBYTES) { + dst->b8[i] = 0; + i++; + } + reverse_bytes(dst->b8, src, len); + + /* Note: this code is correct only for little-endian platform. */ + return true; +} + void p256_from_bin(const uint8_t src[P256_NBYTES], p256_int *dst) { #if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) @@ -193,3 +222,69 @@ enum hmac_result p256_hmac_drbg_generate(struct drbg_ctx *ctx, p256_int *rnd) return result; } + +#ifndef P256_BIN_TEST +#define P256_BIN_TEST 0 +#endif + +#ifdef CRYPTO_TEST_SETUP + +#if P256_BIN_TEST +#include "console.h" + +static int cmd_p256_bin_test(int argc, char *argv[]) +{ + static const uint8_t i1[] = { + 0, 0, 0x10, 0x11, 0x12, 0x13, 0x20, 0x21, 0x22, + 0x23, 0x30, 0x31, 0x32, 0x33, 0x40, 0x41, 0x42, 0x43, + 0x50, 0x51, 0x52, 0x53, 0x60, 0x61, 0x62, 0x63, 0x70, + 0x71, 0x72, 0x73, 0x80, 0x81, 0x82, 0x83, 0x84 + }; + p256_int e = { .a = { 0x80818283, 0x70717273, 0x60616263, 0x50515253, + 0x40414243, 0x30313233, 0x20212223, + 0x10111213 } }; + + p256_int r; + bool passed = true; + bool result; + + /* zero padded */ + result = p256_from_be_bin_size(i1, 34, &r); + passed = result && (p256_cmp(&r, &e) == 0); + ccprintf("in=%ph\nout=%ph\n", HEX_BUF(i1, 34), HEX_BUF(&r, sizeof(r))); + + /* right sized. */ + memset(&r, 0, sizeof(r)); + result = p256_from_be_bin_size(i1 + 2, 32, &r); + passed = passed && (p256_cmp(&r, &e) == 0); + ccprintf("in=%ph\nout=%ph\n", HEX_BUF(i1 + 2, 32), + HEX_BUF(&r, sizeof(r))); + + /** + * Smaller big num, where padding high byte(s) with zeroes is needed. + * we do this by loading 31 byte starting 1 byte higher. + * This will result in same value as in 'e' except that the + * highest byte will be 0 (e.a[7] == 0x00111213). + */ + memset(&r, 0, sizeof(r)); + result = p256_from_be_bin_size(i1 + 3, 31, &r); + + /* Update expected result by clearing high byte */ + e.b8[31] = 0x00; + passed = passed && (p256_cmp(&r, &e) == 0); + ccprintf("in=%ph\nout=%ph\n", HEX_BUF(i1 + 3, 31), + HEX_BUF(&r, sizeof(r))); + + /* larger big num. */ + result = p256_from_be_bin_size(i1 + 2, 33, &r); + passed = passed && !result; + + ccprintf("p256_from_be_bin_size() test %s\n", + (passed) ? "PASSED" : "NOT PASSED"); + + return EC_SUCCESS; +} +DECLARE_SAFE_CONSOLE_COMMAND(p256_test, cmd_p256_bin_test, NULL, NULL); +#endif /* P256_BIN_TEST */ + +#endif /* CRYPTO_TEST_SETUP */ diff --git a/board/cr50/dcrypto/u2f.c b/board/cr50/dcrypto/u2f.c index 9f987fbd64..91c80577b6 100644 --- a/board/cr50/dcrypto/u2f.c +++ b/board/cr50/dcrypto/u2f.c @@ -433,7 +433,7 @@ static bool g2f_individual_key_pair(const struct u2f_state *state, p256_int *d, #define G2F_CERT_NAME "CrO2" size_t g2f_attestation_cert_serial(const struct u2f_state *state, - const uint8_t *serial, uint8_t *buf) + const p256_int *serial, uint8_t *buf) { p256_int d, pk_x, pk_y; @@ -441,9 +441,8 @@ size_t g2f_attestation_cert_serial(const struct u2f_state *state, return 0; /* Note that max length is not currently respected here. */ - return DCRYPTO_x509_gen_u2f_cert_name(&d, &pk_x, &pk_y, - (p256_int *)serial, G2F_CERT_NAME, - buf, + return DCRYPTO_x509_gen_u2f_cert_name(&d, &pk_x, &pk_y, serial, + G2F_CERT_NAME, buf, G2F_ATTESTATION_CERT_MAX_LEN); } diff --git a/board/cr50/dcrypto/u2f_impl.h b/board/cr50/dcrypto/u2f_impl.h index 7f0f10ef36..be3fbd6b76 100644 --- a/board/cr50/dcrypto/u2f_impl.h +++ b/board/cr50/dcrypto/u2f_impl.h @@ -145,7 +145,7 @@ enum ec_error_list u2f_authorize_keyhandle(const struct u2f_state *state, * @return size of certificate written to buf, 0 on error. */ size_t g2f_attestation_cert_serial(const struct u2f_state *state, - const uint8_t *serial, uint8_t *buf); + const p256_int *serial, uint8_t *buf); /** * Verify that provided key handle and public key match. diff --git a/board/cr50/tpm2/ecc.c b/board/cr50/tpm2/ecc.c index 90a6dac08b..8f9392af28 100644 --- a/board/cr50/tpm2/ecc.c +++ b/board/cr50/tpm2/ecc.c @@ -14,66 +14,22 @@ #include "util.h" #include "dcrypto.h" -static void reverse_tpm2b(TPM2B *b) -{ - reverse(b->buffer, b->size); -} - TPM2B_BYTE_VALUE(4); TPM2B_BYTE_VALUE(32); -static int check_p256_param(const TPM2B_ECC_PARAMETER *a) -{ - return a->b.size == sizeof(p256_int); -} - -static int check_p256_param_in_range(const TPM2B_ECC_PARAMETER *a) -{ - return a->b.size <= sizeof(p256_int); -} - -static int check_p256_point_in_range(const TPMS_ECC_POINT *a) -{ - return check_p256_param_in_range(&a->x) && - check_p256_param_in_range(&a->y); -} - -static void append_zeros_to_p256_param(TPM2B_ECC_PARAMETER *a) -{ - while (a->b.size < sizeof(p256_int)) { - a->b.buffer[a->b.size] = 0; - a->b.size++; - } -} - -static void append_zeros_to_p256_point(TPMS_ECC_POINT *a) -{ - append_zeros_to_p256_param(&a->x); - append_zeros_to_p256_param(&a->y); -} - BOOL _cpri__EccIsPointOnCurve(TPM_ECC_CURVE curve_id, TPMS_ECC_POINT *q) { int result; - int x_size = q->x.b.size; - int y_size = q->y.b.size; + p256_int x, y; switch (curve_id) { case TPM_ECC_NIST_P256: - if (!check_p256_point_in_range(q)) - return FALSE; - - reverse_tpm2b(&q->x.b); - reverse_tpm2b(&q->y.b); - append_zeros_to_p256_point(q); - result = dcrypto_p256_is_valid_point((p256_int *) q->x.b.buffer, - (p256_int *) q->y.b.buffer); + if (!p256_from_be_bin_size(q->x.b.buffer, q->x.b.size, &x) || + !p256_from_be_bin_size(q->y.b.buffer, q->y.b.size, &y)) + return FALSE; - q->x.b.size = x_size; - q->y.b.size = y_size; - reverse_tpm2b(&q->x.b); - reverse_tpm2b(&q->y.b); + result = dcrypto_p256_is_valid_point(&x, &y); if (result) return TRUE; @@ -90,61 +46,44 @@ CRYPT_RESULT _cpri__EccPointMultiply( TPM2B_ECC_PARAMETER *n1, TPMS_ECC_POINT *in, TPM2B_ECC_PARAMETER *n2) { int result; + p256_int n, in_x, in_y, out_x, out_y; switch (curve_id) { case TPM_ECC_NIST_P256: - if ((n1 != NULL && n2 != NULL) || - (n1 == NULL && n2 == NULL)) + if ((n1 != NULL && n2 != NULL) || (n1 == NULL && n2 == NULL)) /* Only one of n1 or n2 must be specified. */ return CRYPT_PARAMETER; - if ((n2 != NULL && in == NULL) || - (n2 == NULL && in != NULL)) + if ((n2 != NULL && in == NULL) || (n2 == NULL && in != NULL)) return CRYPT_PARAMETER; - if (n1 != NULL && !check_p256_param(n1)) + if (n1 != NULL && + !p256_from_be_bin_size(n1->b.buffer, n1->b.size, &n)) return CRYPT_PARAMETER; if (in != NULL && !_cpri__EccIsPointOnCurve(curve_id, in)) return CRYPT_POINT; - if (n2 != NULL && !check_p256_param(n2)) + if (n2 != NULL && + !p256_from_be_bin_size(n2->b.buffer, n2->b.size, &n)) return CRYPT_PARAMETER; if (n1 != NULL) { - reverse_tpm2b(&n1->b); - - result = DCRYPTO_p256_base_point_mul( - (p256_int *) out->x.b.buffer, - (p256_int *) out->y.b.buffer, - (p256_int *) n1->b.buffer); - - reverse_tpm2b(&n1->b); + result = + DCRYPTO_p256_base_point_mul(&out_x, &out_y, &n); } else { - const int x_size = in->x.b.size; - const int y_size = in->y.b.size; - - reverse_tpm2b(&n2->b); - reverse_tpm2b(&in->x.b); - reverse_tpm2b(&in->y.b); - append_zeros_to_p256_point(in); - - result = DCRYPTO_p256_point_mul( - (p256_int *) out->x.b.buffer, - (p256_int *) out->y.b.buffer, - (p256_int *) n2->b.buffer, - (p256_int *) in->x.b.buffer, - (p256_int *) in->y.b.buffer); - - reverse_tpm2b(&n2->b); - in->x.b.size = x_size; - in->y.b.size = y_size; - reverse_tpm2b(&in->x.b); - reverse_tpm2b(&in->y.b); + if (!p256_from_be_bin_size(in->x.b.buffer, in->x.b.size, + &in_x) || + !p256_from_be_bin_size(in->y.b.buffer, in->y.b.size, + &in_y)) + return CRYPT_PARAMETER; + + result = DCRYPTO_p256_point_mul(&out_x, &out_y, &n, + &in_x, &in_y); } + p256_clear(&n); if (result) { out->x.b.size = sizeof(p256_int); out->y.b.size = sizeof(p256_int); - reverse_tpm2b(&out->x.b); - reverse_tpm2b(&out->y.b); - + p256_to_bin(&out_x, out->x.b.buffer); + p256_to_bin(&out_y, out->y.b.buffer); return CRYPT_SUCCESS; } else { return CRYPT_NO_RESULT; @@ -225,22 +164,22 @@ CRYPT_RESULT _cpri__GenerateKeyEcc( } for (; count != 0; count++) { + p256_int x, y, key; + memcpy(marshaled_counter.t.buffer, &count, sizeof(count)); _cpri__KDFa(hash_alg, &local_seed.b, label, local_extra, &marshaled_counter.b, sizeof(key_bytes) * 8, key_bytes, NULL, FALSE); - if (DCRYPTO_p256_key_from_bytes( - (p256_int *) q->x.b.buffer, - (p256_int *) q->y.b.buffer, - (p256_int *) d->b.buffer, key_bytes)) { + if (DCRYPTO_p256_key_from_bytes(&x, &y, &key, key_bytes)) { q->x.b.size = sizeof(p256_int); + p256_to_bin(&x, q->x.b.buffer); + q->y.b.size = sizeof(p256_int); - reverse_tpm2b(&q->x.b); - reverse_tpm2b(&q->y.b); + p256_to_bin(&y, q->y.b.buffer); d->b.size = sizeof(p256_int); - reverse_tpm2b(&d->b); - + p256_to_bin(&key, d->b.buffer); + p256_clear(&key); break; } } @@ -263,7 +202,7 @@ CRYPT_RESULT _cpri__SignEcc( { uint8_t digest_local[sizeof(p256_int)]; const size_t digest_len = MIN(digest->size, sizeof(digest_local)); - p256_int p256_digest; + p256_int p256_digest, key, p256_r, p256_s; int result; if (curve_id != TPM_ECC_NIST_P256) @@ -271,31 +210,23 @@ CRYPT_RESULT _cpri__SignEcc( switch (scheme) { case TPM_ALG_ECDSA: { - const UINT16 d_size = d->b.size; - - if (!check_p256_param_in_range(d)) + if (!p256_from_be_bin_size(d->b.buffer, d->b.size, &key)) return CRYPT_PARAMETER; + /* Truncate / zero-pad the digest as appropriate. */ memset(digest_local, 0, sizeof(digest_local)); memcpy(digest_local + sizeof(digest_local) - digest_len, - digest->buffer, digest_len); + digest->buffer, digest_len); p256_from_bin(digest_local, &p256_digest); - reverse_tpm2b(&d->b); - append_zeros_to_p256_param(d); - - result = fips_p256_ecdsa_sign( - (p256_int *) d->b.buffer, - &p256_digest, - (p256_int *) r->b.buffer, - (p256_int *) s->b.buffer); - d->b.size = d_size; - reverse_tpm2b(&d->b); + result = fips_p256_ecdsa_sign(&key, &p256_digest, &p256_r, + &p256_s); + p256_clear(&key); r->b.size = sizeof(p256_int); s->b.size = sizeof(p256_int); - reverse_tpm2b(&r->b); - reverse_tpm2b(&s->b); + p256_to_bin(&p256_r, r->b.buffer); + p256_to_bin(&p256_s, s->b.buffer); if (result) return CRYPT_SUCCESS; @@ -314,7 +245,7 @@ CRYPT_RESULT _cpri__ValidateSignatureEcc( { uint8_t digest_local[sizeof(p256_int)]; const size_t digest_len = MIN(digest->size, sizeof(digest_local)); - p256_int p256_digest; + p256_int p256_digest, q_x, q_y, p256_r, p256_s; int result; if (curve_id != TPM_ECC_NIST_P256) @@ -322,43 +253,20 @@ CRYPT_RESULT _cpri__ValidateSignatureEcc( switch (scheme) { case TPM_ALG_ECDSA: { - const UINT16 qx_size = q->x.b.size; - const UINT16 qy_size = q->y.b.size; - const UINT16 r_size = r->b.size; - const UINT16 s_size = s->b.size; + if (!p256_from_be_bin_size(q->x.b.buffer, q->x.b.size, &q_x) || + !p256_from_be_bin_size(q->y.b.buffer, q->y.b.size, &q_y) || + !p256_from_be_bin_size(r->b.buffer, r->b.size, &p256_r) || + !p256_from_be_bin_size(s->b.buffer, s->b.size, &p256_s)) + return CRYPT_PARAMETER; /* Truncate / zero-pad the digest as appropriate. */ memset(digest_local, 0, sizeof(digest_local)); memcpy(digest_local + sizeof(digest_local) - digest_len, - digest->buffer, digest_len); + digest->buffer, digest_len); p256_from_bin(digest_local, &p256_digest); - reverse_tpm2b(&q->x.b); - reverse_tpm2b(&q->y.b); - append_zeros_to_p256_point(q); - - reverse_tpm2b(&r->b); - append_zeros_to_p256_param(r); - - reverse_tpm2b(&s->b); - append_zeros_to_p256_param(s); - - result = dcrypto_p256_ecdsa_verify( - (p256_int *) q->x.b.buffer, - (p256_int *) q->y.b.buffer, - &p256_digest, - (p256_int *) r->b.buffer, - (p256_int *) s->b.buffer); - - /* restore original size */ - q->x.b.size = qx_size; - q->y.b.size = qy_size; - reverse_tpm2b(&q->x.b); - reverse_tpm2b(&q->y.b); - r->b.size = r_size; - reverse_tpm2b(&r->b); - s->b.size = s_size; - reverse_tpm2b(&s->b); + result = dcrypto_p256_ecdsa_verify(&q_x, &q_y, &p256_digest, + &p256_r, &p256_s); if (result) return CRYPT_SUCCESS; @@ -375,26 +283,26 @@ CRYPT_RESULT _cpri__GetEphemeralEcc(TPMS_ECC_POINT *q, TPM2B_ECC_PARAMETER *d, { int result; uint8_t key_bytes[P256_NBYTES] __aligned(4); + p256_int x, y, key; if (curve_id != TPM_ECC_NIST_P256) return CRYPT_PARAMETER; rand_bytes(key_bytes, sizeof(key_bytes)); - result = DCRYPTO_p256_key_from_bytes((p256_int *) q->x.b.buffer, - (p256_int *) q->y.b.buffer, - (p256_int *) d->b.buffer, - key_bytes); + result = DCRYPTO_p256_key_from_bytes(&x, &y, &key, key_bytes); + always_memset(key_bytes, 0, sizeof(key_bytes)); if (result) { q->x.b.size = sizeof(p256_int); q->y.b.size = sizeof(p256_int); - reverse_tpm2b(&q->x.b); - reverse_tpm2b(&q->y.b); + p256_to_bin(&x, q->x.b.buffer); + p256_to_bin(&y, q->y.b.buffer); d->b.size = sizeof(p256_int); - reverse_tpm2b(&d->b); + p256_to_bin(&key, d->b.buffer); + p256_clear(&key); return CRYPT_SUCCESS; } else { diff --git a/common/u2f.c b/common/u2f.c index e5fe20371e..0795d88359 100644 --- a/common/u2f.c +++ b/common/u2f.c @@ -21,7 +21,6 @@ #define CPRINTF(format, args...) cprintf(CC_EXTENSION, format, ##args) - size_t g2f_attestation_cert(uint8_t *buf) { uint8_t *serial; @@ -34,7 +33,21 @@ size_t g2f_attestation_cert(uint8_t *buf) if (system_get_chip_unique_id(&serial) != P256_NBYTES) return 0; - return g2f_attestation_cert_serial(state, serial, buf); +#ifdef CHIP_G + /** + * chip/g implementation of system_get_chip_unique_id() always + * returns 32-bit aligned pointer, but host mock-ups do no guarantee + * that, so copy data to aligned location. + */ + return g2f_attestation_cert_serial(state, (p256_int *)serial, buf); +#else + { + p256_int p256_serial; + + memcpy(&p256_serial, serial, sizeof(p256_serial)); + return g2f_attestation_cert_serial(state, &p256_serial, buf); + } +#endif } /* U2F GENERATE command */ |