Add Ed25519 scalar and point operations to WASM crypto backend (Phase 2)
Adds 16 operations via curve25519-dalek: scAdd, scSub, scMul, scMulAdd, scMulSub, scReduce32, scReduce64, scInvert, scCheck, scIsZero, scalarMultBase, scalarMultPoint, pointAddCompressed, pointSubCompressed, pointNegate, doubleScalarMultBase. Uses variable-time Straus algorithm for point multiplication. All 44 equivalence tests pass byte-for-byte. Benchmarks: scMulAdd 3.5x, scalarMultBase 2.3x, pointAdd 16.7x faster.
This commit is contained in:
@@ -9,6 +9,14 @@
|
||||
|
||||
import { keccak256 as jsKeccak } from '../keccak.js';
|
||||
import { blake2b as jsBlake2b } from '../blake2b.js';
|
||||
import {
|
||||
scAdd, scSub, scMul, scMulAdd, scMulSub,
|
||||
scReduce32, scReduce64, scInvert, scCheck, scIsZero
|
||||
} from '../transaction/serialization.js';
|
||||
import {
|
||||
scalarMultBase, scalarMultPoint, pointAddCompressed,
|
||||
pointSubCompressed, pointNegate, doubleScalarMultBase
|
||||
} from '../ed25519.js';
|
||||
|
||||
export class JsCryptoBackend {
|
||||
constructor() {
|
||||
@@ -26,4 +34,30 @@ export class JsCryptoBackend {
|
||||
blake2b(data, outLen, key) {
|
||||
return jsBlake2b(data, outLen, key);
|
||||
}
|
||||
|
||||
// Scalar ops
|
||||
scAdd(a, b) { return scAdd(a, b); }
|
||||
scSub(a, b) { return scSub(a, b); }
|
||||
scMul(a, b) { return scMul(a, b); }
|
||||
scMulAdd(a, b, c) { return scMulAdd(a, b, c); }
|
||||
scMulSub(a, b, c) { return scMulSub(a, b, c); }
|
||||
scReduce32(s) { return scReduce32(s); }
|
||||
scReduce64(s) { return scReduce64(s); }
|
||||
scInvert(a) { return scInvert(a); }
|
||||
scCheck(s) { return scCheck(s); }
|
||||
scIsZero(s) { return scIsZero(s); }
|
||||
|
||||
// Point ops
|
||||
scalarMultBase(s) { return scalarMultBase(s); }
|
||||
scalarMultPoint(s, p) { return scalarMultPoint(s, p); }
|
||||
pointAddCompressed(p, q) { return pointAddCompressed(p, q); }
|
||||
pointSubCompressed(p, q) { return pointSubCompressed(p, q); }
|
||||
pointNegate(p) { return pointNegate(p); }
|
||||
doubleScalarMultBase(a, p, b) {
|
||||
// JS doubleScalarMultBase expects decompressed point object, not bytes.
|
||||
// Compose from primitives instead: a*P + b*G
|
||||
const aP = scalarMultPoint(a, p);
|
||||
const bG = scalarMultBase(b);
|
||||
return pointAddCompressed(aP, bG);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,4 +56,24 @@ export class WasmCryptoBackend {
|
||||
}
|
||||
return this.wasm.blake2b_hash(data, outLen);
|
||||
}
|
||||
|
||||
// Scalar ops
|
||||
scAdd(a, b) { return this.wasm.sc_add(a, b); }
|
||||
scSub(a, b) { return this.wasm.sc_sub(a, b); }
|
||||
scMul(a, b) { return this.wasm.sc_mul(a, b); }
|
||||
scMulAdd(a, b, c) { return this.wasm.sc_mul_add(a, b, c); }
|
||||
scMulSub(a, b, c) { return this.wasm.sc_mul_sub(a, b, c); }
|
||||
scReduce32(s) { return this.wasm.sc_reduce32(s); }
|
||||
scReduce64(s) { return this.wasm.sc_reduce64(s); }
|
||||
scInvert(a) { return this.wasm.sc_invert(a); }
|
||||
scCheck(s) { return this.wasm.sc_check(s); }
|
||||
scIsZero(s) { return this.wasm.sc_is_zero(s); }
|
||||
|
||||
// Point ops
|
||||
scalarMultBase(s) { return this.wasm.scalar_mult_base(s); }
|
||||
scalarMultPoint(s, p) { return this.wasm.scalar_mult_point(s, p); }
|
||||
pointAddCompressed(p, q) { return this.wasm.point_add_compressed(p, q); }
|
||||
pointSubCompressed(p, q) { return this.wasm.point_sub_compressed(p, q); }
|
||||
pointNegate(p) { return this.wasm.point_negate(p); }
|
||||
doubleScalarMultBase(a, p, b) { return this.wasm.double_scalar_mult_base(a, p, b); }
|
||||
}
|
||||
|
||||
@@ -14,6 +14,10 @@ export {
|
||||
getCurrentBackendType,
|
||||
keccak256,
|
||||
blake2b,
|
||||
scAdd, scSub, scMul, scMulAdd, scMulSub,
|
||||
scReduce32, scReduce64, scInvert, scCheck, scIsZero,
|
||||
scalarMultBase, scalarMultPoint, pointAddCompressed,
|
||||
pointSubCompressed, pointNegate, doubleScalarMultBase,
|
||||
} from './provider.js';
|
||||
|
||||
// Backends (for direct access / testing)
|
||||
|
||||
@@ -68,3 +68,23 @@ export function keccak256(data) {
|
||||
export function blake2b(data, outLen, key) {
|
||||
return getCryptoBackend().blake2b(data, outLen, key);
|
||||
}
|
||||
|
||||
// Scalar ops
|
||||
export function scAdd(a, b) { return getCryptoBackend().scAdd(a, b); }
|
||||
export function scSub(a, b) { return getCryptoBackend().scSub(a, b); }
|
||||
export function scMul(a, b) { return getCryptoBackend().scMul(a, b); }
|
||||
export function scMulAdd(a, b, c) { return getCryptoBackend().scMulAdd(a, b, c); }
|
||||
export function scMulSub(a, b, c) { return getCryptoBackend().scMulSub(a, b, c); }
|
||||
export function scReduce32(s) { return getCryptoBackend().scReduce32(s); }
|
||||
export function scReduce64(s) { return getCryptoBackend().scReduce64(s); }
|
||||
export function scInvert(a) { return getCryptoBackend().scInvert(a); }
|
||||
export function scCheck(s) { return getCryptoBackend().scCheck(s); }
|
||||
export function scIsZero(s) { return getCryptoBackend().scIsZero(s); }
|
||||
|
||||
// Point ops
|
||||
export function scalarMultBase(s) { return getCryptoBackend().scalarMultBase(s); }
|
||||
export function scalarMultPoint(s, p) { return getCryptoBackend().scalarMultPoint(s, p); }
|
||||
export function pointAddCompressed(p, q) { return getCryptoBackend().pointAddCompressed(p, q); }
|
||||
export function pointSubCompressed(p, q) { return getCryptoBackend().pointSubCompressed(p, q); }
|
||||
export function pointNegate(p) { return getCryptoBackend().pointNegate(p); }
|
||||
export function doubleScalarMultBase(a, p, b) { return getCryptoBackend().doubleScalarMultBase(a, p, b); }
|
||||
|
||||
Reference in New Issue
Block a user