coreboot-libre-fam15h-rdimm/3rdparty/chromeec/board/cr50/tpm2/ecc.c

619 lines
14 KiB
C
Raw Normal View History

2024-03-04 11:14:53 +01:00
/* 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.
*/
/*
* TODO(ngm): only the NIST-P256 curve is currently supported.
*/
#include "CryptoEngine.h"
#include "TPMB.h"
#include "trng.h"
#include "util.h"
#include "dcrypto.h"
#include "cryptoc/p256.h"
#include "cryptoc/p256_ecdsa.h"
#include "cryptoc/util.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_point(const TPMS_ECC_POINT *a)
{
return check_p256_param(&a->x) &&
check_p256_param(&a->y);
}
BOOL _cpri__EccIsPointOnCurve(TPM_ECC_CURVE curve_id, TPMS_ECC_POINT *q)
{
int result;
switch (curve_id) {
case TPM_ECC_NIST_P256:
if (!check_p256_point(q))
return FALSE;
reverse_tpm2b(&q->x.b);
reverse_tpm2b(&q->y.b);
result = dcrypto_p256_is_valid_point((p256_int *) q->x.b.buffer,
(p256_int *) q->y.b.buffer);
reverse_tpm2b(&q->x.b);
reverse_tpm2b(&q->y.b);
if (result)
return TRUE;
else
return FALSE;
default:
return FALSE;
}
}
/* out = n1*G, or out = n2*in */
CRYPT_RESULT _cpri__EccPointMultiply(
TPMS_ECC_POINT *out, TPM_ECC_CURVE curve_id,
TPM2B_ECC_PARAMETER *n1, TPMS_ECC_POINT *in, TPM2B_ECC_PARAMETER *n2)
{
int result;
switch (curve_id) {
case TPM_ECC_NIST_P256:
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))
return CRYPT_PARAMETER;
if (n1 != NULL && !check_p256_param(n1))
return CRYPT_PARAMETER;
if (in != NULL &&
(!check_p256_point(in) ||
!_cpri__EccIsPointOnCurve(curve_id, in)))
return CRYPT_POINT;
if (n2 != NULL && !check_p256_param(n2))
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);
} else {
reverse_tpm2b(&n2->b);
reverse_tpm2b(&in->x.b);
reverse_tpm2b(&in->y.b);
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);
reverse_tpm2b(&in->x.b);
reverse_tpm2b(&in->y.b);
}
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);
return CRYPT_SUCCESS;
} else {
return CRYPT_NO_RESULT;
}
default:
return CRYPT_PARAMETER;
}
}
/* The name field of TPM2B_NAME is a TPMT_HA */
static const uint8_t TPM2_ECC_EK_NAME_TEMPLATE[] = {
/* TPM_ALG_SHA256 in big endian. */
0x00, 0x0b,
/* SHA256 digest of the default template TPMT_PUBLIC. */
0x0f, 0x12, 0x77, 0xa2, 0xf3, 0xf3, 0x82, 0xe7,
0xf7, 0x5d, 0xb4, 0x66, 0xfa, 0xc2, 0x34, 0x18,
0x2a, 0x8d, 0x62, 0xf9, 0x7d, 0xfb, 0xaa, 0xe7,
0xb0, 0x6f, 0xdf, 0x52, 0xbd, 0xa5, 0x14, 0x67
};
BUILD_ASSERT(sizeof(TPM2_ECC_EK_NAME_TEMPLATE) == 2 + SHA256_DIGEST_SIZE);
/* The first 4 bytes of the wrong template used in factory
* c2e0319340fb48f102539ea98363f81e2d306e918dd778abf05473a2a60dae09
*/
static const TPM2B_4_BYTE_VALUE TPM2_ECC_EK_NAME_CR50 = {
.t = {
.size = 4,
.buffer = {
0xc2, 0xe0, 0x31, 0x93
},
}
};
/* Key generation based on FIPS-186.4 section B.1.2 (Key Generation by
* Testing Candidates) */
CRYPT_RESULT _cpri__GenerateKeyEcc(
TPMS_ECC_POINT *q, TPM2B_ECC_PARAMETER *d,
TPM_ECC_CURVE curve_id, TPM_ALG_ID hash_alg,
TPM2B *seed, const char *label, TPM2B *extra, UINT32 *counter)
{
TPM2B_4_BYTE_VALUE marshaled_counter = { .t = {4} };
TPM2B_32_BYTE_VALUE local_seed = { .t = {32} };
TPM2B *local_extra;
uint32_t count = 0;
uint8_t key_bytes[P256_NBYTES];
LITE_HMAC_CTX hmac;
if (curve_id != TPM_ECC_NIST_P256)
return CRYPT_PARAMETER;
/* extra may be empty, but seed must be specified. */
if (seed == NULL || seed->size < PRIMARY_SEED_SIZE)
return CRYPT_PARAMETER;
if (counter != NULL)
count = *counter;
if (count == 0)
count++;
/* Hash down the primary seed for ECC key generation, so that
* the derivation tree is distinct from RSA key derivation. */
DCRYPTO_HMAC_SHA256_init(&hmac, seed->buffer, seed->size);
HASH_update(&hmac.hash, "ECC", 4);
memcpy(local_seed.t.buffer, DCRYPTO_HMAC_final(&hmac),
local_seed.t.size);
always_memset(&hmac, 0, sizeof(hmac));
/* b/35576109: the personalize code uses only the first 4 bytes
* of extra.
*/
if (extra && extra->size == sizeof(TPM2_ECC_EK_NAME_TEMPLATE) &&
memcmp(extra->buffer,
TPM2_ECC_EK_NAME_TEMPLATE,
sizeof(TPM2_ECC_EK_NAME_TEMPLATE)) == 0) {
local_extra = (TPM2B *) &TPM2_ECC_EK_NAME_CR50.b;
} else {
local_extra = extra;
}
for (; count != 0; count++) {
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)) {
q->x.b.size = sizeof(p256_int);
q->y.b.size = sizeof(p256_int);
reverse_tpm2b(&q->x.b);
reverse_tpm2b(&q->y.b);
d->b.size = sizeof(p256_int);
reverse_tpm2b(&d->b);
break;
}
}
always_memset(local_seed.t.buffer, 0, local_seed.t.size);
always_memset(key_bytes, 0, sizeof(key_bytes));
if (count == 0)
FAIL(FATAL_ERROR_INTERNAL);
if (counter != NULL)
*counter = count;
return CRYPT_SUCCESS;
}
CRYPT_RESULT _cpri__SignEcc(
TPM2B_ECC_PARAMETER *r, TPM2B_ECC_PARAMETER *s,
TPM_ALG_ID scheme, TPM_ALG_ID hash_alg, TPM_ECC_CURVE curve_id,
TPM2B_ECC_PARAMETER *d, TPM2B *digest, TPM2B_ECC_PARAMETER *k)
{
uint8_t digest_local[sizeof(p256_int)];
const size_t digest_len = MIN(digest->size, sizeof(digest_local));
p256_int p256_digest;
int result;
struct drbg_ctx drbg;
if (curve_id != TPM_ECC_NIST_P256)
return CRYPT_PARAMETER;
switch (scheme) {
case TPM_ALG_ECDSA:
if (!check_p256_param(d))
return CRYPT_PARAMETER;
/* Trucate / 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);
p256_from_bin(digest_local, &p256_digest);
reverse_tpm2b(&d->b);
hmac_drbg_init_rand(&drbg, 512);
result = dcrypto_p256_ecdsa_sign(&drbg,
(p256_int *) d->b.buffer,
&p256_digest,
(p256_int *) r->b.buffer,
(p256_int *) s->b.buffer);
reverse_tpm2b(&d->b);
r->b.size = sizeof(p256_int);
s->b.size = sizeof(p256_int);
reverse_tpm2b(&r->b);
reverse_tpm2b(&s->b);
if (result)
return CRYPT_SUCCESS;
else
return CRYPT_FAIL;
default:
return CRYPT_PARAMETER;
}
}
CRYPT_RESULT _cpri__ValidateSignatureEcc(
TPM2B_ECC_PARAMETER *r, TPM2B_ECC_PARAMETER *s,
TPM_ALG_ID scheme, TPM_ALG_ID hash_alg,
TPM_ECC_CURVE curve_id, TPMS_ECC_POINT *q, TPM2B *digest)
{
uint8_t digest_local[sizeof(p256_int)];
const size_t digest_len = MIN(digest->size, sizeof(digest_local));
p256_int p256_digest;
int result;
if (curve_id != TPM_ECC_NIST_P256)
return CRYPT_PARAMETER;
switch (scheme) {
case TPM_ALG_ECDSA:
/* Trucate / 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);
p256_from_bin(digest_local, &p256_digest);
reverse_tpm2b(&q->x.b);
reverse_tpm2b(&q->y.b);
reverse_tpm2b(&r->b);
reverse_tpm2b(&s->b);
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);
reverse_tpm2b(&q->x.b);
reverse_tpm2b(&q->y.b);
reverse_tpm2b(&r->b);
reverse_tpm2b(&s->b);
if (result)
return CRYPT_SUCCESS;
else
return CRYPT_FAIL;
default:
return CRYPT_PARAMETER;
}
}
CRYPT_RESULT _cpri__GetEphemeralEcc(TPMS_ECC_POINT *q, TPM2B_ECC_PARAMETER *d,
TPM_ECC_CURVE curve_id)
{
int result;
uint8_t key_bytes[P256_NBYTES] __aligned(4);
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);
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);
d->b.size = sizeof(p256_int);
reverse_tpm2b(&d->b);
return CRYPT_SUCCESS;
} else {
return CRYPT_FAIL;
}
}
#ifdef CRYPTO_TEST_SETUP
#include "extension.h"
enum {
TEST_SIGN = 0,
TEST_VERIFY = 1,
TEST_KEYGEN = 2,
TEST_KEYDERIVE = 3
};
struct TPM2B_ECC_PARAMETER_aligned {
uint16_t pad;
TPM2B_ECC_PARAMETER d;
} __packed __aligned(4);
struct TPM2B_MAX_BUFFER_aligned {
uint16_t pad;
TPM2B_MAX_BUFFER d;
} __packed __aligned(4);
static const struct TPM2B_ECC_PARAMETER_aligned NIST_P256_d = {
.d = {
.t = {32, {
0xfc, 0x44, 0x1e, 0x07, 0x74, 0x4e, 0x48, 0xf1,
0x09, 0xb7, 0xe6, 0x6b, 0x29, 0x48, 0x2f, 0x7b,
0x7e, 0x3e, 0xc9, 0x1f, 0xa2, 0x7f, 0xd4, 0x87,
0x09, 0x91, 0xb2, 0x89, 0xfe, 0xa0, 0xd2, 0x0a
}
}
}
};
static const struct TPM2B_ECC_PARAMETER_aligned NIST_P256_qx = {
.d = {
.t = {32, {
0x12, 0xc3, 0xd6, 0xa2, 0x67, 0x9c, 0xa8, 0xee,
0x3c, 0x4d, 0x92, 0x7f, 0x20, 0x4e, 0xd5, 0xbc,
0xb4, 0x57, 0x7a, 0x04, 0xb0, 0xac, 0x02, 0xb2,
0xa3, 0x6a, 0xb3, 0xe9, 0xe1, 0x07, 0x81, 0xde
}
}
}
};
static const struct TPM2B_ECC_PARAMETER_aligned NIST_P256_qy = {
.d = {
.t = {32, {
0x5c, 0x85, 0xad, 0x74, 0x13, 0x97, 0x11, 0x72,
0xfc, 0xa5, 0x73, 0x8f, 0xee, 0x9d, 0x0e, 0x7b,
0xc5, 0x9f, 0xfd, 0x8a, 0x62, 0x6d, 0x68, 0x9b,
0xc6, 0xcc, 0xa4, 0xb5, 0x86, 0x65, 0x52, 0x1d
}
}
}
};
#define MAX_MSG_BYTES MAX_DIGEST_BUFFER
static int point_equals(const TPMS_ECC_POINT *a, const TPMS_ECC_POINT *b)
{
int diff = 0;
diff = a->x.b.size != b->x.b.size;
diff |= a->y.b.size != b->y.b.size;
if (!diff) {
diff |= !DCRYPTO_equals(
a->x.b.buffer, b->x.b.buffer, a->x.b.size);
diff |= !DCRYPTO_equals(
a->y.b.buffer, b->y.b.buffer, a->y.b.size);
}
return !diff;
}
static void ecc_command_handler(void *cmd_body, size_t cmd_size,
size_t *response_size_out)
{
uint8_t *cmd;
uint8_t op;
uint8_t curve_id;
uint8_t sign_mode;
uint8_t hashing;
uint16_t in_len;
uint8_t in[MAX_MSG_BYTES];
uint16_t digest_len;
struct TPM2B_MAX_BUFFER_aligned digest;
uint8_t *out = (uint8_t *) cmd_body;
uint32_t *response_size = (uint32_t *) response_size_out;
TPMS_ECC_POINT q;
TPM2B_ECC_PARAMETER d;
struct TPM2B_ECC_PARAMETER_aligned r;
struct TPM2B_ECC_PARAMETER_aligned s;
/* Command format.
*
* OFFSET FIELD
* 0 OP
* 1 CURVE_ID
* 2 SIGN_MODE
* 3 HASHING
* 4 MSB IN LEN
* 5 LSB IN LEN
* 6 IN
* 6 + IN_LEN MSB DIGEST LEN
* 7 + IN_LEN LSB DIGEST LEN
* 8 + IN_LEN DIGEST
*/
cmd = (uint8_t *) cmd_body;
op = *cmd++;
curve_id = *cmd++;
sign_mode = *cmd++;
hashing = *cmd++;
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);
cmd += in_len;
digest_len = ((uint16_t) (cmd[0] << 8)) | cmd[1];
cmd += 2;
if (digest_len > sizeof(digest.d.t.buffer)) {
*response_size = 0;
return;
}
digest.d.t.size = digest_len;
memcpy(digest.d.t.buffer, cmd, digest_len);
cmd += digest_len;
/* Make copies of d, and q, as const data is immutable. */
switch (curve_id) {
case TPM_ECC_NIST_P256:
d = NIST_P256_d.d;
q.x = NIST_P256_qx.d;
q.y = NIST_P256_qy.d;
break;
default:
*response_size = 0;
return;
}
switch (op) {
case TEST_SIGN:
if (_cpri__SignEcc(&r.d, &s.d, sign_mode, hashing,
curve_id, &d, &digest.d.b, NULL)
!= CRYPT_SUCCESS) {
*response_size = 0;
return;
}
memcpy(out, r.d.b.buffer, r.d.b.size);
out += r.d.b.size;
memcpy(out, s.d.b.buffer, s.d.b.size);
*response_size = r.d.b.size + s.d.b.size;
break;
case TEST_VERIFY:
r.d.b.size = in_len / 2;
memcpy(r.d.b.buffer, in, r.d.b.size);
s.d.b.size = in_len / 2;
memcpy(s.d.b.buffer, in + r.d.b.size, s.d.b.size);
if (_cpri__ValidateSignatureEcc(
&r.d, &s.d, sign_mode, hashing, curve_id,
&q, &digest.d.b) != CRYPT_SUCCESS) {
*response_size = 0;
} else {
*out = 1;
*response_size = 1;
}
return;
case TEST_KEYGEN:
{
struct TPM2B_ECC_PARAMETER_aligned d_local;
TPMS_ECC_POINT q_local;
if (_cpri__GetEphemeralEcc(&q, &d_local.d, curve_id)
!= CRYPT_SUCCESS) {
*response_size = 0;
return;
}
if (_cpri__EccIsPointOnCurve(curve_id, &q) != TRUE) {
*response_size = 0;
return;
}
/* Verify correspondence of secret with the public point. */
if (_cpri__EccPointMultiply(
&q_local, curve_id, &d_local.d,
NULL, NULL) != CRYPT_SUCCESS) {
*response_size = 0;
return;
}
if (!point_equals(&q, &q_local)) {
*response_size = 0;
return;
}
*out = 1;
*response_size = 1;
return;
}
case TEST_KEYDERIVE:
{
/* Random seed. */
TPM2B_SEED seed;
struct TPM2B_ECC_PARAMETER_aligned d_local;
TPMS_ECC_POINT q_local;
const char *label = "ecc_test";
if (in_len > PRIMARY_SEED_SIZE) {
*response_size = 0;
return;
}
seed.t.size = in_len;
memcpy(seed.t.buffer, in, in_len);
if (_cpri__GenerateKeyEcc(
&q, &d_local.d, curve_id, hashing,
&seed.b, label, NULL, NULL) != CRYPT_SUCCESS) {
*response_size = 0;
return;
}
if (_cpri__EccIsPointOnCurve(curve_id, &q) != TRUE) {
*response_size = 0;
return;
}
/* Verify correspondence of secret with the public point. */
if (_cpri__EccPointMultiply(
&q_local, curve_id, &d_local.d,
NULL, NULL) != CRYPT_SUCCESS) {
*response_size = 0;
return;
}
if (!point_equals(&q, &q_local)) {
*response_size = 0;
return;
}
*out = 1;
*response_size = 1;
return;
}
default:
*response_size = 0;
return;
}
}
DECLARE_EXTENSION_COMMAND(EXTENSION_ECC, ecc_command_handler);
#endif /* CRYPTO_TEST_SETUP */