diff --git a/ecdsa.c b/ecdsa.c index 0a46190b7..78eedb49f 100644 --- a/ecdsa.c +++ b/ecdsa.c @@ -735,7 +735,12 @@ int ecdsa_sign_digest(const ecdsa_curve *curve, const uint8_t *priv_key, const u *pby = R.y.val[0] & 1; } // r = (rx mod n) - bn_mod(&R.x, &curve->order); + if (!bn_is_less(&R.x, &curve->order)) { + bn_subtract(&R.x, &curve->order, &R.x); + if (pby) { + *pby |= 2; + } + } // if r is zero, we fail if (bn_is_zero(&R.x)) { @@ -766,7 +771,7 @@ int ecdsa_sign_digest(const ecdsa_curve *curve, const uint8_t *priv_key, const u if (bn_is_less(&curve->order_half, &k)) { bn_subtract(&curve->order, &k, &k); if (pby) { - *pby = !*pby; + *pby ^= 1; } } // we are done, R.x and k is the result signature @@ -950,6 +955,55 @@ int ecdsa_verify_double(const ecdsa_curve *curve, const uint8_t *pub_key, const return res; } +// Compute public key from signature and recovery id. +// returns 0 if verification succeeded +int ecdsa_verify_digest_recover(const ecdsa_curve *curve, uint8_t *pub_key, const uint8_t *sig, const uint8_t *digest, int recid) +{ + bignum256 r, s, e; + curve_point cp, cp2; + + // read r and s + bn_read_be(sig, &r); + bn_read_be(sig + 32, &s); + if (!bn_is_less(&r, &curve->order) || bn_is_zero(&r)) { + return 1; + } + if (!bn_is_less(&s, &curve->order) || bn_is_zero(&s)) { + return 1; + } + // cp = R = k * G (k is secret nonce when signing) + memcpy(&cp.x, &r, sizeof(bignum256)); + if (recid & 2) { + bn_add(&cp.x, &curve->order); + if (!bn_is_less(&cp.x, &curve->prime)) { + return 1; + } + } + // compute y from x + uncompress_coords(curve, recid & 1, &cp.x, &cp.y); + if (!ecdsa_validate_pubkey(curve, &cp)) { + return 1; + } + // e = -digest + bn_read_be(digest, &e); + bn_mod(&e, &curve->order); + bn_subtract(&curve->order, &e, &e); + // r := r^-1 + bn_inverse(&r, &curve->order); + // cp := s * R = s * k *G + point_multiply(curve, &s, &cp, &cp); + // cp2 := -digest * G + scalar_multiply(curve, &e, &cp2); + // cp := (s * k - digest) * G = (r*priv) * G = r * Pub + point_add(curve, &cp2, &cp); + // cp := r^{-1} * r * Pub = Pub + point_multiply(curve, &r, &cp, &cp); + pub_key[0] = 0x04; + bn_write_be(&cp.x, pub_key + 1); + bn_write_be(&cp.y, pub_key + 33); + return 0; +} + // returns 0 if verification succeeded int ecdsa_verify_digest(const ecdsa_curve *curve, const uint8_t *pub_key, const uint8_t *sig, const uint8_t *digest) { diff --git a/ecdsa.h b/ecdsa.h index 2a6edde65..45cbb81d5 100644 --- a/ecdsa.h +++ b/ecdsa.h @@ -75,6 +75,7 @@ int ecdsa_validate_pubkey(const ecdsa_curve *curve, const curve_point *pub); int ecdsa_verify(const ecdsa_curve *curve, const uint8_t *pub_key, const uint8_t *sig, const uint8_t *msg, uint32_t msg_len); int ecdsa_verify_double(const ecdsa_curve *curve, const uint8_t *pub_key, const uint8_t *sig, const uint8_t *msg, uint32_t msg_len); int ecdsa_verify_digest(const ecdsa_curve *curve, const uint8_t *pub_key, const uint8_t *sig, const uint8_t *digest); +int ecdsa_verify_digest_recover(const ecdsa_curve *curve, uint8_t *pub_key, const uint8_t *sig, const uint8_t *digest, int recid); int ecdsa_sig_to_der(const uint8_t *sig, uint8_t *der); // Private