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 }