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,421 @@
|
|
|
1
|
+
/* frozen_string_literal: true */
|
|
2
|
+
#include "secp256k1_native.h"
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
* scalar.c — Scalar arithmetic modulo the secp256k1 curve order N.
|
|
6
|
+
*
|
|
7
|
+
* N = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
|
|
8
|
+
*
|
|
9
|
+
* Reduction strategy
|
|
10
|
+
* ------------------
|
|
11
|
+
* c_N = 2^256 - N = 0x14551231950B75FC4402DA1732FC9BEBF (129 bits)
|
|
12
|
+
*
|
|
13
|
+
* For a 512-bit product P = hi:lo (both 256 bits), we want P mod N.
|
|
14
|
+
*
|
|
15
|
+
* P mod N = lo + c_N × hi (mod N)
|
|
16
|
+
*
|
|
17
|
+
* c_N × hi can be at most 2^129 × (2^256 - 1) < 2^385, which is 385 bits.
|
|
18
|
+
* After adding lo (256 bits) the sum fits in at most 385 bits, i.e.
|
|
19
|
+
* 129 bits above bit 255.
|
|
20
|
+
*
|
|
21
|
+
* We perform this fold once using an 8-limb accumulator. Then the overflow
|
|
22
|
+
* above bit 255 is at most ~2^129, and we fold again. After two folds the
|
|
23
|
+
* result fits in 256 bits plus at most 1 bit of overflow that a conditional
|
|
24
|
+
* subtraction of N handles.
|
|
25
|
+
*
|
|
26
|
+
* The fold is done scalar_reduce(), which accepts the 8-limb product buffer
|
|
27
|
+
* directly so no intermediate allocation is needed.
|
|
28
|
+
*
|
|
29
|
+
* Constant-time discipline
|
|
30
|
+
* ------------------------
|
|
31
|
+
* scalar_reduce, scalar_add_internal use branchless conditional selection.
|
|
32
|
+
* scalar_inv_internal iterates over bits of the public constant N-2, which
|
|
33
|
+
* is safe.
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
/* -----------------------------------------------------------------------
|
|
37
|
+
* Compile-time constants for N reduction
|
|
38
|
+
* ----------------------------------------------------------------------- */
|
|
39
|
+
|
|
40
|
+
/*
|
|
41
|
+
* Fold constant c_N = 2^256 - N, split into three 64-bit limbs.
|
|
42
|
+
*
|
|
43
|
+
* c_N = 0x014551231950B75FC4402DA1732FC9BEBF
|
|
44
|
+
*
|
|
45
|
+
* Limb 0 (bits 0- 63): 0x402DA1732FC9BEBF
|
|
46
|
+
* Limb 1 (bits 64-127): 0x4551231950B75FC4
|
|
47
|
+
* Limb 2 (bit 128): 0x0000000000000001
|
|
48
|
+
* Limbs 3+ are zero.
|
|
49
|
+
*/
|
|
50
|
+
#define CN_LO UINT64_C(0x402DA1732FC9BEBF)
|
|
51
|
+
#define CN_MID UINT64_C(0x4551231950B75FC4)
|
|
52
|
+
/* Limb 2 = 1 (i.e. c_N has a single set bit at position 128) */
|
|
53
|
+
|
|
54
|
+
/* N - 2, the exponent for Fermat's little theorem (scalar inverse).
|
|
55
|
+
* N-2 is N with the least-significant 64-bit word decremented by 2.
|
|
56
|
+
* Little-endian limb order (d[0] = least-significant). */
|
|
57
|
+
static const uint256_t N_MINUS_2 = {{
|
|
58
|
+
0xBFD25E8CD036413FULL, /* bits 0-63: N[0] - 2 */
|
|
59
|
+
0xBAAEDCE6AF48A03BULL, /* bits 64-127 */
|
|
60
|
+
0xFFFFFFFFFFFFFFFEULL, /* bits 128-191 */
|
|
61
|
+
0xFFFFFFFFFFFFFFFFULL /* bits 192-255 */
|
|
62
|
+
}};
|
|
63
|
+
|
|
64
|
+
/* Scalar element 1 */
|
|
65
|
+
static const uint256_t SCALAR_ONE = {{ 1ULL, 0ULL, 0ULL, 0ULL }};
|
|
66
|
+
|
|
67
|
+
/* -----------------------------------------------------------------------
|
|
68
|
+
* Modular reduction mod N
|
|
69
|
+
* ----------------------------------------------------------------------- */
|
|
70
|
+
|
|
71
|
+
/*
|
|
72
|
+
* scalar_reduce_limbs — reduce an 8-limb (512-bit) product modulo N.
|
|
73
|
+
*
|
|
74
|
+
* The limbs array is product[0..7] in little-endian order.
|
|
75
|
+
*
|
|
76
|
+
* Strategy:
|
|
77
|
+
*
|
|
78
|
+
* First fold: add c_N × product[4..7] into product[0..3].
|
|
79
|
+
*
|
|
80
|
+
* c_N = CN_LO + CN_MID×2^64 + 1×2^128
|
|
81
|
+
*
|
|
82
|
+
* For each high limb h[j] = product[4+j] (j = 0..3), we add:
|
|
83
|
+
* h[j] × CN_LO into accumulator starting at position j
|
|
84
|
+
* h[j] × CN_MID into accumulator starting at position j+1
|
|
85
|
+
* h[j] × 1 into accumulator starting at position j+2
|
|
86
|
+
*
|
|
87
|
+
* Equivalently, we walk the 8-limb array from position 4 downwards,
|
|
88
|
+
* folding each limb into the low four limbs.
|
|
89
|
+
*
|
|
90
|
+
* After the first fold the 512-bit value has been reduced to at most
|
|
91
|
+
* ~385 bits. The overflow above bit 255 (stored in the temporary carry
|
|
92
|
+
* words) requires a second fold. After two folds the result fits in
|
|
93
|
+
* 256 bits + at most 1 bit, handled by a conditional subtraction.
|
|
94
|
+
*
|
|
95
|
+
* We accumulate into an 8-limb array and reuse the upper limbs as
|
|
96
|
+
* temporaries for the folded-in contributions, so no extra allocation is
|
|
97
|
+
* needed.
|
|
98
|
+
*/
|
|
99
|
+
static void scalar_reduce_limbs(uint256_t *r, uint64_t product[8])
|
|
100
|
+
{
|
|
101
|
+
uint128_t acc;
|
|
102
|
+
int i;
|
|
103
|
+
|
|
104
|
+
/* ----------------------------------------------------------------
|
|
105
|
+
* First fold: multiply product[4..7] by c_N and add into [0..7].
|
|
106
|
+
*
|
|
107
|
+
* We process high limbs j = 0..3 (corresponding to product[4..7]).
|
|
108
|
+
* For limb h = product[4+j], we fold:
|
|
109
|
+
* position j: h × CN_LO
|
|
110
|
+
* position j+1: h × CN_MID
|
|
111
|
+
* position j+2: h × 1 (the 2^128 term)
|
|
112
|
+
*
|
|
113
|
+
* We walk j from 0 to 3, updating product[j..j+2] with carries.
|
|
114
|
+
* Since j goes up to 3 and we write to j+2 <= 5, we can overwrite
|
|
115
|
+
* product[4..7] freely after we've read them.
|
|
116
|
+
*
|
|
117
|
+
* Implementation: iterate over j = 0..3, accumulate into a running
|
|
118
|
+
* carry array. To keep it clean, we use a separate 8-limb result
|
|
119
|
+
* buffer initialised from product[0..3].
|
|
120
|
+
* ---------------------------------------------------------------- */
|
|
121
|
+
|
|
122
|
+
/* Copy the low 4 limbs into an 8-limb result buffer.
|
|
123
|
+
* Upper 4 limbs will accumulate folded-in contributions. */
|
|
124
|
+
uint64_t t[8];
|
|
125
|
+
for (i = 0; i < 4; i++) t[i] = product[i];
|
|
126
|
+
for (i = 4; i < 8; i++) t[i] = 0;
|
|
127
|
+
|
|
128
|
+
/* Fold each high limb into t. */
|
|
129
|
+
for (i = 0; i < 4; i++) {
|
|
130
|
+
uint64_t h = product[4 + i];
|
|
131
|
+
|
|
132
|
+
/* h × CN_LO → accumulate into t[i] with carry propagation. */
|
|
133
|
+
acc = (uint128_t)h * CN_LO + t[i];
|
|
134
|
+
t[i] = (uint64_t)acc;
|
|
135
|
+
acc = (acc >> 64);
|
|
136
|
+
/* Carry from CN_LO product — propagate. */
|
|
137
|
+
acc += (uint128_t)h * CN_MID + t[i + 1];
|
|
138
|
+
t[i + 1] = (uint64_t)acc;
|
|
139
|
+
acc = (acc >> 64);
|
|
140
|
+
/* h × 2^128 — just add h into t[i+2] with carry. */
|
|
141
|
+
acc += (uint128_t)h + t[i + 2];
|
|
142
|
+
t[i + 2] = (uint64_t)acc;
|
|
143
|
+
/* Propagate carry into t[i+3]. */
|
|
144
|
+
acc = (acc >> 64) + t[i + 3];
|
|
145
|
+
t[i + 3] = (uint64_t)acc;
|
|
146
|
+
/* Any carry beyond i+3 (into t[i+4]) is handled in the next
|
|
147
|
+
* iteration or in the second fold below. */
|
|
148
|
+
if (i < 3) {
|
|
149
|
+
t[i + 4] += (uint64_t)(acc >> 64);
|
|
150
|
+
}
|
|
151
|
+
/* When i == 3, t[7] already holds the final carry — discard it
|
|
152
|
+
* after the second fold. */
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/* ----------------------------------------------------------------
|
|
156
|
+
* Second fold: t[4..7] now holds the overflow from the first fold.
|
|
157
|
+
* Fold it again in the same way.
|
|
158
|
+
* ---------------------------------------------------------------- */
|
|
159
|
+
|
|
160
|
+
/* Save current t[4..7] as the new "high" limbs, then zero them. */
|
|
161
|
+
uint64_t hi2[4];
|
|
162
|
+
for (i = 0; i < 4; i++) { hi2[i] = t[4 + i]; t[4 + i] = 0; }
|
|
163
|
+
|
|
164
|
+
for (i = 0; i < 4; i++) {
|
|
165
|
+
uint64_t h = hi2[i];
|
|
166
|
+
if (h == 0) continue;
|
|
167
|
+
|
|
168
|
+
acc = (uint128_t)h * CN_LO + t[i];
|
|
169
|
+
t[i] = (uint64_t)acc;
|
|
170
|
+
acc = (acc >> 64);
|
|
171
|
+
|
|
172
|
+
acc += (uint128_t)h * CN_MID + t[i + 1];
|
|
173
|
+
t[i + 1] = (uint64_t)acc;
|
|
174
|
+
acc = (acc >> 64);
|
|
175
|
+
|
|
176
|
+
acc += (uint128_t)h + t[i + 2];
|
|
177
|
+
t[i + 2] = (uint64_t)acc;
|
|
178
|
+
acc = (acc >> 64);
|
|
179
|
+
|
|
180
|
+
acc = (uint128_t)(uint64_t)acc + t[i + 3];
|
|
181
|
+
t[i + 3] = (uint64_t)acc;
|
|
182
|
+
if (i < 3) t[i + 4] += (uint64_t)(acc >> 64);
|
|
183
|
+
/* After two folds, any carry here is negligible (< 2). */
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/* The result is now in t[0..3] with at most a tiny overflow in t[4].
|
|
187
|
+
* Copy t[0..3] into r and apply a conditional subtraction. */
|
|
188
|
+
r->d[0] = t[0]; r->d[1] = t[1]; r->d[2] = t[2]; r->d[3] = t[3];
|
|
189
|
+
|
|
190
|
+
/* Handle residual overflow from t[4] (at most 1 after two folds of
|
|
191
|
+
* a 512-bit input): add t[4] × c_N into r. */
|
|
192
|
+
uint64_t carry3 = t[4];
|
|
193
|
+
if (carry3) {
|
|
194
|
+
/* carry3 is at most a few bits wide — use simple arithmetic. */
|
|
195
|
+
uint128_t a0 = (uint128_t)carry3 * CN_LO + r->d[0];
|
|
196
|
+
r->d[0] = (uint64_t)a0;
|
|
197
|
+
uint128_t a1 = (uint128_t)carry3 * CN_MID + r->d[1] + (a0 >> 64);
|
|
198
|
+
r->d[1] = (uint64_t)a1;
|
|
199
|
+
uint128_t a2 = (uint128_t)carry3 + r->d[2] + (a1 >> 64);
|
|
200
|
+
r->d[2] = (uint64_t)a2;
|
|
201
|
+
r->d[3] += (uint64_t)(a2 >> 64);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/* Branchless final conditional subtraction: keep r - N if r >= N. */
|
|
205
|
+
uint256_t reduced;
|
|
206
|
+
uint64_t borrow = uint256_sub(&reduced, r, &CURVE_N);
|
|
207
|
+
uint64_t mask = -(uint64_t)(borrow != 0); /* all 1s if r < N */
|
|
208
|
+
for (i = 0; i < 4; i++) {
|
|
209
|
+
r->d[i] = (r->d[i] & mask) | (reduced.d[i] & ~mask);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/*
|
|
214
|
+
* scalar_reduce — reduce a 512-bit value (hi:lo) modulo N.
|
|
215
|
+
*
|
|
216
|
+
* Public entry point used by rb_scalar_mod for reducing arbitrary-width
|
|
217
|
+
* (up to 512-bit) values passed from Ruby.
|
|
218
|
+
*/
|
|
219
|
+
void scalar_reduce(uint256_t *r, const uint256_t *hi, const uint256_t *lo)
|
|
220
|
+
{
|
|
221
|
+
uint64_t product[8];
|
|
222
|
+
product[0] = lo->d[0]; product[1] = lo->d[1];
|
|
223
|
+
product[2] = lo->d[2]; product[3] = lo->d[3];
|
|
224
|
+
product[4] = hi->d[0]; product[5] = hi->d[1];
|
|
225
|
+
product[6] = hi->d[2]; product[7] = hi->d[3];
|
|
226
|
+
scalar_reduce_limbs(r, product);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/* -----------------------------------------------------------------------
|
|
230
|
+
* Internal scalar operations — visible to jacobian.c via the header
|
|
231
|
+
* ----------------------------------------------------------------------- */
|
|
232
|
+
|
|
233
|
+
/*
|
|
234
|
+
* scalar_mul_internal — 256×256 → 512-bit product, then scalar_reduce.
|
|
235
|
+
*/
|
|
236
|
+
void scalar_mul_internal(uint256_t *r, const uint256_t *a, const uint256_t *b)
|
|
237
|
+
{
|
|
238
|
+
uint64_t product[8];
|
|
239
|
+
uint128_t acc;
|
|
240
|
+
uint64_t carry;
|
|
241
|
+
int i, j;
|
|
242
|
+
|
|
243
|
+
for (i = 0; i < 8; i++) product[i] = 0;
|
|
244
|
+
|
|
245
|
+
for (i = 0; i < 4; i++) {
|
|
246
|
+
carry = 0;
|
|
247
|
+
for (j = 0; j < 4; j++) {
|
|
248
|
+
acc = (uint128_t)a->d[i] * b->d[j] + product[i + j] + carry;
|
|
249
|
+
product[i + j] = (uint64_t)acc;
|
|
250
|
+
carry = (uint64_t)(acc >> 64);
|
|
251
|
+
}
|
|
252
|
+
acc = (uint128_t)product[i + 4] + carry;
|
|
253
|
+
product[i + 4] = (uint64_t)acc;
|
|
254
|
+
if (i < 3) product[i + 5] += (uint64_t)(acc >> 64);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
scalar_reduce_limbs(r, product);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/*
|
|
261
|
+
* scalar_sqr_internal — squaring; delegates to scalar_mul_internal.
|
|
262
|
+
*/
|
|
263
|
+
static void scalar_sqr_internal(uint256_t *r, const uint256_t *a)
|
|
264
|
+
{
|
|
265
|
+
scalar_mul_internal(r, a, a);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/*
|
|
269
|
+
* scalar_add_internal — modular addition mod N.
|
|
270
|
+
*
|
|
271
|
+
* Computes a + b, then branchlessly subtracts N if the result >= N.
|
|
272
|
+
*/
|
|
273
|
+
void scalar_add_internal(uint256_t *r, const uint256_t *a, const uint256_t *b)
|
|
274
|
+
{
|
|
275
|
+
uint256_t sum;
|
|
276
|
+
uint64_t overflow = uint256_add(&sum, a, b);
|
|
277
|
+
|
|
278
|
+
uint256_t reduced;
|
|
279
|
+
uint64_t borrow = uint256_sub(&reduced, &sum, &CURVE_N);
|
|
280
|
+
|
|
281
|
+
/* Keep reduced unless (no overflow AND borrow).
|
|
282
|
+
* If overflow == 1: sum > 2^256 > N, so we want reduced.
|
|
283
|
+
* If overflow == 0 and borrow == 0: sum >= N, want reduced.
|
|
284
|
+
* If overflow == 0 and borrow == 1: sum < N, want sum. */
|
|
285
|
+
uint64_t keep_original = (~overflow) & borrow;
|
|
286
|
+
uint64_t mask = -(uint64_t)(keep_original != 0);
|
|
287
|
+
int i;
|
|
288
|
+
for (i = 0; i < 4; i++) {
|
|
289
|
+
r->d[i] = (sum.d[i] & mask) | (reduced.d[i] & ~mask);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/*
|
|
294
|
+
* scalar_inv_internal — modular inverse via Fermat's little theorem.
|
|
295
|
+
*
|
|
296
|
+
* Computes a^(N-2) mod N using square-and-multiply over the 256 bits of N-2.
|
|
297
|
+
* The exponent N-2 is a public constant so branching on its bits is safe.
|
|
298
|
+
*/
|
|
299
|
+
void scalar_inv_internal(uint256_t *r, const uint256_t *a)
|
|
300
|
+
{
|
|
301
|
+
uint256_t result;
|
|
302
|
+
uint256_t base;
|
|
303
|
+
uint256_copy(&result, &SCALAR_ONE);
|
|
304
|
+
uint256_copy(&base, a);
|
|
305
|
+
|
|
306
|
+
/* Process bits from MSB (255) to LSB (0). */
|
|
307
|
+
int i;
|
|
308
|
+
for (i = 255; i >= 0; i--) {
|
|
309
|
+
scalar_sqr_internal(&result, &result);
|
|
310
|
+
if (uint256_bit(&N_MINUS_2, i)) {
|
|
311
|
+
scalar_mul_internal(&result, &result, &base);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
uint256_copy(r, &result);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/* -----------------------------------------------------------------------
|
|
318
|
+
* Ruby-facing wrapper functions
|
|
319
|
+
* ----------------------------------------------------------------------- */
|
|
320
|
+
|
|
321
|
+
/*
|
|
322
|
+
* call-seq:
|
|
323
|
+
* Secp256k1Native.scalar_mod(a) -> Integer
|
|
324
|
+
*
|
|
325
|
+
* Reduce +a+ modulo the curve order N. Handles negative Ruby Integers:
|
|
326
|
+
* if +a+ is negative, the result is +a mod N+ in the range [0, N).
|
|
327
|
+
*/
|
|
328
|
+
static VALUE rb_scalar_mod(VALUE self, VALUE a)
|
|
329
|
+
{
|
|
330
|
+
(void)self;
|
|
331
|
+
|
|
332
|
+
/* Handle negative values by delegating to Ruby's own % operator which
|
|
333
|
+
* always returns a non-negative result for a positive modulus. */
|
|
334
|
+
VALUE n_rb = uint256_to_rb(&CURVE_N);
|
|
335
|
+
int negative = RTEST(rb_funcall(a, rb_intern("<"), 1, INT2FIX(0)));
|
|
336
|
+
|
|
337
|
+
VALUE a_norm;
|
|
338
|
+
if (negative) {
|
|
339
|
+
/* Ruby % is always non-negative when the modulus is positive */
|
|
340
|
+
a_norm = rb_funcall(a, rb_intern("%"), 1, n_rb);
|
|
341
|
+
} else {
|
|
342
|
+
a_norm = a;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
uint256_t ua = rb_to_uint256(a_norm);
|
|
346
|
+
uint256_t zero_limbs = {{ 0ULL, 0ULL, 0ULL, 0ULL }};
|
|
347
|
+
uint256_t r;
|
|
348
|
+
scalar_reduce(&r, &zero_limbs, &ua);
|
|
349
|
+
return uint256_to_rb(&r);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/*
|
|
353
|
+
* call-seq:
|
|
354
|
+
* Secp256k1Native.scalar_mul(a, b) -> Integer
|
|
355
|
+
*
|
|
356
|
+
* Scalar multiplication: returns +(a * b) mod N+.
|
|
357
|
+
*/
|
|
358
|
+
static VALUE rb_scalar_mul(VALUE self, VALUE a, VALUE b)
|
|
359
|
+
{
|
|
360
|
+
(void)self;
|
|
361
|
+
uint256_t ua = rb_to_uint256(a);
|
|
362
|
+
uint256_t ub = rb_to_uint256(b);
|
|
363
|
+
uint256_t r;
|
|
364
|
+
scalar_mul_internal(&r, &ua, &ub);
|
|
365
|
+
return uint256_to_rb(&r);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/*
|
|
369
|
+
* call-seq:
|
|
370
|
+
* Secp256k1Native.scalar_inv(a) -> Integer
|
|
371
|
+
*
|
|
372
|
+
* Modular inverse: returns +a^(N-2) mod N+.
|
|
373
|
+
*
|
|
374
|
+
* @raise [ArgumentError] if a is zero mod N.
|
|
375
|
+
*/
|
|
376
|
+
static VALUE rb_scalar_inv(VALUE self, VALUE a)
|
|
377
|
+
{
|
|
378
|
+
(void)self;
|
|
379
|
+
uint256_t ua = rb_to_uint256(a);
|
|
380
|
+
|
|
381
|
+
/* Reduce a mod N before zero-checking */
|
|
382
|
+
uint256_t zero_limbs = {{ 0ULL, 0ULL, 0ULL, 0ULL }};
|
|
383
|
+
uint256_t a_reduced;
|
|
384
|
+
scalar_reduce(&a_reduced, &zero_limbs, &ua);
|
|
385
|
+
|
|
386
|
+
if (uint256_is_zero(&a_reduced)) {
|
|
387
|
+
rb_raise(rb_eArgError, "scalar inverse is undefined for zero");
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
uint256_t r;
|
|
391
|
+
scalar_inv_internal(&r, &a_reduced);
|
|
392
|
+
return uint256_to_rb(&r);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/*
|
|
396
|
+
* call-seq:
|
|
397
|
+
* Secp256k1Native.scalar_add(a, b) -> Integer
|
|
398
|
+
*
|
|
399
|
+
* Scalar addition: returns +(a + b) mod N+.
|
|
400
|
+
*/
|
|
401
|
+
static VALUE rb_scalar_add(VALUE self, VALUE a, VALUE b)
|
|
402
|
+
{
|
|
403
|
+
(void)self;
|
|
404
|
+
uint256_t ua = rb_to_uint256(a);
|
|
405
|
+
uint256_t ub = rb_to_uint256(b);
|
|
406
|
+
uint256_t r;
|
|
407
|
+
scalar_add_internal(&r, &ua, &ub);
|
|
408
|
+
return uint256_to_rb(&r);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/* -----------------------------------------------------------------------
|
|
412
|
+
* Registration — called from Init_secp256k1_native in secp256k1_native.c
|
|
413
|
+
* ----------------------------------------------------------------------- */
|
|
414
|
+
|
|
415
|
+
void register_scalar_methods(VALUE mod)
|
|
416
|
+
{
|
|
417
|
+
rb_define_module_function(mod, "scalar_mod", rb_scalar_mod, 1);
|
|
418
|
+
rb_define_module_function(mod, "scalar_mul", rb_scalar_mul, 2);
|
|
419
|
+
rb_define_module_function(mod, "scalar_inv", rb_scalar_inv, 1);
|
|
420
|
+
rb_define_module_function(mod, "scalar_add", rb_scalar_add, 2);
|
|
421
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#include "secp256k1_native.h"
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* Secp256k1Native
|
|
5
|
+
*
|
|
6
|
+
* Native C extension providing accelerated secp256k1 field, scalar, and point
|
|
7
|
+
* arithmetic. Methods are registered here as tasks are implemented; the module
|
|
8
|
+
* is intentionally empty at scaffold stage.
|
|
9
|
+
*
|
|
10
|
+
* The extension is designed to be used standalone or as a dependency of the
|
|
11
|
+
* bsv-sdk gem, which delegates hot-path operations to this module when available.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/* Module handle — set during Init, used by sub-files when they register methods. */
|
|
15
|
+
VALUE rb_mSecp256k1Native;
|
|
16
|
+
|
|
17
|
+
/*
|
|
18
|
+
* Entry point called by Ruby when the extension is required.
|
|
19
|
+
*
|
|
20
|
+
* Defines Secp256k1Native as a top-level module. Field, scalar, and point
|
|
21
|
+
* methods are added by the registration helpers below.
|
|
22
|
+
*/
|
|
23
|
+
void Init_secp256k1_native(void) {
|
|
24
|
+
rb_mSecp256k1Native = rb_define_module("Secp256k1Native");
|
|
25
|
+
|
|
26
|
+
register_field_methods(rb_mSecp256k1Native);
|
|
27
|
+
register_scalar_methods(rb_mSecp256k1Native);
|
|
28
|
+
register_jacobian_methods(rb_mSecp256k1Native);
|
|
29
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
#ifndef SECP256K1_NATIVE_H
|
|
2
|
+
#define SECP256K1_NATIVE_H
|
|
3
|
+
|
|
4
|
+
#include "ruby.h"
|
|
5
|
+
#include <stdint.h>
|
|
6
|
+
#include <string.h>
|
|
7
|
+
|
|
8
|
+
/* 128-bit unsigned integer — available on GCC/Clang with -std=c99 on
|
|
9
|
+
* all platforms supported by this extension (Linux x86_64, macOS arm64/x86_64).
|
|
10
|
+
* extconf.rb guards entry to this compilation unit on __uint128_t availability.
|
|
11
|
+
* Ruby's own config.h may already define uint128_t as a macro, so guard here. */
|
|
12
|
+
#ifndef uint128_t
|
|
13
|
+
typedef unsigned __int128 uint128_t;
|
|
14
|
+
#endif
|
|
15
|
+
|
|
16
|
+
/* 256-bit unsigned integer stored as 4 × 64-bit limbs in little-endian order
|
|
17
|
+
* (d[0] is the least-significant 64-bit word). */
|
|
18
|
+
typedef struct {
|
|
19
|
+
uint64_t d[4];
|
|
20
|
+
} uint256_t;
|
|
21
|
+
|
|
22
|
+
/* -----------------------------------------------------------------------
|
|
23
|
+
* secp256k1 field prime: P = 2^256 - 2^32 - 977
|
|
24
|
+
* Stored little-endian: d[0] = least significant word.
|
|
25
|
+
* ----------------------------------------------------------------------- */
|
|
26
|
+
static const uint256_t FIELD_P = {{
|
|
27
|
+
0xFFFFFFFEFFFFFC2FULL, /* bits 0-63 */
|
|
28
|
+
0xFFFFFFFFFFFFFFFFULL, /* bits 64-127 */
|
|
29
|
+
0xFFFFFFFFFFFFFFFFULL, /* bits 128-191 */
|
|
30
|
+
0xFFFFFFFFFFFFFFFFULL /* bits 192-255 */
|
|
31
|
+
}};
|
|
32
|
+
|
|
33
|
+
/* -----------------------------------------------------------------------
|
|
34
|
+
* secp256k1 curve order: N = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE
|
|
35
|
+
* BAAEDCE6AF48A03BBFD25E8CD0364141
|
|
36
|
+
* Stored little-endian.
|
|
37
|
+
* ----------------------------------------------------------------------- */
|
|
38
|
+
static const uint256_t CURVE_N = {{
|
|
39
|
+
0xBFD25E8CD0364141ULL, /* bits 0-63 */
|
|
40
|
+
0xBAAEDCE6AF48A03BULL, /* bits 64-127 */
|
|
41
|
+
0xFFFFFFFFFFFFFFFEULL, /* bits 128-191 */
|
|
42
|
+
0xFFFFFFFFFFFFFFFFULL /* bits 192-255 */
|
|
43
|
+
}};
|
|
44
|
+
|
|
45
|
+
/* -----------------------------------------------------------------------
|
|
46
|
+
* Low-level 256-bit helpers — defined in field.c, declared here so that
|
|
47
|
+
* scalar.c and jacobian.c can call them without crossing the Ruby↔C boundary.
|
|
48
|
+
* ----------------------------------------------------------------------- */
|
|
49
|
+
|
|
50
|
+
uint64_t uint256_add(uint256_t *r, const uint256_t *a, const uint256_t *b);
|
|
51
|
+
uint64_t uint256_sub(uint256_t *r, const uint256_t *a, const uint256_t *b);
|
|
52
|
+
void uint256_copy(uint256_t *dst, const uint256_t *src);
|
|
53
|
+
int uint256_bit(const uint256_t *x, int i);
|
|
54
|
+
uint64_t uint256_is_zero(const uint256_t *x);
|
|
55
|
+
|
|
56
|
+
/* -----------------------------------------------------------------------
|
|
57
|
+
* Ruby Integer <-> uint256_t marshalling helpers
|
|
58
|
+
* ----------------------------------------------------------------------- */
|
|
59
|
+
|
|
60
|
+
/* Flags for rb_integer_pack / rb_integer_unpack:
|
|
61
|
+
* - LSWORD_FIRST: first word in the array is the least-significant
|
|
62
|
+
* - NATIVE_BYTE_ORDER: use platform byte order within each word
|
|
63
|
+
* Together these match the uint256_t layout (4 × uint64_t, little-endian words). */
|
|
64
|
+
#define U256_PACK_FLAGS (INTEGER_PACK_LSWORD_FIRST | INTEGER_PACK_NATIVE_BYTE_ORDER)
|
|
65
|
+
|
|
66
|
+
/* Convert a Ruby Integer to uint256_t.
|
|
67
|
+
*
|
|
68
|
+
* Raises ArgumentError if the value is negative or too large for 256 bits.
|
|
69
|
+
* Declared here; defined in field.c so only one copy exists in the binary. */
|
|
70
|
+
uint256_t rb_to_uint256(VALUE rb_int);
|
|
71
|
+
|
|
72
|
+
/* Convert a uint256_t to a Ruby Integer.
|
|
73
|
+
* Declared here; defined in field.c so only one copy exists in the binary. */
|
|
74
|
+
VALUE uint256_to_rb(const uint256_t *n);
|
|
75
|
+
|
|
76
|
+
/* -----------------------------------------------------------------------
|
|
77
|
+
* Field arithmetic — internal functions declared here so that jacobian.c
|
|
78
|
+
* can call them directly without crossing the Ruby↔C boundary.
|
|
79
|
+
* ----------------------------------------------------------------------- */
|
|
80
|
+
|
|
81
|
+
void fred_internal(uint256_t *r, const uint256_t *hi, const uint256_t *lo);
|
|
82
|
+
void fmul_internal(uint256_t *r, const uint256_t *a, const uint256_t *b);
|
|
83
|
+
void fsqr_internal(uint256_t *r, const uint256_t *a);
|
|
84
|
+
void fadd_internal(uint256_t *r, const uint256_t *a, const uint256_t *b);
|
|
85
|
+
void fsub_internal(uint256_t *r, const uint256_t *a, const uint256_t *b);
|
|
86
|
+
void fneg_internal(uint256_t *r, const uint256_t *a);
|
|
87
|
+
void finv_internal(uint256_t *r, const uint256_t *a);
|
|
88
|
+
int fsqrt_internal(uint256_t *r, const uint256_t *a);
|
|
89
|
+
|
|
90
|
+
/* Registration helper — called from Init_secp256k1_native. */
|
|
91
|
+
void register_field_methods(VALUE mod);
|
|
92
|
+
|
|
93
|
+
/* -----------------------------------------------------------------------
|
|
94
|
+
* Scalar arithmetic — internal functions declared here so that jacobian.c
|
|
95
|
+
* can call them directly without crossing the Ruby↔C boundary.
|
|
96
|
+
* ----------------------------------------------------------------------- */
|
|
97
|
+
|
|
98
|
+
void scalar_reduce(uint256_t *r, const uint256_t *hi, const uint256_t *lo);
|
|
99
|
+
void scalar_mul_internal(uint256_t *r, const uint256_t *a, const uint256_t *b);
|
|
100
|
+
void scalar_add_internal(uint256_t *r, const uint256_t *a, const uint256_t *b);
|
|
101
|
+
void scalar_inv_internal(uint256_t *r, const uint256_t *a);
|
|
102
|
+
|
|
103
|
+
/* Registration helper — called from Init_secp256k1_native. */
|
|
104
|
+
void register_scalar_methods(VALUE mod);
|
|
105
|
+
|
|
106
|
+
/* -----------------------------------------------------------------------
|
|
107
|
+
* Jacobian point operations — internal functions declared here so that
|
|
108
|
+
* future modules (e.g. a scalar multiply module) can call them directly
|
|
109
|
+
* in C without crossing the Ruby↔C boundary.
|
|
110
|
+
* ----------------------------------------------------------------------- */
|
|
111
|
+
|
|
112
|
+
void jp_double_internal(uint256_t r[3], const uint256_t p[3]);
|
|
113
|
+
void jp_add_internal(uint256_t r[3], const uint256_t p[3], const uint256_t q[3]);
|
|
114
|
+
void jp_neg_internal(uint256_t r[3], const uint256_t p[3]);
|
|
115
|
+
void scalar_multiply_ct_internal(uint256_t r[3], const uint256_t *k, const uint256_t base[3]);
|
|
116
|
+
|
|
117
|
+
/* Registration helper — called from Init_secp256k1_native. */
|
|
118
|
+
void register_jacobian_methods(VALUE mod);
|
|
119
|
+
|
|
120
|
+
/* Module handle — set during Init_secp256k1_native. */
|
|
121
|
+
extern VALUE rb_mSecp256k1Native;
|
|
122
|
+
|
|
123
|
+
#endif /* SECP256K1_NATIVE_H */
|