diff --git a/consensus/ibft/src/integration-test/java/org/hyperledger/besu/consensus/ibft/tests/round/IbftRoundIntegrationTest.java b/consensus/ibft/src/integration-test/java/org/hyperledger/besu/consensus/ibft/tests/round/IbftRoundIntegrationTest.java new file mode 100644 index 0000000000..91f924289d --- /dev/null +++ b/consensus/ibft/src/integration-test/java/org/hyperledger/besu/consensus/ibft/tests/round/IbftRoundIntegrationTest.java @@ -0,0 +1,183 @@ +/* + * 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.consensus.ibft.tests.round; + +import static java.util.Collections.emptyList; +import static java.util.Optional.empty; +import static org.assertj.core.api.Assertions.assertThat; +import static org.hyperledger.besu.consensus.ibft.IbftContextBuilder.setupContextWithValidators; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.when; + +import org.hyperledger.besu.consensus.ibft.ConsensusRoundIdentifier; +import org.hyperledger.besu.consensus.ibft.IbftContext; +import org.hyperledger.besu.consensus.ibft.IbftExtraData; +import org.hyperledger.besu.consensus.ibft.RoundTimer; +import org.hyperledger.besu.consensus.ibft.blockcreation.IbftBlockCreator; +import org.hyperledger.besu.consensus.ibft.network.IbftMessageTransmitter; +import org.hyperledger.besu.consensus.ibft.payload.MessageFactory; +import org.hyperledger.besu.consensus.ibft.statemachine.IbftRound; +import org.hyperledger.besu.consensus.ibft.statemachine.RoundState; +import org.hyperledger.besu.consensus.ibft.support.StubValidatorMulticaster; +import org.hyperledger.besu.consensus.ibft.validation.MessageValidator; +import org.hyperledger.besu.crypto.NodeKey; +import org.hyperledger.besu.crypto.NodeKeyUtils; +import org.hyperledger.besu.crypto.SECP256K1.Signature; +import org.hyperledger.besu.ethereum.ProtocolContext; +import org.hyperledger.besu.ethereum.chain.MinedBlockObserver; +import org.hyperledger.besu.ethereum.chain.MutableBlockchain; +import org.hyperledger.besu.ethereum.core.Block; +import org.hyperledger.besu.ethereum.core.BlockBody; +import org.hyperledger.besu.ethereum.core.BlockHeader; +import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture; +import org.hyperledger.besu.ethereum.core.BlockImporter; +import org.hyperledger.besu.ethereum.core.Hash; +import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; +import org.hyperledger.besu.plugin.services.securitymodule.SecurityModuleException; +import org.hyperledger.besu.util.Subscribers; + +import java.math.BigInteger; +import java.util.Optional; + +import org.apache.tuweni.bytes.Bytes; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class IbftRoundIntegrationTest { + + private final MessageFactory peerMessageFactory = new MessageFactory(NodeKeyUtils.generate()); + private final ConsensusRoundIdentifier roundIdentifier = new ConsensusRoundIdentifier(1, 0); + private final Subscribers subscribers = Subscribers.create(); + private ProtocolContext protocolContext; + + @Mock private MutableBlockchain blockChain; + @Mock private WorldStateArchive worldStateArchive; + @Mock private BlockImporter blockImporter; + + @Mock private IbftBlockCreator blockCreator; + @Mock private MessageValidator messageValidator; + @Mock private RoundTimer roundTimer; + @Mock private NodeKey nodeKey; + private MessageFactory throwingMessageFactory; + private IbftMessageTransmitter transmitter; + @Mock private StubValidatorMulticaster multicaster; + + private Block proposedBlock; + + private final Signature remoteCommitSeal = + Signature.create(BigInteger.ONE, BigInteger.ONE, (byte) 1); + + @Before + public void setup() { + protocolContext = + new ProtocolContext<>( + blockChain, worldStateArchive, setupContextWithValidators(emptyList())); + + when(messageValidator.validateProposal(any())).thenReturn(true); + when(messageValidator.validatePrepare(any())).thenReturn(true); + when(messageValidator.validateCommit(any())).thenReturn(true); + + when(nodeKey.sign(any())).thenThrow(new SecurityModuleException("Hsm Is Down")); + + throwingMessageFactory = new MessageFactory(nodeKey); + transmitter = new IbftMessageTransmitter(throwingMessageFactory, multicaster); + + IbftExtraData proposedExtraData = + new IbftExtraData(Bytes.wrap(new byte[32]), emptyList(), empty(), 0, emptyList()); + final BlockHeaderTestFixture headerTestFixture = new BlockHeaderTestFixture(); + headerTestFixture.extraData(proposedExtraData.encode()); + headerTestFixture.number(1); + final BlockHeader header = headerTestFixture.buildHeader(); + proposedBlock = new Block(header, new BlockBody(emptyList(), emptyList())); + + when(blockImporter.importBlock(any(), any(), any())).thenReturn(true); + } + + @Test + public void signingFailsOnReceiptOfProposalUpdatesRoundButTransmitsNothing() { + final int QUORUM_SIZE = 1; + final RoundState roundState = new RoundState(roundIdentifier, QUORUM_SIZE, messageValidator); + final IbftRound round = + new IbftRound( + roundState, + blockCreator, + protocolContext, + blockImporter, + subscribers, + nodeKey, + throwingMessageFactory, + transmitter, + roundTimer); + + round.handleProposalMessage( + peerMessageFactory.createProposal(roundIdentifier, proposedBlock, Optional.empty())); + + assertThat(roundState.getProposedBlock()).isNotEmpty(); + assertThat(roundState.isPrepared()).isTrue(); + assertThat(roundState.isCommitted()).isFalse(); + + verifyNoInteractions(multicaster); + } + + @Test + public void failuresToSignStillAllowBlockToBeImported() { + final int QUORUM_SIZE = 2; + final RoundState roundState = new RoundState(roundIdentifier, QUORUM_SIZE, messageValidator); + final IbftRound round = + new IbftRound( + roundState, + blockCreator, + protocolContext, + blockImporter, + subscribers, + nodeKey, + throwingMessageFactory, + transmitter, + roundTimer); + + // inject a block first, then a prepare on it. + round.handleProposalMessage( + peerMessageFactory.createProposal(roundIdentifier, proposedBlock, Optional.empty())); + + assertThat(roundState.getProposedBlock()).isNotEmpty(); + assertThat(roundState.isPrepared()).isFalse(); + assertThat(roundState.isCommitted()).isFalse(); + + round.handlePrepareMessage(peerMessageFactory.createPrepare(roundIdentifier, Hash.EMPTY)); + + assertThat(roundState.getProposedBlock()).isNotEmpty(); + assertThat(roundState.isPrepared()).isTrue(); + assertThat(roundState.isCommitted()).isFalse(); + verifyNoInteractions(multicaster); + + round.handleCommitMessage( + peerMessageFactory.createCommit(roundIdentifier, Hash.EMPTY, remoteCommitSeal)); + assertThat(roundState.isCommitted()).isFalse(); + verifyNoInteractions(multicaster); + + round.handleCommitMessage( + peerMessageFactory.createCommit(roundIdentifier, Hash.EMPTY, remoteCommitSeal)); + assertThat(roundState.isCommitted()).isTrue(); + verifyNoInteractions(multicaster); + + verify(blockImporter).importBlock(any(), any(), any()); + } +} diff --git a/consensus/ibft/src/main/java/org/hyperledger/besu/consensus/ibft/network/IbftMessageTransmitter.java b/consensus/ibft/src/main/java/org/hyperledger/besu/consensus/ibft/network/IbftMessageTransmitter.java index 06ef2a3378..80da787070 100644 --- a/consensus/ibft/src/main/java/org/hyperledger/besu/consensus/ibft/network/IbftMessageTransmitter.java +++ b/consensus/ibft/src/main/java/org/hyperledger/besu/consensus/ibft/network/IbftMessageTransmitter.java @@ -29,11 +29,17 @@ import org.hyperledger.besu.consensus.ibft.statemachine.PreparedRoundArtifacts; import org.hyperledger.besu.crypto.SECP256K1.Signature; import org.hyperledger.besu.ethereum.core.Block; import org.hyperledger.besu.ethereum.core.Hash; +import org.hyperledger.besu.plugin.services.securitymodule.SecurityModuleException; import java.util.Optional; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + public class IbftMessageTransmitter { + private static final Logger LOG = LogManager.getLogger(); + private final MessageFactory messageFactory; private final ValidatorMulticaster multicaster; @@ -47,42 +53,57 @@ public class IbftMessageTransmitter { final ConsensusRoundIdentifier roundIdentifier, final Block block, final Optional roundChangeCertificate) { - final Proposal data = - messageFactory.createProposal(roundIdentifier, block, roundChangeCertificate); + try { + final Proposal data = + messageFactory.createProposal(roundIdentifier, block, roundChangeCertificate); - final ProposalMessageData message = ProposalMessageData.create(data); + final ProposalMessageData message = ProposalMessageData.create(data); - multicaster.send(message); + multicaster.send(message); + } catch (final SecurityModuleException e) { + LOG.warn("Failed to generate signature for Proposal (not sent): {} ", e.getMessage()); + } } public void multicastPrepare(final ConsensusRoundIdentifier roundIdentifier, final Hash digest) { - final Prepare data = messageFactory.createPrepare(roundIdentifier, digest); + try { + final Prepare data = messageFactory.createPrepare(roundIdentifier, digest); - final PrepareMessageData message = PrepareMessageData.create(data); + final PrepareMessageData message = PrepareMessageData.create(data); - multicaster.send(message); + multicaster.send(message); + } catch (final SecurityModuleException e) { + LOG.warn("Failed to generate signature for Prepare (not sent): {} ", e.getMessage()); + } } public void multicastCommit( final ConsensusRoundIdentifier roundIdentifier, final Hash digest, final Signature commitSeal) { - final Commit data = messageFactory.createCommit(roundIdentifier, digest, commitSeal); + try { + final Commit data = messageFactory.createCommit(roundIdentifier, digest, commitSeal); - final CommitMessageData message = CommitMessageData.create(data); + final CommitMessageData message = CommitMessageData.create(data); - multicaster.send(message); + multicaster.send(message); + } catch (final SecurityModuleException e) { + LOG.warn("Failed to generate signature for Commit (not sent): {} ", e.getMessage()); + } } public void multicastRoundChange( final ConsensusRoundIdentifier roundIdentifier, final Optional preparedRoundArtifacts) { + try { + final RoundChange data = + messageFactory.createRoundChange(roundIdentifier, preparedRoundArtifacts); - final RoundChange data = - messageFactory.createRoundChange(roundIdentifier, preparedRoundArtifacts); - - final RoundChangeMessageData message = RoundChangeMessageData.create(data); + final RoundChangeMessageData message = RoundChangeMessageData.create(data); - multicaster.send(message); + multicaster.send(message); + } catch (final SecurityModuleException e) { + LOG.warn("Failed to generate signature for RoundChange (not sent): {} ", e.getMessage()); + } } } diff --git a/consensus/ibft/src/main/java/org/hyperledger/besu/consensus/ibft/payload/MessageFactory.java b/consensus/ibft/src/main/java/org/hyperledger/besu/consensus/ibft/payload/MessageFactory.java index 509f0f6326..ff4633b99f 100644 --- a/consensus/ibft/src/main/java/org/hyperledger/besu/consensus/ibft/payload/MessageFactory.java +++ b/consensus/ibft/src/main/java/org/hyperledger/besu/consensus/ibft/payload/MessageFactory.java @@ -79,7 +79,6 @@ public class MessageFactory { private SignedData createSignedMessage(final M payload) { final Signature signature = nodeKey.sign(hashForSignature(payload)); - return new SignedData<>(payload, Util.publicKeyToAddress(nodeKey.getPublicKey()), signature); } diff --git a/consensus/ibft/src/main/java/org/hyperledger/besu/consensus/ibft/statemachine/IbftBlockHeightManager.java b/consensus/ibft/src/main/java/org/hyperledger/besu/consensus/ibft/statemachine/IbftBlockHeightManager.java index 03ed5cd1d1..a98f2ecf1f 100644 --- a/consensus/ibft/src/main/java/org/hyperledger/besu/consensus/ibft/statemachine/IbftBlockHeightManager.java +++ b/consensus/ibft/src/main/java/org/hyperledger/besu/consensus/ibft/statemachine/IbftBlockHeightManager.java @@ -32,6 +32,7 @@ import org.hyperledger.besu.consensus.ibft.payload.Payload; import org.hyperledger.besu.consensus.ibft.validation.FutureRoundProposalMessageValidator; import org.hyperledger.besu.consensus.ibft.validation.MessageValidatorFactory; import org.hyperledger.besu.ethereum.core.BlockHeader; +import org.hyperledger.besu.plugin.services.securitymodule.SecurityModuleException; import java.time.Clock; import java.util.Collection; @@ -139,15 +140,20 @@ public class IbftBlockHeightManager implements BlockHeightManager { startNewRound(currentRound.getRoundIdentifier().getRoundNumber() + 1); - final RoundChange localRoundChange = - messageFactory.createRoundChange( - currentRound.getRoundIdentifier(), latestPreparedRoundArtifacts); + try { + final RoundChange localRoundChange = + messageFactory.createRoundChange( + currentRound.getRoundIdentifier(), latestPreparedRoundArtifacts); + + // Its possible the locally created RoundChange triggers the transmission of a NewRound + // message - so it must be handled accordingly. + handleRoundChangePayload(localRoundChange); + } catch (final SecurityModuleException e) { + LOG.warn("Failed to create signed RoundChange message.", e); + } + transmitter.multicastRoundChange( currentRound.getRoundIdentifier(), latestPreparedRoundArtifacts); - - // Its possible the locally created RoundChange triggers the transmission of a NewRound - // message - so it must be handled accordingly. - handleRoundChangePayload(localRoundChange); } @Override diff --git a/consensus/ibft/src/main/java/org/hyperledger/besu/consensus/ibft/statemachine/IbftController.java b/consensus/ibft/src/main/java/org/hyperledger/besu/consensus/ibft/statemachine/IbftController.java index 7a4aa2b3c9..c1ed0bfee7 100644 --- a/consensus/ibft/src/main/java/org/hyperledger/besu/consensus/ibft/statemachine/IbftController.java +++ b/consensus/ibft/src/main/java/org/hyperledger/besu/consensus/ibft/statemachine/IbftController.java @@ -153,7 +153,7 @@ public class IbftController { if (newBlockHeader.getNumber() == currentMiningParent.getNumber()) { if (newBlockHeader.getHash().equals(currentMiningParent.getHash())) { LOG.trace( - "Discarding duplicate NewChainHead event. chainHeight={} newBlockHash={} parentBlockHash", + "Discarding duplicate NewChainHead event. chainHeight={} newBlockHash={} parentBlockHash={}", newBlockHeader.getNumber(), newBlockHeader.getHash(), currentMiningParent.getHash()); diff --git a/consensus/ibft/src/main/java/org/hyperledger/besu/consensus/ibft/statemachine/IbftRound.java b/consensus/ibft/src/main/java/org/hyperledger/besu/consensus/ibft/statemachine/IbftRound.java index 3969767594..dd38c4eb91 100644 --- a/consensus/ibft/src/main/java/org/hyperledger/besu/consensus/ibft/statemachine/IbftRound.java +++ b/consensus/ibft/src/main/java/org/hyperledger/besu/consensus/ibft/statemachine/IbftRound.java @@ -38,6 +38,7 @@ import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.BlockImporter; import org.hyperledger.besu.ethereum.core.Hash; import org.hyperledger.besu.ethereum.mainnet.HeaderValidationMode; +import org.hyperledger.besu.plugin.services.securitymodule.SecurityModuleException; import org.hyperledger.besu.util.Subscribers; import java.util.Optional; @@ -118,8 +119,13 @@ public class IbftRound { private void updateStateWithProposalAndTransmit( final Block block, final Optional roundChangeCertificate) { - final Proposal proposal = - messageFactory.createProposal(getRoundIdentifier(), block, roundChangeCertificate); + final Proposal proposal; + try { + proposal = messageFactory.createProposal(getRoundIdentifier(), block, roundChangeCertificate); + } catch (final SecurityModuleException e) { + LOG.warn("Failed to create a signed Proposal, waiting for next round.", e); + return; + } transmitter.multicastProposal( proposal.getRoundIdentifier(), proposal.getBlock(), proposal.getRoundChangeCertificate()); @@ -132,11 +138,15 @@ public class IbftRound { if (updateStateWithProposedBlock(msg)) { LOG.debug("Sending prepare message. round={}", roundState.getRoundIdentifier()); - final Prepare localPrepareMessage = - messageFactory.createPrepare(getRoundIdentifier(), block.getHash()); - transmitter.multicastPrepare( - localPrepareMessage.getRoundIdentifier(), localPrepareMessage.getDigest()); - peerIsPrepared(localPrepareMessage); + try { + final Prepare localPrepareMessage = + messageFactory.createPrepare(getRoundIdentifier(), block.getHash()); + peerIsPrepared(localPrepareMessage); + transmitter.multicastPrepare( + localPrepareMessage.getRoundIdentifier(), localPrepareMessage.getDigest()); + } catch (final SecurityModuleException e) { + LOG.warn("Failed to create a signed Prepare; {}", e.getMessage()); + } } } @@ -158,23 +168,41 @@ public class IbftRound { final boolean wasPrepared = roundState.isPrepared(); final boolean wasCommitted = roundState.isCommitted(); final boolean blockAccepted = roundState.setProposedBlock(msg); + if (blockAccepted) { + final Block block = roundState.getProposedBlock().get(); + + final Signature commitSeal; + try { + commitSeal = createCommitSeal(block); + } catch (final SecurityModuleException e) { + LOG.warn("Failed to construct commit seal; {}", e.getMessage()); + return blockAccepted; + } + // There are times handling a proposed block is enough to enter prepared. if (wasPrepared != roundState.isPrepared()) { LOG.debug("Sending commit message. round={}", roundState.getRoundIdentifier()); - final Block block = roundState.getProposedBlock().get(); - transmitter.multicastCommit(getRoundIdentifier(), block.getHash(), createCommitSeal(block)); + transmitter.multicastCommit(getRoundIdentifier(), block.getHash(), commitSeal); + } + + // can automatically add _our_ commit message to the roundState + // cannot create a prepare message here, as it may be _our_ proposal, and thus we cannot also + // prepare + try { + final Commit localCommitMessage = + messageFactory.createCommit( + roundState.getRoundIdentifier(), msg.getBlock().getHash(), commitSeal); + roundState.addCommitMessage(localCommitMessage); + } catch (final SecurityModuleException e) { + LOG.warn("Failed to create signed Commit message; {}", e.getMessage()); + return blockAccepted; } + + // It is possible sufficient commit seals are now available and the block should be imported if (wasCommitted != roundState.isCommitted()) { importBlockToChain(); } - - final Commit localCommitMessage = - messageFactory.createCommit( - roundState.getRoundIdentifier(), - msg.getBlock().getHash(), - createCommitSeal(roundState.getProposedBlock().get())); - peerIsCommitted(localCommitMessage); } return blockAccepted; @@ -186,7 +214,13 @@ public class IbftRound { if (wasPrepared != roundState.isPrepared()) { LOG.debug("Sending commit message. round={}", roundState.getRoundIdentifier()); final Block block = roundState.getProposedBlock().get(); - transmitter.multicastCommit(getRoundIdentifier(), block.getHash(), createCommitSeal(block)); + try { + transmitter.multicastCommit(getRoundIdentifier(), block.getHash(), createCommitSeal(block)); + // Note: the local-node's commit message was added to RoundState on block acceptance + // and thus does not need to be done again here. + } catch (final SecurityModuleException e) { + LOG.warn("Failed to construct a commit seal: {}", e.getMessage()); + } } } diff --git a/consensus/ibft/src/test/java/org/hyperledger/besu/consensus/ibft/statemachine/IbftRoundTest.java b/consensus/ibft/src/test/java/org/hyperledger/besu/consensus/ibft/statemachine/IbftRoundTest.java index c84813e208..13f12bbc89 100644 --- a/consensus/ibft/src/test/java/org/hyperledger/besu/consensus/ibft/statemachine/IbftRoundTest.java +++ b/consensus/ibft/src/test/java/org/hyperledger/besu/consensus/ibft/statemachine/IbftRoundTest.java @@ -21,9 +21,11 @@ import static org.hyperledger.besu.consensus.ibft.IbftContextBuilder.setupContex import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.when; import org.hyperledger.besu.consensus.ibft.ConsensusRoundIdentifier; @@ -49,6 +51,7 @@ import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture; import org.hyperledger.besu.ethereum.core.BlockImporter; import org.hyperledger.besu.ethereum.core.Hash; import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; +import org.hyperledger.besu.plugin.services.securitymodule.SecurityModuleException; import org.hyperledger.besu.util.Subscribers; import java.math.BigInteger; @@ -446,4 +449,39 @@ public class IbftRoundTest { verify(blockImporter, times(1)).importBlock(any(), any(), any()); } + + @Test + public void exceptionDuringNodeKeySigningDoesNotEscape() { + final int QUORUM_SIZE = 1; + final RoundState roundState = new RoundState(roundIdentifier, QUORUM_SIZE, messageValidator); + final NodeKey throwingNodeKey = mock(NodeKey.class); + final MessageFactory throwingMessageFactory = new MessageFactory(throwingNodeKey); + when(throwingNodeKey.sign(any())).thenThrow(new SecurityModuleException("Hsm is Offline")); + + final IbftRound round = + new IbftRound( + roundState, + blockCreator, + protocolContext, + blockImporter, + subscribers, + throwingNodeKey, + throwingMessageFactory, + transmitter, + roundTimer); + + round.handleProposalMessage( + messageFactory.createProposal(roundIdentifier, proposedBlock, Optional.empty())); + + // Verify that no prepare message was constructed by the IbftRound + assertThat( + roundState + .constructPreparedRoundArtifacts() + .get() + .getPreparedCertificate() + .getPreparePayloads()) + .isEmpty(); + + verifyNoInteractions(transmitter); + } } diff --git a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockCreator.java b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockCreator.java index 954c8a1fed..8786f20165 100644 --- a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockCreator.java +++ b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockCreator.java @@ -41,6 +41,7 @@ import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec; import org.hyperledger.besu.ethereum.mainnet.ScheduleBasedBlockHeaderFunctions; import org.hyperledger.besu.ethereum.mainnet.TransactionProcessor; +import org.hyperledger.besu.plugin.services.securitymodule.SecurityModuleException; import java.math.BigInteger; import java.util.List; @@ -186,7 +187,9 @@ public abstract class AbstractBlockCreator implements AsyncBlockCreator { final BlockHeader blockHeader = createFinalBlockHeader(sealableBlockHeader); return new Block(blockHeader, new BlockBody(transactionResults.getTransactions(), ommers)); - + } catch (final SecurityModuleException ex) { + LOG.warn("Failed to create block signature.", ex); + throw ex; } catch (final CancellationException ex) { LOG.trace("Attempt to create block was interrupted."); throw ex; diff --git a/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/rlpx/handshake/ecies/ECIESHandshaker.java b/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/rlpx/handshake/ecies/ECIESHandshaker.java index 83b497c168..1d0aa17a58 100644 --- a/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/rlpx/handshake/ecies/ECIESHandshaker.java +++ b/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/rlpx/handshake/ecies/ECIESHandshaker.java @@ -25,6 +25,7 @@ import org.hyperledger.besu.crypto.SecureRandomProvider; import org.hyperledger.besu.ethereum.p2p.rlpx.handshake.HandshakeException; import org.hyperledger.besu.ethereum.p2p.rlpx.handshake.HandshakeSecrets; import org.hyperledger.besu.ethereum.p2p.rlpx.handshake.Handshaker; +import org.hyperledger.besu.plugin.services.securitymodule.SecurityModuleException; import java.security.SecureRandom; import java.util.Optional; @@ -47,6 +48,7 @@ import org.bouncycastle.crypto.InvalidCipherTextException; * encrypted handshake */ public class ECIESHandshaker implements Handshaker { + private static final Logger LOG = LogManager.getLogger(); private static final SecureRandom RANDOM = SecureRandomProvider.publicSecureRandom(); @@ -203,6 +205,10 @@ public class ECIESHandshaker implements Handshaker { } catch (final InvalidCipherTextException e) { status.set(Handshaker.HandshakeStatus.FAILED); throw new HandshakeException("Decrypting an incoming handshake message failed", e); + } catch (final SecurityModuleException e) { + status.set(Handshaker.HandshakeStatus.FAILED); + throw new HandshakeException( + "Unable to create ECDH Key agreement due to Crypto engine failure", e); } Optional nextMsg = Optional.empty(); @@ -242,10 +248,16 @@ public class ECIESHandshaker implements Handshaker { // Store the message, as we need it to generating our ingress and egress MACs. initiatorMsgEnc = encryptedMsg; - if (version4) { - initiatorMsg = InitiatorHandshakeMessageV4.decode(bytes, nodeKey); - } else { - initiatorMsg = InitiatorHandshakeMessageV1.decode(bytes, nodeKey); + try { + if (version4) { + initiatorMsg = InitiatorHandshakeMessageV4.decode(bytes, nodeKey); + } else { + initiatorMsg = InitiatorHandshakeMessageV1.decode(bytes, nodeKey); + } + } catch (final SecurityModuleException e) { + status.set(Handshaker.HandshakeStatus.FAILED); + throw new HandshakeException( + "Unable to create ECDH Key agreement due to Crypto engine failure", e); } LOG.trace( @@ -290,7 +302,14 @@ public class ECIESHandshaker implements Handshaker { // Compute the secrets and declare this handshake as successful. } - computeSecrets(); + + try { + computeSecrets(); + } catch (final SecurityModuleException e) { + status.set(Handshaker.HandshakeStatus.FAILED); + throw new HandshakeException( + "Unable to create ECDH Key agreement due to Crypto engine failure", e); + } status.set(Handshaker.HandshakeStatus.SUCCESS); LOG.trace("Handshake status set to {}", status.get()); @@ -337,6 +356,7 @@ public class ECIESHandshaker implements Handshaker { void computeSecrets() { final Bytes agreedSecret = SECP256K1.calculateECDHKeyAgreement(ephKeyPair.getPrivateKey(), partyEphPubKey); + final Bytes sharedSecret = keccak256( concatenate(agreedSecret, keccak256(concatenate(responderNonce, initiatorNonce)))); diff --git a/ethereum/referencetests/src/test/resources b/ethereum/referencetests/src/test/resources index 5841af6da4..6af0621522 160000 --- a/ethereum/referencetests/src/test/resources +++ b/ethereum/referencetests/src/test/resources @@ -1 +1 @@ -Subproject commit 5841af6da472fb3f19810354cf9a30afd8e72b5f +Subproject commit 6af0621522dd0274525457741291d391c10002be