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.
@@ -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 */
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Secp256k1
4
+ VERSION = '0.15.0'
5
+ end