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

BFT Block Puller: verify attestation #4242

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 5 additions & 0 deletions gossip/api/crypto.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ type MessageCryptoService interface {
// else returns error
VerifyBlock(channelID common.ChannelID, seqNum uint64, block *cb.Block) error

// VerifyBlockAttestation does the same as VerifyBlock, except it assumes block.Data = nil. It therefore does not
// compute the block.Data.Hash() and compare it to the block.Header.DataHash. This is used when the orderer
// delivers a block with header & metadata only, as an attestation of block existence.
VerifyBlockAttestation(channelID string, block *cb.Block) error

// Sign signs msg with this peer's signing key and outputs
// the signature if no error occurred.
Sign(msg []byte) ([]byte, error)
Expand Down
6 changes: 6 additions & 0 deletions gossip/comm/comm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,12 @@ func (*naiveSecProvider) VerifyBlock(channelID common.ChannelID, seqNum uint64,
return nil
}

// VerifyBlockAttestation returns nil if the block attestation is properly signed,
// else returns error
func (*naiveSecProvider) VerifyBlockAttestation(channelID string, signedBlock *cb.Block) error {
return nil
}

// Sign signs msg with this peer's signing key and outputs
// the signature if no error occurred.
func (*naiveSecProvider) Sign(msg []byte) ([]byte, error) {
Expand Down
4 changes: 4 additions & 0 deletions gossip/gossip/channel/channel_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,10 @@ func (cs *cryptoService) VerifyBlock(channelID common.ChannelID, seqNum uint64,
return args.Get(0).(error)
}

func (*cryptoService) VerifyBlockAttestation(channelID string, signedBlock *cb.Block) error {
panic("Should not be called in this test")
}

func (cs *cryptoService) Sign(msg []byte) ([]byte, error) {
panic("Should not be called in this test")
}
Expand Down
6 changes: 6 additions & 0 deletions gossip/gossip/gossip_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,12 @@ func (*naiveCryptoService) VerifyBlock(channelID common.ChannelID, seqNum uint64
return nil
}

// VerifyBlockAttestation returns nil if the block attestation is properly signed,
// else returns error
func (*naiveCryptoService) VerifyBlockAttestation(channelID string, signedBlock *cb.Block) error {
return nil
}

// Sign signs msg with this peer's signing key and outputs
// the signature if no error occurred.
func (*naiveCryptoService) Sign(msg []byte) ([]byte, error) {
Expand Down
6 changes: 6 additions & 0 deletions gossip/gossip/orgs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,12 @@ func (*configurableCryptoService) VerifyBlock(channelID common.ChannelID, seqNum
return nil
}

// VerifyBlockAttestation returns nil if the block attestation is properly signed,
// else returns error
func (*configurableCryptoService) VerifyBlockAttestation(channelID string, signedBlock *cb.Block) error {
return nil
}

// Sign signs msg with this peer's signing key and outputs
// the signature if no error occurred.
func (*configurableCryptoService) Sign(msg []byte) ([]byte, error) {
Expand Down
6 changes: 6 additions & 0 deletions gossip/identity/identity_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,12 @@ func (*naiveCryptoService) VerifyBlock(channelID common.ChannelID, seqNum uint64
return nil
}

// VerifyBlockAttestation returns nil if the block attestation is properly signed,
// else returns error
func (*naiveCryptoService) VerifyBlockAttestation(channelID string, signedBlock *cb.Block) error {
return nil
}

// VerifyByChannel verifies a peer's signature on a message in the context
// of a specific channel
func (*naiveCryptoService) VerifyByChannel(_ common.ChannelID, _ api.PeerIdentityType, _, _ []byte) error {
Expand Down
6 changes: 6 additions & 0 deletions gossip/service/gossip_service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -843,6 +843,12 @@ func (*naiveCryptoService) VerifyBlock(chainID gossipcommon.ChannelID, seqNum ui
return nil
}

// VerifyBlockAttestation returns nil if the block attestation is properly signed,
// else returns error
func (*naiveCryptoService) VerifyBlockAttestation(channelID string, signedBlock *common.Block) error {
return nil
}

// Sign signs msg with this peer's signing key and outputs
// the signature if no error occurred.
func (*naiveCryptoService) Sign(msg []byte) ([]byte, error) {
Expand Down
6 changes: 6 additions & 0 deletions gossip/state/state_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,12 @@ func (*cryptoServiceMock) VerifyBlock(channelID common.ChannelID, seqNum uint64,
return nil
}

// VerifyBlockAttestation returns nil if the block attestation is properly signed,
// else returns error
func (*cryptoServiceMock) VerifyBlockAttestation(channelID string, signedBlock *pcomm.Block) error {
return nil
}

// Sign signs msg with this peer's signing key and outputs
// the signature if no error occurred.
func (*cryptoServiceMock) Sign(msg []byte) ([]byte, error) {
Expand Down
23 changes: 23 additions & 0 deletions internal/peer/gossip/mcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,10 @@ func (s *MSPMessageCryptoService) VerifyBlock(chainID common.ChannelID, seqNum u
return fmt.Errorf("Header.DataHash is different from Hash(block.Data) for block with id [%d] on channel [%s]", block.Header.Number, chainID)
}

return s.verifyHeaderAndMetadata(channelID, block)
}

func (s *MSPMessageCryptoService) verifyHeaderAndMetadata(channelID string, block *pcommon.Block) error {
// Get the policy manager for channelID
cpm := s.channelPolicyManagerGetter.Manager(channelID)
if cpm == nil {
Expand Down Expand Up @@ -184,6 +188,25 @@ func (s *MSPMessageCryptoService) VerifyBlock(chainID common.ChannelID, seqNum u
return verifier(block.Header, block.Metadata)
}

// VerifyBlockAttestation returns nil when the header matches the metadata signature. It assumed the block.Data is nil
// and therefore does not verify that Header.DataHash is equal to the hash of block.Data. This is used when the orderer
// delivers a block with header & metadata only, as an attestation of block existence.
func (s *MSPMessageCryptoService) VerifyBlockAttestation(chainID string, block *pcommon.Block) error {
if block == nil {
return fmt.Errorf("Invalid Block on channel [%s]. Block is nil.", chainID)
}
if block.Header == nil {
return fmt.Errorf("Invalid Block on channel [%s]. Header must be different from nil.", chainID)
}

// - Unmarshal medatada
if block.Metadata == nil || len(block.Metadata.Metadata) == 0 {
return fmt.Errorf("Block with id [%d] on channel [%s] does not have metadata. Block not valid.", block.Header.Number, chainID)
}

return s.verifyHeaderAndMetadata(chainID, block)
}

// Sign signs msg with this peer's signing key and outputs
// the signature if no error occurred.
func (s *MSPMessageCryptoService) Sign(msg []byte) ([]byte, error) {
Expand Down
154 changes: 112 additions & 42 deletions internal/peer/gossip/mcs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"github.com/hyperledger/fabric/msp"
"github.com/hyperledger/fabric/msp/mgmt"
"github.com/hyperledger/fabric/protoutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
)
Expand Down Expand Up @@ -289,28 +290,68 @@ func TestVerifyBlock(t *testing.T) {
blockRaw2, msg2 := mockBlock(t, "D", 42, aliceSigner, nil)
policyManagerGetter.Managers["D"].(*mocks.ChannelPolicyManager).Policy.(*mocks.Policy).Deserializer.(*mocks.IdentityDeserializer).Msg = msg2

// - Verify block
require.NoError(t, msgCryptoService.VerifyBlock([]byte("C"), 42, blockRaw))
// Wrong sequence number claimed
err = msgCryptoService.VerifyBlock([]byte("C"), 43, blockRaw)
require.Error(t, err)
require.Contains(t, err.Error(), "but actual seqNum inside block is")
delete(policyManagerGetter.Managers, "D")
nilPolMgrErr := msgCryptoService.VerifyBlock([]byte("D"), 42, blockRaw2)
require.Contains(t, nilPolMgrErr.Error(), "Could not acquire policy manager")
require.Error(t, nilPolMgrErr)
require.Error(t, msgCryptoService.VerifyBlock([]byte("A"), 42, blockRaw))
require.Error(t, msgCryptoService.VerifyBlock([]byte("B"), 42, blockRaw))

// - Prepare testing invalid block (wrong data has), Alice signs it.
blockRaw, msg = mockBlock(t, "C", 42, aliceSigner, []byte{0})
policyManagerGetter.Managers["C"].(*mocks.ChannelPolicyManager).Policy.(*mocks.Policy).Deserializer.(*mocks.IdentityDeserializer).Msg = msg

// - Verify block
require.Error(t, msgCryptoService.VerifyBlock([]byte("C"), 42, blockRaw))
t.Run("verify block", func(t *testing.T) {
require.NoError(t, msgCryptoService.VerifyBlock([]byte("C"), 42, blockRaw))
// Wrong sequence number claimed
err = msgCryptoService.VerifyBlock([]byte("C"), 43, blockRaw)
require.Error(t, err)
require.Contains(t, err.Error(), "but actual seqNum inside block is")
delete(policyManagerGetter.Managers, "D")
nilPolMgrErr := msgCryptoService.VerifyBlock([]byte("D"), 42, blockRaw2)
require.Contains(t, nilPolMgrErr.Error(), "Could not acquire policy manager")
require.Error(t, nilPolMgrErr)
require.Error(t, msgCryptoService.VerifyBlock([]byte("A"), 42, blockRaw))
require.Error(t, msgCryptoService.VerifyBlock([]byte("B"), 42, blockRaw))

// - Prepare testing invalid block (wrong data has), Alice signs it.
blockRawInvalid, msgInvalid := mockBlock(t, "C", 42, aliceSigner, []byte{0})
policyManagerGetter.Managers["C"].(*mocks.ChannelPolicyManager).Policy.(*mocks.Policy).Deserializer.(*mocks.IdentityDeserializer).Msg = msgInvalid
defer func() {
policyManagerGetter.Managers["C"].(*mocks.ChannelPolicyManager).Policy.(*mocks.Policy).Deserializer.(*mocks.IdentityDeserializer).Msg = msg
}()

// - Verify block
require.Error(t, msgCryptoService.VerifyBlock([]byte("C"), 42, blockRawInvalid))

// Check invalid args
require.Error(t, msgCryptoService.VerifyBlock([]byte("C"), 42, &common.Block{}))
})

// Check invalid args
require.Error(t, msgCryptoService.VerifyBlock([]byte("C"), 42, &common.Block{}))
t.Run("verify block attestation", func(t *testing.T) {
// An attestation is a signed block with block.Data = nil
attestation := blockRaw
attestation.Data = nil
attestation2 := blockRaw2
attestation2.Data = nil

assert.NoError(t, msgCryptoService.VerifyBlockAttestation("C", attestation))
delete(policyManagerGetter.Managers, "D")
nilPolMgrErr := msgCryptoService.VerifyBlockAttestation("D", attestation2)
assert.Contains(t, nilPolMgrErr.Error(), "Could not acquire policy manager")
assert.Error(t, nilPolMgrErr)
assert.Error(t, msgCryptoService.VerifyBlockAttestation("A", attestation))
assert.Error(t, msgCryptoService.VerifyBlockAttestation("B", attestation))

// - Prepare testing invalid attestation (wrong data has), Alice signs it.
// - Prepare testing invalid attestation (wrong data has), Alice signs it.
_, msgInvalid := mockBlock(t, "C", 42, aliceSigner, []byte{0})
policyManagerGetter.Managers["C"].(*mocks.ChannelPolicyManager).Policy.(*mocks.Policy).Deserializer.(*mocks.IdentityDeserializer).Msg = msgInvalid
defer func() {
policyManagerGetter.Managers["C"].(*mocks.ChannelPolicyManager).Policy.(*mocks.Policy).Deserializer.(*mocks.IdentityDeserializer).Msg = msg
}()

// - Verify attestation
assert.Error(t, msgCryptoService.VerifyBlockAttestation("C", attestation))

// Check invalid args
attestation.Header.DataHash = []byte{0, 1, 2, 3, 4}
assert.Error(t, msgCryptoService.VerifyBlockAttestation("C", attestation))
attestation.Metadata = nil
assert.Error(t, msgCryptoService.VerifyBlockAttestation("C", attestation))
attestation.Header = nil
assert.Error(t, msgCryptoService.VerifyBlockAttestation("C", attestation))
assert.Error(t, msgCryptoService.VerifyBlockAttestation("C", nil))
})
}

func TestVerifyBlockBFT(t *testing.T) {
Expand Down Expand Up @@ -368,28 +409,57 @@ func TestVerifyBlockBFT(t *testing.T) {
blockRaw2, msg2 := mockBlockBFT(t, "D", 42, aliceSigner, nil)
policyManagerGetter.Managers["D"].(*mocks.ChannelPolicyManager).Policy.(*mocks.Policy).Deserializer.(*mocks.IdentityDeserializer).Msg = msg2

// - Verify block
require.NoError(t, msgCryptoService.VerifyBlock([]byte("C"), 42, blockRaw))
// Wrong sequence number claimed
err = msgCryptoService.VerifyBlock([]byte("C"), 43, blockRaw)
require.Error(t, err)
require.Contains(t, err.Error(), "but actual seqNum inside block is")
delete(policyManagerGetter.Managers, "D")
nilPolMgrErr := msgCryptoService.VerifyBlock([]byte("D"), 42, blockRaw2)
require.Contains(t, nilPolMgrErr.Error(), "Could not acquire policy manager")
require.Error(t, nilPolMgrErr)
require.Error(t, msgCryptoService.VerifyBlock([]byte("A"), 42, blockRaw))
require.Error(t, msgCryptoService.VerifyBlock([]byte("B"), 42, blockRaw))

// - Prepare testing invalid block (wrong data has), Alice signs it.
blockRaw, msg = mockBlockBFT(t, "C", 42, aliceSigner, []byte{0})
policyManagerGetter.Managers["C"].(*mocks.ChannelPolicyManager).Policy.(*mocks.Policy).Deserializer.(*mocks.IdentityDeserializer).Msg = msg

// - Verify block
require.Error(t, msgCryptoService.VerifyBlock([]byte("C"), 42, blockRaw))
t.Run("verify block", func(t *testing.T) {
// - Verify block
require.NoError(t, msgCryptoService.VerifyBlock([]byte("C"), 42, blockRaw))
// Wrong sequence number claimed
err = msgCryptoService.VerifyBlock([]byte("C"), 43, blockRaw)
require.Error(t, err)
require.Contains(t, err.Error(), "but actual seqNum inside block is")
delete(policyManagerGetter.Managers, "D")
nilPolMgrErr := msgCryptoService.VerifyBlock([]byte("D"), 42, blockRaw2)
require.Contains(t, nilPolMgrErr.Error(), "Could not acquire policy manager")
require.Error(t, nilPolMgrErr)
require.Error(t, msgCryptoService.VerifyBlock([]byte("A"), 42, blockRaw))
require.Error(t, msgCryptoService.VerifyBlock([]byte("B"), 42, blockRaw))

// - Prepare testing invalid block (wrong data has), Alice signs it.
blockRawInvalid, msgInvalid := mockBlockBFT(t, "C", 42, aliceSigner, []byte{0})
policyManagerGetter.Managers["C"].(*mocks.ChannelPolicyManager).Policy.(*mocks.Policy).Deserializer.(*mocks.IdentityDeserializer).Msg = msgInvalid
defer func() {
policyManagerGetter.Managers["C"].(*mocks.ChannelPolicyManager).Policy.(*mocks.Policy).Deserializer.(*mocks.IdentityDeserializer).Msg = msg
}()
// - Verify block
require.Error(t, msgCryptoService.VerifyBlock([]byte("C"), 42, blockRawInvalid))

// Check invalid args
require.Error(t, msgCryptoService.VerifyBlock([]byte("C"), 42, &common.Block{}))
})

// Check invalid args
require.Error(t, msgCryptoService.VerifyBlock([]byte("C"), 42, &common.Block{}))
t.Run("verify block attestation", func(t *testing.T) {
// An attestation is a signed block with block.Data = nil
attestation := blockRaw
attestation.Data = nil
attestation2 := blockRaw2
attestation2.Data = nil

// - Verify block
require.NoError(t, msgCryptoService.VerifyBlockAttestation("C", attestation))
nilPolMgrErr := msgCryptoService.VerifyBlockAttestation("D", attestation2)
require.Contains(t, nilPolMgrErr.Error(), "Could not acquire policy manager")
require.Error(t, nilPolMgrErr)
require.Error(t, msgCryptoService.VerifyBlockAttestation("A", attestation))
require.Error(t, msgCryptoService.VerifyBlockAttestation("B", attestation))

// - Prepare testing invalid block (has wrong data), Alice signs it.
_, msgInvalid := mockBlockBFT(t, "C", 42, aliceSigner, []byte{0})
policyManagerGetter.Managers["C"].(*mocks.ChannelPolicyManager).Policy.(*mocks.Policy).Deserializer.(*mocks.IdentityDeserializer).Msg = msgInvalid
defer func() {
policyManagerGetter.Managers["C"].(*mocks.ChannelPolicyManager).Policy.(*mocks.Policy).Deserializer.(*mocks.IdentityDeserializer).Msg = msg
}()
// - Verify block
require.Error(t, msgCryptoService.VerifyBlockAttestation("C", attestation))
})
}

func mockBlock(t *testing.T, channel string, seqNum uint64, localSigner *mocks.SignerSerializer, dataHash []byte) (*common.Block, []byte) {
Expand Down
5 changes: 5 additions & 0 deletions internal/pkg/peer/blocksprovider/blocksprovider.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ type GossipServiceAdapter interface {
//go:generate counterfeiter -o fake/block_verifier.go --fake-name BlockVerifier . BlockVerifier
type BlockVerifier interface {
VerifyBlock(channelID gossipcommon.ChannelID, blockNum uint64, block *common.Block) error

// VerifyBlockAttestation does the same as VerifyBlock, except it assumes block.Data = nil. It therefore does not
// compute the block.Data.Hash() and compare it to the block.Header.DataHash. This is used when the orderer
// delivers a block with header & metadata only, as an attestation of block existence.
VerifyBlockAttestation(channelID string, block *common.Block) error
}

//go:generate counterfeiter -o fake/orderer_connection_source.go --fake-name OrdererConnectionSource . OrdererConnectionSource
Expand Down
Loading