github.com/fnagchunpeng/fabric@v2.1.1+incompatible/core/endorser/msgvalidation.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package endorser 8 9 import ( 10 "crypto/sha256" 11 12 "github.com/golang/protobuf/proto" 13 "github.com/hyperledger/fabric-protos-go/common" 14 cb "github.com/hyperledger/fabric-protos-go/common" 15 pb "github.com/hyperledger/fabric-protos-go/peer" 16 "github.com/hyperledger/fabric/core/common/ccprovider" 17 "github.com/hyperledger/fabric/msp" 18 "github.com/hyperledger/fabric/protoutil" 19 "github.com/pkg/errors" 20 ) 21 22 // UnpackedProposal contains the interesting artifacts from inside the proposal. 23 type UnpackedProposal struct { 24 ChaincodeName string 25 ChannelHeader *cb.ChannelHeader 26 Input *pb.ChaincodeInput 27 Proposal *pb.Proposal 28 SignatureHeader *cb.SignatureHeader 29 SignedProposal *pb.SignedProposal 30 ProposalHash []byte 31 } 32 33 func (up *UnpackedProposal) ChannelID() string { 34 return up.ChannelHeader.ChannelId 35 } 36 37 func (up *UnpackedProposal) TxID() string { 38 return up.ChannelHeader.TxId 39 } 40 41 // UnpackProposal creates an an *UnpackedProposal which is guaranteed to have 42 // no zero-ed fields or it returns an error. 43 func UnpackProposal(signedProp *pb.SignedProposal) (*UnpackedProposal, error) { 44 prop, err := protoutil.UnmarshalProposal(signedProp.ProposalBytes) 45 if err != nil { 46 return nil, err 47 } 48 49 hdr, err := protoutil.UnmarshalHeader(prop.Header) 50 if err != nil { 51 return nil, err 52 } 53 54 chdr, err := protoutil.UnmarshalChannelHeader(hdr.ChannelHeader) 55 if err != nil { 56 return nil, err 57 } 58 59 shdr, err := protoutil.UnmarshalSignatureHeader(hdr.SignatureHeader) 60 if err != nil { 61 return nil, err 62 } 63 64 chaincodeHdrExt, err := protoutil.UnmarshalChaincodeHeaderExtension(chdr.Extension) 65 if err != nil { 66 return nil, err 67 } 68 69 if chaincodeHdrExt.ChaincodeId == nil { 70 return nil, errors.Errorf("ChaincodeHeaderExtension.ChaincodeId is nil") 71 } 72 73 if chaincodeHdrExt.ChaincodeId.Name == "" { 74 return nil, errors.Errorf("ChaincodeHeaderExtension.ChaincodeId.Name is empty") 75 } 76 77 cpp, err := protoutil.UnmarshalChaincodeProposalPayload(prop.Payload) 78 if err != nil { 79 return nil, err 80 } 81 82 cis, err := protoutil.UnmarshalChaincodeInvocationSpec(cpp.Input) 83 if err != nil { 84 return nil, err 85 } 86 87 if cis.ChaincodeSpec == nil { 88 return nil, errors.Errorf("chaincode invocation spec did not contain chaincode spec") 89 } 90 91 if cis.ChaincodeSpec.Input == nil { 92 return nil, errors.Errorf("chaincode input did not contain any input") 93 } 94 95 cppNoTransient := &pb.ChaincodeProposalPayload{Input: cpp.Input, TransientMap: nil} 96 ppBytes, err := proto.Marshal(cppNoTransient) 97 if err != nil { 98 return nil, errors.WithMessage(err, "could not marshal non-transient portion of payload") 99 } 100 101 // TODO, this was preserved from the proputils stuff, but should this be BCCSP? 102 103 // The proposal hash is the hash of the concatenation of: 104 // 1) The serialized Channel Header object 105 // 2) The serialized Signature Header object 106 // 3) The hash of the part of the chaincode proposal payload that will go to the tx 107 // (ie, the parts without the transient data) 108 propHash := sha256.New() 109 propHash.Write(hdr.ChannelHeader) 110 propHash.Write(hdr.SignatureHeader) 111 propHash.Write(ppBytes) 112 113 return &UnpackedProposal{ 114 SignedProposal: signedProp, 115 Proposal: prop, 116 ChannelHeader: chdr, 117 SignatureHeader: shdr, 118 ChaincodeName: chaincodeHdrExt.ChaincodeId.Name, 119 Input: cis.ChaincodeSpec.Input, 120 ProposalHash: propHash.Sum(nil)[:], 121 }, nil 122 } 123 124 func (up *UnpackedProposal) Validate(idDeserializer msp.IdentityDeserializer) error { 125 logger := decorateLogger(endorserLogger, &ccprovider.TransactionParams{ 126 ChannelID: up.ChannelHeader.ChannelId, 127 TxID: up.TxID(), 128 }) 129 130 // validate the header type 131 switch common.HeaderType(up.ChannelHeader.Type) { 132 case common.HeaderType_ENDORSER_TRANSACTION: 133 case common.HeaderType_CONFIG: 134 // The CONFIG transaction type has _no_ business coming to the propose API. 135 // In fact, anything coming to the Propose API is by definition an endorser 136 // transaction, so any other header type seems like it ought to be an error... oh well. 137 138 default: 139 return errors.Errorf("invalid header type %s", common.HeaderType(up.ChannelHeader.Type)) 140 } 141 142 // ensure the epoch is 0 143 if up.ChannelHeader.Epoch != 0 { 144 return errors.Errorf("epoch is non-zero") 145 } 146 147 // ensure that there is a nonce 148 if len(up.SignatureHeader.Nonce) == 0 { 149 return errors.Errorf("nonce is empty") 150 } 151 152 // ensure that there is a creator 153 if len(up.SignatureHeader.Creator) == 0 { 154 return errors.New("creator is empty") 155 } 156 157 expectedTxID := protoutil.ComputeTxID(up.SignatureHeader.Nonce, up.SignatureHeader.Creator) 158 if up.TxID() != expectedTxID { 159 return errors.Errorf("incorrectly computed txid '%s' -- expected '%s'", up.TxID(), expectedTxID) 160 } 161 162 if up.SignedProposal.ProposalBytes == nil { 163 return errors.Errorf("empty proposal bytes") 164 } 165 166 if up.SignedProposal.Signature == nil { 167 return errors.Errorf("empty signature bytes") 168 } 169 170 sId, err := protoutil.UnmarshalSerializedIdentity(up.SignatureHeader.Creator) 171 if err != nil { 172 return errors.Errorf("access denied: channel [%s] creator org unknown, creator is malformed", up.ChannelID()) 173 } 174 175 genericAuthError := errors.Errorf("access denied: channel [%s] creator org [%s]", up.ChannelID(), sId.Mspid) 176 177 // get the identity of the creator 178 creator, err := idDeserializer.DeserializeIdentity(up.SignatureHeader.Creator) 179 if err != nil { 180 logger.Warningf("access denied: channel %s", err) 181 return genericAuthError 182 } 183 184 // ensure that creator is a valid certificate 185 err = creator.Validate() 186 if err != nil { 187 logger.Warningf("access denied: identity is not valid: %s", err) 188 return genericAuthError 189 } 190 191 logger = logger.With("mspID", creator.GetMSPIdentifier()) 192 193 logger.Debug("creator is valid") 194 195 // validate the signature 196 err = creator.Verify(up.SignedProposal.ProposalBytes, up.SignedProposal.Signature) 197 if err != nil { 198 logger.Warningf("access denied: creator's signature over the proposal is not valid: %s", err) 199 return genericAuthError 200 } 201 202 logger.Debug("signature is valid") 203 204 return nil 205 }