github.com/fnagchunpeng/fabric@v2.1.1+incompatible/core/endorser/msgvalidation.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package endorser
     8  
     9  import (
    10  	"crypto/sha256"
    11  
    12  	"github.com/golang/protobuf/proto"
    13  	"github.com/hyperledger/fabric-protos-go/common"
    14  	cb "github.com/hyperledger/fabric-protos-go/common"
    15  	pb "github.com/hyperledger/fabric-protos-go/peer"
    16  	"github.com/hyperledger/fabric/core/common/ccprovider"
    17  	"github.com/hyperledger/fabric/msp"
    18  	"github.com/hyperledger/fabric/protoutil"
    19  	"github.com/pkg/errors"
    20  )
    21  
    22  // UnpackedProposal contains the interesting artifacts from inside the proposal.
    23  type UnpackedProposal struct {
    24  	ChaincodeName   string
    25  	ChannelHeader   *cb.ChannelHeader
    26  	Input           *pb.ChaincodeInput
    27  	Proposal        *pb.Proposal
    28  	SignatureHeader *cb.SignatureHeader
    29  	SignedProposal  *pb.SignedProposal
    30  	ProposalHash    []byte
    31  }
    32  
    33  func (up *UnpackedProposal) ChannelID() string {
    34  	return up.ChannelHeader.ChannelId
    35  }
    36  
    37  func (up *UnpackedProposal) TxID() string {
    38  	return up.ChannelHeader.TxId
    39  }
    40  
    41  // UnpackProposal creates an an *UnpackedProposal which is guaranteed to have
    42  // no zero-ed fields or it returns an error.
    43  func UnpackProposal(signedProp *pb.SignedProposal) (*UnpackedProposal, error) {
    44  	prop, err := protoutil.UnmarshalProposal(signedProp.ProposalBytes)
    45  	if err != nil {
    46  		return nil, err
    47  	}
    48  
    49  	hdr, err := protoutil.UnmarshalHeader(prop.Header)
    50  	if err != nil {
    51  		return nil, err
    52  	}
    53  
    54  	chdr, err := protoutil.UnmarshalChannelHeader(hdr.ChannelHeader)
    55  	if err != nil {
    56  		return nil, err
    57  	}
    58  
    59  	shdr, err := protoutil.UnmarshalSignatureHeader(hdr.SignatureHeader)
    60  	if err != nil {
    61  		return nil, err
    62  	}
    63  
    64  	chaincodeHdrExt, err := protoutil.UnmarshalChaincodeHeaderExtension(chdr.Extension)
    65  	if err != nil {
    66  		return nil, err
    67  	}
    68  
    69  	if chaincodeHdrExt.ChaincodeId == nil {
    70  		return nil, errors.Errorf("ChaincodeHeaderExtension.ChaincodeId is nil")
    71  	}
    72  
    73  	if chaincodeHdrExt.ChaincodeId.Name == "" {
    74  		return nil, errors.Errorf("ChaincodeHeaderExtension.ChaincodeId.Name is empty")
    75  	}
    76  
    77  	cpp, err := protoutil.UnmarshalChaincodeProposalPayload(prop.Payload)
    78  	if err != nil {
    79  		return nil, err
    80  	}
    81  
    82  	cis, err := protoutil.UnmarshalChaincodeInvocationSpec(cpp.Input)
    83  	if err != nil {
    84  		return nil, err
    85  	}
    86  
    87  	if cis.ChaincodeSpec == nil {
    88  		return nil, errors.Errorf("chaincode invocation spec did not contain chaincode spec")
    89  	}
    90  
    91  	if cis.ChaincodeSpec.Input == nil {
    92  		return nil, errors.Errorf("chaincode input did not contain any input")
    93  	}
    94  
    95  	cppNoTransient := &pb.ChaincodeProposalPayload{Input: cpp.Input, TransientMap: nil}
    96  	ppBytes, err := proto.Marshal(cppNoTransient)
    97  	if err != nil {
    98  		return nil, errors.WithMessage(err, "could not marshal non-transient portion of payload")
    99  	}
   100  
   101  	// TODO, this was preserved from the proputils stuff, but should this be BCCSP?
   102  
   103  	// The proposal hash is the hash of the concatenation of:
   104  	// 1) The serialized Channel Header object
   105  	// 2) The serialized Signature Header object
   106  	// 3) The hash of the part of the chaincode proposal payload that will go to the tx
   107  	// (ie, the parts without the transient data)
   108  	propHash := sha256.New()
   109  	propHash.Write(hdr.ChannelHeader)
   110  	propHash.Write(hdr.SignatureHeader)
   111  	propHash.Write(ppBytes)
   112  
   113  	return &UnpackedProposal{
   114  		SignedProposal:  signedProp,
   115  		Proposal:        prop,
   116  		ChannelHeader:   chdr,
   117  		SignatureHeader: shdr,
   118  		ChaincodeName:   chaincodeHdrExt.ChaincodeId.Name,
   119  		Input:           cis.ChaincodeSpec.Input,
   120  		ProposalHash:    propHash.Sum(nil)[:],
   121  	}, nil
   122  }
   123  
   124  func (up *UnpackedProposal) Validate(idDeserializer msp.IdentityDeserializer) error {
   125  	logger := decorateLogger(endorserLogger, &ccprovider.TransactionParams{
   126  		ChannelID: up.ChannelHeader.ChannelId,
   127  		TxID:      up.TxID(),
   128  	})
   129  
   130  	// validate the header type
   131  	switch common.HeaderType(up.ChannelHeader.Type) {
   132  	case common.HeaderType_ENDORSER_TRANSACTION:
   133  	case common.HeaderType_CONFIG:
   134  		// The CONFIG transaction type has _no_ business coming to the propose API.
   135  		// In fact, anything coming to the Propose API is by definition an endorser
   136  		// transaction, so any other header type seems like it ought to be an error... oh well.
   137  
   138  	default:
   139  		return errors.Errorf("invalid header type %s", common.HeaderType(up.ChannelHeader.Type))
   140  	}
   141  
   142  	// ensure the epoch is 0
   143  	if up.ChannelHeader.Epoch != 0 {
   144  		return errors.Errorf("epoch is non-zero")
   145  	}
   146  
   147  	// ensure that there is a nonce
   148  	if len(up.SignatureHeader.Nonce) == 0 {
   149  		return errors.Errorf("nonce is empty")
   150  	}
   151  
   152  	// ensure that there is a creator
   153  	if len(up.SignatureHeader.Creator) == 0 {
   154  		return errors.New("creator is empty")
   155  	}
   156  
   157  	expectedTxID := protoutil.ComputeTxID(up.SignatureHeader.Nonce, up.SignatureHeader.Creator)
   158  	if up.TxID() != expectedTxID {
   159  		return errors.Errorf("incorrectly computed txid '%s' -- expected '%s'", up.TxID(), expectedTxID)
   160  	}
   161  
   162  	if up.SignedProposal.ProposalBytes == nil {
   163  		return errors.Errorf("empty proposal bytes")
   164  	}
   165  
   166  	if up.SignedProposal.Signature == nil {
   167  		return errors.Errorf("empty signature bytes")
   168  	}
   169  
   170  	sId, err := protoutil.UnmarshalSerializedIdentity(up.SignatureHeader.Creator)
   171  	if err != nil {
   172  		return errors.Errorf("access denied: channel [%s] creator org unknown, creator is malformed", up.ChannelID())
   173  	}
   174  
   175  	genericAuthError := errors.Errorf("access denied: channel [%s] creator org [%s]", up.ChannelID(), sId.Mspid)
   176  
   177  	// get the identity of the creator
   178  	creator, err := idDeserializer.DeserializeIdentity(up.SignatureHeader.Creator)
   179  	if err != nil {
   180  		logger.Warningf("access denied: channel %s", err)
   181  		return genericAuthError
   182  	}
   183  
   184  	// ensure that creator is a valid certificate
   185  	err = creator.Validate()
   186  	if err != nil {
   187  		logger.Warningf("access denied: identity is not valid: %s", err)
   188  		return genericAuthError
   189  	}
   190  
   191  	logger = logger.With("mspID", creator.GetMSPIdentifier())
   192  
   193  	logger.Debug("creator is valid")
   194  
   195  	// validate the signature
   196  	err = creator.Verify(up.SignedProposal.ProposalBytes, up.SignedProposal.Signature)
   197  	if err != nil {
   198  		logger.Warningf("access denied: creator's signature over the proposal is not valid: %s", err)
   199  		return genericAuthError
   200  	}
   201  
   202  	logger.Debug("signature is valid")
   203  
   204  	return nil
   205  }