Some checks failed
contracts-ci / Install (lockfile) → compile → full test suite (push) Has been cancelled
contracts-ci / Ethereum interop (EIP-2537 BLS, prague hardfork) (push) Has been cancelled
contracts-ci / PQC known-answer tests (NIST vectors) (push) Has been cancelled
contracts-ci / Coverage (scoped, with artifacts) (push) Has been cancelled
The published line and the local line had no common ancestor: the public one carried the redaction pass, the local one carried three weeks of corrections that never shipped. This commit ports the local work onto the public line, keeps every public redaction, and extends the same discretion to seven client mentions that were still named in published comments. Carried: LICENSE year and LICENSING.md; the measured burn figures replacing the deflation claim (the vault holds ~0.137 AERE of 2.8 billion, and burn is a share of validator coinbase revenue, which is zero today); 'audited' removed from next to Bouncy Castle; citation paths rewritten to published form with CITATIONS-UNRESOLVED.md remeasured 2026-08-11; VERIFY-POLICY.md; slashing and ownership comments brought down to what the code does; the AerePyth repair; the shutter test helper the tests cite; runnable package.json entries; the CI file split into a GitHub/Gitea twin pair with a real measured test-run status; and the .gitignore hardening written after a compiled artifact leaked a local path in a sibling repository. A false '2-of-3 multisig' description of the owner account is corrected to what the chain measures: an externally owned account. The self-audit findings catalog stays unpublished pending an explicit decision.
117 lines
5.5 KiB
Solidity
117 lines
5.5 KiB
Solidity
// SPDX-License-Identifier: MIT
|
|
pragma solidity 0.8.23;
|
|
|
|
/**
|
|
* @title AereMLKEM768 - on-chain ML-KEM-768 (FIPS 203) encapsulation verifier
|
|
* @notice Thin view wrapper over the AERE native ML-KEM-768 precompile at 0x0AE6.
|
|
* ML-KEM-768 is AERE's post-quantum CONFIDENTIALITY primitive: every other
|
|
* native PQC precompile is a signature or hash (post-quantum authenticity),
|
|
* this one makes a Module-Lattice KEM key-agreement transcript verifiable
|
|
* on a chain where the 0x0AE6 precompile is activated.
|
|
*
|
|
* @dev HONEST SCOPE - TESTNET ONLY (as of 2026-07-20). The 0x0AE6 precompile this
|
|
* contract wraps is live on the AERE TESTNET ONLY. It is NOT activated on
|
|
* mainnet chain 2800, which has exactly the FIVE precompiles 0x0AE1..0x0AE5
|
|
* (activated block 9,189,161). On mainnet today a staticcall to 0x0AE6 hits an
|
|
* empty account and returns success with EMPTY data, so every length check
|
|
* below fails and this contract reports failure rather than a false positive.
|
|
* Deploying it on mainnet 2800 is therefore safe but useless until activation,
|
|
* which is founder and external-audit gated. Nothing here changes AERE
|
|
* consensus, which remains classical secp256k1 ECDSA QBFT.
|
|
*
|
|
* The precompile performs a DETERMINISTIC ML-KEM.Encaps: given an
|
|
* encapsulation key `ek` (1184 bytes) and 32-byte encapsulation coins `m`,
|
|
* it returns the ciphertext `ct` (1088 bytes) and shared secret `ss` (32
|
|
* bytes) that FIPS-203 Encaps(ek, m) produces. A relying contract compares
|
|
* the recomputed (ct, ss) against a claimed transcript; equality proves the
|
|
* KEM step was performed honestly with the stated coins. Serves the UMBRA
|
|
* PQXDH handshake settlement path and the AERE PQC key registry.
|
|
*
|
|
* @dev The precompile returns EMPTY (0 bytes) for any malformed input (wrong
|
|
* length). Callers must treat an empty return as failure.
|
|
*
|
|
* @dev AUD-CRYPTO-4 - THE SHARED SECRET RETURNED HERE IS PUBLIC. Encapsulation is
|
|
* DETERMINISTIC in the public inputs (ek, m), both supplied as plain calldata,
|
|
* so the shared secret `ss` (FIPS-203 "K") is fully derivable by any observer
|
|
* and returning it in returndata leaks nothing beyond those public inputs. This
|
|
* contract is a TRANSCRIPT-VERIFICATION tool only (prove that Encaps(ek, m)
|
|
* yields a claimed (ct, ss)); it is NOT a live key-agreement channel. Do NOT
|
|
* feed it secret coins `m` or treat `ss` as confidential key material. The
|
|
* precompile does not perform an explicit FIPS-203 ek validity round-trip; this
|
|
* is harmless for the transcript-equality use, but callers must not rely on it
|
|
* to screen a malformed ek.
|
|
*/
|
|
contract AereMLKEM768 {
|
|
/// @dev Native precompile address (AERE Besu PQC fork). TESTNET ONLY: not
|
|
/// activated on mainnet chain 2800. See the HONEST SCOPE note above.
|
|
address public constant MLKEM768 = 0x0000000000000000000000000000000000000AE6;
|
|
|
|
uint256 public constant EK_LEN = 1184; // encapsulation key
|
|
uint256 public constant M_LEN = 32; // coins
|
|
uint256 public constant CT_LEN = 1088; // ciphertext
|
|
uint256 public constant SS_LEN = 32; // shared secret
|
|
|
|
/**
|
|
* @notice Deterministically encapsulate to `ek` using coins `m`.
|
|
* @param ek 1184-byte ML-KEM-768 encapsulation key.
|
|
* @param m 32-byte encapsulation randomness (coins).
|
|
* @return ok true iff the precompile returned a well-formed (ct, ss).
|
|
* @return ct 1088-byte ciphertext.
|
|
* @return ss 32-byte shared secret.
|
|
*/
|
|
function encapsulate(bytes memory ek, bytes memory m)
|
|
public
|
|
view
|
|
returns (bool ok, bytes memory ct, bytes memory ss)
|
|
{
|
|
if (ek.length != EK_LEN || m.length != M_LEN) {
|
|
return (false, "", "");
|
|
}
|
|
bytes memory input = bytes.concat(ek, m);
|
|
(bool success, bytes memory out) = MLKEM768.staticcall(input);
|
|
if (!success || out.length != CT_LEN + SS_LEN) {
|
|
return (false, "", "");
|
|
}
|
|
ct = new bytes(CT_LEN);
|
|
ss = new bytes(SS_LEN);
|
|
for (uint256 i = 0; i < CT_LEN; i++) {
|
|
ct[i] = out[i];
|
|
}
|
|
for (uint256 i = 0; i < SS_LEN; i++) {
|
|
ss[i] = out[CT_LEN + i];
|
|
}
|
|
return (true, ct, ss);
|
|
}
|
|
|
|
/**
|
|
* @notice Verify a claimed ML-KEM-768 transcript (ek, m -> ct, ss).
|
|
* @return valid true iff Encaps(ek, m) reproduces exactly the claimed ct and ss.
|
|
*/
|
|
function verifyTranscript(
|
|
bytes memory ek,
|
|
bytes memory m,
|
|
bytes memory claimedCt,
|
|
bytes memory claimedSs
|
|
) external view returns (bool valid) {
|
|
(bool ok, bytes memory ct, bytes memory ss) = encapsulate(ek, m);
|
|
if (!ok) return false;
|
|
return keccak256(ct) == keccak256(claimedCt) && keccak256(ss) == keccak256(claimedSs);
|
|
}
|
|
|
|
/**
|
|
* @notice Recompute only the shared secret for (ek, m). Convenience for a
|
|
* relying contract that already holds the ciphertext off-chain.
|
|
* @dev AUD-CRYPTO-4 footgun: the returned `ss` is PUBLIC (deterministic in the
|
|
* public inputs ek, m). This is a transcript-verification convenience, NOT
|
|
* a confidential key-agreement primitive; never pass secret coins `m`.
|
|
*/
|
|
function sharedSecret(bytes memory ek, bytes memory m)
|
|
external
|
|
view
|
|
returns (bool ok, bytes memory ss)
|
|
{
|
|
bytes memory ct;
|
|
(ok, ct, ss) = encapsulate(ek, m);
|
|
}
|
|
}
|