Skip to content

Commit

Permalink
Extend FPC Shim support (#637)
Browse files Browse the repository at this point in the history
* Extend FPC Shim support

- add get_signed_proposal
- add get_tx_id
- add get_channel_id
- add get_transient_data

Signed-off-by: Marcus Brandenburger <bur@zurich.ibm.com>

* fixup! Extend FPC Shim support

Signed-off-by: Marcus Brandenburger <bur@zurich.ibm.com>

* fixup! Extend FPC Shim support

Signed-off-by: Marcus Brandenburger <bur@zurich.ibm.com>

* fixup! Extend FPC Shim support

Signed-off-by: Marcus Brandenburger <bur@zurich.ibm.com>

* fixup! Extend FPC Shim support

Signed-off-by: Marcus Brandenburger <bur@zurich.ibm.com>

* fixup! Extend FPC Shim support

Signed-off-by: Marcus Brandenburger <bur@zurich.ibm.com>

* fixup! Extend FPC Shim support

Signed-off-by: Marcus Brandenburger <bur@zurich.ibm.com>

* fixup! Extend FPC Shim support

Signed-off-by: Marcus Brandenburger <bur@zurich.ibm.com>
  • Loading branch information
mbrandenburger authored Dec 7, 2021
1 parent 5713269 commit a949ca9
Show file tree
Hide file tree
Showing 16 changed files with 286 additions and 86 deletions.
15 changes: 15 additions & 0 deletions common/enclave/cc_data.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,21 @@ std::string cc_data::get_enclave_id()
return hex;
}

std::string cc_data::get_channel_id()
{
fpc_CCParameters cc_params = fpc_CCParameters_init_zero;
pb_istream_t istream =
pb_istream_from_buffer((const unsigned char*)cc_parameters_.data(), cc_parameters_.size());
bool b = pb_decode(&istream, fpc_CCParameters_fields, &cc_params);
COND2LOGERR(!b, PB_GET_ERROR(&istream));

return std::string(cc_params.channel_id);

err:
// return empty string in case of error
return std::string();
}

bool cc_data::sign_message(const ByteArray& message, ByteArray& signature) const
{
bool b;
Expand Down
2 changes: 2 additions & 0 deletions common/enclave/cc_data.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ class cc_data
uint32_t* credentials_size);

std::string get_enclave_id();
std::string get_channel_id();

bool sign_message(const ByteArray& message, ByteArray& signature) const;
bool decrypt_key_transport_message(
const ByteArray& encrypted_key_transport_message, ByteArray& key_transport_message) const;
Expand Down
37 changes: 0 additions & 37 deletions ecc/chaincode/enclave/shim.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,12 @@ package enclave

import (
"bytes"
"crypto/x509"
"encoding/pem"
"fmt"
"sync"
"unsafe"

"github.com/hyperledger/fabric-chaincode-go/shim"
"github.com/hyperledger/fabric-private-chaincode/internal/utils"
"github.com/hyperledger/fabric/protoutil"
)

// #cgo CFLAGS: -I${SRCDIR}/../../common/sgxcclib
Expand Down Expand Up @@ -99,40 +96,6 @@ func (r *stubRegistry) get(i int) *Stubs {
return stubs
}

//export get_creator_name
func get_creator_name(msp_id *C.char, max_msp_id_len C.uint32_t, dn *C.char, max_dn_len C.uint32_t, ctx unsafe.Pointer) {
stubs := registry.get(*(*int)(ctx))

// TODO (eventually): replace/simplify below via ext.ClientIdentity,
// should also make it easier to eventually return more than only
// msp & dn ..

serializedID, err := stubs.shimStub.GetCreator()
if err != nil {
panic("error while getting creator")
}
sId, err := protoutil.UnmarshalSerializedIdentity(serializedID)
if err != nil {
panic("Could not deserialize a SerializedIdentity")
}

bl, _ := pem.Decode(sId.IdBytes)
if bl == nil {
panic("Failed to decode PEM structure")
}
cert, err := x509.ParseCertificate(bl.Bytes)
if err != nil {
panic("Unable to parse certificate %s")
}

var goMspId = sId.Mspid
C._cpy_str(msp_id, goMspId, max_msp_id_len)

var goDn = cert.Subject.String()
C._cpy_str(dn, goDn, max_dn_len)
// TODO (eventually): return the eror case of the dn buffer being too small
}

//export get_state
func get_state(key *C.char, val *C.uint8_t, max_val_len C.uint32_t, val_len *C.uint32_t, ctx unsafe.Pointer) {
stubs := registry.get(*(*int)(ctx))
Expand Down
1 change: 1 addition & 0 deletions ecc_enclave/enclave/CMakeLists-common-app-enclave.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ add_library(enclave SHARED ${SOURCE_FILES})
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}
${COMMON_SOURCE_DIR}
${COMMON_SOURCE_DIR}/base64
${COMMON_SOURCE_DIR}/json
${COMMON_SOURCE_DIR}/logging/trusted
${ECC_ENCLAVE_DIR}/enclave
Expand Down
2 changes: 2 additions & 0 deletions ecc_enclave/enclave/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,14 @@ set(SOURCE_FILES
${COMMON_SOURCE_DIR}/utils.c
${COMMON_SOURCE_DIR}/json/parson.c
${COMMON_SOURCE_DIR}/protos/fpc/fpc.pb.c
${COMMON_SOURCE_DIR}/protos/fabric/common/common.pb.c
${COMMON_SOURCE_DIR}/protos/fabric/peer/proposal.pb.c
${COMMON_SOURCE_DIR}/protos/fabric/peer/proposal_response.pb.c
${COMMON_SOURCE_DIR}/protos/fabric/peer/chaincode.pb.c
${COMMON_SOURCE_DIR}/protos/fabric/common/policies.pb.c
${COMMON_SOURCE_DIR}/protos/fabric/ledger/rwset/kvrwset/kv_rwset.pb.c
${COMMON_SOURCE_DIR}/protos/fabric/msp/msp_principal.pb.c
${COMMON_SOURCE_DIR}/protos/fabric/msp/identities.pb.c
${COMMON_SOURCE_DIR}/protos/google/protobuf/any.pb.c
${COMMON_SOURCE_DIR}/protos/google/protobuf/timestamp.pb.c
${NANOPB_PATH}/pb_common.c
Expand Down
80 changes: 80 additions & 0 deletions ecc_enclave/enclave/crypto.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,18 @@
*/

#include "crypto.h"
#include <openssl/bio.h>
#include <openssl/pem.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>
#include <stdexcept>
#include "error.h"
#include "logging.h"
#include "pdo/common/crypto/crypto.h"
#include "sgx_trts.h"

typedef std::unique_ptr<BIO, void (*)(BIO*)> BIO_ptr;

int get_random_bytes(uint8_t* buffer, size_t length)
{
/* WARNING WARNING WARNING */
Expand Down Expand Up @@ -65,3 +72,76 @@ bool compute_message_hash(const ByteArray message, ByteArray& message_hash)
err:
return false;
}

bool validate_message_signature(
const ByteArray signature, const ByteArray message, const ByteArray signer_cert)
{
try
{
// deserialize public key
pdo::crypto::sig::PublicKey pk(extract_encoded_public_key(signer_cert));

// check signature
int r = pk.VerifySignature(message, signature);
COND2ERR(r != 1);
}
catch (const std::exception& e)
{
COND2LOGERR(true, e.what());
}

return true;

err:
return false;
}

std::string extract_encoded_public_key(const ByteArray cert_pem)
{
BIO_ptr certBio(BIO_new(BIO_s_mem()), BIO_free_all);
BIO_write(certBio.get(), cert_pem.data(), cert_pem.size());

X509* cert = PEM_read_bio_X509(certBio.get(), NULL, NULL, NULL);
if (!cert)
{
throw std::runtime_error("cannot parse cert");
}

// extract pk from cert and convert it back to pem format since PDO crypto requires encoded pk
EVP_PKEY* pubkey = X509_get_pubkey(cert);
EC_KEY* eckey = EVP_PKEY_get1_EC_KEY(pubkey);

BIO_ptr pkBio(BIO_new(BIO_s_mem()), BIO_free_all);
PEM_write_bio_EC_PUBKEY(pkBio.get(), eckey);
int keylen = BIO_pending(pkBio.get());
ByteArray pem_str(keylen + 1);
int r = BIO_read(pkBio.get(), pem_str.data(), keylen);
if (r <= 0)
{
throw std::runtime_error("cannot serialize public key");
}
pem_str[keylen] = '\0';

X509_free(cert);
return std::string((const char*)pem_str.data(), pem_str.size());
}

std::string extract_subject_from_cert(const ByteArray cert_pem)
{
BIO_ptr certBio(BIO_new(BIO_s_mem()), BIO_free_all);
BIO_write(certBio.get(), cert_pem.data(), cert_pem.size());

X509* cert = PEM_read_bio_X509(certBio.get(), NULL, NULL, NULL);
if (!cert)
{
throw std::runtime_error("cannot parse cert");
}

char* subj = X509_NAME_oneline(X509_get_subject_name(cert), NULL, 0);
X509_free(cert);

// Go: CN=peer0.org1.example.com,OU=COP,L=San Francisco,ST=California,C=US
// This: /C=US/ST=California/L=San Francisco/OU=COP/CN=peer0.org1.example.com

return std::string(subj);
}
7 changes: 7 additions & 0 deletions ecc_enclave/enclave/crypto.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,10 @@ bool decrypt_message(const ByteArray key, const ByteArray& encrypted_message, By
bool encrypt_message(const ByteArray key, const ByteArray& message, ByteArray& encrypted_message);

bool compute_message_hash(const ByteArray message, ByteArray& message_hash);

bool validate_message_signature(
const ByteArray signature, const ByteArray message, const ByteArray serialized_signer);

std::string extract_encoded_public_key(const ByteArray cert_pem);

std::string extract_subject_from_cert(const ByteArray cert_pem);
95 changes: 95 additions & 0 deletions ecc_enclave/enclave/enclave.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@
#include "crypto.h"
#include "enclave_t.h"
#include "error.h"
#include "fabric/common/common.pb.h"
#include "fabric/msp/identities.pb.h"
#include "fabric/peer/chaincode.pb.h"
#include "fabric/peer/proposal.pb.h"
#include "fpc/fpc.pb.h"
#include "logging.h"
#include "pb_decode.h"
Expand Down Expand Up @@ -48,6 +52,97 @@ int ecall_cc_invoke(const uint8_t* signed_proposal_proto_bytes,
ByteArray response_encryption_key;

ctx.u_shim_ctx = u_shim_ctx;
ctx.signed_proposal = ByteArray(
signed_proposal_proto_bytes, signed_proposal_proto_bytes + signed_proposal_proto_bytes_len);

// unmarshall proposal
{
pb_istream_t istream;

protos_SignedProposal signed_proposal = protos_SignedProposal_init_zero;
istream = pb_istream_from_buffer(
(const unsigned char*)signed_proposal_proto_bytes, signed_proposal_proto_bytes_len);
b = pb_decode(&istream, protos_SignedProposal_fields, &signed_proposal);
COND2LOGERR(!b, PB_GET_ERROR(&istream));

protos_Proposal proposal = protos_Proposal_init_zero;
istream =
pb_istream_from_buffer((const unsigned char*)signed_proposal.proposal_bytes->bytes,
signed_proposal.proposal_bytes->size);
b = pb_decode(&istream, protos_Proposal_fields, &proposal);
COND2LOGERR(!b, PB_GET_ERROR(&istream));

common_Header header = common_Header_init_zero;
istream = pb_istream_from_buffer(
(const unsigned char*)proposal.header->bytes, proposal.header->size);
b = pb_decode(&istream, common_Header_fields, &header);
COND2LOGERR(!b, PB_GET_ERROR(&istream));

// set tx_id and channel id
common_ChannelHeader channel_header = common_ChannelHeader_init_zero;
istream = pb_istream_from_buffer(
(const unsigned char*)header.channel_header->bytes, header.channel_header->size);
b = pb_decode(&istream, common_ChannelHeader_fields, &channel_header);
COND2LOGERR(!b, PB_GET_ERROR(&istream));

ctx.tx_id = std::string(channel_header.tx_id);
ctx.channel_id = std::string(channel_header.channel_id);
COND2LOGERR(ctx.channel_id != g_cc_data->get_channel_id(),
"channel id of the tx proposal does not match as initialized with cc_parameters");

// TODO implement me
// transient data
// protos_ChaincodeProposalPayload cc_proposal_payload =
// protos_ChaincodeProposalPayload_init_zero;
// istream = pb_istream_from_buffer(
// (const unsigned char*)proposal.payload->bytes, proposal.payload->size);
// b = pb_decode(&istream, protos_ChaincodeProposalPayload_fields, &cc_proposal_payload);
// COND2LOGERR(!b, PB_GET_ERROR(&istream));

// transform _protos_ChaincodeProposalPayload_TransientMapEntry to std::map
// ctx.transient_data = ;

// set creator
common_SignatureHeader signature_header = common_SignatureHeader_init_zero;
istream = pb_istream_from_buffer(
(const unsigned char*)header.signature_header->bytes, header.signature_header->size);
b = pb_decode(&istream, common_SignatureHeader_fields, &signature_header);
COND2LOGERR(!b, PB_GET_ERROR(&istream));

ByteArray signature = ByteArray(signed_proposal.signature->bytes,
signed_proposal.signature->bytes + signed_proposal.signature->size);
ByteArray message = ByteArray(signed_proposal.proposal_bytes->bytes,
signed_proposal.proposal_bytes->bytes + signed_proposal.proposal_bytes->size);

ctx.creator = ByteArray(signature_header.creator->bytes,
signature_header.creator->bytes + signature_header.creator->size);

msp_SerializedIdentity identity = msp_SerializedIdentity_init_zero;
istream = pb_istream_from_buffer((const unsigned char*)(signature_header.creator->bytes),
signature_header.creator->size);
b = pb_decode(&istream, msp_SerializedIdentity_fields, &identity);
COND2LOGERR(!b, PB_GET_ERROR(&istream));

ByteArray encoded_signer_cert =
ByteArray(identity.id_bytes->bytes, identity.id_bytes->bytes + identity.id_bytes->size);
b = validate_message_signature(signature, message, encoded_signer_cert);
COND2LOGERR(!b, "signature validation failed");

ctx.creator_msp_id = std::string(identity.mspid);
ctx.creator_name = extract_subject_from_cert(encoded_signer_cert);

// TODO unwrap ChaincodeRequestMessage from signed proposal
// once this is in place we can remove ChaincodeRequestMessage from ecall

// TODO implement me
// ByteArray binding_data;
// binding_data.insert(binding_data.end(), signature_header.nonce->bytes,
// signature_header.nonce->size); binding_data.insert(binding_data.end(),
// signature_header.creator->bytes, signature_header.creator->size);
// TODO add channel_header->epoch (as ByteArray) to binding (enforce little endian)
// b = compute_message_hash(binding_data, ctx->binding);
// COND2LOGERR(!b, "cannot compute binding");
}

{
pb_istream_t istream;
Expand Down
5 changes: 0 additions & 5 deletions ecc_enclave/enclave/enclave.edl
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,6 @@ enclave {
};

untrusted {
void ocall_get_creator_name(
[out, size=max_msp_id_len] char *msp_id, uint32_t max_msp_id_len,
[out, size=max_dn_len] char *dn, uint32_t max_dn_len,
[user_check] void *u_shim_ctx);

void ocall_get_state(
[in, string] const char *key,
[out, size=max_val_len] uint8_t *val, uint32_t max_val_len, [out] uint32_t *val_len,
Expand Down
33 changes: 24 additions & 9 deletions ecc_enclave/enclave/shim.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,10 @@ static sgx_thread_mutex_t global_mutex = SGX_THREAD_MUTEX_INITIALIZER;
void get_creator_name(
char* msp_id, uint32_t max_msp_id_len, char* dn, uint32_t max_dn_len, shim_ctx_ptr_t ctx)
{
// TODO: right now the implementation is not secure yet as below function is unvalidated
// from the (untrusted) peer.
// To securely implement it, we will require the signed proposal to be passed
// from the stub (see, e.g., ChaincodeStub in go shim core/chaincode/shim/stub.go)
// and then verified. This in turn will require verification of certificates based
// on the MSP info channel. As TLCC already has to do keep track of MSP and do related
// verification , we can off-load some of that to TLCC (as we anyway have to talk to it
// to get channel MSP info)
ocall_get_creator_name(msp_id, max_msp_id_len, dn, max_dn_len, ctx->u_shim_ctx);
strcpy_s(msp_id, max_msp_id_len, ctx->creator_msp_id.c_str());
strcpy_s(dn, max_dn_len, ctx->creator_name.c_str());

return;
}

void get_state(
Expand Down Expand Up @@ -274,3 +269,23 @@ int get_func_and_params(
err:
return -1;
}

void get_signed_proposal(ByteArray& signed_proposal, shim_ctx_ptr_t ctx)
{
signed_proposal = ctx->signed_proposal;
}

void get_channel_id(std::string& channel_id, shim_ctx_ptr_t ctx)
{
channel_id = ctx->channel_id;
}

void get_tx_id(std::string& tx_id, shim_ctx_ptr_t ctx)
{
tx_id = ctx->tx_id;
}

void get_creator(ByteArray& creator, shim_ctx_ptr_t ctx)
{
creator = ctx->creator;
}
Loading

0 comments on commit a949ca9

Please sign in to comment.