Aere Network public source. Everything here can be checked against the live chain (chain id 2800, https://rpc.aere.network). Scope note, stated up front rather than buried: consensus on chain 2800 is classical secp256k1 ECDSA QBFT. The post-quantum work in this repository is at the signature, precompile, account and transport layers. Nothing here makes the consensus post-quantum, and no document in it should be read as claiming so.
59 lines
2.5 KiB
JavaScript
59 lines
2.5 KiB
JavaScript
const { expect } = require("chai");
|
|
const { ethers } = require("hardhat");
|
|
|
|
const Q = 12289, N = 64;
|
|
function ringMul(a, b) {
|
|
const t = new Array(N).fill(0);
|
|
for (let i = 0; i < N; i++) { if (!a[i]) continue; for (let j = 0; j < N; j++) {
|
|
const p = (a[i] * b[j]) % Q; const k = i + j;
|
|
if (k < N) t[k] = (t[k] + p) % Q; else t[k - N] = (t[k - N] + Q - p) % Q;
|
|
} }
|
|
return t;
|
|
}
|
|
const short = () => Array.from({ length: N }, () => (((Math.floor(Math.random() * 7) - 3) % Q) + Q) % Q); // coeffs in [-3,3]
|
|
const randPoly = () => Array.from({ length: N }, () => Math.floor(Math.random() * Q));
|
|
|
|
describe("AereLatticeVerifier (Falcon/Dilithium-family lattice core)", function () {
|
|
async function deploy() {
|
|
const V = await ethers.getContractFactory("AereLatticeVerifier");
|
|
const v = await V.deploy(); await v.waitForDeployment(); return v;
|
|
}
|
|
|
|
it("accepts a valid short-vector lattice signature", async function () {
|
|
const v = await deploy();
|
|
const s1 = short(), s2 = short(), h = randPoly();
|
|
const c = ringMul(s2, h).map((tv, i) => (s1[i] + tv) % Q); // c = s1 + s2*h
|
|
expect(await v.verify(h, c, s1, s2, 20000)).to.equal(true);
|
|
});
|
|
|
|
it("rejects a tampered s1 (relation broken)", async function () {
|
|
const v = await deploy();
|
|
const s1 = short(), s2 = short(), h = randPoly();
|
|
const c = ringMul(s2, h).map((tv, i) => (s1[i] + tv) % Q);
|
|
const bad = [...s1]; bad[0] = (bad[0] + 1) % Q;
|
|
expect(await v.verify(h, c, bad, s2, 20000)).to.equal(false);
|
|
});
|
|
|
|
it("rejects a forgery: the relation holds but (s1,s2) is NOT short", async function () {
|
|
const v = await deploy();
|
|
const s1 = short(), s2 = short(), h = randPoly();
|
|
const c = ringMul(s2, h).map((tv, i) => (s1[i] + tv) % Q);
|
|
// attacker picks a different short s2b, solves s1b = c - s2b*h — which is LARGE
|
|
const s2b = short();
|
|
const tb = ringMul(s2b, h);
|
|
const s1b = c.map((cv, i) => ((cv - tb[i]) % Q + Q) % Q);
|
|
// relation c - s2b*h == s1b holds by construction, but norm blows the bound
|
|
expect(await v.verify(h, c, s1b, s2b, 20000)).to.equal(false);
|
|
});
|
|
|
|
it("ringMul is negacyclic (x^n = -1): x^(n-1) * x = -1", async function () {
|
|
const v = await deploy();
|
|
const a = new Array(N).fill(0); a[N - 1] = 1; // x^(n-1)
|
|
const b = new Array(N).fill(0); b[1] = 1; // x
|
|
const t = (await v.ringMul(a, b)).map(Number);
|
|
// x^(n-1) * x = x^n = -1 == q-1 at coefficient 0
|
|
expect(t[0]).to.equal(Q - 1);
|
|
expect(t.slice(1).every(x => x === 0)).to.equal(true);
|
|
});
|
|
});
|