diff --git a/build.gradle b/build.gradle index 4af0a4eef4a..b9d3821c316 100644 --- a/build.gradle +++ b/build.gradle @@ -126,6 +126,14 @@ allprojects { maven { url "https://consensys.bintray.com/pegasys-repo" } maven { url "https://splunk.jfrog.io/splunk/ext-releases-local" } maven { url "https://dl.bintray.com/open-telemetry/maven" } + maven { + name = "GitHubPackages" + url = uri("https://maven.pkg.github.com/lacchain/liboqs-java/") + credentials { + username = project.findProperty("gpr.user") ?: System.getenv("USERNAME") + password = project.findProperty("gpr.key") ?: System.getenv("TOKEN") + } + } } dependencies { errorprone "com.google.errorprone:error_prone_core" } diff --git a/config/src/main/java/org/hyperledger/besu/config/GenesisConfigOptions.java b/config/src/main/java/org/hyperledger/besu/config/GenesisConfigOptions.java index e895169065c..14acd68c55d 100644 --- a/config/src/main/java/org/hyperledger/besu/config/GenesisConfigOptions.java +++ b/config/src/main/java/org/hyperledger/besu/config/GenesisConfigOptions.java @@ -171,6 +171,8 @@ public interface GenesisConfigOptions { */ OptionalLong getThanosBlockNumber(); + OptionalLong getLacchainPostQuantumBlockNumber(); + Optional getChainId(); OptionalInt getContractSizeLimit(); diff --git a/config/src/main/java/org/hyperledger/besu/config/JsonGenesisConfigOptions.java b/config/src/main/java/org/hyperledger/besu/config/JsonGenesisConfigOptions.java index 661f0388916..30a42d939e3 100644 --- a/config/src/main/java/org/hyperledger/besu/config/JsonGenesisConfigOptions.java +++ b/config/src/main/java/org/hyperledger/besu/config/JsonGenesisConfigOptions.java @@ -283,6 +283,11 @@ public OptionalLong getThanosBlockNumber() { return getOptionalLong("thanosblock"); } + @Override + public OptionalLong getLacchainPostQuantumBlockNumber() { + return getOptionalLong("lacchainpqblock"); + } + @Override public Optional getChainId() { return getOptionalBigInteger("chainid"); diff --git a/config/src/main/java/org/hyperledger/besu/config/StubGenesisConfigOptions.java b/config/src/main/java/org/hyperledger/besu/config/StubGenesisConfigOptions.java index 3f421132ed5..67ea6a4c843 100644 --- a/config/src/main/java/org/hyperledger/besu/config/StubGenesisConfigOptions.java +++ b/config/src/main/java/org/hyperledger/besu/config/StubGenesisConfigOptions.java @@ -49,6 +49,7 @@ public class StubGenesisConfigOptions implements GenesisConfigOptions { private final OptionalLong aghartaBlockNumber = OptionalLong.empty(); private final OptionalLong phoenixBlockNumber = OptionalLong.empty(); private final OptionalLong thanosBlockNumber = OptionalLong.empty(); + private final OptionalLong lacchainPostQuantumBlockNumber = OptionalLong.empty(); private Optional chainId = Optional.empty(); private OptionalInt contractSizeLimit = OptionalInt.empty(); private OptionalInt stackSizeLimit = OptionalInt.empty(); @@ -200,6 +201,11 @@ public OptionalLong getThanosBlockNumber() { return thanosBlockNumber; } + @Override + public OptionalLong getLacchainPostQuantumBlockNumber() { + return lacchainPostQuantumBlockNumber; + } + @Override public OptionalInt getContractSizeLimit() { return contractSizeLimit; diff --git a/config/src/main/resources/dev.json b/config/src/main/resources/dev.json index 05a4f830ba7..9e92a1c836e 100644 --- a/config/src/main/resources/dev.json +++ b/config/src/main/resources/dev.json @@ -9,6 +9,7 @@ "byzantiumBlock": 0, "constantinopleBlock": 0, "petersburgBlock": 0, + "lacchainpqblock": 0, "contractSizeLimit": 2147483647, "ethash": { "fixeddifficulty": 100 diff --git a/docker/openjdk-11/Dockerfile b/docker/openjdk-11/Dockerfile index ada2e42d5fe..46bd450791f 100644 --- a/docker/openjdk-11/Dockerfile +++ b/docker/openjdk-11/Dockerfile @@ -1,5 +1,22 @@ -FROM openjdk:11.0.7-jre-slim-buster +FROM openjdk:11.0.7-jre-slim-buster as base + +RUN apt-get update && apt-get install --no-install-recommends -yV \ + dpkg-dev \ + wget \ + ca-certificates + +RUN mkdir /debs/ +RUN wget --directory-prefix=/debs/ https://github.com/lacchain/liboqs-debian/releases/download/0.4.0/liboqs_0.4.0_amd64.deb +RUN wget --directory-prefix=/debs/ https://github.com/lacchain/liboqs-debian/releases/download/0.4.0/SHA256SUMS +RUN cd /debs/ && sha256sum --check --ignore-missing --status SHA256SUMS && dpkg-scanpackages . /dev/null | gzip -9c > Packages.gz +RUN echo "deb [trusted=yes] file:/debs ./" >> /etc/apt/sources.list + +FROM base as runner + +RUN apt-get update && apt-get install --no-install-recommends -yV \ + liboqs \ + && rm -rf /var/lib/apt/lists/* RUN adduser --disabled-password --gecos "" --home /opt/besu besu && \ chown besu:besu /opt/besu diff --git a/ethereum/core/build.gradle b/ethereum/core/build.gradle index a64a893c214..c2c2a4416ba 100644 --- a/ethereum/core/build.gradle +++ b/ethereum/core/build.gradle @@ -48,6 +48,7 @@ dependencies { implementation 'org.apache.tuweni:units' implementation 'org.hyperledger.besu:bls12-381' implementation 'org.immutables:value-annotations' + implementation 'org.openquantumsafe:liboqs-java' implementation 'org.xerial.snappy:snappy-java' diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/Address.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/Address.java index 6643dc1c96b..7cda9e04a68 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/Address.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/Address.java @@ -50,6 +50,7 @@ public class Address extends DelegatingBytes implements org.hyperledger.besu.plu public static final Address BLS12_PAIRING = Address.precompiled(0x10); public static final Address BLS12_MAP_FP_TO_G1 = Address.precompiled(0x11); public static final Address BLS12_MAP_FP2_TO_G2 = Address.precompiled(0x12); + public static final Address LACCHAIN_FALCON = Address.precompiled(0x13); // Last address that can be generated for a pre-compiled contract public static final Integer PRIVACY = Byte.MAX_VALUE - 1; diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/LacchainProtocolSpecs.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/LacchainProtocolSpecs.java new file mode 100644 index 00000000000..e0cd009fe0d --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/LacchainProtocolSpecs.java @@ -0,0 +1,50 @@ +/* + * Copyright IADB. + * + * 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.ethereum.mainnet; + +import org.hyperledger.besu.ethereum.core.Account; +import org.hyperledger.besu.ethereum.core.Address; +import org.hyperledger.besu.ethereum.mainnet.precompiles.FalconPrecompiledContract; + +import java.math.BigInteger; +import java.util.Optional; +import java.util.OptionalInt; + +public class LacchainProtocolSpecs { + public static ProtocolSpecBuilder postQuantumDefinition( + final Optional chainId, + final OptionalInt contractSizeLimit, + final OptionalInt configStackSizeLimit, + final boolean enableRevertReason, + final boolean quorumCompatibilityMode) { + return MainnetProtocolSpecs.istanbulDefinition( + chainId, + contractSizeLimit, + configStackSizeLimit, + enableRevertReason, + quorumCompatibilityMode) + .precompileContractRegistryBuilder( + precompiledContractConfiguration -> { + PrecompileContractRegistry lacchainContractsRegistry = + MainnetPrecompiledContractRegistries.istanbul(precompiledContractConfiguration); + lacchainContractsRegistry.put( + Address.LACCHAIN_FALCON, + Account.DEFAULT_VERSION, + new FalconPrecompiledContract( + precompiledContractConfiguration.getGasCalculator())); + return lacchainContractsRegistry; + }); + } +} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ProtocolScheduleBuilder.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ProtocolScheduleBuilder.java index 093d314882b..318707e24ca 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ProtocolScheduleBuilder.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ProtocolScheduleBuilder.java @@ -307,6 +307,15 @@ public ProtocolSchedule createProtocolSchedule() { isRevertReasonEnabled, config.getEcip1017EraRounds(), quorumCompatibilityMode)); + addProtocolSpec( + protocolSchedule, + config.getLacchainPostQuantumBlockNumber(), + LacchainProtocolSpecs.postQuantumDefinition( + chainId, + config.getContractSizeLimit(), + config.getEvmStackSize(), + isRevertReasonEnabled, + quorumCompatibilityMode)); LOG.info("Protocol schedule created with milestones: {}", protocolSchedule.listMilestones()); return protocolSchedule; diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/precompiles/FalconPrecompiledContract.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/precompiles/FalconPrecompiledContract.java new file mode 100644 index 00000000000..07d817713db --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/precompiles/FalconPrecompiledContract.java @@ -0,0 +1,93 @@ +/* + * Copyright IADB. + * + * 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.ethereum.mainnet.precompiles; + +import static java.nio.charset.StandardCharsets.UTF_8; + +import org.hyperledger.besu.crypto.Hash; +import org.hyperledger.besu.ethereum.core.Gas; +import org.hyperledger.besu.ethereum.mainnet.AbstractPrecompiledContract; +import org.hyperledger.besu.ethereum.vm.GasCalculator; +import org.hyperledger.besu.ethereum.vm.MessageFrame; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.bytes.Bytes32; +import org.openquantumsafe.Signature; + +/** + * note: Liboqs - random number generation defaults to /dev/urandom a better form is to use the + * OQS_RAND_agl_openssl "OpenSSL" random number algorithm, then set the environment default engine + * to IBRand for quantum entropy + */ +public class FalconPrecompiledContract extends AbstractPrecompiledContract { + + private static final Logger LOG = LogManager.getLogger(); + + private static final Bytes METHOD_ABI = + Hash.keccak256(Bytes.of("verify(bytes,bytes,bytes)".getBytes(UTF_8))).slice(0, 4); + // taken from liboqs C sig.h header, OQS_SIG_alg_falcon_512 + private static final String SIGNATURE_ALGORITHM = "Falcon-512"; + + public FalconPrecompiledContract(final GasCalculator gasCalculator) { + super("Falcon", gasCalculator); + } + + @Override + public Gas gasRequirement(final Bytes input) { + return gasCalculator().sha256PrecompiledContractGasCost(input); + } + + @Override + public Bytes compute(final Bytes methodInput, final MessageFrame messageFrame) { + Bytes methodAbi = methodInput.slice(0, METHOD_ABI.size()); + if (!methodAbi.xor(METHOD_ABI).isZero()) { + throw new IllegalArgumentException("Unexpected method ABI: " + methodAbi.toHexString()); + } + Bytes input = methodInput.slice(METHOD_ABI.size()); + int signatureOffset = input.slice(0, 32).trimLeadingZeros().toInt(); + int pubKeyOffset = input.slice(32, 32).trimLeadingZeros().toInt(); + int dataOffset = input.slice(64, 32).trimLeadingZeros().toInt(); + + int signatureLength = input.slice(signatureOffset, 32).trimLeadingZeros().toInt(); + int pubKeyLength = input.slice(pubKeyOffset, 32).trimLeadingZeros().toInt(); + int dataLength = input.slice(dataOffset, 32).trimLeadingZeros().toInt(); + + Bytes signatureSlice = input.slice(signatureOffset + 32, signatureLength); + Bytes pubKeySlice = input.slice(pubKeyOffset + 32, pubKeyLength); + Bytes dataSlice = input.slice(dataOffset + 32, dataLength); + + if (LOG.isTraceEnabled()) { + LOG.trace( + "{} verify: signature={}, pubKey={}, data={}", + SIGNATURE_ALGORITHM, + signatureSlice.toHexString(), + pubKeySlice.toHexString(), + dataSlice.toHexString()); + } + Signature verifier = new Signature(SIGNATURE_ALGORITHM); + final boolean verifies = + verifier.verify(dataSlice.toArray(), signatureSlice.toArray(), pubKeySlice.toArray()); + + if (verifies) { + LOG.debug("Signature is VALID"); + return Bytes32.leftPad(Bytes.of(0)); + } else { + LOG.debug("Signature is INVALID"); + return Bytes32.leftPad(Bytes.of(1)); + } + } +} diff --git a/gradle/versions.gradle b/gradle/versions.gradle index e0260acf034..ccd74f69726 100644 --- a/gradle/versions.gradle +++ b/gradle/versions.gradle @@ -129,6 +129,8 @@ dependencyManagement { dependency 'org.springframework.security:spring-security-crypto:5.2.3.RELEASE' + dependency 'org.openquantumsafe:liboqs-java:1.1-SNAPSHOT' + dependency 'org.web3j:abi:4.5.15' dependency 'org.web3j:besu:4.5.15' dependency 'org.web3j:core:4.5.15'