github.com/adnan-c/fabric_e2e_couchdb@v0.6.1-preview.0.20170228180935-21ce6b23cf91/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 mspmgmt "github.com/hyperledger/fabric/msp/mgmt" 25 "github.com/hyperledger/fabric/protos/common" 26 pb "github.com/hyperledger/fabric/protos/peer" 27 "github.com/hyperledger/fabric/protos/utils" 28 "github.com/op/go-logging" 29 ) 30 31 var putilsLogger = logging.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 if len(tx.Actions) == 0 { 290 return errors.New("At least one TransactionAction is required") 291 } 292 293 putilsLogger.Debugf("validateEndorserTransaction info: there are %d actions", len(tx.Actions)) 294 295 for _, act := range tx.Actions { 296 // check for nil argument 297 if act == nil { 298 return errors.New("Nil action") 299 } 300 301 // if the type is ENDORSER_TRANSACTION we unmarshal a SignatureHeader 302 sHdr, err := utils.GetSignatureHeader(act.Header) 303 if err != nil { 304 return err 305 } 306 307 // validate the SignatureHeader - here we actually only 308 // care about the nonce since the creator is in the outer header 309 err = validateSignatureHeader(sHdr) 310 if err != nil { 311 return err 312 } 313 314 putilsLogger.Debugf("validateEndorserTransaction info: signature header is valid") 315 316 // if the type is ENDORSER_TRANSACTION we unmarshal a ChaincodeActionPayload 317 ccActionPayload, err := utils.GetChaincodeActionPayload(act.Payload) 318 if err != nil { 319 return err 320 } 321 322 // extract the proposal response payload 323 prp, err := utils.GetProposalResponsePayload(ccActionPayload.Action.ProposalResponsePayload) 324 if err != nil { 325 return err 326 } 327 328 // build the original header by stitching together 329 // the common ChannelHeader and the per-action SignatureHeader 330 hdrOrig := &common.Header{ChannelHeader: hdr.ChannelHeader, SignatureHeader: act.Header} 331 332 // compute proposalHash 333 pHash, err := utils.GetProposalHash2(hdrOrig, ccActionPayload.ChaincodeProposalPayload) 334 if err != nil { 335 return err 336 } 337 338 // ensure that the proposal hash matches 339 if bytes.Compare(pHash, prp.ProposalHash) != 0 { 340 return errors.New("proposal hash does not match") 341 } 342 } 343 344 return nil 345 } 346 347 // ValidateTransaction checks that the transaction envelope is properly formed 348 func ValidateTransaction(e *common.Envelope) (*common.Payload, pb.TxValidationCode) { 349 putilsLogger.Debugf("ValidateTransactionEnvelope starts for envelope %p", e) 350 351 // check for nil argument 352 if e == nil { 353 putilsLogger.Errorf("Error: nil envelope") 354 return nil, pb.TxValidationCode_NIL_ENVELOPE 355 } 356 357 // get the payload from the envelope 358 payload, err := utils.GetPayload(e) 359 if err != nil { 360 putilsLogger.Errorf("GetPayload returns err %s", err) 361 return nil, pb.TxValidationCode_BAD_PAYLOAD 362 } 363 364 putilsLogger.Debugf("Header is %s", payload.Header) 365 366 // validate the header 367 chdr, shdr, err := validateCommonHeader(payload.Header) 368 if err != nil { 369 putilsLogger.Errorf("validateCommonHeader returns err %s", err) 370 return nil, pb.TxValidationCode_BAD_COMMON_HEADER 371 } 372 373 // validate the signature in the envelope 374 err = checkSignatureFromCreator(shdr.Creator, e.Signature, e.Payload, chdr.ChannelId) 375 if err != nil { 376 putilsLogger.Errorf("checkSignatureFromCreator returns err %s", err) 377 return nil, pb.TxValidationCode_BAD_CREATOR_SIGNATURE 378 } 379 380 // TODO: ensure that creator can transact with us (some ACLs?) which set of APIs is supposed to give us this info? 381 382 // continue the validation in a way that depends on the type specified in the header 383 switch common.HeaderType(chdr.Type) { 384 case common.HeaderType_ENDORSER_TRANSACTION: 385 // Verify that the transaction ID has been computed properly. 386 // This check is needed to ensure that the lookup into the ledger 387 // for the same TxID catches duplicates. 388 err = utils.CheckProposalTxID( 389 chdr.TxId, 390 shdr.Nonce, 391 shdr.Creator) 392 393 if err != nil { 394 putilsLogger.Errorf("CheckProposalTxID returns err %s", err) 395 return nil, pb.TxValidationCode_BAD_PROPOSAL_TXID 396 } 397 398 err = validateEndorserTransaction(payload.Data, payload.Header) 399 putilsLogger.Debugf("ValidateTransactionEnvelope returns err %s", err) 400 401 if err != nil { 402 putilsLogger.Errorf("validateEndorserTransaction returns err %s", err) 403 return payload, pb.TxValidationCode_INVALID_ENDORSER_TRANSACTION 404 } else { 405 return payload, pb.TxValidationCode_VALID 406 } 407 case common.HeaderType_CONFIG: 408 // Config transactions have signatures inside which will be validated, especially at genesis there may be no creator or 409 // signature on the outermost envelope 410 411 err = validateConfigTransaction(payload.Data, payload.Header) 412 413 if err != nil { 414 putilsLogger.Errorf("validateConfigTransaction returns err %s", err) 415 return payload, pb.TxValidationCode_INVALID_CONFIG_TRANSACTION 416 } else { 417 return payload, pb.TxValidationCode_VALID 418 } 419 default: 420 return nil, pb.TxValidationCode_UNSUPPORTED_TX_PAYLOAD 421 } 422 }