1
+ //! From libsecp256k1:
2
+ //!
3
+ //! The Secp256k1 curve has an endomorphism, where lambda * (x, y) = (beta * x, y), where
4
+ //! lambda is {0x53,0x63,0xad,0x4c,0xc0,0x5c,0x30,0xe0,0xa5,0x26,0x1c,0x02,0x88,0x12,0x64,0x5a,
5
+ //! 0x12,0x2e,0x22,0xea,0x20,0x81,0x66,0x78,0xdf,0x02,0x96,0x7c,0x1b,0x23,0xbd,0x72}
6
+ //!
7
+ //! "Guide to Elliptic Curve Cryptography" (Hankerson, Menezes, Vanstone) gives an algorithm
8
+ //! (algorithm 3.74) to find k1 and k2 given k, such that k1 + k2 * lambda == k mod n, and k1
9
+ //! and k2 have a small size.
10
+ //! It relies on constants a1, b1, a2, b2. These constants for the value of lambda above are:
11
+ //!
12
+ //! - a1 = {0x30,0x86,0xd2,0x21,0xa7,0xd4,0x6b,0xcd,0xe8,0x6c,0x90,0xe4,0x92,0x84,0xeb,0x15}
13
+ //! - b1 = -{0xe4,0x43,0x7e,0xd6,0x01,0x0e,0x88,0x28,0x6f,0x54,0x7f,0xa9,0x0a,0xbf,0xe4,0xc3}
14
+ //! - a2 = {0x01,0x14,0xca,0x50,0xf7,0xa8,0xe2,0xf3,0xf6,0x57,0xc1,0x10,0x8d,0x9d,0x44,0xcf,0xd8}
15
+ //! - b2 = {0x30,0x86,0xd2,0x21,0xa7,0xd4,0x6b,0xcd,0xe8,0x6c,0x90,0xe4,0x92,0x84,0xeb,0x15}
16
+ //!
17
+ //! The algorithm then computes c1 = round(b1 * k / n) and c2 = round(b2 * k / n), and gives
18
+ //! k1 = k - (c1*a1 + c2*a2) and k2 = -(c1*b1 + c2*b2). Instead, we use modular arithmetic, and
19
+ //! compute k1 as k - k2 * lambda, avoiding the need for constants a1 and a2.
20
+ //!
21
+ //! g1, g2 are precomputed constants used to replace division with a rounded multiplication
22
+ //! when decomposing the scalar for an endomorphism-based point multiplication.
23
+ //!
24
+ //! The possibility of using precomputed estimates is mentioned in "Guide to Elliptic Curve
25
+ //! Cryptography" (Hankerson, Menezes, Vanstone) in section 3.5.
26
+ //!
27
+ //! The derivation is described in the paper "Efficient Software Implementation of Public-Key
28
+ //! Cryptography on Sensor Networks Using the MSP430X Microcontroller" (Gouvea, Oliveira, Lopez),
29
+ //! Section 4.3 (here we use a somewhat higher-precision estimate):
30
+ //! d = a1*b2 - b1*a2
31
+ //! g1 = round((2^272)*b2/d)
32
+ //! g2 = round((2^272)*b1/d)
33
+ //!
34
+ //! (Note that 'd' is also equal to the curve order here because [a1,b1] and [a2,b2] are found
35
+ //! as outputs of the Extended Euclidean Algorithm on inputs 'order' and 'lambda').
36
+ //!
37
+ //! @fjarri:
38
+ //!
39
+ //! To be precise, the method used here is based on "An Alternate Decomposition of an Integer for
40
+ //! Faster Point Multiplication on Certain Elliptic Curves" by Young-Ho Park, Sangtae Jeong,
41
+ //! Chang Han Kim, and Jongin Lim
42
+ //! (https://link.springer.com/chapter/10.1007%2F3-540-45664-3_23)
43
+ //!
44
+ //! The precision used for `g1` and `g2` is not enough to ensure correct approximation at all times.
45
+ //! For example, `2^272 * b1 / n` used to calculate `g2` is rounded down.
46
+ //! This means that the approximation `z' = k * g2 / 2^272` always slightly underestimates
47
+ //! the real value `z = b1 * k / n`. Therefore, when the fractional part of `z` is just slightly
48
+ //! above 0.5, it will be rounded up, but `z'` will have the fractional part slightly below 0.5 and
49
+ //! will be rounded down.
50
+ //!
51
+ //! The difference `z - z' = k * delta / 2^272`, where `delta = b1 * 2^272 mod n`.
52
+ //! The closest `z` can get to the fractional part equal to .5 is `1 / (2n)` (since `n` is odd).
53
+ //! Therefore, to guarantee that `z'` will always be rounded to the same value, one must have
54
+ //! `delta / 2^m < 1 / (2n * (n - 1))`, where `m` is the power of 2 used for the approximation.
55
+ //! This means that one should use at least `m = 512` (since `0 < delta < 1`).
56
+ //! Indeed, tests show that with only `m = 272` the approximation produces off-by-1 errors
57
+ //! occasionally.
58
+ //!
59
+ //! Now since `r1` is calculated as `k - r2 * lambda mod n`, the contract
60
+ //! `r1 + r2 * lambda = k mod n` is always satisfied. The method guarantees both `r1` and `r2` to be
61
+ //! less than `sqrt(n)` (so, fit in 128 bits) if the rounding is applied correctly - but in our case
62
+ //! the off-by-1 errors will produce different `r1` and `r2` which are not necessarily bounded by
63
+ //! `sqrt(n)`.
64
+ //!
65
+ //! In experiments, I was not able to detect any case where they would go outside the 128 bit bound,
66
+ //! but I cannot be sure that it cannot happen.
67
+
1
68
use crate :: arithmetic:: { scalar:: Scalar , ProjectivePoint } ;
2
69
use core:: ops:: { Mul , MulAssign } ;
3
70
use elliptic_curve:: subtle:: { Choice , ConditionallySelectable , ConstantTimeEq } ;
@@ -41,146 +108,32 @@ impl LookupTable {
41
108
}
42
109
}
43
110
44
- /// Returns `[a_0, ..., a_64]` such that `sum(a_j * 2^(j * 4)) == x`,
45
- /// and `-8 <= a_j <= 7`.
46
- #[ cfg( not( feature = "endomorphism-mul" ) ) ]
47
- fn to_radix_16 ( x : & Scalar ) -> [ i8 ; 65 ] {
48
- // `x` can have up to 256 bits, so we need an additional byte to store the carry.
49
- let mut output = [ 0i8 ; 65 ] ;
50
-
51
- // Step 1: change radix.
52
- // Convert from radix 256 (bytes) to radix 16 (nibbles)
53
- let bytes = x. to_bytes ( ) ;
54
- for i in 0 ..32 {
55
- output[ 2 * i] = ( bytes[ 31 - i] & 0xf ) as i8 ;
56
- output[ 2 * i + 1 ] = ( ( bytes[ 31 - i] >> 4 ) & 0xf ) as i8 ;
57
- }
58
-
59
- // Step 2: recenter coefficients from [0,16) to [-8,8)
60
- for i in 0 ..64 {
61
- let carry = ( output[ i] + 8 ) >> 4 ;
62
- output[ i] -= carry << 4 ;
63
- output[ i + 1 ] += carry;
64
- }
65
-
66
- output
67
- }
68
-
69
- #[ cfg( not( feature = "endomorphism-mul" ) ) ]
70
- fn mul_windowed ( x : & ProjectivePoint , k : & Scalar ) -> ProjectivePoint {
71
- let scalar_digits = to_radix_16 ( k) ;
72
- let lookup_table = LookupTable :: from ( x) ;
73
- let mut acc = lookup_table. select ( scalar_digits[ 64 ] ) ;
74
- for i in ( 0 ..64 ) . rev ( ) {
75
- for _j in 0 ..4 {
76
- acc = acc. double ( ) ;
77
- }
78
- acc += & lookup_table. select ( scalar_digits[ i] ) ;
79
- }
80
- acc
81
- }
82
-
83
- /*
84
- From libsecp256k1:
85
-
86
- The Secp256k1 curve has an endomorphism, where lambda * (x, y) = (beta * x, y), where
87
- lambda is {0x53,0x63,0xad,0x4c,0xc0,0x5c,0x30,0xe0,0xa5,0x26,0x1c,0x02,0x88,0x12,0x64,0x5a,
88
- 0x12,0x2e,0x22,0xea,0x20,0x81,0x66,0x78,0xdf,0x02,0x96,0x7c,0x1b,0x23,0xbd,0x72}
89
-
90
- "Guide to Elliptic Curve Cryptography" (Hankerson, Menezes, Vanstone) gives an algorithm
91
- (algorithm 3.74) to find k1 and k2 given k, such that k1 + k2 * lambda == k mod n, and k1
92
- and k2 have a small size.
93
- It relies on constants a1, b1, a2, b2. These constants for the value of lambda above are:
94
-
95
- - a1 = {0x30,0x86,0xd2,0x21,0xa7,0xd4,0x6b,0xcd,0xe8,0x6c,0x90,0xe4,0x92,0x84,0xeb,0x15}
96
- - b1 = -{0xe4,0x43,0x7e,0xd6,0x01,0x0e,0x88,0x28,0x6f,0x54,0x7f,0xa9,0x0a,0xbf,0xe4,0xc3}
97
- - a2 = {0x01,0x14,0xca,0x50,0xf7,0xa8,0xe2,0xf3,0xf6,0x57,0xc1,0x10,0x8d,0x9d,0x44,0xcf,0xd8}
98
- - b2 = {0x30,0x86,0xd2,0x21,0xa7,0xd4,0x6b,0xcd,0xe8,0x6c,0x90,0xe4,0x92,0x84,0xeb,0x15}
99
-
100
- The algorithm then computes c1 = round(b1 * k / n) and c2 = round(b2 * k / n), and gives
101
- k1 = k - (c1*a1 + c2*a2) and k2 = -(c1*b1 + c2*b2). Instead, we use modular arithmetic, and
102
- compute k1 as k - k2 * lambda, avoiding the need for constants a1 and a2.
103
-
104
- g1, g2 are precomputed constants used to replace division with a rounded multiplication
105
- when decomposing the scalar for an endomorphism-based point multiplication.
106
-
107
- The possibility of using precomputed estimates is mentioned in "Guide to Elliptic Curve
108
- Cryptography" (Hankerson, Menezes, Vanstone) in section 3.5.
109
-
110
- The derivation is described in the paper "Efficient Software Implementation of Public-Key
111
- Cryptography on Sensor Networks Using the MSP430X Microcontroller" (Gouvea, Oliveira, Lopez),
112
- Section 4.3 (here we use a somewhat higher-precision estimate):
113
- d = a1*b2 - b1*a2
114
- g1 = round((2^272)*b2/d)
115
- g2 = round((2^272)*b1/d)
116
-
117
- (Note that 'd' is also equal to the curve order here because [a1,b1] and [a2,b2] are found
118
- as outputs of the Extended Euclidean Algorithm on inputs 'order' and 'lambda').
119
- */
120
-
121
- /*
122
- @fjarri:
123
-
124
- To be precise, the method used here is based on
125
- "An Alternate Decomposition of an Integer for Faster Point Multiplication on Certain Elliptic Curves"
126
- by Young-Ho Park, Sangtae Jeong, Chang Han Kim, and Jongin Lim
127
- (https://link.springer.com/chapter/10.1007%2F3-540-45664-3_23)
128
-
129
- The precision used for `g1` and `g2` is not enough to ensure correct approximation at all times.
130
- For example, `2^272 * b1 / n` used to calculate `g2` is rounded down.
131
- This means that the approximation `z' = k * g2 / 2^272` always slightly underestimates
132
- the real value `z = b1 * k / n`. Therefore, when the fractional part of `z` is just slightly above
133
- 0.5, it will be rounded up, but `z'` will have the fractional part slightly below 0.5 and will be
134
- rounded down.
135
-
136
- The difference `z - z' = k * delta / 2^272`, where `delta = b1 * 2^272 mod n`.
137
- The closest `z` can get to the fractional part equal to .5 is `1 / (2n)` (since `n` is odd).
138
- Therefore, to guarantee that `z'` will always be rounded to the same value, one must have
139
- `delta / 2^m < 1 / (2n * (n - 1))`, where `m` is the power of 2 used for the approximation.
140
- This means that one should use at least `m = 512` (since `0 < delta < 1`).
141
- Indeed, tests show that with only `m = 272` the approximation produces off-by-1 errors occasionally.
142
-
143
- Now since `r1` is calculated as `k - r2 * lambda mod n`, the contract `r1 + r2 * lambda = k mod n`
144
- is always satisfied. The method guarantees both `r1` and `r2` to be less than `sqrt(n)`
145
- (so, fit in 128 bits) if the rounding is applied correctly - but in our case the off-by-1 errors
146
- will produce different `r1` and `r2` which are not necessarily bounded by `sqrt(n)`.
147
-
148
- In experiments, I was not able to detect any case where they would go outside the 128 bit bound,
149
- but I cannot be sure that it cannot happen.
150
- */
151
-
152
- #[ cfg( feature = "endomorphism-mul" ) ]
153
111
const MINUS_LAMBDA : Scalar = Scalar :: from_bytes_unchecked ( & [
154
112
0xac , 0x9c , 0x52 , 0xb3 , 0x3f , 0xa3 , 0xcf , 0x1f , 0x5a , 0xd9 , 0xe3 , 0xfd , 0x77 , 0xed , 0x9b , 0xa4 ,
155
113
0xa8 , 0x80 , 0xb9 , 0xfc , 0x8e , 0xc7 , 0x39 , 0xc2 , 0xe0 , 0xcf , 0xc8 , 0x10 , 0xb5 , 0x12 , 0x83 , 0xcf ,
156
114
] ) ;
157
115
158
- #[ cfg( feature = "endomorphism-mul" ) ]
159
116
const MINUS_B1 : Scalar = Scalar :: from_bytes_unchecked ( & [
160
117
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
161
118
0xe4 , 0x43 , 0x7e , 0xd6 , 0x01 , 0x0e , 0x88 , 0x28 , 0x6f , 0x54 , 0x7f , 0xa9 , 0x0a , 0xbf , 0xe4 , 0xc3 ,
162
119
] ) ;
163
120
164
- #[ cfg( feature = "endomorphism-mul" ) ]
165
121
const MINUS_B2 : Scalar = Scalar :: from_bytes_unchecked ( & [
166
122
0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xfe ,
167
123
0x8a , 0x28 , 0x0a , 0xc5 , 0x07 , 0x74 , 0x34 , 0x6d , 0xd7 , 0x65 , 0xcd , 0xa8 , 0x3d , 0xb1 , 0x56 , 0x2c ,
168
124
] ) ;
169
125
170
- #[ cfg( feature = "endomorphism-mul" ) ]
171
126
const G1 : Scalar = Scalar :: from_bytes_unchecked ( & [
172
127
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x30 , 0x86 ,
173
128
0xd2 , 0x21 , 0xa7 , 0xd4 , 0x6b , 0xcd , 0xe8 , 0x6c , 0x90 , 0xe4 , 0x92 , 0x84 , 0xeb , 0x15 , 0x3d , 0xab ,
174
129
] ) ;
175
130
176
- #[ cfg( feature = "endomorphism-mul" ) ]
177
131
const G2 : Scalar = Scalar :: from_bytes_unchecked ( & [
178
132
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0xe4 , 0x43 ,
179
133
0x7e , 0xd6 , 0x01 , 0x0e , 0x88 , 0x28 , 0x6f , 0x54 , 0x7f , 0xa9 , 0x0a , 0xbf , 0xe4 , 0xc4 , 0x22 , 0x12 ,
180
134
] ) ;
181
135
182
136
/// Find r1 and r2 given k, such that r1 + r2 * lambda == k mod n.
183
- #[ cfg( feature = "endomorphism-mul" ) ]
184
137
fn decompose_scalar ( k : & Scalar ) -> ( Scalar , Scalar ) {
185
138
// these _var calls are constant time since the shift amount is constant
186
139
let c1 = k. mul_shift_var ( & G1 , 272 ) ;
@@ -197,7 +150,6 @@ fn decompose_scalar(k: &Scalar) -> (Scalar, Scalar) {
197
150
/// Returns `[a_0, ..., a_32]` such that `sum(a_j * 2^(j * 4)) == x`,
198
151
/// and `-8 <= a_j <= 7`.
199
152
/// Assumes `x < 2^128`.
200
- #[ cfg( feature = "endomorphism-mul" ) ]
201
153
fn to_radix_16_half ( x : & Scalar ) -> [ i8 ; 33 ] {
202
154
// `x` can have up to 256 bits, so we need an additional byte to store the carry.
203
155
let mut output = [ 0i8 ; 33 ] ;
@@ -222,7 +174,6 @@ fn to_radix_16_half(x: &Scalar) -> [i8; 33] {
222
174
output
223
175
}
224
176
225
- #[ cfg( feature = "endomorphism-mul" ) ]
226
177
fn mul_windowed ( x : & ProjectivePoint , k : & Scalar ) -> ProjectivePoint {
227
178
let ( r1, r2) = decompose_scalar ( k) ;
228
179
let x_beta = x. endomorphism ( ) ;
0 commit comments