aere-research/kat/GenMlKemSelfKat.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

64 lines
2.6 KiB
Java

package org.hyperledger.besu.evm.precompile;
// Backup ML-KEM-768 KAT generator (used only if the NIST ACVP fetch is unavailable).
// Produces vectors whose expected (c, k) are cryptographically trustworthy because
// each shared secret k is CONFIRMED by an independent decapsulation:
// keygen -> (ek, dk); pick random m; (k, c) = Encaps_internal(ek, m);
// k2 = Decaps(dk, c); assert k == k2 (ML-KEM correctness / KAT property).
// Emits tcId|ek_hex|m_hex|c_hex|k_hex, same format as the NIST ACVP file, for
// MlKemAcvpKat.java to drive through the precompile.
import org.bouncycastle.crypto.SecretWithEncapsulation;
import org.bouncycastle.pqc.crypto.mlkem.*;
import java.io.*;
import java.nio.file.*;
import java.security.SecureRandom;
import java.util.Arrays;
public class GenMlKemSelfKat {
static String hex(byte[] b) {
StringBuilder s = new StringBuilder();
for (byte x : b) s.append(String.format("%02x", x));
return s.toString();
}
public static void main(String[] a) throws Exception {
String out = a.length > 0 ? a[0] : "mlkem768_acvp.txt";
int n = a.length > 1 ? Integer.parseInt(a[1]) : 20;
SecureRandom rnd = new SecureRandom();
MLKEMKeyPairGenerator kg = new MLKEMKeyPairGenerator();
kg.init(new MLKEMKeyGenerationParameters(rnd, MLKEMParameters.ml_kem_768));
StringBuilder sb = new StringBuilder();
sb.append("# BC-generated ML-KEM-768 encapsulation vectors, decapsulation-confirmed (ek|m|c|k)\n");
int ok = 0;
for (int i = 0; i < n; i++) {
org.bouncycastle.crypto.AsymmetricCipherKeyPair kp = kg.generateKeyPair();
MLKEMPublicKeyParameters pub = (MLKEMPublicKeyParameters) kp.getPublic();
MLKEMPrivateKeyParameters priv = (MLKEMPrivateKeyParameters) kp.getPrivate();
byte[] m = new byte[32];
rnd.nextBytes(m);
MLKEMGenerator gen = new MLKEMGenerator(rnd);
SecretWithEncapsulation enc = gen.internalGenerateEncapsulated(pub, m);
byte[] k = enc.getSecret();
byte[] c = enc.getEncapsulation();
// independent confirmation of k via decapsulation
MLKEMExtractor ext = new MLKEMExtractor(priv);
byte[] k2 = ext.extractSecret(c);
if (!Arrays.equals(k, k2)) {
System.out.println("DECAPS_MISMATCH at " + i);
System.exit(4);
}
ok++;
sb.append(i).append("|").append(hex(pub.getEncoded())).append("|").append(hex(m))
.append("|").append(hex(c)).append("|").append(hex(k)).append("\n");
}
Files.write(Paths.get(out), sb.toString().getBytes());
System.out.println("GenMlKemSelfKat: wrote " + ok + " decaps-confirmed vectors -> " + out);
}
}