github.com/yimialmonte/fabric@v2.1.1+incompatible/core/common/validation/msgvalidation.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package validation
     8  
     9  import (
    10  	"bytes"
    11  
    12  	"github.com/hyperledger/fabric-protos-go/common"
    13  	pb "github.com/hyperledger/fabric-protos-go/peer"
    14  	"github.com/hyperledger/fabric/bccsp"
    15  	"github.com/hyperledger/fabric/common/flogging"
    16  	mspmgmt "github.com/hyperledger/fabric/msp/mgmt"
    17  	"github.com/hyperledger/fabric/protoutil"
    18  	"github.com/pkg/errors"
    19  )
    20  
    21  var putilsLogger = flogging.MustGetLogger("protoutils")
    22  
    23  // given a creator, a message and a signature,
    24  // this function returns nil if the creator
    25  // is a valid cert and the signature is valid
    26  func checkSignatureFromCreator(creatorBytes, sig, msg []byte, ChainID string, cryptoProvider bccsp.BCCSP) error {
    27  	putilsLogger.Debugf("begin")
    28  
    29  	// check for nil argument
    30  	if creatorBytes == nil || sig == nil || msg == nil {
    31  		return errors.New("nil arguments")
    32  	}
    33  
    34  	mspObj := mspmgmt.GetIdentityDeserializer(ChainID, cryptoProvider)
    35  	if mspObj == nil {
    36  		return errors.Errorf("could not get msp for channel [%s]", ChainID)
    37  	}
    38  
    39  	// get the identity of the creator
    40  	creator, err := mspObj.DeserializeIdentity(creatorBytes)
    41  	if err != nil {
    42  		return errors.WithMessage(err, "MSP error")
    43  	}
    44  
    45  	putilsLogger.Debugf("creator is %s", creator.GetIdentifier())
    46  
    47  	// ensure that creator is a valid certificate
    48  	err = creator.Validate()
    49  	if err != nil {
    50  		return errors.WithMessage(err, "creator certificate is not valid")
    51  	}
    52  
    53  	putilsLogger.Debugf("creator is valid")
    54  
    55  	// validate the signature
    56  	err = creator.Verify(msg, sig)
    57  	if err != nil {
    58  		return errors.WithMessage(err, "creator's signature over the proposal is not valid")
    59  	}
    60  
    61  	putilsLogger.Debugf("exits successfully")
    62  
    63  	return nil
    64  }
    65  
    66  // checks for a valid SignatureHeader
    67  func validateSignatureHeader(sHdr *common.SignatureHeader) error {
    68  	// check for nil argument
    69  	if sHdr == nil {
    70  		return errors.New("nil SignatureHeader provided")
    71  	}
    72  
    73  	// ensure that there is a nonce
    74  	if sHdr.Nonce == nil || len(sHdr.Nonce) == 0 {
    75  		return errors.New("invalid nonce specified in the header")
    76  	}
    77  
    78  	// ensure that there is a creator
    79  	if sHdr.Creator == nil || len(sHdr.Creator) == 0 {
    80  		return errors.New("invalid creator specified in the header")
    81  	}
    82  
    83  	return nil
    84  }
    85  
    86  // checks for a valid ChannelHeader
    87  func validateChannelHeader(cHdr *common.ChannelHeader) error {
    88  	// check for nil argument
    89  	if cHdr == nil {
    90  		return errors.New("nil ChannelHeader provided")
    91  	}
    92  
    93  	// validate the header type
    94  	switch common.HeaderType(cHdr.Type) {
    95  	case common.HeaderType_ENDORSER_TRANSACTION:
    96  	case common.HeaderType_CONFIG_UPDATE:
    97  	case common.HeaderType_CONFIG:
    98  	default:
    99  		return errors.Errorf("invalid header type %s", common.HeaderType(cHdr.Type))
   100  	}
   101  
   102  	putilsLogger.Debugf("validateChannelHeader info: header type %d", common.HeaderType(cHdr.Type))
   103  
   104  	// TODO: validate chainID in cHdr.ChainID
   105  
   106  	// Validate epoch in cHdr.Epoch
   107  	// Currently we enforce that Epoch is 0.
   108  	// TODO: This check will be modified once the Epoch management
   109  	// will be in place.
   110  	if cHdr.Epoch != 0 {
   111  		return errors.Errorf("invalid Epoch in ChannelHeader. Expected 0, got [%d]", cHdr.Epoch)
   112  	}
   113  
   114  	// TODO: Validate version in cHdr.Version
   115  
   116  	return nil
   117  }
   118  
   119  // checks for a valid Header
   120  func validateCommonHeader(hdr *common.Header) (*common.ChannelHeader, *common.SignatureHeader, error) {
   121  	if hdr == nil {
   122  		return nil, nil, errors.New("nil header")
   123  	}
   124  
   125  	chdr, err := protoutil.UnmarshalChannelHeader(hdr.ChannelHeader)
   126  	if err != nil {
   127  		return nil, nil, err
   128  	}
   129  
   130  	shdr, err := protoutil.UnmarshalSignatureHeader(hdr.SignatureHeader)
   131  	if err != nil {
   132  		return nil, nil, err
   133  	}
   134  
   135  	err = validateChannelHeader(chdr)
   136  	if err != nil {
   137  		return nil, nil, err
   138  	}
   139  
   140  	err = validateSignatureHeader(shdr)
   141  	if err != nil {
   142  		return nil, nil, err
   143  	}
   144  
   145  	return chdr, shdr, nil
   146  }
   147  
   148  // validateConfigTransaction validates the payload of a
   149  // transaction assuming its type is CONFIG
   150  func validateConfigTransaction(data []byte, hdr *common.Header) error {
   151  	putilsLogger.Debugf("validateConfigTransaction starts for data %p, header %s", data, hdr)
   152  
   153  	// check for nil argument
   154  	if data == nil || hdr == nil {
   155  		return errors.New("nil arguments")
   156  	}
   157  
   158  	// There is no need to do this validation here, the configtx.Validator handles this
   159  
   160  	return nil
   161  }
   162  
   163  // validateEndorserTransaction validates the payload of a
   164  // transaction assuming its type is ENDORSER_TRANSACTION
   165  func validateEndorserTransaction(data []byte, hdr *common.Header) error {
   166  	putilsLogger.Debugf("validateEndorserTransaction starts for data %p, header %s", data, hdr)
   167  
   168  	// check for nil argument
   169  	if data == nil || hdr == nil {
   170  		return errors.New("nil arguments")
   171  	}
   172  
   173  	// if the type is ENDORSER_TRANSACTION we unmarshal a Transaction message
   174  	tx, err := protoutil.UnmarshalTransaction(data)
   175  	if err != nil {
   176  		return err
   177  	}
   178  
   179  	// check for nil argument
   180  	if tx == nil {
   181  		return errors.New("nil transaction")
   182  	}
   183  
   184  	// TODO: validate tx.Version
   185  
   186  	// TODO: validate ChaincodeHeaderExtension
   187  
   188  	// hlf version 1 only supports a single action per transaction
   189  	if len(tx.Actions) != 1 {
   190  		return errors.Errorf("only one action per transaction is supported, tx contains %d", len(tx.Actions))
   191  	}
   192  
   193  	putilsLogger.Debugf("validateEndorserTransaction info: there are %d actions", len(tx.Actions))
   194  
   195  	for _, act := range tx.Actions {
   196  		// check for nil argument
   197  		if act == nil {
   198  			return errors.New("nil action")
   199  		}
   200  
   201  		// if the type is ENDORSER_TRANSACTION we unmarshal a SignatureHeader
   202  		sHdr, err := protoutil.UnmarshalSignatureHeader(act.Header)
   203  		if err != nil {
   204  			return err
   205  		}
   206  
   207  		// validate the SignatureHeader - here we actually only
   208  		// care about the nonce since the creator is in the outer header
   209  		err = validateSignatureHeader(sHdr)
   210  		if err != nil {
   211  			return err
   212  		}
   213  
   214  		putilsLogger.Debugf("validateEndorserTransaction info: signature header is valid")
   215  
   216  		// if the type is ENDORSER_TRANSACTION we unmarshal a ChaincodeActionPayload
   217  		ccActionPayload, err := protoutil.UnmarshalChaincodeActionPayload(act.Payload)
   218  		if err != nil {
   219  			return err
   220  		}
   221  
   222  		// extract the proposal response payload
   223  		prp, err := protoutil.UnmarshalProposalResponsePayload(ccActionPayload.Action.ProposalResponsePayload)
   224  		if err != nil {
   225  			return err
   226  		}
   227  
   228  		// build the original header by stitching together
   229  		// the common ChannelHeader and the per-action SignatureHeader
   230  		hdrOrig := &common.Header{ChannelHeader: hdr.ChannelHeader, SignatureHeader: act.Header}
   231  
   232  		// compute proposalHash
   233  		pHash, err := protoutil.GetProposalHash2(hdrOrig, ccActionPayload.ChaincodeProposalPayload)
   234  		if err != nil {
   235  			return err
   236  		}
   237  
   238  		// ensure that the proposal hash matches
   239  		if !bytes.Equal(pHash, prp.ProposalHash) {
   240  			return errors.New("proposal hash does not match")
   241  		}
   242  	}
   243  
   244  	return nil
   245  }
   246  
   247  // ValidateTransaction checks that the transaction envelope is properly formed
   248  func ValidateTransaction(e *common.Envelope, cryptoProvider bccsp.BCCSP) (*common.Payload, pb.TxValidationCode) {
   249  	putilsLogger.Debugf("ValidateTransactionEnvelope starts for envelope %p", e)
   250  
   251  	// check for nil argument
   252  	if e == nil {
   253  		putilsLogger.Errorf("Error: nil envelope")
   254  		return nil, pb.TxValidationCode_NIL_ENVELOPE
   255  	}
   256  
   257  	// get the payload from the envelope
   258  	payload, err := protoutil.UnmarshalPayload(e.Payload)
   259  	if err != nil {
   260  		putilsLogger.Errorf("GetPayload returns err %s", err)
   261  		return nil, pb.TxValidationCode_BAD_PAYLOAD
   262  	}
   263  
   264  	putilsLogger.Debugf("Header is %s", payload.Header)
   265  
   266  	// validate the header
   267  	chdr, shdr, err := validateCommonHeader(payload.Header)
   268  	if err != nil {
   269  		putilsLogger.Errorf("validateCommonHeader returns err %s", err)
   270  		return nil, pb.TxValidationCode_BAD_COMMON_HEADER
   271  	}
   272  
   273  	// validate the signature in the envelope
   274  	err = checkSignatureFromCreator(shdr.Creator, e.Signature, e.Payload, chdr.ChannelId, cryptoProvider)
   275  	if err != nil {
   276  		putilsLogger.Errorf("checkSignatureFromCreator returns err %s", err)
   277  		return nil, pb.TxValidationCode_BAD_CREATOR_SIGNATURE
   278  	}
   279  
   280  	// TODO: ensure that creator can transact with us (some ACLs?) which set of APIs is supposed to give us this info?
   281  
   282  	// continue the validation in a way that depends on the type specified in the header
   283  	switch common.HeaderType(chdr.Type) {
   284  	case common.HeaderType_ENDORSER_TRANSACTION:
   285  		// Verify that the transaction ID has been computed properly.
   286  		// This check is needed to ensure that the lookup into the ledger
   287  		// for the same TxID catches duplicates.
   288  		err = protoutil.CheckTxID(
   289  			chdr.TxId,
   290  			shdr.Nonce,
   291  			shdr.Creator)
   292  
   293  		if err != nil {
   294  			putilsLogger.Errorf("CheckTxID returns err %s", err)
   295  			return nil, pb.TxValidationCode_BAD_PROPOSAL_TXID
   296  		}
   297  
   298  		err = validateEndorserTransaction(payload.Data, payload.Header)
   299  		putilsLogger.Debugf("ValidateTransactionEnvelope returns err %s", err)
   300  
   301  		if err != nil {
   302  			putilsLogger.Errorf("validateEndorserTransaction returns err %s", err)
   303  			return payload, pb.TxValidationCode_INVALID_ENDORSER_TRANSACTION
   304  		}
   305  		return payload, pb.TxValidationCode_VALID
   306  	case common.HeaderType_CONFIG:
   307  		// Config transactions have signatures inside which will be validated, especially at genesis there may be no creator or
   308  		// signature on the outermost envelope
   309  
   310  		err = validateConfigTransaction(payload.Data, payload.Header)
   311  
   312  		if err != nil {
   313  			putilsLogger.Errorf("validateConfigTransaction returns err %s", err)
   314  			return payload, pb.TxValidationCode_INVALID_CONFIG_TRANSACTION
   315  		}
   316  		return payload, pb.TxValidationCode_VALID
   317  	default:
   318  		return nil, pb.TxValidationCode_UNSUPPORTED_TX_PAYLOAD
   319  	}
   320  }