From: Chris Duncan Date: Wed, 30 Sep 2026 15:25:32 +0000 (-0700) Subject: Enable p generator and field element subtraction. X-Git-Url: https://git.codecow.com/?a=commitdiff_plain;h=0c8b50ddd9168e46b1a71ad9da4704be2c7bfc10;p=nano25519.git Enable p generator and field element subtraction. --- diff --git a/scripts/fe.gen.mjs b/scripts/fe.gen.mjs index 358b52e..3f62a70 100644 --- a/scripts/fe.gen.mjs +++ b/scripts/fe.gen.mjs @@ -19,10 +19,27 @@ */ export function fe_add (h, f, g) { return ` + // ${h} = ${f} + ${g} ${h}0 = v128.add(${f}0, ${g}0) ${h}1 = v128.add(${f}1, ${g}1) ${h}2 = v128.add(${f}2, ${g}2) - ` +` +} + +/** + * Subtract a FieldElement another and store the result. + * + * @param {string} h Variable name of FieldElement difference destination + * @param {string} f Variable name of FieldElement minuend source + * @param {string} g Variable name of FieldElement subtrahend source + */ +export function fe_sub (h, f, g) { + return ` + // ${h} = ${f} - ${g} + ${h}0 = v128.sub(${f}0, ${g}0) + ${h}1 = v128.sub(${f}1, ${g}1) + ${h}2 = v128.sub(${f}2, ${g}2) +` } export const FE = `//! SPDX-FileCopyrightText: 2026 Chris Duncan @@ -1433,12 +1450,26 @@ export function fe_sq2 (h: FieldElement, f: FieldElement): void { //@ts-expect-error @inline export function fe_sub (h: FieldElement, f: FieldElement, g: FieldElement): void { + const h_ptr = changetype(h) const f_ptr = changetype(f) const g_ptr = changetype(g) - const h_ptr = changetype(h) - v128.store(h_ptr, v128.sub(v128.load(f_ptr, 0), v128.load(g_ptr, 0)), 0) - v128.store(h_ptr, v128.sub(v128.load(f_ptr, 16), v128.load(g_ptr, 16)), 16) - v128.store_lane(h_ptr, v128.sub(v128.load(f_ptr, 32), v128.load(g_ptr, 32)), 0, 32) + + let h0: v128, h1: v128, h2: v128 + + let f0 = v128.load(f_ptr, 0) + let f1 = v128.load(f_ptr, 16) + let f2 = v128.load(f_ptr, 32) + + let g0 = v128.load(g_ptr, 0) + let g1 = v128.load(g_ptr, 16) + let g2 = v128.load(g_ptr, 32) + + ${fe_sub('h', 'f', 'g')} + + // store h + v128.store(h_ptr, h0, 0) + v128.store(h_ptr, h1, 16) + v128.store_lane(h_ptr, h2, 0, 32) } const fe_tobytes_t: FieldElement = fe() diff --git a/scripts/index.mjs b/scripts/index.mjs index bdec654..a264b9d 100644 --- a/scripts/index.mjs +++ b/scripts/index.mjs @@ -23,4 +23,4 @@ const stripNewline = s => s.replaceAll(/\n([\r\s]*\n){2,}/gm, '\n\n') await writeFile('src/assembly/ed25519/blake2b.ts', stripNewline(BLAKE2b)) await writeFile('src/assembly/ed25519/fe.ts', stripNewline(FE)) await writeFile('src/assembly/ed25519/ge.ts', stripNewline(GE)) -// await writeFile('src/assembly/ed25519/p.ts', stripNewline(P)) +await writeFile('src/assembly/ed25519/p.ts', stripNewline(P)) diff --git a/scripts/p.gen.mjs b/scripts/p.gen.mjs index d897f98..04c63d8 100644 --- a/scripts/p.gen.mjs +++ b/scripts/p.gen.mjs @@ -9,6 +9,8 @@ * AssemblyScript compiler and type checker. */ +import { fe_add, fe_sub } from './fe.gen.mjs' + export const P = `//! SPDX-FileCopyrightText: 2026 Chris Duncan //! SPDX-License-Identifier: GPL-3.0-or-later @@ -62,15 +64,66 @@ const ge_p2_dbl_t0: FieldElement = fe() //@ts-expect-error @inline export function ge_p2_dbl (r: ge_p1p1, p: ge_p2): void { - const t0 = ge_p2_dbl_t0 - fe_sq_vec(r.X, p.X, r.Z, p.Y) - fe_sq2(r.T, p.Z) - fe_add(r.Y, p.X, p.Y) - fe_sq(t0, r.Y) - fe_add(r.Y, r.Z, r.X) - fe_sub(r.Z, r.Z, r.X) - fe_sub(r.X, t0, r.Y) - fe_sub(r.T, r.T, r.Z) + const rX_ptr = changetype(r.X) + const rY_ptr = changetype(r.Y) + const rZ_ptr = changetype(r.Z) + const rT_ptr = changetype(r.T) + + const pX_ptr = changetype(p.X) + const pY_ptr = changetype(p.Y) + const pZ_ptr = changetype(p.Z) + + const rYsq = ge_p2_dbl_t0 + const rYsq_ptr = changetype(rYsq) + + fe_sq_vec(r.X, r.Z, p.X, p.Y) // rX = pX²; rY = pY² + fe_sq2(r.T, p.Z) // rT = pZ² + + // Start converting to performant inlined ops with locals to avoid load/store + let rX0 = v128.load(rX_ptr), rX1 = v128.load(rX_ptr, 16), rX2 = v128.load(rX_ptr, 32) + let rY0 = v128.load(rY_ptr), rY1 = v128.load(rY_ptr, 16), rY2 = v128.load(rY_ptr, 32) + let rZ0 = v128.load(rZ_ptr), rZ1 = v128.load(rZ_ptr, 16), rZ2 = v128.load(rZ_ptr, 32) + let rT0 = v128.load(rT_ptr), rT1 = v128.load(rT_ptr, 16), rT2 = v128.load(rT_ptr, 32) + + const pX0 = v128.load(pX_ptr), pX1 = v128.load(pX_ptr, 16), pX2 = v128.load(pX_ptr, 32) + const pY0 = v128.load(pY_ptr), pY1 = v128.load(pY_ptr, 16), pY2 = v128.load(pY_ptr, 32) + const pZ0 = v128.load(pZ_ptr), pZ1 = v128.load(pZ_ptr, 16), pZ2 = v128.load(pZ_ptr, 32) + + // rY = pX + pY + v128.store(rY_ptr, v128.add(pX0, pY0)) + v128.store(rY_ptr, v128.add(pX1, pY1), 16) + v128.store_lane(rY_ptr, v128.add(pX2, pY2), 0, 32) + + // t0 = r.Y² + fe_sq(rYsq, r.Y) + const rYsq0 = v128.load(rYsq_ptr, 0) + const rYsq1 = v128.load(rYsq_ptr, 16) + const rYsq2 = v128.load(rYsq_ptr, 32) + + ${fe_add('rY', 'rZ', 'rX')} + ${fe_sub('rZ', 'rZ', 'rX')} + ${fe_sub('rX', 'rYsq', 'rY')} + ${fe_sub('rT', 'rT', 'rZ')} + + // store r.X + v128.store(rX_ptr, rX0) + v128.store(rX_ptr, rX1, 16) + v128.store_lane(rX_ptr, rX2, 0, 32) + + // store r.Y + v128.store(rY_ptr, rY0) + v128.store(rY_ptr, rY1, 16) + v128.store_lane(rY_ptr, rY2, 0, 32) + + // store r.Z + v128.store(rZ_ptr, rZ0) + v128.store(rZ_ptr, rZ1, 16) + v128.store_lane(rZ_ptr, rZ2, 0, 32) + + // store r.T + v128.store(rT_ptr, rT0) + v128.store(rT_ptr, rT1, 16) + v128.store_lane(rT_ptr, rT2, 0, 32) } /** @@ -165,43 +218,192 @@ export function ge_precomp_0 (h: ge_precomp): void { fe_0(h.xy2d) } -const ge_add_cached_t0: FieldElement = fe() /** * r = p + q */ export function ge_add_cached (r: ge_p1p1, p: ge_p3, q: ge_cached): void { - const t0 = ge_add_cached_t0 - fe_add(r.X, p.Y, p.X) - fe_sub(r.Y, p.Y, p.X) + const rX_ptr = changetype(r.X) + const rY_ptr = changetype(r.Y) + const rZ_ptr = changetype(r.Z) + const rT_ptr = changetype(r.T) + + const pX_ptr = changetype(p.X) + const pY_ptr = changetype(p.Y) + + let pX0 = v128.load(pX_ptr), pX1 = v128.load(pX_ptr, 16), pX2 = v128.load(pX_ptr, 32) + let pY0 = v128.load(pY_ptr), pY1 = v128.load(pY_ptr, 16), pY2 = v128.load(pY_ptr, 32) + + // fe_add(r.X, p.Y, p.X) + let rX0 = v128.add(pY0, pX0) + let rX1 = v128.add(pY1, pX1) + let rX2 = v128.add(pY2, pX2) + + // fe_sub(r.Y, p.Y, p.X) + let rY0 = v128.sub(pY0, pX0) + let rY1 = v128.sub(pY1, pX1) + let rY2 = v128.sub(pY2, pX2) + + // store r.X + v128.store(rX_ptr, rX0) + v128.store(rX_ptr, rX1, 16) + v128.store_lane(rX_ptr, rX2, 0, 32) + + // store r.Y + v128.store(rY_ptr, rY0) + v128.store(rY_ptr, rY1, 16) + v128.store_lane(rY_ptr, rY2, 0, 32) + + // not yet converted to inline for size and complexity fe_mul(r.Z, r.X, q.YplusX) fe_mul(r.Y, r.Y, q.YminusX) fe_mul(r.T, q.T2d, p.T) fe_mul(r.X, p.Z, q.Z) - fe_dbl(t0, r.X) - fe_sub(r.X, r.Z, r.Y) - fe_add(r.Y, r.Z, r.Y) - fe_add(r.Z, t0, r.T) - fe_sub(r.T, t0, r.T) + + // remaining arithmetic done inline to avoid unnecessary load/store + rX0 = v128.load(rX_ptr), rX1 = v128.load(rX_ptr, 16), rX2 = v128.load(rX_ptr, 32) + rY0 = v128.load(rY_ptr), rY1 = v128.load(rY_ptr, 16), rY2 = v128.load(rY_ptr, 32) + let rZ0 = v128.load(rZ_ptr), rZ1 = v128.load(rZ_ptr, 16), rZ2 = v128.load(rZ_ptr, 32) + let rT0 = v128.load(rT_ptr), rT1 = v128.load(rT_ptr, 16), rT2 = v128.load(rT_ptr, 32) + + // fe_dbl(t0, r.X) + const t0 = v128.shl(rX0, 1) + const t1 = v128.shl(rX1, 1) + const t2 = v128.shl(rX2, 1) + + // fe_sub(r.X, r.Z, r.Y) + rX0 = v128.sub(rZ0, rY0) + rX1 = v128.sub(rZ1, rY1) + rX2 = v128.sub(rZ2, rY2) + + // fe_add(r.Y, r.Z, r.Y) + rY0 = v128.add(rZ0, rY0) + rY1 = v128.add(rZ1, rY1) + rY2 = v128.add(rZ2, rY2) + + // fe_add(r.Z, t, r.T) + rZ0 = v128.add(t0, rT0) + rZ1 = v128.add(t1, rT1) + rZ2 = v128.add(t2, rT2) + + // fe_sub(r.T, t, r.T) + rT0 = v128.sub(t0, rT0) + rT1 = v128.sub(t1, rT1) + rT2 = v128.sub(t2, rT2) + + // store r.X + v128.store(rX_ptr, rX0) + v128.store(rX_ptr, rX1, 16) + v128.store_lane(rX_ptr, rX2, 0, 32) + + // store r.Y + v128.store(rY_ptr, rY0) + v128.store(rY_ptr, rY1, 16) + v128.store_lane(rY_ptr, rY2, 0, 32) + + // store r.Z + v128.store(rZ_ptr, rZ0) + v128.store(rZ_ptr, rZ1, 16) + v128.store_lane(rZ_ptr, rZ2, 0, 32) + + // store r.T + v128.store(rT_ptr, rT0) + v128.store(rT_ptr, rT1, 16) + v128.store_lane(rT_ptr, rT2, 0, 32) } -const ge_add_precomp_t0: FieldElement = fe() /** * r = p + q */ //@ts-expect-error @inline export function ge_add_precomp (r: ge_p1p1, p: ge_p3, q: ge_precomp): void { - const t0 = ge_add_precomp_t0 - fe_add(r.X, p.Y, p.X) - fe_sub(r.Y, p.Y, p.X) + const rX_ptr = changetype(r.X) + const rY_ptr = changetype(r.Y) + const rZ_ptr = changetype(r.Z) + const rT_ptr = changetype(r.T) + + const pX_ptr = changetype(p.X) + const pY_ptr = changetype(p.Y) + const pZ_ptr = changetype(p.Z) + + const pX0 = v128.load(pX_ptr), pX1 = v128.load(pX_ptr, 16), pX2 = v128.load(pX_ptr, 32) + const pY0 = v128.load(pY_ptr), pY1 = v128.load(pY_ptr, 16), pY2 = v128.load(pY_ptr, 32) + + // fe_add(r.X, p.Y, p.X) + let rX0 = v128.add(pY0, pX0) + let rX1 = v128.add(pY1, pX1) + let rX2 = v128.add(pY2, pX2) + + // fe_sub(r.Y, p.Y, p.X) + let rY0 = v128.sub(pY0, pX0) + let rY1 = v128.sub(pY1, pX1) + let rY2 = v128.sub(pY2, pX2) + + // store r.X + v128.store(rX_ptr, rX0) + v128.store(rX_ptr, rX1, 16) + v128.store_lane(rX_ptr, rX2, 0, 32) + + // store r.Y + v128.store(rY_ptr, rY0) + v128.store(rY_ptr, rY1, 16) + v128.store_lane(rY_ptr, rY2, 0, 32) + + // not yet converted to inline for size and complexity fe_mul(r.Z, r.X, q.yplusx) fe_mul(r.Y, r.Y, q.yminusx) fe_mul(r.T, q.xy2d, p.T) - fe_dbl(t0, p.Z) - fe_sub(r.X, r.Z, r.Y) - fe_add(r.Y, r.Z, r.Y) - fe_add(r.Z, t0, r.T) - fe_sub(r.T, t0, r.T) + + // remaining arithmetic done inline to avoid unnecessary load/store + rX0 = v128.load(rX_ptr), rX1 = v128.load(rX_ptr, 16), rX2 = v128.load(rX_ptr, 32) + rY0 = v128.load(rY_ptr), rY1 = v128.load(rY_ptr, 16), rY2 = v128.load(rY_ptr, 32) + let rZ0 = v128.load(rZ_ptr), rZ1 = v128.load(rZ_ptr, 16), rZ2 = v128.load(rZ_ptr, 32) + let rT0 = v128.load(rT_ptr), rT1 = v128.load(rT_ptr, 16), rT2 = v128.load(rT_ptr, 32) + + // fe_dbl(t, p.Z) + const pZ0 = v128.shl(v128.load(pZ_ptr), 1) + const pZ1 = v128.shl(v128.load(pZ_ptr, 16), 1) + const pZ2 = v128.shl(v128.load(pZ_ptr, 32), 1) + + // fe_sub(r.X, r.Z, r.Y) + rX0 = v128.sub(rZ0, rY0) + rX1 = v128.sub(rZ1, rY1) + rX2 = v128.sub(rZ2, rY2) + + // fe_add(r.Y, r.Z, r.Y) + rY0 = v128.add(rZ0, rY0) + rY1 = v128.add(rZ1, rY1) + rY2 = v128.add(rZ2, rY2) + + // fe_add(r.Z, t, r.T) + rZ0 = v128.add(pZ0, rT0) + rZ1 = v128.add(pZ1, rT1) + rZ2 = v128.add(pZ2, rT2) + + // fe_sub(r.T, t, r.T) + rT0 = v128.sub(pZ0, rT0) + rT1 = v128.sub(pZ1, rT1) + rT2 = v128.sub(pZ2, rT2) + + // store r.X + v128.store(rX_ptr, rX0) + v128.store(rX_ptr, rX1, 16) + v128.store_lane(rX_ptr, rX2, 0, 32) + + // store r.Y + v128.store(rY_ptr, rY0) + v128.store(rY_ptr, rY1, 16) + v128.store_lane(rY_ptr, rY2, 0, 32) + + // store r.Z + v128.store(rZ_ptr, rZ0) + v128.store(rZ_ptr, rZ1, 16) + v128.store_lane(rZ_ptr, rZ2, 0, 32) + + // store r.T + v128.store(rT_ptr, rT0) + v128.store(rT_ptr, rT1, 16) + v128.store_lane(rT_ptr, rT2, 0, 32) } const ge_sub_p3_q_cached = new ge_cached() diff --git a/src/assembly/ed25519/fe.ts b/src/assembly/ed25519/fe.ts index acb4b1a..fc46ae7 100644 --- a/src/assembly/ed25519/fe.ts +++ b/src/assembly/ed25519/fe.ts @@ -79,6 +79,7 @@ export function fe_add (h: FieldElement, f: FieldElement, g: FieldElement): void let g1 = v128.load(g_ptr, 16) let g2 = v128.load(g_ptr, 32) + // h = f + g h0 = v128.add(f0, g0) h1 = v128.add(f1, g1) h2 = v128.add(f2, g2) @@ -1408,12 +1409,29 @@ export function fe_sq2 (h: FieldElement, f: FieldElement): void { //@ts-expect-error @inline export function fe_sub (h: FieldElement, f: FieldElement, g: FieldElement): void { + const h_ptr = changetype(h) const f_ptr = changetype(f) const g_ptr = changetype(g) - const h_ptr = changetype(h) - v128.store(h_ptr, v128.sub(v128.load(f_ptr, 0), v128.load(g_ptr, 0)), 0) - v128.store(h_ptr, v128.sub(v128.load(f_ptr, 16), v128.load(g_ptr, 16)), 16) - v128.store_lane(h_ptr, v128.sub(v128.load(f_ptr, 32), v128.load(g_ptr, 32)), 0, 32) + + let h0: v128, h1: v128, h2: v128 + + let f0 = v128.load(f_ptr, 0) + let f1 = v128.load(f_ptr, 16) + let f2 = v128.load(f_ptr, 32) + + let g0 = v128.load(g_ptr, 0) + let g1 = v128.load(g_ptr, 16) + let g2 = v128.load(g_ptr, 32) + + // h = f - g + h0 = v128.sub(f0, g0) + h1 = v128.sub(f1, g1) + h2 = v128.sub(f2, g2) + + // store h + v128.store(h_ptr, h0, 0) + v128.store(h_ptr, h1, 16) + v128.store_lane(h_ptr, h2, 0, 32) } const fe_tobytes_t: FieldElement = fe() diff --git a/src/assembly/ed25519/p.ts b/src/assembly/ed25519/p.ts index f560275..d859aa6 100644 --- a/src/assembly/ed25519/p.ts +++ b/src/assembly/ed25519/p.ts @@ -83,23 +83,26 @@ export function ge_p2_dbl (r: ge_p1p1, p: ge_p2): void { // t0 = r.Y² fe_sq(rYsq, r.Y) + const rYsq0 = v128.load(rYsq_ptr, 0) + const rYsq1 = v128.load(rYsq_ptr, 16) + const rYsq2 = v128.load(rYsq_ptr, 32) - // Y = Z + X + // rY = rZ + rX rY0 = v128.add(rZ0, rX0) rY1 = v128.add(rZ1, rX1) rY2 = v128.add(rZ2, rX2) - // Z = Z - X + // rZ = rZ - rX rZ0 = v128.sub(rZ0, rX0) rZ1 = v128.sub(rZ1, rX1) rZ2 = v128.sub(rZ2, rX2) - // X = Y^2 - Y - rX0 = v128.sub(v128.load(rYsq_ptr), rY0) - rX1 = v128.sub(v128.load(rYsq_ptr, 16), rY1) - rX2 = v128.sub(v128.load(rYsq_ptr, 32), rY2) + // rX = rYsq - rY + rX0 = v128.sub(rYsq0, rY0) + rX1 = v128.sub(rYsq1, rY1) + rX2 = v128.sub(rYsq2, rY2) - // T = T - Z + // rT = rT - rZ rT0 = v128.sub(rT0, rZ0) rT1 = v128.sub(rT1, rZ1) rT2 = v128.sub(rT2, rZ2)