From bc5982d35802919c273362297f8c6c1f36087ab5 Mon Sep 17 00:00:00 2001 From: Bruno Vavala Date: Thu, 17 Dec 2020 23:27:48 -0800 Subject: [PATCH 1/7] new protobufs signed response, rwset; pdo c cryptowrappers; proto integrations; signature computation and validation; rwset serialization and replay; fixed echo test Signed-off-by: Bruno Vavala --- Makefile | 2 +- client_sdk/go/fpc/crypto/crypto.go | 26 ++- client_sdk/go/test/main.go | 1 - common/crypto/CMakeLists.txt | 1 + common/crypto/pdo-crypto-c-wrapper.cpp | 66 ++++++ common/crypto/pdo-crypto-c-wrapper.h | 23 ++ common/enclave/cc_data.cpp | 12 + common/enclave/cc_data.h | 1 + common/enclave/common.cpp | 1 + common/error.h | 4 + ecc/chaincode/ecc.go | 13 +- ecc/chaincode/enclave/enclave.go | 39 +--- ecc/chaincode/utils.go | 20 +- ecc_enclave/enclave/CMakeLists.txt | 2 + ecc_enclave/enclave/enclave.cpp | 301 +++++++++---------------- ecc_enclave/enclave/enclave.edl | 6 +- ecc_enclave/enclave/shim.cpp | 51 ++++- ecc_enclave/enclave/shim_internals.cpp | 114 ++++++++++ ecc_enclave/enclave/shim_internals.h | 9 +- internal/utils/validation.go | 144 +++++++----- protos/fabric.options | 3 + protos/fpc.options | 4 + protos/fpc/fpc.proto | 22 +- 23 files changed, 554 insertions(+), 311 deletions(-) create mode 100644 common/crypto/pdo-crypto-c-wrapper.cpp create mode 100644 common/crypto/pdo-crypto-c-wrapper.h create mode 100644 ecc_enclave/enclave/shim_internals.cpp diff --git a/Makefile b/Makefile index 43753d589..8e05c6183 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ TOP = . include $(TOP)/build.mk -SUB_DIRS = protos common internal ercc ecc_enclave ecc fabric client_sdk examples utils integration # docs +SUB_DIRS = protos common internal ercc ecc_enclave ecc fabric client_sdk examples integration # docs FPC_SDK_DEP_DIRS = protos utils/fabric common ecc_enclave ecc FPC_PEER_DEP_DIRS = protos common ercc fabric ecc_enclave ecc diff --git a/client_sdk/go/fpc/crypto/crypto.go b/client_sdk/go/fpc/crypto/crypto.go index c78f7927e..805f4300d 100644 --- a/client_sdk/go/fpc/crypto/crypto.go +++ b/client_sdk/go/fpc/crypto/crypto.go @@ -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 +// #include +// #include +// #include +// #include "pdo-crypto-c-wrapper.h" +import "C" + var logger = flogging.MustGetLogger("fpc-client-crypto") func encrypt(input []byte, encryptionKey []byte) ([]byte, error) { @@ -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 { diff --git a/client_sdk/go/test/main.go b/client_sdk/go/test/main.go index e0f2cdecd..8db6ace63 100644 --- a/client_sdk/go/test/main.go +++ b/client_sdk/go/test/main.go @@ -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") diff --git a/common/crypto/CMakeLists.txt b/common/crypto/CMakeLists.txt index 6138037d2..63134dc55 100644 --- a/common/crypto/CMakeLists.txt +++ b/common/crypto/CMakeLists.txt @@ -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" diff --git a/common/crypto/pdo-crypto-c-wrapper.cpp b/common/crypto/pdo-crypto-c-wrapper.cpp new file mode 100644 index 000000000..af0f38818 --- /dev/null +++ b/common/crypto/pdo-crypto-c-wrapper.cpp @@ -0,0 +1,66 @@ +/* + * Copyright 2020 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "crypto.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 */ diff --git a/common/crypto/pdo-crypto-c-wrapper.h b/common/crypto/pdo-crypto-c-wrapper.h new file mode 100644 index 000000000..8d6abcb07 --- /dev/null +++ b/common/crypto/pdo-crypto-c-wrapper.h @@ -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 diff --git a/common/enclave/cc_data.cpp b/common/enclave/cc_data.cpp index ede5e250e..cec19d55a 100644 --- a/common/enclave/cc_data.cpp +++ b/common/enclave/cc_data.cpp @@ -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; +} diff --git a/common/enclave/cc_data.h b/common/enclave/cc_data.h index f06ad6caf..c37480a57 100644 --- a/common/enclave/cc_data.h +++ b/common/enclave/cc_data.h @@ -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; diff --git a/common/enclave/common.cpp b/common/enclave/common.cpp index fc18dfd84..a51cb3165 100644 --- a/common/enclave/common.cpp +++ b/common/enclave/common.cpp @@ -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: diff --git a/common/error.h b/common/error.h index 27f357df4..7e26ce2f3 100644 --- a/common/error.h +++ b/common/error.h @@ -6,6 +6,10 @@ #pragma once +#ifndef LOG_DEBUG +#define LOG_DEBUG +#endif + #define COND2ERR(b) \ do \ { \ diff --git a/ecc/chaincode/ecc.go b/ecc/chaincode/ecc.go index 44b721640..c56cdbd3e 100644 --- a/ecc/chaincode/ecc.go +++ b/ecc/chaincode/ecc.go @@ -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) @@ -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 ... } diff --git a/ecc/chaincode/enclave/enclave.go b/ecc/chaincode/enclave/enclave.go index 4be9da431..fc82196a5 100644 --- a/ecc/chaincode/enclave/enclave.go +++ b/ecc/chaincode/enclave/enclave.go @@ -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" ) @@ -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 @@ -43,6 +41,8 @@ func NewEnclaveStub() StubInterface { } func (e *EnclaveStub) Init(chaincodeParams, hostParams, attestationParams []byte) ([]byte, error) { + const credentialsBufferMaxLen = 16 * 1024 + if e.isInitialized { return nil, fmt.Errorf("enclave already initialized") } @@ -50,7 +50,7 @@ func (e *EnclaveStub) Init(chaincodeParams, hostParams, attestationParams []byte 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) @@ -70,7 +70,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 { @@ -106,6 +106,8 @@ func (e *EnclaveStub) GetEnclaveId() (string, error) { // ChaincodeInvoke calls the enclave for transaction processing func (e *EnclaveStub) ChaincodeInvoke(stub shim.ChaincodeStubInterface, crmProtoBytes []byte) ([]byte, error) { + const scresmProtoBytesMaxLen = 1024 * 100 // Let's be really conservative ... + if !e.isInitialized { return nil, fmt.Errorf("enclave not yet initialized") } @@ -128,9 +130,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)) @@ -146,31 +148,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 } diff --git a/ecc/chaincode/utils.go b/ecc/chaincode/utils.go index 9b28a1a4d..a26469d6f 100644 --- a/ecc/chaincode/utils.go +++ b/ecc/chaincode/utils.go @@ -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 } diff --git a/ecc_enclave/enclave/CMakeLists.txt b/ecc_enclave/enclave/CMakeLists.txt index da914472d..36a2c4bfb 100644 --- a/ecc_enclave/enclave/CMakeLists.txt +++ b/ecc_enclave/enclave/CMakeLists.txt @@ -10,6 +10,7 @@ set(SOURCE_FILES enclave.cpp enclave_t.c shim.cpp + shim_internals.cpp ${COMMON_SOURCE_DIR}/enclave/common.cpp ${COMMON_SOURCE_DIR}/enclave/cc_data.cpp ${COMMON_SOURCE_DIR}/base64/base64.cpp @@ -65,6 +66,7 @@ include_directories( ${SGX_SSL}/include ${FPC_PATH}/common/crypto ${FPC_PATH}/common/protos + ${FPC_PATH}/common/protos/fpc ${FPC_PATH}/common/protos/fabric ${FPC_PATH}/common/logging/trusted ${FPC_PATH}/common/crypto/pdo/common diff --git a/ecc_enclave/enclave/enclave.cpp b/ecc_enclave/enclave/enclave.cpp index 288c753f4..0a5bec456 100644 --- a/ecc_enclave/enclave/enclave.cpp +++ b/ecc_enclave/enclave/enclave.cpp @@ -5,184 +5,26 @@ * SPDX-License-Identifier: Apache-2.0 */ -#include "enclave_t.h" - -#include "logging.h" -#include "shim.h" -#include "shim_internals.h" -#include "utils.h" - #include "base64.h" +#include "cc_data.h" +#include "enclave_t.h" #include "error.h" - -#include /* for memcpy_s etc */ -#include "sgx_utils.h" - #include "fpc/fpc.pb.h" +#include "logging.h" #include "pb_decode.h" #include "pb_encode.h" +#include "shim.h" +#include "shim_internals.h" -#include "cc_data.h" - -extern sgx_ec256_private_t enclave_sk; -extern sgx_ec256_public_t enclave_pk; - -/* -int gen_response(const char* txType, - uint8_t* response, - uint32_t* response_len_out, - shim_ctx_ptr_t ctx) -{ - int ret; - - // Note: below signature is verified in - // - ecc/crypto/ecdsa.go::Verify (for VSCC) - - // create Hash <- H(txType in {"invoke"} || encoded_args || result || read set || write - // set) - // TODO: we should encode the hash below in an unambiguous fashion (which is not true with - // simple concatenation as done below!) - // Probably easiest by prefixing each field by length in fixed-size format? - sgx_sha256_hash_t hash; - sgx_sha_state_handle_t sha_handle; - sgx_sha256_init(&sha_handle); - LOG_DEBUG("txType: %s", txType); - sgx_sha256_update((const uint8_t*)txType, strlen(txType), sha_handle); - LOG_DEBUG("encoded_args: %s", ctx->encoded_args); - sgx_sha256_update((const uint8_t*)ctx->encoded_args, strlen(ctx->encoded_args), sha_handle); - LOG_DEBUG("response_data len: %d", *response_len_out); - sgx_sha256_update(response, *response_len_out, sha_handle); - - // hash read and write set - LOG_DEBUG("read_set:"); - for (auto& it : ctx->read_set) - { - LOG_DEBUG("\\-> %s", it.c_str()); - sgx_sha256_update((const uint8_t*)it.c_str(), it.size(), sha_handle); - } - - LOG_DEBUG("write_set:"); - for (auto& it : ctx->write_set) - { - LOG_DEBUG("\\-> %s - %s", it.first.c_str(), it.second.c_str()); - sgx_sha256_update((const uint8_t*)it.first.c_str(), it.first.size(), sha_handle); - sgx_sha256_update((const uint8_t*)it.second.c_str(), it.second.size(), sha_handle); - } - - sgx_sha256_get_hash(sha_handle, &hash); - sgx_sha256_close(sha_handle); - - // sig <- sign (hash,sk) - uint8_t sig[sizeof(sgx_ec256_signature_t)]; - sgx_ecc_state_handle_t ecc_handle = NULL; - sgx_ecc256_open_context(&ecc_handle); - ret = sgx_ecdsa_sign((uint8_t*)&hash, SGX_SHA256_HASH_SIZE, &enclave_sk, - (sgx_ec256_signature_t*)sig, ecc_handle); - sgx_ecc256_close_context(ecc_handle); - if (ret != SGX_SUCCESS) - { - LOG_ERROR("Signing failed!! Reason: %#08x", ret); - return ret; - } - LOG_DEBUG("Response signature created!"); - // convert signature to big endian and copy out - bytes_swap(sig, 32); - bytes_swap(sig + 32, 32); - //memcpy(signature, sig, sizeof(sgx_ec256_signature_t)); - - std::string base64_hash = base64_encode((const unsigned char*)hash, 32); - LOG_DEBUG("ecc sig hash (base64): %s", base64_hash.c_str()); - - std::string base64_sig = - base64_encode((const unsigned char*)sig, sizeof(sgx_ec256_signature_t)); - LOG_DEBUG("ecc sig sig (base64): %s", base64_sig.c_str()); - - std::string base64_pk = - base64_encode((const unsigned char*)&enclave_pk, sizeof(sgx_ec256_public_t)); - LOG_DEBUG("ecc sig pk (base64): %s", base64_pk.c_str()); - - return ret; -} -*/ -/* -// chaincode call processing when we have secure channel .. -int invoke_enc(const char* pk, - uint8_t* response, - uint32_t max_response_len, - uint32_t* actual_response_len, - shim_ctx_ptr_t ctx) -{ - LOG_DEBUG("Encrypted invocation"); - sgx_ec256_public_t client_pk = {0}; - - std::string _pk = base64_decode(pk); - uint8_t* pk_bytes = (uint8_t*)_pk.c_str(); - bytes_swap(pk_bytes, 32); - bytes_swap(pk_bytes + 32, 32); - memcpy(&client_pk, pk_bytes, sizeof(sgx_ec256_public_t)); - - sgx_ec256_dh_shared_t shared_dhkey; - - sgx_ecc_state_handle_t ecc_handle = NULL; - sgx_ecc256_open_context(&ecc_handle); - int sgx_ret = - sgx_ecc256_compute_shared_dhkey(&enclave_sk, &client_pk, &shared_dhkey, ecc_handle); - if (sgx_ret != SGX_SUCCESS) - { - LOG_ERROR("Compute shared dhkey: %d", sgx_ret); - return sgx_ret; - } - sgx_ecc256_close_context(ecc_handle); - bytes_swap(&shared_dhkey, 32); - - sgx_sha256_hash_t h; - sgx_sha256_msg( - (const uint8_t*)&shared_dhkey, sizeof(sgx_ec256_dh_shared_t), (sgx_sha256_hash_t*)&h); - - sgx_aes_gcm_128bit_key_t key; - memcpy(key, h, sizeof(sgx_aes_gcm_128bit_key_t)); - - std::string _cipher = base64_decode(ctx->encoded_args); - uint8_t* cipher = (uint8_t*)_cipher.c_str(); - int cipher_len = _cipher.size(); - - uint32_t needed_size = cipher_len - SGX_AESGCM_IV_SIZE - SGX_AESGCM_MAC_SIZE; - // need one byte more for string terminator - char plain[needed_size + 1]; - plain[needed_size] = '\0'; - - // decrypt - sgx_ret = sgx_rijndael128GCM_decrypt(&key, - cipher + SGX_AESGCM_IV_SIZE + SGX_AESGCM_MAC_SIZE, // cipher - needed_size, (uint8_t*)plain, // plain out - cipher, SGX_AESGCM_IV_SIZE, // nonce - NULL, 0, // aad - (sgx_aes_gcm_128bit_tag_t*)(cipher + SGX_AESGCM_IV_SIZE)); // tag - if (sgx_ret != SGX_SUCCESS) - { - LOG_ERROR("Decrypt error: %x", sgx_ret); - return sgx_ret; - } - LOG_DEBUG("invoke_enc: \tdecrypted args: %s", plain); - ctx->json_args = plain; - - return invoke(response, max_response_len, actual_response_len, ctx); -} -*/ - -#include "pdo/common/crypto/crypto.h" -bool crypto_created = false; +#include /* for memcpy_s etc */ -// chaincode call -// output, response <- F(args, input) -// signature <- sign (hash,sk) int ecall_cc_invoke(const uint8_t* signed_proposal_proto_bytes, uint32_t signed_proposal_proto_bytes_len, - const uint8_t* cc_request_message_proto, - uint32_t cc_request_message_proto_len, - uint8_t* cc_response_message_proto, - uint32_t cc_response_message_proto_len_in, - uint32_t* cc_response_message_proto_len_out, + const uint8_t* cc_request_message_bytes, + uint32_t cc_request_message_bytes_len, + uint8_t* signed_cc_response_message_bytes, + uint32_t signed_cc_response_message_bytes_len_in, + uint32_t* signed_cc_response_message_bytes_len_out, void* u_shim_ctx) { LOG_DEBUG("ecall_cc_invoke"); @@ -194,10 +36,12 @@ int ecall_cc_invoke(const uint8_t* signed_proposal_proto_bytes, t_shim_ctx_t ctx; int ret; // estimate max response len (take into account other fields and b64 encoding) - uint32_t response_len = cc_response_message_proto_len_in / 4 * 3 - 1024; - uint8_t response[cc_response_message_proto_len_in / 4 * 3]; + uint32_t response_len = signed_cc_response_message_bytes_len_in / 4 * 3 - 1024; + uint8_t response[signed_cc_response_message_bytes_len_in / 4 * 3]; uint32_t response_len_out = 0; std::string b64_response; + ByteArray cc_response_message; + size_t cc_response_message_estimated_size; ctx.u_shim_ctx = u_shim_ctx; @@ -206,7 +50,7 @@ int ecall_cc_invoke(const uint8_t* signed_proposal_proto_bytes, // set stream for ChaincodeRequestMessage istream = pb_istream_from_buffer( - (const unsigned char*)cc_request_message_proto, cc_request_message_proto_len); + (const unsigned char*)cc_request_message_bytes, cc_request_message_bytes_len); b = pb_decode(&istream, fpc_ChaincodeRequestMessage_fields, &cc_request_message); COND2LOGERR(!b, PB_GET_ERROR(&istream)); @@ -229,7 +73,7 @@ int ecall_cc_invoke(const uint8_t* signed_proposal_proto_bytes, cleartext_cc_request.input.args[i]->size)); } - // the dynamic memory in the message is release at the end + // the dynamic memory in the message is released at the end } ret = invoke(response, response_len, &response_len_out, &ctx); @@ -248,31 +92,104 @@ int ecall_cc_invoke(const uint8_t* signed_proposal_proto_bytes, // TODO: create fabric Response object // TODO: encrypt fabric Response object crm = {}; - crm.encrypted_response = (pb_bytes_array_t*)pb_realloc( - crm.encrypted_response, PB_BYTES_ARRAY_T_ALLOCSIZE(b64_response.length())); - COND2LOGERR(crm.encrypted_response == NULL, "cannot allocate encrypted message"); - crm.encrypted_response->size = b64_response.length(); - ret = memcpy_s(crm.encrypted_response->bytes, crm.encrypted_response->size, - b64_response.c_str(), b64_response.length()); - COND2LOGERR(ret != 0, "cannot encode field"); - // serialize enclave id - enclave_id = g_cc_data->get_enclave_id(); - crm.enclave_id = (char*)pb_realloc(crm.enclave_id, enclave_id.length() + 1); - ret = - memcpy_s(crm.enclave_id, enclave_id.length(), enclave_id.c_str(), enclave_id.length()); - crm.enclave_id[enclave_id.length()] = '\0'; - COND2LOGERR(ret != 0, "cannot encode enclave id"); + { // serialize encrypted response + crm.encrypted_response = (pb_bytes_array_t*)pb_realloc( + crm.encrypted_response, PB_BYTES_ARRAY_T_ALLOCSIZE(b64_response.length())); + COND2LOGERR(crm.encrypted_response == NULL, "cannot allocate encrypted message"); + crm.encrypted_response->size = b64_response.length(); + ret = memcpy_s(crm.encrypted_response->bytes, crm.encrypted_response->size, + b64_response.c_str(), b64_response.length()); + COND2LOGERR(ret != 0, "cannot encode field"); + } + + { // serialize enclave id + enclave_id = g_cc_data->get_enclave_id(); + crm.enclave_id = (char*)pb_realloc(crm.enclave_id, enclave_id.length() + 1); + ret = memcpy_s( + crm.enclave_id, enclave_id.length(), enclave_id.c_str(), enclave_id.length()); + crm.enclave_id[enclave_id.length()] = '\0'; + COND2LOGERR(ret != 0, "cannot encode enclave id"); + } + + { // serialize proposal + pb_istream_t istream; + + // set stream for ChaincodeRequestMessage + istream = pb_istream_from_buffer( + (const unsigned char*)signed_proposal_proto_bytes, signed_proposal_proto_bytes_len); + + b = pb_decode(&istream, protos_SignedProposal_fields, &crm.proposal); + COND2LOGERR(!b, PB_GET_ERROR(&istream)); + COND2LOGERR( + crm.proposal.proposal_bytes == NULL || crm.proposal.proposal_bytes->size == 0, + "zero size proposal"); + + crm.has_proposal = true; + } + + { // serialize rwset + rwset_to_proto(&ctx, &crm.fpc_rw_set); + } + + // estimate response message size + b = pb_get_encoded_size( + &cc_response_message_estimated_size, fpc_ChaincodeResponseMessage_fields, &crm); + COND2LOGERR(!b, "cannot estimate response message size"); // encode proto - ostream = - pb_ostream_from_buffer(cc_response_message_proto, cc_response_message_proto_len_in); + CATCH(b, cc_response_message.resize(cc_response_message_estimated_size)); + COND2LOGERR(!b, "cannot allocate response buffer"); + ostream = pb_ostream_from_buffer(cc_response_message.data(), cc_response_message.size()); b = pb_encode(&ostream, fpc_ChaincodeResponseMessage_fields, &crm); COND2LOGERR(!b, "error encoding proto"); + COND2LOGERR(ostream.bytes_written != cc_response_message_estimated_size, + "encoding size different than estimated"); pb_release(fpc_ChaincodeResponseMessage_fields, &crm); + } + + { + // create signed response message + pb_ostream_t ostream; + + // compute signature + ByteArray signature; + b = g_cc_data->sign_message(cc_response_message, signature); + COND2ERR(!b); + + // fill in protobuf structure + fpc_SignedChaincodeResponseMessage signed_crm = {}; + + // fill in response message + signed_crm.chaincode_response_message = (pb_bytes_array_t*)pb_realloc( + NULL, PB_BYTES_ARRAY_T_ALLOCSIZE(cc_response_message.size())); + COND2LOGERR( + signed_crm.chaincode_response_message == NULL, "cannot allocate response message"); + signed_crm.chaincode_response_message->size = cc_response_message.size(); + ret = memcpy_s(signed_crm.chaincode_response_message->bytes, + signed_crm.chaincode_response_message->size, cc_response_message.data(), + cc_response_message.size()); + COND2LOGERR(ret != 0, "cannot encode field"); + + // fill in signature + signed_crm.signature = + (pb_bytes_array_t*)pb_realloc(NULL, PB_BYTES_ARRAY_T_ALLOCSIZE(signature.size())); + COND2LOGERR(signed_crm.signature == NULL, "cannot allocate signature"); + signed_crm.signature->size = signature.size(); + ret = memcpy_s(signed_crm.signature->bytes, signed_crm.signature->size, signature.data(), + signature.size()); + COND2LOGERR(ret != 0, "cannot encode field"); + + // encode proto + ostream = pb_ostream_from_buffer( + signed_cc_response_message_bytes, signed_cc_response_message_bytes_len_in); + b = pb_encode(&ostream, fpc_SignedChaincodeResponseMessage_fields, &signed_crm); + COND2LOGERR(!b, "error encoding proto"); + + pb_release(fpc_SignedChaincodeResponseMessage_fields, &signed_crm); - *cc_response_message_proto_len_out = ostream.bytes_written; + *signed_cc_response_message_bytes_len_out = ostream.bytes_written; } // release dynamic allocations (TODO:release in case of error) @@ -284,6 +201,6 @@ int ecall_cc_invoke(const uint8_t* signed_proposal_proto_bytes, return 0; err: - *cc_response_message_proto_len_out = 0; - return SGX_ERROR_UNEXPECTED; + *signed_cc_response_message_bytes_len_out = 0; + return 1; } diff --git a/ecc_enclave/enclave/enclave.edl b/ecc_enclave/enclave/enclave.edl index db221b9b0..3b91db21d 100644 --- a/ecc_enclave/enclave/enclave.edl +++ b/ecc_enclave/enclave/enclave.edl @@ -13,9 +13,9 @@ enclave { trusted { public int ecall_cc_invoke( [in, size=signed_proposal_proto_bytes_len] const uint8_t *signed_proposal_proto_bytes, uint32_t signed_proposal_proto_bytes_len, - [in, size=cc_request_message_proto_len] const uint8_t *cc_request_message_proto, uint32_t cc_request_message_proto_len, - [out, size=cc_response_message_proto_len_in] uint8_t *cc_response_message_proto, uint32_t cc_response_message_proto_len_in, - [out] uint32_t *cc_response_message_proto_len_out, + [in, size=cc_request_message_bytes_len] const uint8_t *cc_request_message_bytes, uint32_t cc_request_message_bytes_len, + [out, size=signed_cc_response_message_bytes_len_in] uint8_t *signed_cc_response_message_bytes, uint32_t signed_cc_response_message_bytes_len_in, + [out] uint32_t *signed_cc_response_message_bytes_len_out, [user_check] void *u_shim_ctx); }; diff --git a/ecc_enclave/enclave/shim.cpp b/ecc_enclave/enclave/shim.cpp index cf80f35be..18eec7804 100644 --- a/ecc_enclave/enclave/shim.cpp +++ b/ecc_enclave/enclave/shim.cpp @@ -53,6 +53,7 @@ void get_state( get_public_state(key, encoded_cipher, sizeof(encoded_cipher), &encoded_cipher_len, ctx); // if nothing read, no need for decryption + // TODO: double check if this is an error in Fabric COND2LOGERR(encoded_cipher_len == 0, "no value read"); // if got value size larger than input array, report error @@ -64,6 +65,8 @@ void get_state( COND2LOGERR(encoded_cipher_len != encoded_cipher_s.size(), "Unexpected string length"); // base64 decode + // TODO: double check if Fabric handles values as strings; if it handles binary values, remove + // encoding cipher = base64_decode(encoded_cipher_s); COND2LOGERR(cipher.size() <= pdo::crypto::constants::IV_LEN + pdo::crypto::constants::TAG_LEN, "base64 decoding failed/produced too short a value"); @@ -90,9 +93,9 @@ void get_state( void get_public_state( const char* key, uint8_t* val, uint32_t max_val_len, uint32_t* val_len, shim_ctx_ptr_t ctx) { - // read state - ctx->read_set.insert(std::string(key)); + // TODO error checking and remove exceptions + // read state ocall_get_state(key, val, max_val_len, val_len, ctx->u_shim_ctx); if (*val_len > max_val_len) { @@ -103,17 +106,49 @@ void get_public_state( LOG_DEBUG("Enclave: got state for key=%s len=%d val='%s'", key, *val_len, (*val_len > 0 ? (std::string((const char*)val, *val_len)).c_str() : "")); + + // save in rw set: only save the first key/value-hash pair + std::string key_s(key); + ByteArray value_hash = pdo::crypto::ComputeMessageHash(ByteArray(val, val + *val_len)); + auto it = ctx->read_set.find(key_s); + if (it == ctx->read_set.end()) + { + // key not found, insert it + ctx->read_set.insert({std::string(key), value_hash}); + } + else + { + // key found, ensure value is the same, or fail + if (it->second != value_hash) + { + char s[] = "value read inconsistency"; + LOG_ERROR("%s", s); + throw std::runtime_error(s); + } + } } void put_state(const char* key, uint8_t* val, uint32_t val_len, shim_ctx_ptr_t ctx) { + // TODO error checking and remove exceptions + if (val_len == 0) + { + char s[] = "put state cannot accept zero length values"; + LOG_ERROR("%s", s); + throw std::runtime_error(s); + } + + ByteArray b = ByteArray(val, val + val_len); + ByteArray sek = g_cc_data->get_state_encryption_key(); + ByteArray encrypted_val; // encrypt try { - encrypted_val = pdo::crypto::skenc::EncryptMessage( - g_cc_data->get_state_encryption_key(), ByteArray(val, val + val_len)); + ByteArray value(val, val + val_len); + encrypted_val = + pdo::crypto::skenc::EncryptMessage(g_cc_data->get_state_encryption_key(), value); } catch (...) { @@ -130,8 +165,12 @@ void put_state(const char* key, uint8_t* val, uint32_t val_len, shim_ctx_ptr_t c void put_public_state(const char* key, uint8_t* val, uint32_t val_len, shim_ctx_ptr_t ctx) { - std::string s((const char*)val, val_len); - ctx->write_set.insert({key, s}); + // TODO error checking, ensure write gets to fabric + + // save write -- only the last one for the same key + ctx->write_set.erase(key); + ctx->write_set.insert({key, ByteArray(val, val + val_len)}); + ocall_put_state(key, val, val_len, ctx->u_shim_ctx); } diff --git a/ecc_enclave/enclave/shim_internals.cpp b/ecc_enclave/enclave/shim_internals.cpp new file mode 100644 index 000000000..eac33bf05 --- /dev/null +++ b/ecc_enclave/enclave/shim_internals.cpp @@ -0,0 +1,114 @@ +/* + * Copyright 2020 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "shim_internals.h" +#include /* for memcpy_s etc */ +#include "error.h" +#include "logging.h" + +/* + * rwset_to_proto encodes the rwset in a nanopb/protobuf data structure. + * Strings and binary values are allocated dynamically here. + * The caller owns the structure and is responsible to free the memory through pb_release. + */ +bool rwset_to_proto(t_shim_ctx_t* ctx, fpc_FPCKVSet* fpc_rwset_proto) +{ + int ret; + unsigned int i; + + COND2LOGERR(ctx == NULL || fpc_rwset_proto == NULL, "invalid input parameters"); + + LOG_DEBUG("Prepare rwset serialization"); + + // reset structure + *fpc_rwset_proto = fpc_FPCKVSet_init_default; + + // initialize read sets (i.e., the arrays; later we serialize single items) + fpc_rwset_proto->has_rw_set = true; + fpc_rwset_proto->read_value_hashes_count = ctx->read_set.size(); + fpc_rwset_proto->read_value_hashes = (pb_bytes_array_t**)pb_realloc( + NULL, fpc_rwset_proto->read_value_hashes_count * sizeof(pb_bytes_array_t*)); + COND2ERR(fpc_rwset_proto->read_value_hashes == NULL); + + fpc_rwset_proto->rw_set.reads_count = ctx->read_set.size(); + fpc_rwset_proto->rw_set.reads = (kvrwset_KVRead*)pb_realloc( + NULL, fpc_rwset_proto->rw_set.reads_count * sizeof(kvrwset_KVRead)); + COND2ERR(fpc_rwset_proto->rw_set.reads == NULL); + + // initialiaze write sets (i.e., the arrays; later we serialize single items) + fpc_rwset_proto->rw_set.writes_count = ctx->write_set.size(); + fpc_rwset_proto->rw_set.writes = (kvrwset_KVWrite*)pb_realloc( + NULL, fpc_rwset_proto->rw_set.writes_count * sizeof(kvrwset_KVWrite)); + COND2ERR(fpc_rwset_proto->rw_set.writes == NULL); + + LOG_DEBUG("Serializing read set items"); + i = 0; + for (auto it = ctx->read_set.begin(); it != ctx->read_set.end(); it++, i++) + { + LOG_DEBUG("k=%s , v(hex)=%s , v(len)=%d", it->first.c_str(), + ByteArrayToHexEncodedString(it->second).c_str(), it->second.size()); + + // serialize hash + fpc_rwset_proto->read_value_hashes[i] = + (pb_bytes_array_t*)pb_realloc(NULL, PB_BYTES_ARRAY_T_ALLOCSIZE(it->second.size())); + LOG_DEBUG("checkcond"); + COND2ERR(fpc_rwset_proto->read_value_hashes[i] == NULL); + LOG_DEBUG("size"); + fpc_rwset_proto->read_value_hashes[i]->size = it->second.size(); + LOG_DEBUG("mem"); + ret = memcpy_s(fpc_rwset_proto->read_value_hashes[i]->bytes, + fpc_rwset_proto->read_value_hashes[i]->size, it->second.data(), it->second.size()); + LOG_DEBUG("checkcond"); + COND2ERR(ret != 0); + + // serialize read + fpc_rwset_proto->rw_set.reads[i].has_version = false; + fpc_rwset_proto->rw_set.reads[i].key = (char*)pb_realloc(NULL, it->first.length() + 1); + LOG_DEBUG("checkcond"); + COND2ERR(fpc_rwset_proto->rw_set.reads[i].key == NULL); + LOG_DEBUG("mem"); + ret = memcpy_s(fpc_rwset_proto->rw_set.reads[i].key, it->first.length(), it->first.c_str(), + it->first.length()); + LOG_DEBUG("zero"); + fpc_rwset_proto->rw_set.reads[i].key[it->first.length()] = '\0'; + LOG_DEBUG("checkcond"); + COND2ERR(ret != 0); + } + + LOG_DEBUG("Serializing write set items"); + i = 0; + for (auto it = ctx->write_set.begin(); it != ctx->write_set.end(); it++, i++) + { + LOG_DEBUG( + "k=%s , v(hex)=%s", it->first.c_str(), ByteArrayToHexEncodedString(it->second).c_str()); + + // serialize write + fpc_rwset_proto->rw_set.writes[i].is_delete = false; + + // serialize key + fpc_rwset_proto->rw_set.writes[i].key = (char*)pb_realloc(NULL, it->first.length() + 1); + COND2ERR(fpc_rwset_proto->rw_set.writes[i].key == NULL); + ret = memcpy_s(fpc_rwset_proto->rw_set.writes[i].key, it->first.length(), it->first.c_str(), + it->first.length()); + fpc_rwset_proto->rw_set.writes[i].key[it->first.length()] = '\0'; + COND2ERR(ret != 0); + + // serialize value + fpc_rwset_proto->rw_set.writes[i].value = + (pb_bytes_array_t*)pb_realloc(NULL, it->second.size()); + COND2ERR(fpc_rwset_proto->rw_set.writes[i].value == NULL); + fpc_rwset_proto->rw_set.writes[i].value->size = it->second.size(); + ret = memcpy_s(fpc_rwset_proto->rw_set.writes[i].value->bytes, + fpc_rwset_proto->rw_set.writes[i].value->size, it->second.data(), it->second.size()); + COND2ERR(ret != 0); + } + + LOG_DEBUG("Serialization successful"); + return true; + +err: + return false; +} diff --git a/ecc_enclave/enclave/shim_internals.h b/ecc_enclave/enclave/shim_internals.h index 631860a54..d9f0f08be 100644 --- a/ecc_enclave/enclave/shim_internals.h +++ b/ecc_enclave/enclave/shim_internals.h @@ -11,10 +11,11 @@ #include #include #include +#include "types.h" // read/writeset -typedef std::map write_set_t; -typedef std::set read_set_t; +typedef std::map write_set_t; +typedef std::map read_set_t; // shim context typedef struct t_shim_ctx @@ -24,3 +25,7 @@ typedef struct t_shim_ctx write_set_t write_set; std::vector string_args; } t_shim_ctx_t; + +#include "fpc.pb.h" + +bool rwset_to_proto(t_shim_ctx_t* ctx, fpc_FPCKVSet* fpc_rwset_proto); diff --git a/internal/utils/validation.go b/internal/utils/validation.go index 19b1ca741..db45fa02a 100644 --- a/internal/utils/validation.go +++ b/internal/utils/validation.go @@ -8,95 +8,119 @@ SPDX-License-Identifier: Apache-2.0 package utils import ( - "crypto/ecdsa" - "crypto/x509" + "bytes" + "crypto/sha256" + "encoding/hex" "fmt" - "sort" - "github.com/hyperledger-labs/fabric-private-chaincode/internal/protos" "github.com/hyperledger/fabric-chaincode-go/shim" - "github.com/hyperledger/fabric-protos-go/ledger/rwset/kvrwset" + "github.com/hyperledger/fabric/common/flogging" ) -func ReplayReadWrites(stub shim.ChaincodeStubInterface, rwset *kvrwset.KVRWSet) (readset [][]byte, writeset [][]byte, err error) { +// #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 +// #include +// #include +// #include +// #include "pdo-crypto-c-wrapper.h" +import "C" + +var logger = flogging.MustGetLogger("validate") + +func ReplayReadWrites(stub shim.ChaincodeStubInterface, fpcrwset *protos.FPCKVSet) (err error) { //TODO error checking // nil rwset => nothing to do + if fpcrwset == nil { + return nil + } + + rwset := fpcrwset.GetRwSet() if rwset == nil { - return nil, nil, nil + return fmt.Errorf("no rwset found") } // normal reads - var readKeys []string - readsetMap := make(map[string][]byte) - for _, r := range rwset.Reads { - k := TransformToFPCKey(r.Key) - readKeys = append(readKeys, k) - v, _ := stub.GetState(k) - readsetMap[k] = v + if rwset.GetReads() != nil { + if fpcrwset.GetReadValueHashes() == nil { + return fmt.Errorf("no read value hash associated to reads") + } + if len(fpcrwset.ReadValueHashes) != len(rwset.Reads) { + return fmt.Errorf("%d read value hashes but %d reads", len(fpcrwset.ReadValueHashes), len(rwset.Reads)) + } + for i := 0; i < len(rwset.Reads); i++ { + k := TransformToFPCKey(rwset.Reads[i].Key) + v, err := stub.GetState(k) + if err != nil { + return fmt.Errorf("value not found reading key %s", k) + } + + // compute value hash + h := sha256.New() + h.Write(v) + valueHash := h.Sum(nil) + + // check hashes + if !bytes.Equal(valueHash, fpcrwset.ReadValueHashes[i]) { + logger.Debugf("value(hex): %s", hex.EncodeToString(v)) + logger.Debugf("computed hash(hex): %s", hex.EncodeToString(valueHash)) + logger.Debugf("received hash(hex): %s", hex.EncodeToString(fpcrwset.ReadValueHashes[i])) + return fmt.Errorf("value hash mismatch for key %s", k) + } + } } // range query reads - for _, rqi := range rwset.RangeQueriesInfo { - if rqi.GetRawReads() == nil { - // no raw reads available in this range query - continue - } - for _, qr := range rqi.GetRawReads().KvReads { - k := TransformToFPCKey(qr.Key) - readKeys = append(readKeys, k) - v, _ := stub.GetState(k) - readsetMap[k] = v + if rwset.GetRangeQueriesInfo() != nil { + for _, rqi := range rwset.RangeQueriesInfo { + if rqi.GetRawReads() == nil { + // no raw reads available in this range query + continue + } + for _, qr := range rqi.GetRawReads().KvReads { + k := TransformToFPCKey(qr.Key) + v, err := stub.GetState(k) + if err != nil { + return fmt.Errorf("value not found reading key %s", k) + } + + _ = v + return fmt.Errorf("TODO: not implemented, missing hash check") + } } } // writes - var writeKeys []string - writesetMap := make(map[string][]byte) - for _, w := range rwset.Writes { - k := TransformToFPCKey(w.Key) - writeKeys = append(writeKeys, k) - writesetMap[k] = w.Value - _ = stub.PutState(k, w.Value) - } - - // sort readset and writeset as enclave uses a sorted map - sort.Strings(readKeys) - sort.Strings(writeKeys) - - // prepare sorted read/write set as output - for _, k := range readKeys { - readset = append(readset, []byte(k)) - readset = append(readset, readsetMap[k]) - } - - for _, k := range writeKeys { - writeset = append(writeset, []byte(k)) - writeset = append(writeset, writesetMap[k]) + if rwset.GetWrites() != nil { + for _, w := range rwset.Writes { + k := TransformToFPCKey(w.Key) + _ = stub.PutState(k, w.Value) + } } - return readset, writeset, nil + return nil } -func Validate(responseMsg *protos.ChaincodeResponseMessage, readset, writeset [][]byte, attestedData *protos.AttestedData) error { - if responseMsg.Signature == nil { +func Validate(signedResponseMessage *protos.SignedChaincodeResponseMessage, attestedData *protos.AttestedData) error { + if signedResponseMessage.Signature == nil { return fmt.Errorf("absent enclave signature") } - // Note: below signature was created in ecc_enclave/enclave/enclave.cpp::gen_response - hash := ComputedHash(responseMsg, readset, writeset) + // prepare and do signature verification + enclaveVkPtr := C.CBytes(attestedData.EnclaveVk) + defer C.free(enclaveVkPtr) - // perform enclave signature validation - // TODO refactor this to function - pub, err := x509.ParsePKIXPublicKey(attestedData.EnclaveVk) - if err != nil { - return err - } + responseMessagePtr := C.CBytes(signedResponseMessage.ChaincodeResponseMessage) + defer C.free(responseMessagePtr) + + signaturePtr := C.CBytes(signedResponseMessage.Signature) + defer C.free(signaturePtr) - valid := ecdsa.VerifyASN1(pub.(*ecdsa.PublicKey), hash[:], responseMsg.Signature) - if !valid { - return fmt.Errorf("signature invalid") + ret := C.verify_signature((*C.uint8_t)(enclaveVkPtr), C.uint32_t(len(attestedData.EnclaveVk)), (*C.uint8_t)(responseMessagePtr), C.uint32_t(len(signedResponseMessage.ChaincodeResponseMessage)), (*C.uint8_t)(signaturePtr), C.uint32_t(len(signedResponseMessage.Signature))) + if ret == false { + return fmt.Errorf("enclave signature verification failed") } return nil diff --git a/protos/fabric.options b/protos/fabric.options index efcf974a3..d59e5a1f9 100644 --- a/protos/fabric.options +++ b/protos/fabric.options @@ -86,6 +86,9 @@ protos.ProposalResponsePayload.extension type:FT_POINTER protos.Response.message type:FT_POINTER protos.Response.payload type:FT_POINTER +protos.SignedProposal.proposal_bytes type:FT_POINTER +protos.SignedProposal.signature type:FT_POINTER + protos.ApplicationPolicy.channel_config_policy_reference type:FT_POINTER rwset.TxReadWriteSet.ns_rwset type:FT_POINTER diff --git a/protos/fpc.options b/protos/fpc.options index e2f112a5d..1ffed2feb 100644 --- a/protos/fpc.options +++ b/protos/fpc.options @@ -4,7 +4,11 @@ fpc.CleartextChaincodeRequest.return_encryption_key type:FT_POINTER fpc.ChaincodeRequestMessage.encrypted_request type:FT_POINTER +fpc.FPCKVSet.read_value_hashes type:FT_POINTER + fpc.ChaincodeResponseMessage.encrypted_response type:FT_POINTER fpc.ChaincodeResponseMessage.signature type:FT_POINTER fpc.ChaincodeResponseMessage.enclave_id type:FT_POINTER +fpc.SignedChaincodeResponseMessage.chaincode_response_message type:FT_POINTER +fpc.SignedChaincodeResponseMessage.signature type:FT_POINTER diff --git a/protos/fpc/fpc.proto b/protos/fpc/fpc.proto index 325127345..e46b73efa 100644 --- a/protos/fpc/fpc.proto +++ b/protos/fpc/fpc.proto @@ -108,20 +108,32 @@ message CleartextChaincodeResponse { protos.Response response = 1; } +// FPCKVSet augments the Fabric kvrwset.KVRWSet protobuf to include the hash of the value of each read. +// Specifically, read_value_hashes[i] is the hash of the value associated to rw_set.reads[i].key +message FPCKVSet { + kvrwset.KVRWSet rw_set = 1; + repeated bytes read_value_hashes = 2; +} + message ChaincodeResponseMessage { // an aes128-gcm encryption of the serialization of CleartextChaincodeResponse bytes encrypted_response = 1; // R/W set (of cleartext keys but encrypted values) // This field is only valid for the FPC Lite variant but absent from the full version with in-peer FPC validation - kvrwset.KVRWSet rw_set = 2; + FPCKVSet fpc_rw_set = 2; // signed proposal for this request protos.SignedProposal proposal = 3; - // signature over proposal, cleartext response, (hash of) R/W set - bytes signature = 4; - // identity for public key used to sign - string enclave_id = 5; + string enclave_id = 4; +} + +message SignedChaincodeResponseMessage { + // binary encoding of a ChaincodeResponseMessage protobuf + bytes chaincode_response_message = 1; + + // signature over the chaincode response message + bytes signature = 2; } From 7ce70f72205b285382ff4f54872db09af3c67e0f Mon Sep 17 00:00:00 2001 From: Bruno Vavala Date: Fri, 18 Dec 2020 01:05:35 -0800 Subject: [PATCH 2/7] fix bugs, auction partially working (inconsistencies and missing support for composite-key routines) Signed-off-by: Bruno Vavala --- Makefile | 2 +- ecc_enclave/enclave/enclave.cpp | 10 ++++++++-- ecc_enclave/enclave/shim.cpp | 7 +++++-- ecc_enclave/enclave/shim_internals.cpp | 8 -------- internal/utils/validation.go | 16 +++++++++++++--- 5 files changed, 27 insertions(+), 16 deletions(-) diff --git a/Makefile b/Makefile index 8e05c6183..43753d589 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ TOP = . include $(TOP)/build.mk -SUB_DIRS = protos common internal ercc ecc_enclave ecc fabric client_sdk examples integration # docs +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_PEER_DEP_DIRS = protos common ercc fabric ecc_enclave ecc diff --git a/ecc_enclave/enclave/enclave.cpp b/ecc_enclave/enclave/enclave.cpp index 0a5bec456..e17a7bc7b 100644 --- a/ecc_enclave/enclave/enclave.cpp +++ b/ecc_enclave/enclave/enclave.cpp @@ -35,6 +35,7 @@ int ecall_cc_invoke(const uint8_t* signed_proposal_proto_bytes, fpc_CleartextChaincodeRequest cleartext_cc_request = {}; t_shim_ctx_t ctx; int ret; + int invoke_ret; // estimate max response len (take into account other fields and b64 encoding) uint32_t response_len = signed_cc_response_message_bytes_len_in / 4 * 3 - 1024; uint8_t response[signed_cc_response_message_bytes_len_in / 4 * 3]; @@ -76,8 +77,12 @@ int ecall_cc_invoke(const uint8_t* signed_proposal_proto_bytes, // the dynamic memory in the message is released at the end } - ret = invoke(response, response_len, &response_len_out, &ctx); - COND2ERR(ret != 0); + invoke_ret = invoke(response, response_len, &response_len_out, &ctx); + //invoke_ret is not checked + + // TODO double check or rethink if it is appropriate for a chaincode + // to return an error and still forward the response + // in particular: should the enclave sign a response? and the rwset? could the tx be committed though it failed? b64_response = base64_encode((const unsigned char*)response, response_len_out); @@ -129,6 +134,7 @@ int ecall_cc_invoke(const uint8_t* signed_proposal_proto_bytes, } { // serialize rwset + crm.has_fpc_rw_set = true; rwset_to_proto(&ctx, &crm.fpc_rw_set); } diff --git a/ecc_enclave/enclave/shim.cpp b/ecc_enclave/enclave/shim.cpp index 18eec7804..f3d61772c 100644 --- a/ecc_enclave/enclave/shim.cpp +++ b/ecc_enclave/enclave/shim.cpp @@ -53,8 +53,11 @@ void get_state( get_public_state(key, encoded_cipher, sizeof(encoded_cipher), &encoded_cipher_len, ctx); // if nothing read, no need for decryption - // TODO: double check if this is an error in Fabric - COND2LOGERR(encoded_cipher_len == 0, "no value read"); + if(encoded_cipher_len == 0) + { + *val_len = 0; + return; + } // if got value size larger than input array, report error COND2LOGERR(encoded_cipher_len > sizeof(encoded_cipher), diff --git a/ecc_enclave/enclave/shim_internals.cpp b/ecc_enclave/enclave/shim_internals.cpp index eac33bf05..6a35c7af0 100644 --- a/ecc_enclave/enclave/shim_internals.cpp +++ b/ecc_enclave/enclave/shim_internals.cpp @@ -54,27 +54,19 @@ bool rwset_to_proto(t_shim_ctx_t* ctx, fpc_FPCKVSet* fpc_rwset_proto) // serialize hash fpc_rwset_proto->read_value_hashes[i] = (pb_bytes_array_t*)pb_realloc(NULL, PB_BYTES_ARRAY_T_ALLOCSIZE(it->second.size())); - LOG_DEBUG("checkcond"); COND2ERR(fpc_rwset_proto->read_value_hashes[i] == NULL); - LOG_DEBUG("size"); fpc_rwset_proto->read_value_hashes[i]->size = it->second.size(); - LOG_DEBUG("mem"); ret = memcpy_s(fpc_rwset_proto->read_value_hashes[i]->bytes, fpc_rwset_proto->read_value_hashes[i]->size, it->second.data(), it->second.size()); - LOG_DEBUG("checkcond"); COND2ERR(ret != 0); // serialize read fpc_rwset_proto->rw_set.reads[i].has_version = false; fpc_rwset_proto->rw_set.reads[i].key = (char*)pb_realloc(NULL, it->first.length() + 1); - LOG_DEBUG("checkcond"); COND2ERR(fpc_rwset_proto->rw_set.reads[i].key == NULL); - LOG_DEBUG("mem"); ret = memcpy_s(fpc_rwset_proto->rw_set.reads[i].key, it->first.length(), it->first.c_str(), it->first.length()); - LOG_DEBUG("zero"); fpc_rwset_proto->rw_set.reads[i].key[it->first.length()] = '\0'; - LOG_DEBUG("checkcond"); COND2ERR(ret != 0); } diff --git a/internal/utils/validation.go b/internal/utils/validation.go index db45fa02a..43240ce36 100644 --- a/internal/utils/validation.go +++ b/internal/utils/validation.go @@ -43,6 +43,7 @@ func ReplayReadWrites(stub shim.ChaincodeStubInterface, fpcrwset *protos.FPCKVSe // normal reads if rwset.GetReads() != nil { + logger.Debugf("Replaying reads") if fpcrwset.GetReadValueHashes() == nil { return fmt.Errorf("no read value hash associated to reads") } @@ -54,9 +55,11 @@ func ReplayReadWrites(stub shim.ChaincodeStubInterface, fpcrwset *protos.FPCKVSe k := TransformToFPCKey(rwset.Reads[i].Key) v, err := stub.GetState(k) if err != nil { - return fmt.Errorf("value not found reading key %s", k) + return fmt.Errorf("error (%s) reading key %s", err, k) } + logger.Debugf("read key %s value(hex) %s", k, hex.EncodeToString(v)) + // compute value hash h := sha256.New() h.Write(v) @@ -74,6 +77,7 @@ func ReplayReadWrites(stub shim.ChaincodeStubInterface, fpcrwset *protos.FPCKVSe // range query reads if rwset.GetRangeQueriesInfo() != nil { + logger.Debugf("Replaying range queries") for _, rqi := range rwset.RangeQueriesInfo { if rqi.GetRawReads() == nil { // no raw reads available in this range query @@ -83,7 +87,7 @@ func ReplayReadWrites(stub shim.ChaincodeStubInterface, fpcrwset *protos.FPCKVSe k := TransformToFPCKey(qr.Key) v, err := stub.GetState(k) if err != nil { - return fmt.Errorf("value not found reading key %s", k) + return fmt.Errorf("error (%s) reading key %s", err, k) } _ = v @@ -94,9 +98,15 @@ func ReplayReadWrites(stub shim.ChaincodeStubInterface, fpcrwset *protos.FPCKVSe // writes if rwset.GetWrites() != nil { + logger.Debugf("Replaying writes") for _, w := range rwset.Writes { k := TransformToFPCKey(w.Key) - _ = stub.PutState(k, w.Value) + err := stub.PutState(k, w.Value) + if err != nil { + return fmt.Errorf("error (%s) writing key %s value(hex) %s", err, k, hex.EncodeToString(w.Value)) + } + + logger.Debugf("written key %s value(hex) %s", k, hex.EncodeToString(w.Value)) } } From 26ba7a26cc964d5f1276f720af715ee5a931a78d Mon Sep 17 00:00:00 2001 From: Bruno Vavala Date: Fri, 18 Dec 2020 02:04:02 -0800 Subject: [PATCH 3/7] add wait on invoke __endorse; auction test fixed Signed-off-by: Bruno Vavala --- ecc_enclave/enclave/enclave.cpp | 5 +++-- ecc_enclave/enclave/shim.cpp | 8 ++++++-- fabric/bin/peer.sh | 2 +- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/ecc_enclave/enclave/enclave.cpp b/ecc_enclave/enclave/enclave.cpp index e17a7bc7b..3102d9dba 100644 --- a/ecc_enclave/enclave/enclave.cpp +++ b/ecc_enclave/enclave/enclave.cpp @@ -78,11 +78,12 @@ int ecall_cc_invoke(const uint8_t* signed_proposal_proto_bytes, } invoke_ret = invoke(response, response_len, &response_len_out, &ctx); - //invoke_ret is not checked + // invoke_ret is not checked // TODO double check or rethink if it is appropriate for a chaincode // to return an error and still forward the response - // in particular: should the enclave sign a response? and the rwset? could the tx be committed though it failed? + // in particular: should the enclave sign a response? and the rwset? could the tx be committed + // though it failed? b64_response = base64_encode((const unsigned char*)response, response_len_out); diff --git a/ecc_enclave/enclave/shim.cpp b/ecc_enclave/enclave/shim.cpp index f3d61772c..512c71baf 100644 --- a/ecc_enclave/enclave/shim.cpp +++ b/ecc_enclave/enclave/shim.cpp @@ -53,7 +53,7 @@ void get_state( get_public_state(key, encoded_cipher, sizeof(encoded_cipher), &encoded_cipher_len, ctx); // if nothing read, no need for decryption - if(encoded_cipher_len == 0) + if (encoded_cipher_len == 0) { *val_len = 0; return; @@ -188,6 +188,8 @@ int unmarshal_values( } JSON_Array* pairs = json_value_get_array(root); + COND2ERR(pairs == NULL); + for (int i = 0; i < json_array_get_count(pairs); i++) { JSON_Object* pair = json_array_get_object(pairs, i); @@ -197,13 +199,15 @@ int unmarshal_values( } json_value_free(root); return 1; + +err: + return -1; } void get_state_by_partial_composite_key( const char* comp_key, std::map& values, shim_ctx_ptr_t ctx) { get_public_state_by_partial_composite_key(comp_key, values, ctx); - for (auto& u : values) { // base64 decode diff --git a/fabric/bin/peer.sh b/fabric/bin/peer.sh index 096fadea1..3ee9ce11a 100755 --- a/fabric/bin/peer.sh +++ b/fabric/bin/peer.sh @@ -666,7 +666,7 @@ handle_chaincode_call() { if [ ! -z ${DO_WAIT_TIMEOUT+x} ]; then opt_arg="--waitForEvent ${opt_arg}" fi - try_r $RUN ${FABRIC_BIN_DIR}/peer chaincode invoke "${OTHER_ARGS[@]}" ${opt_arg} -C ${CHAN_ID} -n ${CC_ID} -c '{"Args":["__endorse", "'${encrypted_response}'"]}' + try_r $RUN ${FABRIC_BIN_DIR}/peer chaincode invoke "${OTHER_ARGS[@]}" ${opt_arg} -C ${CHAN_ID} -n ${CC_ID} -c '{"Args":["__endorse", "'${encrypted_response}'"]}' --waitForEvent endorse_response=$(parse_invoke_result_from_log "${RESPONSE}") [ "${endorse_response}" == "OK" ] || die "endorsement failed: '${endorse_response}'" [ -z ${DEBUG+x} ] || say "endorsement returned '${endorse_response}'" From d46f309c224a46acdc2c1c4ed54ff8c951be42d4 Mon Sep 17 00:00:00 2001 From: Bruno Vavala Date: Fri, 18 Dec 2020 12:08:08 -0800 Subject: [PATCH 4/7] fix handling of composite key writes Signed-off-by: Bruno Vavala --- internal/utils/validation.go | 7 +++++++ protos/fpc.options | 1 - 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/internal/utils/validation.go b/internal/utils/validation.go index 43240ce36..e5626f449 100644 --- a/internal/utils/validation.go +++ b/internal/utils/validation.go @@ -101,6 +101,13 @@ func ReplayReadWrites(stub shim.ChaincodeStubInterface, fpcrwset *protos.FPCKVSe logger.Debugf("Replaying writes") for _, w := range rwset.Writes { k := TransformToFPCKey(w.Key) + + // check if composite key, if so, derive Fabric key + if IsFPCCompositeKey(k) { + comp := SplitFPCCompositeKey(k) + k, _ = stub.CreateCompositeKey(comp[0], comp[1:]) + } + err := stub.PutState(k, w.Value) if err != nil { return fmt.Errorf("error (%s) writing key %s value(hex) %s", err, k, hex.EncodeToString(w.Value)) diff --git a/protos/fpc.options b/protos/fpc.options index 1ffed2feb..a790e65fb 100644 --- a/protos/fpc.options +++ b/protos/fpc.options @@ -7,7 +7,6 @@ fpc.ChaincodeRequestMessage.encrypted_request type:FT_POINTER fpc.FPCKVSet.read_value_hashes type:FT_POINTER fpc.ChaincodeResponseMessage.encrypted_response type:FT_POINTER -fpc.ChaincodeResponseMessage.signature type:FT_POINTER fpc.ChaincodeResponseMessage.enclave_id type:FT_POINTER fpc.SignedChaincodeResponseMessage.chaincode_response_message type:FT_POINTER From 4df41e6a4f2e3e9f344a699c63b5c2b31e74d514 Mon Sep 17 00:00:00 2001 From: Bruno Vavala Date: Fri, 18 Dec 2020 12:21:26 -0800 Subject: [PATCH 5/7] fix compile order issue Signed-off-by: Bruno Vavala --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 43753d589..a7539750c 100644 --- a/Makefile +++ b/Makefile @@ -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 From 92afe5cea9a3ee32ec962f01ff92c51fad48068c Mon Sep 17 00:00:00 2001 From: Bruno Vavala Date: Fri, 18 Dec 2020 12:46:57 -0800 Subject: [PATCH 6/7] fix include bug Signed-off-by: Bruno Vavala --- common/crypto/pdo-crypto-c-wrapper.cpp | 1 + common/error.h | 4 ---- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/common/crypto/pdo-crypto-c-wrapper.cpp b/common/crypto/pdo-crypto-c-wrapper.cpp index af0f38818..4dbe06c85 100644 --- a/common/crypto/pdo-crypto-c-wrapper.cpp +++ b/common/crypto/pdo-crypto-c-wrapper.cpp @@ -6,6 +6,7 @@ #include #include "crypto.h" +#include "logging.h" #include "error.h" #include "types.h" diff --git a/common/error.h b/common/error.h index 7e26ce2f3..27f357df4 100644 --- a/common/error.h +++ b/common/error.h @@ -6,10 +6,6 @@ #pragma once -#ifndef LOG_DEBUG -#define LOG_DEBUG -#endif - #define COND2ERR(b) \ do \ { \ From 4138a9f261b7714bf57b96511bdcdb66c592f15e Mon Sep 17 00:00:00 2001 From: Bruno Vavala Date: Fri, 18 Dec 2020 13:51:19 -0800 Subject: [PATCH 7/7] comments and renaming Signed-off-by: Bruno Vavala --- ecc/chaincode/ecc.go | 10 +++++----- ecc/chaincode/enclave/enclave.go | 2 ++ ecc_enclave/enclave/enclave.cpp | 8 ++++---- ecc_enclave/enclave/shim.cpp | 8 ++++++++ fabric/bin/peer.sh | 2 +- internal/utils/validation.go | 1 + 6 files changed, 21 insertions(+), 10 deletions(-) diff --git a/ecc/chaincode/ecc.go b/ecc/chaincode/ecc.go index c56cdbd3e..ec03aeeed 100644 --- a/ecc/chaincode/ecc.go +++ b/ecc/chaincode/ecc.go @@ -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, } } diff --git a/ecc/chaincode/enclave/enclave.go b/ecc/chaincode/enclave/enclave.go index fc82196a5..0b7b24272 100644 --- a/ecc/chaincode/enclave/enclave.go +++ b/ecc/chaincode/enclave/enclave.go @@ -41,6 +41,7 @@ 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 { @@ -106,6 +107,7 @@ 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 { diff --git a/ecc_enclave/enclave/enclave.cpp b/ecc_enclave/enclave/enclave.cpp index 3102d9dba..08cf8867f 100644 --- a/ecc_enclave/enclave/enclave.cpp +++ b/ecc_enclave/enclave/enclave.cpp @@ -99,7 +99,7 @@ int ecall_cc_invoke(const uint8_t* signed_proposal_proto_bytes, // TODO: encrypt fabric Response object crm = {}; - { // serialize encrypted response + { // fill encrypted response crm.encrypted_response = (pb_bytes_array_t*)pb_realloc( crm.encrypted_response, PB_BYTES_ARRAY_T_ALLOCSIZE(b64_response.length())); COND2LOGERR(crm.encrypted_response == NULL, "cannot allocate encrypted message"); @@ -109,7 +109,7 @@ int ecall_cc_invoke(const uint8_t* signed_proposal_proto_bytes, COND2LOGERR(ret != 0, "cannot encode field"); } - { // serialize enclave id + { // fill enclave id enclave_id = g_cc_data->get_enclave_id(); crm.enclave_id = (char*)pb_realloc(crm.enclave_id, enclave_id.length() + 1); ret = memcpy_s( @@ -118,7 +118,7 @@ int ecall_cc_invoke(const uint8_t* signed_proposal_proto_bytes, COND2LOGERR(ret != 0, "cannot encode enclave id"); } - { // serialize proposal + { // fill proposal pb_istream_t istream; // set stream for ChaincodeRequestMessage @@ -134,7 +134,7 @@ int ecall_cc_invoke(const uint8_t* signed_proposal_proto_bytes, crm.has_proposal = true; } - { // serialize rwset + { // fill rwset crm.has_fpc_rw_set = true; rwset_to_proto(&ctx, &crm.fpc_rw_set); } diff --git a/ecc_enclave/enclave/shim.cpp b/ecc_enclave/enclave/shim.cpp index 512c71baf..70c6e68b5 100644 --- a/ecc_enclave/enclave/shim.cpp +++ b/ecc_enclave/enclave/shim.cpp @@ -128,6 +128,14 @@ void get_public_state( LOG_ERROR("%s", s); throw std::runtime_error(s); } + + // IMPORTANT: + // * the read set stores the first key that is read; + // * subsequent reads of this key must return the same value, **no matter any local + // update/write** + // * hence, if a read key is updated later, the get_state must return the original + // (presumably committed) value + // TODO: double check consistency levels with fabric maintainers } } diff --git a/fabric/bin/peer.sh b/fabric/bin/peer.sh index 3ee9ce11a..096fadea1 100755 --- a/fabric/bin/peer.sh +++ b/fabric/bin/peer.sh @@ -666,7 +666,7 @@ handle_chaincode_call() { if [ ! -z ${DO_WAIT_TIMEOUT+x} ]; then opt_arg="--waitForEvent ${opt_arg}" fi - try_r $RUN ${FABRIC_BIN_DIR}/peer chaincode invoke "${OTHER_ARGS[@]}" ${opt_arg} -C ${CHAN_ID} -n ${CC_ID} -c '{"Args":["__endorse", "'${encrypted_response}'"]}' --waitForEvent + try_r $RUN ${FABRIC_BIN_DIR}/peer chaincode invoke "${OTHER_ARGS[@]}" ${opt_arg} -C ${CHAN_ID} -n ${CC_ID} -c '{"Args":["__endorse", "'${encrypted_response}'"]}' endorse_response=$(parse_invoke_result_from_log "${RESPONSE}") [ "${endorse_response}" == "OK" ] || die "endorsement failed: '${endorse_response}'" [ -z ${DEBUG+x} ] || say "endorsement returned '${endorse_response}'" diff --git a/internal/utils/validation.go b/internal/utils/validation.go index e5626f449..db632f846 100644 --- a/internal/utils/validation.go +++ b/internal/utils/validation.go @@ -61,6 +61,7 @@ func ReplayReadWrites(stub shim.ChaincodeStubInterface, fpcrwset *protos.FPCKVSe logger.Debugf("read key %s value(hex) %s", k, hex.EncodeToString(v)) // compute value hash + // TODO: use pdo hash for consistency h := sha256.New() h.Write(v) valueHash := h.Sum(nil)