Reorder verify parameters to align with libsodium. Refactor point classes. Add field element functions, group element functions, and scalar functions to support radix 25 migration. Replace tweetnacl modL function with libsodium scalar functions. Clean up variable names and documentation comments.
//! SPDX-License-Identifier: ISC
import { fe } from './fe'
-import { PrecomputedPoint } from './ge'
+import { ge_precomp } from './ge'
-export const base = StaticArray.fromArray<StaticArray<PrecomputedPoint>>([
- StaticArray.fromArray<PrecomputedPoint>([ /* 0/31 */
+export const base = StaticArray.fromArray<StaticArray<ge_precomp>>([
+ StaticArray.fromArray<ge_precomp>([ /* 0/31 */
{
yplusx: fe([25967493, -14356035, 29566456, 3660896, -12694345, 4014787, 27544626, -11754271, -6079156, 2047605]),
yminusx: fe([-12545711, 934262, -2722910, 3049990, -727428, 9406986, 12720692, 5043384, 19500929, -15469378]),
xy2d: fe([27431194, 8222322, 16448760, -3907995, -18707002, 11938355, -32961401, -2970515, 29551813, 10109425])
}
]),
- StaticArray.fromArray<PrecomputedPoint>([ /* 1/31 */
+ StaticArray.fromArray<ge_precomp>([ /* 1/31 */
{
yplusx: fe([-13657040, -13155431, -31283750, 11777098, 21447386, 6519384, -2378284, -1627556, 10092783, -4764171]),
yminusx: fe([27939166, 14210322, 4677035, 16277044, -22964462, -12398139, -32508754, 12005538, -17810127, 12803510]),
xy2d: fe([1767683, 7197987, -13205226, -2022635, -13091350, 448826, 5799055, 4357868, -4774191, -16323038])
}
]),
- StaticArray.fromArray<PrecomputedPoint>([ /* 2/31 */
+ StaticArray.fromArray<ge_precomp>([ /* 2/31 */
{
yplusx: fe([6721966, 13833823, -23523388, -1551314, 26354293, -11863321, 23365147, -3949732, 7390890, 2759800]),
yminusx: fe([4409041, 2052381, 23373853, 10530217, 7676779, -12885954, 21302353, -4264057, 1244380, -12919645]),
xy2d: fe([-20290863, 8198642, -27410132, 11602123, 1290375, -2799760, 28326862, 1721092, -19558642, -3131606])
}
]),
- StaticArray.fromArray<PrecomputedPoint>([ /* 3/31 */
+ StaticArray.fromArray<ge_precomp>([ /* 3/31 */
{
yplusx: fe([7881532, 10687937, 7578723, 7738378, -18951012, -2553952, 21820786, 8076149, -27868496, 11538389]),
yminusx: fe([-19935666, 3899861, 18283497, -6801568, -15728660, -11249211, 8754525, 7446702, -5676054, 5797016]),
xy2d: fe([27461885, -2977536, 22380810, 1815854, -23033753, -3031938, 7283490, -15148073, -19526700, 7734629])
}
]),
- StaticArray.fromArray<PrecomputedPoint>([ /* 4/31 */
+ StaticArray.fromArray<ge_precomp>([ /* 4/31 */
{
yplusx: fe([-8010264, -9590817, -11120403, 6196038, 29344158, -13430885, 7585295, -3176626, 18549497, 15302069]),
yminusx: fe([-32658337, -6171222, -7672793, -11051681, 6258878, 13504381, 10458790, -6418461, -8872242, 8424746]),
xy2d: fe([-24722913, -4176517, -31150679, 5988919, -26858785, 6685065, 1661597, -12551441, 15271676, -15452665])
}
]),
- StaticArray.fromArray<PrecomputedPoint>([ /* 5/31 */
+ StaticArray.fromArray<ge_precomp>([ /* 5/31 */
{
yplusx: fe([11433042, -13228665, 8239631, -5279517, -1985436, -725718, -18698764, 2167544, -6921301, -13440182]),
yminusx: fe([-31436171, 15575146, 30436815, 12192228, -22463353, 9395379, -9917708, -8638997, 12215110, 12028277]),
xy2d: fe([29439664, 3537914, 23333589, 6997794, -17555561, -11018068, -15209202, -15051267, -9164929, 6580396])
}
]),
- StaticArray.fromArray<PrecomputedPoint>([ /* 6/31 */
+ StaticArray.fromArray<ge_precomp>([ /* 6/31 */
{
yplusx: fe([-12185861, -7679788, 16438269, 10826160, -8696817, -6235611, 17860444, -9273846, -2095802, 9304567]),
yminusx: fe([20714564, -4336911, 29088195, 7406487, 11426967, -5095705, 14792667, -14608617, 5289421, -477127]),
xy2d: fe([28584902, 7787108, -6732942, -15050729, 22846041, -7571236, -3181936, -363524, 4771362, -8419958])
}
]),
- StaticArray.fromArray<PrecomputedPoint>([ /* 7/31 */
+ StaticArray.fromArray<ge_precomp>([ /* 7/31 */
{
yplusx: fe([24949256, 6376279, -27466481, -8174608, -18646154, -9930606, 33543569, -12141695, 3569627, 11342593]),
yminusx: fe([26514989, 4740088, 27912651, 3697550, 19331575, -11472339, 6809886, 4608608, 7325975, -14801071]),
xy2d: fe([19299512, 1155910, 28703737, 14890794, 2925026, 7269399, 26121523, 15467869, -26560550, 5052483])
}
]),
- StaticArray.fromArray<PrecomputedPoint>([ /* 8/31 */
+ StaticArray.fromArray<ge_precomp>([ /* 8/31 */
{
yplusx: fe([-3017432, 10058206, 1980837, 3964243, 22160966, 12322533, -6431123, -12618185, 12228557, -7003677]),
yminusx: fe([32944382, 14922211, -22844894, 5188528, 21913450, -8719943, 4001465, 13238564, -6114803, 8653815]),
xy2d: fe([-11787308, 11500838, 13787581, -13832590, -22430679, 10140205, 1465425, 12689540, -10301319, -13872883])
}
]),
- StaticArray.fromArray<PrecomputedPoint>([ /* 9/31 */
+ StaticArray.fromArray<ge_precomp>([ /* 9/31 */
{
yplusx: fe([5414091, -15386041, -21007664, 9643570, 12834970, 1186149, -2622916, -1342231, 26128231, 6032912]),
yminusx: fe([-26337395, -13766162, 32496025, -13653919, 17847801, -12669156, 3604025, 8316894, -25875034, -10437358]),
xy2d: fe([19492550, -12104365, -29681976, -852630, -3208171, 12403437, 30066266, 8367329, 13243957, 8709688])
}
]),
- StaticArray.fromArray<PrecomputedPoint>([ /* 10/31 */
+ StaticArray.fromArray<ge_precomp>([ /* 10/31 */
{
yplusx: fe([12015105, 2801261, 28198131, 10151021, 24818120, -4743133, -11194191, -5645734, 5150968, 7274186]),
yminusx: fe([2831366, -12492146, 1478975, 6122054, 23825128, -12733586, 31097299, 6083058, 31021603, -9793610]),
xy2d: fe([32529814, -11074689, 30361439, -16689753, -9135940, 1513226, 22922121, 6382134, -5766928, 8371348])
}
]),
- StaticArray.fromArray<PrecomputedPoint>([ /* 11/31 */
+ StaticArray.fromArray<ge_precomp>([ /* 11/31 */
{
yplusx: fe([9923462, 11271500, 12616794, 3544722, -29998368, -1721626, 12891687, -8193132, -26442943, 10486144]),
yminusx: fe([-22597207, -7012665, 8587003, -8257861, 4084309, -12970062, 361726, 2610596, -23921530, -11455195]),
xy2d: fe([-2114751, -14308128, 23019042, 15765735, -25269683, 6002752, 10183197, -13239326, -16395286, -2176112])
}
]),
- StaticArray.fromArray<PrecomputedPoint>([ /* 12/31 */
+ StaticArray.fromArray<ge_precomp>([ /* 12/31 */
{
yplusx: fe([-19025756, 1632005, 13466291, -7995100, -23640451, 16573537, -32013908, -3057104, 22208662, 2000468]),
yminusx: fe([3065073, -1412761, -25598674, -361432, -17683065, -5703415, -8164212, 11248527, -3691214, -7414184]),
xy2d: fe([-14676630, -15644296, 15287174, 11927123, 24177847, -8175568, -796431, 14860609, -26938930, -5863836])
}
]),
- StaticArray.fromArray<PrecomputedPoint>([ /* 13/31 */
+ StaticArray.fromArray<ge_precomp>([ /* 13/31 */
{
yplusx: fe([12962541, 5311799, -10060768, 11658280, 18855286, -7954201, 13286263, -12808704, -4381056, 9882022]),
yminusx: fe([18512079, 11319350, -20123124, 15090309, 18818594, 5271736, -22727904, 3666879, -23967430, -3299429]),
xy2d: fe([-13413405, -12407859, 20757302, -13801832, 14785143, 8976368, -5061276, -2144373, 17846988, -13971927])
}
]),
- StaticArray.fromArray<PrecomputedPoint>([ /* 14/31 */
+ StaticArray.fromArray<ge_precomp>([ /* 14/31 */
{
yplusx: fe([-2244452, -754728, -4597030, -1066309, -6247172, 1455299, -21647728, -9214789, -5222701, 12650267]),
yminusx: fe([-9906797, -16070310, 21134160, 12198166, -27064575, 708126, 387813, 13770293, -19134326, 10958663]),
xy2d: fe([-16916263, -4952973, -30393711, -15158821, 20774812, 15897498, 5736189, 15026997, -2178256, -13455585])
}
]),
- StaticArray.fromArray<PrecomputedPoint>([ /* 15/31 */
+ StaticArray.fromArray<ge_precomp>([ /* 15/31 */
{
yplusx: fe([-8858980, -2219056, 28571666, -10155518, -474467, -10105698, -3801496, 278095, 23440562, -290208]),
yminusx: fe([10226241, -5928702, 15139956, 120818, -14867693, 5218603, 32937275, 11551483, -16571960, -7442864]),
xy2d: fe([27406042, -6041657, 27423596, -4497394, 4996214, 10002360, -28842031, -4545494, -30172742, -4805667])
}
]),
- StaticArray.fromArray<PrecomputedPoint>([ /* 16/31 */
+ StaticArray.fromArray<ge_precomp>([ /* 16/31 */
{
yplusx: fe([11374242, 12660715, 17861383, -12540833, 10935568, 1099227, -13886076, -9091740, -27727044, 11358504]),
yminusx: fe([-12730809, 10311867, 1510375, 10778093, -2119455, -9145702, 32676003, 11149336, -26123651, 4985768]),
xy2d: fe([-1409668, 12530728, -6368726, 10847387, 19531186, -14132160, -11709148, 7791794, -27245943, 4383347])
}
]),
- StaticArray.fromArray<PrecomputedPoint>([ /* 17/31 */
+ StaticArray.fromArray<ge_precomp>([ /* 17/31 */
{
yplusx: fe([-28970898, 5271447, -1266009, -9736989, -12455236, 16732599, -4862407, -4906449, 27193557, 6245191]),
yminusx: fe([-15193956, 5362278, -1783893, 2695834, 4960227, 12840725, 23061898, 3260492, 22510453, 8577507]),
xy2d: fe([-20679756, 7004547, 8824831, -9434977, -4045704, -3750736, -5754762, 108893, 23513200, 16652362])
}
]),
- StaticArray.fromArray<PrecomputedPoint>([ /* 18/31 */
+ StaticArray.fromArray<ge_precomp>([ /* 18/31 */
{
yplusx: fe([-33256173, 4144782, -4476029, -6579123, 10770039, -7155542, -6650416, -12936300, -18319198, 10212860]),
yminusx: fe([2756081, 8598110, 7383731, -6859892, 22312759, -1105012, 21179801, 2600940, -9988298, -12506466]),
xy2d: fe([-2504044, -436966, 25621774, -5678772, 15085042, -5479877, -24884878, -13526194, 5537438, -13914319])
}
]),
- StaticArray.fromArray<PrecomputedPoint>([ /* 19/31 */
+ StaticArray.fromArray<ge_precomp>([ /* 19/31 */
{
yplusx: fe([-11225584, 2320285, -9584280, 10149187, -33444663, 5808648, -14876251, -1729667, 31234590, 6090599]),
yminusx: fe([-9633316, 116426, 26083934, 2897444, -6364437, -2688086, 609721, 15878753, -6970405, -9034768]),
xy2d: fe([18171223, -11934626, -12500402, 15197122, -11038147, -15230035, -19172240, -16046376, 8764035, 12309598])
}
]),
- StaticArray.fromArray<PrecomputedPoint>([ /* 20/31 */
+ StaticArray.fromArray<ge_precomp>([ /* 20/31 */
{
yplusx: fe([5975908, -5243188, -19459362, -9681747, -11541277, 14015782, -23665757, 1228319, 17544096, -10593782]),
yminusx: fe([5811932, -1715293, 3442887, -2269310, -18367348, -8359541, -18044043, -15410127, -5565381, 12348900]),
xy2d: fe([12966261, 15550616, -32038948, -1615346, 21025980, -629444, 5642325, 7188737, 18895762, 12629579])
}
]),
- StaticArray.fromArray<PrecomputedPoint>([ /* 21/31 */
+ StaticArray.fromArray<ge_precomp>([ /* 21/31 */
{
yplusx: fe([14741879, -14946887, 22177208, -11721237, 1279741, 8058600, 11758140, 789443, 32195181, 3895677]),
yminusx: fe([10758205, 15755439, -4509950, 9243698, -4879422, 6879879, -2204575, -3566119, -8982069, 4429647]),
xy2d: fe([-10969595, -6403711, 9591134, 9582310, 11349256, 108879, 16235123, 8601684, -139197, 4242895])
}
]),
- StaticArray.fromArray<PrecomputedPoint>([ /* 22/31 */
+ StaticArray.fromArray<ge_precomp>([ /* 22/31 */
{
yplusx: fe([22092954, -13191123, -2042793, -11968512, 32186753, -11517388, -6574341, 2470660, -27417366, 16625501]),
yminusx: fe([-11057722, 3042016, 13770083, -9257922, 584236, -544855, -7770857, 2602725, -27351616, 14247413]),
xy2d: fe([28042865, -3557089, -12126526, 12259706, -3717498, -6945899, 6766453, -8689599, 18036436, 5803270])
}
]),
- StaticArray.fromArray<PrecomputedPoint>([ /* 23/31 */
+ StaticArray.fromArray<ge_precomp>([ /* 23/31 */
{
yplusx: fe([-817581, 6763912, 11803561, 1585585, 10958447, -2671165, 23855391, 4598332, -6159431, -14117438]),
yminusx: fe([-31031306, -14256194, 17332029, -2383520, 31312682, -5967183, 696309, 50292, -20095739, 11763584]),
xy2d: fe([-28888365, 3510803, -28103278, -1158478, -11238128, -10631454, -15441463, -14453128, -1625486, -6494814])
}
]),
- StaticArray.fromArray<PrecomputedPoint>([ /* 24/31 */
+ StaticArray.fromArray<ge_precomp>([ /* 24/31 */
{
yplusx: fe([793299, -9230478, 8836302, -6235707, -27360908, -2369593, 33152843, -4885251, -9906200, -621852]),
yminusx: fe([5666233, 525582, 20782575, -8038419, -24538499, 14657740, 16099374, 1468826, -6171428, -15186581]),
xy2d: fe([-11430470, 15697596, -21121557, -4420647, 5386314, 15063598, 16514493, -15932110, 29330899, -15076224])
}
]),
- StaticArray.fromArray<PrecomputedPoint>([ /* 25/31 */
+ StaticArray.fromArray<ge_precomp>([ /* 25/31 */
{
yplusx: fe([-25499735, -4378794, -15222908, -6901211, 16615731, 2051784, 3303702, 15490, -27548796, 12314391]),
yminusx: fe([15683520, -6003043, 18109120, -9980648, 15337968, -5997823, -16717435, 15921866, 16103996, -3731215]),
xy2d: fe([-30264870, -7647865, 5112249, -7036672, -1499807, -6974257, 43168, -5537701, -32302074, 16215819])
}
]),
- StaticArray.fromArray<PrecomputedPoint>([ /* 26/31 */
+ StaticArray.fromArray<ge_precomp>([ /* 26/31 */
{
yplusx: fe([-6898905, 9824394, -12304779, -4401089, -31397141, -6276835, 32574489, 12532905, -7503072, -8675347]),
yminusx: fe([-27343522, -16515468, -27151524, -10722951, 946346, 16291093, 254968, 7168080, 21676107, -1943028]),
xy2d: fe([24536016, -16515207, 12715592, -3862155, 1511293, 10047386, -3842346, -7129159, -28377538, 10048127])
}
]),
- StaticArray.fromArray<PrecomputedPoint>([ /* 27/31 */
+ StaticArray.fromArray<ge_precomp>([ /* 27/31 */
{
yplusx: fe([-12622226, -6204820, 30718825, 2591312, -10617028, 12192840, 18873298, -7297090, -32297756, 15221632]),
yminusx: fe([-26478122, -11103864, 11546244, -1852483, 9180880, 7656409, -21343950, 2095755, 29769758, 6593415]),
xy2d: fe([476239, 6601091, -6152790, -9723375, 17503545, -4863900, 27672959, 13403813, 11052904, 5219329])
}
]),
- StaticArray.fromArray<PrecomputedPoint>([ /* 28/31 */
+ StaticArray.fromArray<ge_precomp>([ /* 28/31 */
{
yplusx: fe([20678546, -8375738, -32671898, 8849123, -5009758, 14574752, 31186971, -3973730, 9014762, -8579056]),
yminusx: fe([-13644050, -10350239, -15962508, 5075808, -1514661, -11534600, -33102500, 9160280, 8473550, -3256838]),
xy2d: fe([-19240248, -11254599, -29509029, -7499965, -5835763, 13005411, -6066489, 12194497, 32960380, 1459310])
}
]),
- StaticArray.fromArray<PrecomputedPoint>([ /* 29/31 */
+ StaticArray.fromArray<ge_precomp>([ /* 29/31 */
{
yplusx: fe([19852034, 7027924, 23669353, 10020366, 8586503, -6657907, 394197, -6101885, 18638003, -11174937]),
yminusx: fe([31395534, 15098109, 26581030, 8030562, -16527914, -5007134, 9012486, -7584354, -6643087, -5442636]),
xy2d: fe([-26378102, -7965207, -22167821, 15789297, -18055342, -6168792, -1984914, 15707771, 26342023, 10146099])
}
]),
- StaticArray.fromArray<PrecomputedPoint>([ /* 30/31 */
+ StaticArray.fromArray<ge_precomp>([ /* 30/31 */
{
yplusx: fe([-26016874, -219943, 21339191, -41388, 19745256, -2878700, -29637280, 2227040, 21612326, -545728]),
yminusx: fe([-13077387, 1184228, 23562814, -5970442, -20351244, -6348714, 25764461, 12243797, -20856566, 11649658]),
xy2d: fe([-29663431, -15113610, 32259991, -344482, 24295849, -12912123, 23161163, 8839127, 27485041, 7356032])
}
]),
- StaticArray.fromArray<PrecomputedPoint>([ /* 31/31 */
+ StaticArray.fromArray<ge_precomp>([ /* 31/31 */
{
yplusx: fe([9661027, 705443, 11980065, -5370154, -1628543, 14661173, -6346142, 2625015, 28431036, -16771834]),
yminusx: fe([-23839233, -8311415, -25945511, 7480958, -17681669, -8354183, -22545972, 14150565, 15970762, 4099461]),
//! SPDX-License-Identifier: GPL-3.0-or-later
/**
- * Arithmetic on field elements. These are integers in the range 0 <= n < p
- * where p = 2²⁵⁵-19. They are represented as 16 limbs of 16-bit values so that
- * n = 256⁰*a[0] + 256¹*a[1] + ... + 256³¹*a[31]
+ * Arithmetic on field elements. These are integers in the range
+ *
+ * `0 <= n < p`
+ *
+ * where
+ *
+ * `p = 2²⁵⁵-19`
+ *
+ * They are represented as 16 limbs of 16-bit values so that
+ *
+ * `n = 256⁰a[0] + 256¹a[1] + ... + 256³¹a[31]`
*/
+import { load_3, load_4 } from "./utils"
+
/**
* 16-wide x 16-bit array of values representing some large integer `n mod p`.
*/
v128.store(q, v128.bitselect(pi, qi, c), 48)
}
+/**
+ * Ignores top bit of s.
+ */
+export function fe_frombytes (h: FieldElement, s: StaticArray<u8>): void {
+ let h0: i64 = load_4(s, 0)
+ let h1: i64 = load_3(s, 4) << 6
+ let h2: i64 = load_3(s, 7) << 5
+ let h3: i64 = load_3(s, 10) << 3
+ let h4: i64 = load_3(s, 13) << 2
+ let h5: i64 = load_4(s, 16)
+ let h6: i64 = load_3(s, 20) << 7
+ let h7: i64 = load_3(s, 23) << 5
+ let h8: i64 = load_3(s, 26) << 4
+ let h9: i64 = (load_3(s, 29) & 8388607) << 2
+
+ let carry0: i64
+ let carry1: i64
+ let carry2: i64
+ let carry3: i64
+ let carry4: i64
+ let carry5: i64
+ let carry6: i64
+ let carry7: i64
+ let carry8: i64
+ let carry9: i64
+
+ carry9 = (h9 + i64(1 << 24)) >> 25
+ h0 += carry9 * 19
+ h9 -= carry9 << 25
+ carry1 = (h1 + i64(1 << 24)) >> 25
+ h2 += carry1
+ h1 -= carry1 << 25
+ carry3 = (h3 + i64(1 << 24)) >> 25
+ h4 += carry3
+ h3 -= carry3 << 25
+ carry5 = (h5 + i64(1 << 24)) >> 25
+ h6 += carry5
+ h5 -= carry5 << 25
+ carry7 = (h7 + i64(1 << 24)) >> 25
+ h8 += carry7
+ h7 -= carry7 << 25
+
+ carry0 = (h0 + i64(1 << 25)) >> 26
+ h1 += carry0
+ h0 -= carry0 << 26
+ carry2 = (h2 + i64(1 << 25)) >> 26
+ h3 += carry2
+ h2 -= carry2 << 26
+ carry4 = (h4 + i64(1 << 25)) >> 26
+ h5 += carry4
+ h4 -= carry4 << 26
+ carry6 = (h6 + i64(1 << 25)) >> 26
+ h7 += carry6
+ h6 -= carry6 << 26
+ carry8 = (h8 + i64(1 << 25)) >> 26
+ h9 += carry8
+ h8 -= carry8 << 26
+
+ h[0] = i32(h0)
+ h[1] = i32(h1)
+ h[2] = i32(h2)
+ h[3] = i32(h3)
+ h[4] = i32(h4)
+ h[5] = i32(h5)
+ h[6] = i32(h6)
+ h[7] = i32(h7)
+ h[8] = i32(h8)
+ h[9] = i32(h9)
+}
+
+/*
+ * Inversion - sets out to 0 if z=0
+ */
+const t0: FieldElement = fe()
+const t1: FieldElement = fe()
+const t2: FieldElement = fe()
+const t3: FieldElement = fe()
+export function fe_invert (out: FieldElement, z: FieldElement): void {
+ fe_sq(t0, z)
+ fe_sq(t1, t0)
+ fe_sq(t1, t1)
+ fe_mul(t1, z, t1)
+ fe_mul(t0, t0, t1)
+ fe_sq(t2, t0)
+ fe_mul(t1, t1, t2)
+ fe_sq(t2, t1)
+ for (let i = 1; i < 5; ++i) {
+ fe_sq(t2, t2)
+ }
+ fe_mul(t1, t2, t1)
+ fe_sq(t2, t1)
+ for (let i = 1; i < 10; ++i) {
+ fe_sq(t2, t2)
+ }
+ fe_mul(t2, t2, t1)
+ fe_sq(t3, t2)
+ for (let i = 1; i < 20; ++i) {
+ fe_sq(t3, t3)
+ }
+ fe_mul(t2, t3, t2)
+ for (let i = 1; i < 11; ++i) {
+ fe_sq(t2, t2)
+ }
+ fe_mul(t1, t2, t1)
+ fe_sq(t2, t1)
+ for (let i = 1; i < 50; ++i) {
+ fe_sq(t2, t2)
+ }
+ fe_mul(t2, t2, t1)
+ fe_sq(t3, t2)
+ for (let i = 1; i < 100; ++i) {
+ fe_sq(t3, t3)
+ }
+ fe_mul(t2, t3, t2)
+ for (let i = 1; i < 51; ++i) {
+ fe_sq(t2, t2)
+ }
+ fe_mul(t1, t2, t1)
+ for (let i = 1; i < 6; ++i) {
+ fe_sq(t1, t1)
+ }
+ fe_mul(out, t1, t0)
+}
+
+/*
+ return 1 if f is in {1,3,5,...,q-2}
+ return 0 if f is in {0,2,4,...,q-1}
+
+ Preconditions:
+ |f| bounded by 1.1*2²⁶,1.1*2²⁵,1.1*2²⁶,1.1*2²⁵,etc.
+ */
+const fe_isnegative_s = new StaticArray<u8>(32)
+//@ts-expect-error
+@inline
+export function fe_isnegative (f: FieldElement): u8 {
+ const s = fe_isnegative_s
+ fe_tobytes(s, f)
+ return s[0] & 1
+}
+
const multiply_t = new StaticArray<i64>(32)
/**
* Multiply two FieldElements and store the product.
v128.store(o, v128.neg<i32>(ai), 48)
}
+/*
+ Preconditions:
+ |h| bounded by 1.1*2²⁶,1.1*2²⁵,1.1*2²⁶,1.1*2²⁵,etc.
+
+ Write p=2²⁵⁵-19; q=floor(h/p).
+ Basic claim: q = floor(2⁻²⁵⁵(h + 19 2^(-25)h9 + 2^(-1))).
+
+ Proof:
+ Have |h|<=p so |q|<=1 so |19² 2⁻²⁵⁵ q|<1/4.
+ Also have |h-2²³⁰ h9|<2²³¹ so |19 2⁻²⁵⁵(h-2²³⁰ h9)|<1/4.
+
+ Write y=2^(-1)-19² 2⁻²⁵⁵q-19 2⁻²⁵⁵(h-2²³⁰ h9).
+ Then 0<y<1.
+
+ Write r=h-pq.
+ Have 0 <= r <= p-1 = 2²⁵⁵-20.
+ Thus 0 <= r+19(2⁻²⁵⁵)r < r+19(2⁻²⁵⁵)2²⁵⁵ <= 2²⁵⁵-1.
+
+ Write x=r+19(2⁻²⁵⁵)r+y.
+ Then 0 < x < 2²⁵⁵ so floor(2⁻²⁵⁵x) = 0 so floor(q+2⁻²⁵⁵x) = q.
+
+ Have q+2⁻²⁵⁵x = 2⁻²⁵⁵(h + 19 2^(-25) h9 + 2^(-1))
+ so floor(2⁻²⁵⁵(h + 19 2^(-25) h9 + 2^(-1))) = q.
+*/
+export function fe_reduce (h: FieldElement, f: FieldElement): void {
+ let h0 = f[0]
+ let h1 = f[1]
+ let h2 = f[2]
+ let h3 = f[3]
+ let h4 = f[4]
+ let h5 = f[5]
+ let h6 = f[6]
+ let h7 = f[7]
+ let h8 = f[8]
+ let h9 = f[9]
+ let q: i32
+ let carry0: i32, carry1: i32, carry2: i32, carry3: i32, carry4: i32, carry5: i32, carry6: i32, carry7: i32, carry8: i32, carry9: i32
+
+ q = (19 * h9 + (i32(1) << 24)) >> 25
+ q = (h0 + q) >> 26
+ q = (h1 + q) >> 25
+ q = (h2 + q) >> 26
+ q = (h3 + q) >> 25
+ q = (h4 + q) >> 26
+ q = (h5 + q) >> 25
+ q = (h6 + q) >> 26
+ q = (h7 + q) >> 25
+ q = (h8 + q) >> 26
+ q = (h9 + q) >> 25
+
+ /* Goal: Output h-(2²⁵⁵-19)q, which is between 0 and 2²⁵⁵-20. */
+ h0 += 19 * q
+ /* Goal: Output h-2²⁵⁵ q, which is between 0 and 2²⁵⁵-20. */
+
+ carry0 = h0 >> 26
+ h1 += carry0
+ h0 -= carry0 << 26
+ carry1 = h1 >> 25
+ h2 += carry1
+ h1 -= carry1 << 25
+ carry2 = h2 >> 26
+ h3 += carry2
+ h2 -= carry2 << 26
+ carry3 = h3 >> 25
+ h4 += carry3
+ h3 -= carry3 << 25
+ carry4 = h4 >> 26
+ h5 += carry4
+ h4 -= carry4 << 26
+ carry5 = h5 >> 25
+ h6 += carry5
+ h5 -= carry5 << 25
+ carry6 = h6 >> 26
+ h7 += carry6
+ h6 -= carry6 << 26
+ carry7 = h7 >> 25
+ h8 += carry7
+ h7 -= carry7 << 25
+ carry8 = h8 >> 26
+ h9 += carry8
+ h8 -= carry8 << 26
+ carry9 = h9 >> 25
+ h9 -= carry9 << 25
+
+ h[0] = h0
+ h[1] = h1
+ h[2] = h2
+ h[3] = h3
+ h[4] = h4
+ h[5] = h5
+ h[6] = h6
+ h[7] = h7
+ h[8] = h8
+ h[9] = h9
+}
+
/**
* Square a FieldElement and store the result.
*
v128.store(o, v128.sub<i32>(ai, bi), 48)
}
+/**
+ * Goal: Output h0+...+2²⁵⁵ h10-2²⁵⁵ q, which is between 0 and 2²⁵⁵-20.
+ * Have h0+...+2²³⁰ h9 between 0 and 2²⁵⁵-1;
+ * evidently 2²⁵⁵ h10-2²⁵⁵ q = 0.
+ *
+ * Goal: Output h0+...+2²³⁰ h9.
+ */
+const t: FieldElement = fe()
+export function fe_tobytes (s: StaticArray<u8>, h: FieldElement): void {
+ fe_reduce(t, h)
+ s[0] = u8(t[0] >> 0)
+ s[1] = u8(t[0] >> 8)
+ s[2] = u8(t[0] >> 16)
+ s[3] = u8((t[0] >> 24) | (t[1] * (i32(1) << 2)))
+ s[4] = u8(t[1] >> 6)
+ s[5] = u8(t[1] >> 14)
+ s[6] = u8((t[1] >> 22) | (t[2] * (i32(1) << 3)))
+ s[7] = u8(t[2] >> 5)
+ s[8] = u8(t[2] >> 13)
+ s[9] = u8((t[2] >> 21) | (t[3] * (i32(1) << 5)))
+ s[10] = u8(t[3] >> 3)
+ s[11] = u8(t[3] >> 11)
+ s[12] = u8((t[3] >> 19) | (t[4] * (i32(1) << 6)))
+ s[13] = u8(t[4] >> 2)
+ s[14] = u8(t[4] >> 10)
+ s[15] = u8(t[4] >> 18)
+ s[16] = u8(t[5] >> 0)
+ s[17] = u8(t[5] >> 8)
+ s[18] = u8(t[5] >> 16)
+ s[19] = u8((t[5] >> 24) | (t[6] * (i32(1) << 1)))
+ s[20] = u8(t[6] >> 7)
+ s[21] = u8(t[6] >> 15)
+ s[22] = u8((t[6] >> 23) | (t[7] * (i32(1) << 3)))
+ s[23] = u8(t[7] >> 5)
+ s[24] = u8(t[7] >> 13)
+ s[25] = u8((t[7] >> 21) | (t[8] * (i32(1) << 4)))
+ s[26] = u8(t[8] >> 4)
+ s[27] = u8(t[8] >> 12)
+ s[28] = u8((t[8] >> 20) | (t[9] * (i32(1) << 6)))
+ s[29] = u8(t[9] >> 2)
+ s[30] = u8(t[9] >> 10)
+ s[31] = u8(t[9] >> 18)
+}
+
/**
* Internal function.
*
//! SPDX-License-Identifier: GPL-3.0-or-later
import { base } from './base'
-import { fe, fe_0, fe_1, fe_add, fe_cmov, fe_copy, fe_mul, fe_neg, fe_sq, fe_sq2, fe_sub, FieldElement } from './fe'
+import { ed25519_d2 } from './constants'
+import { fe, fe_0, fe_1, fe_add, fe_cmov, fe_copy, fe_invert, fe_isnegative, fe_mul, fe_neg, fe_sq, fe_sq2, fe_sub, fe_tobytes, FieldElement } from './fe'
+import { equal, negative } from './utils'
/**
-ge means group element.
-
-Here the group is the set of pairs (x,y) of field elements
-satisfying -x^2 + y^2 = 1 + d x^2y^2
-where d = -121665/121666.
-
-Representations:
-ge25519_p2 (projective): (X:Y:Z) satisfying x=X/Z, y=Y/Z
-ge25519_p3 (extended): (X:Y:Z:T) satisfying x=X/Z, y=Y/Z, XY=ZT
-ge25519_p1p1 (completed): ((X:Z),(Y:T)) satisfying x=X/Z, y=Y/T
-ge25519_precomp (Duif): (y+x,y-x,2dxy)
+ * ge means group element.
+ *
+ * Here the group is the set of pairs (x,y) of field elements satisfying
+ *
+ * -x² + y² = 1 + d x²y²
+ *
+ * where d = -121665/121666.
+ *
+ * Representations:
+ * - ge_p2 (projective): (X:Y:Z) satisfying x=X/Z, y=Y/Z
+ * - ge_p3 (extended): (X:Y:Z:T) satisfying x=X/Z, y=Y/Z, XY=ZT
+ * - ge_p1p1 (completed): ((X:Z),(Y:T)) satisfying x=X/Z, y=Y/T
+ * - ge_precomp (Duif): (y+x,y-x,2dxy)
*/
-export class ProjectivePoint {
+export class ge_p2 {
X: FieldElement = fe()
Y: FieldElement = fe()
Z: FieldElement = fe()
}
-export class ExtendedPoint extends ProjectivePoint {
+export class ge_p3 extends ge_p2 {
+ T: FieldElement = fe()
+ __brand: 'ge_p3' = 'ge_p3'
+}
+
+export class ge_p1p1 extends ge_p2 {
T: FieldElement = fe()
+ __brand: 'ge_p1p1' = 'ge_p1p1'
}
-export class PrecomputedPoint {
+export class ge_precomp {
yplusx: FieldElement = fe()
yminusx: FieldElement = fe()
xy2d: FieldElement = fe()
}
-export class CachedPoint {
+export class ge_cached {
YplusX: FieldElement = fe()
YminusX: FieldElement = fe()
Z: FieldElement = fe()
//@ts-expect-error
@inline
-export function ge_p2_0 (h: ProjectivePoint): void {
+export function ge_p2_0 (h: ge_p2): void {
fe_0(h.X)
fe_1(h.Y)
fe_1(h.Z)
//@ts-expect-error
@inline
-export function ge_p3_0 (h: ExtendedPoint): void {
+export function ge_p3_0 (h: ge_p3): void {
ge_p2_0(h)
fe_0(h.T)
}
//@ts-expect-error
@inline
-export function ge_precomp_0 (h: PrecomputedPoint): void {
+export function ge_precomp_0 (h: ge_precomp): void {
fe_0(h.yplusx)
fe_0(h.yminusx)
fe_0(h.xy2d)
}
-const optblocker_u8: u8 = 0
-function negative (b: i8): i8 {
- const x: u8 = u8(b) /* 0..127: no 128..255: yes */
- return ((x >> 5) ^ optblocker_u8) >> 2 /* 1: yes; 0: no */
-}
-
-function ge_cmov (t: PrecomputedPoint, u: PrecomputedPoint, b: i8): void {
+function ge_cmov (t: ge_precomp, u: ge_precomp, b: i8): void {
fe_cmov(t.yplusx, u.yplusx, b)
fe_cmov(t.yminusx, u.yminusx, b)
fe_cmov(t.xy2d, u.xy2d, b)
}
-function equal (b: i8, c: i8): u8 {
- const x: u8 = u8(b) ^ u8(c) /* 0: yes; 1..255: no */
- let y: u32 = u32(x) /* 0: yes; 1..255: no */
- y--
- return ((y >> 29) ^ optblocker_u8) >> 2 /* 1: yes; 0: no */
-}
-
-const ge25519_cmov8_minust = new PrecomputedPoint()
-function ge_cmov8 (t: PrecomputedPoint, precomp: StaticArray<PrecomputedPoint>, b: i8): void {
+const ge25519_cmov8_minust = new ge_precomp()
+function ge_cmov8 (t: ge_precomp, precomp: StaticArray<ge_precomp>, b: i8): void {
const minust = ge25519_cmov8_minust
const bnegative: u8 = negative(b)
const babs: u8 = b - (((-bnegative) & b) * (i8(1) << 1))
ge_cmov(t, minust, bnegative)
}
-function ge_cmov8_base (t: PrecomputedPoint, pos: i32, b: i8): void {
- const base_pos = changetype<StaticArray<StaticArray<PrecomputedPoint>>>(base)[pos]
- ge_cmov8(t, base_pos, b)
+function ge_cmov8_base (t: ge_precomp, pos: i32, b: i8): void {
+ ge_cmov8(t, base[pos], b)
}
/**
* r = p + q
*/
const ge_add_precomp_t0: FieldElement = fe()
-function ge_add_precomp (r: ExtendedPoint, p: ExtendedPoint, q: PrecomputedPoint): void {
+function ge_add_precomp (r: ge_p1p1, p: ge_p3, q: ge_precomp): void {
const t0 = ge_add_precomp_t0
fe_add(r.X, p.Y, p.X)
fe_sub(r.Y, p.Y, p.X)
fe_sub(r.T, t0, r.T)
}
-function ge_p1p1_to_p3 (r: ExtendedPoint, p: ExtendedPoint): void {
+function ge_p1p1_to_p3 (r: ge_p3, p: ge_p1p1): void {
fe_mul(r.X, p.X, p.T)
fe_mul(r.Y, p.Y, p.Z)
fe_mul(r.Z, p.Z, p.T)
fe_mul(r.T, p.X, p.Y)
}
-function ge_p3_to_p2 (r: ProjectivePoint, p: ExtendedPoint): void {
+function ge_p3_to_p2 (r: ge_p2, p: ge_p3): void {
fe_copy(r.X, p.X)
fe_copy(r.Y, p.Y)
fe_copy(r.Z, p.Z)
}
const ge_p2_dbl_t0: FieldElement = fe()
-function ge_p2_dbl (r: ExtendedPoint, p: ProjectivePoint): void {
+function ge_p2_dbl (r: ge_p1p1, p: ge_p2): void {
const t0 = ge_p2_dbl_t0
fe_sq(r.X, p.X)
fe_sq(r.Z, p.Y)
/**
* r = 2 * p
*/
-const ge_p3_dbl_q = new ProjectivePoint()
-function ge_p3_dbl (r: ExtendedPoint, p: ExtendedPoint): void {
+const ge_p3_dbl_q = new ge_p2()
+function ge_p3_dbl (r: ge_p1p1, p: ge_p3): void {
const q = ge_p3_dbl_q
ge_p3_to_p2(q, p)
ge_p2_dbl(r, q)
/**
* r = p
*/
-function ge_p1p1_to_p2 (r: ProjectivePoint, p: ExtendedPoint): void {
+function ge_p1p1_to_p2 (r: ge_p2, p: ge_p1p1): void {
fe_mul(r.X, p.X, p.T)
fe_mul(r.Y, p.Y, p.Z)
fe_mul(r.Z, p.Z, p.T)
}
-// /**
-// h = a * B (with precomputation)
-// where a = 256⁰*a[0] + 256¹*a[1] + ... + 256³¹*a[31]
-// B is the Ed25519 base point (x,4/5) with x positive
-// (as bytes: 0x5866666666666666666666666666666666666666666666666666666666666666)
-// Preconditions:
-// a[31] <= 127
-// */
-const ge_scalarmult_base_e: StaticArray<i8> = new StaticArray<i8>(64)
-const ge_scalarmult_base_r = new ExtendedPoint()
-const ge_scalarmult_base_s = new ProjectivePoint()
-const ge_scalarmult_base_t = new PrecomputedPoint()
-export function ge_scalarmult_base (h: ExtendedPoint, a: StaticArray<u8>): void {
+/**
+ * r = p
+ */
+function ge_p3_to_cached (r: ge_cached, p: ge_p3): void {
+ fe_add(r.YplusX, p.Y, p.X)
+ fe_sub(r.YminusX, p.Y, p.X)
+ fe_copy(r.Z, p.Z)
+ fe_mul(r.T2d, p.T, ed25519_d2)
+}
+
+/**
+ * r = p + q
+ */
+const ge_add_cached_t0: FieldElement = fe()
+function ge_add_cached (r: ge_p1p1, p: ge_p3, q: ge_cached): void {
+ const t0 = ge_add_cached_t0
+ fe_add(r.X, p.Y, p.X)
+ fe_sub(r.Y, p.Y, p.X)
+ fe_mul(r.Z, r.X, q.YplusX)
+ fe_mul(r.Y, r.Y, q.YminusX)
+ fe_mul(r.T, q.T2d, p.T)
+ fe_mul(r.X, p.Z, q.Z)
+ fe_add(t0, r.X, r.X)
+ fe_sub(r.X, r.Z, r.Y)
+ fe_add(r.Y, r.Z, r.Y)
+ fe_add(r.Z, t0, r.T)
+ fe_sub(r.T, t0, r.T)
+}
+
+function ge_cached_0 (h: ge_cached): void {
+ fe_1(h.YplusX)
+ fe_1(h.YminusX)
+ fe_1(h.Z)
+ fe_0(h.T2d)
+}
+
+
+function ge_cmov_cached (t: ge_cached, u: ge_cached, b: u8): void {
+ fe_cmov(t.YplusX, u.YplusX, b)
+ fe_cmov(t.YminusX, u.YminusX, b)
+ fe_cmov(t.Z, u.Z, b)
+ fe_cmov(t.T2d, u.T2d, b)
+}
+
+const ge_cmov8_cached_minust: ge_cached = new ge_cached()
+function ge_cmov8_cached (t: ge_cached, cached: StaticArray<ge_cached>, b: i8): void {
+ const minust = ge_cmov8_cached_minust
+ const bnegative: u8 = negative(b)
+ const babs: u8 = b - (((-bnegative) & b) << 1)
+
+ ge_cached_0(t)
+ ge_cmov_cached(t, cached[0], equal(babs, 1))
+ ge_cmov_cached(t, cached[1], equal(babs, 2))
+ ge_cmov_cached(t, cached[2], equal(babs, 3))
+ ge_cmov_cached(t, cached[3], equal(babs, 4))
+ ge_cmov_cached(t, cached[4], equal(babs, 5))
+ ge_cmov_cached(t, cached[5], equal(babs, 6))
+ ge_cmov_cached(t, cached[6], equal(babs, 7))
+ ge_cmov_cached(t, cached[7], equal(babs, 8))
+ fe_copy(minust.YplusX, t.YminusX)
+ fe_copy(minust.YminusX, t.YplusX)
+ fe_copy(minust.Z, t.Z)
+ fe_neg(minust.T2d, t.T2d)
+ ge_cmov_cached(t, minust, bnegative)
+}
+
+const ge_scalarmult_e: StaticArray<i8> = new StaticArray<i8>(64)
+const ge_scalarmult_r: ge_p1p1 = new ge_p1p1()
+const ge_scalarmult_s: ge_p2 = new ge_p2()
+const ge_scalarmult_t2: ge_p1p1 = new ge_p1p1()
+const ge_scalarmult_t3: ge_p1p1 = new ge_p1p1()
+const ge_scalarmult_t4: ge_p1p1 = new ge_p1p1()
+const ge_scalarmult_t5: ge_p1p1 = new ge_p1p1()
+const ge_scalarmult_t6: ge_p1p1 = new ge_p1p1()
+const ge_scalarmult_t7: ge_p1p1 = new ge_p1p1()
+const ge_scalarmult_t8: ge_p1p1 = new ge_p1p1()
+const ge_scalarmult_p2: ge_p3 = new ge_p3()
+const ge_scalarmult_p3: ge_p3 = new ge_p3()
+const ge_scalarmult_p4: ge_p3 = new ge_p3()
+const ge_scalarmult_p5: ge_p3 = new ge_p3()
+const ge_scalarmult_p6: ge_p3 = new ge_p3()
+const ge_scalarmult_p7: ge_p3 = new ge_p3()
+const ge_scalarmult_p8: ge_p3 = new ge_p3()
+const ge_scalarmult_pi: StaticArray<ge_cached> = new StaticArray<ge_cached>(8)
+const ge_scalarmult_t: ge_cached = new ge_cached()
+/**
+ * `h = a * p`
+ *
+ * where
+ *
+ * `a = 256⁰a[0] + 256¹a[1] + ... + 256³¹a[31]`
+ *
+ * Preconditions:
+ * - `a[31] <= 127`
+ * - `p` is public
+ */
+export function ge_scalarmult (h: ge_p3, a: StaticArray<u8>, p: ge_p3): void {
+ const e = ge_scalarmult_e
+ const r = ge_scalarmult_r
+ const s = ge_scalarmult_s
+ const t2 = ge_scalarmult_t2
+ const t3 = ge_scalarmult_t3
+ const t4 = ge_scalarmult_t4
+ const t5 = ge_scalarmult_t5
+ const t6 = ge_scalarmult_t6
+ const t7 = ge_scalarmult_t7
+ const t8 = ge_scalarmult_t8
+ const p2 = ge_scalarmult_p2
+ const p3 = ge_scalarmult_p3
+ const p4 = ge_scalarmult_p4
+ const p5 = ge_scalarmult_p5
+ const p6 = ge_scalarmult_p6
+ const p7 = ge_scalarmult_p7
+ const p8 = ge_scalarmult_p8
+ const pi = ge_scalarmult_pi
+ const t = ge_scalarmult_t
+ let carry: i8
+ let i: i8
+
+ ge_p3_to_cached(pi[1 - 1], p) /* p */
+
+ ge_p3_dbl(t2, p)
+ ge_p1p1_to_p3(p2, t2)
+ ge_p3_to_cached(pi[2 - 1], p2) /* 2p = 2*p */
+
+ ge_add_cached(t3, p, pi[2 - 1])
+ ge_p1p1_to_p3(p3, t3)
+ ge_p3_to_cached(pi[3 - 1], p3) /* 3p = 2p+p */
+
+ ge_p3_dbl(t4, p2)
+ ge_p1p1_to_p3(p4, t4)
+ ge_p3_to_cached(pi[4 - 1], p4) /* 4p = 2*2p */
+
+ ge_add_cached(t5, p, pi[4 - 1])
+ ge_p1p1_to_p3(p5, t5)
+ ge_p3_to_cached(pi[5 - 1], p5) /* 5p = 4p+p */
+
+ ge_p3_dbl(t6, p3)
+ ge_p1p1_to_p3(p6, t6)
+ ge_p3_to_cached(pi[6 - 1], p6) /* 6p = 2*3p */
+
+ ge_add_cached(t7, p, pi[6 - 1])
+ ge_p1p1_to_p3(p7, t7)
+ ge_p3_to_cached(pi[7 - 1], p7) /* 7p = 6p+p */
+
+ ge_p3_dbl(t8, p4)
+ ge_p1p1_to_p3(p8, t8)
+ ge_p3_to_cached(pi[8 - 1], p8) /* 8p = 2*4p */
+
+ for (i = 0; i < 32; ++i) {
+ e[2 * i + 0] = (a[i] >> 0) & 15
+ e[2 * i + 1] = (a[i] >> 4) & 15
+ }
+ /* each e[i] is between 0 and 15 */
+ /* e[63] is between 0 and 7 */
+
+ carry = 0
+ for (i = 0; i < 63; ++i) {
+ e[i] += carry
+ carry = e[i] + 8
+ carry >>= 4
+ e[i] -= i8(carry << 4)
+ }
+ e[63] += carry
+ /* each e[i] is between -8 and 8 */
+
+ ge_p3_0(h)
+
+ for (i = 63; i != 0; i--) {
+ ge_cmov8_cached(t, pi, e[i])
+ ge_add_cached(r, h, t)
+
+ ge_p1p1_to_p2(s, r)
+ ge_p2_dbl(r, s)
+ ge_p1p1_to_p2(s, r)
+ ge_p2_dbl(r, s)
+ ge_p1p1_to_p2(s, r)
+ ge_p2_dbl(r, s)
+ ge_p1p1_to_p2(s, r)
+ ge_p2_dbl(r, s)
+
+ ge_p1p1_to_p3(h, r) /* *16 */
+ }
+ ge_cmov8_cached(t, pi, e[i])
+ ge_add_cached(r, h, t)
+
+ ge_p1p1_to_p3(h, r)
+}
+
+const ge_scalarmult_base_e: StaticArray<u8> = new StaticArray<u8>(64)
+const ge_scalarmult_base_r: ge_p1p1 = new ge_p1p1()
+const ge_scalarmult_base_s = new ge_p2()
+const ge_scalarmult_base_t = new ge_precomp()
+/**
+ * `h = a * B` (with precomputation)
+ *
+ * where
+ *
+ * `a = 256⁰a[0] + 256¹a[1] + ... + 256³¹a[31]`
+ *
+ * B is the Ed25519 base point `(x,⅘)` with `x` positive. As bytes:
+ *
+ * `0x5866666666666666666666666666666666666666666666666666666666666666`
+ *
+ * Preconditions:
+ * - `a[31] <= 127`
+ */
+export function ge_scalarmult_base (h: ge_p3, a: StaticArray<u8>): void {
const e = ge_scalarmult_base_e
let carry: i8 = 0
const r = ge_scalarmult_base_r
ge_p1p1_to_p3(h, r)
}
}
+
+const ge_p3_tobytes_recip: FieldElement = fe()
+const ge_p3_tobytes_x: FieldElement = fe()
+const ge_p3_tobytes_y: FieldElement = fe()
+export function ge_p3_tobytes (s: StaticArray<u8>, h: ge_p3): void {
+ const recip = ge_p3_tobytes_recip
+ const x = ge_p3_tobytes_x
+ const y = ge_p3_tobytes_y
+ fe_invert(recip, h.Z)
+ fe_mul(x, h.X, recip)
+ fe_mul(y, h.Y, recip)
+ fe_tobytes(s, y)
+ s[31] ^= fe_isnegative(x) << 7
+}
+
+/**
+ * true if `s < p`
+ */
+export function ge_is_canonical (s: StaticArray<u8>): boolean {
+ let c: u32 = (s[31] & 0x7f) ^ 0x7f
+ for (let i = 30; i > 0; i--) {
+ c |= s[i] ^ 0xff
+ }
+ c = (c - 1) >> 8
+ const d: u32 = (0xec - u32(s[0])) >> 8
+ return (c & d & 1) == 0
+}
\r
import { Blake2b } from './blake2b'\r
import { fe, fe_add, fe_copy, fe_cswap, fe_mul, fe_neg, fe_sq, fe_sub, FieldElement } from './fe'\r
-import { ExtendedPoint, ge_p3_0 } from './ge'\r
+import { ge_is_canonical, ge_p3, ge_p3_0 } from './ge'\r
+import { sc_is_canonical, sc_muladd, sc_reduce } from './sc'\r
\r
const BLOCKHASH_BYTES: i32 = 32\r
const PRIVATEKEY_BYTES: i32 = 32\r
const SIGNATURE_BYTES: i32 = 64\r
const SIGNEDBLOCKHASH_BYTES: i32 = SIGNATURE_BYTES + BLOCKHASH_BYTES\r
\r
-const BASE: ExtendedPoint = {\r
+const BASE: ge_p3 = {\r
X: fe([0xd51a, 0x8f25, 0x2d60, 0xc956, 0xa7b2, 0x9525, 0xc760, 0x692c, 0xdc5c, 0xfdd6, 0xe231, 0xc0a4, 0x53fe, 0xcd6e, 0x36d3, 0x2169]),\r
Y: fe([0x6658, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666]),\r
Z: fe([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),\r
- T: fe([0xdd90, 0xa5b7, 0x8ab3, 0x6dde, 0x52f5, 0x7751, 0x9f80, 0x20f0, 0xe37d, 0x64ab, 0x4e8e, 0x66ea, 0x7665, 0xd78b, 0x5f0f, 0xe787])\r
+ T: fe([0xdd90, 0xa5b7, 0x8ab3, 0x6dde, 0x52f5, 0x7751, 0x9f80, 0x20f0, 0xe37d, 0x64ab, 0x4e8e, 0x66ea, 0x7665, 0xd78b, 0x5f0f, 0xe787]),\r
+ __brand: 'ge_p3'\r
}\r
const D: FieldElement = fe([0x78a3, 0x1359, 0x4dca, 0x75eb, 0xd8ab, 0x4141, 0x0a4d, 0x0070, 0xe898, 0x7779, 0x4079, 0x8cc7, 0xfe73, 0x2b6f, 0x6cee, 0x5203])\r
const D2: FieldElement = fe([0xf159, 0x26b2, 0x9b94, 0xebd6, 0xb156, 0x8283, 0x149a, 0x00e0, 0xd130, 0xeef3, 0x80f2, 0x198e, 0xfce7, 0x56df, 0xd9dc, 0x2406])\r
const INPUT_BUFFER = memory.data(128)\r
const OUTPUT_BUFFER = memory.data(64)\r
\r
-// a, b = StaticArray<u8>[32]\r
+// a, b = 32 bytes\r
//@ts-expect-error\r
@inline\r
-function bitsdiff (a: usize, b: usize): bool {\r
- let d = load<i64>(a) ^ load<i64>(b)\r
- d |= load<i64>(a, 8) ^ load<i64>(b, 8)\r
- d |= load<i64>(a, 16) ^ load<i64>(b, 16)\r
- d |= load<i64>(a, 24) ^ load<i64>(b, 24)\r
+function bitsdiff (a: StaticArray<number>, b: StaticArray<number>): bool {\r
+ const ai = changetype<usize>(a)\r
+ const bi = changetype<usize>(b)\r
+ let d = load<i64>(ai) ^ load<i64>(bi)\r
+ d |= load<i64>(ai, 8) ^ load<i64>(bi, 8)\r
+ d |= load<i64>(ai, 16) ^ load<i64>(bi, 16)\r
+ d |= load<i64>(ai, 24) ^ load<i64>(bi, 24)\r
return d !== 0\r
}\r
\r
const d = neq_d\r
pack25519(c, a)\r
pack25519(d, b)\r
- return bitsdiff(changetype<usize>(c), changetype<usize>(d))\r
+ return bitsdiff(c, d)\r
}\r
\r
const pack_m: FieldElement = fe()\r
* X3 = E*F, Y3 = G*H, Z3 = F*G, T3 = E*H\r
*/\r
// p, q = StaticArray<i32>[4][16]\r
-function add (p: ExtendedPoint, q: ExtendedPoint): void {\r
+function add (p: ge_p3, q: ge_p3): void {\r
fe_sub(a, p.Y, p.X)\r
fe_sub(t, q.Y, q.X)\r
fe_mul(a, a, t)\r
* E = (X+Y)²-A-B, G = D+B, F = G-C, H = D-B\r
* X3 = E*F, Y3 = G*H, Z3 = F*G, T3 = E*H\r
*/\r
-function double (p: ExtendedPoint): void {\r
+function double (p: ge_p3): void {\r
fe_sq(a, p.X)\r
fe_sq(b, p.Y)\r
fe_sq(c, p.Z)\r
// offsets i32x16 = 64 bytes\r
//@ts-expect-error\r
@inline\r
-function cswap (p: ExtendedPoint, q: ExtendedPoint, b: i32): void {\r
+function cswap (p: ge_p3, q: ge_p3, b: i32): void {\r
fe_cswap(p.X, q.X, b)\r
fe_cswap(p.Y, q.Y, b)\r
fe_cswap(p.Z, q.Z, b)\r
*\r
* p = StaticArray<i32>[4][16] = (X,Y,Z,T) as 16 limbs each\r
*/\r
-function pack (r: StaticArray<u8>, p: ExtendedPoint): void {\r
+function pack (r: StaticArray<u8>, p: ge_p3): void {\r
inv25519(zi, p.Z)\r
fe_mul(tx, p.X, zi)\r
fe_mul(ty, p.Y, zi)\r
}\r
\r
// p, q = StaticArray<i32>[4][16]\r
-function scalarmult (p: ExtendedPoint, q: ExtendedPoint, s: StaticArray<u8>): void {\r
+function scalarmult (p: ge_p3, q: ge_p3, s: StaticArray<u8>): void {\r
for (let i = 255; i >= 0; i--) {\r
const b: i32 = (s[i >> 3] >> u8(i & 7)) & 1\r
cswap(p, q, b)\r
}\r
\r
// p = StaticArray<i32>[4][16]\r
-const scalarbase_q = new ExtendedPoint()\r
-function scalarbase (p: ExtendedPoint, s: StaticArray<u8>): void {\r
+const scalarbase_q = new ge_p3()\r
+function scalarbase (p: ge_p3, s: StaticArray<u8>): void {\r
const q = scalarbase_q\r
fe_copy(q.X, BASE.X)\r
fe_copy(q.Y, BASE.Y)\r
scalarmult(p, q, s)\r
}\r
\r
-// Group order of the curve ≈ 2²⁵²\r
-const L = StaticArray.fromArray<i32>([\r
- 0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58,\r
- 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14,\r
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\r
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10\r
-])\r
-\r
-function modL (r: StaticArray<u8>, x: StaticArray<i32>): void {\r
- let c: i32\r
- let t: i32\r
- let v: i32\r
- for (let i = 63; i >= 32; --i) {\r
- c = 0\r
- const xi = x[i]\r
- for (let j = i - 32, k = i - 12; j < k; j++) {\r
- t = (xi * L[j - (i - 32)]) << 4\r
- v = x[j] + c - t\r
- c = (v + 128) >> 8\r
- x[j] = v - (c << 8)\r
- }\r
- x[i - 12] += c\r
- x[i] = 0\r
- }\r
- c = 0\r
- const x31 = x[31]\r
- for (let j = 0; j < 32; j++) {\r
- x[j] += c - (x31 >> 4) * L[j]\r
- c = x[j] >> 8\r
- x[j] &= 255\r
- }\r
- for (let j = 0; j < 32; j++) {\r
- x[j] -= c * L[j]\r
- }\r
- for (let i = 0; i < 32; i++) {\r
- x[i + 1] += x[i] >> 8\r
- r[i] = u8(x[i] & 255)\r
- }\r
-}\r
-\r
-const reduce_x = new StaticArray<i32>(64)\r
-function reduce (r: StaticArray<u8>): void {\r
- const x = reduce_x\r
- for (let i = 0; i < 64; i++) {\r
- x[i] = i32(r[i])\r
- r[i] = 0\r
- }\r
- modL(r, x)\r
-}\r
-\r
-// r = StaticArray<i32>[4][16]\r
const unpack_chk: FieldElement = fe()\r
const unpack_num: FieldElement = fe()\r
-const unpack_den = new ExtendedPoint()\r
+const unpack_den = new ge_p3()\r
const unpack_t: FieldElement = fe()\r
-function unpackneg (r: ExtendedPoint, p: StaticArray<u8>): i8 {\r
+function unpackneg (r: ge_p3, p: StaticArray<u8>): i8 {\r
const chk = unpack_chk\r
const num = unpack_num\r
const den = unpack_den\r
return 0\r
}\r
\r
-// Validate signature scalar S is canonical (S < L)\r
-function canonical (S: StaticArray<u8>): boolean {\r
- // If S >= 2^253 then S >= L for sure.\r
- if ((S[31] & 0xE0) != 0) return false\r
-\r
- // Check S-L for underflow (c=1) which means S < L\r
- let c = 0\r
- for (let i = 0; i < 32; i++) {\r
- const diff = S[i] - L[i] - c\r
- c = (diff >> 31) & 1\r
- }\r
- return c == 1\r
+//@ts-expect-error\r
+@inline\r
+function clamp (k: StaticArray<u8>): void {\r
+ k[0] &= 248\r
+ k[31] &= 127\r
+ k[31] |= 64\r
}\r
\r
const blake2b = new Blake2b()\r
blake2b.init().update(i).digest(o)\r
}\r
\r
-const crypto_derive_h = new StaticArray<u8>(64)\r
-const crypto_derive_p = new ExtendedPoint()\r
+const crypto_derive_A = new ge_p3()\r
function crypto_derive (pk: StaticArray<u8>, sk: StaticArray<u8>): void {\r
- const h = crypto_derive_h\r
- const p = crypto_derive_p\r
- ge_p3_0(p)\r
+ const A = crypto_derive_A\r
+ ge_p3_0(A)\r
\r
- crypto_hash(h, sk)\r
- h[0] &= 248\r
- h[31] &= 127\r
- h[31] |= 64\r
+ crypto_hash(sk, sk)\r
+ clamp(sk)\r
\r
- scalarbase(p, h)\r
- pack(pk, p)\r
+ scalarbase(A, sk)\r
+ pack(pk, A)\r
}\r
\r
-const crypto_sign_d = new StaticArray<u8>(64)\r
-const crypto_sign_h = new StaticArray<u8>(64)\r
-const crypto_sign_p = new ExtendedPoint()\r
-const crypto_sign_r = new StaticArray<u8>(64)\r
-const crypto_sign_x = new StaticArray<i32>(64)\r
-const crypto_sign_s = new StaticArray<u8>(SIGNATURE_BYTES)\r
-const crypto_sign_prv = new StaticArray<u8>(PRIVATEKEY_BYTES)\r
-function crypto_sign (sm: StaticArray<u8>, m: StaticArray<u8>, sk: StaticArray<u8>): void {\r
- const d = crypto_sign_d\r
- const h = crypto_sign_h\r
- const p = crypto_sign_p\r
- ge_p3_0(p)\r
-\r
- const r = crypto_sign_r\r
- const x = crypto_sign_x\r
- const s = crypto_sign_s\r
- const prv = changetype<usize>(crypto_sign_prv)\r
-\r
- memory.copy(prv, changetype<usize>(sk), PRIVATEKEY_BYTES)\r
- crypto_hash(d, changetype<StaticArray<u8>>(prv))\r
- d[0] &= 248\r
- d[31] &= 127\r
- d[31] |= 64\r
-\r
- memory.copy(changetype<usize>(s), changetype<usize>(d) + 32, 32)\r
- memory.copy(changetype<usize>(sm), changetype<usize>(d) + 32, 32)\r
-\r
- memory.copy(changetype<usize>(s) + 32, changetype<usize>(m), 32)\r
- memory.copy(changetype<usize>(sm) + 64, changetype<usize>(m), 32)\r
-\r
- crypto_hash(r, s)\r
- reduce(r)\r
- scalarbase(p, r)\r
- pack(sm, p)\r
-\r
- for (let i = 32; i < 64; i++) {\r
- sm[i] = sk[i]\r
- }\r
- crypto_hash(h, sm)\r
- reduce(h)\r
-\r
- x.fill(0)\r
- for (let i = 0; i < 32; i++) {\r
- x[i] = r[i]\r
- }\r
- for (let i = 0; i < 32; i++) {\r
- for (let j = 0; j < 32; j++) {\r
- x[i + j] += i32(h[i]) * i32(d[j])\r
- }\r
- }\r
-\r
- for (let i = 0; i < SIGNATURE_BYTES; i++) {\r
- s[i] = sm[i + 32]\r
- }\r
- modL(s, x)\r
- for (let i = 0; i < SIGNATURE_BYTES; i++) {\r
- sm[i + 32] = s[i]\r
- }\r
+const crypto_sign_az = new StaticArray<u8>(64)\r
+const crypto_sign_nonce = new StaticArray<u8>(64)\r
+const crypto_sign_hram = new StaticArray<u8>(64)\r
+const crypto_sign_R = new ge_p3()\r
+const crypto_sign_zm = new StaticArray<u8>(SIGNATURE_BYTES)\r
+const crypto_sign_t = new StaticArray<u8>(PRIVATEKEY_BYTES)\r
+function crypto_sign (s: StaticArray<u8>, m: StaticArray<u8>, sk: StaticArray<u8>): void {\r
+ const az = crypto_sign_az\r
+ const nonce = crypto_sign_nonce\r
+ const hram = crypto_sign_hram\r
+ const R = crypto_sign_R\r
+ const zm = crypto_sign_zm\r
+ const t = crypto_sign_t\r
+ ge_p3_0(R)\r
+\r
+ // Hash secret key to private scalar `a` and prefix for nonce derivation `z`\r
+ memory.copy(changetype<usize>(t), changetype<usize>(sk), PRIVATEKEY_BYTES)\r
+ crypto_hash(az, t)\r
+ clamp(az)\r
+\r
+ // Derive nonce from prefix `z` and message `m`\r
+ memory.copy(changetype<usize>(zm), changetype<usize>(az) + 32, 32)\r
+ memory.copy(changetype<usize>(zm) + 32, changetype<usize>(m), 32)\r
+ crypto_hash(nonce, zm)\r
+ sc_reduce(nonce)\r
+\r
+ // Compute R = rB\r
+ scalarbase(R, nonce)\r
+ pack(s, R)\r
+\r
+ // Concatenate public key `A` and message `M`\r
+ // from parameter arguments: A = sk[0,32], M = m\r
+ memory.copy(changetype<usize>(s) + 32, changetype<usize>(sk) + 32, 32)\r
+ memory.copy(changetype<usize>(s) + 64, changetype<usize>(m), 32)\r
+\r
+ // Compute challenge hash now that `s = (R || A || M)`\r
+ crypto_hash(hram, s)\r
+ sc_reduce(hram)\r
+\r
+ // Compute `S = (r + h*a) mod L` and construct final signature `s = (R || S)`\r
+ sc_muladd(changetype<StaticArray<u8>>(changetype<usize>(s) + 32), az, hram, nonce)\r
+\r
+ // Clean up sensitive data\r
+ az.fill(0)\r
+ nonce.fill(0)\r
}\r
\r
-const crypto_verify_open_d = new StaticArray<u8>(64)\r
-const crypto_verify_open_p = new ExtendedPoint()\r
-const crypto_verify_open_q = new ExtendedPoint()\r
+const crypto_verify_open_hram = new StaticArray<u8>(64)\r
+const crypto_verify_open_p = new ge_p3()\r
+const crypto_verify_open_q = new ge_p3()\r
const crypto_verify_open_t = new StaticArray<u8>(32)\r
-const crypto_verify_open_sm = new StaticArray<u8>(SIGNEDBLOCKHASH_BYTES)\r
+const crypto_verify_open_ram = new StaticArray<u8>(SIGNEDBLOCKHASH_BYTES)\r
const crypto_verify_open_S = new StaticArray<u8>(32)\r
/**\r
-* Verifies block hash `h` was signed with signature `s` against public key `k`.\r
+* Verify signature `s` was made by signing message `m` using public key `pk`.\r
*/\r
-function crypto_verify (h: StaticArray<u8>, s: StaticArray<u8>, k: StaticArray<u8>): bool {\r
- const d = crypto_verify_open_d\r
+function crypto_verify (s: StaticArray<u8>, m: StaticArray<u8>, pk: StaticArray<u8>): bool {\r
+ const hram = crypto_verify_open_hram\r
const p = crypto_verify_open_p\r
const q = crypto_verify_open_q\r
ge_p3_0(p)\r
ge_p3_0(q)\r
\r
const t = crypto_verify_open_t\r
- const sm = crypto_verify_open_sm\r
+ const ram = crypto_verify_open_ram\r
const S = crypto_verify_open_S\r
\r
+ // fail if public key `k` is non-canonical (`p = 2²⁵⁵-19 <= k`)\r
+ if (!ge_is_canonical(pk)) return false\r
+\r
+ // fail if private scalar `S` is non-canonical (`L <= S`)\r
+ memory.copy(changetype<usize>(S), changetype<usize>(s) + 32, 32)\r
+ if (!sc_is_canonical(S)) return false\r
+\r
// fail\r
- if (unpackneg(q, k)) return false\r
+ if (unpackneg(q, pk)) return false\r
\r
// signature is nonce point R and scalar S (R || S)\r
- // data to hash is nonce point R, public key A, and message M (R || A || M)\r
+ // data to hash is nonce point R, public key A, and message M\r
+ // from parameter arguments: R = s[0,32], A = pk, M = m\r
// R, S, A, and M are all 32-byte values in this implementation\r
- for (let i = 0; i < 32; i++) {\r
- S[i] = s[i + 32]\r
- sm[i] = s[i]\r
- sm[i + 32] = k[i]\r
- sm[i + 64] = h[i]\r
- }\r
-\r
- // fail as non-canonical\r
- if (!canonical(S)) return false\r
-\r
- crypto_hash(d, sm)\r
- reduce(d)\r
- scalarmult(p, q, d)\r
+ memory.copy(changetype<usize>(ram), changetype<usize>(s), 32)\r
+ memory.copy(changetype<usize>(ram) + 32, changetype<usize>(pk), 32)\r
+ memory.copy(changetype<usize>(ram) + 64, changetype<usize>(m), 32)\r
+ crypto_hash(hram, ram)\r
+ sc_reduce(hram)\r
\r
+ scalarmult(p, q, hram)\r
ge_p3_0(q)\r
scalarbase(q, S)\r
add(p, q)\r
pack(t, p)\r
\r
// fail if any bit differs from encoded nonce point R, else pass\r
- return !bitsdiff(changetype<usize>(s), changetype<usize>(t))\r
+ return !bitsdiff(s, t)\r
}\r
\r
// Returns the pointer to the static input buffer (128 bytes).\r
return OUTPUT_BUFFER\r
}\r
\r
-const derive_sk = new StaticArray<u8>(PRIVATEKEY_BYTES)\r
const derive_pk = new StaticArray<u8>(PUBLICKEY_BYTES)\r
+const derive_sk = new StaticArray<u8>(PRIVATEKEY_BYTES)\r
/**\r
* Derives a Nano public key from a private key and writes it to the static\r
* output buffer. This mirrors the functionality of\r
* `nacl.sign.keyPair.fromSeed()`, returning just the `publicKey` property.\r
*/\r
export function derive (): void {\r
- const sk = derive_sk\r
const pk = derive_pk\r
+ const sk = derive_sk\r
for (let i = 0; i < PRIVATEKEY_BYTES; i++) {\r
sk[i] = load<u8>(INPUT_BUFFER + i)\r
}\r
* Verifies a signature on a block hash against a public key. This mirrors the\r
* functionality of `nacl.sign.detached.verify()`.\r
*\r
-* @param {u64} h0-h3 - Blockhash (32 bytes as 4 × u64)\r
* @param {u64} s0-s7 - Signature (64 bytes as 8 × u64)\r
+* @param {u64} h0-h3 - Blockhash (32 bytes as 4 × u64)\r
* @param {u64} k0-k3 - Public key (32 bytes as 4 × u64)\r
* @returns {i32} - 1 if valid, 0 if invalid\r
*/\r
export function verify (): i32 {\r
- const h = verify_h\r
const s = verify_s\r
+ const h = verify_h\r
const k = verify_k\r
\r
let ptr = INPUT_BUFFER\r
- for (let i = 0; i < BLOCKHASH_BYTES; i++) {\r
- h[i] = load<u8>(usize(i) + ptr)\r
- }\r
- ptr += BLOCKHASH_BYTES\r
for (let i = 0; i < SIGNATURE_BYTES; i++) {\r
s[i] = load<u8>(usize(i) + ptr)\r
}\r
ptr += SIGNATURE_BYTES\r
+ for (let i = 0; i < BLOCKHASH_BYTES; i++) {\r
+ h[i] = load<u8>(usize(i) + ptr)\r
+ }\r
+ ptr += BLOCKHASH_BYTES\r
for (let i = 0; i < PUBLICKEY_BYTES; i++) {\r
k[i] = load<u8>(usize(i) + ptr)\r
}\r
\r
const start = performance.now()\r
- const result = crypto_verify(h, s, k)\r
+ const result = crypto_verify(s, h, k)\r
const end = performance.now()\r
trace('verify time', 1, end - start)\r
\r
--- /dev/null
+//! SPDX-FileCopyrightText: 2026 Chris Duncan <chris@codecow.com>
+//! SPDX-License-Identifier: GPL-3.0-or-later
+
+/**
+ * Scalar operations modulo `L` where `L` is the group order of Curve25519.
+ */
+
+import { load_3, load_4 } from './utils'
+
+/**
+ * `L = 2²⁵² + 27742317777372353535851937790883648493`
+ */
+const L = StaticArray.fromArray<i32>([
+ 0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58,
+ 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10
+])
+
+/**
+ * Validate signature scalar S is canonical (S < L)
+ * - Accept immediately if `S < 2²⁵²` since this implies `S < L`
+ * - Reject immediately if `S >= 2²⁵³` since this implies `S >= L`
+ * - Otherwise, check canonicity for `2²⁵² <= S < 2²⁵³`
+ */
+export function sc_is_canonical (S: StaticArray<u8>): boolean {
+ if ((S[31] & 0xF0) == 0) return true
+ if ((S[31] & 0xE0) != 0) return false
+
+ // Check `S-L` for underflow (c=1) which implies `S < L`
+ let c: i32 = 0
+ for (let i = 0; i < 32; i++) {
+ const diff: i32 = S[i] - L[i] - c
+ c = (diff >> 31) & 1
+ }
+ return c == 1
+}
+
+/**
+ * Input:
+ *
+ * `256⁰a[0] + 256¹a[1] + ... + 256⁶³a[31] = a`
+ * `256⁰b[0] + 256¹b[1] + ... + 256⁶³b[31] = b`
+ * `256⁰c[0] + 256¹c[1] + ... + 256⁶³c[31] = c`
+ *
+ * Output:
+ *
+ * `256⁰s[0] + 256¹s[1] + ... + 256³¹s[31] = (ab+c) mod L`
+ */
+export function sc_muladd (s: StaticArray<u8>, a: StaticArray<u8>, b: StaticArray<u8>, c: StaticArray<u8>): void {
+ let a0: i64 = 2097151 & load_3(a, 0)
+ let a1: i64 = 2097151 & (load_4(a, 2) >> 5)
+ let a2: i64 = 2097151 & (load_3(a, 5) >> 2)
+ let a3: i64 = 2097151 & (load_4(a, 7) >> 7)
+ let a4: i64 = 2097151 & (load_4(a, 10) >> 4)
+ let a5: i64 = 2097151 & (load_3(a, 13) >> 1)
+ let a6: i64 = 2097151 & (load_4(a, 15) >> 6)
+ let a7: i64 = 2097151 & (load_3(a, 18) >> 3)
+ let a8: i64 = 2097151 & load_3(a, 21)
+ let a9: i64 = 2097151 & (load_4(a, 23) >> 5)
+ let a10: i64 = 2097151 & (load_3(a, 26) >> 2)
+ let a11: i64 = (load_4(a, 28) >> 7)
+
+ let b0: i64 = 2097151 & load_3(b, 0)
+ let b1: i64 = 2097151 & (load_4(b, 2) >> 5)
+ let b2: i64 = 2097151 & (load_3(b, 5) >> 2)
+ let b3: i64 = 2097151 & (load_4(b, 7) >> 7)
+ let b4: i64 = 2097151 & (load_4(b, 10) >> 4)
+ let b5: i64 = 2097151 & (load_3(b, 13) >> 1)
+ let b6: i64 = 2097151 & (load_4(b, 15) >> 6)
+ let b7: i64 = 2097151 & (load_3(b, 18) >> 3)
+ let b8: i64 = 2097151 & load_3(b, 21)
+ let b9: i64 = 2097151 & (load_4(b, 23) >> 5)
+ let b10: i64 = 2097151 & (load_3(b, 26) >> 2)
+ let b11: i64 = (load_4(b, 28) >> 7)
+
+ let c0: i64 = 2097151 & load_3(c, 0)
+ let c1: i64 = 2097151 & (load_4(c, 2) >> 5)
+ let c2: i64 = 2097151 & (load_3(c, 5) >> 2)
+ let c3: i64 = 2097151 & (load_4(c, 7) >> 7)
+ let c4: i64 = 2097151 & (load_4(c, 10) >> 4)
+ let c5: i64 = 2097151 & (load_3(c, 13) >> 1)
+ let c6: i64 = 2097151 & (load_4(c, 15) >> 6)
+ let c7: i64 = 2097151 & (load_3(c, 18) >> 3)
+ let c8: i64 = 2097151 & load_3(c, 21)
+ let c9: i64 = 2097151 & (load_4(c, 23) >> 5)
+ let c10: i64 = 2097151 & (load_3(c, 26) >> 2)
+ let c11: i64 = (load_4(c, 28) >> 7)
+
+ let s0: i64
+ let s1: i64
+ let s2: i64
+ let s3: i64
+ let s4: i64
+ let s5: i64
+ let s6: i64
+ let s7: i64
+ let s8: i64
+ let s9: i64
+ let s10: i64
+ let s11: i64
+ let s12: i64
+ let s13: i64
+ let s14: i64
+ let s15: i64
+ let s16: i64
+ let s17: i64
+ let s18: i64
+ let s19: i64
+ let s20: i64
+ let s21: i64
+ let s22: i64
+ let s23: i64
+
+ let carry0: i64
+ let carry1: i64
+ let carry2: i64
+ let carry3: i64
+ let carry4: i64
+ let carry5: i64
+ let carry6: i64
+ let carry7: i64
+ let carry8: i64
+ let carry9: i64
+ let carry10: i64
+ let carry11: i64
+ let carry12: i64
+ let carry13: i64
+ let carry14: i64
+ let carry15: i64
+ let carry16: i64
+ let carry17: i64
+ let carry18: i64
+ let carry19: i64
+ let carry20: i64
+ let carry21: i64
+ let carry22: i64
+
+ s0 = c0 + a0 * b0
+ s1 = c1 + a0 * b1 + a1 * b0
+ s2 = c2 + a0 * b2 + a1 * b1 + a2 * b0
+ s3 = c3 + a0 * b3 + a1 * b2 + a2 * b1 + a3 * b0
+ s4 = c4 + a0 * b4 + a1 * b3 + a2 * b2 + a3 * b1 + a4 * b0
+ s5 = c5 + a0 * b5 + a1 * b4 + a2 * b3 + a3 * b2 + a4 * b1 + a5 * b0
+ s6 = c6 + a0 * b6 + a1 * b5 + a2 * b4 + a3 * b3 + a4 * b2 + a5 * b1 +
+ a6 * b0
+ s7 = c7 + a0 * b7 + a1 * b6 + a2 * b5 + a3 * b4 + a4 * b3 + a5 * b2 +
+ a6 * b1 + a7 * b0
+ s8 = c8 + a0 * b8 + a1 * b7 + a2 * b6 + a3 * b5 + a4 * b4 + a5 * b3 +
+ a6 * b2 + a7 * b1 + a8 * b0
+ s9 = c9 + a0 * b9 + a1 * b8 + a2 * b7 + a3 * b6 + a4 * b5 + a5 * b4 +
+ a6 * b3 + a7 * b2 + a8 * b1 + a9 * b0
+ s10 = c10 + a0 * b10 + a1 * b9 + a2 * b8 + a3 * b7 + a4 * b6 + a5 * b5 +
+ a6 * b4 + a7 * b3 + a8 * b2 + a9 * b1 + a10 * b0
+ s11 = c11 + a0 * b11 + a1 * b10 + a2 * b9 + a3 * b8 + a4 * b7 + a5 * b6 +
+ a6 * b5 + a7 * b4 + a8 * b3 + a9 * b2 + a10 * b1 + a11 * b0
+ s12 = a1 * b11 + a2 * b10 + a3 * b9 + a4 * b8 + a5 * b7 + a6 * b6 +
+ a7 * b5 + a8 * b4 + a9 * b3 + a10 * b2 + a11 * b1
+ s13 = a2 * b11 + a3 * b10 + a4 * b9 + a5 * b8 + a6 * b7 + a7 * b6 +
+ a8 * b5 + a9 * b4 + a10 * b3 + a11 * b2
+ s14 = a3 * b11 + a4 * b10 + a5 * b9 + a6 * b8 + a7 * b7 + a8 * b6 +
+ a9 * b5 + a10 * b4 + a11 * b3
+ s15 = a4 * b11 + a5 * b10 + a6 * b9 + a7 * b8 + a8 * b7 + a9 * b6 +
+ a10 * b5 + a11 * b4
+ s16 =
+ a5 * b11 + a6 * b10 + a7 * b9 + a8 * b8 + a9 * b7 + a10 * b6 + a11 * b5
+ s17 = a6 * b11 + a7 * b10 + a8 * b9 + a9 * b8 + a10 * b7 + a11 * b6
+ s18 = a7 * b11 + a8 * b10 + a9 * b9 + a10 * b8 + a11 * b7
+ s19 = a8 * b11 + a9 * b10 + a10 * b9 + a11 * b8
+ s20 = a9 * b11 + a10 * b10 + a11 * b9
+ s21 = a10 * b11 + a11 * b10
+ s22 = a11 * b11
+ s23 = 0
+
+ carry0 = (s0 + i64(1 << 20)) >> 21
+ s1 += carry0
+ s0 -= carry0 << 21
+ carry2 = (s2 + i64(1 << 20)) >> 21
+ s3 += carry2
+ s2 -= carry2 << 21
+ carry4 = (s4 + i64(1 << 20)) >> 21
+ s5 += carry4
+ s4 -= carry4 << 21
+ carry6 = (s6 + i64(1 << 20)) >> 21
+ s7 += carry6
+ s6 -= carry6 << 21
+ carry8 = (s8 + i64(1 << 20)) >> 21
+ s9 += carry8
+ s8 -= carry8 << 21
+ carry10 = (s10 + i64(1 << 20)) >> 21
+ s11 += carry10
+ s10 -= carry10 << 21
+ carry12 = (s12 + i64(1 << 20)) >> 21
+ s13 += carry12
+ s12 -= carry12 << 21
+ carry14 = (s14 + i64(1 << 20)) >> 21
+ s15 += carry14
+ s14 -= carry14 << 21
+ carry16 = (s16 + i64(1 << 20)) >> 21
+ s17 += carry16
+ s16 -= carry16 << 21
+ carry18 = (s18 + i64(1 << 20)) >> 21
+ s19 += carry18
+ s18 -= carry18 << 21
+ carry20 = (s20 + i64(1 << 20)) >> 21
+ s21 += carry20
+ s20 -= carry20 << 21
+ carry22 = (s22 + i64(1 << 20)) >> 21
+ s23 += carry22
+ s22 -= carry22 << 21
+
+ carry1 = (s1 + i64(1 << 20)) >> 21
+ s2 += carry1
+ s1 -= carry1 << 21
+ carry3 = (s3 + i64(1 << 20)) >> 21
+ s4 += carry3
+ s3 -= carry3 << 21
+ carry5 = (s5 + i64(1 << 20)) >> 21
+ s6 += carry5
+ s5 -= carry5 << 21
+ carry7 = (s7 + i64(1 << 20)) >> 21
+ s8 += carry7
+ s7 -= carry7 << 21
+ carry9 = (s9 + i64(1 << 20)) >> 21
+ s10 += carry9
+ s9 -= carry9 << 21
+ carry11 = (s11 + i64(1 << 20)) >> 21
+ s12 += carry11
+ s11 -= carry11 << 21
+ carry13 = (s13 + i64(1 << 20)) >> 21
+ s14 += carry13
+ s13 -= carry13 << 21
+ carry15 = (s15 + i64(1 << 20)) >> 21
+ s16 += carry15
+ s15 -= carry15 << 21
+ carry17 = (s17 + i64(1 << 20)) >> 21
+ s18 += carry17
+ s17 -= carry17 << 21
+ carry19 = (s19 + i64(1 << 20)) >> 21
+ s20 += carry19
+ s19 -= carry19 << 21
+ carry21 = (s21 + i64(1 << 20)) >> 21
+ s22 += carry21
+ s21 -= carry21 << 21
+
+ s11 += s23 * 666643
+ s12 += s23 * 470296
+ s13 += s23 * 654183
+ s14 -= s23 * 997805
+ s15 += s23 * 136657
+ s16 -= s23 * 683901
+
+ s10 += s22 * 666643
+ s11 += s22 * 470296
+ s12 += s22 * 654183
+ s13 -= s22 * 997805
+ s14 += s22 * 136657
+ s15 -= s22 * 683901
+
+ s9 += s21 * 666643
+ s10 += s21 * 470296
+ s11 += s21 * 654183
+ s12 -= s21 * 997805
+ s13 += s21 * 136657
+ s14 -= s21 * 683901
+
+ s8 += s20 * 666643
+ s9 += s20 * 470296
+ s10 += s20 * 654183
+ s11 -= s20 * 997805
+ s12 += s20 * 136657
+ s13 -= s20 * 683901
+
+ s7 += s19 * 666643
+ s8 += s19 * 470296
+ s9 += s19 * 654183
+ s10 -= s19 * 997805
+ s11 += s19 * 136657
+ s12 -= s19 * 683901
+
+ s6 += s18 * 666643
+ s7 += s18 * 470296
+ s8 += s18 * 654183
+ s9 -= s18 * 997805
+ s10 += s18 * 136657
+ s11 -= s18 * 683901
+
+ carry6 = (s6 + i64(1 << 20)) >> 21
+ s7 += carry6
+ s6 -= carry6 << 21
+ carry8 = (s8 + i64(1 << 20)) >> 21
+ s9 += carry8
+ s8 -= carry8 << 21
+ carry10 = (s10 + i64(1 << 20)) >> 21
+ s11 += carry10
+ s10 -= carry10 << 21
+ carry12 = (s12 + i64(1 << 20)) >> 21
+ s13 += carry12
+ s12 -= carry12 << 21
+ carry14 = (s14 + i64(1 << 20)) >> 21
+ s15 += carry14
+ s14 -= carry14 << 21
+ carry16 = (s16 + i64(1 << 20)) >> 21
+ s17 += carry16
+ s16 -= carry16 << 21
+
+ carry7 = (s7 + i64(1 << 20)) >> 21
+ s8 += carry7
+ s7 -= carry7 << 21
+ carry9 = (s9 + i64(1 << 20)) >> 21
+ s10 += carry9
+ s9 -= carry9 << 21
+ carry11 = (s11 + i64(1 << 20)) >> 21
+ s12 += carry11
+ s11 -= carry11 << 21
+ carry13 = (s13 + i64(1 << 20)) >> 21
+ s14 += carry13
+ s13 -= carry13 << 21
+ carry15 = (s15 + i64(1 << 20)) >> 21
+ s16 += carry15
+ s15 -= carry15 << 21
+
+ s5 += s17 * 666643
+ s6 += s17 * 470296
+ s7 += s17 * 654183
+ s8 -= s17 * 997805
+ s9 += s17 * 136657
+ s10 -= s17 * 683901
+
+ s4 += s16 * 666643
+ s5 += s16 * 470296
+ s6 += s16 * 654183
+ s7 -= s16 * 997805
+ s8 += s16 * 136657
+ s9 -= s16 * 683901
+
+ s3 += s15 * 666643
+ s4 += s15 * 470296
+ s5 += s15 * 654183
+ s6 -= s15 * 997805
+ s7 += s15 * 136657
+ s8 -= s15 * 683901
+
+ s2 += s14 * 666643
+ s3 += s14 * 470296
+ s4 += s14 * 654183
+ s5 -= s14 * 997805
+ s6 += s14 * 136657
+ s7 -= s14 * 683901
+
+ s1 += s13 * 666643
+ s2 += s13 * 470296
+ s3 += s13 * 654183
+ s4 -= s13 * 997805
+ s5 += s13 * 136657
+ s6 -= s13 * 683901
+
+ s0 += s12 * 666643
+ s1 += s12 * 470296
+ s2 += s12 * 654183
+ s3 -= s12 * 997805
+ s4 += s12 * 136657
+ s5 -= s12 * 683901
+ s12 = 0
+
+ carry0 = (s0 + i64(1 << 20)) >> 21
+ s1 += carry0
+ s0 -= carry0 << 21
+ carry2 = (s2 + i64(1 << 20)) >> 21
+ s3 += carry2
+ s2 -= carry2 << 21
+ carry4 = (s4 + i64(1 << 20)) >> 21
+ s5 += carry4
+ s4 -= carry4 << 21
+ carry6 = (s6 + i64(1 << 20)) >> 21
+ s7 += carry6
+ s6 -= carry6 << 21
+ carry8 = (s8 + i64(1 << 20)) >> 21
+ s9 += carry8
+ s8 -= carry8 << 21
+ carry10 = (s10 + i64(1 << 20)) >> 21
+ s11 += carry10
+ s10 -= carry10 << 21
+
+ carry1 = (s1 + i64(1 << 20)) >> 21
+ s2 += carry1
+ s1 -= carry1 << 21
+ carry3 = (s3 + i64(1 << 20)) >> 21
+ s4 += carry3
+ s3 -= carry3 << 21
+ carry5 = (s5 + i64(1 << 20)) >> 21
+ s6 += carry5
+ s5 -= carry5 << 21
+ carry7 = (s7 + i64(1 << 20)) >> 21
+ s8 += carry7
+ s7 -= carry7 << 21
+ carry9 = (s9 + i64(1 << 20)) >> 21
+ s10 += carry9
+ s9 -= carry9 << 21
+ carry11 = (s11 + i64(1 << 20)) >> 21
+ s12 += carry11
+ s11 -= carry11 << 21
+
+ s0 += s12 * 666643
+ s1 += s12 * 470296
+ s2 += s12 * 654183
+ s3 -= s12 * 997805
+ s4 += s12 * 136657
+ s5 -= s12 * 683901
+ s12 = 0
+
+ carry0 = s0 >> 21
+ s1 += carry0
+ s0 -= carry0 << 21
+ carry1 = s1 >> 21
+ s2 += carry1
+ s1 -= carry1 << 21
+ carry2 = s2 >> 21
+ s3 += carry2
+ s2 -= carry2 << 21
+ carry3 = s3 >> 21
+ s4 += carry3
+ s3 -= carry3 << 21
+ carry4 = s4 >> 21
+ s5 += carry4
+ s4 -= carry4 << 21
+ carry5 = s5 >> 21
+ s6 += carry5
+ s5 -= carry5 << 21
+ carry6 = s6 >> 21
+ s7 += carry6
+ s6 -= carry6 << 21
+ carry7 = s7 >> 21
+ s8 += carry7
+ s7 -= carry7 << 21
+ carry8 = s8 >> 21
+ s9 += carry8
+ s8 -= carry8 << 21
+ carry9 = s9 >> 21
+ s10 += carry9
+ s9 -= carry9 << 21
+ carry10 = s10 >> 21
+ s11 += carry10
+ s10 -= carry10 << 21
+ carry11 = s11 >> 21
+ s12 += carry11
+ s11 -= carry11 << 21
+
+ s0 += s12 * 666643
+ s1 += s12 * 470296
+ s2 += s12 * 654183
+ s3 -= s12 * 997805
+ s4 += s12 * 136657
+ s5 -= s12 * 683901
+
+ carry0 = s0 >> 21
+ s1 += carry0
+ s0 -= carry0 << 21
+ carry1 = s1 >> 21
+ s2 += carry1
+ s1 -= carry1 << 21
+ carry2 = s2 >> 21
+ s3 += carry2
+ s2 -= carry2 << 21
+ carry3 = s3 >> 21
+ s4 += carry3
+ s3 -= carry3 << 21
+ carry4 = s4 >> 21
+ s5 += carry4
+ s4 -= carry4 << 21
+ carry5 = s5 >> 21
+ s6 += carry5
+ s5 -= carry5 << 21
+ carry6 = s6 >> 21
+ s7 += carry6
+ s6 -= carry6 << 21
+ carry7 = s7 >> 21
+ s8 += carry7
+ s7 -= carry7 << 21
+ carry8 = s8 >> 21
+ s9 += carry8
+ s8 -= carry8 << 21
+ carry9 = s9 >> 21
+ s10 += carry9
+ s9 -= carry9 << 21
+ carry10 = s10 >> 21
+ s11 += carry10
+ s10 -= carry10 << 21
+
+ s[0] = u8(s0 >> 0)
+ s[1] = u8(s0 >> 8)
+ s[2] = u8((s0 >> 16) | (s1 << 5))
+ s[3] = u8(s1 >> 3)
+ s[4] = u8(s1 >> 11)
+ s[5] = u8((s1 >> 19) | (s2 << 2))
+ s[6] = u8(s2 >> 6)
+ s[7] = u8((s2 >> 14) | (s3 << 7))
+ s[8] = u8(s3 >> 1)
+ s[9] = u8(s3 >> 9)
+ s[10] = u8((s3 >> 17) | (s4 << 4))
+ s[11] = u8(s4 >> 4)
+ s[12] = u8(s4 >> 12)
+ s[13] = u8((s4 >> 20) | (s5 << 1))
+ s[14] = u8(s5 >> 7)
+ s[15] = u8((s5 >> 15) | (s6 << 6))
+ s[16] = u8(s6 >> 2)
+ s[17] = u8(s6 >> 10)
+ s[18] = u8((s6 >> 18) | (s7 << 3))
+ s[19] = u8(s7 >> 5)
+ s[20] = u8(s7 >> 13)
+ s[21] = u8(s8 >> 0)
+ s[22] = u8(s8 >> 8)
+ s[23] = u8((s8 >> 16) | (s9 << 5))
+ s[24] = u8(s9 >> 3)
+ s[25] = u8(s9 >> 11)
+ s[26] = u8((s9 >> 19) | (s10 << 2))
+ s[27] = u8(s10 >> 6)
+ s[28] = u8((s10 >> 14) | (s11 << 7))
+ s[29] = u8(s11 >> 1)
+ s[30] = u8(s11 >> 9)
+ s[31] = u8(s11 >> 17)
+}
+
+/**
+ * Input:
+ *
+ * `256⁰s[0] + 256¹s[1] + ... + 256⁶³s[63] = s`
+ *
+ * Output:
+ *
+ * `256⁰s[0] + 256¹s[1] + ... + 256³¹s[31] = s mod L`
+ *
+ * Overwrites s in place.
+ */
+export function sc_reduce (s: StaticArray<u8>): void {
+ let s0: i64 = 2097151 & load_3(s, 0)
+ let s1: i64 = 2097151 & (load_4(s, 2) >> 5)
+ let s2: i64 = 2097151 & (load_3(s, 5) >> 2)
+ let s3: i64 = 2097151 & (load_4(s, 7) >> 7)
+ let s4: i64 = 2097151 & (load_4(s, 10) >> 4)
+ let s5: i64 = 2097151 & (load_3(s, 13) >> 1)
+ let s6: i64 = 2097151 & (load_4(s, 15) >> 6)
+ let s7: i64 = 2097151 & (load_3(s, 18) >> 3)
+ let s8: i64 = 2097151 & load_3(s, 21)
+ let s9: i64 = 2097151 & (load_4(s, 23) >> 5)
+ let s10: i64 = 2097151 & (load_3(s, 26) >> 2)
+ let s11: i64 = 2097151 & (load_4(s, 28) >> 7)
+ let s12: i64 = 2097151 & (load_4(s, 31) >> 4)
+ let s13: i64 = 2097151 & (load_3(s, 34) >> 1)
+ let s14: i64 = 2097151 & (load_4(s, 36) >> 6)
+ let s15: i64 = 2097151 & (load_3(s, 39) >> 3)
+ let s16: i64 = 2097151 & load_3(s, 42)
+ let s17: i64 = 2097151 & (load_4(s, 44) >> 5)
+ let s18: i64 = 2097151 & (load_3(s, 47) >> 2)
+ let s19: i64 = 2097151 & (load_4(s, 49) >> 7)
+ let s20: i64 = 2097151 & (load_4(s, 52) >> 4)
+ let s21: i64 = 2097151 & (load_3(s, 55) >> 1)
+ let s22: i64 = 2097151 & (load_4(s, 57) >> 6)
+ let s23: i64 = (load_4(s, 60) >> 3)
+
+ let carry0: i64
+ let carry1: i64
+ let carry2: i64
+ let carry3: i64
+ let carry4: i64
+ let carry5: i64
+ let carry6: i64
+ let carry7: i64
+ let carry8: i64
+ let carry9: i64
+ let carry10: i64
+ let carry11: i64
+ let carry12: i64
+ let carry13: i64
+ let carry14: i64
+ let carry15: i64
+ let carry16: i64
+
+ s11 += s23 * 666643
+ s12 += s23 * 470296
+ s13 += s23 * 654183
+ s14 -= s23 * 997805
+ s15 += s23 * 136657
+ s16 -= s23 * 683901
+
+ s10 += s22 * 666643
+ s11 += s22 * 470296
+ s12 += s22 * 654183
+ s13 -= s22 * 997805
+ s14 += s22 * 136657
+ s15 -= s22 * 683901
+
+ s9 += s21 * 666643
+ s10 += s21 * 470296
+ s11 += s21 * 654183
+ s12 -= s21 * 997805
+ s13 += s21 * 136657
+ s14 -= s21 * 683901
+
+ s8 += s20 * 666643
+ s9 += s20 * 470296
+ s10 += s20 * 654183
+ s11 -= s20 * 997805
+ s12 += s20 * 136657
+ s13 -= s20 * 683901
+
+ s7 += s19 * 666643
+ s8 += s19 * 470296
+ s9 += s19 * 654183
+ s10 -= s19 * 997805
+ s11 += s19 * 136657
+ s12 -= s19 * 683901
+
+ s6 += s18 * 666643
+ s7 += s18 * 470296
+ s8 += s18 * 654183
+ s9 -= s18 * 997805
+ s10 += s18 * 136657
+ s11 -= s18 * 683901
+
+ carry6 = (s6 + i64(1 << 20)) >> 21
+ s7 += carry6
+ s6 -= carry6 * u64(1 << 21)
+ carry8 = (s8 + i64(1 << 20)) >> 21
+ s9 += carry8
+ s8 -= carry8 * u64(1 << 21)
+ carry10 = (s10 + i64(1 << 20)) >> 21
+ s11 += carry10
+ s10 -= carry10 * u64(1 << 21)
+ carry12 = (s12 + i64(1 << 20)) >> 21
+ s13 += carry12
+ s12 -= carry12 * u64(1 << 21)
+ carry14 = (s14 + i64(1 << 20)) >> 21
+ s15 += carry14
+ s14 -= carry14 * u64(1 << 21)
+ carry16 = (s16 + i64(1 << 20)) >> 21
+ s17 += carry16
+ s16 -= carry16 * u64(1 << 21)
+
+ carry7 = (s7 + i64(1 << 20)) >> 21
+ s8 += carry7
+ s7 -= carry7 * u64(1 << 21)
+ carry9 = (s9 + i64(1 << 20)) >> 21
+ s10 += carry9
+ s9 -= carry9 * u64(1 << 21)
+ carry11 = (s11 + i64(1 << 20)) >> 21
+ s12 += carry11
+ s11 -= carry11 * u64(1 << 21)
+ carry13 = (s13 + i64(1 << 20)) >> 21
+ s14 += carry13
+ s13 -= carry13 * u64(1 << 21)
+ carry15 = (s15 + i64(1 << 20)) >> 21
+ s16 += carry15
+ s15 -= carry15 * u64(1 << 21)
+
+ s5 += s17 * 666643
+ s6 += s17 * 470296
+ s7 += s17 * 654183
+ s8 -= s17 * 997805
+ s9 += s17 * 136657
+ s10 -= s17 * 683901
+
+ s4 += s16 * 666643
+ s5 += s16 * 470296
+ s6 += s16 * 654183
+ s7 -= s16 * 997805
+ s8 += s16 * 136657
+ s9 -= s16 * 683901
+
+ s3 += s15 * 666643
+ s4 += s15 * 470296
+ s5 += s15 * 654183
+ s6 -= s15 * 997805
+ s7 += s15 * 136657
+ s8 -= s15 * 683901
+
+ s2 += s14 * 666643
+ s3 += s14 * 470296
+ s4 += s14 * 654183
+ s5 -= s14 * 997805
+ s6 += s14 * 136657
+ s7 -= s14 * 683901
+
+ s1 += s13 * 666643
+ s2 += s13 * 470296
+ s3 += s13 * 654183
+ s4 -= s13 * 997805
+ s5 += s13 * 136657
+ s6 -= s13 * 683901
+
+ s0 += s12 * 666643
+ s1 += s12 * 470296
+ s2 += s12 * 654183
+ s3 -= s12 * 997805
+ s4 += s12 * 136657
+ s5 -= s12 * 683901
+ s12 = 0
+
+ carry0 = (s0 + i64(1 << 20)) >> 21
+ s1 += carry0
+ s0 -= carry0 * u64(1 << 21)
+ carry2 = (s2 + i64(1 << 20)) >> 21
+ s3 += carry2
+ s2 -= carry2 * u64(1 << 21)
+ carry4 = (s4 + i64(1 << 20)) >> 21
+ s5 += carry4
+ s4 -= carry4 * u64(1 << 21)
+ carry6 = (s6 + i64(1 << 20)) >> 21
+ s7 += carry6
+ s6 -= carry6 * u64(1 << 21)
+ carry8 = (s8 + i64(1 << 20)) >> 21
+ s9 += carry8
+ s8 -= carry8 * u64(1 << 21)
+ carry10 = (s10 + i64(1 << 20)) >> 21
+ s11 += carry10
+ s10 -= carry10 * u64(1 << 21)
+
+ carry1 = (s1 + i64(1 << 20)) >> 21
+ s2 += carry1
+ s1 -= carry1 * u64(1 << 21)
+ carry3 = (s3 + i64(1 << 20)) >> 21
+ s4 += carry3
+ s3 -= carry3 * u64(1 << 21)
+ carry5 = (s5 + i64(1 << 20)) >> 21
+ s6 += carry5
+ s5 -= carry5 * u64(1 << 21)
+ carry7 = (s7 + i64(1 << 20)) >> 21
+ s8 += carry7
+ s7 -= carry7 * u64(1 << 21)
+ carry9 = (s9 + i64(1 << 20)) >> 21
+ s10 += carry9
+ s9 -= carry9 * u64(1 << 21)
+ carry11 = (s11 + i64(1 << 20)) >> 21
+ s12 += carry11
+ s11 -= carry11 * u64(1 << 21)
+
+ s0 += s12 * 666643
+ s1 += s12 * 470296
+ s2 += s12 * 654183
+ s3 -= s12 * 997805
+ s4 += s12 * 136657
+ s5 -= s12 * 683901
+ s12 = 0
+
+ carry0 = s0 >> 21
+ s1 += carry0
+ s0 -= carry0 * u64(1 << 21)
+ carry1 = s1 >> 21
+ s2 += carry1
+ s1 -= carry1 * u64(1 << 21)
+ carry2 = s2 >> 21
+ s3 += carry2
+ s2 -= carry2 * u64(1 << 21)
+ carry3 = s3 >> 21
+ s4 += carry3
+ s3 -= carry3 * u64(1 << 21)
+ carry4 = s4 >> 21
+ s5 += carry4
+ s4 -= carry4 * u64(1 << 21)
+ carry5 = s5 >> 21
+ s6 += carry5
+ s5 -= carry5 * u64(1 << 21)
+ carry6 = s6 >> 21
+ s7 += carry6
+ s6 -= carry6 * u64(1 << 21)
+ carry7 = s7 >> 21
+ s8 += carry7
+ s7 -= carry7 * u64(1 << 21)
+ carry8 = s8 >> 21
+ s9 += carry8
+ s8 -= carry8 * u64(1 << 21)
+ carry9 = s9 >> 21
+ s10 += carry9
+ s9 -= carry9 * u64(1 << 21)
+ carry10 = s10 >> 21
+ s11 += carry10
+ s10 -= carry10 * u64(1 << 21)
+ carry11 = s11 >> 21
+ s12 += carry11
+ s11 -= carry11 * u64(1 << 21)
+
+ s0 += s12 * 666643
+ s1 += s12 * 470296
+ s2 += s12 * 654183
+ s3 -= s12 * 997805
+ s4 += s12 * 136657
+ s5 -= s12 * 683901
+
+ carry0 = s0 >> 21
+ s1 += carry0
+ s0 -= carry0 * u64(1 << 21)
+ carry1 = s1 >> 21
+ s2 += carry1
+ s1 -= carry1 * u64(1 << 21)
+ carry2 = s2 >> 21
+ s3 += carry2
+ s2 -= carry2 * u64(1 << 21)
+ carry3 = s3 >> 21
+ s4 += carry3
+ s3 -= carry3 * u64(1 << 21)
+ carry4 = s4 >> 21
+ s5 += carry4
+ s4 -= carry4 * u64(1 << 21)
+ carry5 = s5 >> 21
+ s6 += carry5
+ s5 -= carry5 * u64(1 << 21)
+ carry6 = s6 >> 21
+ s7 += carry6
+ s6 -= carry6 * u64(1 << 21)
+ carry7 = s7 >> 21
+ s8 += carry7
+ s7 -= carry7 * u64(1 << 21)
+ carry8 = s8 >> 21
+ s9 += carry8
+ s8 -= carry8 * u64(1 << 21)
+ carry9 = s9 >> 21
+ s10 += carry9
+ s9 -= carry9 * u64(1 << 21)
+ carry10 = s10 >> 21
+ s11 += carry10
+ s10 -= carry10 * u64(1 << 21)
+
+ s[0] = u8(s0 >> 0)
+ s[1] = u8(s0 >> 8)
+ s[2] = u8((s0 >> 16) | (s1 * u64(1 << 5)))
+ s[3] = u8(s1 >> 3)
+ s[4] = u8(s1 >> 11)
+ s[5] = u8((s1 >> 19) | (s2 * u64(1 << 2)))
+ s[6] = u8(s2 >> 6)
+ s[7] = u8((s2 >> 14) | (s3 * u64(1 << 7)))
+ s[8] = u8(s3 >> 1)
+ s[9] = u8(s3 >> 9)
+ s[10] = u8((s3 >> 17) | (s4 * u64(1 << 4)))
+ s[11] = u8(s4 >> 4)
+ s[12] = u8(s4 >> 12)
+ s[13] = u8((s4 >> 20) | (s5 * u64(1 << 1)))
+ s[14] = u8(s5 >> 7)
+ s[15] = u8((s5 >> 15) | (s6 * u64(1 << 6)))
+ s[16] = u8(s6 >> 2)
+ s[17] = u8(s6 >> 10)
+ s[18] = u8((s6 >> 18) | (s7 * u64(1 << 3)))
+ s[19] = u8(s7 >> 5)
+ s[20] = u8(s7 >> 13)
+ s[21] = u8(s8 >> 0)
+ s[22] = u8(s8 >> 8)
+ s[23] = u8((s8 >> 16) | (s9 * u64(1 << 5)))
+ s[24] = u8(s9 >> 3)
+ s[25] = u8(s9 >> 11)
+ s[26] = u8((s9 >> 19) | (s10 * u64(1 << 2)))
+ s[27] = u8(s10 >> 6)
+ s[28] = u8((s10 >> 14) | (s11 * u64(1 << 7)))
+ s[29] = u8(s11 >> 1)
+ s[30] = u8(s11 >> 9)
+ s[31] = u8(s11 >> 17)
+}
--- /dev/null
+//! SPDX-FileCopyrightText: 2026 Chris Duncan <chris@codecow.com>
+//! SPDX-License-Identifier: GPL-3.0-or-later
+
+const optblocker_u8: u8 = 0
+
+//@ts-expect-error
+@inline
+export function equal (b: i8, c: i8): u8 {
+ const x: u8 = u8(b) ^ u8(c) /* 0: yes; 1..255: no */
+ let y: u32 = u32(x) /* 0: yes; 1..255: no */
+ y--
+ return u8(((y >> 29) ^ optblocker_u8) >> 2) /* 1: yes; 0: no */
+}
+
+//@ts-expect-error
+@inline
+export function load_3 (input: StaticArray<u8>, i: u8): u64 {
+ let result: u64 = u64(input[i])
+ result |= u64(input[i + 1]) << 8
+ result |= u64(input[i + 2]) << 16
+ return result
+}
+
+//@ts-expect-error
+@inline
+export function load_4 (input: StaticArray<u8>, i: u8): u64 {
+ let result: u64 = load_3(input, i)
+ result |= u64(input[i + 3]) << 24
+ return result
+}
+
+//@ts-expect-error
+@inline
+export function negative (b: i8): i8 {
+ const x: u8 = u8(b) /* 0..127: no 128..255: yes */
+ return ((x >> 5) ^ optblocker_u8) >> 2 /* 1: yes; 0: no */
+}
}
export async function test (api, size, runs, isDebug, isSelfCheck) {
+ const selectedApi = api
// Execute once on load to initialize worker and WASM
const h = random(), k = random(64)
await NanoNaCl.sign(h, k)
}
}
}
- const verify = (h, s, pk) => {
+ const verify = (s, h, pk) => {
switch (api) {
case 'NanoNaCl': {
- return NanoNaCl.verify(h, s, pk)
+ return NanoNaCl.verify(s, h, pk)
}
case 'NanocurrencyWeb': {
return NanocurrencyWeb.tools.verify(pk, s, h)
}
if (isSelfCheck) {
+ api = 'NanoNaCl'
document.getElementById('status').innerHTML = `RUNNING SELF-CHECK`
console.log(`%cNanoNaCl`, 'color:green', 'Checking validation against known values')
TEST_VECTORS ??= {
let result
// PASS
- result = await NanoNaCl.derive(TEST_VECTORS.privateKey)
+ result = await derive(TEST_VECTORS.privateKey)
console.log(result)
result = result.toUpperCase() === TEST_VECTORS.publicKey.toUpperCase()
console.log(`derive() output for good private key is ${result === true ? 'correct' : 'incorrect'}`)
expect.push(result)
- result = await NanoNaCl.sign(TEST_VECTORS.blockHash, TEST_VECTORS.privateKey + TEST_VECTORS.publicKey)
+ result = await sign(TEST_VECTORS.blockHash, TEST_VECTORS.privateKey, TEST_VECTORS.publicKey)
console.log(result)
result = result.toUpperCase() === TEST_VECTORS.signature.toUpperCase()
console.log(`sign() output for good block hash and secret key is ${result === true ? 'correct' : 'incorrect'}`)
expect.push(result)
- result = await NanoNaCl.verify(TEST_VECTORS.blockHash, TEST_VECTORS.signature, TEST_VECTORS.publicKey)
+ result = await verify(TEST_VECTORS.signature, TEST_VECTORS.blockHash, TEST_VECTORS.publicKey)
console.log(result)
result = result === true
console.log(`verify() output for good block hash, signature, and private key is ${result === true ? 'correct' : 'incorrect'}`)
expect.push(result)
// XFAIL
- result = await NanoNaCl.verify(TEST_VECTORS.blockHash, TEST_VECTORS.badSignature, TEST_VECTORS.publicKey)
+ result = await verify(TEST_VECTORS.badSignature, TEST_VECTORS.blockHash, TEST_VECTORS.publicKey)
console.log(result)
result = result === false
console.log(`verify() output for non-canonical signature is ${result === true ? 'correct' : 'incorrect'}`)
expect.push(result)
- result = await NanoNaCl.verify(zeroes, TEST_VECTORS.signature, TEST_VECTORS.publicKey)
+ result = await verify(TEST_VECTORS.signature, zeroes, TEST_VECTORS.publicKey)
console.log(result)
result = result === false
console.log(`verify() output for bad block hash is ${result === true ? 'correct' : 'incorrect'}`)
expect.push(result)
- result = await NanoNaCl.verify(TEST_VECTORS.blockHash, `${zeroes}${zeroes}`, TEST_VECTORS.publicKey)
+ result = await verify(`${zeroes}${zeroes}`, TEST_VECTORS.blockHash, TEST_VECTORS.publicKey)
console.log(result)
result = result === false
console.log(`verify() output for bad signature is ${result === true ? 'correct' : 'incorrect'}`)
expect.push(result)
- result = await NanoNaCl.verify(TEST_VECTORS.blockHash, TEST_VECTORS.signature, zeroes)
+ result = await verify(TEST_VECTORS.signature, TEST_VECTORS.blockHash, zeroes)
console.log(result)
result = result === false
console.log(`verify() output for bad public key is ${result === true ? 'correct' : 'incorrect'}`)
document.getElementById('output').innerHTML += `Error: ${err.message}<br/>`
console.error(err)
return
+ } finally {
+ api = selectedApi
}
}
start = performance.now()
publicKey = await derive(privateKey)
signature = await sign(blockHash, privateKey, publicKey)
- isValid = await verify(blockHash, signature, publicKey)
+ isValid = await verify(signature, blockHash, publicKey)
end = performance.now()
} catch (err) {
document.getElementById('output').innerHTML += `Error: ${err.message}<br/>`
const validation = document.getElementById('validation')
validation.innerText = '⏳'
if (signature.length === 128 && blockHash.length === 64 && publicKey.length === 64) {
- NanoNaCl.verify(blockHash, signature, publicKey)
+ NanoNaCl.verify(signature, blockHash, publicKey)
.then(result => {
validation.innerText = result
? '✔️'
let memory: WebAssembly.Memory
let derive: (k: Uint8Array) => Uint8Array<ArrayBuffer>
let sign: (h: Uint8Array, k: Uint8Array) => Uint8Array<ArrayBuffer>
- let verify: (h: Uint8Array, s: Uint8Array, k: Uint8Array) => boolean
+ let verify: (s: Uint8Array, h: Uint8Array, k: Uint8Array) => boolean
async function setup (): Promise<void> {
try {
return s
}
- verify = function (h: Uint8Array, s: Uint8Array, k: Uint8Array): boolean {
+ verify = function (s: Uint8Array, h: Uint8Array, k: Uint8Array): boolean {
// assembly/nano-nacl/verify() => bool
let buffer: DataView | undefined = new DataView(memory.buffer)
let inPtr = exports.getInputPointer()
- for (let i = 0; i < 32; i++) {
- buffer.setUint8(inPtr + i, h[i])
- }
- inPtr += 32
for (let i = 0; i < 64; i++) {
buffer.setUint8(inPtr + i, s[i])
}
inPtr += 64
+ for (let i = 0; i < 32; i++) {
+ buffer.setUint8(inPtr + i, h[i])
+ }
+ inPtr += 32
for (let i = 0; i < 32; i++) {
buffer.setUint8(inPtr + i, k[i])
}
result = signature
} else if (action === 'verify') {
const { blockHash, publicKey, signature } = data
- const blockHashBytes = hex2bytes('block hash', 32, blockHash)
const signatureBytes = hex2bytes('signature', 64, signature)
+ const blockHashBytes = hex2bytes('block hash', 32, blockHash)
const publicKeyBytes = hex2bytes('public key', 32, publicKey)
- const isVerified = verify(blockHashBytes, signatureBytes, publicKeyBytes)
+ const isVerified = verify(signatureBytes, blockHashBytes, publicKeyBytes)
if (isVerified == null) {
throw new TypeError('Invalid verification from WASM verify()')
}
/**
* Nano block signature verification using WebAssembly.
*/
-export async function verify (blockHash: string, signature: string, publicKey: string): Promise<boolean> {
- const result = await run({ action: 'verify', blockHash, signature, publicKey })
+export async function verify (signature: string, blockHash: string, publicKey: string): Promise<boolean> {
+ const result = await run({ action: 'verify', signature, blockHash, publicKey })
return result === '01'
}