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