aere-research/kat/HashToPointKat.java
Aere Network 4a0b48588c Initial public release
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.
2026-07-20 01:02:30 +03:00

120 lines
5.2 KiB
Java

package org.hyperledger.besu.evm.precompile;
// AERE Falcon HashToPoint precompile (0x0AE7) KAT + vector generator.
//
// 1. Generates real Falcon-512 signatures with Bouncy Castle, extracts each
// (nonce, message), and runs them through the REAL HashToPointPrecompiledContract.
// 2. Emits two files:
// hashtopoint_vectors.txt : tcId|logn|nonce_hex|message_hex|precompile_coeffs_hex
// (the coeffs are the precompile output; htp_reference.py recomputes them
// independently with Python's stdlib SHAKE256 and must match)
// falcon512_bench_vectors.txt : tcId|pk_hex|sm_hex
// (real (pk, signed-message) pairs for the on-chain Falcon-verify benchmark
// and the precompile-vs-in-EVM integration check)
// 3. Adds deterministic edge vectors (all-zero nonce, empty message, a logn=10 case).
import org.apache.tuweni.bytes.Bytes;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.pqc.crypto.falcon.*;
import org.hyperledger.besu.evm.gascalculator.CancunGasCalculator;
import java.io.*;
import java.nio.file.*;
import java.security.SecureRandom;
import java.util.*;
public class HashToPointKat {
static String hex(byte[] b) {
StringBuilder s = new StringBuilder();
for (byte x : b) s.append(String.format("%02x", x));
return s.toString();
}
static HashToPointPrecompiledContract PC =
new HashToPointPrecompiledContract(new CancunGasCalculator());
// Run the precompile for logn || nonce || message, return coeff bytes (2*n).
static byte[] htp(int logn, byte[] nonce, byte[] message) {
byte[] input = new byte[1 + nonce.length + message.length];
input[0] = (byte) logn;
System.arraycopy(nonce, 0, input, 1, nonce.length);
System.arraycopy(message, 0, input, 1 + nonce.length, message.length);
Bytes out = PC.computePrecompile(Bytes.wrap(input), null).output();
return out == null ? new byte[0] : out.toArrayUnsafe();
}
public static void main(String[] a) throws Exception {
String htpVec = a.length > 0 ? a[0] : "hashtopoint_vectors.txt";
String benchVec = a.length > 1 ? a[1] : "falcon512_bench_vectors.txt";
int nSigs = a.length > 2 ? Integer.parseInt(a[2]) : 8;
SecureRandom rnd = new SecureRandom();
StringBuilder htp = new StringBuilder();
StringBuilder bench = new StringBuilder();
int tc = 0;
// ---- real Falcon-512 signatures ----
FalconKeyPairGenerator kg = new FalconKeyPairGenerator();
kg.init(new FalconKeyGenerationParameters(rnd, FalconParameters.falcon_512));
for (int i = 0; i < nSigs; i++) {
AsymmetricCipherKeyPair kp = kg.generateKeyPair();
FalconPublicKeyParameters pub = (FalconPublicKeyParameters) kp.getPublic();
FalconPrivateKeyParameters priv = (FalconPrivateKeyParameters) kp.getPrivate();
byte[] message = ("AERE Falcon HashToPoint KAT vector #" + i + " (isolated fork)").getBytes();
FalconSigner fs = new FalconSigner();
fs.init(true, priv);
byte[] bcSig = fs.generateSignature(message); // (0x30|logn) || nonce(40) || compressed
byte[] nonce = Arrays.copyOfRange(bcSig, 1, 41);
byte[] compressed = Arrays.copyOfRange(bcSig, 41, bcSig.length);
// HashToPoint vector
byte[] coeffs = htp(9, nonce, message);
htp.append(tc).append("|9|").append(hex(nonce)).append("|").append(hex(message))
.append("|").append(hex(coeffs)).append("\n");
// Falcon bench vector: pk = 0x09 || h ; sm = sigLen(2) || nonce(40) || message || (0x29||compressed)
byte[] h = pub.getH();
byte[] pk = new byte[1 + h.length];
pk[0] = 0x09;
System.arraycopy(h, 0, pk, 1, h.length);
byte[] sigBlock = new byte[1 + compressed.length];
sigBlock[0] = (byte) 0x29; // 0x20 | logn(9)
System.arraycopy(compressed, 0, sigBlock, 1, compressed.length);
int sigLen = sigBlock.length;
ByteArrayOutputStream sm = new ByteArrayOutputStream();
sm.write((sigLen >> 8) & 0xff);
sm.write(sigLen & 0xff);
sm.write(nonce);
sm.write(message);
sm.write(sigBlock);
bench.append(tc).append("|").append(hex(pk)).append("|").append(hex(sm.toByteArray())).append("\n");
tc++;
}
// ---- deterministic edge vectors (HashToPoint only) ----
byte[] zeroNonce = new byte[40];
byte[][] edgeMsgs = new byte[][] {
new byte[0],
"abc".getBytes(),
"The quick brown fox jumps over the lazy dog".getBytes()
};
for (byte[] msg : edgeMsgs) {
byte[] c9 = htp(9, zeroNonce, msg);
htp.append(tc).append("|9|").append(hex(zeroNonce)).append("|").append(hex(msg))
.append("|").append(hex(c9)).append("\n");
tc++;
}
// one Falcon-1024 sized (logn=10) HashToPoint vector
byte[] c10 = htp(10, zeroNonce, "AERE Falcon-1024 HashToPoint".getBytes());
htp.append(tc).append("|10|").append(hex(zeroNonce)).append("|")
.append(hex("AERE Falcon-1024 HashToPoint".getBytes())).append("|").append(hex(c10)).append("\n");
tc++;
Files.write(Paths.get(htpVec), htp.toString().getBytes());
Files.write(Paths.get(benchVec), bench.toString().getBytes());
System.out.println("HashToPoint: wrote " + tc + " vectors to " + htpVec
+ " and " + nSigs + " Falcon bench vectors to " + benchVec);
}
}