github.com/darrenli6/fabric-sdk-example@v0.0.0-20220109053535-94b13b56df8c/core/common/validation/msgvalidation.go (about)

     1  /*
     2  Copyright IBM Corp. 2016 All Rights Reserved.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8  		 http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package validation
    18  
    19  import (
    20  	"bytes"
    21  	"errors"
    22  	"fmt"
    23  
    24  	"github.com/hyperledger/fabric/common/flogging"
    25  	mspmgmt "github.com/hyperledger/fabric/msp/mgmt"
    26  	"github.com/hyperledger/fabric/protos/common"
    27  	pb "github.com/hyperledger/fabric/protos/peer"
    28  	"github.com/hyperledger/fabric/protos/utils"
    29  )
    30  
    31  var putilsLogger = flogging.MustGetLogger("protoutils")
    32  
    33  // validateChaincodeProposalMessage checks the validity of a Proposal message of type CHAINCODE
    34  func validateChaincodeProposalMessage(prop *pb.Proposal, hdr *common.Header) (*pb.ChaincodeHeaderExtension, error) {
    35  	if prop == nil || hdr == nil {
    36  		return nil, errors.New("Nil arguments")
    37  	}
    38  
    39  	putilsLogger.Debugf("validateChaincodeProposalMessage starts for proposal %p, header %p", prop, hdr)
    40  
    41  	// 4) based on the header type (assuming it's CHAINCODE), look at the extensions
    42  	chaincodeHdrExt, err := utils.GetChaincodeHeaderExtension(hdr)
    43  	if err != nil {
    44  		return nil, errors.New("Invalid header extension for type CHAINCODE")
    45  	}
    46  
    47  	if chaincodeHdrExt.ChaincodeId == nil {
    48  		return nil, errors.New("ChaincodeHeaderExtension.ChaincodeId is nil")
    49  	}
    50  
    51  	putilsLogger.Debugf("validateChaincodeProposalMessage info: header extension references chaincode %s", chaincodeHdrExt.ChaincodeId)
    52  
    53  	//    - ensure that the chaincodeID is correct (?)
    54  	// TODO: should we even do this? If so, using which interface?
    55  
    56  	//    - ensure that the visibility field has some value we understand
    57  	// currently the fabric only supports full visibility: this means that
    58  	// there are no restrictions on which parts of the proposal payload will
    59  	// be visible in the final transaction; this default approach requires
    60  	// no additional instructions in the PayloadVisibility field which is
    61  	// therefore expected to be nil; however the fabric may be extended to
    62  	// encode more elaborate visibility mechanisms that shall be encoded in
    63  	// this field (and handled appropriately by the peer)
    64  	if chaincodeHdrExt.PayloadVisibility != nil {
    65  		return nil, errors.New("Invalid payload visibility field")
    66  	}
    67  
    68  	return chaincodeHdrExt, nil
    69  }
    70  
    71  // ValidateProposalMessage checks the validity of a SignedProposal message
    72  // this function returns Header and ChaincodeHeaderExtension messages since they
    73  // have been unmarshalled and validated
    74  func ValidateProposalMessage(signedProp *pb.SignedProposal) (*pb.Proposal, *common.Header, *pb.ChaincodeHeaderExtension, error) {
    75  	if signedProp == nil {
    76  		return nil, nil, nil, errors.New("Nil arguments")
    77  	}
    78  
    79  	putilsLogger.Debugf("ValidateProposalMessage starts for signed proposal %p", signedProp)
    80  
    81  	// extract the Proposal message from signedProp
    82  	prop, err := utils.GetProposal(signedProp.ProposalBytes)
    83  	if err != nil {
    84  		return nil, nil, nil, err
    85  	}
    86  
    87  	// 1) look at the ProposalHeader
    88  	hdr, err := utils.GetHeader(prop.Header)
    89  	if err != nil {
    90  		return nil, nil, nil, err
    91  	}
    92  
    93  	// validate the header
    94  	chdr, shdr, err := validateCommonHeader(hdr)
    95  	if err != nil {
    96  		return nil, nil, nil, err
    97  	}
    98  
    99  	// validate the signature
   100  	err = checkSignatureFromCreator(shdr.Creator, signedProp.Signature, signedProp.ProposalBytes, chdr.ChannelId)
   101  	if err != nil {
   102  		return nil, nil, nil, err
   103  	}
   104  
   105  	// Verify that the transaction ID has been computed properly.
   106  	// This check is needed to ensure that the lookup into the ledger
   107  	// for the same TxID catches duplicates.
   108  	err = utils.CheckProposalTxID(
   109  		chdr.TxId,
   110  		shdr.Nonce,
   111  		shdr.Creator)
   112  	if err != nil {
   113  		return nil, nil, nil, err
   114  	}
   115  
   116  	// continue the validation in a way that depends on the type specified in the header
   117  	switch common.HeaderType(chdr.Type) {
   118  	case common.HeaderType_CONFIG:
   119  		//which the types are different the validation is the same
   120  		//viz, validate a proposal to a chaincode. If we need other
   121  		//special validation for confguration, we would have to implement
   122  		//special validation
   123  		fallthrough
   124  	case common.HeaderType_ENDORSER_TRANSACTION:
   125  		// validation of the proposal message knowing it's of type CHAINCODE
   126  		chaincodeHdrExt, err := validateChaincodeProposalMessage(prop, hdr)
   127  		if err != nil {
   128  			return nil, nil, nil, err
   129  		}
   130  
   131  		return prop, hdr, chaincodeHdrExt, err
   132  	default:
   133  		//NOTE : we proably need a case
   134  		return nil, nil, nil, fmt.Errorf("Unsupported proposal type %d", common.HeaderType(chdr.Type))
   135  	}
   136  }
   137  
   138  // given a creator, a message and a signature,
   139  // this function returns nil if the creator
   140  // is a valid cert and the signature is valid
   141  func checkSignatureFromCreator(creatorBytes []byte, sig []byte, msg []byte, ChainID string) error {
   142  	putilsLogger.Debugf("checkSignatureFromCreator starts")
   143  
   144  	// check for nil argument
   145  	if creatorBytes == nil || sig == nil || msg == nil {
   146  		return errors.New("Nil arguments")
   147  	}
   148  
   149  	mspObj := mspmgmt.GetIdentityDeserializer(ChainID)
   150  	if mspObj == nil {
   151  		return fmt.Errorf("could not get msp for chain [%s]", ChainID)
   152  	}
   153  
   154  	// get the identity of the creator
   155  	creator, err := mspObj.DeserializeIdentity(creatorBytes)
   156  	if err != nil {
   157  		return fmt.Errorf("Failed to deserialize creator identity, err %s", err)
   158  	}
   159  
   160  	putilsLogger.Debugf("checkSignatureFromCreator info: creator is %s", creator.GetIdentifier())
   161  
   162  	// ensure that creator is a valid certificate
   163  	err = creator.Validate()
   164  	if err != nil {
   165  		return fmt.Errorf("The creator certificate is not valid, err %s", err)
   166  	}
   167  
   168  	putilsLogger.Debugf("checkSignatureFromCreator info: creator is valid")
   169  
   170  	// validate the signature
   171  	err = creator.Verify(msg, sig)
   172  	if err != nil {
   173  		return fmt.Errorf("The creator's signature over the proposal is not valid, err %s", err)
   174  	}
   175  
   176  	putilsLogger.Debugf("checkSignatureFromCreator exists successfully")
   177  
   178  	return nil
   179  }
   180  
   181  // checks for a valid SignatureHeader
   182  func validateSignatureHeader(sHdr *common.SignatureHeader) error {
   183  	// check for nil argument
   184  	if sHdr == nil {
   185  		return errors.New("Nil SignatureHeader provided")
   186  	}
   187  
   188  	// ensure that there is a nonce
   189  	if sHdr.Nonce == nil || len(sHdr.Nonce) == 0 {
   190  		return errors.New("Invalid nonce specified in the header")
   191  	}
   192  
   193  	// ensure that there is a creator
   194  	if sHdr.Creator == nil || len(sHdr.Creator) == 0 {
   195  		return errors.New("Invalid creator specified in the header")
   196  	}
   197  
   198  	return nil
   199  }
   200  
   201  // checks for a valid ChannelHeader
   202  func validateChannelHeader(cHdr *common.ChannelHeader) error {
   203  	// check for nil argument
   204  	if cHdr == nil {
   205  		return errors.New("Nil ChannelHeader provided")
   206  	}
   207  
   208  	// validate the header type
   209  	if common.HeaderType(cHdr.Type) != common.HeaderType_ENDORSER_TRANSACTION &&
   210  		common.HeaderType(cHdr.Type) != common.HeaderType_CONFIG_UPDATE &&
   211  		common.HeaderType(cHdr.Type) != common.HeaderType_CONFIG {
   212  		return fmt.Errorf("invalid header type %s", common.HeaderType(cHdr.Type))
   213  	}
   214  
   215  	putilsLogger.Debugf("validateChannelHeader info: header type %d", common.HeaderType(cHdr.Type))
   216  
   217  	// TODO: validate chainID in cHdr.ChainID
   218  
   219  	// Validate epoch in cHdr.Epoch
   220  	// Currently we enforce that Epoch is 0.
   221  	// TODO: This check will be modified once the Epoch management
   222  	// will be in place.
   223  	if cHdr.Epoch != 0 {
   224  		return fmt.Errorf("Invalid Epoch in ChannelHeader. It must be 0. It was [%d]", cHdr.Epoch)
   225  	}
   226  
   227  	// TODO: Validate version in cHdr.Version
   228  
   229  	return nil
   230  }
   231  
   232  // checks for a valid Header
   233  func validateCommonHeader(hdr *common.Header) (*common.ChannelHeader, *common.SignatureHeader, error) {
   234  	if hdr == nil {
   235  		return nil, nil, errors.New("Nil header")
   236  	}
   237  
   238  	chdr, err := utils.UnmarshalChannelHeader(hdr.ChannelHeader)
   239  	if err != nil {
   240  		return nil, nil, err
   241  	}
   242  
   243  	shdr, err := utils.GetSignatureHeader(hdr.SignatureHeader)
   244  	if err != nil {
   245  		return nil, nil, err
   246  	}
   247  
   248  	err = validateChannelHeader(chdr)
   249  	if err != nil {
   250  		return nil, nil, err
   251  	}
   252  
   253  	err = validateSignatureHeader(shdr)
   254  	if err != nil {
   255  		return nil, nil, err
   256  	}
   257  
   258  	return chdr, shdr, nil
   259  }
   260  
   261  // validateConfigTransaction validates the payload of a
   262  // transaction assuming its type is CONFIG
   263  func validateConfigTransaction(data []byte, hdr *common.Header) error {
   264  	putilsLogger.Debugf("validateConfigTransaction starts for data %p, header %s", data, hdr)
   265  
   266  	// check for nil argument
   267  	if data == nil || hdr == nil {
   268  		return errors.New("Nil arguments")
   269  	}
   270  
   271  	// There is no need to do this validation here, the configtx.Manager handles this
   272  
   273  	return nil
   274  }
   275  
   276  // validateEndorserTransaction validates the payload of a
   277  // transaction assuming its type is ENDORSER_TRANSACTION
   278  func validateEndorserTransaction(data []byte, hdr *common.Header) error {
   279  	putilsLogger.Debugf("validateEndorserTransaction starts for data %p, header %s", data, hdr)
   280  
   281  	// check for nil argument
   282  	if data == nil || hdr == nil {
   283  		return errors.New("Nil arguments")
   284  	}
   285  
   286  	// if the type is ENDORSER_TRANSACTION we unmarshal a Transaction message
   287  	tx, err := utils.GetTransaction(data)
   288  	if err != nil {
   289  		return err
   290  	}
   291  
   292  	// check for nil argument
   293  	if tx == nil {
   294  		return errors.New("Nil transaction")
   295  	}
   296  
   297  	// TODO: validate tx.Version
   298  
   299  	// TODO: validate ChaincodeHeaderExtension
   300  
   301  	// hlf version 1 only supports a single action per transaction
   302  	if len(tx.Actions) != 1 {
   303  		return fmt.Errorf("Only one action per transaction is supported (tx contains %d)", len(tx.Actions))
   304  	}
   305  
   306  	putilsLogger.Debugf("validateEndorserTransaction info: there are %d actions", len(tx.Actions))
   307  
   308  	for _, act := range tx.Actions {
   309  		// check for nil argument
   310  		if act == nil {
   311  			return errors.New("Nil action")
   312  		}
   313  
   314  		// if the type is ENDORSER_TRANSACTION we unmarshal a SignatureHeader
   315  		sHdr, err := utils.GetSignatureHeader(act.Header)
   316  		if err != nil {
   317  			return err
   318  		}
   319  
   320  		// validate the SignatureHeader - here we actually only
   321  		// care about the nonce since the creator is in the outer header
   322  		err = validateSignatureHeader(sHdr)
   323  		if err != nil {
   324  			return err
   325  		}
   326  
   327  		putilsLogger.Debugf("validateEndorserTransaction info: signature header is valid")
   328  
   329  		// if the type is ENDORSER_TRANSACTION we unmarshal a ChaincodeActionPayload
   330  		ccActionPayload, err := utils.GetChaincodeActionPayload(act.Payload)
   331  		if err != nil {
   332  			return err
   333  		}
   334  
   335  		// extract the proposal response payload
   336  		prp, err := utils.GetProposalResponsePayload(ccActionPayload.Action.ProposalResponsePayload)
   337  		if err != nil {
   338  			return err
   339  		}
   340  
   341  		// build the original header by stitching together
   342  		// the common ChannelHeader and the per-action SignatureHeader
   343  		hdrOrig := &common.Header{ChannelHeader: hdr.ChannelHeader, SignatureHeader: act.Header}
   344  
   345  		// compute proposalHash
   346  		pHash, err := utils.GetProposalHash2(hdrOrig, ccActionPayload.ChaincodeProposalPayload)
   347  		if err != nil {
   348  			return err
   349  		}
   350  
   351  		// ensure that the proposal hash matches
   352  		if bytes.Compare(pHash, prp.ProposalHash) != 0 {
   353  			return errors.New("proposal hash does not match")
   354  		}
   355  	}
   356  
   357  	return nil
   358  }
   359  
   360  // ValidateTransaction checks that the transaction envelope is properly formed
   361  func ValidateTransaction(e *common.Envelope) (*common.Payload, pb.TxValidationCode) {
   362  	putilsLogger.Debugf("ValidateTransactionEnvelope starts for envelope %p", e)
   363  
   364  	// check for nil argument
   365  	if e == nil {
   366  		putilsLogger.Errorf("Error: nil envelope")
   367  		return nil, pb.TxValidationCode_NIL_ENVELOPE
   368  	}
   369  
   370  	// get the payload from the envelope
   371  	payload, err := utils.GetPayload(e)
   372  	if err != nil {
   373  		putilsLogger.Errorf("GetPayload returns err %s", err)
   374  		return nil, pb.TxValidationCode_BAD_PAYLOAD
   375  	}
   376  
   377  	putilsLogger.Debugf("Header is %s", payload.Header)
   378  
   379  	// validate the header
   380  	chdr, shdr, err := validateCommonHeader(payload.Header)
   381  	if err != nil {
   382  		putilsLogger.Errorf("validateCommonHeader returns err %s", err)
   383  		return nil, pb.TxValidationCode_BAD_COMMON_HEADER
   384  	}
   385  
   386  	// validate the signature in the envelope
   387  	err = checkSignatureFromCreator(shdr.Creator, e.Signature, e.Payload, chdr.ChannelId)
   388  	if err != nil {
   389  		putilsLogger.Errorf("checkSignatureFromCreator returns err %s", err)
   390  		return nil, pb.TxValidationCode_BAD_CREATOR_SIGNATURE
   391  	}
   392  
   393  	// TODO: ensure that creator can transact with us (some ACLs?) which set of APIs is supposed to give us this info?
   394  
   395  	// continue the validation in a way that depends on the type specified in the header
   396  	switch common.HeaderType(chdr.Type) {
   397  	case common.HeaderType_ENDORSER_TRANSACTION:
   398  		// Verify that the transaction ID has been computed properly.
   399  		// This check is needed to ensure that the lookup into the ledger
   400  		// for the same TxID catches duplicates.
   401  		err = utils.CheckProposalTxID(
   402  			chdr.TxId,
   403  			shdr.Nonce,
   404  			shdr.Creator)
   405  
   406  		if err != nil {
   407  			putilsLogger.Errorf("CheckProposalTxID returns err %s", err)
   408  			return nil, pb.TxValidationCode_BAD_PROPOSAL_TXID
   409  		}
   410  
   411  		err = validateEndorserTransaction(payload.Data, payload.Header)
   412  		putilsLogger.Debugf("ValidateTransactionEnvelope returns err %s", err)
   413  
   414  		if err != nil {
   415  			putilsLogger.Errorf("validateEndorserTransaction returns err %s", err)
   416  			return payload, pb.TxValidationCode_INVALID_ENDORSER_TRANSACTION
   417  		} else {
   418  			return payload, pb.TxValidationCode_VALID
   419  		}
   420  	case common.HeaderType_CONFIG:
   421  		// Config transactions have signatures inside which will be validated, especially at genesis there may be no creator or
   422  		// signature on the outermost envelope
   423  
   424  		err = validateConfigTransaction(payload.Data, payload.Header)
   425  
   426  		if err != nil {
   427  			putilsLogger.Errorf("validateConfigTransaction returns err %s", err)
   428  			return payload, pb.TxValidationCode_INVALID_CONFIG_TRANSACTION
   429  		} else {
   430  			return payload, pb.TxValidationCode_VALID
   431  		}
   432  	default:
   433  		return nil, pb.TxValidationCode_UNSUPPORTED_TX_PAYLOAD
   434  	}
   435  }