diff options
author | nagendra modadugu <ngm@google.com> | 2015-12-08 21:07:54 -0800 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2016-01-11 22:03:13 -0800 |
commit | 7a254e9851e37a5473efb8e61059d47b52bcee76 (patch) | |
tree | f37e151d293b26962f79234b6103d5245e4f4d0b | |
parent | 4368dcfb32942740dd11188de6a8658cdd448a5a (diff) | |
download | chrome-ec-7a254e9851e37a5473efb8e61059d47b52bcee76.tar.gz |
Initial RSA implementation.stabilize-7821.B
Includes support for encrypt / decrypt,
and sign / verify; padding schemes OAEP /
PKCS1; supporting bignum library.
RSA key sizes must be a multiple of 32-bits
(with the top bit set). Keying material,
input and output buffers are required to be
word-aligned.
BRANCH=none
TEST=added encrypt/decrypt sign/verify tests, compatibility with openssl tested
BUG=chrome-os-partner:43025,chrome-os-partner:47524
Change-Id: I6bc324c651e3178bb45bb75ab5935d9bc07efbce
Signed-off-by: nagendra modadugu <ngm@google.com>
Reviewed-on: https://chromium-review.googlesource.com/316942
Commit-Ready: Marius Schilder <mschilder@chromium.org>
Tested-by: Nagendra Modadugu <ngm@google.com>
Reviewed-by: Marius Schilder <mschilder@chromium.org>
-rw-r--r-- | board/cr50/build.mk | 1 | ||||
-rw-r--r-- | board/cr50/tpm2/rsa.c | 424 | ||||
-rw-r--r-- | board/cr50/tpm2/stubs.c | 59 | ||||
-rw-r--r-- | chip/g/build.mk | 2 | ||||
-rw-r--r-- | chip/g/dcrypto/bn.c | 247 | ||||
-rw-r--r-- | chip/g/dcrypto/dcrypto.h | 47 | ||||
-rw-r--r-- | chip/g/dcrypto/internal.h | 26 | ||||
-rw-r--r-- | chip/g/dcrypto/rsa.c | 506 | ||||
-rw-r--r-- | include/extension.h | 1 | ||||
-rw-r--r-- | test/tpm_test/rsa_test.py | 169 | ||||
-rw-r--r-- | test/tpm_test/subcmd.py | 1 | ||||
-rwxr-xr-x | test/tpm_test/tpmtest.py | 9 |
12 files changed, 1431 insertions, 61 deletions
diff --git a/board/cr50/build.mk b/board/cr50/build.mk index 2e42b7b920..c2efe7e907 100644 --- a/board/cr50/build.mk +++ b/board/cr50/build.mk @@ -34,6 +34,7 @@ board-y += tpm2/aes.o board-y += tpm2/hash.o board-y += tpm2/hash_data.o board-y += tpm2/platform.o +board-y += tpm2/rsa.o board-y += tpm2/stubs.o # Build and link with an external library diff --git a/board/cr50/tpm2/rsa.c b/board/cr50/tpm2/rsa.c new file mode 100644 index 0000000000..dfc996bca4 --- /dev/null +++ b/board/cr50/tpm2/rsa.c @@ -0,0 +1,424 @@ +/* Copyright 2015 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. + */ + +#include "CryptoEngine.h" + +#include "dcrypto.h" + +#include <assert.h> + +static int check_key(const RSA_KEY *key) +{ + if (key->publicKey->size & 0x3) + /* Only word-multiple sizes supported. */ + return 0; + return 1; +} + +static int check_encrypt_params(TPM_ALG_ID padding_alg, TPM_ALG_ID hash_alg, + enum padding_mode *padding, + enum hashing_mode *hashing) +{ + if (padding_alg == TPM_ALG_RSAES) { + *padding = PADDING_MODE_PKCS1; + } else if (padding_alg == TPM_ALG_OAEP) { + /* Only SHA1 and SHA256 supported with OAEP. */ + if (hash_alg == TPM_ALG_SHA1) + *hashing = HASH_SHA1; + else if (hash_alg == TPM_ALG_SHA256) + *hashing = HASH_SHA256; + else + /* Unsupported hash algorithm. */ + return 0; + *padding = PADDING_MODE_OAEP; + } else { + return 0; /* NULL padding unsupported. */ + } + return 1; +} + +static int check_sign_params(TPM_ALG_ID padding_alg, TPM_ALG_ID hash_alg, + enum padding_mode *padding, + enum hashing_mode *hashing) +{ + /* TODO: add support for PSS. */ + if (padding_alg == TPM_ALG_RSASSA) { + if (hash_alg == TPM_ALG_SHA1) + *hashing = HASH_SHA1; + else if (hash_alg == TPM_ALG_SHA256) + *hashing = HASH_SHA256; + else + return 0; + *padding = PADDING_MODE_PKCS1; + } else { + return 0; + } + return 1; +} + +CRYPT_RESULT _cpri__EncryptRSA(uint32_t *out_len, uint8_t *out, + RSA_KEY *key, TPM_ALG_ID padding_alg, + uint32_t in_len, uint8_t *in, + TPM_ALG_ID hash_alg, const char *label) +{ + struct RSA rsa; + enum padding_mode padding; + enum hashing_mode hashing; + + if (!check_key(key)) + return CRYPT_FAIL; + if (!check_encrypt_params(padding_alg, hash_alg, &padding, &hashing)) + return CRYPT_FAIL; + + rsa.e = key->exponent; + rsa.N.dmax = key->publicKey->size / sizeof(uint32_t); + rsa.N.d = (uint32_t *) &key->publicKey->buffer; + rsa.d.dmax = 0; + rsa.d.d = NULL; + + if (DCRYPTO_rsa_encrypt(&rsa, out, out_len, in, in_len, padding, + hashing, label)) + return CRYPT_SUCCESS; + else + return CRYPT_FAIL; +} + +CRYPT_RESULT _cpri__DecryptRSA(uint32_t *out_len, uint8_t *out, + RSA_KEY *key, TPM_ALG_ID padding_alg, + uint32_t in_len, uint8_t *in, + TPM_ALG_ID hash_alg, const char *label) +{ + struct RSA rsa; + enum padding_mode padding; + enum hashing_mode hashing; + + if (!check_key(key)) + return CRYPT_FAIL; + if (!check_encrypt_params(padding_alg, hash_alg, &padding, &hashing)) + return CRYPT_FAIL; + + rsa.e = key->exponent; + rsa.N.dmax = key->publicKey->size / sizeof(uint32_t); + rsa.N.d = (uint32_t *) &key->publicKey->buffer; + rsa.d.dmax = key->privateKey->size / sizeof(uint32_t); + rsa.d.d = (uint32_t *) &key->privateKey->buffer; + + if (DCRYPTO_rsa_decrypt(&rsa, out, out_len, in, in_len, padding, + hashing, label)) + return CRYPT_SUCCESS; + else + return CRYPT_FAIL; +} + +CRYPT_RESULT _cpri__SignRSA(uint32_t *out_len, uint8_t *out, + RSA_KEY *key, TPM_ALG_ID padding_alg, + TPM_ALG_ID hash_alg, uint32_t in_len, uint8_t *in) +{ + struct RSA rsa; + enum padding_mode padding; + enum hashing_mode hashing; + + if (!check_key(key)) + return CRYPT_FAIL; + if (!check_sign_params(padding_alg, hash_alg, &padding, &hashing)) + return CRYPT_FAIL; + + rsa.e = key->exponent; + rsa.N.dmax = key->publicKey->size / sizeof(uint32_t); + rsa.N.d = (uint32_t *) &key->publicKey->buffer; + rsa.d.dmax = key->privateKey->size / sizeof(uint32_t); + rsa.d.d = (uint32_t *) &key->privateKey->buffer; + + if (DCRYPTO_rsa_sign(&rsa, out, out_len, in, in_len, padding, hashing)) + return CRYPT_SUCCESS; + else + return CRYPT_FAIL; +} + +CRYPT_RESULT _cpri__ValidateSignatureRSA( + RSA_KEY *key, TPM_ALG_ID padding_alg, TPM_ALG_ID hash_alg, + uint32_t digest_len, uint8_t *digest, uint32_t sig_len, + uint8_t *sig, uint16_t salt_len) +{ + struct RSA rsa; + enum padding_mode padding; + enum hashing_mode hashing; + + if (!check_key(key)) + return CRYPT_FAIL; + if (!check_sign_params(padding_alg, hash_alg, &padding, &hashing)) + return CRYPT_FAIL; + + rsa.e = key->exponent; + rsa.N.dmax = key->publicKey->size / sizeof(uint32_t); + rsa.N.d = (uint32_t *) &key->publicKey->buffer; + rsa.d.dmax = 0; + rsa.d.d = NULL; + + if (DCRYPTO_rsa_verify(&rsa, digest, digest_len, sig, sig_len, + padding, hashing)) + return CRYPT_SUCCESS; + else + return CRYPT_FAIL; +} + +#ifdef CRYPTO_TEST_SETUP + +#include "extension.h" + +enum { + TEST_RSA_ENCRYPT = 0, + TEST_RSA_DECRYPT = 1, + TEST_RSA_SIGN = 2, + TEST_RSA_VERIFY = 3, + TEST_RSA_KEYGEN = 4 +}; + +struct TPM2B_aligned { + uint16_t pad; + TPM2B_PUBLIC_KEY_RSA bn; +} __packed __aligned(4); + +static const struct TPM2B_aligned RSA_768_N = { + .bn = { + .t = {96, { + 0x69, 0x85, 0x39, 0x2d, 0x78, 0x2b, 0x90, 0x75, + 0xe1, 0x7c, 0xc1, 0x7b, 0xbd, 0x5b, 0xdd, 0xfd, + 0x00, 0x36, 0xf7, 0x38, 0x74, 0x33, 0x2b, 0xa8, + 0x53, 0x89, 0x10, 0xa7, 0x2d, 0x3c, 0xe6, 0x00, + 0xa3, 0xe5, 0x8b, 0x5f, 0xed, 0x77, 0x32, 0xc0, + 0x0f, 0xe2, 0x2c, 0x51, 0x1b, 0x46, 0xba, 0x18, + 0xc0, 0x4e, 0x1b, 0x44, 0xdf, 0x94, 0xcc, 0x15, + 0xe1, 0x67, 0x48, 0x3a, 0x12, 0xc4, 0x0c, 0x82, + 0xd2, 0xfa, 0xfe, 0x74, 0x6e, 0x49, 0xa4, 0x8b, + 0x64, 0xc2, 0x3b, 0x33, 0x36, 0x72, 0x24, 0xdb, + 0x17, 0x86, 0x5a, 0x35, 0xd2, 0x23, 0x20, 0xd4, + 0x7c, 0xf0, 0x32, 0xd9, 0x46, 0xed, 0xdb, 0xb0 + } + } + } +}; + +static const struct TPM2B_aligned RSA_768_D = { + .bn = { + .t = {96, { + 0x01, 0x40, 0x76, 0x7b, 0x41, 0xd6, 0xd9, 0x17, + 0xfe, 0x52, 0x6d, 0xdd, 0x24, 0x70, 0xbc, 0x97, + 0x7e, 0xcf, 0x54, 0x22, 0x4c, 0x71, 0x29, 0xf5, + 0xb2, 0xe2, 0xf6, 0xf8, 0x8b, 0x9e, 0x20, 0x1a, + 0x1e, 0x67, 0xee, 0x59, 0xf9, 0x83, 0x6b, 0x91, + 0x8d, 0xdf, 0x03, 0xfc, 0xdd, 0x0f, 0x35, 0xd7, + 0xa2, 0x5d, 0x06, 0x3f, 0x45, 0xb9, 0xb0, 0x23, + 0x90, 0x7b, 0x11, 0x32, 0xc1, 0xf2, 0x12, 0xdb, + 0x61, 0xf9, 0xa7, 0x31, 0x24, 0xc8, 0x66, 0x4e, + 0x49, 0x72, 0xb9, 0xce, 0xa6, 0x5b, 0xab, 0x46, + 0x45, 0xdf, 0x75, 0x76, 0x3e, 0xd3, 0x42, 0x9f, + 0x5c, 0x1b, 0x8c, 0x25, 0x50, 0xb9, 0xad, 0xae + } + } + } +}; + +static const struct TPM2B_aligned RSA_2048_N = { + .bn = { + .t = {256, { + 0x99, 0xa9, 0x93, 0xdf, 0xe8, 0xde, 0x41, 0x07, + 0xe9, 0xb1, 0x4f, 0x53, 0xa6, 0x11, 0xe3, 0x67, + 0x88, 0xc5, 0x9a, 0x57, 0xa5, 0x38, 0x1f, 0x69, + 0x51, 0xf2, 0xa7, 0xb5, 0x6a, 0xd2, 0x1a, 0xf2, + 0x0c, 0x62, 0xad, 0x33, 0x1f, 0x82, 0x21, 0x4a, + 0x72, 0xb3, 0x6e, 0xba, 0xfd, 0x66, 0x3e, 0xef, + 0x40, 0x78, 0xa7, 0x37, 0x97, 0x4a, 0x74, 0x63, + 0x23, 0x05, 0x2e, 0x55, 0x6d, 0x36, 0xd0, 0xb7, + 0x8c, 0xb7, 0x83, 0x60, 0x3b, 0xa1, 0x58, 0x5d, + 0xdc, 0xef, 0xf7, 0x2c, 0x5e, 0x05, 0x27, 0xbc, + 0xb0, 0x4d, 0xc9, 0xff, 0x04, 0x50, 0x22, 0x97, + 0xe7, 0x15, 0x66, 0xa5, 0x24, 0x0e, 0x86, 0xa6, + 0x36, 0x9c, 0x92, 0xa2, 0x16, 0x51, 0xed, 0xc2, + 0xea, 0xbf, 0xf4, 0xb2, 0x5e, 0x3a, 0xd7, 0xc5, + 0xa3, 0xfa, 0xf0, 0xcf, 0xcf, 0x7b, 0xc8, 0x5c, + 0x07, 0xe2, 0xcc, 0xa8, 0xb8, 0x36, 0x76, 0xb1, + 0xc9, 0xbb, 0x48, 0x38, 0xbe, 0x0b, 0x57, 0xce, + 0x05, 0x2d, 0xf1, 0xdb, 0x7b, 0x94, 0xb6, 0xcd, + 0x3a, 0xa8, 0x50, 0x49, 0xca, 0x18, 0xb3, 0x52, + 0x18, 0x49, 0xde, 0x10, 0xf8, 0x41, 0x40, 0x6e, + 0x51, 0xaf, 0xdd, 0x06, 0xc3, 0x30, 0xc7, 0x57, + 0x6b, 0xd4, 0xdc, 0x10, 0x46, 0x30, 0x04, 0x23, + 0x98, 0xc0, 0xf0, 0xb4, 0xeb, 0x5d, 0xc9, 0x6e, + 0x50, 0x1f, 0xd7, 0xd9, 0xac, 0xf2, 0x0d, 0x06, + 0xe3, 0x9b, 0x5e, 0xde, 0x2a, 0xaa, 0xb1, 0xaf, + 0xd6, 0x97, 0x68, 0x2d, 0xeb, 0x0c, 0x7b, 0x75, + 0x49, 0x23, 0x64, 0xbe, 0x90, 0x53, 0x82, 0x99, + 0xa2, 0x50, 0x78, 0x0c, 0x9f, 0x72, 0xc1, 0x0a, + 0x0f, 0x32, 0x75, 0xed, 0x1f, 0x6e, 0xef, 0x2c, + 0x2e, 0x1d, 0x4c, 0x19, 0x85, 0x5c, 0x90, 0x95, + 0xe3, 0x4b, 0x86, 0xf5, 0xb7, 0x9f, 0x73, 0xcd, + 0xbe, 0x15, 0x8e, 0x43, 0x2e, 0x61, 0xd7, 0x9c + } + } + } +}; + +static const struct TPM2B_aligned RSA_2048_D = { + .bn = { + .t = {256, { + 0xf5, 0x95, 0x99, 0xca, 0x31, 0x84, 0x66, 0x4c, + 0xa9, 0x29, 0x24, 0x74, 0x22, 0x29, 0xb4, 0x64, + 0x5b, 0x22, 0xeb, 0x5d, 0x2f, 0xe3, 0x62, 0x21, + 0x02, 0x16, 0x33, 0x16, 0xe4, 0xad, 0x10, 0x52, + 0x3f, 0xf0, 0xf1, 0x86, 0x68, 0x54, 0x47, 0x24, + 0xcc, 0x5c, 0x08, 0x82, 0x0f, 0x68, 0xdd, 0x79, + 0x55, 0x11, 0x07, 0x6d, 0x56, 0x89, 0x30, 0xf1, + 0x7f, 0xaf, 0xb1, 0xb8, 0x41, 0xe8, 0x7a, 0x82, + 0x03, 0x1a, 0x95, 0xd7, 0x00, 0x7c, 0xb7, 0x04, + 0xee, 0x8e, 0x9b, 0xbc, 0x4f, 0xdf, 0xa8, 0x38, + 0xea, 0xbf, 0xfb, 0x79, 0xa0, 0xd3, 0xd6, 0xc2, + 0x1f, 0x67, 0xa2, 0x88, 0x2b, 0x1d, 0x23, 0xc6, + 0x19, 0xfc, 0x27, 0x45, 0xcf, 0xbd, 0xc7, 0xe9, + 0x6e, 0x7a, 0xe2, 0x84, 0x4c, 0x9c, 0x16, 0x65, + 0xb0, 0xa6, 0x88, 0xc5, 0xbe, 0x30, 0x70, 0xb9, + 0xc6, 0x6d, 0x3f, 0xf5, 0xcd, 0x52, 0x97, 0x54, + 0x15, 0x26, 0xd2, 0x06, 0x82, 0xcc, 0xe7, 0x02, + 0x1a, 0x23, 0xb8, 0x0a, 0x71, 0xde, 0x91, 0x82, + 0xe4, 0x1e, 0xbe, 0x67, 0xeb, 0x94, 0x24, 0x22, + 0xe7, 0x27, 0xfa, 0x52, 0xf2, 0x94, 0x5e, 0x6e, + 0x85, 0xc1, 0x47, 0x42, 0xdc, 0xae, 0x8b, 0xaf, + 0x4e, 0x32, 0xc6, 0x8d, 0xd3, 0xc0, 0xa2, 0x6b, + 0x02, 0x96, 0x76, 0x0a, 0x96, 0x87, 0x16, 0x35, + 0xc1, 0xea, 0xf7, 0x91, 0xa4, 0xa3, 0x1b, 0x40, + 0xc0, 0x95, 0x20, 0x14, 0x9f, 0x32, 0xad, 0x39, + 0x19, 0x29, 0xea, 0x80, 0x33, 0x2c, 0x31, 0x86, + 0xca, 0x5e, 0x89, 0xf0, 0x74, 0xdf, 0x8f, 0xdc, + 0xa3, 0xf3, 0xbe, 0x26, 0xd0, 0xa3, 0xb4, 0x7c, + 0x6e, 0xdf, 0xad, 0xdb, 0x26, 0xf3, 0xaa, 0xfb, + 0x68, 0x56, 0x43, 0xb9, 0x7f, 0x19, 0x70, 0x67, + 0x5a, 0x66, 0x15, 0x6f, 0xe2, 0x14, 0x8f, 0xbc, + 0x89, 0x8b, 0x4a, 0xdf, 0x1f, 0x02, 0x9d, 0x4e + } + } + } +}; + +static const RSA_KEY RSA_768 = { + 65537, (TPM2B *) &RSA_768_N.bn.b, (TPM2B *) &RSA_768_D.bn.b +}; +static const RSA_KEY RSA_2048 = { + 65537, (TPM2B *) &RSA_2048_N.bn.b, (TPM2B *) &RSA_2048_D.bn.b +}; + +#define MAX_MSG_BYTES RSA_MAX_BYTES + +static void rsa_command_handler(void *cmd_body, + size_t cmd_size, + size_t *response_size_out) +{ + uint8_t *cmd; + uint8_t op; + uint8_t padding_alg; + uint8_t hashing_alg; + uint16_t key_len; + uint16_t in_len; + uint8_t in[MAX_MSG_BYTES]; + uint16_t digest_len; + uint8_t digest[SHA_DIGEST_MAX_BYTES]; + uint8_t *out = (uint8_t *) cmd_body; + RSA_KEY *key; + uint32_t *response_size = (uint32_t *) response_size_out; + + assert(sizeof(size_t) == sizeof(uint32_t)); + + /* Command format. + * + * OFFSET FIELD + * 0 OP + * 1 PADDING + * 2 HASHING + * 3 MSB KEY LEN + * 4 LSB KEY LEN + * 5 MSB IN LEN + * 6 LSB IN LEN + * 7 IN + * 7 + IN_LEN MSB DIGEST LEN + * 8 + IN_LEN LSB DIGEST LEN + * 9 + IN_LEN DIGEST + */ + cmd = (uint8_t *) cmd_body; + op = *cmd++; + padding_alg = *cmd++; + hashing_alg = *cmd++; + key_len = ((uint16_t) (cmd[0] << 8)) | cmd[1]; + cmd += 2; + in_len = ((uint16_t) (cmd[0] << 8)) | cmd[1]; + cmd += 2; + if (in_len > sizeof(in)) { + *response_size = 0; + return; + } + memcpy(in, cmd, in_len); + if (op == TEST_RSA_VERIFY) { + cmd += in_len; + digest_len = ((uint16_t) (cmd[0] << 8)) | cmd[1]; + cmd += 2; + if (digest_len > sizeof(digest)) { + *response_size = 0; + return; + } + memcpy(digest, cmd, digest_len); + } + + switch (key_len) { + case 768: + key = (RSA_KEY *) &RSA_768; + break; + case 2048: + key = (RSA_KEY *) &RSA_2048; + break; + default: + *response_size = 0; + return; + } + + switch (op) { + case TEST_RSA_ENCRYPT: + if (_cpri__EncryptRSA( + response_size, out, key, + padding_alg, in_len, in, hashing_alg, "") + != CRYPT_SUCCESS) + *response_size = 0; + return; + case TEST_RSA_DECRYPT: + if (_cpri__DecryptRSA( + response_size, out, key, + padding_alg, in_len, in, hashing_alg, "") + != CRYPT_SUCCESS) + *response_size = 0; + return; + case TEST_RSA_SIGN: + if (_cpri__SignRSA( + response_size, out, key, + padding_alg, hashing_alg, in_len, in) + != CRYPT_SUCCESS) + *response_size = 0; + return; + case TEST_RSA_VERIFY: + if (_cpri__ValidateSignatureRSA( + key, padding_alg, hashing_alg, digest_len, + digest, in_len, in, 0) + != CRYPT_SUCCESS) { + *response_size = 0; + } else { + *out = 1; + *response_size = 1; + } + return; + case TEST_RSA_KEYGEN: + *response_size = 0; + break; + } +} + +DECLARE_EXTENSION_COMMAND(EXTENSION_RSA, rsa_command_handler); + +#endif /* CRYPTO_TEST_SETUP */ diff --git a/board/cr50/tpm2/stubs.c b/board/cr50/tpm2/stubs.c index 7d78d5cef4..08ea641d61 100644 --- a/board/cr50/tpm2/stubs.c +++ b/board/cr50/tpm2/stubs.c @@ -33,21 +33,6 @@ UINT16 _cpri__CompleteHMAC( return -1; } -CRYPT_RESULT _cpri__DecryptRSA( - UINT32 * dOutSize, // OUT: the size of the decrypted data - BYTE * dOut, // OUT: the decrypted data - RSA_KEY * key, // IN: the key to use for decryption - TPM_ALG_ID padType, // IN: the type of padding - UINT32 cInSize, // IN: the amount of data to decrypt - BYTE * cIn, // IN: the data to decrypt - TPM_ALG_ID hashAlg, // IN: in case this is needed for the scheme - const char *label // IN: in case it is needed for the scheme - ) -{ - ecprintf("%s called\n", __func__); - return CRYPT_FAIL; -} - CRYPT_RESULT _cpri__DrbgGetPutState( GET_PUT direction, int bufferSize, @@ -109,21 +94,6 @@ CRYPT_RESULT _cpri__EccPointMultiply( return CRYPT_FAIL; } -CRYPT_RESULT _cpri__EncryptRSA( - UINT32 * cOutSize, // OUT: the size of the encrypted data - BYTE * cOut, // OUT: the encrypted data - RSA_KEY * key, // IN: the key to use for encryption - TPM_ALG_ID padType, // IN: the type of padding - UINT32 dInSize, // IN: the amount of data to encrypt - BYTE * dIn, // IN: the data to encrypt - TPM_ALG_ID hashAlg, // IN: in case this is needed - const char *label // IN: in case it is needed - ) -{ - ecprintf("%s called\n", __func__); - return CRYPT_FAIL; -} - CRYPT_RESULT _cpri__GenerateKeyEcc( TPMS_ECC_POINT * Qout, // OUT: the public point TPM2B_ECC_PARAMETER * dOut, // OUT: the private scalar @@ -246,20 +216,6 @@ CRYPT_RESULT _cpri__SignEcc( return CRYPT_FAIL; } -CRYPT_RESULT _cpri__SignRSA( - UINT32 * sigOutSize, // OUT: size of signature - BYTE * sigOut, // OUT: signature - RSA_KEY * key, // IN: key to use - TPM_ALG_ID scheme, // IN: the scheme to use - TPM_ALG_ID hashAlg, // IN: hash algorithm for PKSC1v1_5 - UINT32 hInSize, // IN: size of digest to be signed - BYTE * hIn // IN: digest buffer - ) -{ - ecprintf("%s called\n", __func__); - return CRYPT_FAIL; -} - UINT16 _cpri__StartHMAC( TPM_ALG_ID hashAlg, // IN: the algorithm to use BOOL sequence, // IN: indicates if the state should be saved @@ -329,21 +285,6 @@ CRYPT_RESULT _cpri__ValidateSignatureEcc( return CRYPT_FAIL; } -CRYPT_RESULT _cpri__ValidateSignatureRSA( - RSA_KEY * key, // IN: key to use - TPM_ALG_ID scheme, // IN: the scheme to use - TPM_ALG_ID hashAlg, // IN: hash algorithm - UINT32 hInSize, // IN: size of digest to be checked - BYTE * hIn, // IN: digest buffer - UINT32 sigInSize, // IN: size of signature - BYTE * sigIn, // IN: signature - UINT16 saltSize // IN: salt size for PSS - ) -{ - ecprintf("%s called\n", __func__); - return CRYPT_FAIL; -} - int _math__Comp( const UINT32 aSize, // IN: size of a const BYTE * a, // IN: a buffer diff --git a/chip/g/build.mk b/chip/g/build.mk index 33436921d8..3564865f6b 100644 --- a/chip/g/build.mk +++ b/chip/g/build.mk @@ -26,6 +26,8 @@ chip-y += uart.o endif chip-$(CONFIG_DCRYPTO)+= dcrypto/aes.o +chip-$(CONFIG_DCRYPTO)+= dcrypto/bn.o +chip-$(CONFIG_DCRYPTO)+= dcrypto/rsa.o chip-$(CONFIG_DCRYPTO)+= dcrypto/sha1.o chip-$(CONFIG_DCRYPTO)+= dcrypto/sha256.o diff --git a/chip/g/dcrypto/bn.c b/chip/g/dcrypto/bn.c new file mode 100644 index 0000000000..829d10e602 --- /dev/null +++ b/chip/g/dcrypto/bn.c @@ -0,0 +1,247 @@ +/* Copyright 2015 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. + */ + +#include "dcrypto.h" +#include "internal.h" + +#include <assert.h> + +#ifdef CONFIG_WATCHDOG +extern void watchdog_reload(void); +#else +static inline void watchdog_reload(void) { } +#endif + +void bn_init(struct BIGNUM *b, void *buf, size_t len) +{ + /* Only word-multiple sized buffers accepted. */ + assert((len & 0x3) == 0); + /* Only word-aligned buffers accepted. */ + assert(((uintptr_t) buf & 0x3) == 0); + b->dmax = len / BN_BYTES; + dcrypto_memset(buf, 0x00, len); + b->d = (uint32_t *) buf; +} + +int bn_check_topbit(const struct BIGNUM *N) +{ + return N->d[N->dmax - 1] >> 31; +} + +/* a[n]. */ +static int bn_is_bit_set(const struct BIGNUM *a, int n) +{ + int i, j; + + if (n < 0) + return 0; + + i = n / BN_BITS2; + j = n % BN_BITS2; + if (a->dmax <= i) + return 0; + + return (a->d[i] >> j) & 1; +} + +/* a[] >= b[]. */ +/* TODO(ngm): constant time. */ +static int bn_gte(const struct BIGNUM *a, const struct BIGNUM *b) +{ + int i; + + for (i = a->dmax - 1; a->d[i] == b->d[i] && i > 0; --i) + ; + return a->d[i] >= b->d[i]; +} + +/* c[] = c[] - a[], assumes c > a. */ +static uint32_t bn_sub(struct BIGNUM *c, const struct BIGNUM *a) +{ + int64_t A = 0; + int i; + + for (i = 0; i < a->dmax; i++) { + A += (uint64_t) c->d[i] - a->d[i]; + c->d[i] = (uint32_t) A; + A >>= 32; + } + return (uint32_t) A; /* 0 or -1. */ +} + +/* c[] = c[] + a[]. */ +static uint32_t bn_add(struct BIGNUM *c, const struct BIGNUM *a) +{ + uint64_t A = 0; + int i; + + for (i = 0; i < a->dmax; ++i) { + A += (uint64_t) c->d[i] + a->d[i]; + c->d[i] = (uint32_t) A; + A >>= 32; + } + + return (uint32_t) A; /* 0 or 1. */ +} + +/* r[] <<= 1. */ +static uint32_t bn_lshift(struct BIGNUM *r) +{ + int i; + uint32_t w; + uint32_t carry = 0; + + for (i = 0; i < r->dmax; i++) { + w = (r->d[i] << 1) | carry; + carry = r->d[i] >> 31; + r->d[i] = w; + } + return carry; +} + +/* Montgomery c[] += a * b[] / R % N. */ +/* TODO(ngm): constant time. */ +static void bn_mont_mul_add(struct BIGNUM *c, const uint32_t a, + const struct BIGNUM *b, const uint32_t nprime, + const struct BIGNUM *N) +{ + uint32_t A, B, d0; + int i; + + { + register uint64_t tmp; + + tmp = c->d[0] + (uint64_t) a * b->d[0]; + A = tmp >> 32; + d0 = (uint32_t) tmp * (uint32_t) nprime; + tmp = (uint32_t)tmp + (uint64_t) d0 * N->d[0]; + B = tmp >> 32; + } + + for (i = 0; i < N->dmax - 1;) { + register uint64_t tmp; + + tmp = A + (uint64_t) a * b->d[i + 1] + c->d[i + 1]; + A = tmp >> 32; + tmp = B + (uint64_t) d0 * N->d[i + 1] + (uint32_t) tmp; + c->d[i] = (uint32_t) tmp; + B = tmp >> 32; + ++i; + } + + { + uint64_t tmp = (uint64_t) A + B; + + c->d[i] = (uint32_t) tmp; + A = tmp >> 32; /* 0 or 1. */ + if (A) + bn_sub(c, N); + } +} + +/* Montgomery c[] = a[] * b[] / R % N. */ +static void bn_mont_mul(struct BIGNUM *c, const struct BIGNUM *a, + const struct BIGNUM *b, const uint32_t nprime, + const struct BIGNUM *N) +{ + int i; + + for (i = 0; i < N->dmax; i++) + c->d[i] = 0; + + bn_mont_mul_add(c, a ? a->d[0] : 1, b, nprime, N); + for (i = 1; i < N->dmax; i++) + bn_mont_mul_add(c, a ? a->d[i] : 0, b, nprime, N); +} + +/* Mongomery R * R % N, R = 1 << (1 + log2N). */ +/* TODO(ngm): constant time. */ +static void bn_compute_RR(struct BIGNUM *RR, const struct BIGNUM *N) +{ + int i; + + bn_sub(RR, N); /* R - N = R % N since R < 2N */ + + /* Repeat 2 * R % N, log2(R) times. */ + for (i = 0; i < N->dmax * BN_BITS2; i++) { + if (bn_lshift(RR)) + assert(bn_sub(RR, N) == -1); + if (bn_gte(RR, N)) + bn_sub(RR, N); + } +} + +/* Montgomery nprime = -1 / n0 % (2 ^ 32). */ +static uint32_t bn_compute_nprime(const uint32_t n0) +{ + int i; + uint32_t ninv = 1; + + /* Repeated Hensel lifting. */ + for (i = 0; i < 5; i++) + ninv *= 2 - (n0 * ninv); + + return ~ninv + 1; /* Two's complement. */ +} + +/* Montgomery output = input ^ exp % N. */ +/* TODO(ngm): this implementation not timing or side-channel safe by + * any measure. */ +void bn_mont_modexp(struct BIGNUM *output, const struct BIGNUM *input, + const struct BIGNUM *exp, const struct BIGNUM *N) +{ + int i; + uint32_t nprime; + uint32_t RR_buf[RSA_MAX_WORDS]; + uint32_t acc_buf[RSA_MAX_WORDS]; + uint32_t aR_buf[RSA_MAX_WORDS]; + + struct BIGNUM RR; + struct BIGNUM acc; + struct BIGNUM aR; + + bn_init(&RR, RR_buf, bn_size(N)); + bn_init(&acc, acc_buf, bn_size(N)); + bn_init(&aR, aR_buf, bn_size(N)); + + nprime = bn_compute_nprime(N->d[0]); + bn_compute_RR(&RR, N); + bn_mont_mul(&acc, NULL, &RR, nprime, N); /* R = 1 * RR / R % N */ + bn_mont_mul(&aR, input, &RR, nprime, N); /* aR = a * RR / R % N */ + output->d[0] = 1; + + /* TODO(ngm): burn stack space and use windowing. */ + for (i = exp->dmax * BN_BITS2 - 1; i >= 0; i--) { + bn_mont_mul(output, &acc, &acc, nprime, N); + if (bn_is_bit_set(exp, i)) { + bn_mont_mul(&acc, output, &aR, nprime, N); + } else { + struct BIGNUM tmp = *output; + + *output = acc; + acc = tmp; + } + /* Poke the watchdog. + * TODO(ngm): may be unnecessary with + * a faster implementation. + */ + watchdog_reload(); + } + + bn_mont_mul(output, NULL, &acc, nprime, N); /* Convert out. */ + /* Copy to output buffer if necessary. */ + if (acc.d != acc_buf) { + memcpy(acc.d, acc_buf, bn_size(output)); + *output = acc; + } + + if (bn_sub(output, N)) + bn_add(output, N); /* Final reduce. */ + output->dmax = N->dmax; + + dcrypto_memset(RR_buf, 0, sizeof(RR_buf)); + dcrypto_memset(acc_buf, 0, sizeof(acc_buf)); + dcrypto_memset(aR_buf, 0, sizeof(aR_buf)); +} diff --git a/chip/g/dcrypto/dcrypto.h b/chip/g/dcrypto/dcrypto.h index a39350fd13..3d8e78ab96 100644 --- a/chip/g/dcrypto/dcrypto.h +++ b/chip/g/dcrypto/dcrypto.h @@ -31,6 +31,11 @@ struct HASH_CTX; /* Forward declaration. */ typedef struct HASH_CTX SHA1_CTX; typedef struct HASH_CTX SHA256_CTX; +enum hashing_mode { + HASH_SHA1 = 0, + HASH_SHA256 = 1 +}; + #define DCRYPTO_HASH_update(ctx, data, len) \ ((ctx)->vtab->update((ctx), (data), (len))) #define DCRYPTO_HASH_final(ctx) \ @@ -72,4 +77,46 @@ const uint8_t *DCRYPTO_SHA1_hash(const uint8_t *data, uint32_t n, const uint8_t *DCRYPTO_SHA256_hash(const uint8_t *data, uint32_t n, uint8_t *digest); +/* + * RSA. + */ + +/* Largest supported key size, 2048-bits. */ +#define RSA_MAX_BYTES 256 +#define RSA_MAX_WORDS (RSA_MAX_BYTES / sizeof(uint32_t)) + +struct RSA { + uint32_t e; + struct BIGNUM N; + struct BIGNUM d; +}; + +enum padding_mode { + PADDING_MODE_PKCS1 = 0, + PADDING_MODE_OAEP = 1 +}; + +/* Calculate r = m ^ e mod N */ +int DCRYPTO_rsa_encrypt(struct RSA *rsa, uint8_t *out, uint32_t *out_len, + const uint8_t *in, const uint32_t in_len, + enum padding_mode padding, enum hashing_mode hashing, + const char *label); + +/* Calculate r = m ^ d mod N */ +int DCRYPTO_rsa_decrypt(struct RSA *rsa, uint8_t *out, uint32_t *out_len, + const uint8_t *in, const uint32_t in_len, + enum padding_mode padding, enum hashing_mode hashing, + const char *label); + +/* Calculate r = m ^ d mod N */ +int DCRYPTO_rsa_sign(struct RSA *rsa, uint8_t *out, uint32_t *out_len, + const uint8_t *in, const uint32_t in_len, + enum padding_mode padding, enum hashing_mode hashing); + +/* Calculate r = m ^ e mod N */ +int DCRYPTO_rsa_verify(struct RSA *rsa, const uint8_t *digest, + uint32_t digest_len, const uint8_t *sig, + const uint32_t sig_len, enum padding_mode padding, + enum hashing_mode hashing); + #endif /* ! __EC_CHIP_G_DCRYPTO_DCRYPTO_H */ diff --git a/chip/g/dcrypto/internal.h b/chip/g/dcrypto/internal.h index 3be8a406a7..8757ab5e86 100644 --- a/chip/g/dcrypto/internal.h +++ b/chip/g/dcrypto/internal.h @@ -12,6 +12,9 @@ #include "sha1.h" #include "sha256.h" +/* + * SHA. + */ #define CTRL_CTR_BIG_ENDIAN (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) #define CTRL_ENABLE 1 #define CTRL_ENCRYPT 1 @@ -79,4 +82,27 @@ void dcrypto_sha_update(struct HASH_CTX *unused, const uint8_t *data, uint32_t n); void dcrypto_sha_wait(enum sha_mode mode, uint32_t *digest); +/* + * BIGNUM. + */ +#define BN_BITS2 32 +#define BN_BYTES 4 + +struct BIGNUM { + uint32_t dmax; /* Size of d, in 32-bit words. */ + uint32_t *d; /* Word array, little endian format ... */ +}; + +void bn_init(struct BIGNUM *bn, void *buf, size_t len); +#define bn_size(b) ((b)->dmax * BN_BYTES) +int bn_check_topbit(const struct BIGNUM *N); +void bn_mont_modexp(struct BIGNUM *output, const struct BIGNUM *input, + const struct BIGNUM *exp, const struct BIGNUM *N); + +/* + * Utility functions. + */ +/* TODO(ngm): memset that doesn't get optimized out. */ +#define dcrypto_memset(p, b, len) memset((p), (b), (len)) + #endif /* ! __EC_CHIP_G_DCRYPTO_INTERNAL_H */ diff --git a/chip/g/dcrypto/rsa.c b/chip/g/dcrypto/rsa.c new file mode 100644 index 0000000000..d9774a2101 --- /dev/null +++ b/chip/g/dcrypto/rsa.c @@ -0,0 +1,506 @@ +/* Copyright 2015 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. + */ + +#include "dcrypto.h" +#include "internal.h" + +#include "trng.h" + +#include <assert.h> + +static void MGF1_xor(uint8_t *dst, uint32_t dst_len, + const uint8_t *seed, uint32_t seed_len, + enum hashing_mode hashing) +{ + struct HASH_CTX ctx; + struct { + uint8_t b3; + uint8_t b2; + uint8_t b1; + uint8_t b0; + } cnt; + const uint8_t *digest; + const size_t hash_size = (hashing == HASH_SHA1) ? SHA1_DIGEST_BYTES + : SHA256_DIGEST_BYTES; + + cnt.b0 = cnt.b1 = cnt.b2 = cnt.b3 = 0; + while (dst_len) { + int i; + + if (hashing == HASH_SHA1) + DCRYPTO_SHA1_init(&ctx, 0); + else + DCRYPTO_SHA256_init(&ctx, 0); + + DCRYPTO_HASH_update(&ctx, seed, seed_len); + DCRYPTO_HASH_update(&ctx, (uint8_t *) &cnt, sizeof(cnt)); + digest = DCRYPTO_HASH_final(&ctx); + for (i = 0; i < dst_len && i < hash_size; ++i) + *dst++ ^= *digest++; + dst_len -= i; + if (!++cnt.b0) + ++cnt.b1; + } +} + +/* + * struct OAEP { // MSB to LSB. + * uint8_t zero; + * uint8_t seed[HASH_SIZE]; + * uint8_t phash[HASH_SIZE]; + * uint8_t PS[]; // Variable length (optional) zero-pad. + * uint8_t one; // 0x01, message demarcator. + * uint8_t msg[]; // Input message. + * }; + */ +/* encrypt */ +static int oaep_pad(uint8_t *output, uint32_t output_len, + const uint8_t *msg, uint32_t msg_len, + enum hashing_mode hashing, const char *label) +{ + int i; + const size_t hash_size = (hashing == HASH_SHA1) ? SHA1_DIGEST_BYTES + : SHA256_DIGEST_BYTES; + uint8_t *const seed = output + 1; + uint8_t *const phash = seed + hash_size; + uint8_t *const PS = phash + hash_size; + const uint32_t max_msg_len = output_len - 2 - 2 * hash_size; + const uint32_t ps_len = max_msg_len - msg_len; + uint8_t *const one = PS + ps_len; + struct HASH_CTX ctx; + + if (output_len < 2 + 2 * hash_size) + return 0; /* Key size too small for chosen hash. */ + if (msg_len > output_len - 2 - 2 * hash_size) + return 0; /* Input message too large for key size. */ + + dcrypto_memset(output, 0, output_len); + for (i = 0; i < hash_size;) { + uint32_t r = rand(); + + seed[i++] = r >> 0; + seed[i++] = r >> 8; + seed[i++] = r >> 16; + seed[i++] = r >> 24; + } + + if (hashing == HASH_SHA1) + DCRYPTO_SHA1_init(&ctx, 0); + else + DCRYPTO_SHA256_init(&ctx, 0); + + DCRYPTO_HASH_update(&ctx, label, label ? strlen(label) : 0); + memcpy(phash, DCRYPTO_HASH_final(&ctx), hash_size); + *one = 1; + memcpy(one + 1, msg, msg_len); + MGF1_xor(phash, hash_size + 1 + max_msg_len, + seed, hash_size, hashing); + MGF1_xor(seed, hash_size, phash, hash_size + 1 + max_msg_len, + hashing); + return 1; +} + +/* decrypt */ +/* TODO(ngm): constant time. */ +static int check_oaep_pad(uint8_t *out, uint32_t *out_len, + uint8_t *padded, uint32_t padded_len, + enum hashing_mode hashing, const char *label) +{ + const size_t hash_size = (hashing == HASH_SHA1) ? SHA1_DIGEST_BYTES + : SHA256_DIGEST_BYTES; + uint8_t *seed = padded + 1; + uint8_t *phash = seed + hash_size; + uint8_t *PS = phash + hash_size; + const uint32_t max_msg_len = padded_len - 2 - 2 * hash_size; + struct HASH_CTX ctx; + int one_index = -1; + int bad; + int i; + + if (padded_len < 2 + 2 * hash_size) + return 0; /* Invalid input size. */ + + /* Recover seed. */ + MGF1_xor(seed, hash_size, phash, hash_size + 1 + max_msg_len, hashing); + /* Recover db. */ + MGF1_xor(phash, hash_size + 1 + max_msg_len, seed, hash_size, hashing); + + if (hashing == HASH_SHA1) + DCRYPTO_SHA1_init(&ctx, 0); + else + DCRYPTO_SHA256_init(&ctx, 0); + DCRYPTO_HASH_update(&ctx, label, label ? strlen(label) : 0); + + bad = memcmp(phash, DCRYPTO_HASH_final(&ctx), hash_size); + bad |= padded[0]; + + for (i = PS - padded; i < padded_len; i++) { + if (padded[i] == 1) { + one_index = i; + break; + } else if (padded[i] != 0) { + bad = 1; + break; + } + } + + if (one_index < 0 || bad) + return 0; + one_index++; + if (*out_len < padded_len - one_index) + return 0; + memcpy(out, padded + one_index, padded_len - one_index); + *out_len = padded_len - one_index; + return 1; +} + +/* Constants from RFC 3447. */ +#define RSA_PKCS1_PADDING_SIZE 11 + +/* encrypt */ +static int pkcs1_type2_pad(uint8_t *padded, uint32_t padded_len, + const uint8_t *in, uint32_t in_len) +{ + uint32_t PS_len; + + if (padded_len < RSA_PKCS1_PADDING_SIZE) + return 0; + if (in_len > padded_len - RSA_PKCS1_PADDING_SIZE) + return 0; + PS_len = padded_len - 3 - in_len; + + *(padded++) = 0; + *(padded++) = 2; + while (PS_len) { + int i; + uint32_t r = rand(); + + for (i = 0; i < 4 && PS_len; i++) { + uint8_t b = ((uint8_t *) &r)[i]; + + if (b) { + *padded++ = b; + PS_len--; + } + } + } + *(padded++) = 0; + memcpy(padded, in, in_len); + return 1; +} + +/* decrypt */ +/* TODO(ngm): constant time */ +static int check_pkcs1_type2_pad(uint8_t *out, uint32_t *out_len, + const uint8_t *padded, uint32_t padded_len) +{ + int i; + + if (padded_len < RSA_PKCS1_PADDING_SIZE) + return 0; + if (padded[0] != 0 || padded[1] != 2) + return 0; + for (i = 2; i < padded_len; i++) { + if (padded[i] == 0) + break; + } + + if (i == padded_len) + return 0; + i++; + if (i < RSA_PKCS1_PADDING_SIZE) + return 0; + if (*out_len < padded_len - i) + return 0; + memcpy(out, &padded[i], padded_len - i); + *out_len = padded_len - i; + return 1; +} + +static const uint8_t SHA1_DER[] = { + 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, + 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14 +}; +static const uint8_t SHA256_DER[] = { + 0x30, 0x31, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, + 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, + 0x00, 0x04, 0x20 +}; + +/* sign */ +static int pkcs1_type1_pad(uint8_t *padded, uint32_t padded_len, + const uint8_t *in, uint32_t in_len, + enum hashing_mode hashing) +{ + const uint8_t *der = (hashing == HASH_SHA1) ? &SHA1_DER[0] + : &SHA256_DER[0]; + const uint32_t der_size = (hashing == HASH_SHA1) ? sizeof(SHA1_DER) + : sizeof(SHA256_DER); + const uint32_t hash_size = (hashing == HASH_SHA1) ? SHA1_DIGEST_BYTES + : SHA256_DIGEST_BYTES; + uint32_t ps_len; + + if (padded_len < RSA_PKCS1_PADDING_SIZE + der_size) + return 0; + if (in_len != hash_size) + return 0; + if (in_len > padded_len - RSA_PKCS1_PADDING_SIZE - der_size) + return 0; + ps_len = padded_len - 3 - der_size - in_len; + + *(padded++) = 0; + *(padded++) = 1; + dcrypto_memset(padded, 0xFF, ps_len); + padded += ps_len; + *(padded++) = 0; + memcpy(padded, der, der_size); + padded += der_size; + memcpy(padded, in, in_len); + return 1; +} + +/* verify */ +/* TODO(ngm): constant time */ +static int check_pkcs1_type1_pad(const uint8_t *msg, uint32_t msg_len, + const uint8_t *padded, uint32_t padded_len, + enum hashing_mode hashing) +{ + int i; + const uint8_t *der = (hashing == HASH_SHA1) ? &SHA1_DER[0] + : &SHA256_DER[0]; + const uint32_t der_size = (hashing == HASH_SHA1) ? sizeof(SHA1_DER) + : sizeof(SHA256_DER); + const uint32_t hash_size = (hashing == HASH_SHA1) ? SHA1_DIGEST_BYTES + : SHA256_DIGEST_BYTES; + uint32_t ps_len; + + if (msg_len != hash_size) + return 0; + if (padded_len < RSA_PKCS1_PADDING_SIZE + der_size + hash_size) + return 0; + ps_len = padded_len - 3 - der_size - hash_size; + + if (padded[0] != 0 || padded[1] != 1) + return 0; + for (i = 2; i < ps_len + 2; i++) { + if (padded[i] != 0xFF) + return 0; + } + + if (padded[i++] != 0) + return 0; + if (memcmp(&padded[i], der, der_size) != 0) + return 0; + i += der_size; + return memcmp(msg, &padded[i], hash_size) == 0; +} + +static void reverse(uint8_t *start, size_t len) +{ + int i; + uint8_t *end = start + len; + + for (i = 0; i < len / 2; ++i) { + uint8_t tmp = *start; + + *start++ = *--end; + *end = tmp; + } +} + +static int check_modulus_params(const struct BIGNUM *N, uint32_t *out_len) +{ + if (bn_size(N) > RSA_MAX_BYTES) + return 0; /* Unsupported key size. */ + if (!bn_check_topbit(N)) /* Check that top bit is set. */ + return 0; + if (out_len && *out_len < bn_size(N)) + return 0; /* Output buffer too small. */ + return 1; +} + +int DCRYPTO_rsa_encrypt(struct RSA *rsa, uint8_t *out, uint32_t *out_len, + const uint8_t *in, const uint32_t in_len, + enum padding_mode padding, enum hashing_mode hashing, + const char *label) +{ + uint32_t padded_buf[RSA_MAX_WORDS]; + uint32_t e_buf[BN_BYTES / sizeof(uint32_t)]; + + struct BIGNUM padded; + struct BIGNUM e; + struct BIGNUM encrypted; + + if (!check_modulus_params(&rsa->N, out_len)) + return 0; + + bn_init(&padded, padded_buf, bn_size(&rsa->N)); + bn_init(&encrypted, out, bn_size(&rsa->N)); + bn_init(&e, e_buf, sizeof(e_buf)); + *e.d = rsa->e; + + switch (padding) { + case PADDING_MODE_OAEP: + if (!oaep_pad((uint8_t *) padded.d, bn_size(&padded), + (const uint8_t *) in, in_len, hashing, label)) + return 0; + break; + case PADDING_MODE_PKCS1: + if (!pkcs1_type2_pad((uint8_t *) padded.d, bn_size(&padded), + (const uint8_t *) in, in_len)) + return 0; + break; + default: + return 0; /* Unsupported padding mode. */ + } + + /* Reverse from big-endian to little-endian notation. */ + reverse((uint8_t *) padded.d, bn_size(&padded)); + bn_mont_modexp(&encrypted, &padded, &e, &rsa->N); + /* Back to big-endian notation. */ + reverse((uint8_t *) encrypted.d, bn_size(&encrypted)); + *out_len = bn_size(&encrypted); + + dcrypto_memset(padded_buf, 0, sizeof(padded_buf)); + dcrypto_memset(e_buf, 0, sizeof(e_buf)); + return 1; +} + +int DCRYPTO_rsa_decrypt(struct RSA *rsa, uint8_t *out, uint32_t *out_len, + const uint8_t *in, const uint32_t in_len, + enum padding_mode padding, enum hashing_mode hashing, + const char *label) +{ + uint32_t encrypted_buf[RSA_MAX_WORDS]; + uint32_t padded_buf[RSA_MAX_WORDS]; + + struct BIGNUM encrypted; + struct BIGNUM padded; + int ret = 1; + + if (!check_modulus_params(&rsa->N, out_len)) + return 0; + if (in_len != bn_size(&rsa->N)) + return 0; /* Invalid input length. */ + + /* TODO(ngm): this copy can be eliminated if input may be modified. */ + bn_init(&encrypted, encrypted_buf, in_len); + memcpy(encrypted_buf, in, in_len); + bn_init(&padded, padded_buf, in_len); + + /* Reverse from big-endian to little-endian notation. */ + reverse((uint8_t *) encrypted.d, encrypted.dmax * BN_BYTES); + bn_mont_modexp(&padded, &encrypted, &rsa->d, &rsa->N); + /* Back to big-endian notation. */ + reverse((uint8_t *) padded.d, padded.dmax * BN_BYTES); + + switch (padding) { + case PADDING_MODE_OAEP: + if (!check_oaep_pad(out, out_len, (uint8_t *) padded.d, + bn_size(&padded), hashing, label)) + ret = 0; + break; + case PADDING_MODE_PKCS1: + if (!check_pkcs1_type2_pad( + out, out_len, (const uint8_t *) padded.d, + bn_size(&padded))) + ret = 0; + break; + default: + /* Unsupported padding mode. */ + ret = 0; + break; + } + + dcrypto_memset(encrypted_buf, 0, sizeof(encrypted_buf)); + dcrypto_memset(padded_buf, 0, sizeof(padded_buf)); + return ret; +} + +int DCRYPTO_rsa_sign(struct RSA *rsa, uint8_t *out, uint32_t *out_len, + const uint8_t *in, const uint32_t in_len, + enum padding_mode padding, enum hashing_mode hashing) +{ + uint32_t padded_buf[RSA_MAX_WORDS]; + + struct BIGNUM padded; + struct BIGNUM signature; + + if (!check_modulus_params(&rsa->N, out_len)) + return 0; + + bn_init(&padded, padded_buf, bn_size(&rsa->N)); + bn_init(&signature, out, bn_size(&rsa->N)); + + /* TODO(ngm): add support for PSS. */ + switch (padding) { + case PADDING_MODE_PKCS1: + if (!pkcs1_type1_pad((uint8_t *) padded.d, bn_size(&padded), + (const uint8_t *) in, in_len, hashing)) + return 0; + break; + default: + return 0; + } + + /* Reverse from big-endian to little-endian notation. */ + reverse((uint8_t *) padded.d, bn_size(&padded)); + bn_mont_modexp(&signature, &padded, &rsa->d, &rsa->N); + /* Back to big-endian notation. */ + reverse((uint8_t *) signature.d, bn_size(&signature)); + *out_len = bn_size(&rsa->N); + + dcrypto_memset(padded_buf, 0, sizeof(padded_buf)); + return 1; +} + +int DCRYPTO_rsa_verify(struct RSA *rsa, const uint8_t *digest, + uint32_t digest_len, const uint8_t *sig, + const uint32_t sig_len, enum padding_mode padding, + enum hashing_mode hashing) +{ + uint32_t padded_buf[RSA_MAX_WORDS]; + uint32_t signature_buf[RSA_MAX_WORDS]; + uint32_t e_buf[BN_BYTES / sizeof(uint32_t)]; + + struct BIGNUM padded; + struct BIGNUM signature; + struct BIGNUM e; + int ret = 1; + + if (!check_modulus_params(&rsa->N, NULL)) + return 0; + if (sig_len != bn_size(&rsa->N)) + return 0; /* Invalid input length. */ + + bn_init(&signature, signature_buf, bn_size(&rsa->N)); + memcpy(signature_buf, sig, bn_size(&rsa->N)); + bn_init(&padded, padded_buf, bn_size(&rsa->N)); + bn_init(&e, e_buf, sizeof(e_buf)); + *e.d = rsa->e; + + /* Reverse from big-endian to little-endian notation. */ + reverse((uint8_t *) signature.d, signature.dmax * BN_BYTES); + bn_mont_modexp(&padded, &signature, &e, &rsa->N); + /* Back to big-endian notation. */ + reverse((uint8_t *) padded.d, padded.dmax * BN_BYTES); + + switch (padding) { + case PADDING_MODE_PKCS1: + if (!check_pkcs1_type1_pad( + digest, digest_len, (uint8_t *) padded.d, + bn_size(&padded), hashing)) + ret = 0; + break; + default: + /* Unsupported padding mode. */ + ret = 0; + break; + } + + dcrypto_memset(padded_buf, 0, sizeof(padded_buf)); + dcrypto_memset(signature_buf, 0, sizeof(signature_buf)); + return ret; +} diff --git a/include/extension.h b/include/extension.h index f9757c1510..c985640fa9 100644 --- a/include/extension.h +++ b/include/extension.h @@ -48,6 +48,7 @@ struct extension_command { enum { EXTENSION_AES = 0, EXTENSION_HASH = 1, + EXTENSION_RSA = 2, }; diff --git a/test/tpm_test/rsa_test.py b/test/tpm_test/rsa_test.py new file mode 100644 index 0000000000..c337c524cc --- /dev/null +++ b/test/tpm_test/rsa_test.py @@ -0,0 +1,169 @@ +#!/usr/bin/python +# Copyright 2015 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. + +"""Module for testing rsa functions using extended commands.""" + +import hashlib +import struct + +import subcmd +import utils + + +_RSA_OPCODES = { + 'ENCRYPT': 0x00, + 'DECRYPT': 0x01, + 'SIGN': 0x02, + 'VERIFY': 0x03, + 'KEYGEN': 0x04 +} + + +# TPM2 ALG codes. +_RSA_PADDING = { + 'PKCS1-SSA': 0x14, + 'PKCS1-ES': 0x15, + 'OAEP': 0x17 +} + + +# TPM2 ALG codes. +_HASH = { + 'NONE': 0x00, + 'SHA1': 0x04, + 'SHA256': 0x0B +} + + +# Command format. +# +# 0x00 OP +# 0x00 PADDING +# 0x00 HASHING +# 0x00 MSB KEY LEN +# 0x00 LSB KEY LEN +# 0x00 MSB IN LEN +# 0x00 LSB IN LEN +# .... IN +# 0x00 MSB DIGEST LEN +# 0x00 LSB DIGEST LEN +# .... DIGEST +# +_RSA_CMD_FORMAT = '{o:c}{p:c}{h:c}{kl:s}{ml:s}{msg}{dl:s}{dig}' + + +def _decrypt_cmd(padding, hashing, key_len, msg): + op = _RSA_OPCODES['DECRYPT'] + msg_len = len(msg) + return _RSA_CMD_FORMAT.format(o=op, p=padding, h=hashing, + kl=struct.pack('>H', key_len), + ml=struct.pack('>H', msg_len), msg=msg, + dl='', dig='') + + +def _encrypt_cmd(padding, hashing, key_len, msg): + op = _RSA_OPCODES['ENCRYPT'] + msg_len = len(msg) + return _RSA_CMD_FORMAT.format(o=op, p=padding, h=hashing, + kl=struct.pack('>H', key_len), + ml=struct.pack('>H', msg_len), msg=msg, + dl='', dig='') + + +def _sign_cmd(padding, hashing, key_len, msg): + op = _RSA_OPCODES['SIGN'] + digest = '' + if hashing == _HASH['SHA1']: + digest = hashlib.sha1(msg).digest() + elif hashing == _HASH['SHA256']: + digest = hashlib.sha256(msg).digest() + digest_len = len(digest) + return _RSA_CMD_FORMAT.format(o=op, p=padding, h=hashing, + kl=struct.pack('>H', key_len), + ml=struct.pack('>H', digest_len), msg=digest, + dl='', dig='') + + +def _verify_cmd(padding, hashing, key_len, sig, msg): + op = _RSA_OPCODES['VERIFY'] + sig_len = len(sig) + digest = '' + if hashing == _HASH['SHA1']: + digest = hashlib.sha1(msg).digest() + elif hashing == _HASH['SHA256']: + digest = hashlib.sha256(msg).digest() + digest_len = len(digest) + return _RSA_CMD_FORMAT.format(o=op, p=padding, h=hashing, + kl=struct.pack('>H', key_len), + ml=struct.pack('>H', sig_len), msg=sig, + dl=struct.pack('>H', digest_len), dig=digest) + + +# +# TEST VECTORS. +# +_ENCRYPT_INPUTS = ( + ('OAEP', 'SHA1', 768), + ('OAEP', 'SHA256', 768), + ('PKCS1-ES', 'NONE', 768), + ('PKCS1-ES', 'NONE', 2048), +) + + +_SIGN_INPUTS = ( + # TODO(ngm): add support for PSS. + ('PKCS1-SSA', 'SHA1', 768), + ('PKCS1-SSA', 'SHA256', 768), +) + + +class RSAError(Exception): + pass + + +def _encrypt_tests(tpm): + msg = 'Hello CR50!' + + for data in _ENCRYPT_INPUTS: + padding, hashing, key_len = data + test_name = 'RSA-ENC:%s:%s:%d' % data + cmd = _encrypt_cmd(_RSA_PADDING[padding], _HASH[hashing], key_len, msg) + wrapped_response = tpm.command(tpm.wrap_ext_command(subcmd.RSA, cmd)) + ciphertext = tpm.unwrap_ext_response(subcmd.RSA, wrapped_response) + + cmd = _decrypt_cmd(_RSA_PADDING[padding], _HASH[hashing], + key_len, ciphertext) + wrapped_response = tpm.command(tpm.wrap_ext_command(subcmd.RSA, cmd)) + plaintext = tpm.unwrap_ext_response(subcmd.RSA, wrapped_response) + if msg != plaintext: + raise RSAError('%s error:%s%s' % ( + test_name, utils.hex_dump(msg), utils.hex_dump(plaintext))) + print('%sSUCCESS: %s' % (utils.cursor_back(), test_name)) + + +def _sign_tests(tpm): + msg = 'Hello CR50!' + + for data in _SIGN_INPUTS: + padding, hashing, key_len = data + test_name = 'RSA-SIGN:%s:%s:%d' % data + cmd = _sign_cmd(_RSA_PADDING[padding], _HASH[hashing], key_len, msg) + wrapped_response = tpm.command(tpm.wrap_ext_command(subcmd.RSA, cmd)) + signature = tpm.unwrap_ext_response(subcmd.RSA, wrapped_response) + + cmd = _verify_cmd(_RSA_PADDING[padding], _HASH[hashing], + key_len, signature, msg) + wrapped_response = tpm.command(tpm.wrap_ext_command(subcmd.RSA, cmd)) + verified = tpm.unwrap_ext_response(subcmd.RSA, wrapped_response) + expected = '\x01' + if verified != expected: + raise RSAError('%s error:%s%s' % ( + test_name, utils.hex_dump(verified), utils.hex_dump(expected))) + print('%sSUCCESS: %s' % (utils.cursor_back(), test_name)) + + +def rsa_test(tpm): + _encrypt_tests(tpm) + _sign_tests(tpm) diff --git a/test/tpm_test/subcmd.py b/test/tpm_test/subcmd.py index dbfcadb1c5..f7bd8090a0 100644 --- a/test/tpm_test/subcmd.py +++ b/test/tpm_test/subcmd.py @@ -8,3 +8,4 @@ # Keep these codes in sync with include/extension.h. AES = 0 HASH = 1 +RSA = 2 diff --git a/test/tpm_test/tpmtest.py b/test/tpm_test/tpmtest.py index 571a4548d9..52d7205022 100755 --- a/test/tpm_test/tpmtest.py +++ b/test/tpm_test/tpmtest.py @@ -19,8 +19,11 @@ root_dir = os.path.dirname(os.path.abspath(sys.argv[0])) sys.path.append(os.path.join(root_dir, '..', '..', 'build', 'tpm_test')) import crypto_test -import hash_test import ftdi_spi_tpm +import hash_test +import rsa_test +import subcmd + # Extension command for dcypto testing EXT_CMD = 0xbaccd00a @@ -133,7 +136,9 @@ if __name__ == '__main__': crypto_test.crypto_tests(t, os.path.join(root_dir, 'crypto_test.xml')) hash_test.hash_test(t) - except (TpmError, crypto_test.CryptoError, hash_test.HashError) as e: + rsa_test.rsa_test(t) + except (TpmError, crypto_test.CryptoError, hash_test.HashError, + rsa_test.RSAError) as e: print() print('Error:', e) if debug_needed: |