From 65ac8befebdd55445e6fd973b1b845d5a6004b17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Vejpustek?= Date: Fri, 2 Aug 2024 14:38:29 +0200 Subject: [PATCH] refactor(crypto): do not use precomputed tables by native implementation of ECC [no changelog] --- core/SConscript.firmware | 3 +- core/SConscript.unix | 3 +- crypto/Makefile | 5 ++- crypto/bip32.c | 8 +++- crypto/ecdsa.c | 14 +++++- crypto/ecdsa_internal.h | 3 +- crypto/options.h | 2 +- crypto/tests/test_check.c | 88 ++++++++++++++++++++++++++++++++++++ crypto/zkp_ecdsa.c | 94 ++++++++++++++++++++++++++++++++++----- crypto/zkp_ecdsa.h | 2 + legacy/firmware/Makefile | 5 ++- 11 files changed, 205 insertions(+), 22 deletions(-) diff --git a/core/SConscript.firmware b/core/SConscript.firmware index c175b4d78..5fe9b917e 100644 --- a/core/SConscript.firmware +++ b/core/SConscript.firmware @@ -183,11 +183,12 @@ if FEATURE_FLAGS["SECP256K1_ZKP"]: 'USE_EXTERNAL_ASM', 'USE_EXTERNAL_DEFAULT_CALLBACKS', ('ECMULT_GEN_PREC_BITS', '2'), - ('ECMULT_WINDOW_SIZE', '8'), + ('ECMULT_WINDOW_SIZE', '2'), 'ENABLE_MODULE_GENERATOR', 'ENABLE_MODULE_RECOVERY', 'ENABLE_MODULE_SCHNORRSIG', 'ENABLE_MODULE_EXTRAKEYS', + 'ENABLE_MODULE_ECDH', ] SOURCE_MOD_SECP256K1_ZKP = [ 'vendor/secp256k1-zkp/src/secp256k1.c', diff --git a/core/SConscript.unix b/core/SConscript.unix index fc19b8b6a..0d8161185 100644 --- a/core/SConscript.unix +++ b/core/SConscript.unix @@ -184,11 +184,12 @@ if FEATURE_FLAGS["SECP256K1_ZKP"]: ('SECP256K1_CONTEXT_SIZE', '208'), 'USE_EXTERNAL_DEFAULT_CALLBACKS', ('ECMULT_GEN_PREC_BITS', '2'), - ('ECMULT_WINDOW_SIZE', '8'), + ('ECMULT_WINDOW_SIZE', '2'), 'ENABLE_MODULE_GENERATOR', 'ENABLE_MODULE_RECOVERY', 'ENABLE_MODULE_SCHNORRSIG', 'ENABLE_MODULE_EXTRAKEYS', + 'ENABLE_MODULE_ECDH', ] SOURCE_MOD_SECP256K1_ZKP = [ 'vendor/secp256k1-zkp/src/secp256k1.c', diff --git a/crypto/Makefile b/crypto/Makefile index ef1f5bac7..34bab1175 100644 --- a/crypto/Makefile +++ b/crypto/Makefile @@ -65,11 +65,12 @@ endif ZKP_CFLAGS = \ -DECMULT_GEN_PREC_BITS=4 \ - -DECMULT_WINDOW_SIZE=8 \ + -DECMULT_WINDOW_SIZE=2 \ -DENABLE_MODULE_GENERATOR \ -DENABLE_MODULE_RECOVERY \ -DENABLE_MODULE_SCHNORRSIG \ - -DENABLE_MODULE_EXTRAKEYS + -DENABLE_MODULE_EXTRAKEYS \ + -DENABLE_MODULE_ECDH ZKP_PATH = ../vendor/secp256k1-zkp # this is specific for 64-bit builds CFLAGS += -DSECP256K1_CONTEXT_SIZE=208 diff --git a/crypto/bip32.c b/crypto/bip32.c index 09723d9fd..a5c56679d 100644 --- a/crypto/bip32.c +++ b/crypto/bip32.c @@ -294,8 +294,12 @@ int hdnode_public_ckd_cp(const ecdsa_curve *curve, const curve_point *parent, hmac_sha512(parent_chain_code, 32, data, sizeof(data), I); bn_read_be(I, &c); if (bn_is_less(&c, &curve->order)) { // < order - scalar_multiply(curve, &c, child); // b = c * G - point_add(curve, parent, child); // b = a + b + // b = c * G + uint8_t child_pubkey[65] = {}; + ecdsa_get_public_key65(curve, I, child_pubkey); + ecdsa_read_pubkey(curve, child_pubkey, child); + + point_add(curve, parent, child); // b = a + b if (!point_is_infinity(child)) { if (child_chain_code) { memcpy(child_chain_code, I + 32, 32); diff --git a/crypto/ecdsa.c b/crypto/ecdsa.c index 79fdaec67..0e3175e60 100644 --- a/crypto/ecdsa.c +++ b/crypto/ecdsa.c @@ -635,8 +635,8 @@ int scalar_multiply(const ecdsa_curve *curve, const bignum256 *k, #endif -int ecdh_multiply(const ecdsa_curve *curve, const uint8_t *priv_key, - const uint8_t *pub_key, uint8_t *session_key) { +int tc_ecdh_multiply(const ecdsa_curve *curve, const uint8_t *priv_key, + const uint8_t *pub_key, uint8_t *session_key) { curve_point point = {0}; if (!ecdsa_read_pubkey(curve, pub_key, &point)) { return 1; @@ -1312,3 +1312,13 @@ int ecdsa_recover_pub_from_sig(const ecdsa_curve *curve, uint8_t *pub_key, #endif return tc_ecdsa_recover_pub_from_sig(curve, pub_key, sig, digest, recid); } + +int ecdh_multiply(const ecdsa_curve *curve, const uint8_t *priv_key, + const uint8_t *pub_key, uint8_t *session_key) { +#ifdef USE_SECP256K1_ZKP_ECDSA + if (curve == &secp256k1) { + return zkp_ecdh_multiply(curve, priv_key, pub_key, session_key); + } +#endif + return tc_ecdh_multiply(curve, priv_key, pub_key, session_key); +} diff --git a/crypto/ecdsa_internal.h b/crypto/ecdsa_internal.h index 6d5c98465..0397f6250 100644 --- a/crypto/ecdsa_internal.h +++ b/crypto/ecdsa_internal.h @@ -39,5 +39,6 @@ int tc_ecdsa_verify_digest(const ecdsa_curve *curve, const uint8_t *pub_key, int tc_ecdsa_recover_pub_from_sig(const ecdsa_curve *curve, uint8_t *pub_key, const uint8_t *sig, const uint8_t *digest, int recid); - +int tc_ecdh_multiply(const ecdsa_curve *curve, const uint8_t *priv_key, + const uint8_t *pub_key, uint8_t *session_key); #endif diff --git a/crypto/options.h b/crypto/options.h index 94f5c3ba6..4bf9a9904 100644 --- a/crypto/options.h +++ b/crypto/options.h @@ -25,7 +25,7 @@ // use precomputed Curve Points (some scalar multiples of curve base point G) #ifndef USE_PRECOMPUTED_CP -#define USE_PRECOMPUTED_CP 1 +#define USE_PRECOMPUTED_CP 0 #endif // use fast inverse method diff --git a/crypto/tests/test_check.c b/crypto/tests/test_check.c index 1a7ba1644..cce8dc46f 100644 --- a/crypto/tests/test_check.c +++ b/crypto/tests/test_check.c @@ -4115,6 +4115,88 @@ START_TEST(test_aes) { } END_TEST +static void test_ecdh_multiply_helper( + int (*ecdh_multiply_fn)(const ecdsa_curve *curve, const uint8_t *priv_key, + const uint8_t *pub_key, uint8_t *session_key)) { + static struct { + const char *priv_key; + const char *pub_key; + int res; + const char *session_key; + } tests[] = { + // Compressed public key + {"1618cc490a4a5b38d9f877759a2c312026fe9f459edb9ba49e91b6de4a237cad", + "0322c16706cd0d80dbb4e314799ba1323420e7ffa859a0cf0c82a444192bb6c997", 0, + "047b27ebb15e8197c9a560afc1bd45e2a78b864829fc1257333a1a2d7d30d79eed17889" + "8ad4960f756cf155cf46d22f2e3b21df10ee5bad560c467ae0d79427a70"}, + // Uncompressed public key + {"d56e61dea5c8412292c8a645f20846109a228532b475ff33b7c32663ddb66600", + "0282dc03a182e2613a9d2e3f2dc1bdd9e6b541708a719f610c06d1a09be62d3aa2", 0, + "0496d82e44fc23e9e8b8f7fabe95fcaff6073b66341324320a7bde5fdb3c7a7990c762c" + "c7ce477ca97c4e650a7f9297d240579d46630abbdb6d938732e864f8280"}, + // Invalid compressed public key + {"d2ffd4daa78a7fb253edb315e27f5841df00a581ab2c3330ffded22be98b7c96", + "026a575a9f3d4e945366a78e7961b312018451df8485ee1210767a0575225c2764", 1, + ""}, + // Invalid uncompressed public key + {"7da05844e870b1674c14a9a80b45ef36d221680707b727a7b6c70b2a25e99717", + "71ebf221ab355d070a570525e4c4bff94b27ca97e67fcdfb993f6546e7e1ead0ea09ed1" + "f7e55282f2787c7fc54cc5815660641cd95148a4b56f15818a04c7f72", + 1, ""}, + // Invalid compressed public key - the point at infinity + {"6753537e70935a9c85b0bad5b2aa54fb565b50bcf086acc7836a94318bc442d4", + "000000000000000000000000000000000000000000000000000000000000000000", 1, + ""}, + // Invalid uncompressed public key - the point at infinity + {"6753537e70935a9c85b0bad5b2aa54fb565b50bcf086acc7836a94318bc442d4", + "0000000000000000000000000000000000000000000000000000000000000000000000" + "0" + "00000000000000000000000000000000000000000000000000000000000", + 1, ""}, + // Invalid private key - the group order + {"fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", + "02e38ad3515c21358659ef113864b26e6caaa6bb780ce02daaf1c13f4fe24bb632", 2, + ""}, + // Invalid private key - zero + {"0000000000000000000000000000000000000000000000000000000000000000", + "038325242a489e883afdc86da5a714e4ffc86530e1b6660ac87ffb87c0045e486c", 2, + ""}, + }; + + const ecdsa_curve *curve = &secp256k1; + uint8_t priv_key[32] = {0}; + uint8_t pub_key[33] = {0}; + uint8_t session_key[65] = {0}; + uint8_t expected_session_key[65] = {0}; + int expected_res = 0; + int res = 0; + + for (size_t i = 0; i < sizeof(tests) / sizeof(*tests); i++) { + memcpy(priv_key, fromhex(tests[i].priv_key), 32); + memcpy(pub_key, fromhex(tests[i].pub_key), 33); + if (expected_res == 0) { + memcpy(expected_session_key, fromhex(tests[i].session_key), 65); + } + expected_res = tests[i].res; + + res = ecdh_multiply_fn(curve, priv_key, pub_key, session_key); + ck_assert_int_eq(expected_res, res); + if (expected_res == 0) { + ck_assert_mem_eq(expected_session_key, session_key, 65); + } + } +} + +START_TEST(test_tc_ecdh_multiply) { + test_ecdh_multiply_helper(tc_ecdh_multiply); +} +END_TEST + +START_TEST(test_zkp_ecdh_multiply) { + test_ecdh_multiply_helper(zkp_ecdh_multiply); +} +END_TEST + // test vectors from // https://datatracker.ietf.org/doc/html/rfc3610 // https://doi.org/10.6028/NIST.SP.800-38C @@ -7074,6 +7156,7 @@ START_TEST(test_ecdsa_der) { } END_TEST +#if USE_PRECOMPUTED_CP static void test_codepoints_curve(const ecdsa_curve *curve) { int i, j; bignum256 a; @@ -7111,6 +7194,7 @@ START_TEST(test_codepoints_secp256k1) { test_codepoints_curve(&secp256k1); } END_TEST START_TEST(test_codepoints_nist256p1) { test_codepoints_curve(&nist256p1); } END_TEST +#endif static void test_mult_border_cases_curve(const ecdsa_curve *curve) { bignum256 a; @@ -11033,10 +11117,12 @@ Suite *test_suite(void) { tcase_add_test(tc, test_tc_ecdsa_get_public_key65); tcase_add_test(tc, test_tc_ecdsa_recover_pub_from_sig); tcase_add_test(tc, test_tc_ecdsa_verify_digest); + tcase_add_test(tc, test_tc_ecdh_multiply); tcase_add_test(tc, test_zkp_ecdsa_get_public_key33); tcase_add_test(tc, test_zkp_ecdsa_get_public_key65); tcase_add_test(tc, test_zkp_ecdsa_recover_pub_from_sig); tcase_add_test(tc, test_zkp_ecdsa_verify_digest); + tcase_add_test(tc, test_zkp_ecdh_multiply); #if USE_RFC6979 tcase_add_test(tc, test_tc_ecdsa_sign_digest_deterministic); tcase_add_test(tc, test_zkp_ecdsa_sign_digest_deterministic); @@ -11150,10 +11236,12 @@ Suite *test_suite(void) { tcase_add_test(tc, test_pubkey_uncompress); suite_add_tcase(s, tc); +#if USE_PRECOMPUTED_CP tc = tcase_create("codepoints"); tcase_add_test(tc, test_codepoints_secp256k1); tcase_add_test(tc, test_codepoints_nist256p1); suite_add_tcase(s, tc); +#endif tc = tcase_create("mult_border_cases"); tcase_add_test(tc, test_mult_border_cases_secp256k1); diff --git a/crypto/zkp_ecdsa.c b/crypto/zkp_ecdsa.c index aca2c2177..6260e63a7 100644 --- a/crypto/zkp_ecdsa.c +++ b/crypto/zkp_ecdsa.c @@ -29,6 +29,7 @@ #include "zkp_context.h" #include "vendor/secp256k1-zkp/include/secp256k1.h" +#include "vendor/secp256k1-zkp/include/secp256k1_ecdh.h" #include "vendor/secp256k1-zkp/include/secp256k1_extrakeys.h" #include "vendor/secp256k1-zkp/include/secp256k1_preallocated.h" #include "vendor/secp256k1-zkp/include/secp256k1_recovery.h" @@ -40,6 +41,16 @@ static bool is_zero_digest(const uint8_t *digest) { return memcmp(digest, zeroes, 32) == 0; } +static size_t get_public_key_length(const uint8_t *public_key_bytes) { + if (public_key_bytes[0] == 0x04) { + return 65; + } else if (public_key_bytes[0] == 0x02 || public_key_bytes[0] == 0x03) { + return 33; + } else { + return 0; + } +} + // ECDSA compressed public key derivation // curve has to be &secp256k1 // private_key_bytes has 32 bytes @@ -321,16 +332,9 @@ int zkp_ecdsa_verify_digest(const ecdsa_curve *curve, int result = 0; - int public_key_length = 0; - - if (result == 0) { - if (public_key_bytes[0] == 0x04) { - public_key_length = 65; - } else if (public_key_bytes[0] == 0x02 || public_key_bytes[0] == 0x03) { - public_key_length = 33; - } else { - result = 1; - } + size_t public_key_length = get_public_key_length(public_key_bytes); + if (public_key_length == 0) { + result = 1; } if (result == 0) { @@ -403,3 +407,73 @@ int zkp_ecdsa_verify(const ecdsa_curve *curve, HasherType hasher_type, memzero(hash, sizeof(hash)); return result; } + +static int plain_hash_function(unsigned char *output, const unsigned char *x32, + const unsigned char *y32, void *data) { + (void)data; + output[0] = 0x04; + memcpy(output + 1, x32, 32); + memcpy(output + 33, y32, 32); + return 1; +} + +// ECDH multiplication +// curve has to be &secp256k1 +// private_key_bytes has 32 bytes +// public_key_bytes has 33 or 65 bytes +// session key has 65 bytes +// returns 0 on success +int zkp_ecdh_multiply(const ecdsa_curve *curve, + const uint8_t *private_key_bytes, + const uint8_t *public_key_bytes, uint8_t *session_key) { + assert(curve == &secp256k1); + if (curve != &secp256k1) { + return 1; + } + + int result = 0; + + size_t public_key_length = get_public_key_length(public_key_bytes); + if (public_key_length == 0) { + result = 1; + } + + const secp256k1_context *context_read_only = zkp_context_get_read_only(); + secp256k1_pubkey public_key = {0}; + + if (result == 0) { + if (secp256k1_ec_pubkey_parse(context_read_only, &public_key, + public_key_bytes, public_key_length) != 1) { + result = 1; + } + } + + secp256k1_context *context_writable = NULL; + if (result == 0) { + context_writable = zkp_context_acquire_writable(); + if (context_writable == NULL) { + result = 1; + } + } + if (result == 0) { + if (secp256k1_context_writable_randomize(context_writable) != 0) { + result = 1; + } + } + + if (result == 0) { + if (secp256k1_ecdh(context_writable, session_key, &public_key, + private_key_bytes, plain_hash_function, NULL) != 1) { + result = 1; + } + } + + if (context_writable) { + zkp_context_release_writable(); + context_writable = NULL; + } + + memzero(&public_key, sizeof(public_key)); + + return result; +} diff --git a/crypto/zkp_ecdsa.h b/crypto/zkp_ecdsa.h index aa9b0256f..2977eac29 100644 --- a/crypto/zkp_ecdsa.h +++ b/crypto/zkp_ecdsa.h @@ -27,4 +27,6 @@ int zkp_ecdsa_verify_digest(const ecdsa_curve *curve, int zkp_ecdsa_verify(const ecdsa_curve *curve, HasherType hasher_sign, const uint8_t *pub_key, const uint8_t *sig, const uint8_t *msg, uint32_t msg_len); +int zkp_ecdh_multiply(const ecdsa_curve *curve, const uint8_t *priv_key, + const uint8_t *pub_key, uint8_t *session_key); #endif diff --git a/legacy/firmware/Makefile b/legacy/firmware/Makefile index 3f003cd2c..f85ba1ab7 100644 --- a/legacy/firmware/Makefile +++ b/legacy/firmware/Makefile @@ -19,11 +19,12 @@ ZKP_CFLAGS = \ -DUSE_EXTERNAL_ASM \ -DUSE_EXTERNAL_DEFAULT_CALLBACKS \ -DECMULT_GEN_PREC_BITS=4 \ - -DECMULT_WINDOW_SIZE=8 \ + -DECMULT_WINDOW_SIZE=2 \ -DENABLE_MODULE_GENERATOR \ -DENABLE_MODULE_RECOVERY \ -DENABLE_MODULE_SCHNORRSIG \ - -DENABLE_MODULE_EXTRAKEYS + -DENABLE_MODULE_EXTRAKEYS \ + -DENABLE_MODULE_ECDH OBJS += secp256k1-zkp.o OBJS += precomputed_ecmult.o