From d3d88591d0b5245343d09ec46afa75d65e5d1c2c Mon Sep 17 00:00:00 2001 From: Jochen Hoenicke Date: Sun, 2 Apr 2017 00:08:04 +0200 Subject: [PATCH] Added co-signing for ed25519. --- ecdsa.c | 21 ++++++++---- ecdsa.h | 3 +- ed25519-donna/ed25519.c | 71 ++++++++++++++++++++++++++++++++++++++++- ed25519-donna/ed25519.h | 5 +++ tests.c | 56 +++++++++++++++++++++++++++++++- 5 files changed, 147 insertions(+), 9 deletions(-) diff --git a/ecdsa.c b/ecdsa.c index 310dad325..81dc2a82f 100644 --- a/ecdsa.c +++ b/ecdsa.c @@ -658,7 +658,7 @@ int ecdh_multiply(const ecdsa_curve *curve, const uint8_t *priv_key, const uint8 return 0; } -void init_k_rfc6979(const uint8_t *priv_key, const uint8_t *hash, rfc6979_state *state) { +void init_rfc6979(const uint8_t *priv_key, const uint8_t *hash, rfc6979_state *state) { uint8_t bx[2*32]; uint8_t buf[32 + 1 + 2*32]; @@ -684,18 +684,27 @@ void init_k_rfc6979(const uint8_t *priv_key, const uint8_t *hash, rfc6979_state MEMSET_BZERO(buf, sizeof(buf)); } -// generate K in a deterministic way, according to RFC6979 -// http://tools.ietf.org/html/rfc6979 -void generate_k_rfc6979(bignum256 *k, rfc6979_state *state) +// generate next number from deterministic random number generator +void generate_rfc6979(uint8_t rand[32], rfc6979_state *state) { uint8_t buf[32 + 1]; hmac_sha256(state->k, sizeof(state->k), state->v, sizeof(state->v), state->v); - bn_read_be(state->v, k); memcpy(buf, state->v, sizeof(state->v)); buf[sizeof(state->v)] = 0x00; hmac_sha256(state->k, sizeof(state->k), buf, sizeof(state->v) + 1, state->k); hmac_sha256(state->k, sizeof(state->k), state->v, sizeof(state->v), state->v); + memcpy(rand, buf, 32); + MEMSET_BZERO(buf, sizeof(buf)); +} + +// generate K in a deterministic way, according to RFC6979 +// http://tools.ietf.org/html/rfc6979 +void generate_k_rfc6979(bignum256 *k, rfc6979_state *state) +{ + uint8_t buf[32]; + generate_rfc6979(buf, state); + bn_read_be(buf, k); MEMSET_BZERO(buf, sizeof(buf)); } @@ -739,7 +748,7 @@ int ecdsa_sign_digest(const ecdsa_curve *curve, const uint8_t *priv_key, const u #if USE_RFC6979 rfc6979_state rng; - init_k_rfc6979(priv_key, digest, &rng); + init_rfc6979(priv_key, digest, &rng); #endif bn_read_be(digest, &z); diff --git a/ecdsa.h b/ecdsa.h index 163558a7d..596ec1ec6 100644 --- a/ecdsa.h +++ b/ecdsa.h @@ -91,7 +91,8 @@ int ecdsa_verify_digest_recover(const ecdsa_curve *curve, uint8_t *pub_key, cons int ecdsa_sig_to_der(const uint8_t *sig, uint8_t *der); // Private -void init_k_rfc6979(const uint8_t *priv_key, const uint8_t *hash, rfc6979_state *rng); +void init_rfc6979(const uint8_t *priv_key, const uint8_t *hash, rfc6979_state *rng); +void generate_rfc6979(uint8_t rand[32], rfc6979_state *rng); void generate_k_rfc6979(bignum256 *k, rfc6979_state *rng); void generate_k_random(bignum256 *k, const bignum256 *prime); diff --git a/ed25519-donna/ed25519.c b/ed25519-donna/ed25519.c index 631d15f9d..9a91896b2 100644 --- a/ed25519-donna/ed25519.c +++ b/ed25519-donna/ed25519.c @@ -44,6 +44,75 @@ ed25519_publickey(const ed25519_secret_key sk, ed25519_public_key pk) { ge25519_pack(pk, &A); } +int +ed25519_cosi_combine_publickeys(ed25519_public_key res, const ed25519_public_key *pks, size_t n) { + size_t i = 0; + ge25519 P; + ge25519_pniels sump; + ge25519_p1p1 sump1; + + if (n == 1) { + memcpy(res, pks, sizeof(ed25519_public_key)); + return 0; + } + if (!ge25519_unpack_negative_vartime(&P, pks[i++])) + return -1; + ge25519_full_to_pniels(&sump, &P); + while (i < n-1) { + if (!ge25519_unpack_negative_vartime(&P, pks[i++])) + return -1; + ge25519_pnielsadd(&sump, &P, &sump); + } + if (!ge25519_unpack_negative_vartime(&P, pks[i++])) + return -1; + ge25519_pnielsadd_p1p1(&sump1, &P, &sump, 0); + ge25519_p1p1_to_partial(&P, &sump1); + curve25519_neg(P.x, P.x); + ge25519_pack(res, &P); + return 0; +} + +void +ed25519_cosi_combine_signatures(ed25519_signature res, const ed25519_public_key R, const ed25519_cosi_signature *sigs, size_t n) { + bignum256modm s, t; + size_t i; + + i = 0; + expand256_modm(s, sigs[i++], 32); + while (i < n) { + expand256_modm(t, sigs[i++], 32); + add256_modm(s, s, t); + } + memcpy(res, R, 32); + contract256_modm(res + 32, s); +} + +void +ed25519_cosi_sign(const unsigned char *m, size_t mlen, const ed25519_secret_key sk, const ed25519_secret_key nonce, const ed25519_public_key R, const ed25519_public_key pk, ed25519_cosi_signature sig) { + bignum256modm r, S, a; + hash_512bits extsk, extnonce, hram; + + ed25519_extsk(extsk, sk); + ed25519_extsk(extnonce, nonce); + + /* r = nonce */ + expand256_modm(r, extnonce, 32); + + /* S = H(R,A,m).. */ + ed25519_hram(hram, R, pk, m, mlen); + expand256_modm(S, hram, 64); + + /* S = H(R,A,m)a */ + expand256_modm(a, extsk, 32); + mul256_modm(S, S, a); + + /* S = (r + H(R,A,m)a) */ + add256_modm(S, S, r); + + /* S = (r + H(R,A,m)a) mod L */ + contract256_modm(sig, S); +} + void ed25519_sign(const unsigned char *m, size_t mlen, const ed25519_secret_key sk, const ed25519_public_key pk, ed25519_signature RS) { ed25519_hash_context ctx; @@ -75,7 +144,7 @@ ed25519_sign(const unsigned char *m, size_t mlen, const ed25519_secret_key sk, c /* S = (r + H(R,A,m)a) */ add256_modm(S, S, r); - /* S = (r + H(R,A,m)a) mod L */ + /* S = (r + H(R,A,m)a) mod L */ contract256_modm(RS + 32, S); } diff --git a/ed25519-donna/ed25519.h b/ed25519-donna/ed25519.h index 96a2727b4..2fa64e402 100644 --- a/ed25519-donna/ed25519.h +++ b/ed25519-donna/ed25519.h @@ -5,6 +5,7 @@ extern "C" { #endif +typedef unsigned char ed25519_cosi_signature[32]; typedef unsigned char ed25519_signature[64]; typedef unsigned char ed25519_public_key[32]; typedef unsigned char ed25519_secret_key[32]; @@ -12,6 +13,10 @@ typedef unsigned char ed25519_secret_key[32]; typedef unsigned char curve25519_key[32]; void ed25519_publickey(const ed25519_secret_key sk, ed25519_public_key pk); + +int ed25519_cosi_combine_publickeys(ed25519_public_key res, const ed25519_public_key *pks, size_t n); +void ed25519_cosi_combine_signatures(ed25519_signature res, const ed25519_public_key R, const ed25519_cosi_signature *sigs, size_t n); +void ed25519_cosi_sign(const unsigned char *m, size_t mlen, const ed25519_secret_key key, const ed25519_secret_key nonce, const ed25519_public_key R, const ed25519_public_key pk, ed25519_cosi_signature sig); int ed25519_sign_open(const unsigned char *m, size_t mlen, const ed25519_public_key pk, const ed25519_signature RS); void ed25519_sign(const unsigned char *m, size_t mlen, const ed25519_secret_key sk, const ed25519_public_key pk, ed25519_signature RS); diff --git a/tests.c b/tests.c index 97e0ec7ed..a9c37ab01 100644 --- a/tests.c +++ b/tests.c @@ -1287,7 +1287,7 @@ END_TEST #define test_deterministic(KEY, MSG, K) do { \ sha256_Raw((uint8_t *)MSG, strlen(MSG), buf); \ - init_k_rfc6979(fromhex(KEY), buf, &rng); \ + init_rfc6979(fromhex(KEY), buf, &rng); \ generate_k_rfc6979(&k, &rng); \ bn_write_be(&k, buf); \ ck_assert_mem_eq(buf, fromhex(K), 32); \ @@ -2650,6 +2650,56 @@ START_TEST(test_ed25519) { } END_TEST +START_TEST(test_ed25519_cosi) { + int MAXN=10; + ed25519_secret_key keys[MAXN]; + ed25519_public_key pubkeys[MAXN]; + ed25519_secret_key nonces[MAXN]; + ed25519_public_key Rs[MAXN]; + ed25519_cosi_signature sigs[MAXN]; + unsigned char msg[32]; + rfc6979_state rng; + int res; + + init_rfc6979(fromhex("26c76712d89d906e6672dafa614c42e5cb1caac8c6568e4d2493087db51f0d36"), + fromhex("26659c1cf7321c178c07437150639ff0c5b7679c7ea195253ed9abda2e081a37"), &rng); + + for (int N = 1; N < 11; N++) { + ed25519_public_key pk; + ed25519_public_key R; + ed25519_signature sig; + /* phase 0: create priv/pubkeys and combine pubkeys */ + for (int j = 0; j < N; j++) { + generate_rfc6979(keys[j], &rng); + ed25519_publickey(keys[j], pubkeys[j]); + } + res = ed25519_cosi_combine_publickeys(pk, pubkeys, N); + ck_assert_int_eq(res, 0); + + generate_rfc6979(msg, &rng); + + /* phase 1: create nonces, commitments (R values) and combine commitments */ + for (int j = 0; j < N; j++) { + generate_rfc6979(nonces[j], &rng); + ed25519_publickey(nonces[j], Rs[j]); + } + res = ed25519_cosi_combine_publickeys(R, Rs, N); + ck_assert_int_eq(res, 0); + + /* phase 2: sign and combine signatures */ + for (int j = 0; j < N; j++) { + ed25519_cosi_sign(msg, sizeof(msg), keys[j], nonces[j], R, pk, sigs[j]); + } + + ed25519_cosi_combine_signatures(sig, R, sigs, N); + + /* check signature */ + res = ed25519_sign_open(msg, sizeof(msg), pk, sig); + ck_assert_int_eq(res, 0); + } +} +END_TEST + static void test_bip32_ecdh_init_node(HDNode *node, const char *seed_str, const char *curve_name) { hdnode_from_seed((const uint8_t *)seed_str, strlen(seed_str), curve_name, node); hdnode_fill_public_key(node); @@ -3040,6 +3090,10 @@ Suite *test_suite(void) tcase_add_test(tc, test_ed25519); suite_add_tcase(s, tc); + tc = tcase_create("ed25519_cosi"); + tcase_add_test(tc, test_ed25519_cosi); + suite_add_tcase(s, tc); + tc = tcase_create("script"); tcase_add_test(tc, test_output_script); suite_add_tcase(s, tc);