Line data Source code
1 : #include "fd_secp256k1_private.h"
2 :
3 : /* Given the coordinate X and the odd-ness of the Y coordinate, recovers Y and
4 : returns the affine group element. Returns NULL if there is no valid pair. */
5 : static inline fd_secp256k1_point_t *
6 2254 : fd_secp256k1_recovery_y( fd_secp256k1_point_t *r, fd_secp256k1_fp_t const *x, int odd ) {
7 2254 : fd_secp256k1_fp_t x2[1], x3[1];
8 :
9 : /* x^3 + b */
10 2254 : fd_secp256k1_fp_sqr( x2, x );
11 2254 : fd_secp256k1_fp_mul( x3, x2, x );
12 2254 : fd_secp256k1_fp_add( x3, x3, fd_secp256k1_const_b_mont );
13 :
14 : /* y^2 = x^3 + b <=> y = sqrt(x^3 + b) */
15 2254 : if( FD_UNLIKELY( !fd_secp256k1_fp_sqrt( r->y, x3 ) ) ) {
16 81 : return NULL;
17 81 : }
18 :
19 2173 : if( fd_secp256k1_fp_is_odd( r->y ) != odd ) {
20 1063 : fd_secp256k1_fp_negate( r->y, r->y );
21 1063 : }
22 :
23 2173 : fd_secp256k1_fp_set( r->x, x );
24 2173 : fd_secp256k1_fp_set( r->z, fd_secp256k1_const_one_mont );
25 2173 : return r;
26 2254 : }
27 :
28 : uchar *
29 : fd_secp256k1_recover( uchar public_key[64],
30 : uchar const msg_hash[32],
31 : uchar const sig[64],
32 2645 : int recovery_id ) {
33 2645 : if( FD_UNLIKELY( !( recovery_id>=0 && recovery_id<=3 ) ) ) {
34 : /* COV: the callers do the same check */
35 183 : return NULL;
36 183 : }
37 :
38 2462 : fd_secp256k1_scalar_t s[1];
39 2462 : fd_secp256k1_scalar_t rs[1];
40 2462 : if( FD_UNLIKELY( !fd_secp256k1_scalar_frombytes( rs, &sig[ 0 ] ) ) ) {
41 6 : return NULL;
42 6 : }
43 2456 : if( FD_UNLIKELY( !fd_secp256k1_scalar_frombytes( s, &sig[ 32 ] ) ) ) {
44 2 : return NULL;
45 2 : }
46 :
47 2454 : fd_secp256k1_fp_t r[1];
48 2454 : fd_secp256k1_fp_tomont( r, rs );
49 :
50 2454 : if( recovery_id & 2 ) {
51 : /* If rs >= p - n, return NULL. Otherwise, add the n to r.
52 : https://github.com/bitcoin-core/secp256k1/blob/v0.7.1/src/modules/recovery/main_impl.h#L104-L109 */
53 203 : if( FD_UNLIKELY( fd_uint256_cmp( rs, fd_secp256k1_const_p_minus_n ) >= 0 ) ) {
54 199 : return NULL;
55 199 : }
56 : /* Note that *only* r is incremented, rs is left unchanged. */
57 4 : fd_secp256k1_fp_add( r, r, fd_secp256k1_const_n_mont );
58 4 : }
59 :
60 : /* Recover the full public key group element. */
61 2255 : fd_secp256k1_point_t a[1];
62 2255 : if( FD_UNLIKELY( !fd_secp256k1_recovery_y( a, r, recovery_id & 1 ) ) ) {
63 81 : return NULL;
64 81 : }
65 :
66 2174 : fd_uint256_t msg[1];
67 2174 : memcpy( msg, msg_hash, 32 );
68 2174 : fd_uint256_bswap( msg, msg );
69 : /* The message scalar is unconditionally reduced to the scalar field.
70 : https://github.com/bitcoin-core/secp256k1/blob/v0.7.1/src/scalar_4x64_impl.h#L151 */
71 2174 : fd_secp256k1_scalar_reduce( msg, msg );
72 2174 : fd_secp256k1_scalar_tomont( msg, msg );
73 :
74 2174 : fd_secp256k1_scalar_t rn[1], u1[1], u2[1];
75 2174 : fd_secp256k1_point_t pubkey[1];
76 :
77 : /* We delay converting rs into montgomery domain since
78 : we may need to perform the comparison against p-n first. */
79 2174 : fd_secp256k1_scalar_tomont( s, s );
80 :
81 : /* Unfortunately s2n-bignum has no API for performing
82 : in-montgomery inversion, so we invert and then convert. */
83 2174 : fd_secp256k1_scalar_invert( rn, rs );
84 2174 : fd_secp256k1_scalar_tomont( rn, rn );
85 :
86 2174 : fd_secp256k1_scalar_mul ( u1, rn, msg );
87 2174 : fd_secp256k1_scalar_negate( u1, u1 );
88 2174 : fd_secp256k1_scalar_mul ( u2, rn, s );
89 :
90 2174 : fd_secp256k1_scalar_demont( u2, u2 );
91 2174 : fd_secp256k1_scalar_demont( u1, u1 );
92 2174 : fd_secp256k1_double_base_mul( pubkey, u1, a, u2 );
93 :
94 : /* If the computed pubkey is the identity point, we return NULL
95 : https://github.com/bitcoin-core/secp256k1/blob/v0.7.1/src/modules/recovery/main_impl.h#L120 */
96 2174 : if( FD_UNLIKELY( fd_secp256k1_point_is_identity( pubkey ) ) ) {
97 2 : return NULL;
98 2 : }
99 :
100 : /* Serialize the public key into an uncompressed form.
101 : The output does not have the recovery_id. */
102 2172 : fd_secp256k1_point_to_affine( pubkey, pubkey );
103 2172 : fd_secp256k1_fp_tobytes( &public_key[ 0 ], pubkey->x );
104 2172 : fd_secp256k1_fp_tobytes( &public_key[ 32 ], pubkey->y );
105 2172 : return public_key;
106 2174 : }
|