diff options
author | nagendra modadugu <ngm@google.com> | 2016-01-22 11:37:48 -0800 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2016-02-08 15:24:29 -0800 |
commit | 7ac69e594b9a092dfeb76a4969396f7ddde4fbee (patch) | |
tree | c8c9eb1d45a74fbc9b9892e28a0c75b46818cb2a | |
parent | 89424bfbedba6c06f4a4ef9e444a2c4d0575883a (diff) | |
download | chrome-ec-7ac69e594b9a092dfeb76a4969396f7ddde4fbee.tar.gz |
CR50: Add initial elliptic curve crypto implementation.
This change adds support for NIST-P256 curve operations.
BRANCH=none
BUG=chrome-os-partner:43025,chrome-os-partner:47524
TEST=new tests under test/tpm2/ pass.
Change-Id: I03a35ff3ab8af3c52282d882937880bfa2bdcd32
Signed-off-by: nagendra modadugu <ngm@google.com>
Reviewed-on: https://chromium-review.googlesource.com/324540
Commit-Ready: Nagendra Modadugu <ngm@google.com>
Tested-by: Nagendra Modadugu <ngm@google.com>
Reviewed-by: Marius Schilder <mschilder@chromium.org>
Reviewed-by: Vadim Bendebury <vbendeb@chromium.org>
-rw-r--r-- | board/cr50/build.mk | 1 | ||||
-rw-r--r-- | board/cr50/ec.tasklist | 2 | ||||
-rw-r--r-- | board/cr50/tpm2/ecc.c | 464 | ||||
-rw-r--r-- | board/cr50/tpm2/stubs.c | 80 | ||||
-rw-r--r-- | chip/g/build.mk | 4 | ||||
-rw-r--r-- | chip/g/dcrypto/dcrypto.h | 18 | ||||
-rw-r--r-- | chip/g/dcrypto/hmac.c | 56 | ||||
-rw-r--r-- | chip/g/dcrypto/internal.h | 62 | ||||
-rw-r--r-- | chip/g/dcrypto/p256.c | 444 | ||||
-rw-r--r-- | chip/g/dcrypto/p256_ec.c | 1403 | ||||
-rw-r--r-- | chip/g/dcrypto/p256_ecdsa.c | 103 | ||||
-rw-r--r-- | include/extension.h | 1 | ||||
-rw-r--r-- | test/tpm_test/ecc_test.py | 169 | ||||
-rw-r--r-- | test/tpm_test/subcmd.py | 1 | ||||
-rwxr-xr-x | test/tpm_test/tpmtest.py | 2 |
15 files changed, 2729 insertions, 81 deletions
diff --git a/board/cr50/build.mk b/board/cr50/build.mk index b8f15919d6..9960700996 100644 --- a/board/cr50/build.mk +++ b/board/cr50/build.mk @@ -31,6 +31,7 @@ dirs-y += $(BDIR)/tpm2 board-y = board.o board-y += tpm2/NVMem.o board-y += tpm2/aes.o +board-y += tpm2/ecc.o board-y += tpm2/hash.o board-y += tpm2/hash_data.o board-y += tpm2/platform.o diff --git a/board/cr50/ec.tasklist b/board/cr50/ec.tasklist index f48774f1b8..52f4af3000 100644 --- a/board/cr50/ec.tasklist +++ b/board/cr50/ec.tasklist @@ -19,6 +19,6 @@ #define CONFIG_TASK_LIST \ TASK_ALWAYS(HOOKS, hook_task, NULL, TASK_STACK_SIZE) \ TASK_ALWAYS(BLOB, blob_task, NULL, TASK_STACK_SIZE) \ - TASK_NOTEST(TPM, tpm_task, NULL, 4096) \ + TASK_NOTEST(TPM, tpm_task, NULL, 8192) \ TASK_NOTEST(HOSTCMD, host_command_task, NULL, TASK_STACK_SIZE) \ TASK_ALWAYS(CONSOLE, console_task, NULL, TASK_STACK_SIZE) diff --git a/board/cr50/tpm2/ecc.c b/board/cr50/tpm2/ecc.c new file mode 100644 index 0000000000..d3ca3069f8 --- /dev/null +++ b/board/cr50/tpm2/ecc.c @@ -0,0 +1,464 @@ +/* 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 "dcrypto.h" + +TPM2B_BYTE_VALUE(4); + +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) +{ + switch (curve_id) { + case TPM_ECC_NIST_P256: + if (!check_p256_point(q)) + return FALSE; + + if (DCRYPTO_p256_valid_point((p256_int *) q->x.b.buffer, + (p256_int *) q->y.b.buffer)) + return TRUE; + else + return FALSE; + default: + return FALSE; + } +} + +/* out = n1*G + 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 (!check_p256_param(n1)) + return CRYPT_PARAMETER; + if (in != NULL && !check_p256_point(in)) + return CRYPT_PARAMETER; + if (n2 != NULL && !check_p256_param(n2)) + return CRYPT_PARAMETER; + + if (in == NULL || n2 == NULL) + result = DCRYPTO_p256_base_point_mul( + (p256_int *) out->x.b.buffer, + (p256_int *) out->y.b.buffer, + (p256_int *) n1->b.buffer); + else + result = DCRYPTO_p256_points_mul( + (p256_int *) out->x.b.buffer, + (p256_int *) out->y.b.buffer, + (p256_int *) n1->b.buffer, + (p256_int *) n2->b.buffer, + (p256_int *) in->x.b.buffer, + (p256_int *) in->y.b.buffer); + + if (result) { + out->x.b.size = sizeof(p256_int); + out->y.b.size = sizeof(p256_int); + return CRYPT_SUCCESS; + } else { + return CRYPT_NO_RESULT; + } + default: + return CRYPT_FAIL; + } +} + +/* 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} }; + uint32_t count = 0; + uint8_t key_bytes[P256_NBYTES]; + + 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++; + + for (; count != 0; count++) { + memcpy(marshaled_counter.t.buffer, &count, sizeof(count)); + _cpri__KDFa(hash_alg, seed, label, 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)) + break; + } + + 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; + + 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); + DCRYPTO_p256_ecdsa_sign((p256_int *) d->b.buffer, + &p256_digest, + (p256_int *) r->b.buffer, + (p256_int *) s->b.buffer); + r->b.size = sizeof(p256_int); + s->b.size = sizeof(p256_int); + return CRYPT_SUCCESS; + 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; + + 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); + if (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)) + 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) +{ + 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)); + 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)) + 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, { + 0x0a, 0xd2, 0xa0, 0xfe, 0x89, 0xb2, 0x91, 0x09, + 0x87, 0xd4, 0x7f, 0xa2, 0x1f, 0xc9, 0x3e, 0x7e, + 0x7b, 0x2f, 0x48, 0x29, 0x6b, 0xe6, 0xb7, 0x09, + 0xf1, 0x48, 0x4e, 0x74, 0x07, 0x1e, 0x44, 0xfc + } + } + } +}; + +static const struct TPM2B_ECC_PARAMETER_aligned NIST_P256_qx = { + .d = { + .t = {32, { + 0xde, 0x81, 0x07, 0xe1, 0xe9, 0xb3, 0x6a, 0xa3, + 0xb2, 0x02, 0xac, 0xb0, 0x04, 0x7a, 0x57, 0xb4, + 0xbc, 0xd5, 0x4e, 0x20, 0x7f, 0x92, 0x4d, 0x3c, + 0xee, 0xa8, 0x9c, 0x67, 0xa2, 0xd6, 0xc3, 0x12 + } + } + } +}; + +static const struct TPM2B_ECC_PARAMETER_aligned NIST_P256_qy = { + .d = { + .t = {32, { + 0x1d, 0x52, 0x65, 0x86, 0xb5, 0xa4, 0xcc, 0xc6, + 0x9b, 0x68, 0x6d, 0x62, 0x8a, 0xfd, 0x9f, 0xc5, + 0x7b, 0x0e, 0x9d, 0xee, 0x8f, 0x73, 0xa5, 0xfc, + 0x72, 0x11, 0x97, 0x13, 0x74, 0xad, 0x85, 0x5c + } + } + } +}; + +#define MAX_MSG_BYTES MAX_DIGEST_BUFFER + +static int point_equals(const TPMS_ECC_POINT *a, const TPMS_ECC_POINT *b) +{ + return a->x.b.size == b->x.b.size && + a->y.b.size == b->y.b.size && + memcmp(a->x.b.buffer, b->x.b.buffer, a->x.b.size) == 0 && + memcmp(a->y.b.buffer, b->y.b.buffer, a->y.b.size) == 0; + +} + +static void ec_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; + TPM2B_ECC_PARAMETER *qx; + TPM2B_ECC_PARAMETER *qy; + 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; + + switch (curve_id) { + case TPM_ECC_NIST_P256: + d = (TPM2B_ECC_PARAMETER *) &NIST_P256_d.d; + qx = (TPM2B_ECC_PARAMETER *) &NIST_P256_qx.d; + qy = (TPM2B_ECC_PARAMETER *) &NIST_P256_qy.d; + q.x = *qx; + q.y = *qy; + 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 = "ec_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_EC, ec_command_handler); + +#endif /* CRYPTO_TEST_SETUP */ diff --git a/board/cr50/tpm2/stubs.c b/board/cr50/tpm2/stubs.c index 37259d49fd..2ffb1d40ba 100644 --- a/board/cr50/tpm2/stubs.c +++ b/board/cr50/tpm2/stubs.c @@ -46,45 +46,6 @@ CRYPT_RESULT _cpri__EccCommitCompute( return CRYPT_FAIL; } -BOOL _cpri__EccIsPointOnCurve( - TPM_ECC_CURVE curveId, // IN: the curve selector - TPMS_ECC_POINT * Q // IN: the point. - ) -{ - ecprintf("%s called\n", __func__); - return 0; -} - -CRYPT_RESULT _cpri__EccPointMultiply( - TPMS_ECC_POINT * Rout, // OUT: the product point R - TPM_ECC_CURVE curveId, // IN: the curve to use - TPM2B_ECC_PARAMETER * dIn, // IN: value to multiply against the - // curve generator - TPMS_ECC_POINT * Qin, // IN: point Q - TPM2B_ECC_PARAMETER * uIn // IN: scalar value for the multiplier of Q - ) -{ - 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 - TPM_ECC_CURVE curveId, // IN: the curve identifier - TPM_ALG_ID hashAlg, // IN: hash algorithm to use in the key - // generation process - TPM2B * seed, // IN: the seed to use - const char *label, // IN: A label for the generation process. - TPM2B * extra, // IN: Party 1 data for the KDF - UINT32 * counter // IN/OUT: Counter value to allow KDF - // iteration to be propagated across multiple functions - ) -{ - ecprintf("%s called\n", __func__); - return CRYPT_FAIL; -} - CRYPT_RESULT _cpri__GenerateKeyRSA( TPM2B * n, // OUT: The public modulu TPM2B * p, // OUT: One of the prime factors of n @@ -102,31 +63,6 @@ CRYPT_RESULT _cpri__GenerateKeyRSA( return CRYPT_FAIL; } -CRYPT_RESULT _cpri__GetEphemeralEcc( - TPMS_ECC_POINT * Qout, // OUT: the public point - TPM2B_ECC_PARAMETER * dOut, // OUT: the private scalar - TPM_ECC_CURVE curveId // IN: the curve for the key - ) -{ - ecprintf("%s called\n", __func__); - return CRYPT_FAIL; -} - -CRYPT_RESULT _cpri__SignEcc( - TPM2B_ECC_PARAMETER * rOut, // OUT: r component of the signature - TPM2B_ECC_PARAMETER * sOut, // OUT: s component of the signature - TPM_ALG_ID scheme, // IN: the scheme selector - TPM_ALG_ID hashAlg, // IN: the hash algorithm if need - TPM_ECC_CURVE curveId, // IN: the curve used in the signature process - TPM2B_ECC_PARAMETER * dIn, // IN: the private key - TPM2B * digest, // IN: the digest to sign - TPM2B_ECC_PARAMETER * kIn // IN: k for input - ) -{ - ecprintf("%s called\n", __func__); - return CRYPT_FAIL; -} - BOOL _cpri__Startup( void) { @@ -159,22 +95,6 @@ CRYPT_RESULT _cpri__TestKeyRSA( return CRYPT_FAIL; } -CRYPT_RESULT _cpri__ValidateSignatureEcc( - TPM2B_ECC_PARAMETER * rIn, // IN: r component of the signature - TPM2B_ECC_PARAMETER * sIn, // IN: s component of the signature - TPM_ALG_ID scheme, // IN: the scheme selector - TPM_ALG_ID hashAlg, // IN: the hash algorithm used (not used - // in all schemes) - TPM_ECC_CURVE curveId, // IN: the curve used in the - // signature process - TPMS_ECC_POINT * Qin, // IN: the public point of the key - TPM2B * digest // IN: the digest that was signed - ) -{ - 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 6db09c1307..d615a927ed 100644 --- a/chip/g/build.mk +++ b/chip/g/build.mk @@ -27,6 +27,10 @@ endif chip-$(CONFIG_DCRYPTO)+= dcrypto/aes.o chip-$(CONFIG_DCRYPTO)+= dcrypto/bn.o +chip-$(CONFIG_DCRYPTO)+= dcrypto/hmac.o +chip-$(CONFIG_DCRYPTO)+= dcrypto/p256.o +chip-$(CONFIG_DCRYPTO)+= dcrypto/p256_ec.o +chip-$(CONFIG_DCRYPTO)+= dcrypto/p256_ecdsa.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/dcrypto.h b/chip/g/dcrypto/dcrypto.h index 3d8e78ab96..c0422208ae 100644 --- a/chip/g/dcrypto/dcrypto.h +++ b/chip/g/dcrypto/dcrypto.h @@ -119,4 +119,22 @@ int DCRYPTO_rsa_verify(struct RSA *rsa, const uint8_t *digest, const uint32_t sig_len, enum padding_mode padding, enum hashing_mode hashing); +/* + * EC. + */ +int DCRYPTO_p256_valid_point(const p256_int *x, const p256_int *y); +int DCRYPTO_p256_base_point_mul(p256_int *out_x, p256_int *out_y, + const p256_int *n); +int DCRYPTO_p256_points_mul(p256_int *out_x, p256_int *out_y, + const p256_int *n1, const p256_int *n2, + const p256_int *in_x, const p256_int *in_y); +int DCRYPTO_p256_key_from_bytes(p256_int *x, p256_int *y, p256_int *d, + const uint8_t key_bytes[P256_NBYTES]); + +void DCRYPTO_p256_ecdsa_sign(const p256_int *d, const p256_int *digest, + p256_int *r, p256_int *s); +int DCRYPTO_p256_ecdsa_verify(const p256_int *key_x, const p256_int *key_y, + const p256_int *digest, const p256_int *r, + const p256_int *s); + #endif /* ! __EC_CHIP_G_DCRYPTO_DCRYPTO_H */ diff --git a/chip/g/dcrypto/hmac.c b/chip/g/dcrypto/hmac.c new file mode 100644 index 0000000000..1b6a820159 --- /dev/null +++ b/chip/g/dcrypto/hmac.c @@ -0,0 +1,56 @@ +/* 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 <stdint.h> + +#include "internal.h" +#include "dcrypto.h" + +static void HMAC_init(struct HMAC_CTX *ctx, const void *key, unsigned int len) +{ + unsigned int i; + + memset(&ctx->opad[0], 0, sizeof(ctx->opad)); + + if (len > sizeof(ctx->opad)) { + DCRYPTO_SHA256_init(&ctx->hash, 0); + DCRYPTO_HASH_update(&ctx->hash, key, len); + memcpy(&ctx->opad[0], DCRYPTO_HASH_final(&ctx->hash), + DCRYPTO_HASH_size(&ctx->hash)); + } else { + memcpy(&ctx->opad[0], key, len); + } + + for (i = 0; i < sizeof(ctx->opad); ++i) + ctx->opad[i] ^= 0x36; + + DCRYPTO_SHA256_init(&ctx->hash, 0); + /* hash ipad */ + DCRYPTO_HASH_update(&ctx->hash, ctx->opad, sizeof(ctx->opad)); + + for (i = 0; i < sizeof(ctx->opad); ++i) + ctx->opad[i] ^= (0x36 ^ 0x5c); +} + +void dcrypto_HMAC_SHA256_init(struct HMAC_CTX *ctx, const void *key, + unsigned int len) +{ + DCRYPTO_SHA256_init(&ctx->hash, 0); + HMAC_init(ctx, key, len); +} + +const uint8_t *dcrypto_HMAC_final(struct HMAC_CTX *ctx) +{ + uint8_t digest[SHA_DIGEST_MAX_BYTES]; /* upto SHA2 */ + + memcpy(digest, DCRYPTO_HASH_final(&ctx->hash), + (DCRYPTO_HASH_size(&ctx->hash) <= sizeof(digest) ? + DCRYPTO_HASH_size(&ctx->hash) : sizeof(digest))); + DCRYPTO_SHA256_init(&ctx->hash, 0); + DCRYPTO_HASH_update(&ctx->hash, ctx->opad, sizeof(ctx->opad)); + DCRYPTO_HASH_update(&ctx->hash, digest, DCRYPTO_HASH_size(&ctx->hash)); + memset(&ctx->opad[0], 0, sizeof(ctx->opad)); /* wipe key */ + return DCRYPTO_HASH_final(&ctx->hash); +} diff --git a/chip/g/dcrypto/internal.h b/chip/g/dcrypto/internal.h index 36aa35f6bf..09685c3004 100644 --- a/chip/g/dcrypto/internal.h +++ b/chip/g/dcrypto/internal.h @@ -83,6 +83,21 @@ void dcrypto_sha_update(struct HASH_CTX *unused, void dcrypto_sha_wait(enum sha_mode mode, uint32_t *digest); /* + * HMAC. + */ +struct HMAC_CTX { + struct HASH_CTX hash; + uint8_t opad[64]; +}; + +#define HASH_update(ctx, data, len) \ + ((ctx)->vtab->update((ctx), (data), (len))) +void dcrypto_HMAC_SHA256_init(struct HMAC_CTX *ctx, const void *key, + unsigned int len); +#define dcrypto_HMAC_update(ctx, data, len) HASH_update(&(ctx)->hash, data, len) +const uint8_t *dcrypto_HMAC_final(struct HMAC_CTX *ctx); + +/* * BIGNUM. */ #define BN_BITS2 32 @@ -102,6 +117,53 @@ void bn_mont_modexp(struct BIGNUM *output, const struct BIGNUM *input, const struct BIGNUM *exp, const struct BIGNUM *N); /* + * EC. + */ +#define P256_BITSPERDIGIT 32 +#define P256_NDIGITS 8 +#define P256_NBYTES 32 + +typedef uint32_t p256_digit; +typedef int32_t p256_sdigit; +typedef uint64_t p256_ddigit; +typedef int64_t p256_sddigit; + +/* Define p256_int as a struct to leverage struct assignment. */ +typedef struct { + p256_digit a[P256_NDIGITS] __packed; +} p256_int; + +#define P256_DIGITS(x) ((x)->a) +#define P256_DIGIT(x, y) ((x)->a[y]) + +#define P256_ZERO { {0} } +#define P256_ONE { {1} } + +/* Curve constants. */ +extern const p256_int SECP256r1_n; +extern const p256_int SECP256r1_p; +extern const p256_int SECP256r1_b; + +void p256_init(p256_int *a); +void p256_from_bin(const uint8_t src[P256_NBYTES], p256_int *dst); +#define p256_clear(a) p256_init((a)) +int p256_is_zero(const p256_int *a); +int p256_cmp(const p256_int *a, const p256_int *b); +int p256_get_bit(const p256_int *scalar, int bit); +p256_digit p256_shl(const p256_int *a, int n, p256_int *b); +void p256_shr(const p256_int *a, int n, p256_int *b); +int p256_add(const p256_int *a, const p256_int *b, p256_int *c); +int p256_add_d(const p256_int *a, p256_digit d, p256_int *b); +void p256_points_mul_vartime( + const p256_int *n1, const p256_int *n2, const p256_int *in_x, + const p256_int *in_y, p256_int *out_x, p256_int *out_y); +void p256_mod(const p256_int *MOD, const p256_int *in, p256_int *out); +void p256_modmul(const p256_int *MOD, const p256_int *a, + const p256_digit top_b, const p256_int *b, p256_int *c); +void p256_modinv(const p256_int *MOD, const p256_int *a, p256_int *b); +void p256_modinv_vartime(const p256_int *MOD, const p256_int *a, p256_int *b); + +/* * Utility functions. */ /* TODO(ngm): memset that doesn't get optimized out. */ diff --git a/chip/g/dcrypto/p256.c b/chip/g/dcrypto/p256.c new file mode 100644 index 0000000000..9c3af545bc --- /dev/null +++ b/chip/g/dcrypto/p256.c @@ -0,0 +1,444 @@ +/* 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> + +const p256_int SECP256r1_n = /* curve order */ + { {0xfc632551, 0xf3b9cac2, 0xa7179e84, 0xbce6faad, -1, -1, 0, -1} }; +static const p256_int SECP256r1_nMin2 = /* curve order - 2 */ + { {0xfc632551 - 2, 0xf3b9cac2, 0xa7179e84, 0xbce6faad, -1, -1, 0, -1} }; +const p256_int SECP256r1_p = /* curve field size */ + { {-1, -1, -1, 0, 0, 0, 1, -1 } }; +const p256_int SECP256r1_b = /* curve b */ + { {0x27d2604b, 0x3bce3c3e, 0xcc53b0f6, 0x651d06b0, + 0x769886bc, 0xb3ebbd55, 0xaa3a93e7, 0x5ac635d8} }; +static const p256_int p256_one = P256_ONE; + +void p256_init(p256_int *a) +{ + memset(a, 0, sizeof(*a)); +} + +int p256_get_bit(const p256_int *scalar, int bit) +{ + return (P256_DIGIT(scalar, bit / P256_BITSPERDIGIT) + >> (bit & (P256_BITSPERDIGIT - 1))) & 1; +} + +p256_digit p256_shl(const p256_int *a, int n, p256_int *b) +{ + int i; + p256_digit top = P256_DIGIT(a, P256_NDIGITS - 1); + + n %= P256_BITSPERDIGIT; + for (i = P256_NDIGITS - 1; i > 0; --i) { + p256_digit accu = (P256_DIGIT(a, i) << n); + + accu |= (P256_DIGIT(a, i - 1) >> (P256_BITSPERDIGIT - n)); + P256_DIGIT(b, i) = accu; + } + P256_DIGIT(b, i) = (P256_DIGIT(a, i) << n); + + top >>= (P256_BITSPERDIGIT - n); + + return top; +} + +void p256_shr(const p256_int *a, int n, p256_int *b) +{ + int i; + + n %= P256_BITSPERDIGIT; + for (i = 0; i < P256_NDIGITS - 1; ++i) { + p256_digit accu = (P256_DIGIT(a, i) >> n); + + accu |= (P256_DIGIT(a, i + 1) << (P256_BITSPERDIGIT - n)); + P256_DIGIT(b, i) = accu; + } + P256_DIGIT(b, i) = (P256_DIGIT(a, i) >> n); +} + +int p256_is_zero(const p256_int *a) +{ + int i, result = 0; + + for (i = 0; i < P256_NDIGITS; ++i) + result |= P256_DIGIT(a, i); + return !result; +} + +int p256_cmp(const p256_int *a, const p256_int *b) +{ + int i; + p256_sddigit borrow = 0; + p256_digit notzero = 0; + + for (i = 0; i < P256_NDIGITS; ++i) { + borrow += (p256_sddigit) P256_DIGIT(a, i) - P256_DIGIT(b, i); + /* Track whether any result digit is ever not zero. + * Relies on !!(non-zero) evaluating to 1, e.g., !!(-1) + * evaluating to 1. */ + notzero |= !!((p256_digit) borrow); + borrow >>= P256_BITSPERDIGIT; + } + return (int) borrow | notzero; +} + +/* c = a - b. Returns borrow: 0 or -1. */ +int p256_sub(const p256_int *a, const p256_int *b, p256_int *c) +{ + int i; + p256_sddigit borrow = 0; + + for (i = 0; i < P256_NDIGITS; ++i) { + borrow += (p256_sddigit) P256_DIGIT(a, i) - P256_DIGIT(b, i); + if (c) + P256_DIGIT(c, i) = (p256_digit) borrow; + borrow >>= P256_BITSPERDIGIT; + } + return (int) borrow; +} + +/* c = a + b. Returns carry: 0 or 1. */ +int p256_add(const p256_int *a, const p256_int *b, p256_int *c) +{ + int i; + p256_ddigit carry = 0; + + for (i = 0; i < P256_NDIGITS; ++i) { + carry += (p256_ddigit) P256_DIGIT(a, i) + P256_DIGIT(b, i); + if (c) + P256_DIGIT(c, i) = (p256_digit) carry; + carry >>= P256_BITSPERDIGIT; + } + return (int) carry; +} + +/* b = a + d. Returns carry, 0 or 1. */ +int p256_add_d(const p256_int *a, p256_digit d, p256_int *b) +{ + int i; + p256_ddigit carry = d; + + for (i = 0; i < P256_NDIGITS; ++i) { + carry += (p256_ddigit) P256_DIGIT(a, i); + if (b) + P256_DIGIT(b, i) = (p256_digit) carry; + carry >>= P256_BITSPERDIGIT; + } + return (int) carry; +} + +/* top, c[] += a[] * b */ +/* Returns new top. */ +static p256_digit p256_muladd(const p256_int *a, p256_digit b, + p256_digit top, p256_digit *c) +{ + int i; + p256_ddigit carry = 0; + + for (i = 0; i < P256_NDIGITS; ++i) { + carry += *c; + carry += (p256_ddigit) P256_DIGIT(a, i) * b; + *c++ = (p256_digit) carry; + carry >>= P256_BITSPERDIGIT; + } + return top + (p256_digit) carry; +} + +/* top, c[] -= top_a, a[] */ +static p256_digit p256_subtop(p256_digit top_a, const p256_digit *a, + p256_digit top_c, p256_digit *c) +{ + int i; + p256_sddigit borrow = 0; + + for (i = 0; i < P256_NDIGITS; ++i) { + borrow += *c; + borrow -= *a++; + *c++ = (p256_digit) borrow; + borrow >>= P256_BITSPERDIGIT; + } + borrow += top_c; + borrow -= top_a; + top_c = (p256_digit) borrow; + assert((borrow >> P256_BITSPERDIGIT) == 0); + return top_c; +} + +/* top, c[] += MOD[] & mask (0 or -1) */ +/* returns new top. */ +static p256_digit p256_addM(const p256_int *MOD, p256_digit top, + p256_digit *c, p256_digit mask) +{ + int i; + p256_ddigit carry = 0; + + for (i = 0; i < P256_NDIGITS; ++i) { + carry += *c; + carry += P256_DIGIT(MOD, i) & mask; + *c++ = (p256_digit) carry; + carry >>= P256_BITSPERDIGIT; + } + return top + (p256_digit) carry; +} + +/* top, c[] -= MOD[] & mask (0 or -1) */ +/* returns new top. */ +static p256_digit p256_subM(const p256_int *MOD, p256_digit top, + p256_digit *c, p256_digit mask) +{ + int i; + p256_sddigit borrow = 0; + + for (i = 0; i < P256_NDIGITS; ++i) { + borrow += *c; + borrow -= P256_DIGIT(MOD, i) & mask; + *c++ = (p256_digit) borrow; + borrow >>= P256_BITSPERDIGIT; + } + return top + (p256_digit) borrow; +} + +/* Convert in. */ +void p256_from_bin(const uint8_t src[P256_NBYTES], p256_int *dst) +{ + int i; + const uint8_t *p = &src[0]; + + for (i = P256_NDIGITS - 1; i >= 0; --i) { + P256_DIGIT(dst, i) = + (p[0] << 24) | + (p[1] << 16) | + (p[2] << 8) | + p[3]; + p += 4; + } +} + +void p256_mod(const p256_int *MOD, const p256_int *in, p256_int *out) +{ + if (out != in) + *out = *in; + p256_addM(MOD, 0, P256_DIGITS(out), + p256_subM(MOD, 0, P256_DIGITS(out), -1)); +} + + +void p256_modmul(const p256_int *MOD, const p256_int *a, + const p256_digit top_b, const p256_int *b, p256_int *c) +{ + p256_digit tmp[P256_NDIGITS * 2 + 1] = { 0 }; + p256_digit top = 0; + int i; + + /* Multiply/add into tmp. */ + for (i = 0; i < P256_NDIGITS; ++i) { + if (i) + tmp[i + P256_NDIGITS - 1] = top; + top = p256_muladd(a, P256_DIGIT(b, i), 0, tmp + i); + } + + /* Multiply/add top digit. */ + tmp[i + P256_NDIGITS - 1] = top; + top = p256_muladd(a, top_b, 0, tmp + i); + + /* Reduce tmp, digit by digit. */ + for (; i >= 0; --i) { + p256_digit reducer[P256_NDIGITS] = { 0 }; + p256_digit top_reducer; + + /* top can be any value at this point. + * Guestimate reducer as top * MOD, since msw of MOD is -1. */ + top_reducer = p256_muladd(MOD, top, 0, reducer); + + /* Subtract reducer from top | tmp. */ + top = p256_subtop(top_reducer, reducer, top, tmp + i); + + /* top is now either 0 or 1. Make it 0, fixed-timing. */ + assert(top <= 1); + + top = p256_subM(MOD, top, tmp + i, ~(top - 1)); + + assert(top == 0); + + /* We have now reduced the top digit off tmp. Fetch + * new top digit. */ + top = tmp[i + P256_NDIGITS - 1]; + } + + /* tmp might still be larger than MOD, yet same bit length. + * Make sure it is less, fixed-timing. */ + p256_addM(MOD, 0, tmp, p256_subM(MOD, 0, tmp, -1)); + + memcpy(c, tmp, P256_NBYTES); +} + +/* if (mask) dst = src, fixed-timing style. */ +static void conditional_copy(const p256_int *src, p256_int *dst, int mask) +{ + int i; + + for (i = 0; i < P256_NDIGITS; ++i) { + p256_digit b = P256_DIGIT(src, i) & mask; /* 0 or src[i] */ + + b |= P256_DIGIT(dst, i) & ~mask; /* dst[i] or 0 */ + P256_DIGIT(dst, i) = b; + } +} + +/* -1 iff (x & 15) == 0, 0 otherwise. */ +/* Relies on arithmetic shift right behavior. */ +#define ZEROtoONES(x) (((int32_t)(((x) & 15) - 1)) >> 31) + +/* tbl[0] = tbl[idx], fixed-timing style. */ +static void set0ToIdx(p256_int tbl[16], int idx) +{ + int32_t i; + + tbl[0] = p256_one; + for (i = 1; i < 16; ++i) + conditional_copy(&tbl[i], &tbl[0], ZEROtoONES(i - idx)); +} + +/* b = 1/a mod MOD, fixed timing, Fermat's little theorem. */ +void p256_modinv(const p256_int *MOD, const p256_int *a, p256_int *b) +{ + int i; + p256_int tbl[16]; + + /* tbl[i] = a**i, tbl[0] unused. */ + tbl[1] = *a; + for (i = 2; i < 16; ++i) + p256_modmul(MOD, &tbl[i-1], 0, a, &tbl[i]); + + *b = p256_one; + for (i = 256; i > 0; i -= 4) { + int32_t idx = 0; + + p256_modmul(MOD, b, 0, b, b); + p256_modmul(MOD, b, 0, b, b); + p256_modmul(MOD, b, 0, b, b); + p256_modmul(MOD, b, 0, b, b); + idx |= p256_get_bit(&SECP256r1_nMin2, i - 1) << 3; + idx |= p256_get_bit(&SECP256r1_nMin2, i - 2) << 2; + idx |= p256_get_bit(&SECP256r1_nMin2, i - 3) << 1; + idx |= p256_get_bit(&SECP256r1_nMin2, i - 4) << 0; + set0ToIdx(tbl, idx); /* tbl[0] = tbl[idx] */ + p256_modmul(MOD, b, 0, &tbl[0], &tbl[0]); + conditional_copy(&tbl[0], b, ~ZEROtoONES(idx)); + } +} + +static int p256_is_even(const p256_int *a) +{ + return !(P256_DIGIT(a, 0) & 1); +} + +static void p256_shr1(const p256_int *a, int highbit, p256_int *b) +{ + int i; + + for (i = 0; i < P256_NDIGITS - 1; ++i) { + p256_digit accu = (P256_DIGIT(a, i) >> 1); + + accu |= (P256_DIGIT(a, i + 1) << (P256_BITSPERDIGIT - 1)); + P256_DIGIT(b, i) = accu; + } + P256_DIGIT(b, i) = (P256_DIGIT(a, i) >> 1) | + (highbit << (P256_BITSPERDIGIT - 1)); +} + +/* b = 1/a mod MOD, binary euclid. */ +void p256_modinv_vartime(const p256_int *MOD, const p256_int *a, p256_int *b) +{ + p256_int R = P256_ZERO; + p256_int S = P256_ONE; + p256_int U = *MOD; + p256_int V = *a; + + for (;;) { + if (p256_is_even(&U)) { + p256_shr1(&U, 0, &U); + if (p256_is_even(&R)) { + p256_shr1(&R, 0, &R); + } else { + /* R = (R + MOD)/2 */ + p256_shr1(&R, p256_add(&R, MOD, &R), &R); + } + } else if (p256_is_even(&V)) { + p256_shr1(&V, 0, &V); + if (p256_is_even(&S)) { + p256_shr1(&S, 0, &S); + } else { + /* S = (S + MOD)/2 */ + p256_shr1(&S, p256_add(&S, MOD, &S) , &S); + } + } else { /* U, V both odd. */ + if (!p256_sub(&V, &U, NULL)) { + p256_sub(&V, &U, &V); + if (p256_sub(&S, &R, &S)) + p256_add(&S, MOD, &S); + if (p256_is_zero(&V)) + break; /* done. */ + } else { + p256_sub(&U, &V, &U); + if (p256_sub(&R, &S, &R)) + p256_add(&R, MOD, &R); + } + } + } + + p256_mod(MOD, &R, b); +} + +int DCRYPTO_p256_valid_point(const p256_int *x, const p256_int *y) +{ + p256_int y2, x3; + + if (p256_cmp(&SECP256r1_p, x) <= 0 || p256_cmp(&SECP256r1_p, y) <= 0 || + p256_is_zero(x) || p256_is_zero(y)) + return 0; + + p256_modmul(&SECP256r1_p, y, 0, y, &y2); /* y^2 */ + + p256_modmul(&SECP256r1_p, x, 0, x, &x3); /* x^2 */ + p256_modmul(&SECP256r1_p, x, 0, &x3, &x3); /* x^3 */ + if (p256_sub(&x3, x, &x3)) + p256_add(&x3, &SECP256r1_p, &x3); /* x^3 - x */ + if (p256_sub(&x3, x, &x3)) + p256_add(&x3, &SECP256r1_p, &x3); /* x^3 - 2x */ + if (p256_sub(&x3, x, &x3)) + p256_add(&x3, &SECP256r1_p, &x3); /* x^3 - 3x */ + if (p256_add(&x3, &SECP256r1_b, &x3)) /* x^3 - 3x + b */ + p256_sub(&x3, &SECP256r1_p, &x3); + if (p256_sub(&x3, &SECP256r1_p, &x3)) /* make sure 0 <= x3 < p */ + p256_add(&x3, &SECP256r1_p, &x3); + + return p256_cmp(&y2, &x3) == 0; +} + +/* + * Key selection based on FIPS-186-4, section B.4.2 (Key Pair + * Generation by Testing Candidates). + */ +int DCRYPTO_p256_key_from_bytes(p256_int *x, p256_int *y, p256_int *d, + const uint8_t key_bytes[P256_NBYTES]) +{ + int valid; + p256_int key; + + p256_from_bin(key_bytes, &key); + if (p256_cmp(&SECP256r1_nMin2, &key) < 0) + return 0; + p256_add(&key, &p256_one, &key); + valid = DCRYPTO_p256_base_point_mul(x, y, &key); + if (valid) + *d = key; + return valid; +} diff --git a/chip/g/dcrypto/p256_ec.c b/chip/g/dcrypto/p256_ec.c new file mode 100644 index 0000000000..8bb4a0e308 --- /dev/null +++ b/chip/g/dcrypto/p256_ec.c @@ -0,0 +1,1403 @@ +/* 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 <stdint.h> + +#include "dcrypto.h" + +typedef uint8_t u8; +typedef uint32_t u32; +typedef int32_t s32; +typedef uint64_t u64; + +/* Our field elements are represented as nine 32-bit limbs. + * + * The value of an felem (field element) is: + * x[0] + (x[1] * 2**29) + (x[2] * 2**57) + ... + (x[8] * 2**228) + * + * That is, each limb is alternately 29 or 28-bits wide in little-endian + * order. + * + * This means that an felem hits 2**257, rather than 2**256 as we would like. A + * 28, 29, ... pattern would cause us to hit 2**256, but that causes problems + * when multiplying as terms end up one bit short of a limb which would require + * much bit-shifting to correct. + * + * Finally, the values stored in an felem are in Montgomery form. So the value + * |y| is stored as (y*R) mod p, where p is the P-256 prime and R is 2**257. + */ +typedef u32 limb; +#define NLIMBS 9 +typedef limb felem[NLIMBS]; + +static const limb kBottom28Bits = 0xfffffff; +static const limb kBottom29Bits = 0x1fffffff; + +/* kOne is the number 1 as an felem. It's 2**257 mod p split up into 29 and + * 28-bit words. */ +static const felem kOne = { + 2, 0, 0, 0xffff800, + 0x1fffffff, 0xfffffff, 0x1fbfffff, 0x1ffffff, + 0 +}; +static const felem kZero = {0}; +static const felem kP = { + 0x1fffffff, 0xfffffff, 0x1fffffff, 0x3ff, + 0, 0, 0x200000, 0xf000000, + 0xfffffff +}; +static const felem k2P = { + 0x1ffffffe, 0xfffffff, 0x1fffffff, 0x7ff, + 0, 0, 0x400000, 0xe000000, + 0x1fffffff +}; +/* kPrecomputed contains precomputed values to aid the calculation of scalar + * multiples of the base point, G. It's actually two, equal length, tables + * concatenated. + * + * The first table contains (x,y) felem pairs for 16 multiples of the base + * point, G. + * + * Index | Index (binary) | Value + * 0 | 0000 | 0G (all zeros, omitted) + * 1 | 0001 | G + * 2 | 0010 | 2**64G + * 3 | 0011 | 2**64G + G + * 4 | 0100 | 2**128G + * 5 | 0101 | 2**128G + G + * 6 | 0110 | 2**128G + 2**64G + * 7 | 0111 | 2**128G + 2**64G + G + * 8 | 1000 | 2**192G + * 9 | 1001 | 2**192G + G + * 10 | 1010 | 2**192G + 2**64G + * 11 | 1011 | 2**192G + 2**64G + G + * 12 | 1100 | 2**192G + 2**128G + * 13 | 1101 | 2**192G + 2**128G + G + * 14 | 1110 | 2**192G + 2**128G + 2**64G + * 15 | 1111 | 2**192G + 2**128G + 2**64G + G + * + * The second table follows the same style, but the terms are 2**32G, + * 2**96G, 2**160G, 2**224G. + * + * This is ~2KB of data. */ +static const limb kPrecomputed[NLIMBS * 2 * 15 * 2] = { + 0x11522878, 0xe730d41, 0xdb60179, 0x4afe2ff, 0x12883add, 0xcaddd88, + 0x119e7edc, 0xd4a6eab, 0x3120bee, 0x1d2aac15, 0xf25357c, 0x19e45cdd, + 0x5c721d0, 0x1992c5a5, 0xa237487, 0x154ba21, 0x14b10bb, 0xae3fe3, + 0xd41a576, 0x922fc51, 0x234994f, 0x60b60d3, 0x164586ae, 0xce95f18, + 0x1fe49073, 0x3fa36cc, 0x5ebcd2c, 0xb402f2f, 0x15c70bf, 0x1561925c, + 0x5a26704, 0xda91e90, 0xcdc1c7f, 0x1ea12446, 0xe1ade1e, 0xec91f22, + 0x26f7778, 0x566847e, 0xa0bec9e, 0x234f453, 0x1a31f21a, 0xd85e75c, + 0x56c7109, 0xa267a00, 0xb57c050, 0x98fb57, 0xaa837cc, 0x60c0792, + 0xcfa5e19, 0x61bab9e, 0x589e39b, 0xa324c5, 0x7d6dee7, 0x2976e4b, + 0x1fc4124a, 0xa8c244b, 0x1ce86762, 0xcd61c7e, 0x1831c8e0, 0x75774e1, + 0x1d96a5a9, 0x843a649, 0xc3ab0fa, 0x6e2e7d5, 0x7673a2a, 0x178b65e8, + 0x4003e9b, 0x1a1f11c2, 0x7816ea, 0xf643e11, 0x58c43df, 0xf423fc2, + 0x19633ffa, 0x891f2b2, 0x123c231c, 0x46add8c, 0x54700dd, 0x59e2b17, + 0x172db40f, 0x83e277d, 0xb0dd609, 0xfd1da12, 0x35c6e52, 0x19ede20c, + 0xd19e0c0, 0x97d0f40, 0xb015b19, 0x449e3f5, 0xe10c9e, 0x33ab581, + 0x56a67ab, 0x577734d, 0x1dddc062, 0xc57b10d, 0x149b39d, 0x26a9e7b, + 0xc35df9f, 0x48764cd, 0x76dbcca, 0xca4b366, 0xe9303ab, 0x1a7480e7, + 0x57e9e81, 0x1e13eb50, 0xf466cf3, 0x6f16b20, 0x4ba3173, 0xc168c33, + 0x15cb5439, 0x6a38e11, 0x73658bd, 0xb29564f, 0x3f6dc5b, 0x53b97e, + 0x1322c4c0, 0x65dd7ff, 0x3a1e4f6, 0x14e614aa, 0x9246317, 0x1bc83aca, + 0xad97eed, 0xd38ce4a, 0xf82b006, 0x341f077, 0xa6add89, 0x4894acd, + 0x9f162d5, 0xf8410ef, 0x1b266a56, 0xd7f223, 0x3e0cb92, 0xe39b672, + 0x6a2901a, 0x69a8556, 0x7e7c0, 0x9b7d8d3, 0x309a80, 0x1ad05f7f, + 0xc2fb5dd, 0xcbfd41d, 0x9ceb638, 0x1051825c, 0xda0cf5b, 0x812e881, + 0x6f35669, 0x6a56f2c, 0x1df8d184, 0x345820, 0x1477d477, 0x1645db1, + 0xbe80c51, 0xc22be3e, 0xe35e65a, 0x1aeb7aa0, 0xc375315, 0xf67bc99, + 0x7fdd7b9, 0x191fc1be, 0x61235d, 0x2c184e9, 0x1c5a839, 0x47a1e26, + 0xb7cb456, 0x93e225d, 0x14f3c6ed, 0xccc1ac9, 0x17fe37f3, 0x4988989, + 0x1a90c502, 0x2f32042, 0xa17769b, 0xafd8c7c, 0x8191c6e, 0x1dcdb237, + 0x16200c0, 0x107b32a1, 0x66c08db, 0x10d06a02, 0x3fc93, 0x5620023, + 0x16722b27, 0x68b5c59, 0x270fcfc, 0xfad0ecc, 0xe5de1c2, 0xeab466b, + 0x2fc513c, 0x407f75c, 0xbaab133, 0x9705fe9, 0xb88b8e7, 0x734c993, + 0x1e1ff8f, 0x19156970, 0xabd0f00, 0x10469ea7, 0x3293ac0, 0xcdc98aa, + 0x1d843fd, 0xe14bfe8, 0x15be825f, 0x8b5212, 0xeb3fb67, 0x81cbd29, + 0xbc62f16, 0x2b6fcc7, 0xf5a4e29, 0x13560b66, 0xc0b6ac2, 0x51ae690, + 0xd41e271, 0xf3e9bd4, 0x1d70aab, 0x1029f72, 0x73e1c35, 0xee70fbc, + 0xad81baf, 0x9ecc49a, 0x86c741e, 0xfe6be30, 0x176752e7, 0x23d416, + 0x1f83de85, 0x27de188, 0x66f70b8, 0x181cd51f, 0x96b6e4c, 0x188f2335, + 0xa5df759, 0x17a77eb6, 0xfeb0e73, 0x154ae914, 0x2f3ec51, 0x3826b59, + 0xb91f17d, 0x1c72949, 0x1362bf0a, 0xe23fddf, 0xa5614b0, 0xf7d8f, + 0x79061, 0x823d9d2, 0x8213f39, 0x1128ae0b, 0xd095d05, 0xb85c0c2, + 0x1ecb2ef, 0x24ddc84, 0xe35e901, 0x18411a4a, 0xf5ddc3d, 0x3786689, + 0x52260e8, 0x5ae3564, 0x542b10d, 0x8d93a45, 0x19952aa4, 0x996cc41, + 0x1051a729, 0x4be3499, 0x52b23aa, 0x109f307e, 0x6f5b6bb, 0x1f84e1e7, + 0x77a0cfa, 0x10c4df3f, 0x25a02ea, 0xb048035, 0xe31de66, 0xc6ecaa3, + 0x28ea335, 0x2886024, 0x1372f020, 0xf55d35, 0x15e4684c, 0xf2a9e17, + 0x1a4a7529, 0xcb7beb1, 0xb2a78a1, 0x1ab21f1f, 0x6361ccf, 0x6c9179d, + 0xb135627, 0x1267b974, 0x4408bad, 0x1cbff658, 0xe3d6511, 0xc7d76f, + 0x1cc7a69, 0xe7ee31b, 0x54fab4f, 0x2b914f, 0x1ad27a30, 0xcd3579e, + 0xc50124c, 0x50daa90, 0xb13f72, 0xb06aa75, 0x70f5cc6, 0x1649e5aa, + 0x84a5312, 0x329043c, 0x41c4011, 0x13d32411, 0xb04a838, 0xd760d2d, + 0x1713b532, 0xbaa0c03, 0x84022ab, 0x6bcf5c1, 0x2f45379, 0x18ae070, + 0x18c9e11e, 0x20bca9a, 0x66f496b, 0x3eef294, 0x67500d2, 0xd7f613c, + 0x2dbbeb, 0xb741038, 0xe04133f, 0x1582968d, 0xbe985f7, 0x1acbc1a, + 0x1a6a939f, 0x33e50f6, 0xd665ed4, 0xb4b7bd6, 0x1e5a3799, 0x6b33847, + 0x17fa56ff, 0x65ef930, 0x21dc4a, 0x2b37659, 0x450fe17, 0xb357b65, + 0xdf5efac, 0x15397bef, 0x9d35a7f, 0x112ac15f, 0x624e62e, 0xa90ae2f, + 0x107eecd2, 0x1f69bbe, 0x77d6bce, 0x5741394, 0x13c684fc, 0x950c910, + 0x725522b, 0xdc78583, 0x40eeabb, 0x1fde328a, 0xbd61d96, 0xd28c387, + 0x9e77d89, 0x12550c40, 0x759cb7d, 0x367ef34, 0xae2a960, 0x91b8bdc, + 0x93462a9, 0xf469ef, 0xb2e9aef, 0xd2ca771, 0x54e1f42, 0x7aaa49, + 0x6316abb, 0x2413c8e, 0x5425bf9, 0x1bed3e3a, 0xf272274, 0x1f5e7326, + 0x6416517, 0xea27072, 0x9cedea7, 0x6e7633, 0x7c91952, 0xd806dce, + 0x8e2a7e1, 0xe421e1a, 0x418c9e1, 0x1dbc890, 0x1b395c36, 0xa1dc175, + 0x1dc4ef73, 0x8956f34, 0xe4b5cf2, 0x1b0d3a18, 0x3194a36, 0x6c2641f, + 0xe44124c, 0xa2f4eaa, 0xa8c25ba, 0xf927ed7, 0x627b614, 0x7371cca, + 0xba16694, 0x417bc03, 0x7c0a7e3, 0x9c35c19, 0x1168a205, 0x8b6b00d, + 0x10e3edc9, 0x9c19bf2, 0x5882229, 0x1b2b4162, 0xa5cef1a, 0x1543622b, + 0x9bd433e, 0x364e04d, 0x7480792, 0x5c9b5b3, 0xe85ff25, 0x408ef57, + 0x1814cfa4, 0x121b41b, 0xd248a0f, 0x3b05222, 0x39bb16a, 0xc75966d, + 0xa038113, 0xa4a1769, 0x11fbc6c, 0x917e50e, 0xeec3da8, 0x169d6eac, + 0x10c1699, 0xa416153, 0xf724912, 0x15cd60b7, 0x4acbad9, 0x5efc5fa, + 0xf150ed7, 0x122b51, 0x1104b40a, 0xcb7f442, 0xfbb28ff, 0x6ac53ca, + 0x196142cc, 0x7bf0fa9, 0x957651, 0x4e0f215, 0xed439f8, 0x3f46bd5, + 0x5ace82f, 0x110916b6, 0x6db078, 0xffd7d57, 0xf2ecaac, 0xca86dec, + 0x15d6b2da, 0x965ecc9, 0x1c92b4c2, 0x1f3811, 0x1cb080f5, 0x2d8b804, + 0x19d1c12d, 0xf20bd46, 0x1951fa7, 0xa3656c3, 0x523a425, 0xfcd0692, + 0xd44ddc8, 0x131f0f5b, 0xaf80e4a, 0xcd9fc74, 0x99bb618, 0x2db944c, + 0xa673090, 0x1c210e1, 0x178c8d23, 0x1474383, 0x10b8743d, 0x985a55b, + 0x2e74779, 0x576138, 0x9587927, 0x133130fa, 0xbe05516, 0x9f4d619, + 0xbb62570, 0x99ec591, 0xd9468fe, 0x1d07782d, 0xfc72e0b, 0x701b298, + 0x1863863b, 0x85954b8, 0x121a0c36, 0x9e7fedf, 0xf64b429, 0x9b9d71e, + 0x14e2f5d8, 0xf858d3a, 0x942eea8, 0xda5b765, 0x6edafff, 0xa9d18cc, + 0xc65e4ba, 0x1c747e86, 0xe4ea915, 0x1981d7a1, 0x8395659, 0x52ed4e2, + 0x87d43b7, 0x37ab11b, 0x19d292ce, 0xf8d4692, 0x18c3053f, 0x8863e13, + 0x4c146c0, 0x6bdf55a, 0x4e4457d, 0x16152289, 0xac78ec2, 0x1a59c5a2, + 0x2028b97, 0x71c2d01, 0x295851f, 0x404747b, 0x878558d, 0x7d29aa4, + 0x13d8341f, 0x8daefd7, 0x139c972d, 0x6b7ea75, 0xd4a9dde, 0xff163d8, + 0x81d55d7, 0xa5bef68, 0xb7b30d8, 0xbe73d6f, 0xaa88141, 0xd976c81, + 0x7e7a9cc, 0x18beb771, 0xd773cbd, 0x13f51951, 0x9d0c177, 0x1c49a78, +}; + + +/* Field element operations: */ + +/* NON_ZERO_TO_ALL_ONES returns: + * 0xffffffff for 0 < x <= 2**31 + * 0 for x == 0 or x > 2**31. + * + * x must be a u32 or an equivalent type such as limb. */ +#define NON_ZERO_TO_ALL_ONES(x) ((((u32)(x) - 1) >> 31) - 1) + +/* felem_reduce_carry adds a multiple of p in order to cancel |carry|, + * which is a term at 2**257. + * + * On entry: carry < 2**3, inout[0,2,...] < 2**29, inout[1,3,...] < 2**28. + * On exit: inout[0,2,..] < 2**30, inout[1,3,...] < 2**29. */ +static void felem_reduce_carry(felem inout, limb carry) +{ + const u32 carry_mask = NON_ZERO_TO_ALL_ONES(carry); + + inout[0] += carry << 1; + inout[3] += 0x10000000 & carry_mask; + /* carry < 2**3 thus (carry << 11) < 2**14 and we added 2**28 in the + * previous line therefore this doesn't underflow. */ + inout[3] -= carry << 11; + inout[4] += (0x20000000 - 1) & carry_mask; + inout[5] += (0x10000000 - 1) & carry_mask; + inout[6] += (0x20000000 - 1) & carry_mask; + inout[6] -= carry << 22; + /* This may underflow if carry is non-zero but, if so, we'll + * fix it in the next line. */ + inout[7] -= 1 & carry_mask; + inout[7] += carry << 25; +} + +/* felem_sum sets out = in+in2. + * + * On entry, in[i]+in2[i] must not overflow a 32-bit word. + * On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29 */ +static void felem_sum(felem out, const felem in, const felem in2) +{ + limb carry = 0; + unsigned i; + + for (i = 0;; i++) { + out[i] = in[i] + in2[i]; + out[i] += carry; + carry = out[i] >> 29; + out[i] &= kBottom29Bits; + + i++; + if (i == NLIMBS) + break; + + out[i] = in[i] + in2[i]; + out[i] += carry; + carry = out[i] >> 28; + out[i] &= kBottom28Bits; + } + + felem_reduce_carry(out, carry); +} + +#define two31m3 ((((limb)1) << 31) - (((limb)1) << 3)) +#define two30m2 ((((limb)1) << 30) - (((limb)1) << 2)) +#define two30p13m2 ((((limb)1) << 30) + (((limb)1) << 13) - (((limb)1) << 2)) +#define two31m2 ((((limb)1) << 31) - (((limb)1) << 2)) +#define two31p24m2 ((((limb)1) << 31) + (((limb)1) << 24) - (((limb)1) << 2)) +#define two30m27m2 ((((limb)1) << 30) - (((limb)1) << 27) - (((limb)1) << 2)) + +/* zero31 is 0 mod p. */ +static const felem zero31 = { two31m3, two30m2, two31m2, two30p13m2, two31m2, + two30m2, two31p24m2, two30m27m2, two31m2 }; + +/* felem_diff sets out = in-in2. + * + * On entry: in[0,2,...] < 2**30, in[1,3,...] < 2**29 and + * in2[0,2,...] < 2**30, in2[1,3,...] < 2**29. + * On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29. */ +static void felem_diff(felem out, const felem in, const felem in2) +{ + limb carry = 0; + unsigned i; + + for (i = 0;; i++) { + out[i] = in[i] - in2[i]; + out[i] += zero31[i]; + out[i] += carry; + carry = out[i] >> 29; + out[i] &= kBottom29Bits; + + i++; + if (i == NLIMBS) + break; + + out[i] = in[i] - in2[i]; + out[i] += zero31[i]; + out[i] += carry; + carry = out[i] >> 28; + out[i] &= kBottom28Bits; + } + + felem_reduce_carry(out, carry); +} + +/* felem_reduce_degree sets out = tmp/R mod p where tmp contains 64-bit words + * with the same 29,28,... bit positions as an felem. + * + * The values in felems are in Montgomery form: x*R mod p where R = 2**257. + * Since we just multiplied two Montgomery values together, the result is + * x*y*R*R mod p. We wish to divide by R in order for the result also to be + * in Montgomery form. + * + * On entry: tmp[i] < 2**64 + * On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29 */ +static void felem_reduce_degree(felem out, u64 tmp[17]) +{ + /* The following table may be helpful when reading this code: + * + * Limb number: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10... + * Width (bits): 29| 28| 29| 28| 29| 28| 29| 28| 29| 28| 29 + * Start bit: 0 | 29| 57| 86|114|143|171|200|228|257|285 + * (odd phase): 0 | 28| 57| 85|114|142|171|199|228|256|285 */ + limb tmp2[18], carry, x, xMask; + unsigned i; + + /* tmp contains 64-bit words with the same 29,28,29-bit positions as an + * felem. So the top of an element of tmp might overlap with another + * element two positions down. The following loop eliminates this + * overlap. */ + tmp2[0] = (limb)(tmp[0] & kBottom29Bits); + + /* In the following we use "(limb) tmp[x]" and "(limb) + * (tmp[x]>>32)" to try and hint to the compiler that it can + * do a single-word shift by selecting the right register + * rather than doing a double-word shift and truncating + * afterwards. */ + tmp2[1] = ((limb) tmp[0]) >> 29; + tmp2[1] |= (((limb)(tmp[0] >> 32)) << 3) & kBottom28Bits; + tmp2[1] += ((limb) tmp[1]) & kBottom28Bits; + carry = tmp2[1] >> 28; + tmp2[1] &= kBottom28Bits; + + for (i = 2; i < 17; i++) { + tmp2[i] = ((limb)(tmp[i - 2] >> 32)) >> 25; + tmp2[i] += ((limb)(tmp[i - 1])) >> 28; + tmp2[i] += (((limb)(tmp[i - 1] >> 32)) << 4) & kBottom29Bits; + tmp2[i] += ((limb) tmp[i]) & kBottom29Bits; + tmp2[i] += carry; + carry = tmp2[i] >> 29; + tmp2[i] &= kBottom29Bits; + + i++; + if (i == 17) + break; + tmp2[i] = ((limb)(tmp[i - 2] >> 32)) >> 25; + tmp2[i] += ((limb)(tmp[i - 1])) >> 29; + tmp2[i] += (((limb)(tmp[i - 1] >> 32)) << 3) & kBottom28Bits; + tmp2[i] += ((limb) tmp[i]) & kBottom28Bits; + tmp2[i] += carry; + carry = tmp2[i] >> 28; + tmp2[i] &= kBottom28Bits; + } + + tmp2[17] = ((limb)(tmp[15] >> 32)) >> 25; + tmp2[17] += ((limb)(tmp[16])) >> 29; + tmp2[17] += (((limb)(tmp[16] >> 32)) << 3); + tmp2[17] += carry; + + /* Montgomery elimination of terms. + * + * Since R is 2**257, we can divide by R with a bitwise shift + * if we can ensure that the right-most 257 bits are all + * zero. We can make that true by adding multiplies of p + * without affecting the value. + * + * So we eliminate limbs from right to left. Since the bottom + * 29 bits of p are all ones, then by adding tmp2[0]*p to tmp2 + * we'll make tmp2[0] == 0. We can do that for 8 further + * limbs and then right shift to eliminate the extra factor of + * R. */ + for (i = 0;; i += 2) { + tmp2[i + 1] += tmp2[i] >> 29; + x = tmp2[i] & kBottom29Bits; + xMask = NON_ZERO_TO_ALL_ONES(x); + tmp2[i] = 0; + + /* The bounds calculations for this loop are + * tricky. Each iteration of the loop eliminates two + * words by adding values to words to their right. + * + * The following table contains the amounts added to + * each word (as an offset from the value of i at the + * top of the loop). The amounts are accounted for + * from the first and second half of the loop + * separately and are written as, for example, 28 to + * mean a value < 2**28. + * + * Word: 3 4 5 6 7 8 9 10 + * Added in top half: 28 11 29 21 29 28 + * 28 29 + * 29 + * Added in bottom half: 29 10 28 21 28 28 + * 29 + * + * The value that is currently offset 7 will be offset + * 5 for the next iteration and then offset 3 for the + * iteration after that. Therefore the total value + * added will be the values added at 7, 5 and 3. + * + * The following table accumulates these values. The + * sums at the bottom are written as, for example, + * 29+28, to mean a value < 2**29+2**28. + * + * Word: 3 4 5 6 7 8 9 10 11 12 13 + * 28 11 10 29 21 29 28 28 28 28 28 + * 29 28 11 28 29 28 29 28 29 28 + * 29 28 21 21 29 21 29 21 + * 10 29 28 21 28 21 28 + * 28 29 28 29 28 29 28 + * 11 10 29 10 29 10 + * 29 28 11 28 11 + * 29 29 + * -------------------------------------------- + * 30+ 31+ 30+ 31+ 30+ + * 28+ 29+ 28+ 29+ 21+ + * 21+ 28+ 21+ 28+ 10 + * 10 21+ 10 21+ + * 11 11 + * + * So the greatest amount is added to tmp2[10] and + * tmp2[12]. If tmp2[10/12] has an initial value of + * <2**29, then the maximum value will be < 2**31 + + * 2**30 + 2**28 + 2**21 + 2**11, which is < 2**32, as + * required. */ + tmp2[i + 3] += (x << 10) & kBottom28Bits; + tmp2[i + 4] += (x >> 18); + + tmp2[i + 6] += (x << 21) & kBottom29Bits; + tmp2[i + 7] += x >> 8; + + /* At position 200, which is the starting bit position + * for word 7, we have a factor of 0xf000000 = 2**28 - + * 2**24. */ + tmp2[i + 7] += 0x10000000 & xMask; + /* Word 7 is 28 bits wide, so the 2**28 term exactly + * hits word 8. */ + tmp2[i + 8] += (x - 1) & xMask; + tmp2[i + 7] -= (x << 24) & kBottom28Bits; + tmp2[i + 8] -= x >> 4; + + tmp2[i + 8] += 0x20000000 & xMask; + tmp2[i + 8] -= x; + tmp2[i + 8] += (x << 28) & kBottom29Bits; + tmp2[i + 9] += ((x >> 1) - 1) & xMask; + + if (i+1 == NLIMBS) + break; + tmp2[i + 2] += tmp2[i + 1] >> 28; + x = tmp2[i + 1] & kBottom28Bits; + xMask = NON_ZERO_TO_ALL_ONES(x); + tmp2[i + 1] = 0; + + tmp2[i + 4] += (x << 11) & kBottom29Bits; + tmp2[i + 5] += (x >> 18); + + tmp2[i + 7] += (x << 21) & kBottom28Bits; + tmp2[i + 8] += x >> 7; + + /* At position 199, which is the starting bit of the + * 8th word when dealing with a context starting on an + * odd word, we have a factor of 0x1e000000 = 2**29 - + * 2**25. Since we have not updated i, the 8th word + * from i+1 is i+8. */ + tmp2[i + 8] += 0x20000000 & xMask; + tmp2[i + 9] += (x - 1) & xMask; + tmp2[i + 8] -= (x << 25) & kBottom29Bits; + tmp2[i + 9] -= x >> 4; + + tmp2[i + 9] += 0x10000000 & xMask; + tmp2[i + 9] -= x; + tmp2[i + 10] += (x - 1) & xMask; + } + + /* We merge the right shift with a carry chain. The words + * above 2**257 have widths of 28,29,... which we need to + * correct when copying them down. */ + carry = 0; + for (i = 0; i < 8; i++) { + /* The maximum value of tmp2[i + 9] occurs on the + * first iteration and is < 2**30+2**29+2**28. Adding + * 2**29 (from tmp2[i + 10]) is therefore safe. */ + out[i] = tmp2[i + 9]; + out[i] += carry; + out[i] += (tmp2[i + 10] << 28) & kBottom29Bits; + carry = out[i] >> 29; + out[i] &= kBottom29Bits; + + i++; + out[i] = tmp2[i + 9] >> 1; + out[i] += carry; + carry = out[i] >> 28; + out[i] &= kBottom28Bits; + } + + out[8] = tmp2[17]; + out[8] += carry; + carry = out[8] >> 29; + out[8] &= kBottom29Bits; + + felem_reduce_carry(out, carry); +} + +/* felem_square sets out=in*in. + * + * On entry: in[0,2,...] < 2**30, in[1,3,...] < 2**29. + * On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29. */ +static void felem_square(felem out, const felem in) +{ + u64 tmp[17]; + + tmp[0] = ((u64) in[0]) * in[0]; + tmp[1] = ((u64) in[0]) * (in[1] << 1); + tmp[2] = ((u64) in[0]) * (in[2] << 1) + + ((u64) in[1]) * (in[1] << 1); + tmp[3] = ((u64) in[0]) * (in[3] << 1) + + ((u64) in[1]) * (in[2] << 1); + tmp[4] = ((u64) in[0]) * (in[4] << 1) + + ((u64) in[1]) * (in[3] << 2) + ((u64) in[2]) * in[2]; + tmp[5] = ((u64) in[0]) * (in[5] << 1) + ((u64) in[1]) * + (in[4] << 1) + ((u64) in[2]) * (in[3] << 1); + tmp[6] = ((u64) in[0]) * (in[6] << 1) + ((u64) in[1]) * + (in[5] << 2) + ((u64) in[2]) * (in[4] << 1) + + ((u64) in[3]) * (in[3] << 1); + tmp[7] = ((u64) in[0]) * (in[7] << 1) + ((u64) in[1]) * + (in[6] << 1) + ((u64) in[2]) * (in[5] << 1) + + ((u64) in[3]) * (in[4] << 1); + /* tmp[8] has the greatest value of 2**61 + 2**60 + 2**61 + + * 2**60 + 2**60, which is < 2**64 as required. */ + tmp[8] = ((u64) in[0]) * (in[8] << 1) + ((u64) in[1]) * + (in[7] << 2) + ((u64) in[2]) * (in[6] << 1) + + ((u64) in[3]) * (in[5] << 2) + ((u64) in[4]) * in[4]; + tmp[9] = ((u64) in[1]) * (in[8] << 1) + ((u64) in[2]) * + (in[7] << 1) + ((u64) in[3]) * (in[6] << 1) + + ((u64) in[4]) * (in[5] << 1); + tmp[10] = ((u64) in[2]) * (in[8] << 1) + ((u64) in[3]) * + (in[7] << 2) + ((u64) in[4]) * (in[6] << 1) + + ((u64) in[5]) * (in[5] << 1); + tmp[11] = ((u64) in[3]) * (in[8] << 1) + ((u64) in[4]) * + (in[7] << 1) + ((u64) in[5]) * (in[6] << 1); + tmp[12] = ((u64) in[4]) * (in[8] << 1) + + ((u64) in[5]) * (in[7] << 2) + ((u64) in[6]) * in[6]; + tmp[13] = ((u64) in[5]) * (in[8] << 1) + + ((u64) in[6]) * (in[7] << 1); + tmp[14] = ((u64) in[6]) * (in[8] << 1) + + ((u64) in[7]) * (in[7] << 1); + tmp[15] = ((u64) in[7]) * (in[8] << 1); + tmp[16] = ((u64) in[8]) * in[8]; + + felem_reduce_degree(out, tmp); +} + +/* felem_mul sets out=in*in2. + * + * On entry: in[0,2,...] < 2**30, in[1,3,...] < 2**29 and + * in2[0,2,...] < 2**30, in2[1,3,...] < 2**29. + * On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29. */ +static void felem_mul(felem out, const felem in, const felem in2) +{ + u64 tmp[17]; + + tmp[0] = ((u64) in[0]) * in2[0]; + tmp[1] = ((u64) in[0]) * (in2[1] << 0) + + ((u64) in[1]) * (in2[0] << 0); + tmp[2] = ((u64) in[0]) * (in2[2] << 0) + ((u64) in[1]) * + (in2[1] << 1) + ((u64) in[2]) * (in2[0] << 0); + tmp[3] = ((u64) in[0]) * (in2[3] << 0) + ((u64) in[1]) * + (in2[2] << 0) + ((u64) in[2]) * (in2[1] << 0) + + ((u64) in[3]) * (in2[0] << 0); + tmp[4] = ((u64) in[0]) * (in2[4] << 0) + ((u64) in[1]) * + (in2[3] << 1) + ((u64) in[2]) * (in2[2] << 0) + + ((u64) in[3]) * (in2[1] << 1) + + ((u64) in[4]) * (in2[0] << 0); + tmp[5] = ((u64) in[0]) * (in2[5] << 0) + ((u64) in[1]) * + (in2[4] << 0) + ((u64) in[2]) * (in2[3] << 0) + + ((u64) in[3]) * (in2[2] << 0) + ((u64) in[4]) * + (in2[1] << 0) + ((u64) in[5]) * (in2[0] << 0); + tmp[6] = ((u64) in[0]) * (in2[6] << 0) + ((u64) in[1]) * + (in2[5] << 1) + ((u64) in[2]) * (in2[4] << 0) + + ((u64) in[3]) * (in2[3] << 1) + ((u64) in[4]) * + (in2[2] << 0) + ((u64) in[5]) * (in2[1] << 1) + + ((u64) in[6]) * (in2[0] << 0); + tmp[7] = ((u64) in[0]) * (in2[7] << 0) + ((u64) in[1]) * + (in2[6] << 0) + ((u64) in[2]) * (in2[5] << 0) + + ((u64) in[3]) * (in2[4] << 0) + ((u64) in[4]) * + (in2[3] << 0) + ((u64) in[5]) * (in2[2] << 0) + + ((u64) in[6]) * (in2[1] << 0) + + ((u64) in[7]) * (in2[0] << 0); + /* tmp[8] has the greatest value but doesn't overflow. See logic in + * felem_square. */ + tmp[8] = ((u64) in[0]) * (in2[8] << 0) + ((u64) in[1]) * + (in2[7] << 1) + ((u64) in[2]) * (in2[6] << 0) + + ((u64) in[3]) * (in2[5] << 1) + ((u64) in[4]) * + (in2[4] << 0) + ((u64) in[5]) * (in2[3] << 1) + + ((u64) in[6]) * (in2[2] << 0) + ((u64) in[7]) * + (in2[1] << 1) + ((u64) in[8]) * (in2[0] << 0); + tmp[9] = ((u64) in[1]) * (in2[8] << 0) + ((u64) in[2]) * + (in2[7] << 0) + ((u64) in[3]) * (in2[6] << 0) + + ((u64) in[4]) * (in2[5] << 0) + ((u64) in[5]) * + (in2[4] << 0) + ((u64) in[6]) * (in2[3] << 0) + + ((u64) in[7]) * (in2[2] << 0) + + ((u64) in[8]) * (in2[1] << 0); + tmp[10] = ((u64) in[2]) * (in2[8] << 0) + ((u64) in[3]) * + (in2[7] << 1) + ((u64) in[4]) * (in2[6] << 0) + + ((u64) in[5]) * (in2[5] << 1) + ((u64) in[6]) * + (in2[4] << 0) + ((u64) in[7]) * (in2[3] << 1) + + ((u64) in[8]) * (in2[2] << 0); + tmp[11] = ((u64) in[3]) * (in2[8] << 0) + ((u64) in[4]) * + (in2[7] << 0) + ((u64) in[5]) * (in2[6] << 0) + + ((u64) in[6]) * (in2[5] << 0) + ((u64) in[7]) * + (in2[4] << 0) + ((u64) in[8]) * (in2[3] << 0); + tmp[12] = ((u64) in[4]) * (in2[8] << 0) + ((u64) in[5]) * + (in2[7] << 1) + ((u64) in[6]) * (in2[6] << 0) + + ((u64) in[7]) * (in2[5] << 1) + + ((u64) in[8]) * (in2[4] << 0); + tmp[13] = ((u64) in[5]) * (in2[8] << 0) + ((u64) in[6]) * + (in2[7] << 0) + ((u64) in[7]) * (in2[6] << 0) + + ((u64) in[8]) * (in2[5] << 0); + tmp[14] = ((u64) in[6]) * (in2[8] << 0) + ((u64) in[7]) * + (in2[7] << 1) + ((u64) in[8]) * (in2[6] << 0); + tmp[15] = ((u64) in[7]) * (in2[8] << 0) + + ((u64) in[8]) * (in2[7] << 0); + tmp[16] = ((u64) in[8]) * (in2[8] << 0); + + felem_reduce_degree(out, tmp); +} + +static void felem_assign(felem out, const felem in) +{ + memcpy(out, in, sizeof(felem)); +} + +/* felem_inv calculates |out| = |in|^{-1} + * + * Based on Fermat's Little Theorem: + * a^p = a (mod p) + * a^{p-1} = 1 (mod p) + * a^{p-2} = a^{-1} (mod p) + */ +static void felem_inv(felem out, const felem in) +{ + felem ftmp, ftmp2; + /* each e_I will hold |in|^{2^I - 1} */ + felem e2, e4, e8, e16, e32, e64; + unsigned i; + + felem_square(ftmp, in); /* 2^1 */ + felem_mul(ftmp, in, ftmp); /* 2^2 - 2^0 */ + felem_assign(e2, ftmp); + felem_square(ftmp, ftmp); /* 2^3 - 2^1 */ + felem_square(ftmp, ftmp); /* 2^4 - 2^2 */ + felem_mul(ftmp, ftmp, e2); /* 2^4 - 2^0 */ + felem_assign(e4, ftmp); + felem_square(ftmp, ftmp); /* 2^5 - 2^1 */ + felem_square(ftmp, ftmp); /* 2^6 - 2^2 */ + felem_square(ftmp, ftmp); /* 2^7 - 2^3 */ + felem_square(ftmp, ftmp); /* 2^8 - 2^4 */ + felem_mul(ftmp, ftmp, e4); /* 2^8 - 2^0 */ + felem_assign(e8, ftmp); + for (i = 0; i < 8; i++) + felem_square(ftmp, ftmp); + /* 2^16 - 2^8 */ + felem_mul(ftmp, ftmp, e8); /* 2^16 - 2^0 */ + felem_assign(e16, ftmp); + for (i = 0; i < 16; i++) + felem_square(ftmp, ftmp); + /* 2^32 - 2^16 */ + felem_mul(ftmp, ftmp, e16); /* 2^32 - 2^0 */ + felem_assign(e32, ftmp); + for (i = 0; i < 32; i++) + felem_square(ftmp, ftmp); + /* 2^64 - 2^32 */ + felem_assign(e64, ftmp); + felem_mul(ftmp, ftmp, in); /* 2^64 - 2^32 + 2^0 */ + for (i = 0; i < 192; i++) + felem_square(ftmp, ftmp); + /* 2^256 - 2^224 + 2^192 */ + + felem_mul(ftmp2, e64, e32); /* 2^64 - 2^0 */ + for (i = 0; i < 16; i++) + felem_square(ftmp2, ftmp2); + /* 2^80 - 2^16 */ + felem_mul(ftmp2, ftmp2, e16); /* 2^80 - 2^0 */ + for (i = 0; i < 8; i++) + felem_square(ftmp2, ftmp2); + /* 2^88 - 2^8 */ + felem_mul(ftmp2, ftmp2, e8); /* 2^88 - 2^0 */ + for (i = 0; i < 4; i++) + felem_square(ftmp2, ftmp2); + /* 2^92 - 2^4 */ + felem_mul(ftmp2, ftmp2, e4); /* 2^92 - 2^0 */ + felem_square(ftmp2, ftmp2); /* 2^93 - 2^1 */ + felem_square(ftmp2, ftmp2); /* 2^94 - 2^2 */ + felem_mul(ftmp2, ftmp2, e2); /* 2^94 - 2^0 */ + felem_square(ftmp2, ftmp2); /* 2^95 - 2^1 */ + felem_square(ftmp2, ftmp2); /* 2^96 - 2^2 */ + felem_mul(ftmp2, ftmp2, in); /* 2^96 - 3 */ + + felem_mul(out, ftmp2, ftmp); /* 2^256 - 2^224 + 2^192 + 2^96 - 3 */ +} + +/* felem_scalar_3 sets out=3*out. + * + * On entry: out[0,2,...] < 2**30, out[1,3,...] < 2**29. + * On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29. */ +static void felem_scalar_3(felem out) +{ + limb carry = 0; + unsigned i; + + for (i = 0;; i++) { + out[i] *= 3; + out[i] += carry; + carry = out[i] >> 29; + out[i] &= kBottom29Bits; + + i++; + if (i == NLIMBS) + break; + + out[i] *= 3; + out[i] += carry; + carry = out[i] >> 28; + out[i] &= kBottom28Bits; + } + + felem_reduce_carry(out, carry); +} + +/* felem_scalar_4 sets out=4*out. + * + * On entry: out[0,2,...] < 2**30, out[1,3,...] < 2**29. + * On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29. */ +static void felem_scalar_4(felem out) +{ + limb carry = 0, next_carry; + unsigned i; + + for (i = 0;; i++) { + next_carry = out[i] >> 27; + out[i] <<= 2; + out[i] &= kBottom29Bits; + out[i] += carry; + carry = next_carry + (out[i] >> 29); + out[i] &= kBottom29Bits; + + i++; + if (i == NLIMBS) + break; + + next_carry = out[i] >> 26; + out[i] <<= 2; + out[i] &= kBottom28Bits; + out[i] += carry; + carry = next_carry + (out[i] >> 28); + out[i] &= kBottom28Bits; + } + + felem_reduce_carry(out, carry); +} + +/* felem_scalar_8 sets out=8*out. + * + * On entry: out[0,2,...] < 2**30, out[1,3,...] < 2**29. + * On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29. */ +static void felem_scalar_8(felem out) +{ + limb carry = 0, next_carry; + unsigned i; + + for (i = 0;; i++) { + next_carry = out[i] >> 26; + out[i] <<= 3; + out[i] &= kBottom29Bits; + out[i] += carry; + carry = next_carry + (out[i] >> 29); + out[i] &= kBottom29Bits; + + i++; + if (i == NLIMBS) + break; + + next_carry = out[i] >> 25; + out[i] <<= 3; + out[i] &= kBottom28Bits; + out[i] += carry; + carry = next_carry + (out[i] >> 28); + out[i] &= kBottom28Bits; + } + + felem_reduce_carry(out, carry); +} + +/* felem_is_zero_vartime returns 1 iff |in| == 0. It takes a variable amount of + * time depending on the value of |in|. */ +static char felem_is_zero_vartime(const felem in) +{ + limb carry; + int i; + limb tmp[NLIMBS]; + + felem_assign(tmp, in); + + /* First, reduce tmp to a minimal form. */ + do { + carry = 0; + for (i = 0;; i++) { + tmp[i] += carry; + carry = tmp[i] >> 29; + tmp[i] &= kBottom29Bits; + + i++; + if (i == NLIMBS) + break; + + tmp[i] += carry; + carry = tmp[i] >> 28; + tmp[i] &= kBottom28Bits; + } + + felem_reduce_carry(tmp, carry); + } while (carry); + + /* tmp < 2**257, so the only possible zero values are 0, p and 2p. */ + return memcmp(tmp, kZero, sizeof(tmp)) == 0 || + memcmp(tmp, kP, sizeof(tmp)) == 0 || + memcmp(tmp, k2P, sizeof(tmp)) == 0; +} + +/* Group operations: + * + * Elements of the elliptic curve group are represented in Jacobian + * coordinates: (x, y, z). An affine point (x', y') is x'=x/z**2, y'=y/z**3 in + * Jacobian form. */ + +/* point_double sets {x_out,y_out,z_out} = 2*{x,y,z}. + * + * See http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l */ +static void point_double(felem x_out, felem y_out, felem z_out, const felem x, + const felem y, const felem z) +{ + felem delta, gamma, alpha, beta, tmp, tmp2; + + felem_square(delta, z); + felem_square(gamma, y); + felem_mul(beta, x, gamma); + + felem_sum(tmp, x, delta); + felem_diff(tmp2, x, delta); + felem_mul(alpha, tmp, tmp2); + felem_scalar_3(alpha); + + felem_sum(tmp, y, z); + felem_square(tmp, tmp); + felem_diff(tmp, tmp, gamma); + felem_diff(z_out, tmp, delta); + + felem_scalar_4(beta); + felem_square(x_out, alpha); + felem_diff(x_out, x_out, beta); + felem_diff(x_out, x_out, beta); + + felem_diff(tmp, beta, x_out); + felem_mul(tmp, alpha, tmp); + felem_square(tmp2, gamma); + felem_scalar_8(tmp2); + felem_diff(y_out, tmp, tmp2); +} + +/* point_add_mixed sets {x_out,y_out,z_out} = {x1,y1,z1} + {x2,y2,1}. + * (i.e. the second point is affine.) + * + * See http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl + * + * Note that this function does not handle P+P, infinity+P nor P+infinity + * correctly. */ +static void point_add_mixed(felem x_out, felem y_out, felem z_out, + const felem x1, const felem y1, const felem z1, + const felem x2, const felem y2) +{ + felem z1z1, z1z1z1, s2, u2, h, i, j, r, rr, v, tmp; + + felem_square(z1z1, z1); + felem_sum(tmp, z1, z1); + + felem_mul(u2, x2, z1z1); + felem_mul(z1z1z1, z1, z1z1); + felem_mul(s2, y2, z1z1z1); + felem_diff(h, u2, x1); + felem_sum(i, h, h); + felem_square(i, i); + felem_mul(j, h, i); + felem_diff(r, s2, y1); + felem_sum(r, r, r); + felem_mul(v, x1, i); + + felem_mul(z_out, tmp, h); + felem_square(rr, r); + felem_diff(x_out, rr, j); + felem_diff(x_out, x_out, v); + felem_diff(x_out, x_out, v); + + felem_diff(tmp, v, x_out); + felem_mul(y_out, tmp, r); + felem_mul(tmp, y1, j); + felem_diff(y_out, y_out, tmp); + felem_diff(y_out, y_out, tmp); +} + +/* point_add sets {x_out,y_out,z_out} = {x1,y1,z1} + {x2,y2,z2}. + * + * See http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl + * + * Note that this function does not handle P+P, infinity+P nor P+infinity + * correctly. */ +static void point_add(felem x_out, felem y_out, felem z_out, const felem x1, + const felem y1, const felem z1, const felem x2, + const felem y2, const felem z2) +{ + felem z1z1, z1z1z1, z2z2, z2z2z2, s1, s2, u1, u2, h, i, j, r, rr, + v, tmp; + + felem_square(z1z1, z1); + felem_square(z2z2, z2); + felem_mul(u1, x1, z2z2); + + felem_sum(tmp, z1, z2); + felem_square(tmp, tmp); + felem_diff(tmp, tmp, z1z1); + felem_diff(tmp, tmp, z2z2); + + felem_mul(z2z2z2, z2, z2z2); + felem_mul(s1, y1, z2z2z2); + + felem_mul(u2, x2, z1z1); + felem_mul(z1z1z1, z1, z1z1); + felem_mul(s2, y2, z1z1z1); + felem_diff(h, u2, u1); + felem_sum(i, h, h); + felem_square(i, i); + felem_mul(j, h, i); + felem_diff(r, s2, s1); + felem_sum(r, r, r); + felem_mul(v, u1, i); + + felem_mul(z_out, tmp, h); + felem_square(rr, r); + felem_diff(x_out, rr, j); + felem_diff(x_out, x_out, v); + felem_diff(x_out, x_out, v); + + felem_diff(tmp, v, x_out); + felem_mul(y_out, tmp, r); + felem_mul(tmp, s1, j); + felem_diff(y_out, y_out, tmp); + felem_diff(y_out, y_out, tmp); +} + +/* point_add_or_double_vartime sets {x_out,y_out,z_out} = {x1,y1,z1} + + * {x2,y2,z2}. + * + * See http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl + * + * This function handles the case where {x1,y1,z1}={x2,y2,z2}. */ +static void point_add_or_double_vartime( + felem x_out, felem y_out, felem z_out, const felem x1, const felem y1, + const felem z1, const felem x2, const felem y2, const felem z2) +{ + felem z1z1, z1z1z1, z2z2, z2z2z2, s1, s2, u1, u2, h, i, j, r, rr, + v, tmp; + char x_equal, y_equal; + + felem_square(z1z1, z1); + felem_square(z2z2, z2); + felem_mul(u1, x1, z2z2); + + felem_sum(tmp, z1, z2); + felem_square(tmp, tmp); + felem_diff(tmp, tmp, z1z1); + felem_diff(tmp, tmp, z2z2); + + felem_mul(z2z2z2, z2, z2z2); + felem_mul(s1, y1, z2z2z2); + + felem_mul(u2, x2, z1z1); + felem_mul(z1z1z1, z1, z1z1); + felem_mul(s2, y2, z1z1z1); + felem_diff(h, u2, u1); + x_equal = felem_is_zero_vartime(h); + felem_sum(i, h, h); + felem_square(i, i); + felem_mul(j, h, i); + felem_diff(r, s2, s1); + y_equal = felem_is_zero_vartime(r); + if (x_equal && y_equal) { + point_double(x_out, y_out, z_out, x1, y1, z1); + return; + } + felem_sum(r, r, r); + felem_mul(v, u1, i); + + felem_mul(z_out, tmp, h); + felem_square(rr, r); + felem_diff(x_out, rr, j); + felem_diff(x_out, x_out, v); + felem_diff(x_out, x_out, v); + + felem_diff(tmp, v, x_out); + felem_mul(y_out, tmp, r); + felem_mul(tmp, s1, j); + felem_diff(y_out, y_out, tmp); + felem_diff(y_out, y_out, tmp); +} + +/* copy_conditional sets out=in if mask = 0xffffffff in constant time. + * + * On entry: mask is either 0 or 0xffffffff. */ +static void copy_conditional(felem out, const felem in, limb mask) +{ + int i; + + for (i = 0; i < NLIMBS; i++) { + const limb tmp = mask & (in[i] ^ out[i]); + + out[i] ^= tmp; + } +} + +/* select_affine_point sets {out_x,out_y} to the index'th entry of table. + * On entry: index < 16, table[0] must be zero. */ +static void select_affine_point(felem out_x, felem out_y, const limb *table, + limb index) +{ + limb i, j; + + memset(out_x, 0, sizeof(felem)); + memset(out_y, 0, sizeof(felem)); + + for (i = 1; i < 16; i++) { + limb mask = i ^ index; + + mask |= mask >> 2; + mask |= mask >> 1; + mask &= 1; + mask--; + for (j = 0; j < NLIMBS; j++, table++) + out_x[j] |= *table & mask; + for (j = 0; j < NLIMBS; j++, table++) + out_y[j] |= *table & mask; + } +} + +/* select_jacobian_point sets {out_x,out_y,out_z} to the index'th entry of + * table. On entry: index < 16, table[0] must be zero. */ +static void select_jacobian_point(felem out_x, felem out_y, felem out_z, + const limb *table, limb index) +{ + limb i, j; + + memset(out_x, 0, sizeof(felem)); + memset(out_y, 0, sizeof(felem)); + memset(out_z, 0, sizeof(felem)); + + /* The implicit value at index 0 is all zero. We don't need to + * perform that iteration of the loop because we already set + * out_* to zero. */ + table += 3 * NLIMBS; + + /* Hit all entries to obscure cache profiling. */ + for (i = 1; i < 16; i++) { + limb mask = i ^ index; + + mask |= mask >> 2; + mask |= mask >> 1; + mask &= 1; + mask--; + for (j = 0; j < NLIMBS; j++, table++) + out_x[j] |= *table & mask; + for (j = 0; j < NLIMBS; j++, table++) + out_y[j] |= *table & mask; + for (j = 0; j < NLIMBS; j++, table++) + out_z[j] |= *table & mask; + } +} + +/* scalar_base_mult sets {nx,ny,nz} = scalar*G where scalar is a little-endian + * number. Note that the value of scalar must be less than the order of the + * group. */ +static void scalar_base_mult(felem nx, felem ny, felem nz, + const p256_int *scalar) +{ + int i, j; + limb n_is_infinity_mask = -1, p_is_noninfinite_mask, mask; + u32 table_offset; + + felem px, py; + felem tx, ty, tz; + + memset(nx, 0, sizeof(felem)); + memset(ny, 0, sizeof(felem)); + memset(nz, 0, sizeof(felem)); + + /* The loop adds bits at positions 0, 64, 128 and 192, followed by + * positions 32,96,160 and 224 and does this 32 times. */ + for (i = 0; i < 32; i++) { + if (i) + point_double(nx, ny, nz, nx, ny, nz); + + table_offset = 0; + for (j = 0; j <= 32; j += 32) { + char bit0 = p256_get_bit(scalar, 31 - i + j); + char bit1 = p256_get_bit(scalar, 95 - i + j); + char bit2 = p256_get_bit(scalar, 159 - i + j); + char bit3 = p256_get_bit(scalar, 223 - i + j); + limb index = + bit0 | (bit1 << 1) | (bit2 << 2) | (bit3 << 3); + + select_affine_point(px, py, + kPrecomputed + table_offset, index); + table_offset += 30 * NLIMBS; + + /* Since scalar is less than the order of the + * group, we know that {nx,ny,nz} != + * {px,py,1}, unless both are zero, which we + * handle below. */ + point_add_mixed(tx, ty, tz, nx, ny, nz, px, py); + /* The result of point_add_mixed is incorrect + * if {nx,ny,nz} is zero (a.k.a. the point at + * infinity). We handle that situation by + * copying the point from the table. */ + copy_conditional(nx, px, n_is_infinity_mask); + copy_conditional(ny, py, n_is_infinity_mask); + copy_conditional(nz, kOne, n_is_infinity_mask); + + /* Equally, the result is also wrong if the + * point from the table is zero, which happens + * when the index is zero. We handle that by + * only copying from {tx,ty,tz} to {nx,ny,nz} + * if index != 0. */ + p_is_noninfinite_mask = NON_ZERO_TO_ALL_ONES(index); + mask = p_is_noninfinite_mask & ~n_is_infinity_mask; + copy_conditional(nx, tx, mask); + copy_conditional(ny, ty, mask); + copy_conditional(nz, tz, mask); + /* If p was not zero, then n is now non-zero. */ + n_is_infinity_mask &= ~p_is_noninfinite_mask; + } + } +} + +/* point_to_affine converts a Jacobian point to an affine point. If + * the input is the point at infinity then it returns (0, 0) in + * constant time. */ +static void point_to_affine(felem x_out, felem y_out, const felem nx, + const felem ny, const felem nz) +{ + felem z_inv, z_inv_sq; + + felem_inv(z_inv, nz); + felem_square(z_inv_sq, z_inv); + felem_mul(x_out, nx, z_inv_sq); + felem_mul(z_inv, z_inv, z_inv_sq); + felem_mul(y_out, ny, z_inv); +} + +/* scalar_base_mult sets {nx,ny,nz} = scalar*{x,y}. */ +static void scalar_mult(felem nx, felem ny, felem nz, const felem x, + const felem y, const p256_int *scalar) +{ + int i; + felem px, py, pz, tx, ty, tz; + felem precomp[16][3]; + limb n_is_infinity_mask, index, p_is_noninfinite_mask, mask; + + /* We precompute 0,1,2,... times {x,y}. */ + memset(precomp, 0, sizeof(felem) * 3); + memcpy(&precomp[1][0], x, sizeof(felem)); + memcpy(&precomp[1][1], y, sizeof(felem)); + memcpy(&precomp[1][2], kOne, sizeof(felem)); + + for (i = 2; i < 16; i += 2) { + point_double(precomp[i][0], precomp[i][1], precomp[i][2], + precomp[i / 2][0], precomp[i / 2][1], + precomp[i / 2][2]); + + point_add_mixed( + precomp[i + 1][0], precomp[i + 1][1], precomp[i + 1][2], + precomp[i][0], precomp[i][1], precomp[i][2], x, y); + } + + memset(nx, 0, sizeof(felem)); + memset(ny, 0, sizeof(felem)); + memset(nz, 0, sizeof(felem)); + n_is_infinity_mask = -1; + + /* We add in a window of four bits each iteration and do this + * 64 times. */ + for (i = 0; i < 256; i += 4) { + if (i) { + point_double(nx, ny, nz, nx, ny, nz); + point_double(nx, ny, nz, nx, ny, nz); + point_double(nx, ny, nz, nx, ny, nz); + point_double(nx, ny, nz, nx, ny, nz); + } + + index = (p256_get_bit(scalar, 255 - i - 0) << 3) | + (p256_get_bit(scalar, 255 - i - 1) << 2) | + (p256_get_bit(scalar, 255 - i - 2) << 1) | + p256_get_bit(scalar, 255 - i - 3); + + /* See the comments in scalar_base_mult about handling + * infinities. */ + select_jacobian_point(px, py, pz, precomp[0][0], index); + point_add(tx, ty, tz, nx, ny, nz, px, py, pz); + copy_conditional(nx, px, n_is_infinity_mask); + copy_conditional(ny, py, n_is_infinity_mask); + copy_conditional(nz, pz, n_is_infinity_mask); + + p_is_noninfinite_mask = NON_ZERO_TO_ALL_ONES(index); + mask = p_is_noninfinite_mask & ~n_is_infinity_mask; + + copy_conditional(nx, tx, mask); + copy_conditional(ny, ty, mask); + copy_conditional(nz, tz, mask); + n_is_infinity_mask &= ~p_is_noninfinite_mask; + } +} + +/* 2^257 mod p256.p */ +#define kRDigits {2, 0, 0, 0xfffffffe, 0xffffffff, 0xffffffff, 0xfffffffd, 1} +/* 1 / 2^257 mod p256.p */ +#define kRInvDigits {0x80000000, 1, 0xffffffff, 0, 0x80000001, 0xfffffffe, \ + 1, 0x7fffffff} + +static const p256_int kR = { kRDigits }; +static const p256_int kRInv = { kRInvDigits }; + +/* to_montgomery sets out = R*in. */ +static void to_montgomery(felem out, const p256_int *in) +{ + p256_int in_shifted; + int i; + + p256_init(&in_shifted); + p256_modmul(&SECP256r1_p, in, 0, &kR, &in_shifted); + + for (i = 0; i < NLIMBS; i++) { + if ((i & 1) == 0) { + out[i] = P256_DIGIT(&in_shifted, 0) & kBottom29Bits; + p256_shr(&in_shifted, 29, &in_shifted); + } else { + out[i] = P256_DIGIT(&in_shifted, 0) & kBottom28Bits; + p256_shr(&in_shifted, 28, &in_shifted); + } + } + + p256_clear(&in_shifted); +} + +/* from_montgomery sets out=in/R. */ +static void from_montgomery(p256_int *out, const felem in) +{ + p256_int result, tmp; + int i, top; + + p256_init(&result); + p256_init(&tmp); + + p256_add_d(&tmp, in[NLIMBS - 1], &result); + for (i = NLIMBS - 2; i >= 0; i--) { + if ((i & 1) == 0) + top = p256_shl(&result, 29, &tmp); + else + top = p256_shl(&result, 28, &tmp); + + top |= p256_add_d(&tmp, in[i], &result); + } + + p256_modmul(&SECP256r1_p, &kRInv, top, &result, out); + + p256_clear(&result); + p256_clear(&tmp); +} + +/* p256_base_point_mul sets {out_x,out_y} = nG, where n is < the + * order of the group. */ +int DCRYPTO_p256_base_point_mul(p256_int *out_x, p256_int *out_y, + const p256_int *n) +{ + felem x, y, z; + + if (p256_is_zero(n) != 0) { + p256_clear(out_x); + p256_clear(out_y); + return 0; + } + + scalar_base_mult(x, y, z, n); + + { + felem x_affine, y_affine; + + point_to_affine(x_affine, y_affine, x, y, z); + from_montgomery(out_x, x_affine); + from_montgomery(out_y, y_affine); + } + + return 1; +} + +/* p256_point_mul sets {out_x,out_y} = n*{in_x,in_y}, where n is < + * the order of the group. */ +void p256_point_mul(const p256_int *n, const p256_int *in_x, + const p256_int *in_y, p256_int *out_x, p256_int *out_y) +{ + felem x, y, z, px, py; + + to_montgomery(px, in_x); + to_montgomery(py, in_y); + + scalar_mult(x, y, z, px, py, n); + + point_to_affine(px, py, x, y, z); + from_montgomery(out_x, px); + from_montgomery(out_y, py); +} + +/* p256_points_mul_vartime sets {out_x,out_y} = n1*G + n2*{in_x,in_y}, where + * n1 and n2 are < the order of the group. + * + * As indicated by the name, this function operates in variable time. This + * is safe because it's used for signature validation which doesn't deal + * with secrets. */ +void p256_points_mul_vartime( + const p256_int *n1, const p256_int *n2, const p256_int *in_x, + const p256_int *in_y, p256_int *out_x, p256_int *out_y) +{ + felem x1, y1, z1, x2, y2, z2, px, py; + + /* If both scalars are zero, then the result is the point at + * infinity. */ + if (p256_is_zero(n1) != 0 && p256_is_zero(n2) != 0) { + p256_clear(out_x); + p256_clear(out_y); + return; + } + + to_montgomery(px, in_x); + to_montgomery(py, in_y); + scalar_base_mult(x1, y1, z1, n1); + scalar_mult(x2, y2, z2, px, py, n2); + + if (p256_is_zero(n2) != 0) { + /* If n2 == 0, then {x2,y2,z2} is zero and the result is just + * {x1,y1,z1}. */ + } else if (p256_is_zero(n1) != 0) { + /* If n1 == 0, then {x1,y1,z1} is zero and the result is just + * {x2,y2,z2}. */ + memcpy(x1, x2, sizeof(x2)); + memcpy(y1, y2, sizeof(y2)); + memcpy(z1, z2, sizeof(z2)); + } else { + /* This function handles the case where {x1,y1,z1} == + * {x2,y2,z2}. */ + point_add_or_double_vartime(x1, y1, z1, x1, y1, z1, x2, y2, z2); + } + + point_to_affine(px, py, x1, y1, z1); + from_montgomery(out_x, px); + from_montgomery(out_y, py); +} + +/* p256_points_mul sets {out_x,out_y} = n1*G + n2*{in_x,in_y}, where + * n1 and n2 are < the order of the group. + * + * As indicated by the name, this function operates in variable time. This + * is safe because it's used for signature validation which doesn't deal + * with secrets. */ +int DCRYPTO_p256_points_mul(p256_int *out_x, p256_int *out_y, + const p256_int *n1, const p256_int *n2, + const p256_int *in_x, const p256_int *in_y) +{ + felem x1, y1, z1, x2, y2, z2, px, py; + + /* If both scalars are zero, then the result is the point at + * infinity. */ + if (p256_is_zero(n1) != 0 && p256_is_zero(n2) != 0) { + p256_clear(out_x); + p256_clear(out_y); + return 0; + } + + to_montgomery(px, in_x); + to_montgomery(py, in_y); + scalar_base_mult(x1, y1, z1, n1); + scalar_mult(x2, y2, z2, px, py, n2); + + if (p256_is_zero(n2) != 0) { + /* If n2 == 0, then {x2,y2,z2} is zero and the result is just + * {x1,y1,z1}. */ + } else if (p256_is_zero(n1) != 0) { + /* If n1 == 0, then {x1,y1,z1} is zero and the result is just + * {x2,y2,z2}. */ + memcpy(x1, x2, sizeof(x2)); + memcpy(y1, y2, sizeof(y2)); + memcpy(z1, z2, sizeof(z2)); + } else { + /* This function handles the case where + * {x1,y1,z1} == {x2,y2,z2}. */ + point_add_or_double_vartime(x1, y1, z1, x1, y1, z1, x2, y2, z2); + } + + point_to_affine(px, py, x1, y1, z1); + from_montgomery(out_x, px); + from_montgomery(out_y, py); + return 1; +} diff --git a/chip/g/dcrypto/p256_ecdsa.c b/chip/g/dcrypto/p256_ecdsa.c new file mode 100644 index 0000000000..2385c58d62 --- /dev/null +++ b/chip/g/dcrypto/p256_ecdsa.c @@ -0,0 +1,103 @@ +/* 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 <stdint.h> + +#include "dcrypto.h" + +/* Compute k based on a given {key, digest} pair, 0 < k < n. */ +static void determine_k(const p256_int *key, const p256_int *digest, + char *tweak, p256_int *k) +{ + do { + p256_int p1, p2; + struct HMAC_CTX hmac; + + /* NOTE: taking the p256_int in-memory representation + * is not endian neutral. Signatures with an + * identical key on identical digests will differ per + * host endianness. This however does not jeopardize + * the key bits. */ + dcrypto_HMAC_SHA256_init(&hmac, key, P256_NBYTES); + dcrypto_HMAC_update(&hmac, tweak, 1); + dcrypto_HMAC_update(&hmac, (uint8_t *) digest, P256_NBYTES); + ++(*tweak); + p256_from_bin(dcrypto_HMAC_final(&hmac), &p1); + + dcrypto_HMAC_SHA256_init(&hmac, key, P256_NBYTES); + dcrypto_HMAC_update(&hmac, tweak, 1); + dcrypto_HMAC_update(&hmac, (uint8_t *) digest, P256_NBYTES); + ++(*tweak); + p256_from_bin(dcrypto_HMAC_final(&hmac), &p2); + + /* Combine p1 and p2 into well distributed k. */ + p256_modmul(&SECP256r1_n, &p1, 0, &p2, k); + + /* (Attempt to) clear stack state. */ + p256_clear(&p1); + p256_clear(&p2); + + } while (p256_is_zero(k)); +} + +void DCRYPTO_p256_ecdsa_sign(const p256_int *key, const p256_int *digest, + p256_int *r, p256_int *s) +{ + char tweak = 'A'; + p256_digit top; + + for (;;) { + p256_int k, kinv; + + determine_k(key, digest, &tweak, &k); + DCRYPTO_p256_base_point_mul(r, s, &k); + p256_mod(&SECP256r1_n, r, r); + + /* Make sure r != 0. */ + if (p256_is_zero(r)) + continue; + + p256_modmul(&SECP256r1_n, r, 0, key, s); + top = p256_add(s, digest, s); + p256_modinv(&SECP256r1_n, &k, &kinv); + p256_modmul(&SECP256r1_n, &kinv, top, s, s); + + /* (Attempt to) clear stack state. */ + p256_clear(&k); + p256_clear(&kinv); + + /* Make sure s != 0. */ + if (p256_is_zero(s)) + continue; + + break; + } +} + +int DCRYPTO_p256_ecdsa_verify(const p256_int *key_x, const p256_int *key_y, + const p256_int *digest, + const p256_int *r, const p256_int *s) +{ + p256_int u, v; + + /* Check public key. */ + if (!DCRYPTO_p256_valid_point(key_x, key_y)) + return 0; + + /* Check r and s are != 0 % n. */ + p256_mod(&SECP256r1_n, r, &u); + p256_mod(&SECP256r1_n, s, &v); + if (p256_is_zero(&u) || p256_is_zero(&v)) + return 0; + + p256_modinv_vartime(&SECP256r1_n, s, &v); + p256_modmul(&SECP256r1_n, digest, 0, &v, &u); /* digest / s % n */ + p256_modmul(&SECP256r1_n, r, 0, &v, &v); /* r / s % n */ + + p256_points_mul_vartime(&u, &v, key_x, key_y, &u, &v); + + p256_mod(&SECP256r1_n, &u, &u); /* (x coord % p) % n */ + return p256_cmp(r, &u) == 0; +} diff --git a/include/extension.h b/include/extension.h index c985640fa9..78b9d63f37 100644 --- a/include/extension.h +++ b/include/extension.h @@ -49,6 +49,7 @@ enum { EXTENSION_AES = 0, EXTENSION_HASH = 1, EXTENSION_RSA = 2, + EXTENSION_EC = 3, }; diff --git a/test/tpm_test/ecc_test.py b/test/tpm_test/ecc_test.py new file mode 100644 index 0000000000..cd4bb0a261 --- /dev/null +++ b/test/tpm_test/ecc_test.py @@ -0,0 +1,169 @@ +#!/usr/bin/python +# Copyright 2016 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 ecc functions using extended commands.""" +import binascii +import hashlib +import os +import struct + +import subcmd +import utils + +_EC_OPCODES = { + 'SIGN': 0x00, + 'VERIFY': 0x01, + 'KEYGEN': 0x02, + 'KEYDERIVE': 0x03, +} + +_EC_CURVES = { + 'NIST-P256': 0x03, +} + +# TPM2 signature codes. +_SIGN_MODE = { + 'NONE': 0x00, + 'ECDSA': 0x18, + # TODO(ngm): add support for SCHNORR. + # 'SCHNORR': 0x1c +} + +# TPM2 ALG codes. +_HASH = { + 'NONE': 0x00, + 'SHA1': 0x04, + 'SHA256': 0x0B +} + +_HASH_FUNC = { + 'NIST-P256': hashlib.sha256 +} + +# Command format. +# +# 0x00 OP +# 0x00 CURVE_ID +# 0x00 SIGN_MODE +# 0x00 HASHING +# 0x00 MSB IN LEN +# 0x00 LSB IN LEN +# .... IN +# 0x00 MSB DIGEST LEN +# 0x00 LSB DIGEST LEN +# .... DIGEST +# +_EC_CMD_FORMAT = '{o:c}{c:c}{s:c}{h:c}{ml:s}{msg}{dl:s}{dig}' + + +def _sign_cmd(curve_id, hash_func, sign_mode, msg): + op = _EC_OPCODES['SIGN'] + digest = hash_func(msg).digest() + digest_len = len(digest) + return _EC_CMD_FORMAT.format(o=op, c=curve_id, s=sign_mode, h=_HASH['NONE'], + ml=struct.pack('>H', 0), msg='', + dl=struct.pack('>H', digest_len), dig=digest) + + +def _verify_cmd(curve_id, hash_func, sign_mode, msg, sig): + op = _EC_OPCODES['VERIFY'] + sig_len = len(sig) + digest = hash_func(msg).digest() + digest_len = len(digest) + return _EC_CMD_FORMAT.format(o=op, c=curve_id, s=sign_mode, h=_HASH['NONE'], + ml=struct.pack('>H', sig_len), msg=sig, + dl=struct.pack('>H', digest_len), dig=digest) + + +def _keygen_cmd(curve_id): + op = _EC_OPCODES['KEYGEN'] + return _EC_CMD_FORMAT.format(o=op, c=curve_id, s=_SIGN_MODE['NONE'], + h=_HASH['NONE'], ml=struct.pack('>H', 0), msg='', + dl=struct.pack('>H', 0), dig='') + + +def _keyderive_cmd(curve_id, seed): + op = _EC_OPCODES['KEYDERIVE'] + seed_len = len(seed) + return _EC_CMD_FORMAT.format(o=op, c=curve_id, s=_SIGN_MODE['NONE'], + h=_HASH['NONE'], ml=struct.pack('>H', seed_len), + msg=seed, dl=struct.pack('>H', 0), dig='') + + +_SIGN_INPUTS = ( + ('NIST-P256', 'ECDSA'), +) + + +_KEYGEN_INPUTS = ( + ('NIST-P256',), +) + + +_KEYDERIVE_INPUTS = ( + # Curve-id, random seed size. + ('NIST-P256', 32), +) + + +class ECError(Exception): + pass + + +def _sign_test(tpm): + msg = 'Hello CR50' + + for data in _SIGN_INPUTS: + curve_id, sign_mode = data + test_name = 'EC-SIGN:%s:%s' % data + cmd = _sign_cmd(_EC_CURVES[curve_id], _HASH_FUNC[curve_id], + _SIGN_MODE[sign_mode], msg) + wrapped_response = tpm.command(tpm.wrap_ext_command(subcmd.EC, cmd)) + signature = tpm.unwrap_ext_response(subcmd.EC, wrapped_response) + + cmd = _verify_cmd(_EC_CURVES[curve_id], _HASH_FUNC[curve_id], + _SIGN_MODE[sign_mode], msg, signature) + wrapped_response = tpm.command(tpm.wrap_ext_command(subcmd.EC, cmd)) + verified = tpm.unwrap_ext_response(subcmd.EC, wrapped_response) + expected = '\x01' + if verified != expected: + raise ECError('%s error:%s:%s' % ( + test_name, utils.hex_dump(verified), utils.hex_dump(expected))) + print('%sSUCCESS: %s' % (utils.cursor_back(), test_name)) + + +def _keygen_test(tpm): + for data in _KEYGEN_INPUTS: + curve_id, = data + test_name = 'EC-KEYGEN:%s' % data + cmd = _keygen_cmd(_EC_CURVES[curve_id]) + wrapped_response = tpm.command(tpm.wrap_ext_command(subcmd.EC, cmd)) + valid = tpm.unwrap_ext_response(subcmd.EC, wrapped_response) + expected = '\x01' + if valid != expected: + raise ECError('%s error:%s:%s' % ( + test_name, utils.hex_dump(valid), utils.hex_dump(expected))) + print('%sSUCCESS: %s' % (utils.cursor_back(), test_name)) + + +def _keyderive_test(tpm): + for data in _KEYDERIVE_INPUTS: + curve_id, seed_bytes = data + seed = os.urandom(seed_bytes) + test_name = 'EC-KEYDERIVE:%s' % data[0] + cmd = _keyderive_cmd(_EC_CURVES[curve_id], seed) + wrapped_response = tpm.command(tpm.wrap_ext_command(subcmd.EC, cmd)) + valid = tpm.unwrap_ext_response(subcmd.EC, wrapped_response) + expected = '\x01' + if valid != expected: + raise ECError('%s error:%s:%s' % ( + test_name, utils.hex_dump(valid), utils.hex_dump(expected))) + print('%sSUCCESS: %s' % (utils.cursor_back(), test_name)) + + +def ecc_test(tpm): + _sign_test(tpm) + _keygen_test(tpm) + _keyderive_test(tpm) diff --git a/test/tpm_test/subcmd.py b/test/tpm_test/subcmd.py index f7bd8090a0..d14c2e9b9c 100644 --- a/test/tpm_test/subcmd.py +++ b/test/tpm_test/subcmd.py @@ -9,3 +9,4 @@ AES = 0 HASH = 1 RSA = 2 +EC = 3 diff --git a/test/tpm_test/tpmtest.py b/test/tpm_test/tpmtest.py index 52d7205022..173f0ec693 100755 --- a/test/tpm_test/tpmtest.py +++ b/test/tpm_test/tpmtest.py @@ -19,6 +19,7 @@ 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 ecc_test import ftdi_spi_tpm import hash_test import rsa_test @@ -135,6 +136,7 @@ if __name__ == '__main__': t = TPM(debug_mode=debug_needed) crypto_test.crypto_tests(t, os.path.join(root_dir, 'crypto_test.xml')) + ecc_test.ecc_test(t) hash_test.hash_test(t) rsa_test.rsa_test(t) except (TpmError, crypto_test.CryptoError, hash_test.HashError, |