Skip to content

Commit

Permalink
support for digital signatures + integration tests work (again) (#501)
Browse files Browse the repository at this point in the history
* pdo crypto cgo wrappers for go
* enclave endorsement signature generation and validation based on  clean protobuf message (serialization), involved slight protobuf change of chaincode response message
* Read/Write-set with (hashed) values for read-set versioning, based on slightly modifed protobufs
  • Loading branch information
bvavala authored Dec 18, 2020
1 parent e736015 commit 50a54ea
Show file tree
Hide file tree
Showing 22 changed files with 594 additions and 321 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ include $(TOP)/build.mk

SUB_DIRS = protos common internal ercc ecc_enclave ecc fabric client_sdk examples utils integration # docs

FPC_SDK_DEP_DIRS = protos utils/fabric common ecc_enclave ecc
FPC_SDK_DEP_DIRS = protos common utils/fabric ecc_enclave ecc
FPC_PEER_DEP_DIRS = protos common ercc fabric ecc_enclave ecc
# FPC_PEER_DEP_DIRS has to include protos, ecc, ecc_enclave, common and ercc only if we run chaincode in external builder directly on host and not indirectly via docker
FPC_PEER_CLI_WRAPPER_DEP_DIRS = utils/fabric
Expand Down
26 changes: 24 additions & 2 deletions client_sdk/go/fpc/crypto/crypto.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,19 @@ import (
"github.com/hyperledger/fabric-protos-go/peer"
"github.com/hyperledger/fabric/common/flogging"
"google.golang.org/protobuf/proto"

"fmt"
)

// #cgo CFLAGS: -I${SRCDIR}/../../../../common/crypto
// #cgo LDFLAGS: -L${SRCDIR}/../../../../common/crypto/_build -L${SRCDIR}/../../../../common/logging/_build -Wl,--start-group -lupdo-crypto-adapt -lupdo-crypto -Wl,--end-group -lcrypto -lulogging -lstdc++
// #include <stdio.h>
// #include <stdlib.h>
// #include <stdbool.h>
// #include <stdint.h>
// #include "pdo-crypto-c-wrapper.h"
import "C"

var logger = flogging.MustGetLogger("fpc-client-crypto")

func encrypt(input []byte, encryptionKey []byte) ([]byte, error) {
Expand Down Expand Up @@ -70,12 +81,23 @@ type EncryptionContextImpl struct {
chaincodeEncryptionKey []byte
}

func (e *EncryptionContextImpl) Reveal(responseBytesB64 []byte) ([]byte, error) {
responseBytes, err := base64.StdEncoding.DecodeString(string(responseBytesB64))
func (e *EncryptionContextImpl) Reveal(signedResponseBytesB64 []byte) ([]byte, error) {
signedResponseBytes, err := base64.StdEncoding.DecodeString(string(signedResponseBytesB64))
if err != nil {
return nil, err
}

signedResponse := &protos.SignedChaincodeResponseMessage{}
err = proto.Unmarshal(signedResponseBytes, signedResponse)
if err != nil {
return nil, err
}

responseBytes := signedResponse.GetChaincodeResponseMessage()
if responseBytes == nil {
return nil, fmt.Errorf("no chaincode response message")
}

response := &protos.ChaincodeResponseMessage{}
err = proto.Unmarshal(responseBytes, response)
if err != nil {
Expand Down
1 change: 0 additions & 1 deletion client_sdk/go/test/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ func populateWallet(wallet *gateway.Wallet) error {
}

func main() {

os.Setenv("GRPC_TRACE", "all")
os.Setenv("GRPC_VERBOSITY", "DEBUG")
os.Setenv("GRPC_GO_LOG_SEVERITY_LEVEL", "INFO")
Expand Down
1 change: 1 addition & 0 deletions common/crypto/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ FILE(GLOB PROJECT_SOURCES
"${PDO_CRYPTO_DIR}/../hex_string.cpp"
"${PDO_CRYPTO_DIR}/../log.cpp"
"pdo-types.cpp"
"pdo-crypto-c-wrapper.cpp"
"$ENV{FPC_PATH}/common/base64/base64.cpp"
"$ENV{FPC_PATH}/common/json/parson.c"
"attestation-api/evidence/*.cpp"
Expand Down
67 changes: 67 additions & 0 deletions common/crypto/pdo-crypto-c-wrapper.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Copyright 2020 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <string.h>
#include "crypto.h"
#include "logging.h"
#include "error.h"
#include "types.h"

#ifdef __cplusplus
extern "C" {
#endif

bool compute_hash(uint8_t* message,
uint32_t message_len,
uint8_t* hash,
uint32_t max_hash_len,
uint32_t* actual_hash_len)
{
ByteArray ba;

COND2ERR(message == NULL);

ba = pdo::crypto::ComputeMessageHash(ByteArray(message, message + message_len));
COND2ERR(ba.size() > max_hash_len);

memcpy(hash, ba.data(), ba.size());
*actual_hash_len = ba.size();
return true;

err:
return false;
}

bool verify_signature(uint8_t* public_key, uint32_t public_key_len, uint8_t* message, uint32_t message_len, uint8_t* signature, uint32_t signature_len)
{
try
{
std::string pk_string((const char*)public_key, public_key_len);
ByteArray msg(message, message + message_len);
ByteArray sig(signature, signature + signature_len);

//deserialize public key
pdo::crypto::sig::PublicKey pk(pk_string);

//check signature
int r = pk.VerifySignature(msg, sig);
COND2ERR(r != 1);
}
catch(...)
{
COND2ERR(true);
}

// verification successful
return true;

err:
return false;
}

#ifdef __cplusplus
}
#endif /* __cplusplus */
23 changes: 23 additions & 0 deletions common/crypto/pdo-crypto-c-wrapper.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* Copyright 2020 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/

#pragma once

#ifdef __cplusplus
extern "C" {
#endif

bool compute_hash(uint8_t* message,
uint32_t message_len,
uint8_t* hash,
uint32_t max_hash_len,
uint32_t* actual_hash_len);

bool verify_signature(uint8_t* public_key, uint32_t public_key_len, uint8_t* message, uint32_t message_len, uint8_t* signature, uint32_t signature_len);

#ifdef __cplusplus
}
#endif
12 changes: 12 additions & 0 deletions common/enclave/cc_data.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -235,3 +235,15 @@ std::string cc_data::get_enclave_id()

return hex;
}

bool cc_data::sign_message(const ByteArray& message, ByteArray& signature) const
{
bool b;
CATCH(b, signature = signature_key_.SignMessage(message));
COND2LOGERR(!b, "message signing failed");

return true;

err:
return false;
}
1 change: 1 addition & 0 deletions common/enclave/cc_data.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class cc_data

ByteArray get_state_encryption_key();
std::string get_enclave_id();
bool sign_message(const ByteArray& message, ByteArray& signature) const;
};

extern cc_data* g_cc_data;
1 change: 1 addition & 0 deletions common/enclave/common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ int ecall_init(const uint8_t* attestation_parameters,
COND2LOGERR(!b, "error getting credentials");
}

LOG_DEBUG("init enclave successful");
return SGX_SUCCESS;

err:
Expand Down
23 changes: 13 additions & 10 deletions ecc/chaincode/ecc.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,27 +115,27 @@ func (t *EnclaveChaincode) invoke(stub shim.ChaincodeStubInterface) pb.Response
return shim.Error(errMsg)
}

chaincodeResponseMessage, errInvoke := t.enclave.ChaincodeInvoke(stub, chaincodeRequestMessage)
signedChaincodeResponseMessage, errInvoke := t.enclave.ChaincodeInvoke(stub, chaincodeRequestMessage)
if errInvoke != nil {
errMsg = fmt.Sprintf("t.enclave.Invoke failed: %s", errInvoke)
logger.Errorf(errMsg)
// likely a chaincode error, so we still want response go back ...
}

chaincodeResponseMessageB64 := []byte(base64.StdEncoding.EncodeToString(chaincodeResponseMessage))
logger.Debugf("base64-encoded response message: '%s'", chaincodeResponseMessageB64)
signedChaincodeResponseMessageB64 := []byte(base64.StdEncoding.EncodeToString(signedChaincodeResponseMessage))
logger.Debugf("base64-encoded response message: '%s'", signedChaincodeResponseMessageB64)

var response pb.Response
if errInvoke == nil {
response = pb.Response{
Status: shim.OK,
Payload: chaincodeResponseMessageB64,
Payload: signedChaincodeResponseMessageB64,
Message: errMsg,
}
} else {
response = pb.Response{
Status: shim.ERROR,
Payload: chaincodeResponseMessageB64,
Payload: signedChaincodeResponseMessageB64,
Message: errMsg,
}
}
Expand All @@ -152,7 +152,7 @@ func (t *EnclaveChaincode) endorse(stub shim.ChaincodeStubInterface) pb.Response
return shim.Error(errMsg)
}

responseMsg, err := extractChaincodeResponseMessage(stub)
signedResponseMsg, responseMsg, err := extractChaincodeResponseMessages(stub)
if err != nil {
errMsg := fmt.Sprintf("cannot extract chaincode response message: %s", err.Error())
logger.Errorf(errMsg)
Expand Down Expand Up @@ -184,18 +184,21 @@ func (t *EnclaveChaincode) endorse(stub shim.ChaincodeStubInterface) pb.Response

// check cc param.MSPID matches MSPID of endorser (Post-MVP)

// replay read/writes from kvrwset from enclave (to prepare commitment to ledger) and extract kvrwset for subsequent validation
readset, writeset, err := utils.ReplayReadWrites(stub, responseMsg.RwSet)
// validate enclave endorsement signature
logger.Debugf("Validating endorsement")
err = utils.Validate(signedResponseMsg, &attestedData)
if err != nil {
return shim.Error(err.Error())
}

// validate enclave endorsement signature
err = utils.Validate(responseMsg, readset, writeset, &attestedData)
// replay read/writes from kvrwset from enclave (to prepare commitment to ledger) and extract kvrwset for subsequent validation
logger.Debugf("Replaying rwset")
err = utils.ReplayReadWrites(stub, responseMsg.FpcRwSet)
if err != nil {
return shim.Error(err.Error())
}

logger.Debugf("Endorsement successful")
return shim.Success([]byte("OK")) // make sure we have a non-empty return on success so we can distinguish success from failure in cli ...
}

Expand Down
41 changes: 13 additions & 28 deletions ecc/chaincode/enclave/enclave.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import (
"unsafe"

"github.com/golang/protobuf/proto"
"github.com/hyperledger-labs/fabric-private-chaincode/internal/protos"
"github.com/hyperledger/fabric-chaincode-go/shim"
"golang.org/x/sync/semaphore"
)
Expand All @@ -29,7 +28,6 @@ import (
import "C"

const enclaveLibFile = "enclave/lib/enclave.signed.so"
const maxResponseSize = 1024 * 100 // Let's be really conservative ...

type EnclaveStub struct {
eid C.enclave_id_t
Expand All @@ -43,14 +41,17 @@ func NewEnclaveStub() StubInterface {
}

func (e *EnclaveStub) Init(chaincodeParams, hostParams, attestationParams []byte) ([]byte, error) {
// Estimate of the buffer length that is necessary for the credentials. It should be conservative.
const credentialsBufferMaxLen = 16 * 1024

if e.isInitialized {
return nil, fmt.Errorf("enclave already initialized")
}

var eid C.enclave_id_t

// prepare output buffer for credentials
credentialsBuffer := C.malloc(maxResponseSize)
credentialsBuffer := C.malloc(credentialsBufferMaxLen)
defer C.free(credentialsBuffer)
credentialsSize := C.uint32_t(0)

Expand All @@ -70,7 +71,7 @@ func (e *EnclaveStub) Init(chaincodeParams, hostParams, attestationParams []byte
(*C.uint8_t)(C.CBytes(hostParams)),
C.uint32_t(len(hostParams)),
(*C.uint8_t)(credentialsBuffer),
C.uint32_t(maxResponseSize),
C.uint32_t(credentialsBufferMaxLen),
&credentialsSize)

if ret != 0 {
Expand Down Expand Up @@ -106,6 +107,9 @@ func (e *EnclaveStub) GetEnclaveId() (string, error) {

// ChaincodeInvoke calls the enclave for transaction processing
func (e *EnclaveStub) ChaincodeInvoke(stub shim.ChaincodeStubInterface, crmProtoBytes []byte) ([]byte, error) {
// Estimate of the buffer length where the enclave will write the response.
const scresmProtoBytesMaxLen = 1024 * 100 // Let's be really conservative ...

if !e.isInitialized {
return nil, fmt.Errorf("enclave not yet initialized")
}
Expand All @@ -128,9 +132,9 @@ func (e *EnclaveStub) ChaincodeInvoke(stub shim.ChaincodeStubInterface, crmProto
defer C.free(unsafe.Pointer(signedProposalPtr))

// prep response
cresmProtoBytesLenOut := C.uint32_t(0) // We pass maximal length separately; set to zero so we can detect valid responses
cresmProtoBytesPtr := C.malloc(maxResponseSize)
defer C.free(cresmProtoBytesPtr)
scresmProtoBytesLenOut := C.uint32_t(0) // We pass maximal length separately; set to zero so we can detect valid responses
scresmProtoBytesPtr := C.malloc(scresmProtoBytesMaxLen)
defer C.free(scresmProtoBytesPtr)

crmProtoBytesPtr := C.CBytes(crmProtoBytes)
defer C.free(unsafe.Pointer(crmProtoBytesPtr))
Expand All @@ -146,31 +150,12 @@ func (e *EnclaveStub) ChaincodeInvoke(stub shim.ChaincodeStubInterface, crmProto
(C.uint32_t)(len(signedProposalBytes)),
(*C.uint8_t)(crmProtoBytesPtr),
(C.uint32_t)(len(crmProtoBytes)),
(*C.uint8_t)(cresmProtoBytesPtr), (C.uint32_t)(maxResponseSize), &cresmProtoBytesLenOut,
(*C.uint8_t)(scresmProtoBytesPtr), (C.uint32_t)(scresmProtoBytesMaxLen), &scresmProtoBytesLenOut,
ctx)
e.sem.Release(1)
if invokeRet != 0 {
return nil, fmt.Errorf("invoke failed. Reason: %d", int(invokeRet))
}

cresmProtoBytes := C.GoBytes(cresmProtoBytesPtr, C.int(cresmProtoBytesLenOut))

responseMsg := &protos.ChaincodeResponseMessage{}
err = proto.Unmarshal(cresmProtoBytes, responseMsg)
if err != nil {
return nil, fmt.Errorf("cannot unmarshal ChaincodeResponseMessage: %s", err.Error())
}

// include proposal here
responseMsg.Proposal = proposal

// TODO set RW set
//responseMsg.RwSet = ...

cresmProtoBytes, err = proto.Marshal(responseMsg)
if err != nil {
return nil, fmt.Errorf("cannot marshal ChaincodeResponseMessage: %s", err.Error())
}

return cresmProtoBytes, nil
return C.GoBytes(scresmProtoBytesPtr, C.int(scresmProtoBytesLenOut)), nil
}
20 changes: 14 additions & 6 deletions ecc/chaincode/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,21 +83,29 @@ func extractInitEnclaveMessage(stub shim.ChaincodeStubInterface) (*protos.InitEn
return initMsg, err
}

func extractChaincodeResponseMessage(stub shim.ChaincodeStubInterface) (*protos.ChaincodeResponseMessage, error) {
func extractChaincodeResponseMessages(stub shim.ChaincodeStubInterface) (*protos.SignedChaincodeResponseMessage, *protos.ChaincodeResponseMessage, error) {
if len(stub.GetStringArgs()) < 2 {
return nil, fmt.Errorf("initEnclaveMessage missing")
return nil, nil, fmt.Errorf("initEnclaveMessage missing")
}

serializedChaincodeResponseMessage, err := base64.StdEncoding.DecodeString(stub.GetStringArgs()[1])
serializedSignedChaincodeResponseMessage, err := base64.StdEncoding.DecodeString(stub.GetStringArgs()[1])
if err != nil {
return nil, err
return nil, nil, err
}

signedResponseMsg := &protos.SignedChaincodeResponseMessage{}
err = proto.Unmarshal(serializedSignedChaincodeResponseMessage, signedResponseMsg)
if err != nil {
return nil, nil, err
}

serializedChaincodeResponseMessage := signedResponseMsg.ChaincodeResponseMessage

responseMsg := &protos.ChaincodeResponseMessage{}
err = proto.Unmarshal(serializedChaincodeResponseMessage, responseMsg)
if err != nil {
return nil, err
return nil, nil, err
}

return responseMsg, err
return signedResponseMsg, responseMsg, err
}
Loading

0 comments on commit 50a54ea

Please sign in to comment.