Skip to content

Commit

Permalink
Merge "Integration of MSP in endorser"
Browse files Browse the repository at this point in the history
  • Loading branch information
mastersingh24 authored and Gerrit Code Review committed Nov 17, 2016
2 parents 069fce8 + 5f98d54 commit 1f9ff4d
Show file tree
Hide file tree
Showing 27 changed files with 1,722 additions and 71 deletions.
11 changes: 10 additions & 1 deletion bddtests/endorser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"github.com/DATA-DOG/godog/gherkin"
"github.com/hyperledger/fabric/core/util"
pb "github.com/hyperledger/fabric/protos/peer"
"github.com/hyperledger/fabric/protos/utils"
"golang.org/x/net/context"
"google.golang.org/grpc"
)
Expand Down Expand Up @@ -243,8 +244,16 @@ func (b *BDDContext) userSendsProposalToEndorsersWithTimeoutOfSeconds(enrollID,
}
defer grpcClient.Close()

proposalBytes, err := utils.GetBytesProposal(proposal)
if err != nil {
respQueue <- &KeyedProposalResponse{endorser, nil, fmt.Errorf("Error serializing proposal bytes")}
return
}
// FIXME: the endorser needs to be given a signed proposal - who should sign?
signedProposal := &pb.SignedProposal{ProposalBytes: proposalBytes, Signature: []byte("signature")}

endorserClient := pb.NewEndorserClient(grpcClient)
if proposalResponse, localErr = endorserClient.ProcessProposal(ctx, proposal); localErr != nil {
if proposalResponse, localErr = endorserClient.ProcessProposal(ctx, signedProposal); localErr != nil {
respQueue <- &KeyedProposalResponse{endorser, nil, fmt.Errorf("Error calling endorser '%s': %s", endorser, localErr)}
return
}
Expand Down
2 changes: 1 addition & 1 deletion core/chaincode/importsysccs.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ var systemChaincodes = []*SystemChaincode{
Enabled: true,
Name: "escc",
Path: "github.com/hyperledger/fabric/core/system_chaincode/escc",
InitArgs: [][]byte{[]byte("")},
InitArgs: [][]byte{[]byte("DEFAULT"), []byte("PEER")}, // TODO: retrieve these aruments properly
Chaincode: &escc.EndorserOneValidSignature{},
},
{
Expand Down
10 changes: 10 additions & 0 deletions core/crypto/bccsp/sw/ecdsakey.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ type ecdsaPrivateKey struct {
k *ecdsa.PrivateKey
}

// FIXME: remove as soon as there's a way to import the key more properly
func NewEcdsaPrivateKey(k *ecdsa.PrivateKey) bccsp.Key {
return &ecdsaPrivateKey{k: k}
}

// Bytes converts this key to its byte representation,
// if this operation is allowed.
func (k *ecdsaPrivateKey) Bytes() (raw []byte, err error) {
Expand Down Expand Up @@ -64,6 +69,11 @@ type ecdsaPublicKey struct {
k *ecdsa.PublicKey
}

// FIXME: remove as soon as there's a way to import the key more properly
func NewEcdsaPublicKey(k *ecdsa.PublicKey) bccsp.Key {
return &ecdsaPublicKey{k: k}
}

// Bytes converts this key to its byte representation,
// if this operation is allowed.
func (k *ecdsaPublicKey) Bytes() (raw []byte, err error) {
Expand Down
86 changes: 55 additions & 31 deletions core/endorser/endorser.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,13 @@ import (
"github.com/hyperledger/fabric/core/ledger"
"github.com/hyperledger/fabric/core/ledger/kvledger"
"github.com/hyperledger/fabric/core/peer"
"github.com/hyperledger/fabric/msp"
"github.com/hyperledger/fabric/protos/common"
pb "github.com/hyperledger/fabric/protos/peer"
putils "github.com/hyperledger/fabric/protos/utils"
)

var devopsLogger = logging.MustGetLogger("endorser")
var endorserLogger = logging.MustGetLogger("endorser")

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

//endorse the proposal by calling the ESCC
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) {
devopsLogger.Infof("endorseProposal starts for proposal %p, simRes %p event %p, visibility %p, ccid %s", proposal, simRes, event, visibility, ccid)
endorserLogger.Infof("endorseProposal starts for proposal %p, simRes %p event %p, visibility %p, ccid %s", proposal, simRes, event, visibility, ccid)

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

devopsLogger.Infof("endorseProposal info: escc for cid %s is %s", ccid, escc)
endorserLogger.Infof("endorseProposal info: escc for cid %s is %s", ccid, escc)

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

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

devopsLogger.Infof("validateChaincodeProposalMessage info: header extension references chaincode %s", chaincodeHdrExt.ChaincodeID)
endorserLogger.Infof("validateChaincodeProposalMessage info: header extension references chaincode %s", chaincodeHdrExt.ChaincodeID)

// - ensure that the chaincodeID is correct (?)
// TODO: should we even do this? If so, using which interface?
Expand All @@ -264,65 +265,88 @@ func (e *Endorser) validateChaincodeProposalMessage(prop *pb.Proposal, hdr *comm
// validateProposalMessage checks the validity of a generic Proposal message
// this function returns Header and ChaincodeHeaderExtension messages since they
// have been unmarshalled and validated
func (e *Endorser) validateProposalMessage(prop *pb.Proposal) (*common.Header, *pb.ChaincodeHeaderExtension, error) {
devopsLogger.Infof("validateProposalMessage starts for proposal %p", prop)
func (e *Endorser) validateProposalMessage(signedProp *pb.SignedProposal) (*pb.Proposal, *common.Header, *pb.ChaincodeHeaderExtension, error) {
endorserLogger.Infof("validateProposalMessage starts for signed proposal %p", signedProp)

// extract the Proposal message from signedProp
prop, err := putils.GetProposal(signedProp.ProposalBytes)
if err != nil {
return nil, nil, nil, err
}

// 1) look at the ProposalHeader
hdr, err := putils.GetHeader(prop)
if err != nil {
return nil, nil, err
return nil, nil, nil, err
}

// - validate the type
if hdr.ChainHeader.Type != int32(common.HeaderType_ENDORSER_TRANSACTION) {
return nil, nil, fmt.Errorf("Invalid proposal type %d", hdr.ChainHeader.Type)
}
// TODO: validate the type

endorserLogger.Infof("validateProposalMessage info: proposal type %d", hdr.ChainHeader.Type)

devopsLogger.Infof("validateProposalMessage info: proposal type %d", hdr.ChainHeader.Type)
// - ensure that the version is what we expect
// TODO: Which is the right version?

// - ensure that the chainID is valid
// TODO: which set of APIs is supposed to give us this info?

// - ensure that there is a nonce and a creator
// ensure that there is a nonce and a creator
if hdr.SignatureHeader.Nonce == nil || len(hdr.SignatureHeader.Nonce) == 0 {
return nil, nil, fmt.Errorf("Invalid nonce specified in the header")
return nil, nil, nil, fmt.Errorf("Invalid nonce specified in the header")
}
if hdr.SignatureHeader.Creator == nil || len(hdr.SignatureHeader.Creator) == 0 {
return nil, nil, fmt.Errorf("Invalid creator specified in the header")
return nil, nil, nil, fmt.Errorf("Invalid creator specified in the header")
}

// - ensure that creator is a valid certificate (depends on membership svc)
// TODO: We need MSP APIs for this
// get the identity of the creator
creator, err := msp.GetManager().DeserializeIdentity(hdr.SignatureHeader.Creator)
if err != nil {
return nil, nil, nil, fmt.Errorf("Failed to deserialize creator identity, err %s", err)
}

// ensure that creator is a valid certificate
valid, err := creator.Validate()
if err != nil {
return nil, nil, nil, fmt.Errorf("Could not determine whether the identity is valid, err %s", err)
} else if !valid {
return nil, nil, nil, fmt.Errorf("The creator certificate is not valid, aborting")
}

// get the identifier and log info on the creator
identifier := creator.Identifier()
endorserLogger.Infof("validateProposalMessage info: creator identity is %s", identifier)

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

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

// - ensure that the version is what we expect
// TODO: Which is the right version?

// - ensure that the chainID is valid
// TODO: which set of APIs is supposed to give us this info?
// 2) validate the signature of creator on header and payload
verified, err := creator.Verify(signedProp.ProposalBytes, signedProp.Signature)
if err != nil {
return nil, nil, nil, fmt.Errorf("Could not determine whether the signature is valid, err %s", err)
} else if !verified {
return nil, nil, nil, fmt.Errorf("The creator's signature over the proposal is not valid, aborting")
}

// 2) perform a check against replay attacks
// 3) perform a check against replay attacks
// TODO

// 3) validate the signature of creator on header and payload
// TODO: We need MSP APIs for this

// validation of the proposal message knowing it's of type CHAINCODE
chaincodeHdrExt, err := e.validateChaincodeProposalMessage(prop, hdr)
if err != nil {
return nil, nil, err
return nil, nil, nil, err
}

return hdr, chaincodeHdrExt, err
return prop, hdr, chaincodeHdrExt, err
}

// ProcessProposal process the Proposal
func (e *Endorser) ProcessProposal(ctx context.Context, prop *pb.Proposal) (*pb.ProposalResponse, error) {
func (e *Endorser) ProcessProposal(ctx context.Context, signedProp *pb.SignedProposal) (*pb.ProposalResponse, error) {
// at first, we check whether the message is valid
// 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
_, hdrExt, err := e.validateProposalMessage(prop)
prop, _, hdrExt, err := e.validateProposalMessage(signedProp)
if err != nil {
return &pb.ProposalResponse{Response: &pb.Response2{Status: 500, Message: err.Error()}}, err
}
Expand Down
70 changes: 62 additions & 8 deletions core/endorser/endorser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import (
"github.com/hyperledger/fabric/core/ledger/kvledger"
"github.com/hyperledger/fabric/core/peer"
"github.com/hyperledger/fabric/core/util"
"github.com/hyperledger/fabric/msp"
pb "github.com/hyperledger/fabric/protos/peer"
pbutils "github.com/hyperledger/fabric/protos/utils"
"github.com/spf13/viper"
Expand All @@ -44,6 +45,8 @@ import (

var testDBWrapper = db.NewTestDBWrapper()
var endorserServer pb.EndorserServer
var mspInstance msp.PeerMSP
var signer msp.SigningIdentity

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

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

//getDeployProposal gets the proposal for the chaincode deployment
//the payload is a ChaincodeDeploymentSpec
func getDeployProposal(cds *pb.ChaincodeDeploymentSpec) (*pb.Proposal, error) {
func getDeployProposal(cds *pb.ChaincodeDeploymentSpec, creator []byte) (*pb.Proposal, error) {
b, err := proto.Marshal(cds)
if err != nil {
return nil, err
Expand All @@ -136,7 +139,21 @@ func getDeployProposal(cds *pb.ChaincodeDeploymentSpec) (*pb.Proposal, error) {
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}}}}

//...and get the proposal for it
return getProposal(lcccSpec)
return getProposal(lcccSpec, creator)
}

func getSignedProposal(prop *pb.Proposal, signer msp.SigningIdentity) (*pb.SignedProposal, error) {
propBytes, err := pbutils.GetBytesProposal(prop)
if err != nil {
return nil, err
}

signature, err := signer.Sign(propBytes)
if err != nil {
return nil, err
}

return &pb.SignedProposal{ProposalBytes: propBytes, Signature: signature}, nil
}

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

creator, err := signer.Serialize()
if err != nil {
return nil, err
}

var prop *pb.Proposal
prop, err = getDeployProposal(depSpec)
prop, err = getDeployProposal(depSpec, creator)
if err != nil {
return nil, err
}

var signedProp *pb.SignedProposal
signedProp, err = getSignedProposal(prop, signer)
if err != nil {
return nil, err
}

var resp *pb.ProposalResponse
resp, err = endorserServer.ProcessProposal(context.Background(), prop)
resp, err = endorserServer.ProcessProposal(context.Background(), signedProp)

return resp, err
}

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

creator, err := signer.Serialize()
if err != nil {
return nil, err
}

var prop *pb.Proposal
prop, err := getProposal(invocation)
prop, err = getProposal(invocation, creator)
if err != nil {
return nil, fmt.Errorf("Error creating proposal %s: %s\n", spec.ChaincodeID, err)
}

resp, err := endorserServer.ProcessProposal(context.Background(), prop)
var signedProp *pb.SignedProposal
signedProp, err = getSignedProposal(prop, signer)
if err != nil {
return nil, err
}

resp, err := endorserServer.ProcessProposal(context.Background(), signedProp)
if err != nil {
return nil, fmt.Errorf("Error endorsing %s: %s\n", spec.ChaincodeID, err)
}
Expand Down Expand Up @@ -324,6 +363,21 @@ func TestMain(m *testing.M) {
}

endorserServer = NewEndorserServer(nil)

// setup the MSP manager so that we can sign/verify
mspMgrConfigFile := "../../msp/peer-config.json"
msp.GetManager().Setup(mspMgrConfigFile)
mspId := "DEFAULT"
id := "PEER"
signingIdentity := &msp.IdentityIdentifier{Mspid: msp.ProviderIdentifier{Value: mspId}, Value: id}
signer, err = msp.GetManager().GetSigningIdentity(signingIdentity)
if err != nil {
os.Exit(-1)
fmt.Printf("Could not initialize msp/signer")
finitPeer(lis)
return
}

retVal := m.Run()

finitPeer(lis)
Expand Down
3 changes: 2 additions & 1 deletion core/endorser/endorser_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -138,13 +138,14 @@ logging:
# info - Set default to INFO
# warning:main,db=debug:chaincode=info - Override default WARNING in main,db,chaincode
# chaincode=info:main=debug:db=debug:warning - Same as above
msp: debug
peer: debug
crypto: info
status: warning
stop: warning
login: warning
vm: warning
chaincode: warning
chaincode: error


###############################################################################
Expand Down
Loading

0 comments on commit 1f9ff4d

Please sign in to comment.