go_study/fabric-main/internal/peer/gossip/mcs.go

355 lines
13 KiB
Go

/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package gossip
import (
"bytes"
"fmt"
"time"
pcommon "github.com/hyperledger/fabric-protos-go/common"
"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/gossip/api"
"github.com/hyperledger/fabric/gossip/common"
"github.com/hyperledger/fabric/internal/pkg/identity"
"github.com/hyperledger/fabric/msp"
"github.com/hyperledger/fabric/protoutil"
"github.com/pkg/errors"
)
var mcsLogger = flogging.MustGetLogger("peer.gossip.mcs")
// Hasher is the interface provides the hash function should be used for all gossip components.
type Hasher interface {
Hash(msg []byte, opts bccsp.HashOpts) (hash []byte, err error)
}
type ChannelConfigGetter func(cid string) channelconfig.Resources
// MSPMessageCryptoService implements the MessageCryptoService interface
// using the peer MSPs (local and channel-related)
//
// In order for the system to be secure it is vital to have the
// MSPs to be up-to-date. Channels' MSPs are updated via
// configuration transactions distributed by the ordering service.
//
// A similar mechanism needs to be in place to update the local MSP, as well.
// This implementation assumes that these mechanisms are all in place and working.
type MSPMessageCryptoService struct {
channelPolicyManagerGetter policies.ChannelPolicyManagerGetter
localSigner identity.SignerSerializer
deserializer DeserializersManager
hasher Hasher
channelConfigGetter ChannelConfigGetter
}
// NewMCS creates a new instance of MSPMessageCryptoService
// that implements MessageCryptoService.
// The method takes in input:
// 1. a policies.ChannelPolicyManagerGetter that gives access to the policy manager of a given channel via the Manager method.
// 2. an instance of identity.SignerSerializer
// 3. an identity deserializer manager
func NewMCS(
channelPolicyManagerGetter policies.ChannelPolicyManagerGetter,
localSigner identity.SignerSerializer,
deserializer DeserializersManager,
hasher Hasher,
channelConfigGetter ChannelConfigGetter,
) *MSPMessageCryptoService {
return &MSPMessageCryptoService{
channelPolicyManagerGetter: channelPolicyManagerGetter,
localSigner: localSigner,
deserializer: deserializer,
hasher: hasher,
channelConfigGetter: channelConfigGetter,
}
}
// ValidateIdentity validates the identity of a remote peer.
// If the identity is invalid, revoked, expired it returns an error.
// Else, returns nil
func (s *MSPMessageCryptoService) ValidateIdentity(peerIdentity api.PeerIdentityType) error {
// As prescribed by the contract of method,
// below we check only that peerIdentity is not
// invalid, revoked or expired.
_, _, err := s.getValidatedIdentity(peerIdentity)
return err
}
// GetPKIidOfCert returns the PKI-ID of a peer's identity
// If any error occurs, the method return nil
// The PKid of a peer is computed as the SHA2-256 of peerIdentity which
// is supposed to be the serialized version of MSP identity.
// This method does not validate peerIdentity.
// This validation is supposed to be done appropriately during the execution flow.
func (s *MSPMessageCryptoService) GetPKIidOfCert(peerIdentity api.PeerIdentityType) common.PKIidType {
// Validate arguments
if len(peerIdentity) == 0 {
mcsLogger.Error("Invalid Peer Identity. It must be different from nil.")
return nil
}
sid, err := s.deserializer.Deserialize(peerIdentity)
if err != nil {
mcsLogger.Errorf("Failed getting validated identity from peer identity %s: [%s]", peerIdentity, err)
return nil
}
// concatenate msp-id and idbytes
// idbytes is the low-level representation of an identity.
// it is supposed to be already in its minimal representation
mspIDRaw := []byte(sid.Mspid)
raw := append(mspIDRaw, sid.IdBytes...)
// Hash
digest, err := s.hasher.Hash(raw, &bccsp.SHA256Opts{})
if err != nil {
mcsLogger.Errorf("Failed computing digest of serialized identity %s: [%s]", peerIdentity, err)
return nil
}
return digest
}
// VerifyBlock returns nil if the block is properly signed, and the claimed seqNum is the
// sequence number that the block's header contains.
// else returns error
func (s *MSPMessageCryptoService) VerifyBlock(chainID common.ChannelID, seqNum uint64, block *pcommon.Block) error {
if block.Header == nil {
return fmt.Errorf("Invalid Block on channel [%s]. Header must be different from nil.", chainID)
}
blockSeqNum := block.Header.Number
if seqNum != blockSeqNum {
return fmt.Errorf("Claimed seqNum is [%d] but actual seqNum inside block is [%d]", seqNum, blockSeqNum)
}
// - Extract channelID and compare with chainID
channelID, err := protoutil.GetChannelIDFromBlock(block)
if err != nil {
return fmt.Errorf("Failed getting channel id from block with id [%d] on channel [%s]: [%s]", block.Header.Number, chainID, err)
}
if channelID != string(chainID) {
return fmt.Errorf("Invalid block's channel id. Expected [%s]. Given [%s]", chainID, channelID)
}
// - 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)
}
// - Verify that Header.DataHash is equal to the hash of block.Data
// This is to ensure that the header is consistent with the data carried by this block
if !bytes.Equal(protoutil.BlockDataHash(block.Data), block.Header.DataHash) {
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 {
return fmt.Errorf("Could not acquire policy manager for channel %s", channelID)
}
mcsLogger.Debugf("Got policy manager for channel [%s]", channelID)
// Get block validation policy
policy, ok := cpm.GetPolicy(policies.BlockValidation)
// ok is true if it was the policy requested, or false if it is the default policy
mcsLogger.Debugf("Got block validation policy for channel [%s] with flag [%t]", channelID, ok)
chConfig := s.channelConfigGetter(channelID)
bftEnabled := chConfig.ChannelConfig().Capabilities().ConsensusTypeBFT()
var consenters []*pcommon.Consenter
if bftEnabled {
cfg, ok := chConfig.OrdererConfig()
if !ok {
return fmt.Errorf("no orderer section in channel config for channel [%s].", channelID)
}
consenters = cfg.Consenters()
}
verifier := protoutil.BlockSignatureVerifier(bftEnabled, consenters, policy)
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) {
return s.localSigner.Sign(msg)
}
// Verify checks that signature is a valid signature of message under a peer's verification key.
// If the verification succeeded, Verify returns nil meaning no error occurred.
// If peerIdentity is nil, then the verification fails.
func (s *MSPMessageCryptoService) Verify(peerIdentity api.PeerIdentityType, signature, message []byte) error {
identity, chainID, err := s.getValidatedIdentity(peerIdentity)
if err != nil {
mcsLogger.Errorf("Failed getting validated identity from peer identity [%s]", err)
return err
}
if len(chainID) == 0 {
// At this stage, this means that peerIdentity
// belongs to this peer's LocalMSP.
// The signature is validated directly
return identity.Verify(message, signature)
}
// At this stage, the signature must be validated
// against the reader policy of the channel
// identified by chainID
return s.VerifyByChannel(chainID, peerIdentity, signature, message)
}
// VerifyByChannel checks that signature is a valid signature of message
// under a peer's verification key, but also in the context of a specific channel.
// If the verification succeeded, Verify returns nil meaning no error occurred.
// If peerIdentity is nil, then the verification fails.
func (s *MSPMessageCryptoService) VerifyByChannel(chainID common.ChannelID, peerIdentity api.PeerIdentityType, signature, message []byte) error {
// Validate arguments
if len(peerIdentity) == 0 {
return errors.New("Invalid Peer Identity. It must be different from nil.")
}
// Get the policy manager for channel chainID
cpm := s.channelPolicyManagerGetter.Manager(string(chainID))
if cpm == nil {
return fmt.Errorf("Could not acquire policy manager for channel %s", string(chainID))
}
mcsLogger.Debugf("Got policy manager for channel [%s]", string(chainID))
// Get channel reader policy
policy, flag := cpm.GetPolicy(policies.ChannelApplicationReaders)
mcsLogger.Debugf("Got reader policy for channel [%s] with flag [%t]", string(chainID), flag)
return policy.EvaluateSignedData(
[]*protoutil.SignedData{{
Data: message,
Identity: []byte(peerIdentity),
Signature: signature,
}},
)
}
func (s *MSPMessageCryptoService) Expiration(peerIdentity api.PeerIdentityType) (time.Time, error) {
id, _, err := s.getValidatedIdentity(peerIdentity)
if err != nil {
return time.Time{}, errors.Wrap(err, "Unable to extract msp.Identity from peer Identity")
}
return id.ExpiresAt(), nil
}
func (s *MSPMessageCryptoService) getValidatedIdentity(peerIdentity api.PeerIdentityType) (msp.Identity, common.ChannelID, error) {
// Validate arguments
if len(peerIdentity) == 0 {
return nil, nil, errors.New("Invalid Peer Identity. It must be different from nil.")
}
sId, err := s.deserializer.Deserialize(peerIdentity)
if err != nil {
mcsLogger.Error("failed deserializing identity", err)
return nil, nil, err
}
// Notice that peerIdentity is assumed to be the serialization of an identity.
// So, first step is the identity deserialization and then verify it.
// First check against the local MSP.
// If the peerIdentity is in the same organization of this node then
// the local MSP is required to take the final decision on the validity
// of the signature.
lDes := s.deserializer.GetLocalDeserializer()
identity, err := lDes.DeserializeIdentity([]byte(peerIdentity))
if err == nil {
// No error means that the local MSP successfully deserialized the identity.
// We now check additional properties.
if err := lDes.IsWellFormed(sId); err != nil {
return nil, nil, errors.Wrap(err, "identity is not well formed")
}
// TODO: The following check will be replaced by a check on the organizational units
// when we allow the gossip network to have organization unit (MSP subdivisions)
// scoped messages.
// The following check is consistent with the SecurityAdvisor#OrgByPeerIdentity
// implementation.
// TODO: Notice that the following check saves us from the fact
// that DeserializeIdentity does not yet enforce MSP-IDs consistency.
// This check can be removed once DeserializeIdentity will be fixed.
if identity.GetMSPIdentifier() == s.deserializer.GetLocalMSPIdentifier() {
// Check identity validity
// Notice that at this stage we don't have to check the identity
// against any channel's policies.
// This will be done by the caller function, if needed.
return identity, nil, identity.Validate()
}
}
// Check against managers
for chainID, mspManager := range s.deserializer.GetChannelDeserializers() {
// Deserialize identity
identity, err := mspManager.DeserializeIdentity([]byte(peerIdentity))
if err != nil {
mcsLogger.Debugf("Failed deserialization identity %s on [%s]: [%s]", peerIdentity, chainID, err)
continue
}
// We managed deserializing the identity with this MSP manager. Now we check if it's well formed.
if err := mspManager.IsWellFormed(sId); err != nil {
return nil, nil, errors.Wrap(err, "identity is not well formed")
}
// Check identity validity
// Notice that at this stage we don't have to check the identity
// against any channel's policies.
// This will be done by the caller function, if needed.
if err := identity.Validate(); err != nil {
mcsLogger.Debugf("Failed validating identity %s on [%s]: [%s]", peerIdentity, chainID, err)
continue
}
mcsLogger.Debugf("Validation succeeded %s on [%s]", peerIdentity, chainID)
return identity, common.ChannelID(chainID), nil
}
return nil, nil, fmt.Errorf("Peer Identity %s cannot be validated. No MSP found able to do that.", peerIdentity)
}