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