Skip to content

Commit 5f98d54

Browse files
committed
Integration of MSP in endorser
This code change integrates the MSP into the peer, endorser and ESCC. Currently the MSP is used to validate the proposal (in the endorser) and endorse it (in the ESCC). Also, the peer CLI uses the MSP to sign the proposal to be sent to the endorser. Two MSP implementations are available: a no-op and one that uses BCCSP (and golang native x509 support) to handle x509 certificates. Missing: * loading the roots of trust from the genesis block using a CSCC call; * loading more than one identity; * specifying the location of the config file in the .yaml config; * a way of identifying signing identities for the CLI; NOTE: this commit also contains some code from - https://gerrit.hyperledger.org/r/#/c/2243/ - https://gerrit.hyperledger.org/r/#/c/2385/ - https://gerrit.hyperledger.org/r/#/c/2465/ Change-Id: I831b7bd01eb8b71cd3a6d9893a4faf6f7fa90fed Signed-off-by: Alessandro Sorniotti <[email protected]> Signed-off-by: Elli Androulaki <[email protected]>
1 parent af5285a commit 5f98d54

27 files changed

+1722
-71
lines changed

bddtests/endorser_test.go

+10-1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
"github.com/DATA-DOG/godog/gherkin"
2828
"github.com/hyperledger/fabric/core/util"
2929
pb "github.com/hyperledger/fabric/protos/peer"
30+
"github.com/hyperledger/fabric/protos/utils"
3031
"golang.org/x/net/context"
3132
"google.golang.org/grpc"
3233
)
@@ -243,8 +244,16 @@ func (b *BDDContext) userSendsProposalToEndorsersWithTimeoutOfSeconds(enrollID,
243244
}
244245
defer grpcClient.Close()
245246

247+
proposalBytes, err := utils.GetBytesProposal(proposal)
248+
if err != nil {
249+
respQueue <- &KeyedProposalResponse{endorser, nil, fmt.Errorf("Error serializing proposal bytes")}
250+
return
251+
}
252+
// FIXME: the endorser needs to be given a signed proposal - who should sign?
253+
signedProposal := &pb.SignedProposal{ProposalBytes: proposalBytes, Signature: []byte("signature")}
254+
246255
endorserClient := pb.NewEndorserClient(grpcClient)
247-
if proposalResponse, localErr = endorserClient.ProcessProposal(ctx, proposal); localErr != nil {
256+
if proposalResponse, localErr = endorserClient.ProcessProposal(ctx, signedProposal); localErr != nil {
248257
respQueue <- &KeyedProposalResponse{endorser, nil, fmt.Errorf("Error calling endorser '%s': %s", endorser, localErr)}
249258
return
250259
}

core/chaincode/importsysccs.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ var systemChaincodes = []*SystemChaincode{
3535
Enabled: true,
3636
Name: "escc",
3737
Path: "github.com/hyperledger/fabric/core/system_chaincode/escc",
38-
InitArgs: [][]byte{[]byte("")},
38+
InitArgs: [][]byte{[]byte("DEFAULT"), []byte("PEER")}, // TODO: retrieve these aruments properly
3939
Chaincode: &escc.EndorserOneValidSignature{},
4040
},
4141
{

core/crypto/bccsp/sw/ecdsakey.go

+10
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ type ecdsaPrivateKey struct {
2828
k *ecdsa.PrivateKey
2929
}
3030

31+
// FIXME: remove as soon as there's a way to import the key more properly
32+
func NewEcdsaPrivateKey(k *ecdsa.PrivateKey) bccsp.Key {
33+
return &ecdsaPrivateKey{k: k}
34+
}
35+
3136
// Bytes converts this key to its byte representation,
3237
// if this operation is allowed.
3338
func (k *ecdsaPrivateKey) Bytes() (raw []byte, err error) {
@@ -64,6 +69,11 @@ type ecdsaPublicKey struct {
6469
k *ecdsa.PublicKey
6570
}
6671

72+
// FIXME: remove as soon as there's a way to import the key more properly
73+
func NewEcdsaPublicKey(k *ecdsa.PublicKey) bccsp.Key {
74+
return &ecdsaPublicKey{k: k}
75+
}
76+
6777
// Bytes converts this key to its byte representation,
6878
// if this operation is allowed.
6979
func (k *ecdsaPublicKey) Bytes() (raw []byte, err error) {

core/endorser/endorser.go

+55-31
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,13 @@ import (
2727
"github.com/hyperledger/fabric/core/ledger"
2828
"github.com/hyperledger/fabric/core/ledger/kvledger"
2929
"github.com/hyperledger/fabric/core/peer"
30+
"github.com/hyperledger/fabric/msp"
3031
"github.com/hyperledger/fabric/protos/common"
3132
pb "github.com/hyperledger/fabric/protos/peer"
3233
putils "github.com/hyperledger/fabric/protos/utils"
3334
)
3435

35-
var devopsLogger = logging.MustGetLogger("endorser")
36+
var endorserLogger = logging.MustGetLogger("endorser")
3637

3738
// The Jira issue that documents Endorser flow along with its relationship to
3839
// the lifecycle chaincode - https://jira.hyperledger.org/browse/FAB-181
@@ -175,7 +176,7 @@ func (e *Endorser) getCDSFromLCCC(ctx context.Context, chaincodeID string, txsim
175176

176177
//endorse the proposal by calling the ESCC
177178
func (e *Endorser) endorseProposal(ctx context.Context, proposal *pb.Proposal, simRes []byte, event *pb.ChaincodeEvent, visibility []byte, ccid *pb.ChaincodeID, txsim ledger.TxSimulator) ([]byte, error) {
178-
devopsLogger.Infof("endorseProposal starts for proposal %p, simRes %p event %p, visibility %p, ccid %s", proposal, simRes, event, visibility, ccid)
179+
endorserLogger.Infof("endorseProposal starts for proposal %p, simRes %p event %p, visibility %p, ccid %s", proposal, simRes, event, visibility, ccid)
179180

180181
// 1) extract the chaincodeDeploymentSpec for the chaincode we are invoking; we need it to get the escc
181182
var escc string
@@ -197,7 +198,7 @@ func (e *Endorser) endorseProposal(ctx context.Context, proposal *pb.Proposal, s
197198
escc = "escc"
198199
}
199200

200-
devopsLogger.Infof("endorseProposal info: escc for cid %s is %s", ccid, escc)
201+
endorserLogger.Infof("endorseProposal info: escc for cid %s is %s", ccid, escc)
201202

202203
// marshalling event bytes
203204
var err error
@@ -239,15 +240,15 @@ func (e *Endorser) endorseProposal(ctx context.Context, proposal *pb.Proposal, s
239240
// FIXME: this method might be of general interest, should we package it somewhere else?
240241
// validateChaincodeProposalMessage checks the validity of a CHAINCODE Proposal message
241242
func (e *Endorser) validateChaincodeProposalMessage(prop *pb.Proposal, hdr *common.Header) (*pb.ChaincodeHeaderExtension, error) {
242-
devopsLogger.Infof("validateChaincodeProposalMessage starts for proposal %p, header %p", prop, hdr)
243+
endorserLogger.Infof("validateChaincodeProposalMessage starts for proposal %p, header %p", prop, hdr)
243244

244245
// 4) based on the header type (assuming it's CHAINCODE), look at the extensions
245246
chaincodeHdrExt, err := putils.GetChaincodeHeaderExtension(hdr)
246247
if err != nil {
247248
return nil, fmt.Errorf("Invalid header extension for type CHAINCODE")
248249
}
249250

250-
devopsLogger.Infof("validateChaincodeProposalMessage info: header extension references chaincode %s", chaincodeHdrExt.ChaincodeID)
251+
endorserLogger.Infof("validateChaincodeProposalMessage info: header extension references chaincode %s", chaincodeHdrExt.ChaincodeID)
251252

252253
// - ensure that the chaincodeID is correct (?)
253254
// TODO: should we even do this? If so, using which interface?
@@ -264,65 +265,88 @@ func (e *Endorser) validateChaincodeProposalMessage(prop *pb.Proposal, hdr *comm
264265
// validateProposalMessage checks the validity of a generic Proposal message
265266
// this function returns Header and ChaincodeHeaderExtension messages since they
266267
// have been unmarshalled and validated
267-
func (e *Endorser) validateProposalMessage(prop *pb.Proposal) (*common.Header, *pb.ChaincodeHeaderExtension, error) {
268-
devopsLogger.Infof("validateProposalMessage starts for proposal %p", prop)
268+
func (e *Endorser) validateProposalMessage(signedProp *pb.SignedProposal) (*pb.Proposal, *common.Header, *pb.ChaincodeHeaderExtension, error) {
269+
endorserLogger.Infof("validateProposalMessage starts for signed proposal %p", signedProp)
270+
271+
// extract the Proposal message from signedProp
272+
prop, err := putils.GetProposal(signedProp.ProposalBytes)
273+
if err != nil {
274+
return nil, nil, nil, err
275+
}
269276

270277
// 1) look at the ProposalHeader
271278
hdr, err := putils.GetHeader(prop)
272279
if err != nil {
273-
return nil, nil, err
280+
return nil, nil, nil, err
274281
}
275282

276-
// - validate the type
277-
if hdr.ChainHeader.Type != int32(common.HeaderType_ENDORSER_TRANSACTION) {
278-
return nil, nil, fmt.Errorf("Invalid proposal type %d", hdr.ChainHeader.Type)
279-
}
283+
// TODO: validate the type
284+
285+
endorserLogger.Infof("validateProposalMessage info: proposal type %d", hdr.ChainHeader.Type)
280286

281-
devopsLogger.Infof("validateProposalMessage info: proposal type %d", hdr.ChainHeader.Type)
287+
// - ensure that the version is what we expect
288+
// TODO: Which is the right version?
289+
290+
// - ensure that the chainID is valid
291+
// TODO: which set of APIs is supposed to give us this info?
282292

283-
// - ensure that there is a nonce and a creator
293+
// ensure that there is a nonce and a creator
284294
if hdr.SignatureHeader.Nonce == nil || len(hdr.SignatureHeader.Nonce) == 0 {
285-
return nil, nil, fmt.Errorf("Invalid nonce specified in the header")
295+
return nil, nil, nil, fmt.Errorf("Invalid nonce specified in the header")
286296
}
287297
if hdr.SignatureHeader.Creator == nil || len(hdr.SignatureHeader.Creator) == 0 {
288-
return nil, nil, fmt.Errorf("Invalid creator specified in the header")
298+
return nil, nil, nil, fmt.Errorf("Invalid creator specified in the header")
289299
}
290300

291-
// - ensure that creator is a valid certificate (depends on membership svc)
292-
// TODO: We need MSP APIs for this
301+
// get the identity of the creator
302+
creator, err := msp.GetManager().DeserializeIdentity(hdr.SignatureHeader.Creator)
303+
if err != nil {
304+
return nil, nil, nil, fmt.Errorf("Failed to deserialize creator identity, err %s", err)
305+
}
306+
307+
// ensure that creator is a valid certificate
308+
valid, err := creator.Validate()
309+
if err != nil {
310+
return nil, nil, nil, fmt.Errorf("Could not determine whether the identity is valid, err %s", err)
311+
} else if !valid {
312+
return nil, nil, nil, fmt.Errorf("The creator certificate is not valid, aborting")
313+
}
314+
315+
// get the identifier and log info on the creator
316+
identifier := creator.Identifier()
317+
endorserLogger.Infof("validateProposalMessage info: creator identity is %s", identifier)
293318

294319
// - ensure that creator is trusted (signed by a trusted CA)
295320
// TODO: We need MSP APIs for this
296321

297322
// - ensure that creator can transact with us (some ACLs?)
298323
// TODO: which set of APIs is supposed to give us this info?
299324

300-
// - ensure that the version is what we expect
301-
// TODO: Which is the right version?
302-
303-
// - ensure that the chainID is valid
304-
// TODO: which set of APIs is supposed to give us this info?
325+
// 2) validate the signature of creator on header and payload
326+
verified, err := creator.Verify(signedProp.ProposalBytes, signedProp.Signature)
327+
if err != nil {
328+
return nil, nil, nil, fmt.Errorf("Could not determine whether the signature is valid, err %s", err)
329+
} else if !verified {
330+
return nil, nil, nil, fmt.Errorf("The creator's signature over the proposal is not valid, aborting")
331+
}
305332

306-
// 2) perform a check against replay attacks
333+
// 3) perform a check against replay attacks
307334
// TODO
308335

309-
// 3) validate the signature of creator on header and payload
310-
// TODO: We need MSP APIs for this
311-
312336
// validation of the proposal message knowing it's of type CHAINCODE
313337
chaincodeHdrExt, err := e.validateChaincodeProposalMessage(prop, hdr)
314338
if err != nil {
315-
return nil, nil, err
339+
return nil, nil, nil, err
316340
}
317341

318-
return hdr, chaincodeHdrExt, err
342+
return prop, hdr, chaincodeHdrExt, err
319343
}
320344

321345
// ProcessProposal process the Proposal
322-
func (e *Endorser) ProcessProposal(ctx context.Context, prop *pb.Proposal) (*pb.ProposalResponse, error) {
346+
func (e *Endorser) ProcessProposal(ctx context.Context, signedProp *pb.SignedProposal) (*pb.ProposalResponse, error) {
323347
// at first, we check whether the message is valid
324348
// TODO: Do the checks performed by this function belong here or in the ESCC? From a security standpoint they should be performed as early as possible so here seems to be a good place
325-
_, hdrExt, err := e.validateProposalMessage(prop)
349+
prop, _, hdrExt, err := e.validateProposalMessage(signedProp)
326350
if err != nil {
327351
return &pb.ProposalResponse{Response: &pb.Response2{Status: 500, Message: err.Error()}}, err
328352
}

core/endorser/endorser_test.go

+62-8
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import (
3434
"github.com/hyperledger/fabric/core/ledger/kvledger"
3535
"github.com/hyperledger/fabric/core/peer"
3636
"github.com/hyperledger/fabric/core/util"
37+
"github.com/hyperledger/fabric/msp"
3738
pb "github.com/hyperledger/fabric/protos/peer"
3839
pbutils "github.com/hyperledger/fabric/protos/utils"
3940
"github.com/spf13/viper"
@@ -44,6 +45,8 @@ import (
4445

4546
var testDBWrapper = db.NewTestDBWrapper()
4647
var endorserServer pb.EndorserServer
48+
var mspInstance msp.PeerMSP
49+
var signer msp.SigningIdentity
4750

4851
//initialize peer and start up. If security==enabled, login as vp
4952
func initPeer() (net.Listener, error) {
@@ -120,13 +123,13 @@ func closeListenerAndSleep(l net.Listener) {
120123

121124
//getProposal gets the proposal for the chaincode invocation
122125
//Currently supported only for Invokes (Queries still go through devops client)
123-
func getProposal(cis *pb.ChaincodeInvocationSpec) (*pb.Proposal, error) {
124-
return pbutils.CreateChaincodeProposal(cis, []byte("cert"))
126+
func getProposal(cis *pb.ChaincodeInvocationSpec, creator []byte) (*pb.Proposal, error) {
127+
return pbutils.CreateChaincodeProposal(cis, creator)
125128
}
126129

127130
//getDeployProposal gets the proposal for the chaincode deployment
128131
//the payload is a ChaincodeDeploymentSpec
129-
func getDeployProposal(cds *pb.ChaincodeDeploymentSpec) (*pb.Proposal, error) {
132+
func getDeployProposal(cds *pb.ChaincodeDeploymentSpec, creator []byte) (*pb.Proposal, error) {
130133
b, err := proto.Marshal(cds)
131134
if err != nil {
132135
return nil, err
@@ -136,7 +139,21 @@ func getDeployProposal(cds *pb.ChaincodeDeploymentSpec) (*pb.Proposal, error) {
136139
lcccSpec := &pb.ChaincodeInvocationSpec{ChaincodeSpec: &pb.ChaincodeSpec{Type: pb.ChaincodeSpec_GOLANG, ChaincodeID: &pb.ChaincodeID{Name: "lccc"}, CtorMsg: &pb.ChaincodeInput{Args: [][]byte{[]byte("deploy"), []byte("default"), b}}}}
137140

138141
//...and get the proposal for it
139-
return getProposal(lcccSpec)
142+
return getProposal(lcccSpec, creator)
143+
}
144+
145+
func getSignedProposal(prop *pb.Proposal, signer msp.SigningIdentity) (*pb.SignedProposal, error) {
146+
propBytes, err := pbutils.GetBytesProposal(prop)
147+
if err != nil {
148+
return nil, err
149+
}
150+
151+
signature, err := signer.Sign(propBytes)
152+
if err != nil {
153+
return nil, err
154+
}
155+
156+
return &pb.SignedProposal{ProposalBytes: propBytes, Signature: signature}, nil
140157
}
141158

142159
func getDeploymentSpec(context context.Context, spec *pb.ChaincodeSpec) (*pb.ChaincodeDeploymentSpec, error) {
@@ -162,28 +179,50 @@ func deploy(endorserServer pb.EndorserServer, spec *pb.ChaincodeSpec, f func(*pb
162179
f(depSpec)
163180
}
164181

182+
creator, err := signer.Serialize()
183+
if err != nil {
184+
return nil, err
185+
}
186+
165187
var prop *pb.Proposal
166-
prop, err = getDeployProposal(depSpec)
188+
prop, err = getDeployProposal(depSpec, creator)
189+
if err != nil {
190+
return nil, err
191+
}
192+
193+
var signedProp *pb.SignedProposal
194+
signedProp, err = getSignedProposal(prop, signer)
167195
if err != nil {
168196
return nil, err
169197
}
170198

171199
var resp *pb.ProposalResponse
172-
resp, err = endorserServer.ProcessProposal(context.Background(), prop)
200+
resp, err = endorserServer.ProcessProposal(context.Background(), signedProp)
173201

174202
return resp, err
175203
}
176204

177205
func invoke(spec *pb.ChaincodeSpec) (*pb.ProposalResponse, error) {
178206
invocation := &pb.ChaincodeInvocationSpec{ChaincodeSpec: spec}
179207

208+
creator, err := signer.Serialize()
209+
if err != nil {
210+
return nil, err
211+
}
212+
180213
var prop *pb.Proposal
181-
prop, err := getProposal(invocation)
214+
prop, err = getProposal(invocation, creator)
182215
if err != nil {
183216
return nil, fmt.Errorf("Error creating proposal %s: %s\n", spec.ChaincodeID, err)
184217
}
185218

186-
resp, err := endorserServer.ProcessProposal(context.Background(), prop)
219+
var signedProp *pb.SignedProposal
220+
signedProp, err = getSignedProposal(prop, signer)
221+
if err != nil {
222+
return nil, err
223+
}
224+
225+
resp, err := endorserServer.ProcessProposal(context.Background(), signedProp)
187226
if err != nil {
188227
return nil, fmt.Errorf("Error endorsing %s: %s\n", spec.ChaincodeID, err)
189228
}
@@ -324,6 +363,21 @@ func TestMain(m *testing.M) {
324363
}
325364

326365
endorserServer = NewEndorserServer(nil)
366+
367+
// setup the MSP manager so that we can sign/verify
368+
mspMgrConfigFile := "../../msp/peer-config.json"
369+
msp.GetManager().Setup(mspMgrConfigFile)
370+
mspId := "DEFAULT"
371+
id := "PEER"
372+
signingIdentity := &msp.IdentityIdentifier{Mspid: msp.ProviderIdentifier{Value: mspId}, Value: id}
373+
signer, err = msp.GetManager().GetSigningIdentity(signingIdentity)
374+
if err != nil {
375+
os.Exit(-1)
376+
fmt.Printf("Could not initialize msp/signer")
377+
finitPeer(lis)
378+
return
379+
}
380+
327381
retVal := m.Run()
328382

329383
finitPeer(lis)

core/endorser/endorser_test.yaml

+2-1
Original file line numberDiff line numberDiff line change
@@ -138,13 +138,14 @@ logging:
138138
# info - Set default to INFO
139139
# warning:main,db=debug:chaincode=info - Override default WARNING in main,db,chaincode
140140
# chaincode=info:main=debug:db=debug:warning - Same as above
141+
msp: debug
141142
peer: debug
142143
crypto: info
143144
status: warning
144145
stop: warning
145146
login: warning
146147
vm: warning
147-
chaincode: warning
148+
chaincode: error
148149

149150

150151
###############################################################################

0 commit comments

Comments
 (0)