From 5f9b3ea02d4312162bf958a9649c36bc4ec4496d Mon Sep 17 00:00:00 2001 From: Angelo De Caro Date: Fri, 16 Sep 2016 10:10:04 +0200 Subject: [PATCH] C2C invocation for confidential contracts. This PR addresses chaincode to chaincode invocation for confidential contracts (https://jira.hyperledger.org/browse/FAB-67). In order to achieve the goal the chaincode handler has been modified to contruct proper ephemeral transactions and security contexts. Let us consider the following scenario to describe the modification apported by this PR. Let us say that we have two chaincodes: A and B where A invokes B at some point of its computation. When a user invoke a chaincode A, using transaction tx, the certificate that the user has put in tx is passed to B when A invokes it. In this way, for example, chaincode B can perfom attribute-based access control. In addition, each chaincode can access it is own encrypted state and modify it in a proper way without affecting other chaincodes' state. This PR has been tested by adding a unit test in exectransaction_test.go. The unit tests, verify that C2C invocation can be perfomed when security is enabled. What will come next: 1. Chaincode to chaincode query. 2. Additional fields transfered to the invoked chaincode to support access control based on signature. Change-Id: I649d1953ac76e8af32d917a089a454fc0bba9fc1 Signed-off-by: Angelo De Caro --- core/chaincode/exectransaction_test.go | 79 +++++++++++++++++++++++--- core/chaincode/handler.go | 52 +++++++++++++---- 2 files changed, 113 insertions(+), 18 deletions(-) diff --git a/core/chaincode/exectransaction_test.go b/core/chaincode/exectransaction_test.go index a1d10fcd052..0bca0bc93f7 100644 --- a/core/chaincode/exectransaction_test.go +++ b/core/chaincode/exectransaction_test.go @@ -827,6 +827,18 @@ func TestChaincodeInvokeChaincode(t *testing.T) { go grpcServer.Serve(lis) + err = chaincodeInvokeChaincode(t, "") + if err != nil { + t.Fail() + t.Logf("Failed chaincode invoke chaincode : %s", err) + closeListenerAndSleep(lis) + return + } + + closeListenerAndSleep(lis) +} + +func chaincodeInvokeChaincode(t *testing.T, user string) (err error) { var ctxt = context.Background() // Deploy first chaincode @@ -836,7 +848,7 @@ func TestChaincodeInvokeChaincode(t *testing.T) { f := "init" args := util.ToChaincodeArgs(f, "a", "100", "b", "200") - spec1 := &pb.ChaincodeSpec{Type: 1, ChaincodeID: cID1, CtorMsg: &pb.ChaincodeInput{Args: args}} + spec1 := &pb.ChaincodeSpec{Type: 1, ChaincodeID: cID1, CtorMsg: &pb.ChaincodeInput{Args: args}, SecureContext: user} _, err = deploy(ctxt, spec1) chaincodeID1 := spec1.ChaincodeID.Name @@ -844,7 +856,6 @@ func TestChaincodeInvokeChaincode(t *testing.T) { t.Fail() t.Logf("Error initializing chaincode %s(%s)", chaincodeID1, err) GetChain(DefaultChain).Stop(ctxt, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec1}) - closeListenerAndSleep(lis) return } @@ -859,7 +870,7 @@ func TestChaincodeInvokeChaincode(t *testing.T) { f = "init" args = util.ToChaincodeArgs(f, "e", "0") - spec2 := &pb.ChaincodeSpec{Type: 1, ChaincodeID: cID2, CtorMsg: &pb.ChaincodeInput{Args: args}} + spec2 := &pb.ChaincodeSpec{Type: 1, ChaincodeID: cID2, CtorMsg: &pb.ChaincodeInput{Args: args}, SecureContext: user} _, err = deploy(ctxt, spec2) chaincodeID2 := spec2.ChaincodeID.Name @@ -868,7 +879,6 @@ func TestChaincodeInvokeChaincode(t *testing.T) { t.Logf("Error initializing chaincode %s(%s)", chaincodeID2, err) GetChain(DefaultChain).Stop(ctxt, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec1}) GetChain(DefaultChain).Stop(ctxt, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec2}) - closeListenerAndSleep(lis) return } @@ -878,7 +888,7 @@ func TestChaincodeInvokeChaincode(t *testing.T) { f = "invoke" args = util.ToChaincodeArgs(f, "e", "1") - spec2 = &pb.ChaincodeSpec{Type: 1, ChaincodeID: cID2, CtorMsg: &pb.ChaincodeInput{Args: args}} + spec2 = &pb.ChaincodeSpec{Type: 1, ChaincodeID: cID2, CtorMsg: &pb.ChaincodeInput{Args: args}, SecureContext: user} // Invoke chaincode var uuid string _, uuid, _, err = invoke(ctxt, spec2, pb.Transaction_CHAINCODE_INVOKE) @@ -888,7 +898,6 @@ func TestChaincodeInvokeChaincode(t *testing.T) { t.Logf("Error invoking <%s>: %s", chaincodeID2, err) GetChain(DefaultChain).Stop(ctxt, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec1}) GetChain(DefaultChain).Stop(ctxt, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec2}) - closeListenerAndSleep(lis) return } @@ -899,13 +908,67 @@ func TestChaincodeInvokeChaincode(t *testing.T) { t.Logf("Incorrect final state after transaction for <%s>: %s", chaincodeID1, err) GetChain(DefaultChain).Stop(ctxt, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec1}) GetChain(DefaultChain).Stop(ctxt, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec2}) - closeListenerAndSleep(lis) return } GetChain(DefaultChain).Stop(ctxt, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec1}) GetChain(DefaultChain).Stop(ctxt, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec2}) - closeListenerAndSleep(lis) + + return +} + +func TestChaincodeInvokeChaincodeWithSec(t *testing.T) { + testDBWrapper.CleanDB(t) + viper.Set("security.enabled", "true") + + //Initialize crypto + if err := crypto.Init(); err != nil { + panic(fmt.Errorf("Failed initializing the crypto layer [%s]", err)) + } + + //set paths for memberservice to pick up + viper.Set("peer.fileSystemPath", filepath.Join(os.TempDir(), "hyperledger", "production")) + viper.Set("server.rootpath", filepath.Join(os.TempDir(), "ca")) + + var err error + var memSrvcLis net.Listener + if memSrvcLis, err = initMemSrvc(); err != nil { + t.Fail() + t.Logf("Error registering user %s", err) + return + } + + time.Sleep(2 * time.Second) + + var peerLis net.Listener + if peerLis, err = initPeer(); err != nil { + finitMemSrvc(memSrvcLis) + t.Fail() + t.Logf("Error registering user %s", err) + return + } + + if err = crypto.RegisterClient("jim", nil, "jim", "6avZQLwcUe9b"); err != nil { + finitMemSrvc(memSrvcLis) + finitPeer(peerLis) + t.Fail() + t.Logf("Error registering user %s", err) + return + } + + //login as jim and test chaincode-chaincode interaction with security + if err = chaincodeInvokeChaincode(t, "jim"); err != nil { + finitMemSrvc(memSrvcLis) + finitPeer(peerLis) + t.Fail() + t.Logf("Error executing test %s", err) + return + } + + //cleanup + finitMemSrvc(memSrvcLis) + finitPeer(peerLis) + } // Test the execution of a chaincode that invokes another chaincode with wrong parameters. Should receive error from diff --git a/core/chaincode/handler.go b/core/chaincode/handler.go index 52c66deeef1..b045a442def 100644 --- a/core/chaincode/handler.go +++ b/core/chaincode/handler.go @@ -161,9 +161,9 @@ func (handler *Handler) deleteRangeQueryIterator(txContext *transactionContext, delete(txContext.rangeQueryIteratorMap, txid) } -//THIS CAN BE REMOVED ONCE WE SUPPORT CONFIDENTIALITY WITH CC-CALLING-CC -//we dissallow chaincode-chaincode interactions till confidentiality implications are understood -func (handler *Handler) canCallChaincode(txid string) *pb.ChaincodeMessage { +//THIS CAN BE REMOVED ONCE WE FULL SUPPORT (Invoke and Query) CONFIDENTIALITY WITH CC-CALLING-CC +//Only invocation are allowed, not queries +func (handler *Handler) canCallChaincode(txid string, isQuery bool) *pb.ChaincodeMessage { secHelper := handler.chaincodeSupport.getSecHelper() if secHelper == nil { return nil @@ -176,7 +176,9 @@ func (handler *Handler) canCallChaincode(txid string) *pb.ChaincodeMessage { } else if txctx.transactionSecContext == nil { errMsg = fmt.Sprintf("[%s]Error transaction context is nil while checking for confidentiality. Sending %s", shorttxid(txid), pb.ChaincodeMessage_ERROR) } else if txctx.transactionSecContext.ConfidentialityLevel != pb.ConfidentialityLevel_PUBLIC { - errMsg = fmt.Sprintf("[%s]Error chaincode-chaincode interactions not supported for with privacy enabled. Sending %s", shorttxid(txid), pb.ChaincodeMessage_ERROR) + if isQuery { + errMsg = fmt.Sprintf("[%s]Error chaincode-chaincode interactions not supported for with privacy enabled. Sending %s", shorttxid(txid), pb.ChaincodeMessage_ERROR) + } } if errMsg != "" { @@ -209,10 +211,12 @@ func (handler *Handler) encryptOrDecrypt(encrypt bool, txid string, payload []by var err error if txctx.transactionSecContext.Type == pb.Transaction_CHAINCODE_DEPLOY { if enc, err = secHelper.GetStateEncryptor(handler.deployTXSecContext, handler.deployTXSecContext); err != nil { + chaincodeLogger.Errorf("error getting crypto encryptor for deploy tx :%s", err) return nil, fmt.Errorf("error getting crypto encryptor for deploy tx :%s", err) } } else if txctx.transactionSecContext.Type == pb.Transaction_CHAINCODE_INVOKE || txctx.transactionSecContext.Type == pb.Transaction_CHAINCODE_QUERY { if enc, err = secHelper.GetStateEncryptor(handler.deployTXSecContext, txctx.transactionSecContext); err != nil { + chaincodeLogger.Errorf("error getting crypto encryptor %s", err) return nil, fmt.Errorf("error getting crypto encryptor %s", err) } } else { @@ -1046,7 +1050,9 @@ func (handler *Handler) enterBusyState(e *fsm.Event, state string) { err = ledgerObj.DeleteState(chaincodeID, key) } else if msg.Type.String() == pb.ChaincodeMessage_INVOKE_CHAINCODE.String() { //check and prohibit C-call-C for CONFIDENTIAL txs - if triggerNextStateMsg = handler.canCallChaincode(msg.Txid); triggerNextStateMsg != nil { + chaincodeLogger.Debugf("[%s] C-call-C", shorttxid(msg.Txid)) + + if triggerNextStateMsg = handler.canCallChaincode(msg.Txid, false); triggerNextStateMsg != nil { return } chaincodeSpec := &pb.ChaincodeSpec{} @@ -1060,12 +1066,21 @@ func (handler *Handler) enterBusyState(e *fsm.Event, state string) { // Get the chaincodeID to invoke newChaincodeID := chaincodeSpec.ChaincodeID.Name + chaincodeLogger.Debugf("[%s] C-call-C %s", shorttxid(msg.Txid), newChaincodeID) // Create the transaction object chaincodeInvocationSpec := &pb.ChaincodeInvocationSpec{ChaincodeSpec: chaincodeSpec} transaction, _ := pb.NewChaincodeExecute(chaincodeInvocationSpec, msg.Txid, pb.Transaction_CHAINCODE_INVOKE) - // Launch the new chaincode if not already running + tsc := handler.getTxContext(msg.Txid).transactionSecContext + + transaction.Nonce = tsc.Nonce + transaction.ConfidentialityLevel = tsc.ConfidentialityLevel + transaction.ConfidentialityProtocolVersion = tsc.ConfidentialityProtocolVersion + transaction.Metadata = tsc.Metadata + transaction.Cert = tsc.Cert + + // cd the new chaincode if not already running _, chaincodeInput, launchErr := handler.chaincodeSupport.Launch(context.Background(), transaction) if launchErr != nil { payload := []byte(launchErr.Error()) @@ -1217,7 +1232,7 @@ func (handler *Handler) initializeSecContext(tx, depTx *pb.Transaction) error { return nil } -func (handler *Handler) setChaincodeSecurityContext(tx *pb.Transaction, msg *pb.ChaincodeMessage) error { +func (handler *Handler) setChaincodeSecurityContext(tx, depTx *pb.Transaction, msg *pb.ChaincodeMessage) error { chaincodeLogger.Debug("setting chaincode security context...") if msg.SecurityContext == nil { msg.SecurityContext = &pb.ChaincodeSecurityContext{} @@ -1248,6 +1263,13 @@ func (handler *Handler) setChaincodeSecurityContext(tx *pb.Transaction, msg *pb. return err } + msg.SecurityContext.Payload = ctorMsgRaw + // TODO: add deploy metadata + if depTx != nil { + msg.SecurityContext.ParentMetadata = depTx.Metadata + } else { + msg.SecurityContext.ParentMetadata = handler.deployTXSecContext.Metadata + } msg.SecurityContext.Payload = ctorMsgRaw msg.SecurityContext.TxTimestamp = tx.Timestamp } @@ -1289,7 +1311,7 @@ func (handler *Handler) initOrReady(txid string, initArgs [][]byte, tx *pb.Trans } //if security is disabled the context elements will just be nil - if err := handler.setChaincodeSecurityContext(tx, ccMsg); err != nil { + if err := handler.setChaincodeSecurityContext(tx, depTx, ccMsg); err != nil { return nil, err } @@ -1317,7 +1339,7 @@ func (handler *Handler) handleQueryChaincode(msg *pb.ChaincodeMessage) { }() //check and prohibit C-call-C for CONFIDENTIAL txs - if serialSendMsg = handler.canCallChaincode(msg.Txid); serialSendMsg != nil { + if serialSendMsg = handler.canCallChaincode(msg.Txid, true); serialSendMsg != nil { return } @@ -1337,6 +1359,16 @@ func (handler *Handler) handleQueryChaincode(msg *pb.ChaincodeMessage) { chaincodeInvocationSpec := &pb.ChaincodeInvocationSpec{ChaincodeSpec: chaincodeSpec} transaction, _ := pb.NewChaincodeExecute(chaincodeInvocationSpec, msg.Txid, pb.Transaction_CHAINCODE_QUERY) + tsc := handler.getTxContext(msg.Txid).transactionSecContext + + transaction.Nonce = tsc.Nonce + transaction.ConfidentialityLevel = tsc.ConfidentialityLevel + transaction.ConfidentialityProtocolVersion = tsc.ConfidentialityProtocolVersion + transaction.Metadata = tsc.Metadata + transaction.Cert = tsc.Cert + + chaincodeLogger.Debugf("[%s]Invoking another chaincode", shorttxid(msg.Txid)) + // Launch the new chaincode if not already running _, chaincodeInput, launchErr := handler.chaincodeSupport.Launch(context.Background(), transaction) if launchErr != nil { @@ -1466,7 +1498,7 @@ func (handler *Handler) sendExecuteMessage(msg *pb.ChaincodeMessage, tx *pb.Tran } //if security is disabled the context elements will just be nil - if err := handler.setChaincodeSecurityContext(tx, msg); err != nil { + if err := handler.setChaincodeSecurityContext(tx, nil, msg); err != nil { return nil, err }