From 50eb498ececbbe97eb26969c0858b94bf1f14287 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Sun, 5 May 2013 02:46:07 +0200 Subject: [PATCH] Compact signatures/pubkey recovery --- include/secp256k1.h | 20 +++++++++++++++ src/ecdsa.h | 3 ++- src/impl/ecdsa.h | 61 ++++++++++++++++++++++++++++++++++++++++----- src/secp256k1.c | 44 +++++++++++++++++++++++++++++++- src/tests.c | 2 +- 5 files changed, 121 insertions(+), 9 deletions(-) diff --git a/include/secp256k1.h b/include/secp256k1.h index 369a95e996c..49e22f89e30 100644 --- a/include/secp256k1.h +++ b/include/secp256k1.h @@ -42,6 +42,26 @@ int secp256k1_ecdsa_sign(const unsigned char *msg, int msglen, const unsigned char *seckey, const unsigned char *nonce); +/** Create a compact ECDSA signature (64 byte + recovery id). + * Returns: 1: signature created + * 0: nonce invalid, try another one + * In: msg: the message being signed + * msglen: the length of the message being signed + * seckey: pointer to a 32-byte secret key (assumed to be valid) + * nonce: pointer to a 32-byte nonce (generated with a cryptographic PRNG) + * Out: sig: pointer to a 64-byte array where the signature will be placed. + * recid: pointer to an int, which will be updated to contain the recovery id. + */ +int secp256k1_ecdsa_sign_compact(const unsigned char *msg, int msglen, + unsigned char *sig64, + const unsigned char *seckey, + const unsigned char *nonce, + int *recid); + +int secp256k1_ecdsa_recover_compact(const unsigned char *msg, int msglen, + const unsigned char *sig64, + unsigned char *pubkey, int *pubkeylen, + int compressed, int recid); /** Verify an ECDSA secret key. * Returns: 1: secret key is valid diff --git a/src/ecdsa.h b/src/ecdsa.h index b18e5065c57..3bb17391c30 100644 --- a/src/ecdsa.h +++ b/src/ecdsa.h @@ -15,7 +15,8 @@ void static secp256k1_ecdsa_pubkey_serialize(secp256k1_ge_t *elem, unsigned char int static secp256k1_ecdsa_sig_parse(secp256k1_ecdsa_sig_t *r, const unsigned char *sig, int size); int static secp256k1_ecdsa_sig_serialize(unsigned char *sig, int *size, const secp256k1_ecdsa_sig_t *a); int static secp256k1_ecdsa_sig_verify(const secp256k1_ecdsa_sig_t *sig, const secp256k1_ge_t *pubkey, const secp256k1_num_t *message); -int static secp256k1_ecdsa_sig_sign(secp256k1_ecdsa_sig_t *sig, const secp256k1_num_t *seckey, const secp256k1_num_t *message, const secp256k1_num_t *nonce); +int static secp256k1_ecdsa_sig_sign(secp256k1_ecdsa_sig_t *sig, const secp256k1_num_t *seckey, const secp256k1_num_t *message, const secp256k1_num_t *nonce, int *recid); +int static secp256k1_ecdsa_sig_recover(const secp256k1_ecdsa_sig_t *sig, secp256k1_ge_t *pubkey, const secp256k1_num_t *message, int recid); void static secp256k1_ecdsa_sig_set_rs(secp256k1_ecdsa_sig_t *sig, const secp256k1_num_t *r, const secp256k1_num_t *s); #endif diff --git a/src/impl/ecdsa.h b/src/impl/ecdsa.h index 51afe20973f..fd37e2a4ec6 100644 --- a/src/impl/ecdsa.h +++ b/src/impl/ecdsa.h @@ -106,6 +106,49 @@ int static secp256k1_ecdsa_sig_recompute(secp256k1_num_t *r2, const secp256k1_ec return ret; } +int static secp256k1_ecdsa_sig_recover(const secp256k1_ecdsa_sig_t *sig, secp256k1_ge_t *pubkey, const secp256k1_num_t *message, int recid) { + const secp256k1_ge_consts_t *c = secp256k1_ge_consts; + + if (secp256k1_num_is_neg(&sig->r) || secp256k1_num_is_neg(&sig->s)) + return 0; + if (secp256k1_num_is_zero(&sig->r) || secp256k1_num_is_zero(&sig->s)) + return 0; + if (secp256k1_num_cmp(&sig->r, &c->order) >= 0 || secp256k1_num_cmp(&sig->s, &c->order) >= 0) + return 0; + + secp256k1_num_t rx; + secp256k1_num_init(&rx); + secp256k1_num_copy(&rx, &sig->r); + if (recid & 2) + secp256k1_num_add(&rx, &rx, &c->order); + unsigned char brx[32]; + secp256k1_num_get_bin(brx, 32, &rx); + secp256k1_num_free(&rx); + secp256k1_fe_t fx; + secp256k1_fe_set_b32(&fx, brx); + secp256k1_ge_t x; + secp256k1_ge_set_xo(&x, &fx, recid & 1); + if (!secp256k1_ge_is_valid(&x)) + return 0; + secp256k1_gej_t xj; + secp256k1_gej_set_ge(&xj, &x); + secp256k1_num_t rn, u1, u2; + secp256k1_num_init(&rn); + secp256k1_num_init(&u1); + secp256k1_num_init(&u2); + secp256k1_num_mod_inverse(&rn, &sig->r, &c->order); + secp256k1_num_mod_mul(&u1, &rn, message, &c->order); + secp256k1_num_sub(&u1, &c->order, &u1); + secp256k1_num_mod_mul(&u2, &rn, &sig->s, &c->order); + secp256k1_gej_t qj; + secp256k1_ecmult(&qj, &xj, &u2, &u1); + secp256k1_ge_set_gej(pubkey, &qj); + secp256k1_num_free(&rn); + secp256k1_num_free(&u1); + secp256k1_num_free(&u2); + return 1; +} + int static secp256k1_ecdsa_sig_verify(const secp256k1_ecdsa_sig_t *sig, const secp256k1_ge_t *pubkey, const secp256k1_num_t *message) { secp256k1_num_t r2; secp256k1_num_init(&r2); @@ -115,17 +158,20 @@ int static secp256k1_ecdsa_sig_verify(const secp256k1_ecdsa_sig_t *sig, const se return ret; } -int static secp256k1_ecdsa_sig_sign(secp256k1_ecdsa_sig_t *sig, const secp256k1_num_t *seckey, const secp256k1_num_t *message, const secp256k1_num_t *nonce) { +int static secp256k1_ecdsa_sig_sign(secp256k1_ecdsa_sig_t *sig, const secp256k1_num_t *seckey, const secp256k1_num_t *message, const secp256k1_num_t *nonce, int *recid) { const secp256k1_ge_consts_t *c = secp256k1_ge_consts; secp256k1_gej_t rp; secp256k1_ecmult_gen(&rp, nonce); - secp256k1_fe_t rx; - secp256k1_gej_get_x(&rx, &rp); + secp256k1_ge_t r; + secp256k1_ge_set_gej(&r, &rp); unsigned char b[32]; - secp256k1_fe_normalize(&rx); - secp256k1_fe_get_b32(b, &rx); + secp256k1_fe_normalize(&r.x); + secp256k1_fe_normalize(&r.y); + secp256k1_fe_get_b32(b, &r.x); secp256k1_num_set_bin(&sig->r, b, 32); + if (recid) + *recid = (secp256k1_num_cmp(&sig->r, &c->order) >= 0 ? 2 : 0) | (secp256k1_fe_is_odd(&r.y) ? 1 : 0); secp256k1_num_mod(&sig->r, &c->order); secp256k1_num_t n; secp256k1_num_init(&n); @@ -137,8 +183,11 @@ int static secp256k1_ecdsa_sig_sign(secp256k1_ecdsa_sig_t *sig, const secp256k1_ secp256k1_num_free(&n); if (secp256k1_num_is_zero(&sig->s)) return 0; - if (secp256k1_num_is_odd(&sig->s)) + if (secp256k1_num_is_odd(&sig->s)) { secp256k1_num_sub(&sig->s, &c->order, &sig->s); + if (recid) + *recid ^= 1; + } return 1; } diff --git a/src/secp256k1.c b/src/secp256k1.c index 64ce6d07821..0b67f7bcac3 100644 --- a/src/secp256k1.c +++ b/src/secp256k1.c @@ -54,7 +54,7 @@ int secp256k1_ecdsa_sign(const unsigned char *message, int messagelen, unsigned secp256k1_num_set_bin(&msg, message, messagelen); secp256k1_ecdsa_sig_t sig; secp256k1_ecdsa_sig_init(&sig); - int ret = secp256k1_ecdsa_sig_sign(&sig, &sec, &msg, &non); + int ret = secp256k1_ecdsa_sig_sign(&sig, &sec, &msg, &non, NULL); if (ret) { secp256k1_ecdsa_sig_serialize(signature, signaturelen, &sig); } @@ -65,6 +65,48 @@ int secp256k1_ecdsa_sign(const unsigned char *message, int messagelen, unsigned return ret; } +int secp256k1_ecdsa_sign_compact(const unsigned char *message, int messagelen, unsigned char *sig64, const unsigned char *seckey, const unsigned char *nonce, int *recid) { + secp256k1_num_t sec, non, msg; + secp256k1_num_init(&sec); + secp256k1_num_init(&non); + secp256k1_num_init(&msg); + secp256k1_num_set_bin(&sec, seckey, 32); + secp256k1_num_set_bin(&non, nonce, 32); + secp256k1_num_set_bin(&msg, message, messagelen); + secp256k1_ecdsa_sig_t sig; + secp256k1_ecdsa_sig_init(&sig); + int ret = secp256k1_ecdsa_sig_sign(&sig, &sec, &msg, &non, recid); + if (ret) { + secp256k1_num_get_bin(sig64, 32, &sig.r); + secp256k1_num_get_bin(sig64 + 32, 32, &sig.s); + } + secp256k1_ecdsa_sig_free(&sig); + secp256k1_num_free(&msg); + secp256k1_num_free(&non); + secp256k1_num_free(&sec); + return ret; +} + +int secp256k1_ecdsa_recover_compact(const unsigned char *msg, int msglen, const unsigned char *sig64, unsigned char *pubkey, int *pubkeylen, int compressed, int recid) { + int ret = 0; + secp256k1_num_t m; + secp256k1_num_init(&m); + secp256k1_ecdsa_sig_t sig; + secp256k1_ecdsa_sig_init(&sig); + secp256k1_num_set_bin(&sig.r, sig64, 32); + secp256k1_num_set_bin(&sig.s, sig64 + 32, 32); + secp256k1_num_set_bin(&m, msg, msglen); + + secp256k1_ge_t q; + if (secp256k1_ecdsa_sig_recover(&sig, &q, &m, recid)) { + secp256k1_ecdsa_pubkey_serialize(&q, pubkey, pubkeylen, compressed); + ret = 1; + } + secp256k1_ecdsa_sig_free(&sig); + secp256k1_num_free(&m); + return ret; +} + int secp256k1_ecdsa_seckey_verify(const unsigned char *seckey) { secp256k1_num_t sec; secp256k1_num_init(&sec); diff --git a/src/tests.c b/src/tests.c index 547c90201a0..0de1be54837 100644 --- a/src/tests.c +++ b/src/tests.c @@ -344,7 +344,7 @@ void test_ecdsa_sign_verify() { secp256k1_ecdsa_sig_init(&sig); do { random_num_order_test(&nonce); - } while(!secp256k1_ecdsa_sig_sign(&sig, &key, &msg, &nonce)); + } while(!secp256k1_ecdsa_sig_sign(&sig, &key, &msg, &nonce, NULL)); assert(secp256k1_ecdsa_sig_verify(&sig, &pub, &msg)); secp256k1_num_inc(&msg); assert(!secp256k1_ecdsa_sig_verify(&sig, &pub, &msg));