github.com/myafeier/fabric@v1.0.1-0.20170722181825-3a4b1f2bce86/core/scc/vscc/validator_onevalidsignature.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 vscc
    18  
    19  import (
    20  	"fmt"
    21  
    22  	"errors"
    23  
    24  	"github.com/golang/protobuf/proto"
    25  
    26  	"github.com/hyperledger/fabric/common/cauthdsl"
    27  	"github.com/hyperledger/fabric/common/flogging"
    28  	"github.com/hyperledger/fabric/core/chaincode/shim"
    29  	"github.com/hyperledger/fabric/core/common/ccprovider"
    30  	"github.com/hyperledger/fabric/core/common/sysccprovider"
    31  	"github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/rwsetutil"
    32  	"github.com/hyperledger/fabric/core/scc/lscc"
    33  	mspmgmt "github.com/hyperledger/fabric/msp/mgmt"
    34  	"github.com/hyperledger/fabric/protos/common"
    35  	"github.com/hyperledger/fabric/protos/ledger/rwset/kvrwset"
    36  	"github.com/hyperledger/fabric/protos/msp"
    37  	pb "github.com/hyperledger/fabric/protos/peer"
    38  	"github.com/hyperledger/fabric/protos/utils"
    39  )
    40  
    41  var logger = flogging.MustGetLogger("vscc")
    42  
    43  const (
    44  	DUPLICATED_IDENTITY_ERROR = "Endorsement policy evaluation failure might be caused by duplicated identities"
    45  )
    46  
    47  // ValidatorOneValidSignature implements the default transaction validation policy,
    48  // which is to check the correctness of the read-write set and the endorsement
    49  // signatures
    50  type ValidatorOneValidSignature struct {
    51  	// sccprovider is the interface with which we call
    52  	// methods of the system chaincode package without
    53  	// import cycles
    54  	sccprovider sysccprovider.SystemChaincodeProvider
    55  }
    56  
    57  // Init is called once when the chaincode started the first time
    58  func (vscc *ValidatorOneValidSignature) Init(stub shim.ChaincodeStubInterface) pb.Response {
    59  	vscc.sccprovider = sysccprovider.GetSystemChaincodeProvider()
    60  
    61  	return shim.Success(nil)
    62  }
    63  
    64  // Invoke is called to validate the specified block of transactions
    65  // This validation system chaincode will check the read-write set validity and at least 1
    66  // correct endorsement. Later we can create more validation system
    67  // chaincodes to provide more sophisticated policy processing such as enabling
    68  // policy specification to be coded as a transaction of the chaincode and the client
    69  // selecting which policy to use for validation using parameter function
    70  // @return serialized Block of valid and invalid transactions identified
    71  // Note that Peer calls this function with 3 arguments, where args[0] is the
    72  // function name, args[1] is the Envelope and args[2] is the validation policy
    73  func (vscc *ValidatorOneValidSignature) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
    74  	// TODO: document the argument in some white paper or design document
    75  	// args[0] - function name (not used now)
    76  	// args[1] - serialized Envelope
    77  	// args[2] - serialized policy
    78  	args := stub.GetArgs()
    79  	if len(args) < 3 {
    80  		return shim.Error("Incorrect number of arguments")
    81  	}
    82  
    83  	if args[1] == nil {
    84  		return shim.Error("No block to validate")
    85  	}
    86  
    87  	if args[2] == nil {
    88  		return shim.Error("No policy supplied")
    89  	}
    90  
    91  	logger.Debugf("VSCC invoked")
    92  
    93  	// get the envelope...
    94  	env, err := utils.GetEnvelopeFromBlock(args[1])
    95  	if err != nil {
    96  		logger.Errorf("VSCC error: GetEnvelope failed, err %s", err)
    97  		return shim.Error(err.Error())
    98  	}
    99  
   100  	// ...and the payload...
   101  	payl, err := utils.GetPayload(env)
   102  	if err != nil {
   103  		logger.Errorf("VSCC error: GetPayload failed, err %s", err)
   104  		return shim.Error(err.Error())
   105  	}
   106  
   107  	chdr, err := utils.UnmarshalChannelHeader(payl.Header.ChannelHeader)
   108  	if err != nil {
   109  		return shim.Error(err.Error())
   110  	}
   111  
   112  	// get the policy
   113  	mgr := mspmgmt.GetManagerForChain(chdr.ChannelId)
   114  	pProvider := cauthdsl.NewPolicyProvider(mgr)
   115  	policy, _, err := pProvider.NewPolicy(args[2])
   116  	if err != nil {
   117  		logger.Errorf("VSCC error: pProvider.NewPolicy failed, err %s", err)
   118  		return shim.Error(err.Error())
   119  	}
   120  
   121  	// validate the payload type
   122  	if common.HeaderType(chdr.Type) != common.HeaderType_ENDORSER_TRANSACTION {
   123  		logger.Errorf("Only Endorser Transactions are supported, provided type %d", chdr.Type)
   124  		return shim.Error(fmt.Sprintf("Only Endorser Transactions are supported, provided type %d", chdr.Type))
   125  	}
   126  
   127  	// ...and the transaction...
   128  	tx, err := utils.GetTransaction(payl.Data)
   129  	if err != nil {
   130  		logger.Errorf("VSCC error: GetTransaction failed, err %s", err)
   131  		return shim.Error(err.Error())
   132  	}
   133  
   134  	// loop through each of the actions within
   135  	for _, act := range tx.Actions {
   136  		cap, err := utils.GetChaincodeActionPayload(act.Payload)
   137  		if err != nil {
   138  			logger.Errorf("VSCC error: GetChaincodeActionPayload failed, err %s", err)
   139  			return shim.Error(err.Error())
   140  		}
   141  
   142  		signatureSet, err := vscc.deduplicateIdentity(cap)
   143  		if err != nil {
   144  			return shim.Error(err.Error())
   145  		}
   146  
   147  		// evaluate the signature set against the policy
   148  		err = policy.Evaluate(signatureSet)
   149  		if err != nil {
   150  			logger.Warningf("Endorsement policy failure for transaction txid=%s, err: %s", chdr.GetTxId(), err.Error())
   151  			if len(signatureSet) < len(cap.Action.Endorsements) {
   152  				// Warning: duplicated identities exist, endorsement failure might be cause by this reason
   153  				return shim.Error(DUPLICATED_IDENTITY_ERROR)
   154  			}
   155  			return shim.Error(fmt.Sprintf("VSCC error: policy evaluation failed, err %s", err))
   156  		}
   157  
   158  		hdrExt, err := utils.GetChaincodeHeaderExtension(payl.Header)
   159  		if err != nil {
   160  			logger.Errorf("VSCC error: GetChaincodeHeaderExtension failed, err %s", err)
   161  			return shim.Error(err.Error())
   162  		}
   163  
   164  		// do some extra validation that is specific to lscc
   165  		if hdrExt.ChaincodeId.Name == "lscc" {
   166  			logger.Debugf("VSCC info: doing special validation for LSCC")
   167  
   168  			err = vscc.ValidateLSCCInvocation(stub, chdr.ChannelId, env, cap, payl)
   169  			if err != nil {
   170  				logger.Errorf("VSCC error: ValidateLSCCInvocation failed, err %s", err)
   171  				return shim.Error(err.Error())
   172  			}
   173  		}
   174  	}
   175  
   176  	logger.Debugf("VSCC exists successfully")
   177  
   178  	return shim.Success(nil)
   179  }
   180  
   181  // checkInstantiationPolicy evaluates an instantiation policy against a signed proposal
   182  func (vscc *ValidatorOneValidSignature) checkInstantiationPolicy(chainName string, env *common.Envelope, instantiationPolicy []byte, payl *common.Payload) error {
   183  	// create a policy object from the policy bytes
   184  	mgr := mspmgmt.GetManagerForChain(chainName)
   185  	if mgr == nil {
   186  		return fmt.Errorf("MSP manager for channel %s is nil, aborting", chainName)
   187  	}
   188  
   189  	npp := cauthdsl.NewPolicyProvider(mgr)
   190  	instPol, _, err := npp.NewPolicy(instantiationPolicy)
   191  	if err != nil {
   192  		return err
   193  	}
   194  
   195  	logger.Debugf("VSCC info: checkInstantiationPolicy starts, policy is %#v", instPol)
   196  
   197  	// get the signature header
   198  	shdr, err := utils.GetSignatureHeader(payl.Header.SignatureHeader)
   199  	if err != nil {
   200  		return err
   201  	}
   202  
   203  	// construct signed data we can evaluate the instantiation policy against
   204  	sd := []*common.SignedData{&common.SignedData{
   205  		Data:      env.Payload,
   206  		Identity:  shdr.Creator,
   207  		Signature: env.Signature,
   208  	}}
   209  	err = instPol.Evaluate(sd)
   210  	if err != nil {
   211  		return fmt.Errorf("chaincode instantiation policy violated, error %s", err)
   212  	}
   213  	return nil
   214  }
   215  
   216  func (vscc *ValidatorOneValidSignature) ValidateLSCCInvocation(stub shim.ChaincodeStubInterface, chid string, env *common.Envelope, cap *pb.ChaincodeActionPayload, payl *common.Payload) error {
   217  	cpp, err := utils.GetChaincodeProposalPayload(cap.ChaincodeProposalPayload)
   218  	if err != nil {
   219  		logger.Errorf("VSCC error: GetChaincodeProposalPayload failed, err %s", err)
   220  		return err
   221  	}
   222  
   223  	cis := &pb.ChaincodeInvocationSpec{}
   224  	err = proto.Unmarshal(cpp.Input, cis)
   225  	if err != nil {
   226  		logger.Errorf("VSCC error: Unmarshal ChaincodeInvocationSpec failed, err %s", err)
   227  		return err
   228  	}
   229  
   230  	if cis.ChaincodeSpec == nil ||
   231  		cis.ChaincodeSpec.Input == nil ||
   232  		cis.ChaincodeSpec.Input.Args == nil {
   233  		logger.Errorf("VSCC error: committing invalid vscc invocation")
   234  		return fmt.Errorf("VSCC error: committing invalid vscc invocation")
   235  	}
   236  
   237  	lsccFunc := string(cis.ChaincodeSpec.Input.Args[0])
   238  	lsccArgs := cis.ChaincodeSpec.Input.Args[1:]
   239  
   240  	logger.Debugf("VSCC info: ValidateLSCCInvocation acting on %s %#v", lsccFunc, lsccArgs)
   241  
   242  	switch lsccFunc {
   243  	case lscc.UPGRADE, lscc.DEPLOY:
   244  		logger.Debugf("VSCC info: validating invocation of lscc function %s on arguments %#v", lsccFunc, lsccArgs)
   245  
   246  		if len(lsccArgs) < 2 || len(lsccArgs) > 5 {
   247  			return fmt.Errorf("Wrong number of arguments for invocation lscc(%s): expected between 2 and 5, received %d", lsccFunc, len(lsccArgs))
   248  		}
   249  
   250  		cdsArgs, err := utils.GetChaincodeDeploymentSpec(lsccArgs[1])
   251  		if err != nil {
   252  			return fmt.Errorf("GetChaincodeDeploymentSpec error %s", err)
   253  		}
   254  
   255  		if cdsArgs == nil || cdsArgs.ChaincodeSpec == nil || cdsArgs.ChaincodeSpec.ChaincodeId == nil ||
   256  			cap.Action == nil || cap.Action.ProposalResponsePayload == nil {
   257  			return fmt.Errorf("VSCC error: invocation of lscc(%s) does not have appropriate arguments", lsccFunc)
   258  		}
   259  
   260  		// get the rwset
   261  		pRespPayload, err := utils.GetProposalResponsePayload(cap.Action.ProposalResponsePayload)
   262  		if err != nil {
   263  			return fmt.Errorf("GetProposalResponsePayload error %s", err)
   264  		}
   265  		if pRespPayload.Extension == nil {
   266  			return fmt.Errorf("nil pRespPayload.Extension")
   267  		}
   268  		respPayload, err := utils.GetChaincodeAction(pRespPayload.Extension)
   269  		if err != nil {
   270  			return fmt.Errorf("GetChaincodeAction error %s", err)
   271  		}
   272  		txRWSet := &rwsetutil.TxRwSet{}
   273  		if err = txRWSet.FromProtoBytes(respPayload.Results); err != nil {
   274  			return fmt.Errorf("txRWSet.FromProtoBytes error %s", err)
   275  		}
   276  
   277  		// extract the rwset for lscc
   278  		var lsccrwset *kvrwset.KVRWSet
   279  		for _, ns := range txRWSet.NsRwSets {
   280  			logger.Debugf("Namespace %s", ns.NameSpace)
   281  			if ns.NameSpace == "lscc" {
   282  				lsccrwset = ns.KvRwSet
   283  				break
   284  			}
   285  		}
   286  
   287  		// retrieve from the ledger the entry for the chaincode at hand
   288  		cdLedger, ccExistsOnLedger, err := vscc.getInstantiatedCC(chid, cdsArgs.ChaincodeSpec.ChaincodeId.Name)
   289  		if err != nil {
   290  			return err
   291  		}
   292  
   293  		/******************************************/
   294  		/* security check 0 - validation of rwset */
   295  		/******************************************/
   296  		// there has to be one
   297  		if lsccrwset == nil {
   298  			return errors.New("No read write set for lscc was found")
   299  		}
   300  		// there can only be a single one
   301  		if len(lsccrwset.Writes) != 1 {
   302  			return errors.New("LSCC can only issue a single putState upon deploy/upgrade")
   303  		}
   304  		// the key name must be the chaincode id
   305  		if lsccrwset.Writes[0].Key != cdsArgs.ChaincodeSpec.ChaincodeId.Name {
   306  			return fmt.Errorf("Expected key %s, found %s", cdsArgs.ChaincodeSpec.ChaincodeId.Name, lsccrwset.Writes[0].Key)
   307  		}
   308  		// the value must be a ChaincodeData struct
   309  		cdRWSet := &ccprovider.ChaincodeData{}
   310  		err = proto.Unmarshal(lsccrwset.Writes[0].Value, cdRWSet)
   311  		if err != nil {
   312  			return fmt.Errorf("Unmarhsalling of ChaincodeData failed, error %s", err)
   313  		}
   314  		// the name must match
   315  		if cdRWSet.Name != cdsArgs.ChaincodeSpec.ChaincodeId.Name {
   316  			return fmt.Errorf("Expected cc name %s, found %s", cdsArgs.ChaincodeSpec.ChaincodeId.Name, cdRWSet.Name)
   317  		}
   318  		// the version must match
   319  		if cdRWSet.Version != cdsArgs.ChaincodeSpec.ChaincodeId.Version {
   320  			return fmt.Errorf("Expected cc version %s, found %s", cdsArgs.ChaincodeSpec.ChaincodeId.Version, cdRWSet.Version)
   321  		}
   322  		// it must only write to 2 namespaces: LSCC's and the cc that we are deploying/upgrading
   323  		for _, ns := range txRWSet.NsRwSets {
   324  			if ns.NameSpace != "lscc" && ns.NameSpace != cdRWSet.Name && len(ns.KvRwSet.Writes) > 0 {
   325  				return fmt.Errorf("LSCC invocation is attempting to write to namespace %s", ns.NameSpace)
   326  			}
   327  		}
   328  
   329  		logger.Debugf("Validating %s for cc %s version %s", lsccFunc, cdRWSet.Name, cdRWSet.Version)
   330  
   331  		switch lsccFunc {
   332  		case lscc.DEPLOY:
   333  			/*****************************************************/
   334  			/* security check 1 - check the instantiation policy */
   335  			/*****************************************************/
   336  			pol := cdRWSet.InstantiationPolicy
   337  			if pol == nil {
   338  				return fmt.Errorf("No installation policy was specified")
   339  			}
   340  			// FIXME: could we actually pull the cds package from the
   341  			// file system to verify whether the policy that is specified
   342  			// here is the same as the one on disk?
   343  			// PROS: we prevent attacks where the policy is replaced
   344  			// CONS: this would be a point of non-determinism
   345  			err = vscc.checkInstantiationPolicy(chid, env, pol, payl)
   346  			if err != nil {
   347  				return err
   348  			}
   349  
   350  			/******************************************************************/
   351  			/* security check 2 - cc not in the LCCC table of instantiated cc */
   352  			/******************************************************************/
   353  			if ccExistsOnLedger {
   354  				return fmt.Errorf("Chaincode %s is already instantiated", cdsArgs.ChaincodeSpec.ChaincodeId.Name)
   355  			}
   356  
   357  		case lscc.UPGRADE:
   358  			/**************************************************************/
   359  			/* security check 1 - cc in the LCCC table of instantiated cc */
   360  			/**************************************************************/
   361  			if !ccExistsOnLedger {
   362  				return fmt.Errorf("Upgrading non-existent chaincode %s", cdsArgs.ChaincodeSpec.ChaincodeId.Name)
   363  			}
   364  
   365  			/*****************************************************/
   366  			/* security check 2 - check the instantiation policy */
   367  			/*****************************************************/
   368  			pol := cdLedger.InstantiationPolicy
   369  			if pol == nil {
   370  				return fmt.Errorf("No installation policy was specified")
   371  			}
   372  			// FIXME: could we actually pull the cds package from the
   373  			// file system to verify whether the policy that is specified
   374  			// here is the same as the one on disk?
   375  			// PROS: we prevent attacks where the policy is replaced
   376  			// CONS: this would be a point of non-determinism
   377  			err = vscc.checkInstantiationPolicy(chid, env, pol, payl)
   378  			if err != nil {
   379  				return err
   380  			}
   381  
   382  			/**********************************************************/
   383  			/* security check 3 - existing cc's version was different */
   384  			/**********************************************************/
   385  			if cdLedger.Version == cdsArgs.ChaincodeSpec.ChaincodeId.Version {
   386  				return fmt.Errorf("Existing version of the cc on the ledger (%s) should be different from the upgraded one", cdsArgs.ChaincodeSpec.ChaincodeId.Version)
   387  			}
   388  		}
   389  
   390  		// all is good!
   391  		return nil
   392  	default:
   393  		return fmt.Errorf("VSCC error: committing an invocation of function %s of lscc is invalid", lsccFunc)
   394  	}
   395  }
   396  
   397  func (vscc *ValidatorOneValidSignature) getInstantiatedCC(chid, ccid string) (cd *ccprovider.ChaincodeData, exists bool, err error) {
   398  	qe, err := vscc.sccprovider.GetQueryExecutorForLedger(chid)
   399  	if err != nil {
   400  		err = fmt.Errorf("Could not retrieve QueryExecutor for channel %s, error %s", chid, err)
   401  		return
   402  	}
   403  	defer qe.Done()
   404  
   405  	bytes, err := qe.GetState("lscc", ccid)
   406  	if err != nil {
   407  		err = fmt.Errorf("Could not retrieve state for chaincode %s on channel %s, error %s", ccid, chid, err)
   408  		return
   409  	}
   410  
   411  	if bytes == nil {
   412  		return
   413  	}
   414  
   415  	cd = &ccprovider.ChaincodeData{}
   416  	err = proto.Unmarshal(bytes, cd)
   417  	if err != nil {
   418  		err = fmt.Errorf("Unmarshalling ChaincodeQueryResponse failed, error %s", err)
   419  		return
   420  	}
   421  
   422  	exists = true
   423  	return
   424  }
   425  
   426  func (vscc *ValidatorOneValidSignature) deduplicateIdentity(cap *pb.ChaincodeActionPayload) ([]*common.SignedData, error) {
   427  	// this is the first part of the signed message
   428  	prespBytes := cap.Action.ProposalResponsePayload
   429  
   430  	// build the signature set for the evaluation
   431  	signatureSet := []*common.SignedData{}
   432  	signatureMap := make(map[string]struct{})
   433  	// loop through each of the endorsements and build the signature set
   434  	for _, endorsement := range cap.Action.Endorsements {
   435  		//unmarshal endorser bytes
   436  		serializedIdentity := &msp.SerializedIdentity{}
   437  		if err := proto.Unmarshal(endorsement.Endorser, serializedIdentity); err != nil {
   438  			logger.Errorf("Unmarshal endorser error: %s", err)
   439  			return nil, fmt.Errorf("Unmarshal endorser error: %s", err)
   440  		}
   441  		identity := serializedIdentity.Mspid + string(serializedIdentity.IdBytes)
   442  		if _, ok := signatureMap[identity]; ok {
   443  			// Endorsement with the same identity has already been added
   444  			logger.Warningf("Ignoring duplicated identity, Mspid: %s, pem:\n%s", serializedIdentity.Mspid, serializedIdentity.IdBytes)
   445  			continue
   446  		}
   447  		signatureSet = append(signatureSet, &common.SignedData{
   448  			// set the data that is signed; concatenation of proposal response bytes and endorser ID
   449  			Data: append(prespBytes, endorsement.Endorser...),
   450  			// set the identity that signs the message: it's the endorser
   451  			Identity: endorsement.Endorser,
   452  			// set the signature
   453  			Signature: endorsement.Signature})
   454  		signatureMap[identity] = struct{}{}
   455  	}
   456  
   457  	logger.Debugf("Signature set is of size %d out of %d endorsement(s)", len(signatureSet), len(cap.Action.Endorsements))
   458  	return signatureSet, nil
   459  }