756 lines
32 KiB
Diff
756 lines
32 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Aere Network <node@aere.network>
|
|
Date: Thu, 10 Sep 2026 22:39:39 +0300
|
|
Subject: [PATCH] Aere Network: post-quantum signature verification precompiles
|
|
(mainnet 0x0ae1-0x0ae5)
|
|
|
|
Adds five native precompiled contracts exposing post-quantum signature
|
|
verification to the EVM, and registers them in populateForFutureEIPs so they
|
|
activate by fork timestamp rather than at genesis.
|
|
|
|
0x0ae1 Falcon-512 verify NIST Falcon round 3
|
|
0x0ae2 Falcon-1024 verify NIST Falcon round 3
|
|
0x0ae3 ML-DSA-44 verify FIPS 204
|
|
0x0ae4 SLH-DSA-SHA2-128s verify FIPS 205
|
|
0x0ae5 SHAKE256 XOF FIPS 202
|
|
|
|
Live on Aere Network chain 2800 from block 9,189,161.
|
|
|
|
Scope: these precompiles verify post-quantum signatures inside the EVM. They
|
|
do not change consensus. Block sealing and validator identity on chain 2800
|
|
remain classical secp256k1 ECDSA QBFT.
|
|
|
|
populateForFutureEIPs is additionally repointed from populateForCancun to
|
|
populateForOsaka, so the fork carries the full Osaka precompile set.
|
|
|
|
No build file change is required: bcprov-jdk18on is already exposed to the evm
|
|
module as an api dependency of crypto:algorithms, and jakarta.validation-api is
|
|
a global subproject dependency.
|
|
Modified upstream files. Two files touched by this patch are modified copies
|
|
of Hyperledger Besu sources, not new work by Aere Network:
|
|
|
|
datatypes/src/main/java/org/hyperledger/besu/datatypes/Address.java
|
|
evm/src/main/java/org/hyperledger/besu/evm/precompile/MainnetPrecompiledContracts.java
|
|
|
|
Aere Network changed both, against upstream commit
|
|
d2032017bb3b8cb215a97303980a1e4a643f7180. Both keep their original
|
|
"Copyright contributors to Hyperledger Besu" headers unchanged, which is what
|
|
Apache License 2.0 section 4(c) requires, and this patch adds to each a
|
|
separate "Modifications Copyright" block naming exactly what was changed. That
|
|
in-file block is the notice required by Apache License 2.0 section 4(b). It
|
|
travels inside the diff, so a tree with this patch applied carries the notice
|
|
whether you used git am or git apply. Every other file this patch touches is
|
|
new and was written by Aere Network, and each carries its own Apache-2.0
|
|
header.
|
|
|
|
Regenerated 2026-09-10 from the production tree over the same file set, so that
|
|
the public recipe carries the same Apache-2.0 section 4(b) modification notices
|
|
that the production sources carry; measured the same day: a build from this
|
|
recipe reproduces the fleet's consensus jars class for class.
|
|
|
|
Corrected 2026-09-11, comments only. This patch still called Bouncy Castle
|
|
"audited" in three places after that adjective had already been removed from the
|
|
production sources and from the copies published beside it, because nothing
|
|
compared the patch with the sources it is generated from. We cannot cite an audit
|
|
of bcprov-jdk18on: it is not a FIPS validated module and we know of no published
|
|
third-party review of it. What is citable is byte-for-byte agreement with the NIST
|
|
KAT and ACVP vectors in both directions, and that is what the comments now say.
|
|
No code changed; the wording did.
|
|
---
|
|
diff --git a/datatypes/src/main/java/org/hyperledger/besu/datatypes/Address.java b/datatypes/src/main/java/org/hyperledger/besu/datatypes/Address.java
|
|
index 950cd59e3..3a63362d7 100644
|
|
--- a/datatypes/src/main/java/org/hyperledger/besu/datatypes/Address.java
|
|
+++ b/datatypes/src/main/java/org/hyperledger/besu/datatypes/Address.java
|
|
@@ -1,33 +1,39 @@
|
|
/*
|
|
* Copyright contributors to Hyperledger Besu.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
|
* the License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
|
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
|
* specific language governing permissions and limitations under the License.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
+ *
|
|
+ * Modifications Copyright contributors to the Aere Network.
|
|
+ *
|
|
+ * This file was modified by contributors to the Aere Network, as required by section 4(b) of the
|
|
+ * Apache License 2.0. The copyright header above is the upstream one and is left exactly as it was
|
|
+ * found, as section 4(c) requires. The change itself is in patches/0001-aere-pqc-precompiles-mainnet.patch.
|
|
*/
|
|
package org.hyperledger.besu.datatypes;
|
|
|
|
import static com.google.common.base.Preconditions.checkArgument;
|
|
import static org.hyperledger.besu.crypto.Hash.keccak256;
|
|
|
|
import org.hyperledger.besu.crypto.SECPPublicKey;
|
|
import org.hyperledger.besu.ethereum.rlp.RLP;
|
|
import org.hyperledger.besu.ethereum.rlp.RLPException;
|
|
import org.hyperledger.besu.ethereum.rlp.RLPInput;
|
|
|
|
import java.util.concurrent.ExecutionException;
|
|
|
|
import com.fasterxml.jackson.annotation.JsonCreator;
|
|
import com.google.common.cache.CacheBuilder;
|
|
import com.google.common.cache.CacheLoader;
|
|
import com.google.common.cache.LoadingCache;
|
|
import org.apache.tuweni.bytes.Bytes;
|
|
import org.apache.tuweni.bytes.Bytes32;
|
|
|
|
@@ -74,40 +80,55 @@ public class Address extends BytesHolder {
|
|
public static final Address BLS12_G1MULTIEXP = Address.precompiled(0xC);
|
|
|
|
/** The constant BLS12_G2ADD. */
|
|
public static final Address BLS12_G2ADD = Address.precompiled(0xD);
|
|
|
|
/** The constant BLS12_G2MULTIEXP. */
|
|
public static final Address BLS12_G2MULTIEXP = Address.precompiled(0xE);
|
|
|
|
/** The constant BLS12_PAIRING. */
|
|
public static final Address BLS12_PAIRING = Address.precompiled(0xF);
|
|
|
|
/** The constant BLS12_MAP_FP_TO_G1. */
|
|
public static final Address BLS12_MAP_FP_TO_G1 = Address.precompiled(0x10);
|
|
|
|
/** The constant BLS12_MAP_FP2_TO_G2. */
|
|
public static final Address BLS12_MAP_FP2_TO_G2 = Address.precompiled(0x11);
|
|
|
|
/** Precompile address for P256_VERIFY. */
|
|
public static final Address P256_VERIFY = Address.precompiled(0x0100);
|
|
|
|
+ /** AERE PQC precompile: Falcon-512 verify. */
|
|
+ public static final Address AERE_FALCON512 = Address.fromHexString("0x0000000000000000000000000000000000000ae1");
|
|
+
|
|
+ /** AERE PQC precompile: Falcon-1024 verify. */
|
|
+ public static final Address AERE_FALCON1024 = Address.fromHexString("0x0000000000000000000000000000000000000ae2");
|
|
+
|
|
+ /** AERE PQC precompile: ML-DSA-44 (FIPS 204) verify. */
|
|
+ public static final Address AERE_MLDSA44 = Address.fromHexString("0x0000000000000000000000000000000000000ae3");
|
|
+
|
|
+ /** AERE PQC precompile: SLH-DSA-SHA2-128s (FIPS 205) verify. */
|
|
+ public static final Address AERE_SLHDSA128S = Address.fromHexString("0x0000000000000000000000000000000000000ae4");
|
|
+
|
|
+ /** AERE PQC precompile: SHAKE256 XOF (FIPS 202). */
|
|
+ public static final Address AERE_SHAKE256 = Address.fromHexString("0x0000000000000000000000000000000000000ae5");
|
|
+
|
|
/** The constant ZERO. */
|
|
public static final Address ZERO = Address.fromHexString("0x0");
|
|
|
|
static LoadingCache<Address, Hash> hashCache =
|
|
CacheBuilder.newBuilder()
|
|
.maximumSize(4000)
|
|
// .weakKeys() // unless we "intern" all addresses we cannot use weak or soft keys.
|
|
.build(
|
|
new CacheLoader<>() {
|
|
@Override
|
|
public Hash load(final Address key) {
|
|
return Hash.hash(key.getBytes());
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Instantiates a new Address.
|
|
*
|
|
* @param bytes the bytes
|
|
*/
|
|
diff --git a/evm/src/main/java/org/hyperledger/besu/evm/precompile/AereFalconSupport.java b/evm/src/main/java/org/hyperledger/besu/evm/precompile/AereFalconSupport.java
|
|
new file mode 100644
|
|
index 000000000..1dbb2b85c
|
|
--- /dev/null
|
|
+++ b/evm/src/main/java/org/hyperledger/besu/evm/precompile/AereFalconSupport.java
|
|
@@ -0,0 +1,94 @@
|
|
+/*
|
|
+ * Copyright contributors to the AERE Network.
|
|
+ *
|
|
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
|
+ * the License. You may obtain a copy of the License at
|
|
+ *
|
|
+ * http://www.apache.org/licenses/LICENSE-2.0
|
|
+ *
|
|
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
|
+ * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
|
+ * specific language governing permissions and limitations under the License.
|
|
+ *
|
|
+ * SPDX-License-Identifier: Apache-2.0
|
|
+ */
|
|
+package org.hyperledger.besu.evm.precompile;
|
|
+
|
|
+import java.util.Arrays;
|
|
+
|
|
+import org.apache.tuweni.bytes.Bytes;
|
|
+import org.apache.tuweni.bytes.Bytes32;
|
|
+import org.bouncycastle.pqc.crypto.falcon.FalconParameters;
|
|
+import org.bouncycastle.pqc.crypto.falcon.FalconPublicKeyParameters;
|
|
+import org.bouncycastle.pqc.crypto.falcon.FalconSigner;
|
|
+
|
|
+/**
|
|
+ * Shared logic for the AERE Falcon-512 / Falcon-1024 verification precompiles.
|
|
+ *
|
|
+ * <p>Transcodes the NIST round-3 reference "signed message" (sm) blob into the encoding Bouncy
|
|
+ * Castle's {@link FalconSigner#verifySignature(byte[], byte[])} expects and drives that
|
|
+ * verifier. No cryptography is reimplemented here.
|
|
+ *
|
|
+ * <p>NIST sm layout: {@code sigLen(2, big-endian) || nonce(40) || message || esig} where
|
|
+ * {@code esig = (0x20+logn) || compressedSig} and {@code sigLen == esig.length}. The Falcon public
|
|
+ * key is {@code (0x00+logn) || packed_h}; Bouncy Castle wants only {@code packed_h} (header
|
|
+ * stripped), and the signature it wants is {@code (0x30+logn) || nonce(40) || compressedSig}.
|
|
+ */
|
|
+final class AereFalconSupport {
|
|
+
|
|
+ static final int NONCE_LEN = 40;
|
|
+
|
|
+ private AereFalconSupport() {}
|
|
+
|
|
+ static Bytes resultWord(final boolean valid) {
|
|
+ return valid ? Bytes32.leftPad(Bytes.of((byte) 1)) : Bytes32.ZERO;
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * Verify a NIST signed-message blob against a Falcon public key.
|
|
+ *
|
|
+ * @param params Bouncy Castle Falcon parameter set (falcon_512 / falcon_1024)
|
|
+ * @param logn 9 for Falcon-512, 10 for Falcon-1024
|
|
+ * @param pkFull the 897- (512) or 1793-byte (1024) public key including the leading header byte
|
|
+ * @param sm the NIST signed-message blob
|
|
+ * @return true iff the signature is valid for the embedded message under pkFull
|
|
+ */
|
|
+ static boolean verify(
|
|
+ final FalconParameters params, final int logn, final byte[] pkFull, final byte[] sm) {
|
|
+ try {
|
|
+ if (pkFull.length < 2 || sm.length < 2 + NONCE_LEN + 2) {
|
|
+ return false;
|
|
+ }
|
|
+ if ((pkFull[0] & 0xff) != logn) {
|
|
+ return false;
|
|
+ }
|
|
+ final int sigLen = ((sm[0] & 0xff) << 8) | (sm[1] & 0xff);
|
|
+ if (sigLen < 2 || 2 + NONCE_LEN + sigLen > sm.length) {
|
|
+ return false;
|
|
+ }
|
|
+ final int msgLen = sm.length - 2 - NONCE_LEN - sigLen;
|
|
+ if (msgLen < 0) {
|
|
+ return false;
|
|
+ }
|
|
+ final byte[] esig = Arrays.copyOfRange(sm, sm.length - sigLen, sm.length);
|
|
+ if ((esig[0] & 0xff) != (0x20 + logn)) {
|
|
+ return false;
|
|
+ }
|
|
+ final byte[] H = Arrays.copyOfRange(pkFull, 1, pkFull.length);
|
|
+ final byte[] nonce = Arrays.copyOfRange(sm, 2, 2 + NONCE_LEN);
|
|
+ final byte[] message = Arrays.copyOfRange(sm, 2 + NONCE_LEN, 2 + NONCE_LEN + msgLen);
|
|
+
|
|
+ final byte[] bcSig = new byte[1 + NONCE_LEN + (esig.length - 1)];
|
|
+ bcSig[0] = (byte) (0x30 + logn);
|
|
+ System.arraycopy(nonce, 0, bcSig, 1, NONCE_LEN);
|
|
+ System.arraycopy(esig, 1, bcSig, 1 + NONCE_LEN, esig.length - 1);
|
|
+
|
|
+ final FalconPublicKeyParameters pub = new FalconPublicKeyParameters(params, H);
|
|
+ final FalconSigner signer = new FalconSigner();
|
|
+ signer.init(false, pub);
|
|
+ return signer.verifySignature(message, bcSig);
|
|
+ } catch (final Throwable t) {
|
|
+ return false;
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/evm/src/main/java/org/hyperledger/besu/evm/precompile/Falcon1024PrecompiledContract.java b/evm/src/main/java/org/hyperledger/besu/evm/precompile/Falcon1024PrecompiledContract.java
|
|
new file mode 100644
|
|
index 000000000..80aca574d
|
|
--- /dev/null
|
|
+++ b/evm/src/main/java/org/hyperledger/besu/evm/precompile/Falcon1024PrecompiledContract.java
|
|
@@ -0,0 +1,62 @@
|
|
+/*
|
|
+ * Copyright contributors to the AERE Network.
|
|
+ *
|
|
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
|
+ * the License. You may obtain a copy of the License at
|
|
+ *
|
|
+ * http://www.apache.org/licenses/LICENSE-2.0
|
|
+ *
|
|
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
|
+ * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
|
+ * specific language governing permissions and limitations under the License.
|
|
+ *
|
|
+ * SPDX-License-Identifier: Apache-2.0
|
|
+ */
|
|
+package org.hyperledger.besu.evm.precompile;
|
|
+
|
|
+import org.hyperledger.besu.evm.frame.MessageFrame;
|
|
+import org.hyperledger.besu.evm.gascalculator.GasCalculator;
|
|
+
|
|
+import jakarta.validation.constraints.NotNull;
|
|
+import org.apache.tuweni.bytes.Bytes;
|
|
+import org.bouncycastle.pqc.crypto.falcon.FalconParameters;
|
|
+
|
|
+/**
|
|
+ * AERE PQC precompile: Falcon-1024 signature verification (NIST round-3 reference encoding).
|
|
+ *
|
|
+ * <p>Input: {@code pk(1793) || sm(rest)} where pk is the reference public key (header 0x0A) and sm
|
|
+ * is the reference signed-message blob. Output: 32-byte word, {@code ...01} valid else {@code ...00}.
|
|
+ */
|
|
+public class Falcon1024PrecompiledContract extends AbstractPrecompiledContract {
|
|
+
|
|
+ static final int PK_LEN = 1793;
|
|
+ private static final int LOGN = 10;
|
|
+ private static final long GAS = 75_000L;
|
|
+
|
|
+ /**
|
|
+ * Instantiates a new Falcon-1024 precompiled contract.
|
|
+ *
|
|
+ * @param gasCalculator the gas calculator
|
|
+ */
|
|
+ Falcon1024PrecompiledContract(final GasCalculator gasCalculator) {
|
|
+ super("AereFalcon1024", gasCalculator);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public long gasRequirement(final Bytes input) {
|
|
+ return GAS;
|
|
+ }
|
|
+
|
|
+ @NotNull
|
|
+ @Override
|
|
+ public PrecompileContractResult computePrecompile(
|
|
+ final Bytes input, @NotNull final MessageFrame messageFrame) {
|
|
+ boolean valid = false;
|
|
+ if (input.size() > PK_LEN) {
|
|
+ final byte[] pk = input.slice(0, PK_LEN).toArrayUnsafe();
|
|
+ final byte[] sm = input.slice(PK_LEN).toArrayUnsafe();
|
|
+ valid = AereFalconSupport.verify(FalconParameters.falcon_1024, LOGN, pk, sm);
|
|
+ }
|
|
+ return PrecompileContractResult.success(AereFalconSupport.resultWord(valid));
|
|
+ }
|
|
+}
|
|
diff --git a/evm/src/main/java/org/hyperledger/besu/evm/precompile/Falcon512PrecompiledContract.java b/evm/src/main/java/org/hyperledger/besu/evm/precompile/Falcon512PrecompiledContract.java
|
|
new file mode 100644
|
|
index 000000000..5df074ceb
|
|
--- /dev/null
|
|
+++ b/evm/src/main/java/org/hyperledger/besu/evm/precompile/Falcon512PrecompiledContract.java
|
|
@@ -0,0 +1,62 @@
|
|
+/*
|
|
+ * Copyright contributors to the AERE Network.
|
|
+ *
|
|
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
|
+ * the License. You may obtain a copy of the License at
|
|
+ *
|
|
+ * http://www.apache.org/licenses/LICENSE-2.0
|
|
+ *
|
|
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
|
+ * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
|
+ * specific language governing permissions and limitations under the License.
|
|
+ *
|
|
+ * SPDX-License-Identifier: Apache-2.0
|
|
+ */
|
|
+package org.hyperledger.besu.evm.precompile;
|
|
+
|
|
+import org.hyperledger.besu.evm.frame.MessageFrame;
|
|
+import org.hyperledger.besu.evm.gascalculator.GasCalculator;
|
|
+
|
|
+import jakarta.validation.constraints.NotNull;
|
|
+import org.apache.tuweni.bytes.Bytes;
|
|
+import org.bouncycastle.pqc.crypto.falcon.FalconParameters;
|
|
+
|
|
+/**
|
|
+ * AERE PQC precompile: Falcon-512 signature verification (NIST round-3 reference encoding).
|
|
+ *
|
|
+ * <p>Input: {@code pk(897) || sm(rest)} where pk is the reference public key (header 0x09) and sm is
|
|
+ * the reference signed-message blob. Output: 32-byte word, {@code ...01} if valid else {@code ...00}.
|
|
+ */
|
|
+public class Falcon512PrecompiledContract extends AbstractPrecompiledContract {
|
|
+
|
|
+ static final int PK_LEN = 897;
|
|
+ private static final int LOGN = 9;
|
|
+ private static final long GAS = 40_000L;
|
|
+
|
|
+ /**
|
|
+ * Instantiates a new Falcon-512 precompiled contract.
|
|
+ *
|
|
+ * @param gasCalculator the gas calculator
|
|
+ */
|
|
+ Falcon512PrecompiledContract(final GasCalculator gasCalculator) {
|
|
+ super("AereFalcon512", gasCalculator);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public long gasRequirement(final Bytes input) {
|
|
+ return GAS;
|
|
+ }
|
|
+
|
|
+ @NotNull
|
|
+ @Override
|
|
+ public PrecompileContractResult computePrecompile(
|
|
+ final Bytes input, @NotNull final MessageFrame messageFrame) {
|
|
+ boolean valid = false;
|
|
+ if (input.size() > PK_LEN) {
|
|
+ final byte[] pk = input.slice(0, PK_LEN).toArrayUnsafe();
|
|
+ final byte[] sm = input.slice(PK_LEN).toArrayUnsafe();
|
|
+ valid = AereFalconSupport.verify(FalconParameters.falcon_512, LOGN, pk, sm);
|
|
+ }
|
|
+ return PrecompileContractResult.success(AereFalconSupport.resultWord(valid));
|
|
+ }
|
|
+}
|
|
diff --git a/evm/src/main/java/org/hyperledger/besu/evm/precompile/MLDSA44PrecompiledContract.java b/evm/src/main/java/org/hyperledger/besu/evm/precompile/MLDSA44PrecompiledContract.java
|
|
new file mode 100644
|
|
index 000000000..f25be641f
|
|
--- /dev/null
|
|
+++ b/evm/src/main/java/org/hyperledger/besu/evm/precompile/MLDSA44PrecompiledContract.java
|
|
@@ -0,0 +1,84 @@
|
|
+/*
|
|
+ * Copyright contributors to the AERE Network.
|
|
+ *
|
|
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
|
+ * the License. You may obtain a copy of the License at
|
|
+ *
|
|
+ * http://www.apache.org/licenses/LICENSE-2.0
|
|
+ *
|
|
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
|
+ * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
|
+ * specific language governing permissions and limitations under the License.
|
|
+ *
|
|
+ * SPDX-License-Identifier: Apache-2.0
|
|
+ */
|
|
+package org.hyperledger.besu.evm.precompile;
|
|
+
|
|
+import org.hyperledger.besu.evm.frame.MessageFrame;
|
|
+import org.hyperledger.besu.evm.gascalculator.GasCalculator;
|
|
+
|
|
+import jakarta.validation.constraints.NotNull;
|
|
+import org.apache.tuweni.bytes.Bytes;
|
|
+import org.apache.tuweni.bytes.Bytes32;
|
|
+import org.bouncycastle.pqc.crypto.mldsa.MLDSAParameters;
|
|
+import org.bouncycastle.pqc.crypto.mldsa.MLDSAPublicKeyParameters;
|
|
+import org.bouncycastle.pqc.crypto.mldsa.MLDSASigner;
|
|
+
|
|
+/**
|
|
+ * AERE PQC precompile: ML-DSA-44 (FIPS 204) signature verification via the INTERNAL interface
|
|
+ * (ML-DSA.Verify_internal, Algorithm 8 — no context/domain-separation prefix).
|
|
+ *
|
|
+ * <p>Input: {@code pk(1312) || sig(2420) || message(rest)}. Output: 32-byte word, {@code ...01}
|
|
+ * valid else {@code ...00}. The internal (rather than pure) verifier is reached by subclassing
|
|
+ * Bouncy Castle's {@link MLDSASigner} and calling its {@code protected internalVerifySignature}.
|
|
+ */
|
|
+public class MLDSA44PrecompiledContract extends AbstractPrecompiledContract {
|
|
+
|
|
+ static final int PK_LEN = 1312;
|
|
+ static final int SIG_LEN = 2420;
|
|
+ private static final long GAS = 55_000L;
|
|
+
|
|
+ /** Subclass exposing Bouncy Castle's protected internal (Verify_internal) verifier. */
|
|
+ private static final class InternalVerifier extends MLDSASigner {
|
|
+ boolean verifyInternal(final byte[] message, final byte[] signature) {
|
|
+ return internalVerifySignature(message, signature);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * Instantiates a new ML-DSA-44 precompiled contract.
|
|
+ *
|
|
+ * @param gasCalculator the gas calculator
|
|
+ */
|
|
+ MLDSA44PrecompiledContract(final GasCalculator gasCalculator) {
|
|
+ super("AereMLDSA44", gasCalculator);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public long gasRequirement(final Bytes input) {
|
|
+ return GAS;
|
|
+ }
|
|
+
|
|
+ @NotNull
|
|
+ @Override
|
|
+ public PrecompileContractResult computePrecompile(
|
|
+ final Bytes input, @NotNull final MessageFrame messageFrame) {
|
|
+ boolean valid = false;
|
|
+ if (input.size() >= PK_LEN + SIG_LEN) {
|
|
+ try {
|
|
+ final byte[] pk = input.slice(0, PK_LEN).toArrayUnsafe();
|
|
+ final byte[] sig = input.slice(PK_LEN, SIG_LEN).toArrayUnsafe();
|
|
+ final byte[] message = input.slice(PK_LEN + SIG_LEN).toArrayUnsafe();
|
|
+ final MLDSAPublicKeyParameters pub =
|
|
+ new MLDSAPublicKeyParameters(MLDSAParameters.ml_dsa_44, pk);
|
|
+ final InternalVerifier verifier = new InternalVerifier();
|
|
+ verifier.init(false, pub);
|
|
+ valid = verifier.verifyInternal(message, sig);
|
|
+ } catch (final Throwable t) {
|
|
+ valid = false;
|
|
+ }
|
|
+ }
|
|
+ return PrecompileContractResult.success(
|
|
+ valid ? Bytes32.leftPad(Bytes.of((byte) 1)) : Bytes32.ZERO);
|
|
+ }
|
|
+}
|
|
diff --git a/evm/src/main/java/org/hyperledger/besu/evm/precompile/MainnetPrecompiledContracts.java b/evm/src/main/java/org/hyperledger/besu/evm/precompile/MainnetPrecompiledContracts.java
|
|
index 28d84abf1..554a1e111 100644
|
|
--- a/evm/src/main/java/org/hyperledger/besu/evm/precompile/MainnetPrecompiledContracts.java
|
|
+++ b/evm/src/main/java/org/hyperledger/besu/evm/precompile/MainnetPrecompiledContracts.java
|
|
@@ -1,33 +1,39 @@
|
|
/*
|
|
* Copyright contributors to Hyperledger Besu.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
|
* the License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
|
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
|
* specific language governing permissions and limitations under the License.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
+ *
|
|
+ * Modifications Copyright contributors to the Aere Network.
|
|
+ *
|
|
+ * This file was modified by contributors to the Aere Network, as required by section 4(b) of the
|
|
+ * Apache License 2.0. The copyright header above is the upstream one and is left exactly as it was
|
|
+ * found, as section 4(c) requires. The change itself is in patches/0001-aere-pqc-precompiles-mainnet.patch.patch.
|
|
*/
|
|
package org.hyperledger.besu.evm.precompile;
|
|
|
|
import static org.hyperledger.besu.datatypes.Address.P256_VERIFY;
|
|
|
|
import org.hyperledger.besu.datatypes.Address;
|
|
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
|
|
|
|
/** Provides the various precompiled contracts used on mainnet hard forks. */
|
|
public interface MainnetPrecompiledContracts {
|
|
|
|
/**
|
|
* Frontier precompile contract registry.
|
|
*
|
|
* @param gasCalculator the gas calculator
|
|
* @return the precompile contract registry
|
|
*/
|
|
static PrecompileContractRegistry frontier(final GasCalculator gasCalculator) {
|
|
PrecompileContractRegistry precompileContractRegistry = new PrecompileContractRegistry();
|
|
populateForFrontier(precompileContractRegistry, gasCalculator);
|
|
@@ -209,23 +215,32 @@ public interface MainnetPrecompiledContracts {
|
|
/**
|
|
* FutureEIPs precompile contract registry.
|
|
*
|
|
* @param gasCalculator the gas calculator
|
|
* @return the precompile contract registry
|
|
*/
|
|
static PrecompileContractRegistry futureEIPs(final GasCalculator gasCalculator) {
|
|
PrecompileContractRegistry precompileContractRegistry = new PrecompileContractRegistry();
|
|
populateForFutureEIPs(precompileContractRegistry, gasCalculator);
|
|
return precompileContractRegistry;
|
|
}
|
|
|
|
/**
|
|
* Populate registry for Future EIPs.
|
|
*
|
|
* @param registry the registry
|
|
* @param gasCalculator the gas calculator
|
|
*/
|
|
static void populateForFutureEIPs(
|
|
final PrecompileContractRegistry registry, final GasCalculator gasCalculator) {
|
|
- populateForCancun(registry, gasCalculator);
|
|
+ // AERE "AerePQC" fork: full Osaka precompile set plus native post-quantum verifiers.
|
|
+ // Activated on a running chain via genesis config "futureEipsTime"; no re-genesis needed.
|
|
+ populateForOsaka(registry, gasCalculator);
|
|
+
|
|
+ // Native post-quantum precompiles (Bouncy Castle BCPQC verifiers).
|
|
+ registry.put(Address.AERE_FALCON512, new Falcon512PrecompiledContract(gasCalculator));
|
|
+ registry.put(Address.AERE_FALCON1024, new Falcon1024PrecompiledContract(gasCalculator));
|
|
+ registry.put(Address.AERE_MLDSA44, new MLDSA44PrecompiledContract(gasCalculator));
|
|
+ registry.put(Address.AERE_SLHDSA128S, new SLHDSA128sPrecompiledContract(gasCalculator));
|
|
+ registry.put(Address.AERE_SHAKE256, new SHAKE256PrecompiledContract(gasCalculator));
|
|
}
|
|
}
|
|
diff --git a/evm/src/main/java/org/hyperledger/besu/evm/precompile/SHAKE256PrecompiledContract.java b/evm/src/main/java/org/hyperledger/besu/evm/precompile/SHAKE256PrecompiledContract.java
|
|
new file mode 100644
|
|
index 000000000..0c20e6a66
|
|
--- /dev/null
|
|
+++ b/evm/src/main/java/org/hyperledger/besu/evm/precompile/SHAKE256PrecompiledContract.java
|
|
@@ -0,0 +1,96 @@
|
|
+/*
|
|
+ * Copyright contributors to the AERE Network.
|
|
+ *
|
|
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
|
+ * the License. You may obtain a copy of the License at
|
|
+ *
|
|
+ * http://www.apache.org/licenses/LICENSE-2.0
|
|
+ *
|
|
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
|
+ * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
|
+ * specific language governing permissions and limitations under the License.
|
|
+ *
|
|
+ * SPDX-License-Identifier: Apache-2.0
|
|
+ */
|
|
+package org.hyperledger.besu.evm.precompile;
|
|
+
|
|
+import org.hyperledger.besu.evm.frame.MessageFrame;
|
|
+import org.hyperledger.besu.evm.gascalculator.GasCalculator;
|
|
+
|
|
+import jakarta.validation.constraints.NotNull;
|
|
+import org.apache.tuweni.bytes.Bytes;
|
|
+import org.bouncycastle.crypto.digests.SHAKEDigest;
|
|
+
|
|
+/**
|
|
+ * AERE PQC precompile: SHAKE256 extendable-output function (FIPS 202).
|
|
+ *
|
|
+ * <p>Input layout: outLen (32 bytes, big-endian, capped at MAX_OUTPUT) || data (arbitrary length).
|
|
+ * Output: exactly {@code outLen} bytes of SHAKE256(data).
|
|
+ *
|
|
+ * <p>SHAKE256 is the hashing bottleneck inside Falcon, ML-DSA and SLH-DSA; exposing it natively lets
|
|
+ * on-chain PQC flows offload the hot path to Bouncy Castle rather than hand-rolled Solidity. The
|
|
+ * provider we ship, bcprov-jdk18on, is not a FIPS validated module and we know of no published
|
|
+ * third-party audit of it; what is citable is byte-for-byte agreement with the NIST KAT and ACVP
|
|
+ * vectors, in both directions. See AERE-NIST-VALIDATION-STATUS.md section 3.4.
|
|
+ */
|
|
+public class SHAKE256PrecompiledContract extends AbstractPrecompiledContract {
|
|
+
|
|
+ /** Upper bound on requested output length to keep gas/allocation bounded. */
|
|
+ static final int MAX_OUTPUT = 1 << 16; // 65536 bytes
|
|
+
|
|
+ private static final int BASE_GAS = 60;
|
|
+ private static final int GAS_PER_WORD = 12;
|
|
+
|
|
+ /**
|
|
+ * Instantiates a new SHAKE256 precompiled contract.
|
|
+ *
|
|
+ * @param gasCalculator the gas calculator
|
|
+ */
|
|
+ SHAKE256PrecompiledContract(final GasCalculator gasCalculator) {
|
|
+ super("AereSHAKE256", gasCalculator);
|
|
+ }
|
|
+
|
|
+ private static int outputLength(final Bytes input) {
|
|
+ if (input.size() < 32) {
|
|
+ return 0;
|
|
+ }
|
|
+ // Big-endian 32-byte length; only the low 4 bytes are honoured, then capped.
|
|
+ long v = input.slice(28, 4).toLong() & 0xFFFFFFFFL;
|
|
+ // If any of the high 28 bytes are non-zero the value is enormous; cap regardless.
|
|
+ if (!input.slice(0, 28).isZero()) {
|
|
+ return MAX_OUTPUT;
|
|
+ }
|
|
+ if (v > MAX_OUTPUT) {
|
|
+ return MAX_OUTPUT;
|
|
+ }
|
|
+ return (int) v;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public long gasRequirement(final Bytes input) {
|
|
+ int outLen = outputLength(input);
|
|
+ int dataLen = input.size() < 32 ? 0 : input.size() - 32;
|
|
+ long words = ((long) dataLen + 31) / 32 + ((long) outLen + 31) / 32;
|
|
+ return BASE_GAS + GAS_PER_WORD * words;
|
|
+ }
|
|
+
|
|
+ @NotNull
|
|
+ @Override
|
|
+ public PrecompileContractResult computePrecompile(
|
|
+ final Bytes input, @NotNull final MessageFrame messageFrame) {
|
|
+ if (input.size() < 32) {
|
|
+ return PrecompileContractResult.success(Bytes.EMPTY);
|
|
+ }
|
|
+ final int outLen = outputLength(input);
|
|
+ final byte[] data = input.slice(32).toArrayUnsafe();
|
|
+ final SHAKEDigest digest = new SHAKEDigest(256);
|
|
+ if (data.length > 0) {
|
|
+ digest.update(data, 0, data.length);
|
|
+ }
|
|
+ final byte[] out = new byte[outLen];
|
|
+ if (outLen > 0) {
|
|
+ digest.doFinal(out, 0, outLen);
|
|
+ }
|
|
+ return PrecompileContractResult.success(Bytes.wrap(out));
|
|
+ }
|
|
+}
|
|
diff --git a/evm/src/main/java/org/hyperledger/besu/evm/precompile/SLHDSA128sPrecompiledContract.java b/evm/src/main/java/org/hyperledger/besu/evm/precompile/SLHDSA128sPrecompiledContract.java
|
|
new file mode 100644
|
|
index 000000000..2666602f4
|
|
--- /dev/null
|
|
+++ b/evm/src/main/java/org/hyperledger/besu/evm/precompile/SLHDSA128sPrecompiledContract.java
|
|
@@ -0,0 +1,84 @@
|
|
+/*
|
|
+ * Copyright contributors to the AERE Network.
|
|
+ *
|
|
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
|
+ * the License. You may obtain a copy of the License at
|
|
+ *
|
|
+ * http://www.apache.org/licenses/LICENSE-2.0
|
|
+ *
|
|
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
|
+ * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
|
+ * specific language governing permissions and limitations under the License.
|
|
+ *
|
|
+ * SPDX-License-Identifier: Apache-2.0
|
|
+ */
|
|
+package org.hyperledger.besu.evm.precompile;
|
|
+
|
|
+import org.hyperledger.besu.evm.frame.MessageFrame;
|
|
+import org.hyperledger.besu.evm.gascalculator.GasCalculator;
|
|
+
|
|
+import jakarta.validation.constraints.NotNull;
|
|
+import org.apache.tuweni.bytes.Bytes;
|
|
+import org.apache.tuweni.bytes.Bytes32;
|
|
+import org.bouncycastle.pqc.crypto.slhdsa.SLHDSAParameters;
|
|
+import org.bouncycastle.pqc.crypto.slhdsa.SLHDSAPublicKeyParameters;
|
|
+import org.bouncycastle.pqc.crypto.slhdsa.SLHDSASigner;
|
|
+
|
|
+/**
|
|
+ * AERE PQC precompile: SLH-DSA-SHA2-128s (SPHINCS+, FIPS 205) signature verification via the
|
|
+ * INTERNAL interface (slh_verify_internal, Algorithm 20 — message hashed directly, no prefix).
|
|
+ *
|
|
+ * <p>Input: {@code pk(32) || sig(7856) || message(rest)}. Output: 32-byte word, {@code ...01} valid
|
|
+ * else {@code ...00}. The internal verifier is reached by subclassing Bouncy Castle's
|
|
+ * {@link SLHDSASigner} and calling its {@code protected internalVerifySignature}.
|
|
+ */
|
|
+public class SLHDSA128sPrecompiledContract extends AbstractPrecompiledContract {
|
|
+
|
|
+ static final int PK_LEN = 32;
|
|
+ static final int SIG_LEN = 7856;
|
|
+ private static final long GAS = 350_000L;
|
|
+
|
|
+ /** Subclass exposing Bouncy Castle's protected internal (slh_verify_internal) verifier. */
|
|
+ private static final class InternalVerifier extends SLHDSASigner {
|
|
+ boolean verifyInternal(final byte[] message, final byte[] signature) {
|
|
+ return internalVerifySignature(message, signature);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * Instantiates a new SLH-DSA-128s precompiled contract.
|
|
+ *
|
|
+ * @param gasCalculator the gas calculator
|
|
+ */
|
|
+ SLHDSA128sPrecompiledContract(final GasCalculator gasCalculator) {
|
|
+ super("AereSLHDSA128s", gasCalculator);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public long gasRequirement(final Bytes input) {
|
|
+ return GAS;
|
|
+ }
|
|
+
|
|
+ @NotNull
|
|
+ @Override
|
|
+ public PrecompileContractResult computePrecompile(
|
|
+ final Bytes input, @NotNull final MessageFrame messageFrame) {
|
|
+ boolean valid = false;
|
|
+ if (input.size() >= PK_LEN + SIG_LEN) {
|
|
+ try {
|
|
+ final byte[] pk = input.slice(0, PK_LEN).toArrayUnsafe();
|
|
+ final byte[] sig = input.slice(PK_LEN, SIG_LEN).toArrayUnsafe();
|
|
+ final byte[] message = input.slice(PK_LEN + SIG_LEN).toArrayUnsafe();
|
|
+ final SLHDSAPublicKeyParameters pub =
|
|
+ new SLHDSAPublicKeyParameters(SLHDSAParameters.sha2_128s, pk);
|
|
+ final InternalVerifier verifier = new InternalVerifier();
|
|
+ verifier.init(false, pub);
|
|
+ valid = verifier.verifyInternal(message, sig);
|
|
+ } catch (final Throwable t) {
|
|
+ valid = false;
|
|
+ }
|
|
+ }
|
|
+ return PrecompileContractResult.success(
|
|
+ valid ? Bytes32.leftPad(Bytes.of((byte) 1)) : Bytes32.ZERO);
|
|
+ }
|
|
+}
|