secp256k1-native 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +14 -0
- data/LICENSE +21 -0
- data/README.md +139 -0
- data/ext/secp256k1_native/extconf.rb +24 -0
- data/ext/secp256k1_native/field.c +622 -0
- data/ext/secp256k1_native/jacobian.c +459 -0
- data/ext/secp256k1_native/scalar.c +421 -0
- data/ext/secp256k1_native/secp256k1_native.c +29 -0
- data/ext/secp256k1_native/secp256k1_native.h +123 -0
- data/lib/secp256k1/version.rb +5 -0
- data/lib/secp256k1.rb +626 -0
- data/lib/secp256k1_native.bundle +0 -0
- data/secp256k1-native.gemspec +29 -0
- metadata +58 -0
|
@@ -0,0 +1,622 @@
|
|
|
1
|
+
/* frozen_string_literal: true */
|
|
2
|
+
#include "secp256k1_native.h"
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
* field.c — Field arithmetic modulo the secp256k1 prime P.
|
|
6
|
+
*
|
|
7
|
+
* P = 2^256 - 2^32 - 977 (= 2^256 - 0x1000003D1)
|
|
8
|
+
*
|
|
9
|
+
* All internal functions accept and return uint256_t values whose limbs
|
|
10
|
+
* are in the range [0, P). Ruby-facing wrappers handle marshalling via
|
|
11
|
+
* rb_integer_pack / rb_integer_unpack.
|
|
12
|
+
*
|
|
13
|
+
* Constant-time discipline
|
|
14
|
+
* ------------------------
|
|
15
|
+
* fred, fsub, and fneg use branchless conditional selection (bitwise
|
|
16
|
+
* masks derived from carry/borrow flags) so that execution time does not
|
|
17
|
+
* depend on the field values. finv and fsqrt iterate over the bits of
|
|
18
|
+
* the public constants P-2 and (P+1)/4, which is safe because those
|
|
19
|
+
* constants are not secret.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/* -----------------------------------------------------------------------
|
|
23
|
+
* Ruby Integer <-> uint256_t marshalling — defined here (not in the header)
|
|
24
|
+
* so that only one copy of each function exists in the linked extension.
|
|
25
|
+
* ----------------------------------------------------------------------- */
|
|
26
|
+
|
|
27
|
+
uint256_t rb_to_uint256(VALUE rb_int)
|
|
28
|
+
{
|
|
29
|
+
uint256_t n;
|
|
30
|
+
memset(&n, 0, sizeof(n));
|
|
31
|
+
int result = rb_integer_pack(rb_int, n.d, 4, sizeof(uint64_t), 0, U256_PACK_FLAGS);
|
|
32
|
+
if (result < 0) {
|
|
33
|
+
rb_raise(rb_eArgError, "value is negative (expected non-negative integer)");
|
|
34
|
+
}
|
|
35
|
+
if (result > 1) {
|
|
36
|
+
rb_raise(rb_eArgError, "value exceeds 256 bits");
|
|
37
|
+
}
|
|
38
|
+
return n;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
VALUE uint256_to_rb(const uint256_t *n)
|
|
42
|
+
{
|
|
43
|
+
return rb_integer_unpack(n->d, 4, sizeof(uint64_t), 0, U256_PACK_FLAGS);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/* -----------------------------------------------------------------------
|
|
47
|
+
* Compile-time constants
|
|
48
|
+
* ----------------------------------------------------------------------- */
|
|
49
|
+
|
|
50
|
+
/* Fast-reduction constant: 2^256 ≡ 0x1000003D1 (mod P).
|
|
51
|
+
* This is the value c such that x mod P = x_lo + c × x_hi (two folds). */
|
|
52
|
+
#define FRED_C UINT64_C(0x1000003D1)
|
|
53
|
+
|
|
54
|
+
/* P - 2, the exponent for Fermat's little theorem (field inverse).
|
|
55
|
+
* P = FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F
|
|
56
|
+
* P-2 = FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2D
|
|
57
|
+
* Little-endian limb order (d[0] = least-significant). */
|
|
58
|
+
static const uint256_t P_MINUS_2 = {{
|
|
59
|
+
0xFFFFFFFEFFFFFC2DULL, /* bits 0-63 */
|
|
60
|
+
0xFFFFFFFFFFFFFFFFULL, /* bits 64-127 */
|
|
61
|
+
0xFFFFFFFFFFFFFFFFULL, /* bits 128-191 */
|
|
62
|
+
0xFFFFFFFFFFFFFFFFULL /* bits 192-255 */
|
|
63
|
+
}};
|
|
64
|
+
|
|
65
|
+
/* (P + 1) / 4, the exponent for the modular square root.
|
|
66
|
+
* (P+1)/4 = 3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBFFFFF0C
|
|
67
|
+
* Valid because P ≡ 3 (mod 4).
|
|
68
|
+
* Little-endian limb order. */
|
|
69
|
+
static const uint256_t P_PLUS1_DIV4 = {{
|
|
70
|
+
0xFFFFFFFFBFFFFF0CULL, /* bits 0-63 */
|
|
71
|
+
0xFFFFFFFFFFFFFFFFULL, /* bits 64-127 */
|
|
72
|
+
0xFFFFFFFFFFFFFFFFULL, /* bits 128-191 */
|
|
73
|
+
0x3FFFFFFFFFFFFFFFULL /* bits 192-255 */
|
|
74
|
+
}};
|
|
75
|
+
|
|
76
|
+
/* Field element 1 */
|
|
77
|
+
static const uint256_t FIELD_ONE = {{ 1ULL, 0ULL, 0ULL, 0ULL }};
|
|
78
|
+
|
|
79
|
+
/* -----------------------------------------------------------------------
|
|
80
|
+
* Low-level 256-bit helpers — declared in secp256k1_native.h so scalar.c
|
|
81
|
+
* and jacobian.c can call them without crossing the Ruby↔C boundary.
|
|
82
|
+
* ----------------------------------------------------------------------- */
|
|
83
|
+
|
|
84
|
+
/* Add two 256-bit integers; return carry (0 or 1). */
|
|
85
|
+
uint64_t uint256_add(uint256_t *r, const uint256_t *a, const uint256_t *b)
|
|
86
|
+
{
|
|
87
|
+
uint128_t acc;
|
|
88
|
+
uint64_t carry = 0;
|
|
89
|
+
int i;
|
|
90
|
+
for (i = 0; i < 4; i++) {
|
|
91
|
+
acc = (uint128_t)a->d[i] + b->d[i] + carry;
|
|
92
|
+
r->d[i] = (uint64_t)acc;
|
|
93
|
+
carry = (uint64_t)(acc >> 64);
|
|
94
|
+
}
|
|
95
|
+
return carry;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/* Subtract two 256-bit integers; return borrow (0 or 1).
|
|
99
|
+
*
|
|
100
|
+
* Uses __uint128_t so the borrow logic is unambiguous — no risk of
|
|
101
|
+
* overflow in the borrow expression. */
|
|
102
|
+
uint64_t uint256_sub(uint256_t *r, const uint256_t *a, const uint256_t *b)
|
|
103
|
+
{
|
|
104
|
+
uint128_t acc;
|
|
105
|
+
uint64_t borrow = 0;
|
|
106
|
+
int i;
|
|
107
|
+
for (i = 0; i < 4; i++) {
|
|
108
|
+
acc = (uint128_t)a->d[i] - b->d[i] - borrow;
|
|
109
|
+
r->d[i] = (uint64_t)acc;
|
|
110
|
+
/* Branchless borrow: extract the sign bit of the 128-bit result. */
|
|
111
|
+
borrow = (uint64_t)(acc >> 127);
|
|
112
|
+
}
|
|
113
|
+
return borrow;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/* Copy src into dst. */
|
|
117
|
+
void uint256_copy(uint256_t *dst, const uint256_t *src)
|
|
118
|
+
{
|
|
119
|
+
dst->d[0] = src->d[0];
|
|
120
|
+
dst->d[1] = src->d[1];
|
|
121
|
+
dst->d[2] = src->d[2];
|
|
122
|
+
dst->d[3] = src->d[3];
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/* Return 1 if bit i of x is set, 0 otherwise. */
|
|
126
|
+
int uint256_bit(const uint256_t *x, int i)
|
|
127
|
+
{
|
|
128
|
+
return (int)((x->d[i >> 6] >> (i & 63)) & 1);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/* Return 1 if x is zero, 0 otherwise — branchless.
|
|
132
|
+
* Declared in secp256k1_native.h so jacobian.c and scalar.c can call it. */
|
|
133
|
+
uint64_t uint256_is_zero(const uint256_t *x)
|
|
134
|
+
{
|
|
135
|
+
uint64_t v = x->d[0] | x->d[1] | x->d[2] | x->d[3];
|
|
136
|
+
/* If v == 0, ~v + 1 = 0 (overflow wraps); this is non-zero iff v != 0.
|
|
137
|
+
* Simpler: set bit 63 and shift; or just use carry arithmetic. */
|
|
138
|
+
v = v | (v >> 32); /* fold high into low */
|
|
139
|
+
v = v | (v >> 16);
|
|
140
|
+
v = v | (v >> 8);
|
|
141
|
+
v = v | (v >> 4);
|
|
142
|
+
v = v | (v >> 2);
|
|
143
|
+
v = v | (v >> 1);
|
|
144
|
+
return (v & 1) ^ 1; /* 1 if all bits were 0, 0 otherwise */
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/* -----------------------------------------------------------------------
|
|
148
|
+
* Internal field operations — visible to jacobian.c via the header
|
|
149
|
+
* ----------------------------------------------------------------------- */
|
|
150
|
+
|
|
151
|
+
/*
|
|
152
|
+
* fred_internal — fast reduction modulo P.
|
|
153
|
+
*
|
|
154
|
+
* Accepts a 512-bit value split into hi[4] (high 256 bits) and lo[4]
|
|
155
|
+
* (low 256 bits) and reduces to a 256-bit result in [0, P).
|
|
156
|
+
*
|
|
157
|
+
* Exploits P = 2^256 - c where c = 0x1000003D1:
|
|
158
|
+
* x mod P = x_lo + c × x_hi (first fold)
|
|
159
|
+
* The first fold can produce at most ~288 bits; one more fold suffices:
|
|
160
|
+
* result = first_lo + c × first_hi
|
|
161
|
+
* After two folds the value fits in 256 bits plus a tiny overflow that
|
|
162
|
+
* a single conditional subtraction of P handles.
|
|
163
|
+
*
|
|
164
|
+
* The final subtraction is branchless: compute r - P; if the result
|
|
165
|
+
* underflows (borrow), keep r; otherwise keep r - P.
|
|
166
|
+
*/
|
|
167
|
+
void fred_internal(uint256_t *r, const uint256_t *hi, const uint256_t *lo)
|
|
168
|
+
{
|
|
169
|
+
/* First fold: tmp = lo + c × hi
|
|
170
|
+
*
|
|
171
|
+
* c × hi can be split: c = 2^32 + 977
|
|
172
|
+
* c × hi = hi << 32 + 977 × hi
|
|
173
|
+
*
|
|
174
|
+
* We accumulate into 5 limbs (extra carry limb at index 4) to avoid
|
|
175
|
+
* losing bits, then do a second fold on whatever sits above bit 255. */
|
|
176
|
+
|
|
177
|
+
uint128_t acc;
|
|
178
|
+
uint64_t carry;
|
|
179
|
+
int i;
|
|
180
|
+
|
|
181
|
+
/* tmp: 5 limbs to capture overflow from the first fold */
|
|
182
|
+
uint64_t tmp[5];
|
|
183
|
+
|
|
184
|
+
/* Compute c × hi with carry. c fits in 33 bits, hi fits in 64 bits
|
|
185
|
+
* each, so each product fits in 97 bits — safe in uint128_t. */
|
|
186
|
+
acc = 0;
|
|
187
|
+
carry = 0;
|
|
188
|
+
for (i = 0; i < 4; i++) {
|
|
189
|
+
acc = (uint128_t)hi->d[i] * FRED_C + lo->d[i] + carry;
|
|
190
|
+
tmp[i] = (uint64_t)acc;
|
|
191
|
+
carry = (uint64_t)(acc >> 64);
|
|
192
|
+
}
|
|
193
|
+
tmp[4] = carry;
|
|
194
|
+
|
|
195
|
+
/* Second fold: overflow = tmp[4].
|
|
196
|
+
*
|
|
197
|
+
* After the first fold, tmp[4] can be as large as ~0x1000003D0 (about
|
|
198
|
+
* 33 bits), because hi < P < 2^256 and c = 0x1000003D1, so the carry
|
|
199
|
+
* out of the first fold is at most (P-1)*c / 2^256 < c.
|
|
200
|
+
*
|
|
201
|
+
* We need to add overflow × c back into tmp[0..3]. overflow × c may
|
|
202
|
+
* be as large as ~0x1000003D1 * 0x1000003D1 ≈ 2^66, so it spans two
|
|
203
|
+
* 64-bit limbs. Use uint128_t to handle the full product correctly. */
|
|
204
|
+
uint64_t overflow = tmp[4];
|
|
205
|
+
uint128_t fold = (uint128_t)overflow * FRED_C;
|
|
206
|
+
acc = (uint128_t)tmp[0] + (uint64_t)fold;
|
|
207
|
+
r->d[0] = (uint64_t)acc;
|
|
208
|
+
carry = (uint64_t)(acc >> 64) + (uint64_t)(fold >> 64);
|
|
209
|
+
|
|
210
|
+
for (i = 1; i < 4; i++) {
|
|
211
|
+
acc = (uint128_t)tmp[i] + carry;
|
|
212
|
+
r->d[i] = (uint64_t)acc;
|
|
213
|
+
carry = (uint64_t)(acc >> 64);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/* The second fold loop can produce carry == 1, meaning r = 2^256 + r_low.
|
|
217
|
+
* Since 2^256 ≡ FRED_C (mod P), add carry × FRED_C to fold the overflow.
|
|
218
|
+
* carry is at most 1, so carry × FRED_C ≤ FRED_C < 2^34 — fits easily.
|
|
219
|
+
* This third micro-fold always terminates: r_low < 2^256 and FRED_C < 2^34,
|
|
220
|
+
* so r_low + FRED_C < 2^256 + 2^34 which yields a carry of at most 1 into
|
|
221
|
+
* the 64-bit boundary; that carry ripples at most through the 256-bit word
|
|
222
|
+
* and produces no further overflow. */
|
|
223
|
+
uint128_t adjust = (uint128_t)carry * FRED_C;
|
|
224
|
+
acc = (uint128_t)r->d[0] + (uint64_t)adjust;
|
|
225
|
+
r->d[0] = (uint64_t)acc;
|
|
226
|
+
carry = (uint64_t)(acc >> 64) + (uint64_t)(adjust >> 64);
|
|
227
|
+
for (i = 1; i < 4; i++) {
|
|
228
|
+
acc = (uint128_t)r->d[i] + carry;
|
|
229
|
+
r->d[i] = (uint64_t)acc;
|
|
230
|
+
carry = (uint64_t)(acc >> 64);
|
|
231
|
+
}
|
|
232
|
+
/* Now carry is guaranteed 0 and r < 2P. */
|
|
233
|
+
|
|
234
|
+
/* Branchless final conditional subtraction.
|
|
235
|
+
*
|
|
236
|
+
* Compute reduced = r - P. If it underflows (borrow == 1), r < P so keep r.
|
|
237
|
+
* If borrow == 0, r >= P so keep reduced. */
|
|
238
|
+
uint256_t reduced;
|
|
239
|
+
uint64_t borrow = uint256_sub(&reduced, r, &FIELD_P);
|
|
240
|
+
|
|
241
|
+
/* mask = all 1s if borrow == 1 (keep r), all 0s if borrow == 0 (keep reduced). */
|
|
242
|
+
uint64_t mask = -(uint64_t)(borrow != 0);
|
|
243
|
+
for (i = 0; i < 4; i++) {
|
|
244
|
+
r->d[i] = (r->d[i] & mask) | (reduced.d[i] & ~mask);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/*
|
|
249
|
+
* fmul_internal — 256×256 → 512-bit product, then fred_internal.
|
|
250
|
+
*
|
|
251
|
+
* Uses 4×4 schoolbook multiplication with uint128_t accumulators.
|
|
252
|
+
* The 8-limb product is split into hi[4] and lo[4] for fred_internal.
|
|
253
|
+
*/
|
|
254
|
+
void fmul_internal(uint256_t *r, const uint256_t *a, const uint256_t *b)
|
|
255
|
+
{
|
|
256
|
+
/* 8-limb product accumulator */
|
|
257
|
+
uint64_t product[8];
|
|
258
|
+
uint128_t acc;
|
|
259
|
+
uint64_t carry;
|
|
260
|
+
int i, j;
|
|
261
|
+
|
|
262
|
+
for (i = 0; i < 8; i++) product[i] = 0;
|
|
263
|
+
|
|
264
|
+
for (i = 0; i < 4; i++) {
|
|
265
|
+
carry = 0;
|
|
266
|
+
for (j = 0; j < 4; j++) {
|
|
267
|
+
acc = (uint128_t)a->d[i] * b->d[j] + product[i + j] + carry;
|
|
268
|
+
product[i + j] = (uint64_t)acc;
|
|
269
|
+
carry = (uint64_t)(acc >> 64);
|
|
270
|
+
}
|
|
271
|
+
acc = (uint128_t)product[i + 4] + carry;
|
|
272
|
+
product[i + 4] = (uint64_t)acc;
|
|
273
|
+
if (i < 3) product[i + 5] += (uint64_t)(acc >> 64);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
uint256_t lo, hi;
|
|
277
|
+
lo.d[0] = product[0]; lo.d[1] = product[1];
|
|
278
|
+
lo.d[2] = product[2]; lo.d[3] = product[3];
|
|
279
|
+
hi.d[0] = product[4]; hi.d[1] = product[5];
|
|
280
|
+
hi.d[2] = product[6]; hi.d[3] = product[7];
|
|
281
|
+
|
|
282
|
+
fred_internal(r, &hi, &lo);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/*
|
|
286
|
+
* fsqr_internal — optimised squaring using the identity that cross-terms
|
|
287
|
+
* appear twice.
|
|
288
|
+
*
|
|
289
|
+
* For a = (a0, a1, a2, a3), a² has:
|
|
290
|
+
* - Diagonal terms: ai² (4 terms)
|
|
291
|
+
* - Cross terms: 2 × ai × aj for i < j (6 terms)
|
|
292
|
+
* This saves 6 multiplications vs generic fmul.
|
|
293
|
+
*/
|
|
294
|
+
void fsqr_internal(uint256_t *r, const uint256_t *a)
|
|
295
|
+
{
|
|
296
|
+
/* Use the same schoolbook approach as fmul_internal but with b == a.
|
|
297
|
+
*
|
|
298
|
+
* The optimised squaring identity (cross-terms × 2) is mathematically
|
|
299
|
+
* correct but requires care when the double-width product overflows
|
|
300
|
+
* uint128_t. For simplicity and correctness we delegate to fmul_internal
|
|
301
|
+
* directly; the compiler typically optimises a² to use the same codepath.
|
|
302
|
+
* A hand-tuned squaring optimisation can be added later as a separate task. */
|
|
303
|
+
fmul_internal(r, a, a);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/*
|
|
307
|
+
* fadd_internal — modular addition.
|
|
308
|
+
*
|
|
309
|
+
* Computes a + b, then branchlessly subtracts P if the result >= P.
|
|
310
|
+
*/
|
|
311
|
+
void fadd_internal(uint256_t *r, const uint256_t *a, const uint256_t *b)
|
|
312
|
+
{
|
|
313
|
+
uint256_t sum;
|
|
314
|
+
uint64_t overflow = uint256_add(&sum, a, b);
|
|
315
|
+
|
|
316
|
+
/* If overflow or sum >= P, subtract P. */
|
|
317
|
+
uint256_t reduced;
|
|
318
|
+
uint64_t borrow = uint256_sub(&reduced, &sum, &FIELD_P);
|
|
319
|
+
|
|
320
|
+
/* Keep reduced unless (no overflow AND borrow).
|
|
321
|
+
* If overflow == 1 : sum > 2^256 > P, so we definitely want reduced.
|
|
322
|
+
* If overflow == 0 and borrow == 0 : sum >= P, want reduced.
|
|
323
|
+
* If overflow == 0 and borrow == 1 : sum < P, want sum.
|
|
324
|
+
* Combined: keep sum iff (overflow == 0 && borrow == 1). */
|
|
325
|
+
uint64_t keep_original = (~overflow) & borrow;
|
|
326
|
+
uint64_t mask = -(uint64_t)(keep_original != 0); /* all 1s iff sum < P */
|
|
327
|
+
int i;
|
|
328
|
+
for (i = 0; i < 4; i++) {
|
|
329
|
+
r->d[i] = (sum.d[i] & mask) | (reduced.d[i] & ~mask);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/*
|
|
334
|
+
* fsub_internal — modular subtraction.
|
|
335
|
+
*
|
|
336
|
+
* Computes a - b; if the result underflows, adds P back — branchlessly.
|
|
337
|
+
*/
|
|
338
|
+
void fsub_internal(uint256_t *r, const uint256_t *a, const uint256_t *b)
|
|
339
|
+
{
|
|
340
|
+
uint256_t diff;
|
|
341
|
+
uint64_t borrow = uint256_sub(&diff, a, b);
|
|
342
|
+
|
|
343
|
+
/* If borrow == 1 then a < b and we need to add P. */
|
|
344
|
+
uint256_t corrected;
|
|
345
|
+
uint64_t carry = uint256_add(&corrected, &diff, &FIELD_P);
|
|
346
|
+
(void)carry; /* carry is 0 here since diff + P < 2^256 when borrow == 1 */
|
|
347
|
+
|
|
348
|
+
/* mask: all 1s if borrow == 1 (use corrected), all 0s otherwise (use diff). */
|
|
349
|
+
uint64_t mask = -(uint64_t)(borrow != 0);
|
|
350
|
+
int i;
|
|
351
|
+
for (i = 0; i < 4; i++) {
|
|
352
|
+
r->d[i] = (corrected.d[i] & mask) | (diff.d[i] & ~mask);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/*
|
|
357
|
+
* fneg_internal — modular negation.
|
|
358
|
+
*
|
|
359
|
+
* Returns P - a for non-zero a, and 0 for a == 0 — branchlessly.
|
|
360
|
+
*/
|
|
361
|
+
void fneg_internal(uint256_t *r, const uint256_t *a)
|
|
362
|
+
{
|
|
363
|
+
uint256_t negated;
|
|
364
|
+
uint256_sub(&negated, &FIELD_P, a); /* P - a; no borrow since a <= P-1 */
|
|
365
|
+
|
|
366
|
+
/* If a == 0 the result should be 0, not P. */
|
|
367
|
+
uint64_t is_zero = uint256_is_zero(a);
|
|
368
|
+
uint64_t mask = -(uint64_t)(is_zero != 0); /* all 1s if a is zero */
|
|
369
|
+
int i;
|
|
370
|
+
for (i = 0; i < 4; i++) {
|
|
371
|
+
/* zero mask: 0 where is_zero, negated.d[i] where not */
|
|
372
|
+
r->d[i] = negated.d[i] & ~mask;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/*
|
|
377
|
+
* finv_internal — modular inverse via Fermat's little theorem.
|
|
378
|
+
*
|
|
379
|
+
* Computes a^(P-2) mod P using square-and-multiply over the 256 bits of P-2.
|
|
380
|
+
* The exponent P-2 is a public constant so branching on its bits is safe.
|
|
381
|
+
*/
|
|
382
|
+
void finv_internal(uint256_t *r, const uint256_t *a)
|
|
383
|
+
{
|
|
384
|
+
uint256_t result;
|
|
385
|
+
uint256_t base;
|
|
386
|
+
uint256_copy(&result, &FIELD_ONE);
|
|
387
|
+
uint256_copy(&base, a);
|
|
388
|
+
|
|
389
|
+
/* Process bits from MSB (255) to LSB (0). */
|
|
390
|
+
int i;
|
|
391
|
+
for (i = 255; i >= 0; i--) {
|
|
392
|
+
fsqr_internal(&result, &result);
|
|
393
|
+
if (uint256_bit(&P_MINUS_2, i)) {
|
|
394
|
+
fmul_internal(&result, &result, &base);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
uint256_copy(r, &result);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/*
|
|
401
|
+
* fsqrt_internal — modular square root via a^((P+1)/4) mod P.
|
|
402
|
+
*
|
|
403
|
+
* Valid because P ≡ 3 (mod 4). Returns 1 if a is a quadratic residue,
|
|
404
|
+
* 0 otherwise. The result is written to *r in both cases; the caller
|
|
405
|
+
* should check the return value and discard *r if it returns 0.
|
|
406
|
+
*/
|
|
407
|
+
int fsqrt_internal(uint256_t *r, const uint256_t *a)
|
|
408
|
+
{
|
|
409
|
+
uint256_t result;
|
|
410
|
+
uint256_t base;
|
|
411
|
+
uint256_copy(&result, &FIELD_ONE);
|
|
412
|
+
uint256_copy(&base, a);
|
|
413
|
+
|
|
414
|
+
/* Compute a^((P+1)/4) */
|
|
415
|
+
int i;
|
|
416
|
+
for (i = 255; i >= 0; i--) {
|
|
417
|
+
fsqr_internal(&result, &result);
|
|
418
|
+
if (uint256_bit(&P_PLUS1_DIV4, i)) {
|
|
419
|
+
fmul_internal(&result, &result, &base);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/* Verify: r² must equal a mod P */
|
|
424
|
+
uint256_t check;
|
|
425
|
+
fsqr_internal(&check, &result);
|
|
426
|
+
|
|
427
|
+
/* Reduce a mod P to compare */
|
|
428
|
+
uint256_t a_reduced;
|
|
429
|
+
uint256_t zero = {{ 0ULL, 0ULL, 0ULL, 0ULL }};
|
|
430
|
+
fred_internal(&a_reduced, &zero, a);
|
|
431
|
+
|
|
432
|
+
/* Compare limb by limb */
|
|
433
|
+
uint64_t diff = 0;
|
|
434
|
+
for (i = 0; i < 4; i++) {
|
|
435
|
+
diff |= (check.d[i] ^ a_reduced.d[i]);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
if (diff != 0) return 0; /* not a quadratic residue */
|
|
439
|
+
|
|
440
|
+
uint256_copy(r, &result);
|
|
441
|
+
return 1;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/* -----------------------------------------------------------------------
|
|
445
|
+
* Ruby-facing wrapper functions
|
|
446
|
+
* ----------------------------------------------------------------------- */
|
|
447
|
+
|
|
448
|
+
/*
|
|
449
|
+
* call-seq:
|
|
450
|
+
* Secp256k1Native.fred(x) -> Integer
|
|
451
|
+
*
|
|
452
|
+
* Fast reduction: returns +x+ mod P.
|
|
453
|
+
* Accepts a value up to 512 bits wide (stored in a Ruby Integer).
|
|
454
|
+
*/
|
|
455
|
+
static VALUE rb_fred(VALUE self, VALUE x)
|
|
456
|
+
{
|
|
457
|
+
(void)self;
|
|
458
|
+
/* fred is used for reducing wide intermediates. Pack into 8 limbs. */
|
|
459
|
+
uint64_t limbs[8];
|
|
460
|
+
memset(limbs, 0, sizeof(limbs));
|
|
461
|
+
int result = rb_integer_pack(x, limbs, 8, sizeof(uint64_t), 0, U256_PACK_FLAGS);
|
|
462
|
+
if (result < 0) {
|
|
463
|
+
rb_raise(rb_eArgError, "value is negative");
|
|
464
|
+
}
|
|
465
|
+
if (result > 1) {
|
|
466
|
+
rb_raise(rb_eArgError, "value exceeds 512 bits");
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
uint256_t lo, hi;
|
|
470
|
+
lo.d[0] = limbs[0]; lo.d[1] = limbs[1];
|
|
471
|
+
lo.d[2] = limbs[2]; lo.d[3] = limbs[3];
|
|
472
|
+
hi.d[0] = limbs[4]; hi.d[1] = limbs[5];
|
|
473
|
+
hi.d[2] = limbs[6]; hi.d[3] = limbs[7];
|
|
474
|
+
|
|
475
|
+
uint256_t r;
|
|
476
|
+
fred_internal(&r, &hi, &lo);
|
|
477
|
+
return uint256_to_rb(&r);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
/*
|
|
481
|
+
* call-seq:
|
|
482
|
+
* Secp256k1Native.fmul(a, b) -> Integer
|
|
483
|
+
*
|
|
484
|
+
* Modular multiplication: returns +(a * b) mod P+.
|
|
485
|
+
*/
|
|
486
|
+
static VALUE rb_fmul(VALUE self, VALUE a, VALUE b)
|
|
487
|
+
{
|
|
488
|
+
(void)self;
|
|
489
|
+
uint256_t ua = rb_to_uint256(a);
|
|
490
|
+
uint256_t ub = rb_to_uint256(b);
|
|
491
|
+
uint256_t r;
|
|
492
|
+
fmul_internal(&r, &ua, &ub);
|
|
493
|
+
return uint256_to_rb(&r);
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/*
|
|
497
|
+
* call-seq:
|
|
498
|
+
* Secp256k1Native.fsqr(a) -> Integer
|
|
499
|
+
*
|
|
500
|
+
* Modular squaring: returns +(a * a) mod P+.
|
|
501
|
+
*/
|
|
502
|
+
static VALUE rb_fsqr(VALUE self, VALUE a)
|
|
503
|
+
{
|
|
504
|
+
(void)self;
|
|
505
|
+
uint256_t ua = rb_to_uint256(a);
|
|
506
|
+
uint256_t r;
|
|
507
|
+
fsqr_internal(&r, &ua);
|
|
508
|
+
return uint256_to_rb(&r);
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
/*
|
|
512
|
+
* call-seq:
|
|
513
|
+
* Secp256k1Native.fadd(a, b) -> Integer
|
|
514
|
+
*
|
|
515
|
+
* Modular addition: returns +(a + b) mod P+.
|
|
516
|
+
*/
|
|
517
|
+
static VALUE rb_fadd(VALUE self, VALUE a, VALUE b)
|
|
518
|
+
{
|
|
519
|
+
(void)self;
|
|
520
|
+
uint256_t ua = rb_to_uint256(a);
|
|
521
|
+
uint256_t ub = rb_to_uint256(b);
|
|
522
|
+
uint256_t r;
|
|
523
|
+
fadd_internal(&r, &ua, &ub);
|
|
524
|
+
return uint256_to_rb(&r);
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/*
|
|
528
|
+
* call-seq:
|
|
529
|
+
* Secp256k1Native.fsub(a, b) -> Integer
|
|
530
|
+
*
|
|
531
|
+
* Modular subtraction: returns +(a - b) mod P+.
|
|
532
|
+
*/
|
|
533
|
+
static VALUE rb_fsub(VALUE self, VALUE a, VALUE b)
|
|
534
|
+
{
|
|
535
|
+
(void)self;
|
|
536
|
+
uint256_t ua = rb_to_uint256(a);
|
|
537
|
+
uint256_t ub = rb_to_uint256(b);
|
|
538
|
+
uint256_t r;
|
|
539
|
+
fsub_internal(&r, &ua, &ub);
|
|
540
|
+
return uint256_to_rb(&r);
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
/*
|
|
544
|
+
* call-seq:
|
|
545
|
+
* Secp256k1Native.fneg(a) -> Integer
|
|
546
|
+
*
|
|
547
|
+
* Modular negation: returns +(-a) mod P+ (i.e. +P - a+ for non-zero a).
|
|
548
|
+
*/
|
|
549
|
+
static VALUE rb_fneg(VALUE self, VALUE a)
|
|
550
|
+
{
|
|
551
|
+
(void)self;
|
|
552
|
+
uint256_t ua = rb_to_uint256(a);
|
|
553
|
+
uint256_t r;
|
|
554
|
+
fneg_internal(&r, &ua);
|
|
555
|
+
return uint256_to_rb(&r);
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
/*
|
|
559
|
+
* call-seq:
|
|
560
|
+
* Secp256k1Native.finv(a) -> Integer
|
|
561
|
+
*
|
|
562
|
+
* Modular inverse: returns +a^(P-2) mod P+.
|
|
563
|
+
*
|
|
564
|
+
* @raise [ArgumentError] if a is zero.
|
|
565
|
+
*/
|
|
566
|
+
static VALUE rb_finv(VALUE self, VALUE a)
|
|
567
|
+
{
|
|
568
|
+
(void)self;
|
|
569
|
+
uint256_t ua = rb_to_uint256(a);
|
|
570
|
+
|
|
571
|
+
/* Reduce a mod P before zero-checking */
|
|
572
|
+
uint256_t zero_limbs = {{ 0ULL, 0ULL, 0ULL, 0ULL }};
|
|
573
|
+
uint256_t a_reduced;
|
|
574
|
+
fred_internal(&a_reduced, &zero_limbs, &ua);
|
|
575
|
+
|
|
576
|
+
if (uint256_is_zero(&a_reduced)) {
|
|
577
|
+
rb_raise(rb_eArgError, "field inverse is undefined for zero");
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
uint256_t r;
|
|
581
|
+
finv_internal(&r, &a_reduced);
|
|
582
|
+
return uint256_to_rb(&r);
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
/*
|
|
586
|
+
* call-seq:
|
|
587
|
+
* Secp256k1Native.fsqrt(a) -> Integer or nil
|
|
588
|
+
*
|
|
589
|
+
* Modular square root: returns +a^((P+1)/4) mod P+, or +nil+ if +a+ is
|
|
590
|
+
* not a quadratic residue modulo P.
|
|
591
|
+
*/
|
|
592
|
+
static VALUE rb_fsqrt(VALUE self, VALUE a)
|
|
593
|
+
{
|
|
594
|
+
(void)self;
|
|
595
|
+
uint256_t ua = rb_to_uint256(a);
|
|
596
|
+
|
|
597
|
+
/* Reduce a mod P first */
|
|
598
|
+
uint256_t zero_limbs = {{ 0ULL, 0ULL, 0ULL, 0ULL }};
|
|
599
|
+
uint256_t a_reduced;
|
|
600
|
+
fred_internal(&a_reduced, &zero_limbs, &ua);
|
|
601
|
+
|
|
602
|
+
uint256_t r;
|
|
603
|
+
int ok = fsqrt_internal(&r, &a_reduced);
|
|
604
|
+
if (!ok) return Qnil;
|
|
605
|
+
return uint256_to_rb(&r);
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
/* -----------------------------------------------------------------------
|
|
609
|
+
* Registration — called from Init_secp256k1_native in secp256k1_native.c
|
|
610
|
+
* ----------------------------------------------------------------------- */
|
|
611
|
+
|
|
612
|
+
void register_field_methods(VALUE mod)
|
|
613
|
+
{
|
|
614
|
+
rb_define_module_function(mod, "fred", rb_fred, 1);
|
|
615
|
+
rb_define_module_function(mod, "fmul", rb_fmul, 2);
|
|
616
|
+
rb_define_module_function(mod, "fsqr", rb_fsqr, 1);
|
|
617
|
+
rb_define_module_function(mod, "fadd", rb_fadd, 2);
|
|
618
|
+
rb_define_module_function(mod, "fsub", rb_fsub, 2);
|
|
619
|
+
rb_define_module_function(mod, "fneg", rb_fneg, 1);
|
|
620
|
+
rb_define_module_function(mod, "finv", rb_finv, 1);
|
|
621
|
+
rb_define_module_function(mod, "fsqrt", rb_fsqrt, 1);
|
|
622
|
+
}
|