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