From: Chris Duncan Date: Sat, 7 Mar 2026 07:37:59 +0000 (-0800) Subject: Refactor from tweetnacl 16x16 radix limbs to libsodium 10x25.5-bit radix limb impleme... X-Git-Tag: v1.0.0~10 X-Git-Url: https://git.codecow.com/?a=commitdiff_plain;h=882c6108c02cecea790a9dec40538a21b1596fb5;p=nano25519.git Refactor from tweetnacl 16x16 radix limbs to libsodium 10x25.5-bit radix limb implementation to gain massive speed boost with precomputed base point table. --- diff --git a/assembly/base.ts b/assembly/base.ts index 74079af..c9a7293 100644 --- a/assembly/base.ts +++ b/assembly/base.ts @@ -1350,3 +1350,46 @@ export const base = StaticArray.fromArray>([ } ]) ]) + +export const base2 = StaticArray.fromArray([ + { + 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([-8738181, 4489570, 9688441, -14785194, 10184609, -12363380, 29287919, 11864899, -24514362, -4438546]) + }, + { + yplusx: fe([15636291, -9688557, 24204773, -7912398, 616977, -16685262, 27787600, -14772189, 28944400, -1550024]), + yminusx: fe([16568933, 4717097, -11556148, -1102322, 15682896, -11807043, 16354577, -11775962, 7689662, 11199574]), + xy2d: fe([30464156, -5976125, -11779434, -15670865, 23220365, 15915852, 7512774, 10017326, -17749093, -9920357]) + }, + { + yplusx: fe([10861363, 11473154, 27284546, 1981175, -30064349, 12577861, 32867885, 14515107, -15438304, 10819380]), + yminusx: fe([4708026, 6336745, 20377586, 9066809, -11272109, 6594696, -25653668, 12483688, -12668491, 5581306]), + xy2d: fe([19563160, 16186464, -29386857, 4097519, 10237984, -4348115, 28542350, 13850243, -23678021, -15815942]) + }, + { + yplusx: fe([5153746, 9909285, 1723747, -2777874, 30523605, 5516873, 19480852, 5230134, -23952439, -15175766]), + yminusx: fe([-30269007, -3463509, 7665486, 10083793, 28475525, 1649722, 20654025, 16520125, 30598449, 7715701]), + xy2d: fe([28881845, 14381568, 9657904, 3680757, -20181635, 7843316, -31400660, 1370708, 29794553, -1409300]) + }, + { + yplusx: fe([-22518993, -6692182, 14201702, -8745502, -23510406, 8844726, 18474211, -1361450, -13062696, 13821877]), + yminusx: fe([-6455177, -7839871, 3374702, -4740862, -27098617, -10571707, 31655028, -7212327, 18853322, -14220951]), + xy2d: fe([4566830, -12963868, -28974889, -12240689, -7602672, -2830569, -8514358, -10431137, 2207753, -3209784]) + }, + { + yplusx: fe([-25154831, -4185821, 29681144, 7868801, -6854661, -9423865, -12437364, -663000, -31111463, -16132436]), + yminusx: fe([25576264, -2703214, 7349804, -11814844, 16472782, 9300885, 3844789, 15725684, 171356, 6466918]), + xy2d: fe([23103977, 13316479, 9739013, -16149481, 817875, -15038942, 8965339, -14088058, -30714912, 16193877]) + }, + { + yplusx: fe([-33521811, 3180713, -2394130, 14003687, -16903474, -16270840, 17238398, 4729455, -18074513, 9256800]), + yminusx: fe([-25182317, -4174131, 32336398, 5036987, -21236817, 11360617, 22616405, 9761698, -19827198, 630305]), + xy2d: fe([-13720693, 2639453, -24237460, -7406481, 9494427, -5774029, -6554551, -15960994, -2449256, -14291300]) + }, + { + yplusx: fe([-3151181, -5046075, 9282714, 6866145, -31907062, -863023, -18940575, 15033784, 25105118, -7894876]), + yminusx: fe([-24326370, 15950226, -31801215, -14592823, -11662737, -5090925, 1573892, -2625887, 2198790, -15804619]), + xy2d: fe([-3099351, 10324967, -2241613, 7453183, -5446979, -2735503, -13812022, -16236442, -32461234, -12290683]) + } +]) diff --git a/assembly/fe.ts b/assembly/fe.ts index dad60b7..f2170b0 100644 --- a/assembly/fe.ts +++ b/assembly/fe.ts @@ -10,18 +10,17 @@ * * `p = 2²⁵⁵-19` * - * They are represented as 16 limbs of 16-bit values so that + * They are represented as 10 limbs of alternating 25- and 26-bit values so that * - * `n = 256⁰a[0] + 256¹a[1] + ... + 256³¹a[31]` + * `n = 2⁰a[0] + 2²⁶a[1] + 2⁷⁷a[2] ... + 2²³⁰a[9]` */ - -import { load_3, load_4 } from "./utils" +import { load_3, load_4, sodium_is_zero } from "./utils" /** - * 16-wide x 16-bit array of values representing some large integer `n mod p`. + * 10x 25- and 26-bit array of values representing a large integer `n mod p`. */ export type FieldElement = StaticArray -export function fe (source: Array = new Array(16)): FieldElement { +export function fe (source: Array = new Array(10)): FieldElement { return StaticArray.fromArray(source) } @@ -33,7 +32,7 @@ export function fe (source: Array = new Array(16)): FieldElement { //@ts-expect-error @inline export function fe_0 (h: FieldElement): void { - memory.fill(changetype(h), 0, 64) + memory.fill(changetype(h), 0, 40) } /** @@ -70,13 +69,9 @@ export function fe_add (h: FieldElement, f: FieldElement, g: FieldElement): void bi = v128.load(b, 16) v128.store(o, v128.add(ai, bi), 16) - ai = v128.load(a, 32) - bi = v128.load(b, 32) - v128.store(o, v128.add(ai, bi), 32) - - ai = v128.load(a, 48) - bi = v128.load(b, 48) - v128.store(o, v128.add(ai, bi), 48) + ai = i64x2(load(a + 32), 0) + bi = i64x2(load(b + 32), 0) + v128.store_lane(o, v128.add(ai, bi), 0, 32) } /** @@ -102,13 +97,9 @@ export function fe_cmov (f: FieldElement, g: FieldElement, b: u64): void { qi = v128.load(q, 16) v128.store(p, v128.bitselect(qi, pi, c), 16) - pi = v128.load(p, 32) - qi = v128.load(q, 32) - v128.store(p, v128.bitselect(qi, pi, c), 32) - - pi = v128.load(p, 48) - qi = v128.load(q, 48) - v128.store(p, v128.bitselect(qi, pi, c), 48) + pi = i64x2(load(p + 32), 0) + qi = i64x2(load(q + 32), 0) + v128.store_lane(p, v128.bitselect(qi, pi, c), 0, 32) } /** @@ -120,7 +111,7 @@ export function fe_cmov (f: FieldElement, g: FieldElement, b: u64): void { //@ts-expect-error @inline export function fe_copy (h: FieldElement, f: FieldElement): void { - memory.copy(changetype(h), changetype(f), 64) + memory.copy(changetype(h), changetype(f), 40) } /** @@ -148,15 +139,32 @@ export function fe_cswap (f: FieldElement, g: FieldElement, b: u64): void { v128.store(p, v128.bitselect(qi, pi, c), 16) v128.store(q, v128.bitselect(pi, qi, c), 16) - pi = v128.load(p, 32) - qi = v128.load(q, 32) - v128.store(p, v128.bitselect(qi, pi, c), 32) - v128.store(q, v128.bitselect(pi, qi, c), 32) + pi = i64x2(load(p + 32), 0) + qi = i64x2(load(q + 32), 0) + v128.store_lane(p, v128.bitselect(qi, pi, c), 0, 32) + v128.store_lane(q, v128.bitselect(pi, qi, c), 0, 32) +} + +/** + * Add a FieldElement to itself and store the sum. + * + * @param h FieldElement sum destination + * @param f FieldElement summand source + */ +//@ts-expect-error +@inline +export function fe_dbl (h: FieldElement, f: FieldElement): void { + const o = changetype(h) + const a = changetype(f) + + let ai = v128.load(a) + v128.store(o, v128.add(ai, ai)) + + ai = v128.load(a, 16) + v128.store(o, v128.add(ai, ai), 16) - pi = v128.load(p, 48) - qi = v128.load(q, 48) - v128.store(p, v128.bitselect(qi, pi, c), 48) - v128.store(q, v128.bitselect(pi, qi, c), 48) + ai = i64x2(load(a + 32), 0) + v128.store_lane(o, v128.add(ai, ai), 0, 32) } /** @@ -187,35 +195,35 @@ export function fe_frombytes (h: FieldElement, s: StaticArray): void { carry9 = (h9 + i64(1 << 24)) >> 25 h0 += carry9 * 19 - h9 -= carry9 << 25 + h9 -= carry9 * u64(1 << 25) carry1 = (h1 + i64(1 << 24)) >> 25 h2 += carry1 - h1 -= carry1 << 25 + h1 -= carry1 * u64(1 << 25) carry3 = (h3 + i64(1 << 24)) >> 25 h4 += carry3 - h3 -= carry3 << 25 + h3 -= carry3 * u64(1 << 25) carry5 = (h5 + i64(1 << 24)) >> 25 h6 += carry5 - h5 -= carry5 << 25 + h5 -= carry5 * u64(1 << 25) carry7 = (h7 + i64(1 << 24)) >> 25 h8 += carry7 - h7 -= carry7 << 25 + h7 -= carry7 * u64(1 << 25) carry0 = (h0 + i64(1 << 25)) >> 26 h1 += carry0 - h0 -= carry0 << 26 + h0 -= carry0 * u64(1 << 26) carry2 = (h2 + i64(1 << 25)) >> 26 h3 += carry2 - h2 -= carry2 << 26 + h2 -= carry2 * u64(1 << 26) carry4 = (h4 + i64(1 << 25)) >> 26 h5 += carry4 - h4 -= carry4 << 26 + h4 -= carry4 * u64(1 << 26) carry6 = (h6 + i64(1 << 25)) >> 26 h7 += carry6 - h6 -= carry6 << 26 + h6 -= carry6 * u64(1 << 26) carry8 = (h8 + i64(1 << 25)) >> 26 h9 += carry8 - h8 -= carry8 << 26 + h8 -= carry8 * u64(1 << 26) h[0] = i32(h0) h[1] = i32(h1) @@ -229,13 +237,13 @@ export function fe_frombytes (h: FieldElement, s: StaticArray): void { 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() +/** + * Inversion - sets out to 0 if z=0 + */ export function fe_invert (out: FieldElement, z: FieldElement): void { fe_sq(t0, z) fe_sq(t1, t0) @@ -283,14 +291,14 @@ export function fe_invert (out: FieldElement, z: FieldElement): void { 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(32) +/** + * 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. + */ //@ts-expect-error @inline export function fe_isnegative (f: FieldElement): u8 { @@ -299,7 +307,23 @@ export function fe_isnegative (f: FieldElement): u8 { return s[0] & 1 } -const multiply_t = new StaticArray(32) +const fe_iszero_s = new StaticArray(32) +/** + * return 1 if f == 0 + * return 0 if f != 0 + * + * Preconditions: + * |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. + */ +//@ts-expect-error +@inline +export function fe_iszero (f: FieldElement): u8 { + const s = fe_iszero_s + fe_tobytes(s, f) + return sodium_is_zero(s, 32) +} + +const multiply_t = new StaticArray(24) /** * Multiply two FieldElements and store the product. * @@ -307,27 +331,27 @@ const multiply_t = new StaticArray(32) * @param f FieldElement multiplicand source * @param g FieldElement multiplicand source */ -//@ts-expect-error -@inline export function fe_mul (h: FieldElement, f: FieldElement, g: FieldElement): void { const o = changetype(h) const a = changetype(f) const b = changetype(g) const t = changetype(multiply_t) - memory.fill(t, 0, 256) + memory.fill(t, 0, 160) - const b0 = v128.load(b) - const b4 = v128.load(b + 16) - const b8 = v128.load(b + 32) - const b12 = v128.load(b + 48) + const b0: v128 = v128.load(b) + const b4: v128 = v128.load(b + 16) + const b8: v128 = i32x4(load(b + 32), load(b + 36), 0, 0) - for (let i = 0; i < 16; i++) { + for (let i = 0; i < 10; i++) { + const odd = (i & 1) + 1 const ai = v128.splat(load(a + (i << 2))) let ptr = t + (usize(i) << 3) let pLo = i64x2.extmul_low_i32x4_s(ai, b0) let pHi = i64x2.extmul_high_i32x4_s(ai, b0) + pLo = v128.mul(pLo, i64x2(1, odd)) + pHi = v128.mul(pHi, i64x2(1, odd)) let tLo = v128.load(ptr) let tHi = v128.load(ptr + 16) tLo = i64x2.add(tLo, pLo) @@ -338,6 +362,8 @@ export function fe_mul (h: FieldElement, f: FieldElement, g: FieldElement): void ptr += 32 pLo = i64x2.extmul_low_i32x4_s(ai, b4) pHi = i64x2.extmul_high_i32x4_s(ai, b4) + pLo = v128.mul(pLo, i64x2(1, odd)) + pHi = v128.mul(pHi, i64x2(1, odd)) tLo = v128.load(ptr) tHi = v128.load(ptr + 16) tLo = i64x2.add(tLo, pLo) @@ -348,16 +374,8 @@ export function fe_mul (h: FieldElement, f: FieldElement, g: FieldElement): void ptr += 32 pLo = i64x2.extmul_low_i32x4_s(ai, b8) pHi = i64x2.extmul_high_i32x4_s(ai, b8) - tLo = v128.load(ptr) - tHi = v128.load(ptr + 16) - tLo = i64x2.add(tLo, pLo) - tHi = i64x2.add(tHi, pHi) - v128.store(ptr, tLo) - v128.store(ptr + 16, tHi) - - ptr += 32 - pLo = i64x2.extmul_low_i32x4_s(ai, b12) - pHi = i64x2.extmul_high_i32x4_s(ai, b12) + pLo = v128.mul(pLo, i64x2(1, odd)) + pHi = v128.mul(pHi, i64x2(1, odd)) tLo = v128.load(ptr) tHi = v128.load(ptr + 16) tLo = i64x2.add(tLo, pLo) @@ -365,6 +383,8 @@ export function fe_mul (h: FieldElement, f: FieldElement, g: FieldElement): void v128.store(ptr, tLo) v128.store(ptr + 16, tHi) } + // discard last 4 elements since we do not actually need them + memory.fill(t + 160, 0, 32) normalize(o, t) } @@ -386,37 +406,91 @@ export function fe_neg (h: FieldElement, f: FieldElement): void { ai = v128.load(a, 16) v128.store(o, v128.neg(ai), 16) - ai = v128.load(a, 32) - v128.store(o, v128.neg(ai), 32) - - ai = v128.load(a, 48) - v128.store(o, v128.neg(ai), 48) + ai = i64x2(load(a + 32), 0) + v128.store_lane(o, v128.neg(ai), 0, 32) } -/* - 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> 25 + q = (19 * h9 + (u32(1) << 24)) >> 25 q = (h0 + q) >> 26 q = (h1 + q) >> 25 q = (h2 + q) >> 26 @@ -449,33 +523,33 @@ export function fe_reduce (h: FieldElement, f: FieldElement): void { carry0 = h0 >> 26 h1 += carry0 - h0 -= carry0 << 26 + h0 -= carry0 * u32(1 << 26) carry1 = h1 >> 25 h2 += carry1 - h1 -= carry1 << 25 + h1 -= carry1 * u32(1 << 25) carry2 = h2 >> 26 h3 += carry2 - h2 -= carry2 << 26 + h2 -= carry2 * u32(1 << 26) carry3 = h3 >> 25 h4 += carry3 - h3 -= carry3 << 25 + h3 -= carry3 * u32(1 << 25) carry4 = h4 >> 26 h5 += carry4 - h4 -= carry4 << 26 + h4 -= carry4 * u32(1 << 26) carry5 = h5 >> 25 h6 += carry5 - h5 -= carry5 << 25 + h5 -= carry5 * u32(1 << 25) carry6 = h6 >> 26 h7 += carry6 - h6 -= carry6 << 26 + h6 -= carry6 * u32(1 << 26) carry7 = h7 >> 25 h8 += carry7 - h7 -= carry7 << 25 + h7 -= carry7 * u32(1 << 25) carry8 = h8 >> 26 h9 += carry8 - h8 -= carry8 << 26 + h8 -= carry8 * u32(1 << 26) carry9 = h9 >> 25 - h9 -= carry9 << 25 + h9 -= carry9 * u32(1 << 25) h[0] = h0 h[1] = h1 @@ -536,15 +610,12 @@ export function fe_sub (h: FieldElement, f: FieldElement, g: FieldElement): void bi = v128.load(b, 16) v128.store(o, v128.sub(ai, bi), 16) - ai = v128.load(a, 32) - bi = v128.load(b, 32) - v128.store(o, v128.sub(ai, bi), 32) - - ai = v128.load(a, 48) - bi = v128.load(b, 48) - v128.store(o, v128.sub(ai, bi), 48) + ai = i64x2(load(a + 32), 0) + bi = i64x2(load(b + 32), 0) + v128.store_lane(o, v128.sub(ai, bi), 0, 32) } +const fe_tobytes_t: FieldElement = fe() /** * Goal: Output h0+...+2²⁵⁵ h10-2²⁵⁵ q, which is between 0 and 2²⁵⁵-20. * Have h0+...+2²³⁰ h9 between 0 and 2²⁵⁵-1; @@ -552,38 +623,38 @@ export function fe_sub (h: FieldElement, f: FieldElement, g: FieldElement): void * * Goal: Output h0+...+2²³⁰ h9. */ -const t: FieldElement = fe() export function fe_tobytes (s: StaticArray, h: FieldElement): void { + const t = fe_tobytes_t 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[3] = u8((t[0] >> 24) | (t[1] * (u32(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[6] = u8((t[1] >> 22) | (t[2] * (u32(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[9] = u8((t[2] >> 21) | (t[3] * (u32(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[12] = u8((t[3] >> 19) | (t[4] * (u32(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[19] = u8((t[5] >> 24) | (t[6] * (u32(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[22] = u8((t[6] >> 23) | (t[7] * (u32(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[25] = u8((t[7] >> 21) | (t[8] * (u32(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[28] = u8((t[8] >> 20) | (t[9] * (u32(1) << 6))) s[29] = u8(t[9] >> 2) s[30] = u8(t[9] >> 10) s[31] = u8(t[9] >> 18) @@ -612,198 +683,126 @@ export function fe_tobytes (s: StaticArray, h: FieldElement): void { //@ts-expect-error @inline function normalize (o: usize, t: usize): void { - // reduce + // reduce from 20 limbs to 10 let x = load(t) - let y = load(t, 128) - store(t, x + (38 * y)) + let y = load(t, 80) + store(t, x + (19 * y)) x = load(t, 8) - y = load(t, 136) - store(t, x + (38 * y), 8) + y = load(t, 88) + store(t, x + (19 * y), 8) x = load(t, 16) - y = load(t, 144) - store(t, x + (38 * y), 16) + y = load(t, 96) + store(t, x + (19 * y), 16) x = load(t, 24) - y = load(t, 152) - store(t, x + (38 * y), 24) + y = load(t, 104) + store(t, x + (19 * y), 24) x = load(t, 32) - y = load(t, 160) - store(t, x + (38 * y), 32) + y = load(t, 112) + store(t, x + (19 * y), 32) x = load(t, 40) - y = load(t, 168) - store(t, x + (38 * y), 40) + y = load(t, 120) + store(t, x + (19 * y), 40) x = load(t, 48) - y = load(t, 176) - store(t, x + (38 * y), 48) + y = load(t, 128) + store(t, x + (19 * y), 48) x = load(t, 56) - y = load(t, 184) - store(t, x + (38 * y), 56) + y = load(t, 136) + store(t, x + (19 * y), 56) x = load(t, 64) - y = load(t, 192) - store(t, x + (38 * y), 64) + y = load(t, 144) + store(t, x + (19 * y), 64) x = load(t, 72) - y = load(t, 200) - store(t, x + (38 * y), 72) - - x = load(t, 80) - y = load(t, 208) - store(t, x + (38 * y), 80) - - x = load(t, 88) - y = load(t, 216) - store(t, x + (38 * y), 88) - - x = load(t, 96) - y = load(t, 224) - store(t, x + (38 * y), 96) - - x = load(t, 104) - y = load(t, 232) - store(t, x + (38 * y), 104) - - x = load(t, 112) - y = load(t, 240) - store(t, x + (38 * y), 112) + y = load(t, 152) + store(t, x + (19 * y), 72) // first carry - let c: i64 = load(t) - store(t, c & 0xFFFF) - c >>= 16 - - c += load(t, 8) - store(t, c & 0xFFFF, 8) - c >>= 16 - - c += load(t, 16) - store(t, c & 0xFFFF, 16) - c >>= 16 - - c += load(t, 24) - store(t, c & 0xFFFF, 24) - c >>= 16 - - c += load(t, 32) - store(t, c & 0xFFFF, 32) - c >>= 16 - - c += load(t, 40) - store(t, c & 0xFFFF, 40) - c >>= 16 - - c += load(t, 48) - store(t, c & 0xFFFF, 48) - c >>= 16 - - c += load(t, 56) - store(t, c & 0xFFFF, 56) - c >>= 16 - - c += load(t, 64) - store(t, c & 0xFFFF, 64) - c >>= 16 - - c += load(t, 72) - store(t, c & 0xFFFF, 72) - c >>= 16 - - c += load(t, 80) - store(t, c & 0xFFFF, 80) - c >>= 16 - - c += load(t, 88) - store(t, c & 0xFFFF, 88) - c >>= 16 - - c += load(t, 96) - store(t, c & 0xFFFF, 96) - c >>= 16 - - c += load(t, 104) - store(t, c & 0xFFFF, 104) - c >>= 16 - - c += load(t, 112) - store(t, c & 0xFFFF, 112) - c >>= 16 - - c += load(t, 120) - store(t, c & 0xFFFF, 120) - c >>= 16 - - store(t, load(t) + (38 * c)) - - // second carry and assign result to output - c = load(t) - store(o, c & 0xFFFF) - c >>= 16 - - c += load(t, 8) - store(o, c & 0xFFFF, 4) - c >>= 16 - - c += load(t, 16) - store(o, c & 0xFFFF, 8) - c >>= 16 - - c += load(t, 24) - store(o, c & 0xFFFF, 12) - c >>= 16 - - c += load(t, 32) - store(o, c & 0xFFFF, 16) - c >>= 16 - - c += load(t, 40) - store(o, c & 0xFFFF, 20) - c >>= 16 - - c += load(t, 48) - store(o, c & 0xFFFF, 24) - c >>= 16 - - c += load(t, 56) - store(o, c & 0xFFFF, 28) - c >>= 16 - - c += load(t, 64) - store(o, c & 0xFFFF, 32) - c >>= 16 - - c += load(t, 72) - store(o, c & 0xFFFF, 36) - c >>= 16 - - c += load(t, 80) - store(o, c & 0xFFFF, 40) - c >>= 16 - - c += load(t, 88) - store(o, c & 0xFFFF, 44) - c >>= 16 - - c += load(t, 96) - store(o, c & 0xFFFF, 48) - c >>= 16 - - c += load(t, 104) - store(o, c & 0xFFFF, 52) - c >>= 16 - - c += load(t, 112) - store(o, c & 0xFFFF, 56) - c >>= 16 - - c += load(t, 120) - store(o, c & 0xFFFF, 60) - c >>= 16 - - store(o, load(o) + (38 * c)) + let v: i64 = load(t, 0) + let c: i64 = (v + (1 << 25)) >> 26 + store(t, v - (c << 26), 0) + + v = load(t, 8) + c + c = (v + (1 << 24)) >> 25 + store(t, v - (c << 25), 8) + + v = load(t, 16) + c + c = (v + (1 << 25)) >> 26 + store(t, v - (c << 26), 16) + + v = load(t, 24) + c + c = (v + (1 << 24)) >> 25 + store(t, v - (c << 25), 24) + + v = load(t, 32) + c + c = (v + (1 << 25)) >> 26 + store(t, v - (c << 26), 32) + + v = load(t, 40) + c + c = (v + (1 << 24)) >> 25 + store(t, v - (c << 25), 40) + + v = load(t, 48) + c + c = (v + (1 << 25)) >> 26 + store(t, v - (c << 26), 48) + + v = load(t, 56) + c + c = (v + (1 << 24)) >> 25 + store(t, v - (c << 25), 56) + + v = load(t, 64) + c + c = (v + (1 << 25)) >> 26 + store(t, v - (c << 26), 64) + + v = load(t, 72) + c + c = (v + (1 << 24)) >> 25 + store(t, v - (c << 25), 72) + + // fold final carry back to start then do second carry and assign to output + v = load(t, 0) + (19 * c) + c = (v + (1 << 25)) >> 26 + store(o, v - (c << 26), 0) + + v = load(t, 8) + c + c = (v + (1 << 24)) >> 25 + store(o, v - (c << 25), 4) + + v = load(t, 16) + c + c = (v + (1 << 25)) >> 26 + store(o, v - (c << 26), 8) + + v = load(t, 24) + c + c = (v + (1 << 24)) >> 25 + store(o, v - (c << 25), 12) + + v = load(t, 32) + c + c = (v + (1 << 25)) >> 26 + store(o, v - (c << 26), 16) + + v = load(t, 40) + c + c = (v + (1 << 24)) >> 25 + store(o, v - (c << 25), 20) + + v = load(t, 48) + c + c = (v + (1 << 25)) >> 26 + store(o, v - (c << 26), 24) + + v = load(t, 56) + c + c = (v + (1 << 24)) >> 25 + store(o, v - (c << 25), 28) + + v = load(t, 64) + c + c = (v + (1 << 25)) >> 26 + store(o, v - (c << 26), 32) + + v = load(t, 72) + c + c = (v + (1 << 24)) >> 25 + store(o, v - (c << 25), 36) } diff --git a/assembly/ge.ts b/assembly/ge.ts index a3dfefa..386a7c9 100644 --- a/assembly/ge.ts +++ b/assembly/ge.ts @@ -1,11 +1,6 @@ //! SPDX-FileCopyrightText: 2026 Chris Duncan //! SPDX-License-Identifier: GPL-3.0-or-later -import { base } from './base' -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. * @@ -21,6 +16,10 @@ import { equal, negative } from './utils' * - ge_p1p1 (completed): ((X:Z),(Y:T)) satisfying x=X/Z, y=Y/T * - ge_precomp (Duif): (y+x,y-x,2dxy) */ +import { base, base2 } from './base' +import { ed25519_d, ed25519_d2, fe25519_sqrtm1 } from './constants' +import { fe, fe_0, fe_1, fe_add, fe_cmov, fe_copy, fe_frombytes, fe_invert, fe_isnegative, fe_iszero, fe_mul, fe_neg, fe_pow22523, fe_sq, fe_sq2, fe_sub, fe_tobytes, FieldElement } from './fe' +import { equal, negative, optblocker_u8 } from './utils' export class ge_p2 { X: FieldElement = fe() @@ -51,6 +50,21 @@ export class ge_cached { T2d: FieldElement = fe() } +//@ts-expect-error +@inline +function ge_cached_8 (): StaticArray { + return StaticArray.fromArray([ + new ge_cached(), + new ge_cached(), + new ge_cached(), + new ge_cached(), + new ge_cached(), + new ge_cached(), + new ge_cached(), + new ge_cached() + ]) +} + //@ts-expect-error @inline export function ge_p2_0 (h: ge_p2): void { @@ -69,19 +83,18 @@ export function ge_p3_0 (h: ge_p3): void { //@ts-expect-error @inline export function ge_precomp_0 (h: ge_precomp): void { - fe_0(h.yplusx) - fe_0(h.yminusx) + fe_1(h.yplusx) + fe_1(h.yminusx) fe_0(h.xy2d) } - -function ge_cmov (t: ge_precomp, u: ge_precomp, b: i8): void { +function ge_cmov (t: ge_precomp, u: ge_precomp, b: u8): void { fe_cmov(t.yplusx, u.yplusx, b) fe_cmov(t.yminusx, u.yminusx, b) fe_cmov(t.xy2d, u.xy2d, b) } -const ge25519_cmov8_minust = new ge_precomp() +const ge25519_cmov8_minust: ge_precomp = new ge_precomp() function ge_cmov8 (t: ge_precomp, precomp: StaticArray, b: i8): void { const minust = ge25519_cmov8_minust const bnegative: u8 = negative(b) @@ -107,10 +120,10 @@ function ge_cmov8_base (t: ge_precomp, pos: i32, b: i8): void { ge_cmov8(t, base[pos], b) } +const ge_add_precomp_t0: FieldElement = fe() /** * r = p + q */ -const ge_add_precomp_t0: FieldElement = fe() 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) @@ -152,10 +165,10 @@ function ge_p2_dbl (r: ge_p1p1, p: ge_p2): void { fe_sub(r.T, r.T, r.Z) } +const ge_p3_dbl_q: ge_p2 = new ge_p2() /** * r = 2 * p */ -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) @@ -181,10 +194,10 @@ function ge_p3_to_cached (r: ge_cached, p: ge_p3): void { fe_mul(r.T2d, p.T, ed25519_d2) } +const ge_add_cached_t0: FieldElement = fe() /** * 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) @@ -207,7 +220,6 @@ function ge_cached_0 (h: ge_cached): void { 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) @@ -254,7 +266,7 @@ 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 = new StaticArray(8) +const ge_scalarmult_pi: StaticArray = ge_cached_8() const ge_scalarmult_t: ge_cached = new ge_cached() /** * `h = a * p` @@ -360,10 +372,10 @@ export function ge_scalarmult (h: ge_p3, a: StaticArray, p: ge_p3): void { ge_p1p1_to_p3(h, r) } -const ge_scalarmult_base_e: StaticArray = new StaticArray(64) +const ge_scalarmult_base_e: StaticArray = new StaticArray(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() +const ge_scalarmult_base_s: ge_p2 = new ge_p2() +const ge_scalarmult_base_t: ge_precomp = new ge_precomp() /** * `h = a * B` (with precomputation) * @@ -380,7 +392,6 @@ const ge_scalarmult_base_t = new ge_precomp() */ export function ge_scalarmult_base (h: ge_p3, a: StaticArray): void { const e = ge_scalarmult_base_e - let carry: i8 = 0 const r = ge_scalarmult_base_r const s = ge_scalarmult_base_s const t = ge_scalarmult_base_t @@ -392,11 +403,12 @@ export function ge_scalarmult_base (h: ge_p3, a: StaticArray): void { /* each e[i] is between 0 and 15 */ /* e[63] is between 0 and 7 */ + let carry: i8 = 0 for (let i = 0; i < 63; ++i) { e[i] += carry carry = e[i] + 8 carry >>= 4 - e[i] -= carry << 4 + e[i] -= carry * (i8(1) << 4) } e[63] += carry /* each e[i] is between -8 and 8 */ @@ -442,12 +454,325 @@ export function ge_p3_tobytes (s: StaticArray, h: ge_p3): void { /** * true if `s < p` */ -export function ge_is_canonical (s: StaticArray): boolean { - let c: u32 = (s[31] & 0x7f) ^ 0x7f +export function ge_is_canonical (s: StaticArray): u8 { + let c: u8 = (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 + const d: u8 = (0xec - s[0]) >> 8 + return 1 - (c & d & 1) +} + +const u: FieldElement = fe() +const v: FieldElement = fe() +const v3: FieldElement = fe() +const vxx: FieldElement = fe() +const m_root_check: FieldElement = fe() +const p_root_check: FieldElement = fe() +export function ge_frombytes_negate_vartime (h: ge_p3, s: StaticArray): i32 { + fe_frombytes(h.Y, s) + fe_1(h.Z) + fe_sq(u, h.Y) + fe_mul(v, u, ed25519_d) + fe_sub(u, u, h.Z) /* u = y^2-1 */ + fe_add(v, v, h.Z) /* v = dy^2+1 */ + + fe_sq(v3, v) + fe_mul(v3, v3, v) /* v3 = v^3 */ + fe_sq(h.X, v3) + fe_mul(h.X, h.X, v) + fe_mul(h.X, h.X, u) /* x = uv^7 */ + + fe_pow22523(h.X, h.X) /* x = (uv^7)^((q-5)/8) */ + fe_mul(h.X, h.X, v3) + fe_mul(h.X, h.X, u) /* x = uv^3(uv^7)^((q-5)/8) */ + + fe_sq(vxx, h.X) + fe_mul(vxx, vxx, v) + fe_sub(m_root_check, vxx, u) /* vx^2-u */ + if (fe_iszero(m_root_check) == 0) { + fe_add(p_root_check, vxx, u) /* vx^2+u */ + if (fe_iszero(p_root_check) == 0) { + return -1 + } + fe_mul(h.X, h.X, fe25519_sqrtm1) + } + + if (fe_isnegative(h.X) == (s[31] >> 7)) { /* vartime function - compiler optimization is fine */ + fe_neg(h.X, h.X) + } + fe_mul(h.T, h.X, h.Y) + + return 0 +} + +const ge_frombytes_u: FieldElement = fe() +const ge_frombytes_v: FieldElement = fe() +const ge_frombytes_vxx: FieldElement = fe() +const ge_frombytes_m_root_check: FieldElement = fe() +const ge_frombytes_p_root_check: FieldElement = fe() +const ge_frombytes_negx: FieldElement = fe() +const ge_frombytes_x_sqrtm1: FieldElement = fe() +export function ge_frombytes (h: ge_p3, s: StaticArray): i32 { + const u = ge_frombytes_u + const v = ge_frombytes_v + const vxx = ge_frombytes_vxx + const m_root_check = ge_frombytes_m_root_check + const p_root_check = ge_frombytes_p_root_check + const negx = ge_frombytes_negx + const x_sqrtm1 = ge_frombytes_x_sqrtm1 + let has_m_root: u8 + let has_p_root: u8 + + fe_frombytes(h.Y, s) + fe_1(h.Z) + fe_sq(u, h.Y) + fe_mul(v, u, ed25519_d) + fe_sub(u, u, h.Z) /* u = y^2-1 */ + fe_add(v, v, h.Z) /* v = dy^2+1 */ + + fe_mul(h.X, u, v) + fe_pow22523(h.X, h.X) + fe_mul(h.X, u, h.X) /* u((uv)^((q-5)/8)) */ + + fe_sq(vxx, h.X) + fe_mul(vxx, vxx, v) + fe_sub(m_root_check, vxx, u) /* vx^2-u */ + fe_add(p_root_check, vxx, u) /* vx^2+u */ + has_m_root = fe_iszero(m_root_check) + has_p_root = fe_iszero(p_root_check) + fe_mul(x_sqrtm1, h.X, fe25519_sqrtm1) /* x*sqrt(-1) */ + fe_cmov(h.X, x_sqrtm1, 1 - has_m_root) + + fe_neg(negx, h.X) + fe_cmov(h.X, negx, fe_isnegative(h.X) ^ (((s[31] >> 5) ^ optblocker_u8) >> 2)) + fe_mul(h.T, h.X, h.Y) + + return (has_m_root | has_p_root) - 1 +} + +const ge_has_small_order_y_sqrtm1: FieldElement = fe() +const ge_has_small_order_c: FieldElement = fe() +export function ge_has_small_order (p: ge_p3): i32 { + const y_sqrtm1 = ge_has_small_order_y_sqrtm1 + const c = ge_has_small_order_c + let ret: i32 = 0 + + ret |= fe_iszero(p.X) + ret |= fe_iszero(p.Y) + ret |= fe_iszero(p.Z) + fe_mul(y_sqrtm1, p.Y, fe25519_sqrtm1) + fe_sub(c, y_sqrtm1, p.X) + ret |= fe_iszero(c) + fe_add(c, y_sqrtm1, p.X) + ret |= fe_iszero(c) + + return ret +} + +/** + * r = p + */ +export function ge_p2_to_p3 (r: ge_p3, p: ge_p2): void { + fe_copy(r.X, p.X) + fe_copy(r.Y, p.Y) + fe_copy(r.Z, p.Z) + fe_mul(r.T, p.X, p.Y) +} + +const ge_p3_sub_q_neg: ge_p3 = new ge_p3() +/* r = p-q */ +export function ge_p3_sub (r: ge_p3, p: ge_p3, q: ge_p3): void { + const q_neg = ge_p3_sub_q_neg + ge_p3_neg(q_neg, q) + ge_p3_add(r, p, q_neg) +} + +/* r = -p */ +function ge_p3_neg (r: ge_p3, p: ge_p3): void { + fe_neg(r.X, p.X) + fe_copy(r.Y, p.Y) + fe_copy(r.Z, p.Z) + fe_neg(r.T, p.T) +} + +const ge_p3_add_q_cached = new ge_cached() +const ge_p3_add_q_p1p1 = new ge_p1p1() +/* r = p+q */ +function ge_p3_add (r: ge_p3, p: ge_p3, q: ge_p3): void { + const q_cached = ge_p3_add_q_cached + const p1p1 = ge_p3_add_q_p1p1 + ge_p3_to_cached(q_cached, q) + ge_add_cached(p1p1, p, q_cached) + ge_p1p1_to_p3(r, p1p1) +} + +const ge_double_scalarmult_vartime_aslide: StaticArray = new StaticArray(256) +const ge_double_scalarmult_vartime_bslide: StaticArray = new StaticArray(256) +const ge_double_scalarmult_vartime_Ai: StaticArray = ge_cached_8() /* A,3A,5A,7A,9A,11A,13A,15A */ +const ge_double_scalarmult_vartime_t: ge_p1p1 = new ge_p1p1() +const ge_double_scalarmult_vartime_u: ge_p3 = new ge_p3() +const ge_double_scalarmult_vartime_A2: ge_p3 = new ge_p3() +/** + * r = a * A + b * B + * where a = a[0]+256*a[1]+...+256^31 a[31]. + * and b = b[0]+256*b[1]+...+256^31 b[31]. + * B is the Ed25519 base point (x,4/5) with x positive. + * + * Only used for signatures verification. + */ +export function ge_double_scalarmult_vartime (r: ge_p2, a: StaticArray, A: ge_p3, b: StaticArray): void { + const Bi = base2 + const aslide = ge_double_scalarmult_vartime_aslide + const bslide = ge_double_scalarmult_vartime_bslide + const Ai = ge_double_scalarmult_vartime_Ai /* A,3A,5A,7A,9A,11A,13A,15A */ + const t = ge_double_scalarmult_vartime_t + const u = ge_double_scalarmult_vartime_u + const A2 = ge_double_scalarmult_vartime_A2 + let i: i32 = 0 + + slide_vartime(aslide, a) + slide_vartime(bslide, b) + + ge_p3_to_cached(Ai[0], A) + + ge_p3_dbl(t, A) + ge_p1p1_to_p3(A2, t) + + ge_add_cached(t, A2, Ai[0]) + ge_p1p1_to_p3(u, t) + ge_p3_to_cached(Ai[1], u) + + ge_add_cached(t, A2, Ai[1]) + ge_p1p1_to_p3(u, t) + ge_p3_to_cached(Ai[2], u) + + ge_add_cached(t, A2, Ai[2]) + ge_p1p1_to_p3(u, t) + ge_p3_to_cached(Ai[3], u) + + ge_add_cached(t, A2, Ai[3]) + ge_p1p1_to_p3(u, t) + ge_p3_to_cached(Ai[4], u) + + ge_add_cached(t, A2, Ai[4]) + ge_p1p1_to_p3(u, t) + ge_p3_to_cached(Ai[5], u) + + ge_add_cached(t, A2, Ai[5]) + ge_p1p1_to_p3(u, t) + ge_p3_to_cached(Ai[6], u) + + ge_add_cached(t, A2, Ai[6]) + ge_p1p1_to_p3(u, t) + ge_p3_to_cached(Ai[7], u) + + ge_p2_0(r) + + for (i = 255; i >= 0; --i) { + if (aslide[i] || bslide[i]) { + break + } + } + + for (; i >= 0; --i) { + ge_p2_dbl(t, r) + + if (aslide[i] > 0) { + ge_p1p1_to_p3(u, t) + ge_add_cached(t, u, Ai[aslide[i] / 2]) + } else if (aslide[i] < 0) { + ge_p1p1_to_p3(u, t) + ge_sub_cached(t, u, Ai[(-aslide[i]) / 2]) + } + + if (bslide[i] > 0) { + ge_p1p1_to_p3(u, t) + ge_add_precomp(t, u, Bi[bslide[i] / 2]) + } else if (bslide[i] < 0) { + ge_p1p1_to_p3(u, t) + ge_sub_precomp(t, u, Bi[(-bslide[i]) / 2]) + } + + ge_p1p1_to_p2(r, t) + } +} + +function slide_vartime (r: StaticArray, a: StaticArray): void { + let i: i32 + let b: i32 + let k: i32 + let ribs: i32 + let cmp: i32 + + for (i = 0; i < 256; ++i) { + r[i] = 1 & (a[i >> 3] >> i8(i & 7)) + } + for (i = 0; i < 256; ++i) { + if (!r[i]) { + continue + } + for (b = 1; b <= 6 && i + b < 256; ++b) { + if (!r[i + b]) { + continue + } + ribs = r[i + b] << i8(b) + cmp = r[i] + ribs + if (cmp <= 15) { + r[i] = i8(cmp) + r[i + b] = 0 + } else { + cmp = r[i] - ribs + if (cmp < -15) { + break + } + r[i] = i8(cmp) + for (k = i + b; k < 256; ++k) { + if (!r[k]) { + r[k] = 1 + break + } + r[k] = 0 + } + } + } + } +} + +const ge_sub_cached_t0: FieldElement = fe() +/** + * r = p - q + */ +function ge_sub_cached (r: ge_p1p1, p: ge_p3, q: ge_cached): void { + const t0 = ge_sub_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.YminusX) + fe_mul(r.Y, r.Y, q.YplusX) + 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_sub(r.Z, t0, r.T) + fe_add(r.T, t0, r.T) +} + +const ge_sub_precomp_t0: FieldElement = fe() +/** + * r = p - q + */ +function ge_sub_precomp (r: ge_p1p1, p: ge_p3, q: ge_precomp): void { + const t0 = ge_sub_precomp_t0 + fe_add(r.X, p.Y, p.X) + fe_sub(r.Y, p.Y, p.X) + fe_mul(r.Z, r.X, q.yminusx) + fe_mul(r.Y, r.Y, q.yplusx) + fe_mul(r.T, q.xy2d, p.T) + fe_add(t0, p.Z, p.Z) + fe_sub(r.X, r.Z, r.Y) + fe_add(r.Y, r.Z, r.Y) + fe_sub(r.Z, t0, r.T) + fe_add(r.T, t0, r.T) } diff --git a/assembly/nano-nacl.ts b/assembly/nano-nacl.ts index de259e7..6e2f75b 100644 --- a/assembly/nano-nacl.ts +++ b/assembly/nano-nacl.ts @@ -2,8 +2,7 @@ //! SPDX-License-Identifier: GPL-3.0-or-later import { Blake2b } from './blake2b' -import { fe, fe_add, fe_copy, fe_cswap, fe_mul, fe_neg, fe_sq, fe_sub, FieldElement } from './fe' -import { ge_is_canonical, ge_p3, ge_p3_0 } from './ge' +import { ge_double_scalarmult_vartime, ge_frombytes, ge_frombytes_negate_vartime, ge_has_small_order, ge_is_canonical, ge_p2, ge_p2_to_p3, ge_p3, ge_p3_0, ge_p3_sub, ge_p3_tobytes, ge_scalarmult_base } from './ge' import { sc_is_canonical, sc_muladd, sc_reduce } from './sc' const BLOCKHASH_BYTES: i32 = 32 @@ -13,304 +12,10 @@ const SECRETKEY_BYTES: i32 = PRIVATEKEY_BYTES + PUBLICKEY_BYTES const SIGNATURE_BYTES: i32 = 64 const SIGNEDBLOCKHASH_BYTES: i32 = SIGNATURE_BYTES + BLOCKHASH_BYTES -const BASE: ge_p3 = { - X: fe([0xd51a, 0x8f25, 0x2d60, 0xc956, 0xa7b2, 0x9525, 0xc760, 0x692c, 0xdc5c, 0xfdd6, 0xe231, 0xc0a4, 0x53fe, 0xcd6e, 0x36d3, 0x2169]), - Y: fe([0x6658, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666]), - Z: fe([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), - T: fe([0xdd90, 0xa5b7, 0x8ab3, 0x6dde, 0x52f5, 0x7751, 0x9f80, 0x20f0, 0xe37d, 0x64ab, 0x4e8e, 0x66ea, 0x7665, 0xd78b, 0x5f0f, 0xe787]), - __brand: 'ge_p3' -} -const D: FieldElement = fe([0x78a3, 0x1359, 0x4dca, 0x75eb, 0xd8ab, 0x4141, 0x0a4d, 0x0070, 0xe898, 0x7779, 0x4079, 0x8cc7, 0xfe73, 0x2b6f, 0x6cee, 0x5203]) -const D2: FieldElement = fe([0xf159, 0x26b2, 0x9b94, 0xebd6, 0xb156, 0x8283, 0x149a, 0x00e0, 0xd130, 0xeef3, 0x80f2, 0x198e, 0xfce7, 0x56df, 0xd9dc, 0x2406]) -const I: FieldElement = fe([0xa0b0, 0x4a0e, 0x1b27, 0xc4ee, 0xe478, 0xad2f, 0x1806, 0x2f43, 0xd7a7, 0x3dfb, 0x0099, 0x2b4d, 0xdf0b, 0x4fc1, 0x2480, 0x2b83]) - // Static I/O buffers const INPUT_BUFFER = memory.data(128) const OUTPUT_BUFFER = memory.data(64) -// a, b = 32 bytes -//@ts-expect-error -@inline -function bitsdiff (a: StaticArray, b: StaticArray): bool { - const ai = changetype(a) - const bi = changetype(b) - let d = load(ai) ^ load(bi) - d |= load(ai, 8) ^ load(bi, 8) - d |= load(ai, 16) ^ load(bi, 16) - d |= load(ai, 24) ^ load(bi, 24) - return d !== 0 -} - -// o, a = StaticArray[16] -const pow_c: FieldElement = fe() -function pow2523 (o: FieldElement, a: FieldElement): void { - const c = pow_c - fe_copy(c, a) - - for (let i = 0; i < 249; i++) { - fe_sq(c, c) - fe_mul(c, c, a) - } - fe_sq(c, c) - fe_sq(c, c) - fe_mul(c, c, a) - - fe_copy(o, c) -} - -function car25519 (o: FieldElement): void { - let c: i32 = 0 - for (let i = 0; i < 16; i++) { - c += o[i] - o[i] = c & 0xFFFF - c >>= 16 - } - o[0] += 38 * c -} - -const inv_c: FieldElement = fe() -function inv25519 (o: FieldElement, a: FieldElement): void { - const c = inv_c - fe_copy(c, a) - - for (let i = 0; i < 249; i++) { - fe_sq(c, c) - fe_mul(c, c, a) - } - fe_sq(c, c) - fe_sq(c, c) - fe_mul(c, c, a) - fe_sq(c, c) - fe_sq(c, c) - fe_mul(c, c, a) - fe_sq(c, c) - fe_mul(c, c, a) - - fe_copy(o, c) -} - -// a, b = StaticArray[16] -const neq_c = new StaticArray(32) -const neq_d = new StaticArray(32) -function neq25519 (a: FieldElement, b: FieldElement): bool { - const c = neq_c - const d = neq_d - pack25519(c, a) - pack25519(d, b) - return bitsdiff(c, d) -} - -const pack_m: FieldElement = fe() -const pack_t: FieldElement = fe() -function pack25519 (o: StaticArray, n: FieldElement): void { - const m = pack_m - const t = pack_t - let b: i32 = 0 - memory.copy(changetype(t), changetype(n), 64) - - car25519(t) - car25519(t) - car25519(t) - - for (let j = 0; j < 2; j++) { - m[0] = t[0] - 0xffed - for (let i = 1; i < 15; i++) { - m[i] = t[i] - 0xffff - ((m[i - 1] >> 16) & 1) - m[i - 1] &= 0xffff - } - m[15] = t[15] - 0x7fff - ((m[14] >> 16) & 1) - b = (m[15] >> 16) & 1 - m[14] &= 0xffff - fe_cswap(t, m, 1 - b) - } - - for (let i = 0; i < 16; i++) { - o[(i << 1) + 0] = u8(t[i] & 0xff) - o[(i << 1) + 1] = u8(t[i] >> 8) - } -} - -const par_d = new StaticArray(32) -function par25519 (a: FieldElement): u8 { - const d = par_d - pack25519(d, a) - return d[0] & 1 -} - -function unpack25519 (o: FieldElement, n: StaticArray): void { - for (let i = 0; i < 16; i++) { - o[i] = i32(n[i << 1]) + (i32(n[(i << 1) + 1]) << 8) - } - o[15] &= (1 << 15) - 1 -} - -/** - * Reusable buffers for point addition and doubling. - */ -const a: FieldElement = fe() -const b: FieldElement = fe() -const c: FieldElement = fe() -const d: FieldElement = fe() -const e: FieldElement = fe() -const f: FieldElement = fe() -const g: FieldElement = fe() -const h: FieldElement = fe() -const t: FieldElement = fe() - -/** - * Curve point addition in extended projective coordinates. - * A = (Y1-X1)*(Y2-X2), B = (Y1+X1)*(Y2+X2), C = (T1*2*d)*T2, D = (Z1*2)*Z2 - * E = B-A, F = D-C, G = D+C, H = B+A - * X3 = E*F, Y3 = G*H, Z3 = F*G, T3 = E*H - */ -// p, q = StaticArray[4][16] -function add (p: ge_p3, q: ge_p3): void { - fe_sub(a, p.Y, p.X) - fe_sub(t, q.Y, q.X) - fe_mul(a, a, t) - fe_add(b, p.X, p.Y) - fe_add(t, q.X, q.Y) - fe_mul(b, b, t) - fe_mul(c, p.T, q.T) - fe_mul(c, c, D2) - fe_mul(d, p.Z, q.Z) - fe_add(d, d, d) - - fe_sub(e, b, a) - fe_sub(f, d, c) - fe_add(g, d, c) - fe_add(h, b, a) - - fe_mul(p.X, e, f) - fe_mul(p.Y, h, g) - fe_mul(p.Z, g, f) - fe_mul(p.T, e, h) -} - -/** - * Doubles a curve point in projective coordinates. - * A = X², B = Y², C = 2*Z², D = -A (in mod p, negate is p-A; tweetnacl does 0-A) - * E = (X+Y)²-A-B, G = D+B, F = G-C, H = D-B - * X3 = E*F, Y3 = G*H, Z3 = F*G, T3 = E*H - */ -function double (p: ge_p3): void { - fe_sq(a, p.X) - fe_sq(b, p.Y) - fe_sq(c, p.Z) - fe_add(c, c, c) - fe_neg(d, a) - - fe_add(e, p.X, p.Y) - fe_sq(e, e) - fe_sub(e, e, a) - fe_sub(e, e, b) - fe_add(g, d, b) - fe_sub(f, g, c) - fe_sub(h, d, b) - - fe_mul(p.X, e, f) - fe_mul(p.Y, g, h) - fe_mul(p.Z, f, g) - fe_mul(p.T, e, h) -} - -// p, q = StaticArray[16] -// offsets i32x16 = 64 bytes -//@ts-expect-error -@inline -function cswap (p: ge_p3, q: ge_p3, b: i32): void { - fe_cswap(p.X, q.X, b) - fe_cswap(p.Y, q.Y, b) - fe_cswap(p.Z, q.Z, b) - fe_cswap(p.T, q.T, b) -} - -const tx: FieldElement = fe() -const ty: FieldElement = fe() -const zi: FieldElement = fe() -/** - * Convert projective coordinates to a byte array. - * - * p = StaticArray[4][16] = (X,Y,Z,T) as 16 limbs each -*/ -function pack (r: StaticArray, p: ge_p3): void { - inv25519(zi, p.Z) - fe_mul(tx, p.X, zi) - fe_mul(ty, p.Y, zi) - pack25519(r, ty) - r[31] ^= par25519(tx) << 7 -} - -// p, q = StaticArray[4][16] -function scalarmult (p: ge_p3, q: ge_p3, s: StaticArray): void { - for (let i = 255; i >= 0; i--) { - const b: i32 = (s[i >> 3] >> u8(i & 7)) & 1 - cswap(p, q, b) - add(q, p) - double(p) - cswap(p, q, b) - } -} - -// p = StaticArray[4][16] -const scalarbase_q = new ge_p3() -function scalarbase (p: ge_p3, s: StaticArray): void { - const q = scalarbase_q - fe_copy(q.X, BASE.X) - fe_copy(q.Y, BASE.Y) - fe_copy(q.Z, BASE.Z) - fe_copy(q.T, BASE.T) - scalarmult(p, q, s) -} - -const unpack_chk: FieldElement = fe() -const unpack_num: FieldElement = fe() -const unpack_den = new ge_p3() -const unpack_t: FieldElement = fe() -function unpackneg (r: ge_p3, p: StaticArray): i8 { - const chk = unpack_chk - const num = unpack_num - const den = unpack_den - const t = unpack_t - - unpack25519(r.Y, p) - fe_sq(num, r.Y) - fe_mul(den.X, num, D) - fe_sub(num, num, r.Z) - fe_add(den.X, r.Z, den.X) - - fe_sq(den.Y, den.X) - fe_sq(den.Z, den.Y) - fe_mul(den.T, den.Z, den.Y) - fe_mul(t, den.T, num) - fe_mul(t, t, den.X) - - pow2523(t, t) - fe_mul(t, t, num) - fe_mul(t, t, den.X) - fe_mul(t, t, den.X) - fe_mul(r.X, t, den.X) - - fe_sq(chk, r.X) - fe_mul(chk, chk, den.X) - if (neq25519(chk, num)) { - fe_mul(r.X, r.X, I) - } - - fe_sq(chk, r.X) - fe_mul(chk, chk, den.X) - - if (neq25519(chk, num)) { - return -1 - } - - if (par25519(r.X) === (p[31] >> 7)) { - fe_neg(r.X, r.X) - } - fe_mul(r.T, r.X, r.Y) - return 0 -} - //@ts-expect-error @inline function clamp (k: StaticArray): void { @@ -325,22 +30,19 @@ function crypto_hash (o: StaticArray, i: StaticArray): void { } const crypto_derive_A = new ge_p3() -function crypto_derive (pk: StaticArray, sk: StaticArray): void { +function crypto_derive (pk: StaticArray, sk: StaticArray, seed: StaticArray): void { const A = crypto_derive_A ge_p3_0(A) - crypto_hash(sk, sk) + crypto_hash(sk, seed) clamp(sk) trace(`seed hashed to sk: ${sk.map((b: u8) => b.toString(16).padStart(2, '0')).join('')}`) - scalarbase(A, sk) - - trace(`sk scalarbase to A.X: ${A.X.map((b: i32) => b.toString(16).padStart(8, '0')).join('')}`) - - pack(pk, A) - - trace(`A packed to pk: ${pk.map((b: u8) => b.toString(16).padStart(2, '0')).join('')}`) + ge_scalarmult_base(A, sk) + ge_p3_tobytes(pk, A) + memory.copy(changetype(sk), changetype(seed), PRIVATEKEY_BYTES) + memory.copy(changetype(sk) + 32, changetype(pk), PUBLICKEY_BYTES) } const crypto_sign_az = new StaticArray(64) @@ -370,8 +72,8 @@ function crypto_sign (s: StaticArray, m: StaticArray, sk: StaticArray, m: StaticArray, sk: StaticArray(64) -const crypto_verify_open_p = new ge_p3() -const crypto_verify_open_q = new ge_p3() -const crypto_verify_open_t = new StaticArray(32) -const crypto_verify_open_ram = new StaticArray(SIGNEDBLOCKHASH_BYTES) -const crypto_verify_open_S = new StaticArray(32) +const crypto_verify_h = new StaticArray(64) +const crypto_verify_check = new ge_p3() +const crypto_verify_expected_r = new ge_p3() +const crypto_verify_A = new ge_p3() +const crypto_verify_sb_ah = new ge_p3() +const crypto_verify_sb_ah_p2 = new ge_p2() +const crypto_verify_ram = new StaticArray(SIGNEDBLOCKHASH_BYTES) +const crypto_verify_S = new StaticArray(32) /** -* Verify signature `s` was made by signing message `m` using public key `pk`. -*/ -function crypto_verify (s: StaticArray, m: StaticArray, pk: StaticArray): bool { - const hram = crypto_verify_open_hram - const p = crypto_verify_open_p - const q = crypto_verify_open_q - ge_p3_0(p) - ge_p3_0(q) - - const t = crypto_verify_open_t - const ram = crypto_verify_open_ram - const S = crypto_verify_open_S + * Verify signature `s` was made by signing message `m` using public key `pk`. + */ +function crypto_verify (s: StaticArray, m: StaticArray, pk: StaticArray): i32 { + const h = crypto_verify_h + const check = crypto_verify_check + const expected_r = crypto_verify_expected_r + const A = crypto_verify_A + const sb_ah = crypto_verify_sb_ah + const sb_ah_p2 = crypto_verify_sb_ah_p2 + const ram = crypto_verify_ram + const S = crypto_verify_S // fail if public key `k` is non-canonical (`p = 2²⁵⁵-19 <= k`) - if (!ge_is_canonical(pk)) return false + if (!ge_is_canonical(pk)) return -1 // fail if private scalar `S` is non-canonical (`L <= S`) memory.copy(changetype(S), changetype(s) + 32, 32) - if (!sc_is_canonical(S)) return false + if (!sc_is_canonical(S)) return -1 + + if (ge_frombytes_negate_vartime(A, pk) != 0) return -1 + if (ge_has_small_order(A) != 0) return -1 - // fail - if (unpackneg(q, pk)) return false + if (ge_frombytes(expected_r, s) != 0) return -1 + if (ge_has_small_order(expected_r) != 0) return -1 // signature is nonce point R and scalar S (R || S) // data to hash is nonce point R, public key A, and message M @@ -427,17 +133,14 @@ function crypto_verify (s: StaticArray, m: StaticArray, pk: StaticArray< memory.copy(changetype(ram), changetype(s), 32) memory.copy(changetype(ram) + 32, changetype(pk), 32) memory.copy(changetype(ram) + 64, changetype(m), 32) - crypto_hash(hram, ram) - sc_reduce(hram) + crypto_hash(h, ram) + sc_reduce(h) - scalarmult(p, q, hram) - ge_p3_0(q) - scalarbase(q, S) - add(p, q) - pack(t, p) + ge_double_scalarmult_vartime(sb_ah_p2, h, A, S) + ge_p2_to_p3(sb_ah, sb_ah_p2) + ge_p3_sub(check, expected_r, sb_ah) - // fail if any bit differs from encoded nonce point R, else pass - return !bitsdiff(s, t) + return ge_has_small_order(check) - 1 } // Returns the pointer to the static input buffer (128 bytes). @@ -451,21 +154,23 @@ export function getOutputPointer (): usize { } const derive_pk = new StaticArray(PUBLICKEY_BYTES) -const derive_sk = new StaticArray(PRIVATEKEY_BYTES) +const derive_sk = new StaticArray(SECRETKEY_BYTES) +const derive_seed = new StaticArray(PRIVATEKEY_BYTES) /** -* Derives a Nano public key from a private key and writes it to the static -* output buffer. This mirrors the functionality of -* `nacl.sign.keyPair.fromSeed()`, returning just the `publicKey` property. -*/ + * Derives a Nano public key from a private key and writes it to the static + * output buffer. This mirrors the functionality of + * `nacl.sign.keyPair.fromSeed()`, returning just the `publicKey` property. + */ export function derive (): void { const pk = derive_pk const sk = derive_sk + const seed = derive_seed for (let i = 0; i < PRIVATEKEY_BYTES; i++) { - sk[i] = load(INPUT_BUFFER + i) + seed[i] = load(INPUT_BUFFER + i) } const start = performance.now() - crypto_derive(pk, sk) + crypto_derive(pk, sk, seed) const end = performance.now() trace('derive time', 1, end - start) @@ -478,12 +183,12 @@ const sign_h = new StaticArray(BLOCKHASH_BYTES) const sign_sk = new StaticArray(SECRETKEY_BYTES) const sign_s = new StaticArray(SIGNEDBLOCKHASH_BYTES) /** -* Signs the message using the private key and writes the signature to the static -* output buffer. This mirrors the functionality of `nacl.sign.detached()`. -* -* @param {u64} h0-h3 - Message to sign (32 bytes as 4 × u64) -* @param {u64} k0-k7 - Secret key (32 bytes private key + 32 bytes public key as 8 × u64) -*/ + * Signs the message using the private key and writes the signature to the static + * output buffer. This mirrors the functionality of `nacl.sign.detached()`. + * + * @param {u64} h0-h3 - Message to sign (32 bytes as 4 × u64) + * @param {u64} k0-k7 - Secret key (32 bytes private key + 32 bytes public key as 8 × u64) + */ export function sign (): void { const h = sign_h const sk = sign_sk @@ -511,15 +216,14 @@ const verify_h = new StaticArray(BLOCKHASH_BYTES) const verify_s = new StaticArray(SIGNATURE_BYTES) const verify_k = new StaticArray(PUBLICKEY_BYTES) /** -* Verifies a signature on a block hash against a public key. This mirrors the -* functionality of `nacl.sign.detached.verify()`. -* -* @param {u64} s0-s7 - Signature (64 bytes as 8 × u64) -* @param {u64} h0-h3 - Blockhash (32 bytes as 4 × u64) -* @param {u64} k0-k3 - Public key (32 bytes as 4 × u64) -* @returns {i32} - 1 if valid, 0 if invalid -*/ -export function verify (): i32 { + * Verifies a signature on a block hash against a public key. This mirrors the + * functionality of `nacl.sign.detached.verify()`. + * + * @param {u64} s0-s7 - Signature (64 bytes as 8 × u64) + * @param {u64} h0-h3 - Blockhash (32 bytes as 4 × u64) + * @param {u64} k0-k3 - Public key (32 bytes as 4 × u64) + */ +export function verify (): void { const s = verify_s const h = verify_h const k = verify_k @@ -538,9 +242,9 @@ export function verify (): i32 { } const start = performance.now() - const result = crypto_verify(s, h, k) + const v = crypto_verify(s, h, k) const end = performance.now() trace('verify time', 1, end - start) - return result ? 1 : 0 + store(OUTPUT_BUFFER, v) } diff --git a/assembly/sc.ts b/assembly/sc.ts index bb38322..ae85e4d 100644 --- a/assembly/sc.ts +++ b/assembly/sc.ts @@ -4,7 +4,6 @@ /** * Scalar operations modulo `L` where `L` is the group order of Curve25519. */ - import { load_3, load_4 } from './utils' /** @@ -23,7 +22,7 @@ const L = StaticArray.fromArray([ * - Reject immediately if `S >= 2²⁵³` since this implies `S >= L` * - Otherwise, check canonicity for `2²⁵² <= S < 2²⁵³` */ -export function sc_is_canonical (S: StaticArray): boolean { +export function sc_is_canonical (S: StaticArray): bool { if ((S[31] & 0xF0) == 0) return true if ((S[31] & 0xE0) != 0) return false diff --git a/assembly/utils.ts b/assembly/utils.ts index ab8fdc8..cb4029e 100644 --- a/assembly/utils.ts +++ b/assembly/utils.ts @@ -1,7 +1,7 @@ //! SPDX-FileCopyrightText: 2026 Chris Duncan //! SPDX-License-Identifier: GPL-3.0-or-later -const optblocker_u8: u8 = 0 +export const optblocker_u8: u8 = 0 //@ts-expect-error @inline @@ -31,7 +31,17 @@ export function load_4 (input: StaticArray, i: u8): u64 { //@ts-expect-error @inline -export function negative (b: i8): i8 { +export function negative (b: i8): u8 { const x: u8 = u8(b) /* 0..127: no 128..255: yes */ return ((x >> 5) ^ optblocker_u8) >> 2 /* 1: yes; 0: no */ } + +//@ts-expect-error +@inline +export function sodium_is_zero (n: StaticArray, nlen: i32): u8 { + let d: u8 = 0 + for (let i: i32 = 0; i < nlen; i++) { + d |= n[i] + } + return 1 & ((d - 1) >> 8) +} diff --git a/index.html b/index.html index 37d601f..fcfcd4c 100644 --- a/index.html +++ b/index.html @@ -209,13 +209,15 @@ SPDX-License-Identifier: GPL-3.0-or-later // PASS result = await derive(TEST_VECTORS.privateKey) - console.log(result) + console.log('expected: ', TEST_VECTORS.publicKey.toUpperCase()) + console.log('actual', result?.toUpperCase?.()) 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 sign(TEST_VECTORS.blockHash, TEST_VECTORS.privateKey, TEST_VECTORS.publicKey) - console.log(result) + console.log('expected: ', TEST_VECTORS.signature.toUpperCase()) + console.log('actual', result?.toUpperCase?.()) 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) @@ -233,6 +235,12 @@ SPDX-License-Identifier: GPL-3.0-or-later console.log(`verify() output for non-canonical signature is ${result === true ? 'correct' : 'incorrect'}`) expect.push(result) + result = await verify(TEST_VECTORS.signature, random(), TEST_VECTORS.publicKey) + console.log(result) + result = result === false + console.log(`verify() output for random block hash is ${result === true ? 'correct' : 'incorrect'}`) + expect.push(result) + result = await verify(TEST_VECTORS.signature, zeroes, TEST_VECTORS.publicKey) console.log(result) result = result === false diff --git a/index.ts b/index.ts index b65b0a2..25e2f56 100644 --- a/index.ts +++ b/index.ts @@ -24,7 +24,7 @@ const NanoNaCl = async (bytes: number[]): Promise => { let memory: WebAssembly.Memory let derive: (k: Uint8Array) => Uint8Array let sign: (h: Uint8Array, k: Uint8Array) => Uint8Array - let verify: (s: Uint8Array, h: Uint8Array, k: Uint8Array) => boolean + let verify: (s: Uint8Array, h: Uint8Array, k: Uint8Array) => Uint8Array async function setup (): Promise { try { @@ -111,7 +111,7 @@ const NanoNaCl = async (bytes: number[]): Promise => { return s } - verify = function (s: Uint8Array, h: Uint8Array, k: Uint8Array): boolean { + verify = function (s: Uint8Array, h: Uint8Array, k: Uint8Array): Uint8Array { // assembly/nano-nacl/verify() => bool let buffer: DataView | undefined = new DataView(memory.buffer) let inPtr = exports.getInputPointer() @@ -126,9 +126,12 @@ const NanoNaCl = async (bytes: number[]): Promise => { for (let i = 0; i < 32; i++) { buffer.setUint8(inPtr + i, k[i]) } - const v = exports.verify() + exports.verify() + const outPtr = exports.getOutputPointer() + const v = new Uint8Array(1) + v[0] = buffer.getUint8(outPtr) buffer = undefined - return v === 1 + return v } isReady = true @@ -190,11 +193,11 @@ const NanoNaCl = async (bytes: number[]): Promise => { const signatureBytes = hex2bytes('signature', 64, signature) const blockHashBytes = hex2bytes('block hash', 32, blockHash) const publicKeyBytes = hex2bytes('public key', 32, publicKey) - const isVerified = verify(signatureBytes, blockHashBytes, publicKeyBytes) - if (isVerified == null) { + const verification = verify(signatureBytes, blockHashBytes, publicKeyBytes) + if (verification == null) { throw new TypeError('Invalid verification from WASM verify()') } - result = new Uint8Array([isVerified ? 1 : 0]) + result = verification } } } catch (err: unknown) { @@ -217,9 +220,8 @@ const NanoNaCl = async (bytes: number[]): Promise => { const NanoNaClWorker = `;await (${NanoNaCl})([${nacl}])` /** -* HOST CODE -*/ - + * HOST CODE + */ // Initialize CPU let isWorkerReady: boolean = false let worker: Worker @@ -328,5 +330,5 @@ export async function sign (blockHash: string, secretKey: string): Promise { const result = await run({ action: 'verify', signature, blockHash, publicKey }) - return result === '01' + return result === '00' }