Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support for digital signatures + integration tests work (again) #501

Merged
merged 7 commits into from
Dec 18, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for mock_enclave.go (and for symmetry) you will also need the generate signature?


#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)
bvavala marked this conversation as resolved.
Show resolved Hide resolved
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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This number sounds like magic. Can you add a comment on how you got to this number?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added comment

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm, the comment doesn't really mention on how you got to that number. That it was an estimate i already had figured out before :-)


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 ...
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See above, would be useful to add some comments on how we came to this number. Maybe also add a comment in the protobufs pointing to here so if we change there we also adapt number here?

Copy link
Contributor Author

@bvavala bvavala Dec 18, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added comment, if there isn't enough space...it will fail. In the future we should make it adaptive.


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