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

Microsoft CCF based PDO transaction processor #259

Merged
Show file tree
Hide file tree
Changes from 1 commit
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
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,14 @@ PDO uses a distributed ledger, in this case the
[Hyperledger Sawtooth](https://sawtooth.hyperledger.org/)
distributed ledger, to ensure that there is a single, authoritative
instance of the object, and to provide a means of guaranteeing atomicity of
updates across interacting objects. There is also ongoing efforts to support
[Microsoft Confidential Consortium Framework (CCF)](https://microsoft.github.io/CCF/) based ledger.
PDO performs contract execution and storage off the blockchain, with only a hash of
updates across interacting objects. PDO performs contract execution and storage off the blockchain, with only a hash of
blockchain state stored on the distributed ledger.

There is also an ongoing experimental effort to add support for
[Microsoft Confidential Consortium Framework (CCF)](https://microsoft.github.io/CCF/) based ledger.
Currently, the PDO/CCF combo is restricted to virtual enclaves, and lacks docker as well as
automated test support.

PDO provides benefits for both application developers seeking to define and
implement privacy-preserving distributed ledgers, and for service providers
seeking to provide blockchain services.
Expand Down
1 change: 0 additions & 1 deletion build/__tools__/run-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ function cleanup {

trap cleanup EXIT


# -----------------------------------------------------------------
yell run unit tests for python, common, contracts and eservice
# -----------------------------------------------------------------
Expand Down
10 changes: 3 additions & 7 deletions ccf_transaction_processor/Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ The TP must viewed as a CCF application. Documentation for building and deployin
can be found at https://microsoft.github.io/CCF/. The CCF legder that stores the PDO TP registries is
encrypted, and is accessible only within CCF encalves. Currently PDO/CCF combination is supported
only under the virtual enclave mode for both PDO and CCF. (set env variable SGX_MODE=SIM for PDO &
masomel marked this conversation as resolved.
Show resolved Hide resolved
set cmake flag TARGET=virtual for ccf). Support for HW mode for both PDO and CCF will be added soon.
set cmake flag TARGET=virtual for ccf). Support for HW mode for both PDO and CCF will be added soon. Further,
the current implementation of PDO TP hasn't been tested under multi-threaded CCF enclaves.(CCF 0.7.1 offers initial support for multi-threading). It is recommended that the application is deployed with single worker thread per enclave. Please see https://microsoft.github.io/CCF/developers/threading.html for instructions.

CCF uses mutually authenticated TLS channels for transactions. Given that in PDO client authentication is implemented within the transaction processor itself, we do not utilize the client authentication feature provided by CCF. Once CCF is deployed, CCF's network certificate (networkcert.pem) and one set of user keys (userccf_cert.pem & userccf_privk.pem)
must be made available to all PDO processes that want to submit a CCF transaction. In this case, every
Expand All @@ -26,12 +27,7 @@ As far as PDO is concerned, CCF based TP is functionally identical to the Sawtoo
TP (except for one additional feature described below). A key difference beween the two ledgers
prakashngit marked this conversation as resolved.
Show resolved Hide resolved
is the fact that while the ledger in CCF is encrypted as noted above, the ledger is Sawtooth is stored in plain text.
Even though the no part of conract state gets stored in the ledger in both CCF & Sawtooth, encrypting the
ledger as in CCF helps to hide transaction patterns that are otherwise visible in Sawtooth ledger. Detailed documentation
about Sawtooth based TP can be found at $PDO_SRC/sawtooth/docs. The schema for JSON payloads used to submit
CCF transactions can be found at
[${PDO_SRC}/python/pdo/submitter/ccf/docs/](../python/pdo/submitter/ccf/docs/ccf_payload_schema.json).
For additional references to documentation about PDO, including transaction processor protocols,
see [${PDO_SRC}/README.md](../README.md)
ledger as in CCF helps to hide transaction patterns that are otherwise visible in Sawtooth ledger. Detailed documentation about Sawtooth based TP can be found at $PDO_SRC/sawtooth/docs. The schema for JSON payloads used to submit CCF transactions can be found at [${PDO_SRC}/python/pdo/submitter/ccf/docs/](../python/pdo/submitter/ccf/docs/ccf_payload_schema.json). For additional references to documentation about PDO, including transaction processor protocols, see [${PDO_SRC}/README.md](../README.md)

A feature of the CCF based TP that is not supported by Sawtooth based TP is the fact that
prakashngit marked this conversation as resolved.
Show resolved Hide resolved
responses to read-transactions include a payload signature, where the signature is generated by the CCF enclave
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,6 @@ namespace ccf
vector<uint8_t> contract_enclave_signature;
vector<uint8_t> signature;
vector<uint8_t> nonce;
string verifying_key; // for the pdo_signature
string state_update_info; //json string
};

Expand Down Expand Up @@ -126,7 +125,7 @@ namespace ccf
// check input complies with schema
DECLARE_JSON_TYPE(Update_contract_state::In);
DECLARE_JSON_REQUIRED_FIELDS(Update_contract_state::In, verb, contract_enclave_id, \
contract_enclave_signature, signature, nonce, verifying_key, state_update_info);
contract_enclave_signature, signature, nonce, state_update_info);

DECLARE_JSON_TYPE(Get_current_state_info::In);
DECLARE_JSON_REQUIRED_FIELDS(Get_current_state_info::In, contract_id);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ namespace ccf
string contract_enclave_id;
string contract_id;
masomel marked this conversation as resolved.
Show resolved Hide resolved
string encrypted_state_encryption_key;
string signature;
string signature; // this is the enclave signature for the add enclave to contract transaction
std::vector<ProvisioningKeysToSecretMap> provisioning_key_state_secret_pairs;
prakashngit marked this conversation as resolved.
Show resolved Hide resolved

MSGPACK_DEFINE(contract_enclave_id, contract_id, encrypted_state_encryption_key, signature, provisioning_key_state_secret_pairs);
Expand Down Expand Up @@ -100,7 +100,6 @@ namespace ccf
struct Add_enclave {
struct In {
string contract_id;
string contract_creator_verifying_key_PEM;
string enclave_info; //json string
std::vector<uint8_t> signature;
};
Expand Down Expand Up @@ -128,7 +127,7 @@ namespace ccf
signature, contract_id, provisioning_service_ids);

DECLARE_JSON_TYPE(Add_enclave::In);
DECLARE_JSON_REQUIRED_FIELDS(Add_enclave::In, contract_id, contract_creator_verifying_key_PEM, \
DECLARE_JSON_REQUIRED_FIELDS(Add_enclave::In, contract_id, \
enclave_info, signature);

DECLARE_JSON_TYPE(Verify_contract::In);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ namespace ccf

struct Verify_enclave {
struct In {
string verifying_key; //enclave_id
string enclave_id; //enclave_id
};

struct Out {
Expand All @@ -72,7 +72,7 @@ namespace ccf
registration_block_context, organizational_info, EHS_verifying_key, signature);

DECLARE_JSON_TYPE(Verify_enclave::In);
DECLARE_JSON_REQUIRED_FIELDS(Verify_enclave::In, verifying_key);
DECLARE_JSON_REQUIRED_FIELDS(Verify_enclave::In, enclave_id);

DECLARE_JSON_TYPE(Verify_enclave::Out);
DECLARE_JSON_REQUIRED_FIELDS(Verify_enclave::Out, verifying_key, encryption_key, proof_data, last_registration_block_context, \
Expand Down
45 changes: 18 additions & 27 deletions ccf_transaction_processor/transaction_processor/pdo_tp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ namespace ccfapp

TPHandler::TPHandler(Store& store):
UserHandlerRegistry(store),
enclavetable(store.create<string, EnclaveInfo>("encalves")),
enclavetable(store.create<string, EnclaveInfo>("enclaves")),
contracttable(store.create<string, ContractInfo>("contracts")),
ccltable(store.create<string, ContractStateInfo>("ccl_updates")),
signer(store.create<string, map<string, string>>("signer"))
Expand Down Expand Up @@ -71,10 +71,11 @@ namespace ccfapp

auto signer_local = signer_view->get("signer");
prakashngit marked this conversation as resolved.
Show resolved Hide resolved
try{
// if local signer exists, it means that we are waiting for global commit of the signer.
// The signer is not used until it gets globally committed.
if (!signer_local.has_value()){
// create the key, done only the first time this rpc is called
auto curve = CurveImpl::secp256k1_mbedtls;
auto kp = make_key_pair(curve);
auto kp = make_key_pair(CurveImpl::secp256k1_mbedtls);
auto privk_pem = kp->private_key_pem();
auto pubk_pem = kp->public_key_pem();
map<string, string> key_pair;
Expand Down Expand Up @@ -153,10 +154,10 @@ namespace ccfapp
const auto in = params.get<Register_contract::In>();

// Capture the current view
auto view = tx.get_view(contracttable);
auto contract_view = tx.get_view(contracttable);
prakashngit marked this conversation as resolved.
Show resolved Hide resolved

// Check if enclave was previously registered
auto contract_r = view->get(in.contract_id);
auto contract_r = contract_view->get(in.contract_id);
if (contract_r.has_value())
{
return make_error(
Expand Down Expand Up @@ -186,7 +187,7 @@ namespace ccfapp
}

//store the data
view->put(in.contract_id, new_contract);
contract_view->put(in.contract_id, new_contract);

// No need to commit the Tx, this is automatically taken care of !

Expand All @@ -211,14 +212,8 @@ namespace ccfapp
}
auto contract_info = contract_r.value();

//ensure the contract owner is the one adding enclaves to contract
if (in.contract_creator_verifying_key_PEM != contract_info.contract_creator_verifying_key_PEM){
return make_error(
jsonrpc::StandardErrorCodes::INVALID_PARAMS, "Enclave can only be added by the contract owner");
}

// Verify Pdo transaction signature
if (!verify_pdo_transaction_signature_add_enclave(in.signature, in.contract_creator_verifying_key_PEM, \
// Verify Pdo transaction signature (ensures that the contract ownder is the one adding encalves)
if (!verify_pdo_transaction_signature_add_enclave(in.signature, contract_info.contract_creator_verifying_key_PEM, \
in.contract_id, in.enclave_info)){
return make_error(
jsonrpc::StandardErrorCodes::INVALID_PARAMS, "Invalid PDO payload signature");
Expand Down Expand Up @@ -272,12 +267,12 @@ namespace ccfapp
// To Do: This signature verification does not work. The following function returns "true" until this is fixed
// see git issues for status
if (!verify_enclave_signature_add_enclave(enclave_info_temp.signature, this->enclave_pubk_verifier[enclave_r.value().verifying_key], \
in.contract_creator_verifying_key_PEM, in.contract_id, enclave_info_temp.provisioning_key_state_secret_pairs, \
contract_info.contract_creator_verifying_key_PEM, in.contract_id, enclave_info_temp.provisioning_key_state_secret_pairs, \
enclave_info_temp.encrypted_state_encryption_key)){

// the following code is to aid in debugging the signature verification issue.
string message = in.contract_id;
message += in.contract_creator_verifying_key_PEM;
message += contract_info.contract_creator_verifying_key_PEM;
for (auto prov :enclave_info_temp.provisioning_key_state_secret_pairs ) {
message += prov.pspk;
message += prov.encrypted_secret;
Expand Down Expand Up @@ -375,12 +370,8 @@ namespace ccfapp
jsonrpc::StandardErrorCodes::INVALID_PARAMS, "Init operation must not have CCL dependeices from other contracts");
}

if (in.verifying_key != contract_info.contract_creator_verifying_key_PEM){
return make_error(
jsonrpc::StandardErrorCodes::INVALID_PARAMS, "Init operation can only be performed by the contract creator");
}

if (!verify_pdo_transaction_signature_update_contract_state(in.signature, in.verifying_key, \
// sign check ensures that Init operation can only be performed by the contract creator
if (!verify_pdo_transaction_signature_update_contract_state(in.signature, contract_info.contract_creator_verifying_key_PEM, \
in.contract_enclave_id, in.contract_enclave_signature, in.state_update_info)){
return make_error(
jsonrpc::StandardErrorCodes::INVALID_PARAMS, "Invalid PDO payload signature");
Expand Down Expand Up @@ -417,8 +408,8 @@ namespace ccfapp
jsonrpc::StandardErrorCodes::INVALID_PARAMS, "Update verb must be either init or update");
}

// verify contract enclave signature

// verify contract enclave signature. This signature also ensures (via the notion of channel ids) that
// the contraction invocation was performed by the transaction submitter.
prakashngit marked this conversation as resolved.
Show resolved Hide resolved
if (!verify_enclave_signature_update_contract_state(in.contract_enclave_signature, this->enclave_pubk_verifier[enclave_r.value().verifying_key], \
in.nonce, contract_info.contract_creator_verifying_key_PEM, contract_info.contract_code_hash, \
state_update_info)) {
Expand Down Expand Up @@ -451,7 +442,7 @@ namespace ccfapp
auto verify_enclave = [this](Store::Tx& tx, const nlohmann::json& params) {
prakashngit marked this conversation as resolved.
Show resolved Hide resolved
const auto in = params.get<Verify_enclave::In>();
auto enclave_view = tx.get_view(enclavetable);
auto enclave_r = enclave_view->get_globally_committed(in.verifying_key);
auto enclave_r = enclave_view->get_globally_committed(in.enclave_id);

if (enclave_r.has_value())
{
Expand All @@ -464,14 +455,14 @@ namespace ccfapp
}

string doc_to_sign;
doc_to_sign += in.verifying_key;
doc_to_sign += in.enclave_id;
doc_to_sign += enclave_r.value().encryption_key;
doc_to_sign += enclave_r.value().proof_data;
doc_to_sign += enclave_r.value().registration_block_context;
doc_to_sign += enclave_r.value().EHS_verifying_key;
auto signature = TPHandler::sign_document(doc_to_sign);

return make_success(Verify_enclave::Out{in.verifying_key, enclave_r.value().encryption_key, \
return make_success(Verify_enclave::Out{in.enclave_id, enclave_r.value().encryption_key, \
enclave_r.value().proof_data, enclave_r.value().registration_block_context, \
enclave_r.value().EHS_verifying_key, signature});
}
Expand Down
5 changes: 2 additions & 3 deletions ccf_transaction_processor/transaction_processor/pdo_tp.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ namespace ccfapp
static constexpr auto ADD_ENCLAVE_TO_CONTRACT ="add_enclave_to_contract";
static constexpr auto UPDATE_CONTRACT_STATE ="ccl_update";


//methods that read the tables, used by PDO to verify write transactions
static constexpr auto VERIFY_ENCLAVE_REGISTRATION = "verify_enclave_registration";
static constexpr auto VERIFY_CONTRACT_REGISTRATION = "verify_contract_registration";
Expand All @@ -68,8 +67,8 @@ namespace ccfapp
Store::Map<string, EnclaveInfo>& enclavetable; // key is encalve_id
Store::Map<string, ContractInfo>& contracttable; // key is contract_id
Store::Map<string, ContractStateInfo>& ccltable; // key is contract_id + state_hash (string addition)
Store::Map<string, map<string, string>>& signer; //key string indicates if signer has been initialized or not
// value is pubk: & privk:
Store::Map<string, map<string, string>>& signer; //There is at most one entry in this map. if there is an
//entry key="signer". value is pubk: & privk:

// functions to verify signatures, only wite methods sign transactions, read methods do not.
bool verify_pdo_transaction_signature_register_enclave(const vector<uint8_t>& signature, const string & verifying_key,
Expand Down
2 changes: 0 additions & 2 deletions python/pdo/contract/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -247,8 +247,6 @@ def get_current_state_hash(ledger_config, contract_id) :
logger.info('error getting state hash; %s', str(e))
raise Exception('failed to retrieve contract state hash; {}'.format(contract_id))

return current_state_hash

# --------------------------------------------------
@classmethod
def get_from_ledger(cls, ledger_config, contract_id, current_state_hash = None) :
prakashngit marked this conversation as resolved.
Show resolved Hide resolved
Expand Down
6 changes: 2 additions & 4 deletions python/pdo/submitter/ccf/ccf_submitter.py
Original file line number Diff line number Diff line change
Expand Up @@ -450,9 +450,9 @@ def build_enclave_registration_from_data(

# -----------------------------------------------------------------
@staticmethod
def build_verify_enclave_from_data(verifying_key):
def build_verify_enclave_from_data(enclave_id):
payloadblob = dict()
payloadblob['verifying_key'] = verifying_key
payloadblob['enclave_id'] = enclave_id
# there is no need to sign these verification transactions
return payloadblob

Expand Down Expand Up @@ -509,7 +509,6 @@ def build_add_enclave_to_contract_from_data(
payloadblob = dict()
payloadblob['contract_id'] = contract_id
payloadblob['enclave_info'] = json.dumps(enclave_info_quintuples, sort_keys=True)
payloadblob['contract_creator_verifying_key_PEM'] = contract_creator_keys.verifying_key

# sign the payload after adding a nonce
payloadblob['signature'] = compute_pdo_add_enclave_signature(contract_creator_keys.signing_key,
Expand Down Expand Up @@ -546,7 +545,6 @@ def build_initialize_contract_state_transaction_from_data(
state_update_info['dependency_list'] = dependency_list
state_update_info_string = json.dumps(state_update_info, sort_keys=True)
payloadblob['state_update_info'] = state_update_info_string
payloadblob['verifying_key'] = verifying_key
payloadblob['nonce'] = nonce
payloadblob['signature'] = compute_pdo_update_contract_state_signature(signing_key, verifying_key, contract_enclave_id, \
contract_enclave_signature, state_update_info_string)
Expand Down