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 }