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,459 @@
1
+ /* frozen_string_literal: true */
2
+ #include "secp256k1_native.h"
3
+
4
+ /*
5
+ * jacobian.c — Jacobian point operations on the secp256k1 curve.
6
+ *
7
+ * Points are represented as three uint256_t values [X, Y, Z] in Jacobian
8
+ * (homogeneous projective) coordinates. The affine point (x, y) corresponds
9
+ * to the Jacobian point (X, Y, Z) via:
10
+ *
11
+ * x = X / Z² and y = Y / Z³
12
+ *
13
+ * The point at infinity is represented as [0, 1, 0] (Z = 0).
14
+ *
15
+ * All three functions (jp_double, jp_add, jp_neg) call the internal field
16
+ * operations from field.c directly — no Ruby method dispatch occurs for
17
+ * intermediate field arithmetic. A single jp_double executes ~14 field
18
+ * operations entirely in C; a single jp_add executes ~18.
19
+ *
20
+ * Formulae from hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html
21
+ * (parameter a = 0 for secp256k1).
22
+ *
23
+ * Constant-time discipline
24
+ * ------------------------
25
+ * jp_double: The Y=0 (point at infinity) check is handled branchlessly by
26
+ * computing the full result and masking to JP_INFINITY when Y is zero.
27
+ * jp_add: Branches on pz==0 / qz==0 / h==0 operate on public data in all
28
+ * call paths (the wNAF accumulator starts at infinity, which is public).
29
+ * The field arithmetic within the main computation path is branchless.
30
+ * jp_neg: Branchless — delegates the zero-checking to fneg_internal.
31
+ */
32
+
33
+ /* The point at infinity: [0, 1, 0] */
34
+ static const uint256_t JP_INF_X = {{ 0ULL, 0ULL, 0ULL, 0ULL }};
35
+ static const uint256_t JP_INF_Y = {{ 1ULL, 0ULL, 0ULL, 0ULL }};
36
+ static const uint256_t JP_INF_Z = {{ 0ULL, 0ULL, 0ULL, 0ULL }};
37
+
38
+ /* Small field element constants used in point formulae. */
39
+ static const uint256_t SMALL_2 = {{ 2ULL, 0ULL, 0ULL, 0ULL }};
40
+ static const uint256_t SMALL_3 = {{ 3ULL, 0ULL, 0ULL, 0ULL }};
41
+ static const uint256_t SMALL_4 = {{ 4ULL, 0ULL, 0ULL, 0ULL }};
42
+ static const uint256_t SMALL_8 = {{ 8ULL, 0ULL, 0ULL, 0ULL }};
43
+
44
+ /* Unpack a Ruby Array of 3 Integers into a uint256_t[3].
45
+ * Indices: 0 = X, 1 = Y, 2 = Z. */
46
+ static void unpack_point(uint256_t out[3], VALUE rb_point)
47
+ {
48
+ Check_Type(rb_point, T_ARRAY);
49
+ if (RARRAY_LEN(rb_point) != 3) {
50
+ rb_raise(rb_eArgError, "Jacobian point must be an Array of 3 integers [X, Y, Z]");
51
+ }
52
+ out[0] = rb_to_uint256(rb_ary_entry(rb_point, 0));
53
+ out[1] = rb_to_uint256(rb_ary_entry(rb_point, 1));
54
+ out[2] = rb_to_uint256(rb_ary_entry(rb_point, 2));
55
+ }
56
+
57
+ /* Pack a uint256_t[3] into a new Ruby Array of 3 Integers. */
58
+ static VALUE pack_point(const uint256_t r[3])
59
+ {
60
+ VALUE result = rb_ary_new_capa(3);
61
+ rb_ary_push(result, uint256_to_rb(&r[0]));
62
+ rb_ary_push(result, uint256_to_rb(&r[1]));
63
+ rb_ary_push(result, uint256_to_rb(&r[2]));
64
+ return result;
65
+ }
66
+
67
+ /* -----------------------------------------------------------------------
68
+ * Internal Jacobian point operations — called from Ruby-facing wrappers
69
+ * and from each other. No Ruby interaction occurs within these functions.
70
+ * ----------------------------------------------------------------------- */
71
+
72
+ /*
73
+ * jp_double_internal — double a Jacobian point.
74
+ *
75
+ * Formula (a=0 for secp256k1, from hyperelliptic.org):
76
+ *
77
+ * Y1sq = Y1²
78
+ * S = 4 · X1 · Y1sq
79
+ * M = 3 · X1²
80
+ * X3 = M² - 2·S
81
+ * Y3 = M·(S - X3) - 8·Y1sq²
82
+ * Z3 = 2·Y1·Z1
83
+ *
84
+ * Special case: if Y1 = 0 the point is at infinity. Handled branchlessly:
85
+ * the full result is computed and then replaced by JP_INFINITY when Y1 is
86
+ * zero (using a selection mask derived from uint256_is_zero).
87
+ *
88
+ * Matches the Ruby jp_double implementation exactly, including computation
89
+ * order, so that wNAF cache entries produced by C are identical to those
90
+ * produced by Ruby.
91
+ */
92
+ void jp_double_internal(uint256_t r[3], const uint256_t p[3])
93
+ {
94
+ uint256_t y1sq, s, m, x3, y3, z3;
95
+ uint256_t tmp;
96
+
97
+ /* y1sq = Y1² */
98
+ fsqr_internal(&y1sq, &p[1]);
99
+
100
+ /* s = 4 * (X1 * y1sq) */
101
+ fmul_internal(&tmp, &p[0], &y1sq);
102
+ fmul_internal(&s, &SMALL_4, &tmp);
103
+
104
+ /* m = 3 * X1² */
105
+ fsqr_internal(&tmp, &p[0]);
106
+ fmul_internal(&m, &SMALL_3, &tmp);
107
+
108
+ /* x3 = m² - 2*s */
109
+ fsqr_internal(&tmp, &m);
110
+ uint256_t two_s;
111
+ fmul_internal(&two_s, &SMALL_2, &s);
112
+ fsub_internal(&x3, &tmp, &two_s);
113
+
114
+ /* y3 = m * (s - x3) - 8 * y1sq² */
115
+ uint256_t s_minus_x3, y1sq_sq, eight_y1sq_sq;
116
+ fsub_internal(&s_minus_x3, &s, &x3);
117
+ fmul_internal(&tmp, &m, &s_minus_x3);
118
+ fsqr_internal(&y1sq_sq, &y1sq);
119
+ fmul_internal(&eight_y1sq_sq, &SMALL_8, &y1sq_sq);
120
+ fsub_internal(&y3, &tmp, &eight_y1sq_sq);
121
+
122
+ /* z3 = 2 * Y1 * Z1 */
123
+ fmul_internal(&tmp, &p[1], &p[2]);
124
+ fmul_internal(&z3, &SMALL_2, &tmp);
125
+
126
+ /* Branchless infinity check: if Y1 == 0, result is [0, 1, 0].
127
+ *
128
+ * Compute mask = all 1s if Y1 is zero, all 0s otherwise.
129
+ * Use the mask to select between [x3, y3, z3] and JP_INFINITY. */
130
+ uint64_t is_zero = uint256_is_zero(&p[1]);
131
+ uint64_t mask = -(uint64_t)(is_zero != 0); /* all 1s if Y1 == 0 */
132
+ int i;
133
+ for (i = 0; i < 4; i++) {
134
+ r[0].d[i] = (x3.d[i] & ~mask) | (JP_INF_X.d[i] & mask);
135
+ r[1].d[i] = (y3.d[i] & ~mask) | (JP_INF_Y.d[i] & mask);
136
+ r[2].d[i] = (z3.d[i] & ~mask) | (JP_INF_Z.d[i] & mask);
137
+ }
138
+ }
139
+
140
+ /*
141
+ * jp_add_internal — add two Jacobian points.
142
+ *
143
+ * Formula (from hyperelliptic.org, "add-2007-bl"):
144
+ *
145
+ * Z1Z1 = Z1², Z2Z2 = Z2²
146
+ * U1 = X1·Z2Z2, U2 = X2·Z1Z1
147
+ * S1 = Y1·Z2·Z2Z2, S2 = Y2·Z1·Z1Z1
148
+ * H = U2 - U1
149
+ * R = S2 - S1
150
+ * H2 = H², H3 = H·H2
151
+ * V = U1·H2
152
+ * X3 = R² - H3 - 2·V
153
+ * Y3 = R·(V - X3) - S1·H3
154
+ * Z3 = H·Z1·Z2
155
+ *
156
+ * Special cases (handled with branches — all operate on public data):
157
+ * - pz == 0 (p is infinity) → return q
158
+ * - qz == 0 (q is infinity) → return p
159
+ * - h == 0, r == 0 → points are equal, call jp_double_internal(p)
160
+ * - h == 0, r != 0 → points are negatives of each other → infinity
161
+ *
162
+ * Matches the Ruby jp_add implementation exactly.
163
+ */
164
+ void jp_add_internal(uint256_t r[3], const uint256_t p[3], const uint256_t q[3])
165
+ {
166
+ /* Handle point-at-infinity cases (pz == 0 or qz == 0).
167
+ * These branch on public data (Z coordinates are public in all call paths). */
168
+ if (uint256_is_zero(&p[2])) {
169
+ r[0] = q[0]; r[1] = q[1]; r[2] = q[2];
170
+ return;
171
+ }
172
+ if (uint256_is_zero(&q[2])) {
173
+ r[0] = p[0]; r[1] = p[1]; r[2] = p[2];
174
+ return;
175
+ }
176
+
177
+ uint256_t z1z1, z2z2;
178
+ uint256_t u1, u2;
179
+ uint256_t s1, s2;
180
+ uint256_t tmp;
181
+
182
+ /* z1z1 = Z1², z2z2 = Z2² */
183
+ fsqr_internal(&z1z1, &p[2]);
184
+ fsqr_internal(&z2z2, &q[2]);
185
+
186
+ /* u1 = X1 * z2z2, u2 = X2 * z1z1 */
187
+ fmul_internal(&u1, &p[0], &z2z2);
188
+ fmul_internal(&u2, &q[0], &z1z1);
189
+
190
+ /* s1 = Y1 * Z2 * z2z2, s2 = Y2 * Z1 * z1z1 */
191
+ fmul_internal(&tmp, &q[2], &z2z2);
192
+ fmul_internal(&s1, &p[1], &tmp);
193
+
194
+ fmul_internal(&tmp, &p[2], &z1z1);
195
+ fmul_internal(&s2, &q[1], &tmp);
196
+
197
+ /* h = u2 - u1, r_val = s2 - s1 */
198
+ uint256_t h, r_val;
199
+ fsub_internal(&h, &u2, &u1);
200
+ fsub_internal(&r_val, &s2, &s1);
201
+
202
+ /* Handle the h == 0 special cases.
203
+ * h == 0 means the points have the same X (in affine).
204
+ * r == 0 additionally means the same Y → equal points → double.
205
+ * r != 0 means opposite Y → additive inverse → infinity. */
206
+ if (uint256_is_zero(&h)) {
207
+ if (uint256_is_zero(&r_val)) {
208
+ jp_double_internal(r, p);
209
+ } else {
210
+ r[0] = JP_INF_X;
211
+ r[1] = JP_INF_Y;
212
+ r[2] = JP_INF_Z;
213
+ }
214
+ return;
215
+ }
216
+
217
+ uint256_t h2, h3, v, x3, y3, z3;
218
+
219
+ /* h2 = h², h3 = h * h2 */
220
+ fsqr_internal(&h2, &h);
221
+ fmul_internal(&h3, &h, &h2);
222
+
223
+ /* v = u1 * h2 */
224
+ fmul_internal(&v, &u1, &h2);
225
+
226
+ /* x3 = r² - h3 - 2*v */
227
+ uint256_t r_sq, two_v;
228
+ fsqr_internal(&r_sq, &r_val);
229
+ fmul_internal(&two_v, &SMALL_2, &v);
230
+ fsub_internal(&tmp, &r_sq, &h3);
231
+ fsub_internal(&x3, &tmp, &two_v);
232
+
233
+ /* y3 = r * (v - x3) - s1 * h3 */
234
+ uint256_t v_minus_x3, s1h3;
235
+ fsub_internal(&v_minus_x3, &v, &x3);
236
+ fmul_internal(&tmp, &r_val, &v_minus_x3);
237
+ fmul_internal(&s1h3, &s1, &h3);
238
+ fsub_internal(&y3, &tmp, &s1h3);
239
+
240
+ /* z3 = h * Z1 * Z2 */
241
+ fmul_internal(&tmp, &p[2], &q[2]);
242
+ fmul_internal(&z3, &h, &tmp);
243
+
244
+ r[0] = x3;
245
+ r[1] = y3;
246
+ r[2] = z3;
247
+ }
248
+
249
+ /*
250
+ * jp_neg_internal — negate a Jacobian point.
251
+ *
252
+ * Negation simply flips the Y coordinate: (X, Y, Z) → (X, -Y, Z).
253
+ * The point at infinity (Z = 0) is its own negation.
254
+ *
255
+ * fneg_internal handles the zero case branchlessly (fneg(0) = 0),
256
+ * so this function requires no special-casing.
257
+ *
258
+ * Matches the Ruby jp_neg implementation exactly.
259
+ */
260
+ void jp_neg_internal(uint256_t r[3], const uint256_t p[3])
261
+ {
262
+ r[0] = p[0];
263
+ fneg_internal(&r[1], &p[1]);
264
+ r[2] = p[2];
265
+ }
266
+
267
+ /* -----------------------------------------------------------------------
268
+ * Ruby-facing wrapper functions
269
+ * ----------------------------------------------------------------------- */
270
+
271
+ /*
272
+ * call-seq:
273
+ * Secp256k1Native.jp_double(point) -> Array
274
+ *
275
+ * Double a Jacobian point.
276
+ *
277
+ * @param point [Array(Integer, Integer, Integer)] Jacobian point [X, Y, Z]
278
+ * @return [Array(Integer, Integer, Integer)] doubled point
279
+ */
280
+ static VALUE rb_jp_double(VALUE self, VALUE rb_point)
281
+ {
282
+ (void)self;
283
+ uint256_t p[3], r[3];
284
+ unpack_point(p, rb_point);
285
+ jp_double_internal(r, p);
286
+ return pack_point(r);
287
+ }
288
+
289
+ /*
290
+ * call-seq:
291
+ * Secp256k1Native.jp_add(p, q) -> Array
292
+ *
293
+ * Add two Jacobian points.
294
+ *
295
+ * @param p [Array(Integer, Integer, Integer)] first Jacobian point [X, Y, Z]
296
+ * @param q [Array(Integer, Integer, Integer)] second Jacobian point [X, Y, Z]
297
+ * @return [Array(Integer, Integer, Integer)] sum
298
+ */
299
+ static VALUE rb_jp_add(VALUE self, VALUE rb_p, VALUE rb_q)
300
+ {
301
+ (void)self;
302
+ uint256_t p[3], q[3], r[3];
303
+ unpack_point(p, rb_p);
304
+ unpack_point(q, rb_q);
305
+ jp_add_internal(r, p, q);
306
+ return pack_point(r);
307
+ }
308
+
309
+ /*
310
+ * call-seq:
311
+ * Secp256k1Native.jp_neg(point) -> Array
312
+ *
313
+ * Negate a Jacobian point (flip the Y coordinate).
314
+ *
315
+ * @param point [Array(Integer, Integer, Integer)] Jacobian point [X, Y, Z]
316
+ * @return [Array(Integer, Integer, Integer)] negated point
317
+ */
318
+ static VALUE rb_jp_neg(VALUE self, VALUE rb_point)
319
+ {
320
+ (void)self;
321
+ uint256_t p[3], r[3];
322
+ unpack_point(p, rb_point);
323
+ jp_neg_internal(r, p);
324
+ return pack_point(r);
325
+ }
326
+
327
+ /* -----------------------------------------------------------------------
328
+ * Constant-time scalar multiplication — Montgomery ladder
329
+ * ----------------------------------------------------------------------- */
330
+
331
+ /*
332
+ * cswap — branchless conditional swap of two Jacobian points.
333
+ *
334
+ * Each Jacobian point is three uint256_t values (X, Y, Z = 4 limbs each).
335
+ * If bit == 1, the contents of a and b are swapped.
336
+ * If bit == 0, nothing changes.
337
+ *
338
+ * The mask is derived from bit without any branch on it, so execution time
339
+ * does not depend on the value of the scalar bit being processed.
340
+ */
341
+ static void cswap(uint64_t bit, uint256_t a[3], uint256_t b[3])
342
+ {
343
+ uint64_t mask = -(uint64_t)bit; /* all-ones if bit==1, all-zeros if bit==0 */
344
+ int j, k;
345
+ for (j = 0; j < 3; j++) {
346
+ for (k = 0; k < 4; k++) {
347
+ uint64_t tmp = mask & (a[j].d[k] ^ b[j].d[k]);
348
+ a[j].d[k] ^= tmp;
349
+ b[j].d[k] ^= tmp;
350
+ }
351
+ }
352
+ }
353
+
354
+ /*
355
+ * scalar_multiply_ct_internal — constant-time scalar multiplication via the
356
+ * Montgomery ladder.
357
+ *
358
+ * Computes r = k × base using a branchless double-and-add loop. The two
359
+ * accumulators r0 (result) and r1 (result + base) are swapped before and
360
+ * after each iteration according to the current scalar bit, ensuring that
361
+ * the sequence of point operations executed is independent of k.
362
+ *
363
+ * Invariant: r1 = r0 + base throughout the loop.
364
+ *
365
+ * In-place aliasing: jp_add_internal reads all inputs into stack locals
366
+ * (u1, u2, s1, s2, h, r_val, etc.) before writing the output, and
367
+ * jp_double_internal similarly reads into locals (y1sq, s, m, etc.)
368
+ * before writing. This makes jp_add_internal(r1, r0, r1) and
369
+ * jp_double_internal(r0, r0) safe when output overlaps an input.
370
+ *
371
+ * @param r output: k × base as a Jacobian point
372
+ * @param k secret scalar (256 bits, caller ensures 0 < k < N)
373
+ * @param base base point in Jacobian coordinates
374
+ */
375
+ void scalar_multiply_ct_internal(uint256_t r[3], const uint256_t *k, const uint256_t base[3])
376
+ {
377
+ /* r0 = infinity [0, 1, 0]; r1 = base */
378
+ uint256_t r0[3], r1[3];
379
+ memset(r0, 0, sizeof(uint256_t) * 3);
380
+ r0[1].d[0] = 1; /* Y = 1 */
381
+ memcpy(r1, base, sizeof(uint256_t) * 3);
382
+
383
+ int i;
384
+ for (i = 255; i >= 0; i--) {
385
+ uint64_t bit = (uint64_t)uint256_bit(k, i);
386
+ cswap(bit, r0, r1);
387
+ jp_add_internal(r1, r0, r1);
388
+ jp_double_internal(r0, r0);
389
+ cswap(bit, r0, r1);
390
+ }
391
+
392
+ memcpy(r, r0, sizeof(uint256_t) * 3);
393
+ }
394
+
395
+ /*
396
+ * call-seq:
397
+ * Secp256k1Native.scalar_multiply_ct(k, px, py) -> Array
398
+ *
399
+ * Constant-time scalar multiplication using the Montgomery ladder.
400
+ *
401
+ * Computes k × (px, py) entirely in C with no per-iteration Ruby dispatch.
402
+ * The ladder loop is branchless with respect to the scalar bits (via cswap).
403
+ * Note: jp_add_internal still branches on infinity/collision edge cases,
404
+ * so full constant-time depends on the point operations being hardened
405
+ * separately. The k==0 early return is on a non-secret value (k==0 is
406
+ * never a valid private key or nonce).
407
+ *
408
+ * @param k [Integer] scalar (must be in [0, N))
409
+ * @param px [Integer] affine x-coordinate of the base point
410
+ * @param py [Integer] affine y-coordinate of the base point
411
+ * @return [Array(Integer, Integer, Integer)] result as a Jacobian point
412
+ * @raise [ArgumentError] if k >= N (curve order)
413
+ */
414
+ static VALUE rb_scalar_multiply_ct(VALUE self, VALUE rb_k, VALUE rb_px, VALUE rb_py)
415
+ {
416
+ (void)self;
417
+ uint256_t k = rb_to_uint256(rb_k);
418
+
419
+ /* Validate k < N — belt-and-braces guard for direct callers. */
420
+ uint256_t tmp;
421
+ uint64_t borrow = uint256_sub(&tmp, &k, &CURVE_N);
422
+ if (!borrow) {
423
+ /* k >= N: borrow would be 1 if k < N, so borrow == 0 means k >= N */
424
+ rb_raise(rb_eArgError, "scalar k must be in [0, N) (curve order)");
425
+ }
426
+
427
+ /* k = 0: return the point at infinity [0, 1, 0]. */
428
+ if (uint256_is_zero(&k)) {
429
+ VALUE result = rb_ary_new_capa(3);
430
+ rb_ary_push(result, INT2FIX(0));
431
+ rb_ary_push(result, INT2FIX(1));
432
+ rb_ary_push(result, INT2FIX(0));
433
+ return result;
434
+ }
435
+
436
+ /* Construct Jacobian base point [px, py, 1]. */
437
+ uint256_t base[3];
438
+ base[0] = rb_to_uint256(rb_px);
439
+ base[1] = rb_to_uint256(rb_py);
440
+ memset(&base[2], 0, sizeof(uint256_t));
441
+ base[2].d[0] = 1;
442
+
443
+ uint256_t r[3];
444
+ scalar_multiply_ct_internal(r, &k, base);
445
+
446
+ return pack_point(r);
447
+ }
448
+
449
+ /* -----------------------------------------------------------------------
450
+ * Registration — called from Init_secp256k1_native in secp256k1_native.c
451
+ * ----------------------------------------------------------------------- */
452
+
453
+ void register_jacobian_methods(VALUE mod)
454
+ {
455
+ rb_define_module_function(mod, "jp_double", rb_jp_double, 1);
456
+ rb_define_module_function(mod, "jp_add", rb_jp_add, 2);
457
+ rb_define_module_function(mod, "jp_neg", rb_jp_neg, 1);
458
+ rb_define_module_function(mod, "scalar_multiply_ct", rb_scalar_multiply_ct, 3);
459
+ }