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