// SPDX-License-Identifier: MIT pragma solidity 0.8.23; /** * @title AereLatticeVerifier — lattice PQC verification core (Falcon/Dilithium family) * @notice The mathematical HEART of lattice-based post-quantum signature * verification, on-chain: negacyclic polynomial arithmetic in the ring * R_q = Z_q[x]/(x^n + 1) with q = 12289 (the Falcon modulus), plus the * short-vector (Euclidean norm) bound check. * * Given a public polynomial `h`, a challenge polynomial `c` (in Falcon, * c = HashToPoint(nonce || message)), and a candidate signature * (`s1`, `s2`), it checks the Falcon verification relation * s1 == c - s2 * h (mod q, in the ring) * and that (s1, s2) is a SHORT vector: ||(s1,s2)||^2 <= bound. A short * solution only exists to someone holding the NTRU trapdoor, which is * hard for classical AND quantum computers — that is the post-quantum * security. * * HONEST SCOPE: this is the ring-arithmetic + norm core, parameterised * by n (demo at 256; Falcon uses 512/1024). A SPEC-COMPLETE Falcon-512 * verifier additionally needs SHAKE-256 HashToPoint, Falcon's compressed * signature decoding, and validation against NIST KAT vectors — a * separate, larger effort. This contract proves AERE can do the lattice * math on-chain; it is NOT a drop-in NIST Falcon-512 verifier. */ contract AereLatticeVerifier { uint256 public constant Q = 12289; // Falcon modulus uint256 public constant N = 64; // ring degree (demo; Falcon = 512/1024 via NTT) /// @notice Negacyclic polynomial multiplication a*b in Z_q[x]/(x^n + 1). function ringMul(uint256[] calldata a, uint256[] calldata b) public pure returns (uint256[] memory t) { require(a.length == N && b.length == N, "len"); t = new uint256[](N); for (uint256 i = 0; i < N; i++) { uint256 ai = a[i]; if (ai == 0) continue; for (uint256 j = 0; j < N; j++) { uint256 p = mulmod(ai, b[j], Q); uint256 k = i + j; if (k < N) { t[k] = addmod(t[k], p, Q); } else { // x^n = -1 => subtract into position k-n t[k - N] = addmod(t[k - N], Q - p, Q); } } } } /// @notice Squared Euclidean norm of a polynomial with coefficients centered /// into (-q/2, q/2]. function normSq(uint256[] calldata v) public pure returns (uint256 s) { for (uint256 i = 0; i < v.length; i++) { uint256 x = v[i] % Q; int256 c = x <= Q / 2 ? int256(x) : int256(x) - int256(Q); s += uint256(c * c); } } /** * @notice Verify the Falcon-family lattice relation + short-vector bound. * @param h public polynomial (n coeffs) * @param c challenge polynomial (n coeffs) — Falcon: HashToPoint(msg) * @param s1 signature part 1 (n coeffs) * @param s2 signature part 2 (n coeffs) * @param bound ||(s1,s2)||^2 must be <= bound */ function verify( uint256[] calldata h, uint256[] calldata c, uint256[] calldata s1, uint256[] calldata s2, uint256 bound ) external pure returns (bool) { require(h.length == N && c.length == N && s1.length == N && s2.length == N, "len"); // s2 * h uint256[] memory t = ringMul(s2, h); // s1 must equal c - s2*h (mod q) for (uint256 i = 0; i < N; i++) { uint256 expected = addmod(c[i] % Q, Q - t[i], Q); // (c - t) mod q if (s1[i] % Q != expected) return false; } // short-vector bound if (normSq(s1) + normSq(s2) > bound) return false; return true; } }