diff options
Diffstat (limited to 'board/cr50/tpm2/rsa.c')
-rw-r--r-- | board/cr50/tpm2/rsa.c | 150 |
1 files changed, 146 insertions, 4 deletions
diff --git a/board/cr50/tpm2/rsa.c b/board/cr50/tpm2/rsa.c index e48e4fb352..43e7d4e23d 100644 --- a/board/cr50/tpm2/rsa.c +++ b/board/cr50/tpm2/rsa.c @@ -6,6 +6,7 @@ #include "CryptoEngine.h" #include "dcrypto.h" +#include "trng.h" #include <assert.h> @@ -13,6 +14,9 @@ static void reverse_tpm2b(TPM2B *b) { reverse(b->buffer, b->size); } +TPM2B_BYTE_VALUE(4); + +#define RSA_F4 65537 static int check_key(const RSA_KEY *key) { @@ -251,6 +255,109 @@ CRYPT_RESULT _cpri__TestKeyRSA(TPM2B *d_buf, uint32_t e, } } +/* Each 1024-bit prime generation attempt fails with probability + * ~0.5%. Setting an upper limit on the attempts allows for an + * application to display a message and then reattempt. + * TODO(ngm): tweak this value along with performance improvements. */ +#define MAX_GENERATE_ATTEMPTS 3 + +static int generate_prime(struct BIGNUM *b, TPM_ALG_ID hashing, TPM2B *seed, + const char *label, TPM2B *extra, uint32_t *counter) +{ + TPM2B_4_BYTE_VALUE marshaled_counter = { .t = {4} }; + uint32_t initial_counter; + + initial_counter = *counter; + for (; *counter - initial_counter < MAX_GENERATE_ATTEMPTS; + *counter += 1) { + UINT32_TO_BYTE_ARRAY(*counter, marshaled_counter.t.buffer); + _cpri__KDFa(hashing, seed, label, extra, &marshaled_counter.b, + bn_bits(b), (uint8_t *) b->d, NULL, FALSE); + + if (DCRYPTO_bn_generate_prime(b)) + return 1; + } + + return 0; +} + +CRYPT_RESULT _cpri__GenerateKeyRSA( + TPM2B *N_buf, TPM2B *p_buf, uint16_t num_bits, + uint32_t e_buf, TPM_ALG_ID hashing, TPM2B *seed, + const char *label, TPM2B *extra, uint32_t *counter_in) +{ + const char *label_p = "RSA p!"; + const char *label_q = "RSA q!"; + /* Numbers from NIST SP800-57. + * Fallback conservatively for keys larger than 2048 bits. */ + const uint32_t security_strength = + num_bits <= 1024 ? 80 : + num_bits <= 2048 ? 112 : + 256; + + const uint16_t num_bytes = num_bits / 8; + uint8_t q_buf[RSA_MAX_BYTES / 2]; + + struct BIGNUM e; + struct BIGNUM p; + struct BIGNUM q; + struct BIGNUM N; + + uint32_t counter; + + if (num_bits & 0xF) + return CRYPT_FAIL; + if (num_bytes / 2 > p_buf->size) + return CRYPT_FAIL; + if (N_buf->size > 0 && num_bytes > N_buf->size) + return CRYPT_FAIL; + if (num_bytes > RSA_MAX_BYTES) + return CRYPT_FAIL; + /* Seed size must be at least 2*security_strength per TPM 2.0 spec. */ + if (seed == NULL || seed->size * 8 < 2 * security_strength) + return CRYPT_FAIL; + + if (e_buf == 0) + e_buf = RSA_F4; + + N_buf->size = num_bytes; + DCRYPTO_bn_wrap(&e, &e_buf, sizeof(e_buf)); + DCRYPTO_bn_wrap(&p, p_buf->buffer, num_bytes / 2); + DCRYPTO_bn_wrap(&q, q_buf, num_bytes / 2); + + if (label == NULL) + label = label_p; + if (counter_in != NULL) + counter = *counter_in; + else + counter = 1; + if (!generate_prime(&p, hashing, seed, label, extra, &counter)) { + if (counter_in != NULL) + *counter_in = counter; + return CRYPT_FAIL; + } + + if (label == label_p) + label = label_q; + if (!generate_prime(&q, hashing, seed, label, extra, &counter)) { + if (counter_in != NULL) + *counter_in = counter; + return CRYPT_FAIL; + } + + if (counter_in != NULL) + *counter_in = counter; + N_buf->size = num_bytes; + p_buf->size = num_bytes / 2; + DCRYPTO_bn_wrap(&N, N_buf->buffer, num_bytes); + DCRYPTO_bn_mul(&N, &p, &q); + reverse_tpm2b(N_buf); + reverse_tpm2b(p_buf); + /* TODO(ngm): replace with secure memset. */ + memset(q_buf, 0, sizeof(q_buf)); + return CRYPT_SUCCESS; +} + #ifdef CRYPTO_TEST_SETUP #include "extension.h" @@ -261,7 +368,8 @@ enum { TEST_RSA_SIGN = 2, TEST_RSA_VERIFY = 3, TEST_RSA_KEYGEN = 4, - TEST_RSA_KEYTEST = 5 + TEST_RSA_KEYTEST = 5, + TEST_BN_PRIMEGEN = 6, }; static const TPM2B_PUBLIC_KEY_RSA RSA_768_N = { @@ -454,6 +562,11 @@ static const RSA_KEY RSA_2048 = { #define MAX_MSG_BYTES RSA_MAX_BYTES +/* 128-byte buffer to hold entropy for generating a + * 2048-bit RSA key (assuming ~112 bits of security strength, + * the TPM spec requires a seed of minimum size 28-bytes). */ +TPM2B_BYTE_VALUE(128); + static void rsa_command_handler(void *cmd_body, size_t cmd_size, size_t *response_size_out) @@ -477,6 +590,10 @@ static void rsa_command_handler(void *cmd_body, TPM2B_PUBLIC_KEY_RSA rsa_d; TPM2B_PUBLIC_KEY_RSA rsa_n; + TPM2B_128_BYTE_VALUE seed; + uint8_t bn_buf[RSA_MAX_BYTES]; + struct BIGNUM bn; + assert(sizeof(size_t) == sizeof(uint32_t)); /* Command format. @@ -577,9 +694,6 @@ static void rsa_command_handler(void *cmd_body, *response_size = 1; } return; - case TEST_RSA_KEYGEN: - *response_size = 0; - break; case TEST_RSA_KEYTEST: if (_cpri__TestKeyRSA(&rsa_d.b, 65537, &rsa_n.b, &p.b, &q.b) != CRYPT_SUCCESS) { @@ -607,6 +721,34 @@ static void rsa_command_handler(void *cmd_body, *out = 1; *response_size = 1; return; + case TEST_RSA_KEYGEN: + N.b.size = sizeof(N.t.buffer); + p.b.size = sizeof(p.t.buffer); + seed.b.size = sizeof(seed.t.buffer); + rand_bytes(seed.b.buffer, seed.b.size); + if (_cpri__GenerateKeyRSA( + &N.b, &p.b, key_len, RSA_F4, TPM_ALG_SHA256, + &seed.b, NULL, NULL, NULL) != CRYPT_SUCCESS) { + *response_size = 0; + } else { + memcpy(out, N.b.buffer, N.b.size); + memcpy(out + N.b.size, p.b.buffer, p.b.size); + *response_size = N.b.size + p.b.size; + } + return; + case TEST_BN_PRIMEGEN: + if (in_len > sizeof(bn_buf)) { + *response_size = 0; + return; + } + DCRYPTO_bn_wrap(&bn, bn_buf, in_len); + memcpy(bn_buf, in, in_len); + if (DCRYPTO_bn_generate_prime(&bn)) { + memcpy(out, bn.d, bn_size(&bn)); + *response_size = bn_size(&bn); + } else { + *response_size = 0; + } } } |