Skip to content

Commit

Permalink
Move BlockSignatureVerifier into protoutils (#3594)
Browse files Browse the repository at this point in the history
And refactor VerifyBlock() in orderer and peer gossip to use the common code.

Signed-off-by: andrew-coleman <andrew_coleman@uk.ibm.com>

Signed-off-by: andrew-coleman <andrew_coleman@uk.ibm.com>
  • Loading branch information
andrew-coleman authored Aug 18, 2022
1 parent 7cd6b5b commit f4ea5f7
Show file tree
Hide file tree
Showing 7 changed files with 314 additions and 259 deletions.
65 changes: 2 additions & 63 deletions internal/peer/gossip/mcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,11 @@ import (
"fmt"
"time"

"github.com/golang/protobuf/proto"
pcommon "github.com/hyperledger/fabric-protos-go/common"
pmsp "github.com/hyperledger/fabric-protos-go/msp"
"github.com/hyperledger/fabric/bccsp"
"github.com/hyperledger/fabric/common/channelconfig"
"github.com/hyperledger/fabric/common/flogging"
"github.com/hyperledger/fabric/common/policies"
"github.com/hyperledger/fabric/common/util"
"github.com/hyperledger/fabric/gossip/api"
"github.com/hyperledger/fabric/gossip/common"
"github.com/hyperledger/fabric/internal/pkg/identity"
Expand Down Expand Up @@ -159,15 +156,6 @@ 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)
}

if len(block.Metadata.Metadata) < int(pcommon.BlockMetadataIndex_SIGNATURES)+1 {
return errors.Errorf("no signatures in block metadata")
}

md := &pcommon.Metadata{}
if err := proto.Unmarshal(block.Metadata.Metadata[pcommon.BlockMetadataIndex_SIGNATURES], md); err != nil {
return errors.Wrapf(err, "error unmarshalling signatures from metadata: %v", err)
}

// Get the policy manager for channelID
cpm := s.channelPolicyManagerGetter.Manager(channelID)
if cpm == nil {
Expand All @@ -192,57 +180,8 @@ func (s *MSPMessageCryptoService) VerifyBlock(chainID common.ChannelID, seqNum u
consenters = cfg.Consenters()
}

var signatureSet []*protoutil.SignedData
for _, metadataSignature := range md.Signatures {
var signerIdentity []byte
var signedPayload []byte
// if the SignatureHeader is empty and the IdentifierHeader is present, then the consenter expects us to fetch its identity by its numeric identifier
if bftEnabled && len(metadataSignature.GetSignatureHeader()) == 0 && len(metadataSignature.GetIdentifierHeader()) > 0 {
identifierHeader, err := protoutil.UnmarshalIdentifierHeader(metadataSignature.IdentifierHeader)
if err != nil {
return fmt.Errorf("failed unmarshalling identifier header for block %d: %v", block.Header.Number, err)
}
identifier := identifierHeader.GetIdentifier()
signerIdentity = searchConsenterIdentityByID(consenters, identifier)
if len(signerIdentity) == 0 {
// The identifier is not within the consenter set
continue
}
signedPayload = util.ConcatenateBytes(md.Value, metadataSignature.IdentifierHeader, protoutil.BlockHeaderBytes(block.Header))
} else {
signatureHeader, err := protoutil.UnmarshalSignatureHeader(metadataSignature.GetSignatureHeader())
if err != nil {
return fmt.Errorf("failed unmarshalling signature header for block %d: %v", block.Header.Number, err)
}

signedPayload = util.ConcatenateBytes(md.Value, metadataSignature.SignatureHeader, protoutil.BlockHeaderBytes(block.Header))

signerIdentity = signatureHeader.Creator
}

signatureSet = append(
signatureSet,
&protoutil.SignedData{
Identity: signerIdentity,
Data: signedPayload,
Signature: metadataSignature.Signature,
},
)
}

return policy.EvaluateSignedData(signatureSet)
}

func searchConsenterIdentityByID(consenters []*pcommon.Consenter, identifier uint32) []byte {
for _, consenter := range consenters {
if consenter.Id == identifier {
return protoutil.MarshalOrPanic(&pmsp.SerializedIdentity{
Mspid: consenter.MspId,
IdBytes: consenter.Identity,
})
}
}
return nil
verifier := protoutil.BlockSignatureVerifier(bftEnabled, consenters, policy)
return verifier(block.Header, block.Metadata)
}

// Sign signs msg with this peer's signing key and outputs
Expand Down
10 changes: 0 additions & 10 deletions orderer/common/cluster/export_test.go

This file was deleted.

78 changes: 5 additions & 73 deletions orderer/common/cluster/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import (
"github.com/golang/protobuf/proto"
"github.com/hyperledger/fabric-config/protolator"
"github.com/hyperledger/fabric-protos-go/common"
"github.com/hyperledger/fabric-protos-go/msp"
"github.com/hyperledger/fabric/bccsp"
"github.com/hyperledger/fabric/common/channelconfig"
"github.com/hyperledger/fabric/common/configtx"
Expand Down Expand Up @@ -489,10 +488,8 @@ func globalEndpointsFromConfig(aggregatedTLSCerts [][]byte, bundle *channelconfi
return globalEndpoints
}

type BlockVerifierFunc func(header *common.BlockHeader, metadata *common.BlockMetadata) error

func BlockVerifierBuilder(bccsp bccsp.BCCSP) func(block *common.Block) BlockVerifierFunc {
return func(block *common.Block) BlockVerifierFunc {
func BlockVerifierBuilder(bccsp bccsp.BCCSP) func(block *common.Block) protoutil.BlockVerifierFunc {
return func(block *common.Block) protoutil.BlockVerifierFunc {
bundle, failed := bundleFromConfigBlock(block, bccsp)
if failed != nil {
return failed
Expand All @@ -514,76 +511,11 @@ func BlockVerifierBuilder(bccsp bccsp.BCCSP) func(block *common.Block) BlockVeri
consenters = cfg.Consenters()
}

return blockSignatureVerifier(bftEnabled, consenters, policy)
}
}

func blockSignatureVerifier(bftEnabled bool, consenters []*common.Consenter, policy policies.Policy) BlockVerifierFunc {
return func(header *common.BlockHeader, metadata *common.BlockMetadata) error {
if len(metadata.Metadata) < int(common.BlockMetadataIndex_SIGNATURES)+1 {
return errors.Errorf("no signatures in block metadata")
}

md := &common.Metadata{}
if err := proto.Unmarshal(metadata.Metadata[common.BlockMetadataIndex_SIGNATURES], md); err != nil {
return errors.Wrapf(err, "error unmarshalling signatures from metadata: %v", err)
}

var signatureSet []*protoutil.SignedData
for _, metadataSignature := range md.Signatures {
var signerIdentity []byte
var signedPayload []byte
// if the SignatureHeader is empty and the IdentifierHeader is present, then the consenter expects us to fetch its identity by its numeric identifier
if bftEnabled && len(metadataSignature.GetSignatureHeader()) == 0 && len(metadataSignature.GetIdentifierHeader()) > 0 {
identifierHeader, err := protoutil.UnmarshalIdentifierHeader(metadataSignature.IdentifierHeader)
if err != nil {
return fmt.Errorf("failed unmarshalling identifier header for block %d: %v", header.Number, err)
}
identifier := identifierHeader.GetIdentifier()
signerIdentity = searchConsenterIdentityByID(consenters, identifier)
if len(signerIdentity) == 0 {
// The identifier is not within the consenter set
continue
}
signedPayload = util.ConcatenateBytes(md.Value, metadataSignature.IdentifierHeader, protoutil.BlockHeaderBytes(header))
} else {
signatureHeader, err := protoutil.UnmarshalSignatureHeader(metadataSignature.GetSignatureHeader())
if err != nil {
return fmt.Errorf("failed unmarshalling signature header for block %d: %v", header.Number, err)
}

signedPayload = util.ConcatenateBytes(md.Value, metadataSignature.SignatureHeader, protoutil.BlockHeaderBytes(header))

signerIdentity = signatureHeader.Creator
}

signatureSet = append(
signatureSet,
&protoutil.SignedData{
Identity: signerIdentity,
Data: signedPayload,
Signature: metadataSignature.Signature,
},
)
}

return policy.EvaluateSignedData(signatureSet)
return protoutil.BlockSignatureVerifier(bftEnabled, consenters, policy)
}
}

func searchConsenterIdentityByID(consenters []*common.Consenter, identifier uint32) []byte {
for _, consenter := range consenters {
if consenter.Id == identifier {
return protoutil.MarshalOrPanic(&msp.SerializedIdentity{
Mspid: consenter.MspId,
IdBytes: consenter.Identity,
})
}
}
return nil
}

func bundleFromConfigBlock(block *common.Block, bccsp bccsp.BCCSP) (*channelconfig.Bundle, BlockVerifierFunc) {
func bundleFromConfigBlock(block *common.Block, bccsp bccsp.BCCSP) (*channelconfig.Bundle, protoutil.BlockVerifierFunc) {
if block.Data == nil || len(block.Data.Data) == 0 {
return nil, createErrorFunc(errors.New("block contains no data"))
}
Expand All @@ -601,7 +533,7 @@ func bundleFromConfigBlock(block *common.Block, bccsp bccsp.BCCSP) (*channelconf
return bundle, nil
}

func createErrorFunc(err error) BlockVerifierFunc {
func createErrorFunc(err error) protoutil.BlockVerifierFunc {
return func(_ *common.BlockHeader, _ *common.BlockMetadata) error {
return errors.Wrap(err, "initialized with an invalid config block")
}
Expand Down
113 changes: 0 additions & 113 deletions orderer/common/cluster/util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1274,119 +1274,6 @@ func TestBlockVerifierFunc(t *testing.T) {
require.NoError(t, err)
}

func TestBlockSignatureVerifierEmptyMetadata(t *testing.T) {
policies := mocks.Policy{}

verify := cluster.BlockSignatureVerifier(true, nil, &policies)

header := &common.BlockHeader{}
md := &common.BlockMetadata{}

err := verify(header, md)
require.ErrorContains(t, err, "no signatures in block metadata")
}

func TestBlockSignatureVerifierByIdentifier(t *testing.T) {
consenters := []*common.Consenter{
{
Id: 1,
Host: "host1",
Port: 8001,
MspId: "msp1",
Identity: []byte("identity1"),
},
{
Id: 2,
Host: "host2",
Port: 8002,
MspId: "msp2",
Identity: []byte("identity2"),
},
{
Id: 3,
Host: "host3",
Port: 8003,
MspId: "msp3",
Identity: []byte("identity3"),
},
}

policies := mocks.Policy{}

verify := cluster.BlockSignatureVerifier(true, consenters, &policies)

header := &common.BlockHeader{}
md := &common.BlockMetadata{
Metadata: [][]byte{
protoutil.MarshalOrPanic(&common.Metadata{Signatures: []*common.MetadataSignature{
{
Signature: []byte{},
IdentifierHeader: protoutil.MarshalOrPanic(&common.IdentifierHeader{Identifier: 1}),
},
{
Signature: []byte{},
IdentifierHeader: protoutil.MarshalOrPanic(&common.IdentifierHeader{Identifier: 3}),
},
}}),
},
}

err := verify(header, md)
require.NoError(t, err)
signatureSet := policies.EvaluateSignedDataArgsForCall(0)
require.Len(t, signatureSet, 2)
require.Equal(t, protoutil.MarshalOrPanic(&msp.SerializedIdentity{Mspid: "msp1", IdBytes: []byte("identity1")}), signatureSet[0].Identity)
require.Equal(t, protoutil.MarshalOrPanic(&msp.SerializedIdentity{Mspid: "msp3", IdBytes: []byte("identity3")}), signatureSet[1].Identity)
}

func TestBlockSignatureVerifierByCreator(t *testing.T) {
consenters := []*common.Consenter{
{
Id: 1,
Host: "host1",
Port: 8001,
MspId: "msp1",
Identity: []byte("identity1"),
},
{
Id: 2,
Host: "host2",
Port: 8002,
MspId: "msp2",
Identity: []byte("identity2"),
},
{
Id: 3,
Host: "host3",
Port: 8003,
MspId: "msp3",
Identity: []byte("identity3"),
},
}

policies := mocks.Policy{}

verify := cluster.BlockSignatureVerifier(true, consenters, &policies)

header := &common.BlockHeader{}
md := &common.BlockMetadata{
Metadata: [][]byte{
protoutil.MarshalOrPanic(&common.Metadata{Signatures: []*common.MetadataSignature{
{
Signature: []byte{},
SignatureHeader: protoutil.MarshalOrPanic(&common.SignatureHeader{Creator: []byte("creator1")}),
},
}}),
},
}

err := verify(header, md)
require.NoError(t, err)
signatureSet := policies.EvaluateSignedDataArgsForCall(0)
require.Len(t, signatureSet, 1)
require.Equal(t, []byte("creator1"), signatureSet[0].Identity)
}

func sampleConfigBlock() *common.Block {
return &common.Block{
Header: &common.BlockHeader{
Expand Down
Loading

0 comments on commit f4ea5f7

Please sign in to comment.