Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add SECP256R1 support #2008

Merged
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
aca2e07
WIP: Add SECP256R1 encryption
daniel-iobuilders Mar 12, 2021
71ebcd0
Added correct values to generate signatures
daniel-iobuilders Mar 12, 2021
57aaf22
Replaced exception for enableNative with log message, added compressi…
daniel-iobuilders Mar 12, 2021
a7a99e7
Merge branch 'master' into add_secp256r1_class
daniel-iobuilders Mar 17, 2021
43d3b92
Merged changes from master
daniel-iobuilders Mar 17, 2021
1f3bd1a
Added acceptance tests for node handshake and account transfer
daniel-iobuilders Mar 19, 2021
22390e2
Merge branch 'master' into add_secp256r1_class
daniel-iobuilders Mar 22, 2021
d05a455
Merge branch 'master' into add_secp256r1_class
daniel-iobuilders Mar 24, 2021
8b162fc
Added missing merged file
daniel-iobuilders Mar 24, 2021
80cfd8b
WIP: Acceptance test
daniel-iobuilders Mar 29, 2021
d138d13
Added SECP256R1 configuration to test genesis file, added initialisat…
daniel-iobuilders Apr 4, 2021
936479c
Merge branch 'master' into add_secp256r1_class
daniel-iobuilders Apr 4, 2021
ab6e3c6
Removed unnecessary asterisks in file headers
daniel-iobuilders Apr 4, 2021
7eefe25
Resetted test setup
daniel-iobuilders Apr 4, 2021
3ba1099
Moved custom signing of transaction in acceptance tests to utility class
daniel-iobuilders Apr 7, 2021
56b62b9
removed unused extraData from genesis file for secp256r1, reversed ch…
daniel-iobuilders Apr 7, 2021
5bac4bf
Peer discovery needs to use alternative elliptic curves, if not it ca…
daniel-iobuilders Apr 7, 2021
92097a9
Merge branch 'master' into add_secp256r1_class
daniel-iobuilders Apr 7, 2021
0af80d3
Merge branch 'master' into add_secp256r1_class
daniel-iobuilders Apr 8, 2021
a879f6d
Merge branch 'master' into add_secp256r1_class
daniel-iobuilders Apr 8, 2021
cb7c83a
Add dynamic key name to ENR public key field
daniel-iobuilders Apr 15, 2021
7d45350
Merge branch 'master' into add_secp256r1_class
daniel-iobuilders Apr 15, 2021
31a6fdd
Merge branch 'master' into add_secp256r1_class
daniel-iobuilders Apr 21, 2021
3082e0f
fixed wrong class name in SECP256R1Test
daniel-iobuilders Apr 22, 2021
1d10aef
Merge branch 'master' into add_secp256r1_class
daniel-iobuilders Apr 22, 2021
af6ce2a
Merge branch 'master' into add_secp256r1_class
lucassaldanha Apr 22, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import org.hyperledger.besu.cli.config.NetworkName;
import org.hyperledger.besu.crypto.KeyPair;
import org.hyperledger.besu.crypto.KeyPairUtil;
import org.hyperledger.besu.crypto.SignatureAlgorithm;
import org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcConfiguration;
import org.hyperledger.besu.ethereum.api.jsonrpc.websocket.WebSocketConfiguration;
import org.hyperledger.besu.ethereum.core.Address;
Expand All @@ -35,6 +36,7 @@
import org.hyperledger.besu.tests.acceptance.dsl.node.configuration.genesis.GenesisConfigurationProvider;
import org.hyperledger.besu.tests.acceptance.dsl.transaction.NodeRequests;
import org.hyperledger.besu.tests.acceptance.dsl.transaction.Transaction;
import org.hyperledger.besu.tests.acceptance.dsl.transaction.TransactionWithSignatureAlgorithm;
import org.hyperledger.besu.tests.acceptance.dsl.transaction.admin.AdminRequestFactory;
import org.hyperledger.besu.tests.acceptance.dsl.transaction.bft.BftRequestFactory;
import org.hyperledger.besu.tests.acceptance.dsl.transaction.bft.ConsensusType;
Expand Down Expand Up @@ -691,6 +693,13 @@ public <T> T execute(final Transaction<T> transaction) {
return transaction.execute(nodeRequests());
}

@Override
public <T> T execute(
final TransactionWithSignatureAlgorithm<T> transaction,
final SignatureAlgorithm signatureAlgorithm) {
return transaction.execute(nodeRequests(), signatureAlgorithm);
}

@Override
public void verify(final Condition expected) {
expected.verify(this);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,17 @@
*/
package org.hyperledger.besu.tests.acceptance.dsl.node;

import org.hyperledger.besu.crypto.SignatureAlgorithm;
import org.hyperledger.besu.tests.acceptance.dsl.condition.Condition;
import org.hyperledger.besu.tests.acceptance.dsl.transaction.Transaction;
import org.hyperledger.besu.tests.acceptance.dsl.transaction.TransactionWithSignatureAlgorithm;

public interface Node {

<T> T execute(Transaction<T> transaction);

<T> T execute(
TransactionWithSignatureAlgorithm<T> transaction, SignatureAlgorithm signatureAlgorithm);

void verify(final Condition expected);
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
import static java.util.Arrays.asList;
import static java.util.stream.Collectors.toList;

import org.hyperledger.besu.config.GenesisConfigFile;
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
import org.hyperledger.besu.crypto.SignatureAlgorithmType;
import org.hyperledger.besu.enclave.EnclaveFactory;
import org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcConfiguration;
import org.hyperledger.besu.ethereum.api.jsonrpc.RpcApi;
Expand All @@ -38,6 +41,7 @@
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
Expand All @@ -50,6 +54,8 @@ public class BesuNodeFactory {
private final NodeConfigurationFactory node = new NodeConfigurationFactory();

public BesuNode create(final BesuNodeConfiguration config) throws IOException {
instantiateSignatureAlgorithmFactory(config);

return new BesuNode(
config.getName(),
config.getDataPath(),
Expand Down Expand Up @@ -458,4 +464,32 @@ public BesuNode createNodeWithStaticNodes(final String name, final List<Node> st
public BesuNode runCommand(final String command) throws IOException {
return create(new BesuNodeConfigurationBuilder().name("run " + command).run(command).build());
}

private void instantiateSignatureAlgorithmFactory(final BesuNodeConfiguration config) {
if (SignatureAlgorithmFactory.isInstanceSet()) {
return;
}

Optional<String> ecCurve = getEcCurveFromGenesisFile(config);

if (ecCurve.isEmpty()) {
SignatureAlgorithmFactory.setDefaultInstance();
return;
}

SignatureAlgorithmFactory.setInstance(SignatureAlgorithmType.create(ecCurve.get()));
}

private Optional<String> getEcCurveFromGenesisFile(final BesuNodeConfiguration config) {
Optional<String> genesisConfig =
config.getGenesisConfigProvider().create(Collections.emptyList());

if (genesisConfig.isEmpty()) {
return Optional.empty();
}

GenesisConfigFile genesisConfigFile = GenesisConfigFile.fromConfig(genesisConfig.get());

return genesisConfigFile.getConfigOptions().getEcCurve();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* Copyright ConsenSys AG.
*
* 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.tests.acceptance.dsl.transaction;

import org.hyperledger.besu.crypto.SECPPrivateKey;
import org.hyperledger.besu.crypto.SECPSignature;
import org.hyperledger.besu.crypto.SignatureAlgorithm;
import org.hyperledger.besu.tests.acceptance.dsl.account.Account;

import java.util.List;

import org.apache.tuweni.bytes.Bytes32;
import org.web3j.crypto.Credentials;
import org.web3j.crypto.RawTransaction;
import org.web3j.crypto.Sign;
import org.web3j.crypto.TransactionEncoder;
import org.web3j.rlp.RlpEncoder;
import org.web3j.rlp.RlpList;
import org.web3j.rlp.RlpType;
import org.web3j.utils.Numeric;

public class SignUtil {

private SignUtil() {}

public static String signTransaction(
final RawTransaction transaction,
final Account sender,
final SignatureAlgorithm signatureAlgorithm) {
byte[] encodedTransaction = TransactionEncoder.encode(transaction);

Credentials credentials = sender.web3jCredentialsOrThrow();
SECPPrivateKey privateKey =
signatureAlgorithm.createPrivateKey(credentials.getEcKeyPair().getPrivateKey());

byte[] transactionHash = org.web3j.crypto.Hash.sha3(encodedTransaction);

SECPSignature secpSignature =
signatureAlgorithm.sign(
Bytes32.wrap(transactionHash), signatureAlgorithm.createKeyPair(privateKey));

Sign.SignatureData signature =
new Sign.SignatureData(
// In Ethereum transaction 27 is added to recId (v)
// See https://ethereum.github.io/yellowpaper/paper.pdf
// Appendix F. Signing Transactions (281)
(byte) (secpSignature.getRecId() + 27),
secpSignature.getR().toByteArray(),
secpSignature.getS().toByteArray());
List<RlpType> values = TransactionEncoder.asRlpValues(transaction, signature);
RlpList rlpList = new RlpList(values);
final byte[] encodedSignedTransaction = RlpEncoder.encode(rlpList);

return Numeric.toHexString(encodedSignedTransaction);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* Copyright ConsenSys AG.
*
* 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.tests.acceptance.dsl.transaction;

import org.hyperledger.besu.crypto.SignatureAlgorithm;

@FunctionalInterface
public interface TransactionWithSignatureAlgorithm<T> {
T execute(final NodeRequests node, final SignatureAlgorithm signatureAlgorithm);
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,14 @@
*/
package org.hyperledger.besu.tests.acceptance.dsl.transaction.account;

import static org.web3j.utils.Numeric.toHexString;

import org.hyperledger.besu.crypto.SignatureAlgorithm;
import org.hyperledger.besu.ethereum.core.Hash;
import org.hyperledger.besu.tests.acceptance.dsl.account.Account;
import org.hyperledger.besu.tests.acceptance.dsl.blockchain.Amount;
import org.hyperledger.besu.tests.acceptance.dsl.transaction.NodeRequests;
import org.hyperledger.besu.tests.acceptance.dsl.transaction.SignUtil;
import org.hyperledger.besu.tests.acceptance.dsl.transaction.Transaction;
import org.hyperledger.besu.tests.acceptance.dsl.transaction.TransactionWithSignatureAlgorithm;

import java.io.IOException;
import java.math.BigDecimal;
Expand All @@ -31,8 +32,10 @@
import org.web3j.crypto.TransactionEncoder;
import org.web3j.utils.Convert;
import org.web3j.utils.Convert.Unit;
import org.web3j.utils.Numeric;

public class TransferTransaction implements Transaction<Hash> {
public class TransferTransaction
implements Transaction<Hash>, TransactionWithSignatureAlgorithm<Hash> {

/** Price for each for each GAS units in this transaction (wei). */
private static final BigInteger MINIMUM_GAS_PRICE = BigInteger.valueOf(1000);
Expand Down Expand Up @@ -64,33 +67,41 @@ public TransferTransaction(
@Override
public Hash execute(final NodeRequests node) {
final String signedTransactionData = signedTransactionData();
try {
return Hash.fromHexString(
node.eth().ethSendRawTransaction(signedTransactionData).send().getTransactionHash());
} catch (final IOException e) {
throw new RuntimeException(e);
}
return sendRawTransaction(node, signedTransactionData);
}

@Override
public Hash execute(final NodeRequests node, final SignatureAlgorithm signatureAlgorithm) {
final String signedTransactionData =
signedTransactionDataWithSignatureAlgorithm(signatureAlgorithm);
return sendRawTransaction(node, signedTransactionData);
}

public Amount executionCost() {
return Amount.wei(INTRINSIC_GAS.multiply(gasPrice));
}

public String signedTransactionData() {
final Optional<BigInteger> nonce = getNonce();

final RawTransaction transaction =
RawTransaction.createEtherTransaction(
nonce.orElse(nonce.orElseGet(sender::getNextNonce)),
gasPrice,
INTRINSIC_GAS,
recipient.getAddress(),
Convert.toWei(transferAmount, transferUnit).toBigIntegerExact());
final RawTransaction transaction = createRawTransaction();

return toHexString(
return Numeric.toHexString(
TransactionEncoder.signMessage(transaction, sender.web3jCredentialsOrThrow()));
}

private String signedTransactionDataWithSignatureAlgorithm(
daniel-iobuilders marked this conversation as resolved.
Show resolved Hide resolved
final SignatureAlgorithm signatureAlgorithm) {
return SignUtil.signTransaction(createRawTransaction(), sender, signatureAlgorithm);
}

private Hash sendRawTransaction(final NodeRequests node, final String signedTransactionData) {
try {
return Hash.fromHexString(
node.eth().ethSendRawTransaction(signedTransactionData).send().getTransactionHash());
} catch (final IOException e) {
throw new RuntimeException(e);
}
}

private Optional<BigInteger> getNonce() {
return nonce == null ? Optional.empty() : Optional.of(nonce);
}
Expand All @@ -108,4 +119,15 @@ private BigInteger convertGasPriceToWei(final Amount unconverted) {

return price;
}

private RawTransaction createRawTransaction() {
final Optional<BigInteger> nonce = getNonce();

return RawTransaction.createEtherTransaction(
nonce.orElse(nonce.orElseGet(sender::getNextNonce)),
gasPrice,
INTRINSIC_GAS,
recipient.getAddress(),
Convert.toWei(transferAmount, transferUnit).toBigIntegerExact());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Copyright ConsenSys AG.
*
* 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.tests.acceptance.crypto;

import static org.assertj.core.api.Assertions.assertThat;

import org.hyperledger.besu.crypto.SECP256R1;
import org.hyperledger.besu.ethereum.core.Hash;
import org.hyperledger.besu.tests.acceptance.dsl.AcceptanceTestBase;
import org.hyperledger.besu.tests.acceptance.dsl.account.Account;
import org.hyperledger.besu.tests.acceptance.dsl.node.Node;

import org.junit.Before;
import org.junit.Test;

public class SECP256R1AcceptanceTest extends AcceptanceTestBase {
private Node minerNode;
private Node fullNode;

protected static final String GENESIS_FILE = "/crypto/secp256r1.json";

@Before
public void setUp() throws Exception {
minerNode = besu.createCustomGenesisNode("node1", GENESIS_FILE, true, true);
fullNode = besu.createCustomGenesisNode("node2", GENESIS_FILE, false);
cluster.start(minerNode, fullNode);
}

@Test
public void shouldConnectToOtherPeer() {
minerNode.verify(net.awaitPeerCount(1));
fullNode.verify(net.awaitPeerCount(1));
}

@Test
public void transactionShouldBeSuccessful() {
final Account recipient = accounts.createAccount("recipient");

final Hash transactionHash =
minerNode.execute(accountTransactions.createTransfer(recipient, 5), new SECP256R1());
assertThat(transactionHash).isNotNull();
cluster.verify(recipient.balanceEquals(5));
}
}
23 changes: 23 additions & 0 deletions acceptance-tests/tests/src/test/resources/crypto/secp256r1.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"config": {
"chainId": 1981,
"ecCurve": "secp256r1",
"constantinoplefixblock": 0,
"ethash": {
"fixeddifficulty": 1000
}
},
"nonce": "0x0",
"timestamp": "0x58ee40ba",
"gasLimit": "0x1fffffffffffff",
"difficulty": "0x1",
"mixHash": "0x63746963616c2062797a616e74696e65206661756c7420746f6c6572616e6365",
"alloc": {
"91240f5b6994c7ed80f9f94b1aa847880ad3b150": {
"privateKey": "8f2a55949038a9610f50fb23b5883af3b4ecb3c3bb792cbcefbd1542c692be63",
"info": "This genesis file uses SECP256R1 as elliptic curve. The address is only valid for this curve and invalid with the default SECP256K1 curve.",
"comment": "private key and this comment are ignored. In a real chain, the private key should NOT be stored",
"balance": "0xad78ebc5ac6200000"
}
}
}
Loading