}
])
])
+
+export const base2 = StaticArray.fromArray<ge_precomp>([
+ {
+ 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])
+ }
+])
*
* `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<i32>
-export function fe (source: Array<i32> = new Array<i32>(16)): FieldElement {
+export function fe (source: Array<i32> = new Array<i32>(10)): FieldElement {
return StaticArray.fromArray<i32>(source)
}
//@ts-expect-error
@inline
export function fe_0 (h: FieldElement): void {
- memory.fill(changetype<usize>(h), 0, 64)
+ memory.fill(changetype<usize>(h), 0, 40)
}
/**
bi = v128.load(b, 16)
v128.store(o, v128.add<i32>(ai, bi), 16)
- ai = v128.load(a, 32)
- bi = v128.load(b, 32)
- v128.store(o, v128.add<i32>(ai, bi), 32)
-
- ai = v128.load(a, 48)
- bi = v128.load(b, 48)
- v128.store(o, v128.add<i32>(ai, bi), 48)
+ ai = i64x2(load<i64>(a + 32), 0)
+ bi = i64x2(load<i64>(b + 32), 0)
+ v128.store_lane<i64>(o, v128.add<i32>(ai, bi), 0, 32)
}
/**
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<i64>(p + 32), 0)
+ qi = i64x2(load<i64>(q + 32), 0)
+ v128.store_lane<u64>(p, v128.bitselect(qi, pi, c), 0, 32)
}
/**
//@ts-expect-error
@inline
export function fe_copy (h: FieldElement, f: FieldElement): void {
- memory.copy(changetype<usize>(h), changetype<usize>(f), 64)
+ memory.copy(changetype<usize>(h), changetype<usize>(f), 40)
}
/**
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<i64>(p + 32), 0)
+ qi = i64x2(load<i64>(q + 32), 0)
+ v128.store_lane<i64>(p, v128.bitselect(qi, pi, c), 0, 32)
+ v128.store_lane<i64>(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<usize>(h)
+ const a = changetype<usize>(f)
+
+ let ai = v128.load(a)
+ v128.store(o, v128.add<i32>(ai, ai))
+
+ ai = v128.load(a, 16)
+ v128.store(o, v128.add<i32>(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<i64>(a + 32), 0)
+ v128.store_lane<i64>(o, v128.add<i32>(ai, ai), 0, 32)
}
/**
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)
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)
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)
+/**
+ * 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 {
return s[0] & 1
}
-const multiply_t = new StaticArray<i64>(32)
+const fe_iszero_s = new StaticArray<u8>(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<i64>(24)
/**
* Multiply two FieldElements and store the product.
*
* @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<usize>(h)
const a = changetype<usize>(f)
const b = changetype<usize>(g)
const t = changetype<usize>(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<i32>(b + 32), load<i32>(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<i32>(load<i32>(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<i64>(pLo, i64x2(1, odd))
+ pHi = v128.mul<i64>(pHi, i64x2(1, odd))
let tLo = v128.load(ptr)
let tHi = v128.load(ptr + 16)
tLo = i64x2.add(tLo, pLo)
ptr += 32
pLo = i64x2.extmul_low_i32x4_s(ai, b4)
pHi = i64x2.extmul_high_i32x4_s(ai, b4)
+ pLo = v128.mul<i64>(pLo, i64x2(1, odd))
+ pHi = v128.mul<i64>(pHi, i64x2(1, odd))
tLo = v128.load(ptr)
tHi = v128.load(ptr + 16)
tLo = i64x2.add(tLo, pLo)
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<i64>(pLo, i64x2(1, odd))
+ pHi = v128.mul<i64>(pHi, i64x2(1, odd))
tLo = v128.load(ptr)
tHi = v128.load(ptr + 16)
tLo = i64x2.add(tLo, pLo)
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)
}
ai = v128.load(a, 16)
v128.store(o, v128.neg<i32>(ai), 16)
- ai = v128.load(a, 32)
- v128.store(o, v128.neg<i32>(ai), 32)
-
- ai = v128.load(a, 48)
- v128.store(o, v128.neg<i32>(ai), 48)
+ ai = i64x2(load<i64>(a + 32), 0)
+ v128.store_lane<i64>(o, v128.neg<i32>(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<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.
+const fe_pow22523_t0: FieldElement = fe()
+const fe_pow22523_t1: FieldElement = fe()
+const fe_pow22523_t2: FieldElement = fe()
+/**
+ * returns z^((p-5)/8) = z^(2^252-3)
+ * used to compute square roots since we have p=5 (mod 8); see Cohen and Frey.
+ */
+export function fe_pow22523 (out: FieldElement, z: FieldElement): void {
+ const t0 = fe_pow22523_t0
+ const t1 = fe_pow22523_t1
+ const t2 = fe_pow22523_t2
- Write x=r+19(2⁻²⁵⁵)r+y.
- Then 0 < x < 2²⁵⁵ so floor(2⁻²⁵⁵x) = 0 so floor(q+2⁻²⁵⁵x) = q.
+ fe_sq(t0, z)
+ fe_sq(t1, t0)
+ fe_sq(t1, t1)
+ fe_mul(t1, z, t1)
+ fe_mul(t0, t0, t1)
+ fe_sq(t0, t0)
+ fe_mul(t0, t1, t0)
+ fe_sq(t1, t0)
+ for (let i = 1; i < 5; ++i) {
+ fe_sq(t1, t1)
+ }
+ fe_mul(t0, t1, t0)
+ fe_sq(t1, t0)
+ for (let i = 1; i < 10; ++i) {
+ fe_sq(t1, t1)
+ }
+ fe_mul(t1, t1, t0)
+ fe_sq(t2, t1)
+ for (let i = 1; i < 20; ++i) {
+ fe_sq(t2, t2)
+ }
+ fe_mul(t1, t2, t1)
+ for (let i = 1; i < 11; ++i) {
+ fe_sq(t1, t1)
+ }
+ fe_mul(t0, t1, t0)
+ fe_sq(t1, t0)
+ for (let i = 1; i < 50; ++i) {
+ fe_sq(t1, t1)
+ }
+ fe_mul(t1, t1, t0)
+ fe_sq(t2, t1)
+ for (let i = 1; i < 100; ++i) {
+ fe_sq(t2, t2)
+ }
+ fe_mul(t1, t2, t1)
+ for (let i = 1; i < 51; ++i) {
+ fe_sq(t1, t1)
+ }
+ fe_mul(t0, t1, t0)
+ fe_sq(t0, t0)
+ fe_sq(t0, t0)
+ fe_mul(out, t0, z)
+}
- Have q+2⁻²⁵⁵x = 2⁻²⁵⁵(h + 19 2^(-25) h9 + 2^(-1))
- so floor(2⁻²⁵⁵(h + 19 2^(-25) h9 + 2^(-1))) = q.
-*/
+/**
+ * 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 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 = (19 * h9 + (u32(1) << 24)) >> 25
q = (h0 + q) >> 26
q = (h1 + q) >> 25
q = (h2 + q) >> 26
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
bi = v128.load(b, 16)
v128.store(o, v128.sub<i32>(ai, bi), 16)
- ai = v128.load(a, 32)
- bi = v128.load(b, 32)
- v128.store(o, v128.sub<i32>(ai, bi), 32)
-
- ai = v128.load(a, 48)
- bi = v128.load(b, 48)
- v128.store(o, v128.sub<i32>(ai, bi), 48)
+ ai = i64x2(load<i64>(a + 32), 0)
+ bi = i64x2(load<i64>(b + 32), 0)
+ v128.store_lane<i64>(o, v128.sub<i32>(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;
*
* Goal: Output h0+...+2²³⁰ h9.
*/
-const t: FieldElement = fe()
export function fe_tobytes (s: StaticArray<u8>, 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)
//@ts-expect-error
@inline
function normalize (o: usize, t: usize): void {
- // reduce
+ // reduce from 20 limbs to 10
let x = load<i64>(t)
- let y = load<i64>(t, 128)
- store<i64>(t, x + (38 * y))
+ let y = load<i64>(t, 80)
+ store<i64>(t, x + (19 * y))
x = load<i64>(t, 8)
- y = load<i64>(t, 136)
- store<i64>(t, x + (38 * y), 8)
+ y = load<i64>(t, 88)
+ store<i64>(t, x + (19 * y), 8)
x = load<i64>(t, 16)
- y = load<i64>(t, 144)
- store<i64>(t, x + (38 * y), 16)
+ y = load<i64>(t, 96)
+ store<i64>(t, x + (19 * y), 16)
x = load<i64>(t, 24)
- y = load<i64>(t, 152)
- store<i64>(t, x + (38 * y), 24)
+ y = load<i64>(t, 104)
+ store<i64>(t, x + (19 * y), 24)
x = load<i64>(t, 32)
- y = load<i64>(t, 160)
- store<i64>(t, x + (38 * y), 32)
+ y = load<i64>(t, 112)
+ store<i64>(t, x + (19 * y), 32)
x = load<i64>(t, 40)
- y = load<i64>(t, 168)
- store<i64>(t, x + (38 * y), 40)
+ y = load<i64>(t, 120)
+ store<i64>(t, x + (19 * y), 40)
x = load<i64>(t, 48)
- y = load<i64>(t, 176)
- store<i64>(t, x + (38 * y), 48)
+ y = load<i64>(t, 128)
+ store<i64>(t, x + (19 * y), 48)
x = load<i64>(t, 56)
- y = load<i64>(t, 184)
- store<i64>(t, x + (38 * y), 56)
+ y = load<i64>(t, 136)
+ store<i64>(t, x + (19 * y), 56)
x = load<i64>(t, 64)
- y = load<i64>(t, 192)
- store<i64>(t, x + (38 * y), 64)
+ y = load<i64>(t, 144)
+ store<i64>(t, x + (19 * y), 64)
x = load<i64>(t, 72)
- y = load<i64>(t, 200)
- store<i64>(t, x + (38 * y), 72)
-
- x = load<i64>(t, 80)
- y = load<i64>(t, 208)
- store<i64>(t, x + (38 * y), 80)
-
- x = load<i64>(t, 88)
- y = load<i64>(t, 216)
- store<i64>(t, x + (38 * y), 88)
-
- x = load<i64>(t, 96)
- y = load<i64>(t, 224)
- store<i64>(t, x + (38 * y), 96)
-
- x = load<i64>(t, 104)
- y = load<i64>(t, 232)
- store<i64>(t, x + (38 * y), 104)
-
- x = load<i64>(t, 112)
- y = load<i64>(t, 240)
- store<i64>(t, x + (38 * y), 112)
+ y = load<i64>(t, 152)
+ store<i64>(t, x + (19 * y), 72)
// first carry
- let c: i64 = load<i64>(t)
- store<i64>(t, c & 0xFFFF)
- c >>= 16
-
- c += load<i64>(t, 8)
- store<i64>(t, c & 0xFFFF, 8)
- c >>= 16
-
- c += load<i64>(t, 16)
- store<i64>(t, c & 0xFFFF, 16)
- c >>= 16
-
- c += load<i64>(t, 24)
- store<i64>(t, c & 0xFFFF, 24)
- c >>= 16
-
- c += load<i64>(t, 32)
- store<i64>(t, c & 0xFFFF, 32)
- c >>= 16
-
- c += load<i64>(t, 40)
- store<i64>(t, c & 0xFFFF, 40)
- c >>= 16
-
- c += load<i64>(t, 48)
- store<i64>(t, c & 0xFFFF, 48)
- c >>= 16
-
- c += load<i64>(t, 56)
- store<i64>(t, c & 0xFFFF, 56)
- c >>= 16
-
- c += load<i64>(t, 64)
- store<i64>(t, c & 0xFFFF, 64)
- c >>= 16
-
- c += load<i64>(t, 72)
- store<i64>(t, c & 0xFFFF, 72)
- c >>= 16
-
- c += load<i64>(t, 80)
- store<i64>(t, c & 0xFFFF, 80)
- c >>= 16
-
- c += load<i64>(t, 88)
- store<i64>(t, c & 0xFFFF, 88)
- c >>= 16
-
- c += load<i64>(t, 96)
- store<i64>(t, c & 0xFFFF, 96)
- c >>= 16
-
- c += load<i64>(t, 104)
- store<i64>(t, c & 0xFFFF, 104)
- c >>= 16
-
- c += load<i64>(t, 112)
- store<i64>(t, c & 0xFFFF, 112)
- c >>= 16
-
- c += load<i64>(t, 120)
- store<i64>(t, c & 0xFFFF, 120)
- c >>= 16
-
- store<i64>(t, load<i64>(t) + (38 * c))
-
- // second carry and assign result to output
- c = load<i64>(t)
- store<i32>(o, c & 0xFFFF)
- c >>= 16
-
- c += load<i64>(t, 8)
- store<i32>(o, c & 0xFFFF, 4)
- c >>= 16
-
- c += load<i64>(t, 16)
- store<i32>(o, c & 0xFFFF, 8)
- c >>= 16
-
- c += load<i64>(t, 24)
- store<i32>(o, c & 0xFFFF, 12)
- c >>= 16
-
- c += load<i64>(t, 32)
- store<i32>(o, c & 0xFFFF, 16)
- c >>= 16
-
- c += load<i64>(t, 40)
- store<i32>(o, c & 0xFFFF, 20)
- c >>= 16
-
- c += load<i64>(t, 48)
- store<i32>(o, c & 0xFFFF, 24)
- c >>= 16
-
- c += load<i64>(t, 56)
- store<i32>(o, c & 0xFFFF, 28)
- c >>= 16
-
- c += load<i64>(t, 64)
- store<i32>(o, c & 0xFFFF, 32)
- c >>= 16
-
- c += load<i64>(t, 72)
- store<i32>(o, c & 0xFFFF, 36)
- c >>= 16
-
- c += load<i64>(t, 80)
- store<i32>(o, c & 0xFFFF, 40)
- c >>= 16
-
- c += load<i64>(t, 88)
- store<i32>(o, c & 0xFFFF, 44)
- c >>= 16
-
- c += load<i64>(t, 96)
- store<i32>(o, c & 0xFFFF, 48)
- c >>= 16
-
- c += load<i64>(t, 104)
- store<i32>(o, c & 0xFFFF, 52)
- c >>= 16
-
- c += load<i64>(t, 112)
- store<i32>(o, c & 0xFFFF, 56)
- c >>= 16
-
- c += load<i64>(t, 120)
- store<i32>(o, c & 0xFFFF, 60)
- c >>= 16
-
- store<i64>(o, load<i64>(o) + (38 * c))
+ let v: i64 = load<i64>(t, 0)
+ let c: i64 = (v + (1 << 25)) >> 26
+ store<i64>(t, v - (c << 26), 0)
+
+ v = load<i64>(t, 8) + c
+ c = (v + (1 << 24)) >> 25
+ store<i64>(t, v - (c << 25), 8)
+
+ v = load<i64>(t, 16) + c
+ c = (v + (1 << 25)) >> 26
+ store<i64>(t, v - (c << 26), 16)
+
+ v = load<i64>(t, 24) + c
+ c = (v + (1 << 24)) >> 25
+ store<i64>(t, v - (c << 25), 24)
+
+ v = load<i64>(t, 32) + c
+ c = (v + (1 << 25)) >> 26
+ store<i64>(t, v - (c << 26), 32)
+
+ v = load<i64>(t, 40) + c
+ c = (v + (1 << 24)) >> 25
+ store<i64>(t, v - (c << 25), 40)
+
+ v = load<i64>(t, 48) + c
+ c = (v + (1 << 25)) >> 26
+ store<i64>(t, v - (c << 26), 48)
+
+ v = load<i64>(t, 56) + c
+ c = (v + (1 << 24)) >> 25
+ store<i64>(t, v - (c << 25), 56)
+
+ v = load<i64>(t, 64) + c
+ c = (v + (1 << 25)) >> 26
+ store<i64>(t, v - (c << 26), 64)
+
+ v = load<i64>(t, 72) + c
+ c = (v + (1 << 24)) >> 25
+ store<i64>(t, v - (c << 25), 72)
+
+ // fold final carry back to start then do second carry and assign to output
+ v = load<i64>(t, 0) + (19 * c)
+ c = (v + (1 << 25)) >> 26
+ store<i32>(o, v - (c << 26), 0)
+
+ v = load<i64>(t, 8) + c
+ c = (v + (1 << 24)) >> 25
+ store<i32>(o, v - (c << 25), 4)
+
+ v = load<i64>(t, 16) + c
+ c = (v + (1 << 25)) >> 26
+ store<i32>(o, v - (c << 26), 8)
+
+ v = load<i64>(t, 24) + c
+ c = (v + (1 << 24)) >> 25
+ store<i32>(o, v - (c << 25), 12)
+
+ v = load<i64>(t, 32) + c
+ c = (v + (1 << 25)) >> 26
+ store<i32>(o, v - (c << 26), 16)
+
+ v = load<i64>(t, 40) + c
+ c = (v + (1 << 24)) >> 25
+ store<i32>(o, v - (c << 25), 20)
+
+ v = load<i64>(t, 48) + c
+ c = (v + (1 << 25)) >> 26
+ store<i32>(o, v - (c << 26), 24)
+
+ v = load<i64>(t, 56) + c
+ c = (v + (1 << 24)) >> 25
+ store<i32>(o, v - (c << 25), 28)
+
+ v = load<i64>(t, 64) + c
+ c = (v + (1 << 25)) >> 26
+ store<i32>(o, v - (c << 26), 32)
+
+ v = load<i64>(t, 72) + c
+ c = (v + (1 << 24)) >> 25
+ store<i32>(o, v - (c << 25), 36)
}
//! SPDX-FileCopyrightText: 2026 Chris Duncan <chris@codecow.com>
//! 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.
*
* - 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()
T2d: FieldElement = fe()
}
+//@ts-expect-error
+@inline
+function ge_cached_8 (): StaticArray<ge_cached> {
+ return StaticArray.fromArray<ge_cached>([
+ 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 {
//@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<ge_precomp>, b: i8): void {
const minust = ge25519_cmov8_minust
const bnegative: u8 = negative(b)
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)
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)
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)
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)
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_pi: StaticArray<ge_cached> = ge_cached_8()
const ge_scalarmult_t: ge_cached = new ge_cached()
/**
* `h = a * p`
ge_p1p1_to_p3(h, r)
}
-const ge_scalarmult_base_e: StaticArray<u8> = new StaticArray<u8>(64)
+const ge_scalarmult_base_e: StaticArray<i8> = new StaticArray<i8>(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)
*
*/
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
const s = ge_scalarmult_base_s
const t = ge_scalarmult_base_t
/* 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 */
/**
* true if `s < p`
*/
-export function ge_is_canonical (s: StaticArray<u8>): boolean {
- let c: u32 = (s[31] & 0x7f) ^ 0x7f
+export function ge_is_canonical (s: StaticArray<u8>): 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<u8>): 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<u8>): 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<i8> = new StaticArray<i8>(256)
+const ge_double_scalarmult_vartime_bslide: StaticArray<i8> = new StaticArray<i8>(256)
+const ge_double_scalarmult_vartime_Ai: StaticArray<ge_cached> = 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<u8>, A: ge_p3, b: StaticArray<u8>): 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<i8>, a: StaticArray<u8>): 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)
}
//! SPDX-License-Identifier: GPL-3.0-or-later\r
\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 { ge_is_canonical, ge_p3, ge_p3_0 } from './ge'\r
+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'\r
import { sc_is_canonical, sc_muladd, sc_reduce } from './sc'\r
\r
const BLOCKHASH_BYTES: i32 = 32\r
const SIGNATURE_BYTES: i32 = 64\r
const SIGNEDBLOCKHASH_BYTES: i32 = SIGNATURE_BYTES + BLOCKHASH_BYTES\r
\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
- __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 I: FieldElement = fe([0xa0b0, 0x4a0e, 0x1b27, 0xc4ee, 0xe478, 0xad2f, 0x1806, 0x2f43, 0xd7a7, 0x3dfb, 0x0099, 0x2b4d, 0xdf0b, 0x4fc1, 0x2480, 0x2b83])\r
-\r
// Static I/O buffers\r
const INPUT_BUFFER = memory.data(128)\r
const OUTPUT_BUFFER = memory.data(64)\r
\r
-// a, b = 32 bytes\r
-//@ts-expect-error\r
-@inline\r
-function bitsdiff<T> (a: StaticArray<T>, b: StaticArray<T>): 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
-// o, a = StaticArray<i32>[16]\r
-const pow_c: FieldElement = fe()\r
-function pow2523 (o: FieldElement, a: FieldElement): void {\r
- const c = pow_c\r
- fe_copy(c, a)\r
-\r
- for (let i = 0; i < 249; i++) {\r
- fe_sq(c, c)\r
- fe_mul(c, c, a)\r
- }\r
- fe_sq(c, c)\r
- fe_sq(c, c)\r
- fe_mul(c, c, a)\r
-\r
- fe_copy(o, c)\r
-}\r
-\r
-function car25519 (o: FieldElement): void {\r
- let c: i32 = 0\r
- for (let i = 0; i < 16; i++) {\r
- c += o[i]\r
- o[i] = c & 0xFFFF\r
- c >>= 16\r
- }\r
- o[0] += 38 * c\r
-}\r
-\r
-const inv_c: FieldElement = fe()\r
-function inv25519 (o: FieldElement, a: FieldElement): void {\r
- const c = inv_c\r
- fe_copy(c, a)\r
-\r
- for (let i = 0; i < 249; i++) {\r
- fe_sq(c, c)\r
- fe_mul(c, c, a)\r
- }\r
- fe_sq(c, c)\r
- fe_sq(c, c)\r
- fe_mul(c, c, a)\r
- fe_sq(c, c)\r
- fe_sq(c, c)\r
- fe_mul(c, c, a)\r
- fe_sq(c, c)\r
- fe_mul(c, c, a)\r
-\r
- fe_copy(o, c)\r
-}\r
-\r
-// a, b = StaticArray<i32>[16]\r
-const neq_c = new StaticArray<u8>(32)\r
-const neq_d = new StaticArray<u8>(32)\r
-function neq25519 (a: FieldElement, b: FieldElement): bool {\r
- const c = neq_c\r
- const d = neq_d\r
- pack25519(c, a)\r
- pack25519(d, b)\r
- return bitsdiff(c, d)\r
-}\r
-\r
-const pack_m: FieldElement = fe()\r
-const pack_t: FieldElement = fe()\r
-function pack25519 (o: StaticArray<u8>, n: FieldElement): void {\r
- const m = pack_m\r
- const t = pack_t\r
- let b: i32 = 0\r
- memory.copy(changetype<usize>(t), changetype<usize>(n), 64)\r
-\r
- car25519(t)\r
- car25519(t)\r
- car25519(t)\r
-\r
- for (let j = 0; j < 2; j++) {\r
- m[0] = t[0] - 0xffed\r
- for (let i = 1; i < 15; i++) {\r
- m[i] = t[i] - 0xffff - ((m[i - 1] >> 16) & 1)\r
- m[i - 1] &= 0xffff\r
- }\r
- m[15] = t[15] - 0x7fff - ((m[14] >> 16) & 1)\r
- b = (m[15] >> 16) & 1\r
- m[14] &= 0xffff\r
- fe_cswap(t, m, 1 - b)\r
- }\r
-\r
- for (let i = 0; i < 16; i++) {\r
- o[(i << 1) + 0] = u8(t[i] & 0xff)\r
- o[(i << 1) + 1] = u8(t[i] >> 8)\r
- }\r
-}\r
-\r
-const par_d = new StaticArray<u8>(32)\r
-function par25519 (a: FieldElement): u8 {\r
- const d = par_d\r
- pack25519(d, a)\r
- return d[0] & 1\r
-}\r
-\r
-function unpack25519 (o: FieldElement, n: StaticArray<u8>): void {\r
- for (let i = 0; i < 16; i++) {\r
- o[i] = i32(n[i << 1]) + (i32(n[(i << 1) + 1]) << 8)\r
- }\r
- o[15] &= (1 << 15) - 1\r
-}\r
-\r
-/**\r
- * Reusable buffers for point addition and doubling.\r
- */\r
-const a: FieldElement = fe()\r
-const b: FieldElement = fe()\r
-const c: FieldElement = fe()\r
-const d: FieldElement = fe()\r
-const e: FieldElement = fe()\r
-const f: FieldElement = fe()\r
-const g: FieldElement = fe()\r
-const h: FieldElement = fe()\r
-const t: FieldElement = fe()\r
-\r
-/**\r
- * Curve point addition in extended projective coordinates.\r
- * A = (Y1-X1)*(Y2-X2), B = (Y1+X1)*(Y2+X2), C = (T1*2*d)*T2, D = (Z1*2)*Z2\r
- * E = B-A, F = D-C, G = D+C, H = B+A\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: 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
- fe_add(b, p.X, p.Y)\r
- fe_add(t, q.X, q.Y)\r
- fe_mul(b, b, t)\r
- fe_mul(c, p.T, q.T)\r
- fe_mul(c, c, D2)\r
- fe_mul(d, p.Z, q.Z)\r
- fe_add(d, d, d)\r
-\r
- fe_sub(e, b, a)\r
- fe_sub(f, d, c)\r
- fe_add(g, d, c)\r
- fe_add(h, b, a)\r
-\r
- fe_mul(p.X, e, f)\r
- fe_mul(p.Y, h, g)\r
- fe_mul(p.Z, g, f)\r
- fe_mul(p.T, e, h)\r
-}\r
-\r
-/**\r
- * Doubles a curve point in projective coordinates.\r
- * A = X², B = Y², C = 2*Z², D = -A (in mod p, negate is p-A; tweetnacl does 0-A)\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: ge_p3): void {\r
- fe_sq(a, p.X)\r
- fe_sq(b, p.Y)\r
- fe_sq(c, p.Z)\r
- fe_add(c, c, c)\r
- fe_neg(d, a)\r
-\r
- fe_add(e, p.X, p.Y)\r
- fe_sq(e, e)\r
- fe_sub(e, e, a)\r
- fe_sub(e, e, b)\r
- fe_add(g, d, b)\r
- fe_sub(f, g, c)\r
- fe_sub(h, d, b)\r
-\r
- fe_mul(p.X, e, f)\r
- fe_mul(p.Y, g, h)\r
- fe_mul(p.Z, f, g)\r
- fe_mul(p.T, e, h)\r
-}\r
-\r
-// p, q = StaticArray<i32>[16]\r
-// offsets i32x16 = 64 bytes\r
-//@ts-expect-error\r
-@inline\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
- fe_cswap(p.T, q.T, b)\r
-}\r
-\r
-const tx: FieldElement = fe()\r
-const ty: FieldElement = fe()\r
-const zi: FieldElement = fe()\r
-/**\r
- * Convert projective coordinates to a byte array.\r
- *\r
- * p = StaticArray<i32>[4][16] = (X,Y,Z,T) as 16 limbs each\r
-*/\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
- pack25519(r, ty)\r
- r[31] ^= par25519(tx) << 7\r
-}\r
-\r
-// p, q = StaticArray<i32>[4][16]\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
- add(q, p)\r
- double(p)\r
- cswap(p, q, b)\r
- }\r
-}\r
-\r
-// p = StaticArray<i32>[4][16]\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
- fe_copy(q.Z, BASE.Z)\r
- fe_copy(q.T, BASE.T)\r
- scalarmult(p, q, s)\r
-}\r
-\r
-const unpack_chk: FieldElement = fe()\r
-const unpack_num: FieldElement = fe()\r
-const unpack_den = new ge_p3()\r
-const unpack_t: FieldElement = fe()\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
- const t = unpack_t\r
-\r
- unpack25519(r.Y, p)\r
- fe_sq(num, r.Y)\r
- fe_mul(den.X, num, D)\r
- fe_sub(num, num, r.Z)\r
- fe_add(den.X, r.Z, den.X)\r
-\r
- fe_sq(den.Y, den.X)\r
- fe_sq(den.Z, den.Y)\r
- fe_mul(den.T, den.Z, den.Y)\r
- fe_mul(t, den.T, num)\r
- fe_mul(t, t, den.X)\r
-\r
- pow2523(t, t)\r
- fe_mul(t, t, num)\r
- fe_mul(t, t, den.X)\r
- fe_mul(t, t, den.X)\r
- fe_mul(r.X, t, den.X)\r
-\r
- fe_sq(chk, r.X)\r
- fe_mul(chk, chk, den.X)\r
- if (neq25519(chk, num)) {\r
- fe_mul(r.X, r.X, I)\r
- }\r
-\r
- fe_sq(chk, r.X)\r
- fe_mul(chk, chk, den.X)\r
-\r
- if (neq25519(chk, num)) {\r
- return -1\r
- }\r
-\r
- if (par25519(r.X) === (p[31] >> 7)) {\r
- fe_neg(r.X, r.X)\r
- }\r
- fe_mul(r.T, r.X, r.Y)\r
- return 0\r
-}\r
-\r
//@ts-expect-error\r
@inline\r
function clamp (k: StaticArray<u8>): void {\r
}\r
\r
const crypto_derive_A = new ge_p3()\r
-function crypto_derive (pk: StaticArray<u8>, sk: StaticArray<u8>): void {\r
+function crypto_derive (pk: StaticArray<u8>, sk: StaticArray<u8>, seed: StaticArray<u8>): void {\r
const A = crypto_derive_A\r
ge_p3_0(A)\r
\r
- crypto_hash(sk, sk)\r
+ crypto_hash(sk, seed)\r
clamp(sk)\r
\r
trace(`seed hashed to sk: ${sk.map((b: u8) => b.toString(16).padStart(2, '0')).join('')}`)\r
\r
- scalarbase(A, sk)\r
-\r
- trace(`sk scalarbase to A.X: ${A.X.map((b: i32) => b.toString(16).padStart(8, '0')).join('')}`)\r
-\r
- pack(pk, A)\r
-\r
- trace(`A packed to pk: ${pk.map((b: u8) => b.toString(16).padStart(2, '0')).join('')}`)\r
+ ge_scalarmult_base(A, sk)\r
+ ge_p3_tobytes(pk, A)\r
+ memory.copy(changetype<usize>(sk), changetype<usize>(seed), PRIVATEKEY_BYTES)\r
+ memory.copy(changetype<usize>(sk) + 32, changetype<usize>(pk), PUBLICKEY_BYTES)\r
}\r
\r
const crypto_sign_az = new StaticArray<u8>(64)\r
sc_reduce(nonce)\r
\r
// Compute R = rB\r
- scalarbase(R, nonce)\r
- pack(s, R)\r
+ ge_scalarmult_base(R, nonce)\r
+ ge_p3_tobytes(s, R)\r
\r
// Concatenate public key `A` and message `M`\r
// from parameter arguments: A = sk[0,32], M = m\r
nonce.fill(0)\r
}\r
\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_ram = new StaticArray<u8>(SIGNEDBLOCKHASH_BYTES)\r
-const crypto_verify_open_S = new StaticArray<u8>(32)\r
+const crypto_verify_h = new StaticArray<u8>(64)\r
+const crypto_verify_check = new ge_p3()\r
+const crypto_verify_expected_r = new ge_p3()\r
+const crypto_verify_A = new ge_p3()\r
+const crypto_verify_sb_ah = new ge_p3()\r
+const crypto_verify_sb_ah_p2 = new ge_p2()\r
+const crypto_verify_ram = new StaticArray<u8>(SIGNEDBLOCKHASH_BYTES)\r
+const crypto_verify_S = new StaticArray<u8>(32)\r
/**\r
-* Verify signature `s` was made by signing message `m` using public key `pk`.\r
-*/\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 ram = crypto_verify_open_ram\r
- const S = crypto_verify_open_S\r
+ * Verify signature `s` was made by signing message `m` using public key `pk`.\r
+ */\r
+function crypto_verify (s: StaticArray<u8>, m: StaticArray<u8>, pk: StaticArray<u8>): i32 {\r
+ const h = crypto_verify_h\r
+ const check = crypto_verify_check\r
+ const expected_r = crypto_verify_expected_r\r
+ const A = crypto_verify_A\r
+ const sb_ah = crypto_verify_sb_ah\r
+ const sb_ah_p2 = crypto_verify_sb_ah_p2\r
+ const ram = crypto_verify_ram\r
+ const S = crypto_verify_S\r
\r
// fail if public key `k` is non-canonical (`p = 2²⁵⁵-19 <= k`)\r
- if (!ge_is_canonical(pk)) return false\r
+ if (!ge_is_canonical(pk)) return -1\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
+ if (!sc_is_canonical(S)) return -1\r
+\r
+ if (ge_frombytes_negate_vartime(A, pk) != 0) return -1\r
+ if (ge_has_small_order(A) != 0) return -1\r
\r
- // fail\r
- if (unpackneg(q, pk)) return false\r
+ if (ge_frombytes(expected_r, s) != 0) return -1\r
+ if (ge_has_small_order(expected_r) != 0) return -1\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
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
+ crypto_hash(h, ram)\r
+ sc_reduce(h)\r
\r
- scalarmult(p, q, hram)\r
- ge_p3_0(q)\r
- scalarbase(q, S)\r
- add(p, q)\r
- pack(t, p)\r
+ ge_double_scalarmult_vartime(sb_ah_p2, h, A, S)\r
+ ge_p2_to_p3(sb_ah, sb_ah_p2)\r
+ ge_p3_sub(check, expected_r, sb_ah)\r
\r
- // fail if any bit differs from encoded nonce point R, else pass\r
- return !bitsdiff(s, t)\r
+ return ge_has_small_order(check) - 1\r
}\r
\r
// Returns the pointer to the static input buffer (128 bytes).\r
}\r
\r
const derive_pk = new StaticArray<u8>(PUBLICKEY_BYTES)\r
-const derive_sk = new StaticArray<u8>(PRIVATEKEY_BYTES)\r
+const derive_sk = new StaticArray<u8>(SECRETKEY_BYTES)\r
+const derive_seed = 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
+ * 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 pk = derive_pk\r
const sk = derive_sk\r
+ const seed = derive_seed\r
for (let i = 0; i < PRIVATEKEY_BYTES; i++) {\r
- sk[i] = load<u8>(INPUT_BUFFER + i)\r
+ seed[i] = load<u8>(INPUT_BUFFER + i)\r
}\r
\r
const start = performance.now()\r
- crypto_derive(pk, sk)\r
+ crypto_derive(pk, sk, seed)\r
const end = performance.now()\r
trace('derive time', 1, end - start)\r
\r
const sign_sk = new StaticArray<u8>(SECRETKEY_BYTES)\r
const sign_s = new StaticArray<u8>(SIGNEDBLOCKHASH_BYTES)\r
/**\r
-* Signs the message using the private key and writes the signature to the static\r
-* output buffer. This mirrors the functionality of `nacl.sign.detached()`.\r
-*\r
-* @param {u64} h0-h3 - Message to sign (32 bytes as 4 × u64)\r
-* @param {u64} k0-k7 - Secret key (32 bytes private key + 32 bytes public key as 8 × u64)\r
-*/\r
+ * Signs the message using the private key and writes the signature to the static\r
+ * output buffer. This mirrors the functionality of `nacl.sign.detached()`.\r
+ *\r
+ * @param {u64} h0-h3 - Message to sign (32 bytes as 4 × u64)\r
+ * @param {u64} k0-k7 - Secret key (32 bytes private key + 32 bytes public key as 8 × u64)\r
+ */\r
export function sign (): void {\r
const h = sign_h\r
const sk = sign_sk\r
const verify_s = new StaticArray<u8>(SIGNATURE_BYTES)\r
const verify_k = new StaticArray<u8>(PUBLICKEY_BYTES)\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} 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
+ * 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} 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
+ */\r
+export function verify (): void {\r
const s = verify_s\r
const h = verify_h\r
const k = verify_k\r
}\r
\r
const start = performance.now()\r
- const result = crypto_verify(s, h, k)\r
+ const v = crypto_verify(s, h, k)\r
const end = performance.now()\r
trace('verify time', 1, end - start)\r
\r
- return result ? 1 : 0\r
+ store<u8>(OUTPUT_BUFFER, v)\r
}\r
/**
* Scalar operations modulo `L` where `L` is the group order of Curve25519.
*/
-
import { load_3, load_4 } from './utils'
/**
* - 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 {
+export function sc_is_canonical (S: StaticArray<u8>): bool {
if ((S[31] & 0xF0) == 0) return true
if ((S[31] & 0xE0) != 0) return false
//! SPDX-FileCopyrightText: 2026 Chris Duncan <chris@codecow.com>
//! SPDX-License-Identifier: GPL-3.0-or-later
-const optblocker_u8: u8 = 0
+export const optblocker_u8: u8 = 0
//@ts-expect-error
@inline
//@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<u8>, nlen: i32): u8 {
+ let d: u8 = 0
+ for (let i: i32 = 0; i < nlen; i++) {
+ d |= n[i]
+ }
+ return 1 & ((d - 1) >> 8)
+}
// 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)
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
let memory: WebAssembly.Memory
let derive: (k: Uint8Array) => Uint8Array<ArrayBuffer>
let sign: (h: Uint8Array, k: Uint8Array) => Uint8Array<ArrayBuffer>
- let verify: (s: Uint8Array, h: Uint8Array, k: Uint8Array) => boolean
+ let verify: (s: Uint8Array, h: Uint8Array, k: Uint8Array) => Uint8Array<ArrayBuffer>
async function setup (): Promise<void> {
try {
return s
}
- verify = function (s: Uint8Array, h: Uint8Array, k: Uint8Array): boolean {
+ verify = function (s: Uint8Array, h: Uint8Array, k: Uint8Array): Uint8Array<ArrayBuffer> {
// 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, 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
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) {
const NanoNaClWorker = `;await (${NanoNaCl})([${nacl}])`
/**
-* HOST CODE
-*/
-
+ * HOST CODE
+ */
// Initialize CPU
let isWorkerReady: boolean = false
let worker: Worker
*/
export async function verify (signature: string, blockHash: string, publicKey: string): Promise<boolean> {
const result = await run({ action: 'verify', signature, blockHash, publicKey })
- return result === '01'
+ return result === '00'
}