diff --git a/build.sbt b/build.sbt index 39401148c2..aa242bafa7 100644 --- a/build.sbt +++ b/build.sbt @@ -341,6 +341,7 @@ enclaveBuildTask := { ).! if (cmakeResult != 0) sys.error("C++ build failed.") val nproc = java.lang.Runtime.getRuntime.availableProcessors + val mode = sys.env.get("MODE").get val buildResult = Process(Seq("make", "-j" + nproc), enclaveBuildDir).! if (buildResult != 0) sys.error("C++ build failed.") val installResult = Process(Seq("make", "install"), enclaveBuildDir).! diff --git a/gen_pubkey_header.sh b/gen_pubkey_header.sh new file mode 100755 index 0000000000..d3d059ec3f --- /dev/null +++ b/gen_pubkey_header.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash + +# Copyright (c) Open Enclave SDK contributors. +# Licensed under the MIT License. + +destfile="$1" +pubkey_file="$2" + +cat > "$destfile" << EOF +// Copyright (c) Open Enclave SDK contributors. +// Licensed under the MIT License. + +EOF + +printf 'static const char OTHER_ENCLAVE_PUBLIC_KEY[] =' >> "$destfile" +while IFS="" read -r p || [ -n "$p" ] +do + # Sometimes openssl can insert carriage returns into the PEM files. Let's remove those! + CR=$(printf "\r") + p=$(echo "$p" | tr -d "$CR") + printf '\n \"%s\\n\"' "$p" >> "$destfile" +done < "$pubkey_file" +printf ';\n' >> "$destfile" + +cat >> "$destfile" << EOF + +EOF diff --git a/keys/key_share.txt b/keys/key_share.txt new file mode 100644 index 0000000000..f64efeb118 Binary files /dev/null and b/keys/key_share.txt differ diff --git a/src/enclave/App/App.cpp b/src/enclave/App/App.cpp index b2ae5f74a7..1e1be353af 100644 --- a/src/enclave/App/App.cpp +++ b/src/enclave/App/App.cpp @@ -239,6 +239,88 @@ JNIEXPORT void JNICALL Java_edu_berkeley_cs_rise_opaque_execution_SGXEnclave_Fin env->ReleaseByteArrayElements(shared_key_msg_input, shared_key_msg_bytes, 0); } +/////////////////////////////// Shared Key Gen Begin //////////////////////////////// + +JNIEXPORT jbyteArray JNICALL Java_edu_berkeley_cs_rise_opaque_execution_SGXEnclave_GetPublicKey( + JNIEnv *env, jobject obj, jlong eid) { + (void)obj; + (void)eid; + + uint8_t* report_msg = NULL; + size_t report_msg_size = 0; + + oe_check_and_time("Get enclave public key", + ecall_get_public_key((oe_enclave_t*)eid, + &report_msg, + &report_msg_size)); + + // Allocate memory + jbyteArray report_msg_bytes = env->NewByteArray(report_msg_size); + env->SetByteArrayRegion(report_msg_bytes, 0, report_msg_size, reinterpret_cast(report_msg)); + + return report_msg_bytes; +} + +JNIEXPORT jbyteArray JNICALL Java_edu_berkeley_cs_rise_opaque_execution_SGXEnclave_GetListEncrypted( + JNIEnv *env, jobject obj, jlong eid, jbyteArray shared_key_msg_input) { + (void)obj; + + jboolean if_copy = false; + jbyte *shared_key_msg_bytes = env->GetByteArrayElements(shared_key_msg_input, &if_copy); + uint32_t shared_key_msg_size = static_cast(env->GetArrayLength(shared_key_msg_input)); + + size_t report_msg_size = OE_SHARED_KEY_CIPHERTEXT_SIZE * (shared_key_msg_size / OE_PUBLIC_KEY_SIZE); + uint8_t* report_msg = new uint8_t[report_msg_size]; + + oe_check_and_time("Get List Encrypted", + ecall_get_list_encrypted((oe_enclave_t*)eid, + reinterpret_cast(shared_key_msg_bytes), + shared_key_msg_size, + report_msg, + report_msg_size)); + + // Allocate memory + jbyteArray report_msg_bytes = env->NewByteArray(report_msg_size); + env->SetByteArrayRegion(report_msg_bytes, 0, report_msg_size, reinterpret_cast(report_msg)); + + env->ReleaseByteArrayElements(shared_key_msg_input, (jbyte *) shared_key_msg_bytes, 0); + + delete[] report_msg; + + return report_msg_bytes; +} + +JNIEXPORT jbyteArray JNICALL Java_edu_berkeley_cs_rise_opaque_execution_SGXEnclave_FinishSharedKey( + JNIEnv *env, jobject obj, jlong eid, jbyteArray shared_key_msg_input) { + (void)obj; + + jboolean if_copy = false; + + jbyte *shared_key_msg_bytes = env->GetByteArrayElements(shared_key_msg_input, &if_copy); + uint32_t shared_key_msg_size = static_cast(env->GetArrayLength(shared_key_msg_input)); + + size_t report_msg_size = SGX_AESGCM_KEY_SIZE; + uint8_t* report_msg = new uint8_t[report_msg_size]; + + oe_check_and_time("Finish attestation", + ecall_finish_shared_key((oe_enclave_t*)eid, + reinterpret_cast(shared_key_msg_bytes), + shared_key_msg_size, + report_msg, + report_msg_size)); + + // Allocate memory + jbyteArray report_msg_bytes = env->NewByteArray(report_msg_size); + env->SetByteArrayRegion(report_msg_bytes, 0, report_msg_size, reinterpret_cast(report_msg)); + + env->ReleaseByteArrayElements(shared_key_msg_input, shared_key_msg_bytes, 0); + delete[] report_msg; + + return report_msg_bytes; +} + +/////////////////////////////// Shared Key Gen End //////////////////////////////// + JNIEXPORT void JNICALL Java_edu_berkeley_cs_rise_opaque_execution_SGXEnclave_StopEnclave( JNIEnv *env, jobject obj, jlong eid) { (void)env; @@ -345,6 +427,39 @@ JNIEXPORT jbyteArray JNICALL Java_edu_berkeley_cs_rise_opaque_execution_SGXEncla return ciphertext; } +// Added Decryption function +JNIEXPORT jbyteArray JNICALL Java_edu_berkeley_cs_rise_opaque_execution_SGXEnclave_Decrypt( + JNIEnv *env, jobject obj, jlong eid, jbyteArray ciphertext) { + (void)obj; + + uint32_t clength = (uint32_t)env->GetArrayLength(ciphertext); + jboolean if_copy = false; + uint8_t *ciphertext_ptr = (uint8_t *)env->GetByteArrayElements(ciphertext, &if_copy); + + uint8_t *plaintext_copy = nullptr; + jsize plength = 0; + + if (ciphertext_ptr == nullptr) { + ocall_throw("Encrypt: JNI failed to get input byte array."); + } else { + plength = clength - SGX_AESGCM_IV_SIZE - SGX_AESGCM_MAC_SIZE; + plaintext_copy = new uint8_t[clength]; + + oe_check("Decrypt", ecall_decrypt((oe_enclave_t *)eid, ciphertext_ptr, clength, + plaintext_copy, (uint32_t)plength)); + } + + jbyteArray plaintext = env->NewByteArray(plength); + env->SetByteArrayRegion(plaintext, 0, plength, (jbyte *)plaintext_copy); + + env->ReleaseByteArrayElements(ciphertext, (jbyte *)ciphertext_ptr, 0); + + delete[] plaintext_copy; + + return plaintext; +} + + JNIEXPORT jbyteArray JNICALL Java_edu_berkeley_cs_rise_opaque_execution_SGXEnclave_Sample( JNIEnv *env, jobject obj, jlong eid, jbyteArray input_rows) { (void)obj; diff --git a/src/enclave/App/SGXEnclave.h b/src/enclave/App/SGXEnclave.h index 3fd2d58e2f..80f34c7902 100644 --- a/src/enclave/App/SGXEnclave.h +++ b/src/enclave/App/SGXEnclave.h @@ -80,6 +80,15 @@ Java_edu_berkeley_cs_rise_opaque_execution_SGXEnclave_GenerateReport(JNIEnv *, j JNIEXPORT void JNICALL Java_edu_berkeley_cs_rise_opaque_execution_SGXEnclave_FinishAttestation( JNIEnv *, jobject, jlong, jbyteArray); + JNIEXPORT jbyteArray JNICALL Java_edu_berkeley_cs_rise_opaque_execution_SGXEnclave_GetPublicKey( + JNIEnv *, jobject, jlong); + + JNIEXPORT jbyteArray JNICALL Java_edu_berkeley_cs_rise_opaque_execution_SGXEnclave_GetListEncrypted( + JNIEnv *, jobject, jlong, jbyteArray); + + JNIEXPORT jbyteArray JNICALL Java_edu_berkeley_cs_rise_opaque_execution_SGXEnclave_FinishSharedKey( + JNIEnv *, jobject, jlong, jbyteArray); + #ifdef __cplusplus } #endif diff --git a/src/enclave/Enclave/Attestation.cpp b/src/enclave/Enclave/Attestation.cpp new file mode 100644 index 0000000000..a729c9cbf2 --- /dev/null +++ b/src/enclave/Enclave/Attestation.cpp @@ -0,0 +1,325 @@ +// Copyright (c) Open Enclave SDK contributors. +// Licensed under the MIT License. + +#include +#include +#include +#include + +#include "Attestation.h" +#include "enclave_pubkey.h" + +#include +#include +#include +#include + +Attestation::Attestation(Crypto* crypto) +{ + m_crypto = crypto; +} + +/** + * Get format settings for the given enclave. + */ +bool Attestation::get_format_settings( + const oe_uuid_t* format_id, + uint8_t** format_settings, + size_t* format_settings_size) +{ + bool ret = false; + + // Intialize verifier to get enclave's format settings. + if (oe_verifier_initialize() != OE_OK) + { + throw std::runtime_error("oe_verifier_initialize failed"); + goto exit; + } + + // Use the plugin. + if (oe_verifier_get_format_settings( + format_id, format_settings, format_settings_size) != OE_OK) + { + throw std::runtime_error("oe_verifier_get_format_settings failed"); + goto exit; + } + ret = true; + +exit: + return ret; +} + +/** + * Generate evidence for the given data. + */ +bool Attestation::generate_attestation_evidence( + const oe_uuid_t* format_id, + uint8_t* format_settings, + size_t format_settings_size, + const uint8_t* data, + const size_t data_size, + uint8_t** evidence, + size_t* evidence_size) +{ + bool ret = false; + uint8_t hash[32]; + oe_result_t result = OE_OK; + uint8_t* custom_claims_buffer = nullptr; + size_t custom_claims_buffer_size = 0; + char custom_claim1_name[] = "Event"; + char custom_claim1_value[] = "Attestation sample"; + char custom_claim2_name[] = "Public key hash"; + + // The custom_claims[1].value will be filled with hash of public key later + oe_claim_t custom_claims[2] = { + {.name = custom_claim1_name, + .value = (uint8_t*)custom_claim1_value, + .value_size = sizeof(custom_claim1_value)}, + {.name = custom_claim2_name, .value = nullptr, .value_size = 0}}; + + if (m_crypto->sha256(data, data_size, hash) != 0) + { + throw std::runtime_error("data hashing failed"); + goto exit; + } + + // Initialize attester and use the plugin. + result = oe_attester_initialize(); + if (result != OE_OK) + { + throw std::runtime_error("oe_attester_initialize failed."); + goto exit; + } + + // serialize the custom claims, store hash of data in custom_claims[1].value + custom_claims[1].value = hash; + custom_claims[1].value_size = sizeof(hash); + + if (oe_serialize_custom_claims( + custom_claims, + 2, + &custom_claims_buffer, + &custom_claims_buffer_size) != OE_OK) + { + throw std::runtime_error("oe_serialize_custom_claims failed."); + goto exit; + } + + // Generate evidence based on the format selected by the attester. + result = oe_get_evidence( + format_id, + 0, + custom_claims_buffer, + custom_claims_buffer_size, + format_settings, + format_settings_size, + evidence, + evidence_size, + nullptr, + 0); + if (result != OE_OK) + { + throw std::runtime_error("oe_get_evidence failed.(%s)"); + goto exit; + } + + ret = true; +exit: + return ret; +} + +/** + * Helper function used to make the claim-finding process more convenient. Given + * the claim name, claim list, and its size, returns the claim with that claim + * name in the list. + */ +static const oe_claim_t* _find_claim( + const oe_claim_t* claims, + size_t claims_size, + const char* name) +{ + for (size_t i = 0; i < claims_size; i++) + { + if (strcmp(claims[i].name, name) == 0) + return &(claims[i]); + } + return nullptr; +} + +/** + * Attest the given evidence and accompanying data. It consists of the + * following three steps: + * + * 1) The evidence is first attested using the oe_verify_evidence API. + * This ensures the authenticity of the enclave that generated the evidence. + * 2) Next, to establish trust in the enclave that generated the + * evidence, the signer_id, product_id, and security version values are + * checked to see if they are predefined trusted values. + * 3) Once the enclave's trust has been established, + * the validity of accompanying data is ensured by comparing its SHA256 digest + * against the OE_CLAIM_CUSTOM_CLAIMS_BUFFER claim. + */ +bool Attestation::attest_attestation_evidence( + const oe_uuid_t* format_id, + const uint8_t* evidence, + size_t evidence_size, + const uint8_t* data, + size_t data_size) +{ + bool ret = false; + uint8_t hash[32]; + oe_result_t result = OE_OK; + oe_claim_t* claims = nullptr; + size_t claims_length = 0; + const oe_claim_t* claim; + oe_claim_t* custom_claims = nullptr; + size_t custom_claims_length = 0; + + // Read in the public key as a string + + uint8_t m_enclave_signer_id[OE_SIGNER_ID_SIZE]; + size_t signer_size = sizeof(m_enclave_signer_id); + + if (oe_sgx_get_signer_id_from_public_key( + OTHER_ENCLAVE_PUBLIC_KEY, + sizeof(OTHER_ENCLAVE_PUBLIC_KEY), + m_enclave_signer_id, + &signer_size) != OE_OK) + { + throw std::runtime_error("oe_sgx_get_signer_id_from_public_key failed\n"); + return false; + } + + // While attesting, the evidence being attested must not be tampered + // with. Ensure that it has been copied over to the enclave. + if (!oe_is_within_enclave(evidence, evidence_size)) + { + throw std::runtime_error("Cannot attest evidence in host memory. Unsafe."); + goto exit; + } + + // 1) Validate the evidence's trustworthiness + // Verify the evidence to ensure its authenticity. + result = oe_verify_evidence( + format_id, + evidence, + evidence_size, + nullptr, + 0, + nullptr, + 0, + &claims, + &claims_length); + if (result != OE_OK) + { + throw std::runtime_error("oe_verify_evidence failed (%s).\n"); + goto exit; + } + + // 2) validate the enclave identity's signer_id is the hash of the public + // signing key that was used to sign an enclave. Check that the enclave was + // signed by an trusted entity. + + // Validate the signer id. + if ((claim = _find_claim(claims, claims_length, OE_CLAIM_SIGNER_ID)) == + nullptr) + { + throw std::runtime_error("Could not find claim."); + goto exit; + }; + + if (claim->value_size != OE_SIGNER_ID_SIZE) + { + throw std::runtime_error("signer_id size checking failed"); + goto exit; + } + + if (memcmp(claim->value, m_enclave_signer_id, OE_SIGNER_ID_SIZE) != 0) + { + throw std::runtime_error("signer_id checking failed"); + goto exit; + } + + // Check the enclave's product id. + if ((claim = _find_claim(claims, claims_length, OE_CLAIM_PRODUCT_ID)) == + nullptr) + { + throw std::runtime_error("could not find claim"); + goto exit; + }; + + if (claim->value_size != OE_PRODUCT_ID_SIZE) + { + throw std::runtime_error( + "product_id size checking failed"); + goto exit; + } + + if (*(claim->value) != 1) + { + throw std::runtime_error("product_id checking failed"); + goto exit; + } + + // Check the enclave's security version. + if ((claim = _find_claim( + claims, claims_length, OE_CLAIM_SECURITY_VERSION)) == nullptr) + { + throw std::runtime_error("could not find claim"); + goto exit; + }; + + if (claim->value_size != sizeof(uint32_t)) + { + throw std::runtime_error( + "security_version size checking failed"); + goto exit; + } + + if (*(claim->value) < 1) + { + throw std::runtime_error("security_version checking failed"); + goto exit; + } + + // 3) Validate the custom claims buffer + // Deserialize the custom claims buffer to custom claims list, then fetch + // the hash value of the data held in custom_claims[1]. + if ((claim = _find_claim( + claims, claims_length, OE_CLAIM_CUSTOM_CLAIMS_BUFFER)) == nullptr) + { + throw std::runtime_error("Could not find claim."); + goto exit; + } + + if (m_crypto->sha256(data, data_size, hash) != 0) + { + goto exit; + } + + // deserialize the custom claims buffer + if (oe_deserialize_custom_claims( + claim->value, + claim->value_size, + &custom_claims, + &custom_claims_length) != OE_OK) + { + throw std::runtime_error("oe_deserialize_custom_claims failed."); + goto exit; + } + + if (custom_claims[1].value_size != sizeof(hash) || + memcmp(custom_claims[1].value, hash, sizeof(hash)) != 0) + { + throw std::runtime_error("hash mismatch"); + goto exit; + } + + ret = true; +exit: + // Shut down attester/verifier and free claims. + oe_attester_shutdown(); + oe_verifier_shutdown(); + oe_free_claims(claims, claims_length); + return ret; +} diff --git a/src/enclave/Enclave/Attestation.h b/src/enclave/Enclave/Attestation.h new file mode 100644 index 0000000000..b9fad2f309 --- /dev/null +++ b/src/enclave/Enclave/Attestation.h @@ -0,0 +1,42 @@ +#include +#include "../Common/mCrypto.h" + +class Attestation +{ + private: + Crypto* m_crypto; + + public: + Attestation(Crypto* crypto); + + // Get format settings. + bool get_format_settings( + const oe_uuid_t* format_id, + uint8_t** format_settings_buffer, + size_t* format_settings_buffer_size); + + // Generate evidence for the given data. + bool generate_attestation_evidence( + const oe_uuid_t* format_id, + uint8_t* format_settings, + size_t format_settings_size, + const uint8_t* data, + size_t data_size, + uint8_t** evidence, + size_t* evidence_size); + + /** + * Attest the given evidence and accompanying data. The evidence + * is first attested using the oe_verify_evidence API. This ensures the + * authenticity of the enclave that generated the evidence. Next the enclave + * signer_id and unique_id values are tested to establish trust of the + * enclave that generated the evidence. + */ + bool attest_attestation_evidence( + const oe_uuid_t* format_id, + const uint8_t* evidence, + size_t evidence_size, + const uint8_t* data, + size_t data_size); +}; + diff --git a/src/enclave/Enclave/CMakeLists.txt b/src/enclave/Enclave/CMakeLists.txt index 07e6130d80..a06d9e4ca8 100644 --- a/src/enclave/Enclave/CMakeLists.txt +++ b/src/enclave/Enclave/CMakeLists.txt @@ -4,6 +4,7 @@ project(OpaqueEnclaveTrusted) set(SOURCES Aggregate.cpp + Attestation.cpp Crypto.cpp Enclave.cpp Filter.cpp @@ -20,7 +21,8 @@ set(SOURCES Random.cpp util.cpp ../Common/mCrypto.cpp - ${CMAKE_CURRENT_BINARY_DIR}/Enclave_t.c) + ${CMAKE_CURRENT_BINARY_DIR}/Enclave_t.c + ${CMAKE_CURRENT_BINARY_DIR}/enclave_pubkey.h) add_custom_command( COMMAND oeedger8r --trusted ${CMAKE_SOURCE_DIR}/Enclave/Enclave.edl @@ -30,11 +32,22 @@ add_custom_command( DEPENDS ${CMAKE_SOURCE_DIR}/Enclave/Enclave.edl OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Enclave_t.h ${CMAKE_CURRENT_BINARY_DIR}/Enclave_t.c ${CMAKE_CURRENT_BINARY_DIR}/Enclave_args.h) +# new key-gen attestation begin + +# Generate header with signer public key +add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/enclave_pubkey.h + COMMAND $ENV{OPAQUE_HOME}/gen_pubkey_header.sh enclave_pubkey.h $ENV{OPAQUE_HOME}/public_key.pub +) + +# new key-gen attestation end + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -nostdinc -fvisibility=hidden -fpie -fstack-protector") set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} ${CMAKE_CXX_FLAGS} -nostdinc++") set(ENCLAVE_LINK_FLAGS "-Wl,--no-undefined -nostdlib -nodefaultlibs -nostartfiles -Wl,-Bstatic -Wl,-Bsymbolic -Wl,--no-undefined -Wl,-pie -Wl,--export-dynamic -Wl,--defsym,__ImageBase=0") add_library(enclave_trusted SHARED ${SOURCES}) + set_property(TARGET enclave_trusted PROPERTY POSITION_INDEPENDENT_CODE OFF) set_target_properties(enclave_trusted PROPERTIES LINK_FLAGS ${ENCLAVE_LINK_FLAGS}) @@ -56,7 +69,7 @@ target_link_libraries(enclave_trusted openenclave::oecore) add_custom_command( - COMMAND openenclave::oesign sign -e $ -c ${CMAKE_CURRENT_SOURCE_DIR}/Enclave.conf -k $ENV{PRIVATE_KEY_PATH} + COMMAND openenclave::oesign sign -e $ -c ${CMAKE_CURRENT_SOURCE_DIR}/Enclave.conf -k $ENV{PRIVATE_KEY_PATH} DEPENDS enclave_trusted ${CMAKE_CURRENT_SOURCE_DIR}/Enclave.conf OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/enclave_trusted.signed) diff --git a/src/enclave/Enclave/Crypto.cpp b/src/enclave/Enclave/Crypto.cpp index fe7e240bf9..ed2519e1ad 100644 --- a/src/enclave/Enclave/Crypto.cpp +++ b/src/enclave/Enclave/Crypto.cpp @@ -1,9 +1,18 @@ +#include "Crypto.h" +#include "Random.h" + #include #include "Crypto.h" #include "Random.h" #include "common.h" #include "util.h" +#include +#include +#include + +// Set this number before creating the enclave +uint8_t num_clients = 1; /** * Symmetric key used to encrypt row data. This key is shared among the driver @@ -15,18 +24,54 @@ */ unsigned char shared_key[SGX_AESGCM_KEY_SIZE] = {0}; +// map username to client key schedule +std::unordered_map> client_key_schedules; +std::unordered_map> client_keys; + +// map user name to public key +// std::unordered_map> client_public_keys; + std::unique_ptr ks; +void initKeySchedule(char* username) { + std::string user(username); + std::unique_ptr user_ks; + unsigned char client_key[SGX_AESGCM_KEY_SIZE]; + + auto iter = client_keys.find(user); + // if (iter == client_keys.end()) { + // ocall_throw("No client key for user: %s", username); + // } else { + memcpy(client_key, (uint8_t*) iter->second.data(), SGX_AESGCM_KEY_SIZE); + // } + + user_ks.reset(new KeySchedule(reinterpret_cast(client_key), SGX_AESGCM_KEY_SIZE)); + client_key_schedules[user] = std::move(user_ks); +} + void initKeySchedule() { + // Use shared key to init key schedule ks.reset(new KeySchedule(reinterpret_cast(shared_key), SGX_AESGCM_KEY_SIZE)); } +void add_client_key(uint8_t *client_key_bytes, uint32_t client_key_size, char* username) { + if (client_key_size <= 0) { + throw std::runtime_error("Add client key failed: Invalid client key size"); + } + + std::vector user_private_key(client_key_bytes, client_key_bytes + client_key_size); + std::string user(username); + client_keys[user] = user_private_key; + + initKeySchedule(username); + +} + void set_shared_key(uint8_t *shared_key_bytes, uint32_t shared_key_size) { if (shared_key_size <= 0) { throw std::runtime_error("Attempting to set a shared key with invalid key size."); } memcpy_s(shared_key, sizeof(shared_key), shared_key_bytes, shared_key_size); - initKeySchedule(); } @@ -46,9 +91,11 @@ void encrypt(uint8_t *plaintext, uint32_t plaintext_length, uint8_t *ciphertext) AesGcm cipher(ks.get(), reinterpret_cast(iv_ptr), SGX_AESGCM_IV_SIZE); cipher.encrypt(plaintext, plaintext_length, ciphertext_ptr, plaintext_length); memcpy(mac_ptr, cipher.tag().t, SGX_AESGCM_MAC_SIZE); + } void decrypt(const uint8_t *ciphertext, uint32_t ciphertext_length, uint8_t *plaintext) { + if (!ks) { throw std::runtime_error("Cannot encrypt without a shared key. Ensure all " "enclaves have completed attestation."); @@ -63,7 +110,21 @@ void decrypt(const uint8_t *ciphertext, uint32_t ciphertext_length, uint8_t *pla AesGcm decipher(ks.get(), iv_ptr, SGX_AESGCM_IV_SIZE); decipher.decrypt(ciphertext_ptr, plaintext_length, plaintext, plaintext_length); if (memcmp(mac_ptr, decipher.tag().t, SGX_AESGCM_MAC_SIZE) != 0) { - printf("Decrypt: invalid mac\n"); + // Shared key doesn't work + // Perhaps we need to use a client key instead + int success = -1; + for (auto& keypair : client_key_schedules) { + AesGcm decipher(keypair.second.get(), iv_ptr, SGX_AESGCM_IV_SIZE); + decipher.decrypt(ciphertext_ptr, plaintext_length, plaintext, plaintext_length); + if (memcmp(mac_ptr, decipher.tag().t, SGX_AESGCM_MAC_SIZE) == 0) { + // std::cout << "We found the proper key, of user " << keypair.first << std::endl; + success = 0; + break; + } + } + if (success == -1) { + throw std::runtime_error("Couldn't decrypt -- proper key unknown\n"); + } } } diff --git a/src/enclave/Enclave/Crypto.h b/src/enclave/Enclave/Crypto.h index 821253711a..4e0402361e 100644 --- a/src/enclave/Enclave/Crypto.h +++ b/src/enclave/Enclave/Crypto.h @@ -21,6 +21,11 @@ extern const sgx_ec256_public_t g_sp_pub_key; */ void set_shared_key(uint8_t *msg4, uint32_t msg4_size); +void add_client_key(uint8_t *client_key_bytes, uint32_t client_key_size, char* username); + +// TODO: Debugging purposes. Remove this function later +void initKeySchedule(); + /** * Encrypt the given plaintext using AES-GCM with a 128-bit key and write the * result to `ciphertext`. The encrypted data will be formatted as follows, diff --git a/src/enclave/Enclave/Enclave.cpp b/src/enclave/Enclave/Enclave.cpp index ec9b6bcadb..6190499058 100644 --- a/src/enclave/Enclave/Enclave.cpp +++ b/src/enclave/Enclave/Enclave.cpp @@ -1,9 +1,13 @@ #include "Enclave_t.h" +#include #include #include +#include "Random.h" + #include "Aggregate.h" +#include "Attestation.h" #include "BroadcastNestedLoopJoin.h" #include "Crypto.h" #include "Filter.h" @@ -22,6 +26,8 @@ #include #include +#include + // This file contains definitions of the ecalls declared in Enclave.edl. Errors // originating within these ecalls are signaled by throwing a // std::runtime_error, which is caught at the top level of the ecall (i.e., @@ -46,6 +52,26 @@ void ecall_encrypt(uint8_t *plaintext, uint32_t plaintext_length, uint8_t *ciphe } } +void ecall_decrypt(uint8_t *ciphertext, uint32_t cipher_length, uint8_t *plaintext, + uint32_t plaintext_length) { + + // Guard against decrypting or overwriting enclave memory + assert(oe_is_outside_enclave(plaintext, plaintext_length) == 1); + assert(oe_is_outside_enclave(ciphertext, cipher_length) == 1); + __builtin_ia32_lfence(); + + try { + // IV (12 bytes) + ciphertext + mac (16 bytes) + assert(cipher_length >= plaintext_length + SGX_AESGCM_IV_SIZE + SGX_AESGCM_MAC_SIZE); + (void)cipher_length; + (void)plaintext_length; + decrypt(ciphertext, cipher_length, plaintext); + } catch (const std::runtime_error &e) { + ocall_throw(e.what()); + } + +} + void ecall_project(uint8_t *condition, size_t condition_length, uint8_t *input_rows, size_t input_rows_length, uint8_t **output_rows, size_t *output_rows_length) { // Guard against operating on arbitrary enclave memory @@ -242,16 +268,17 @@ static Crypto g_crypto; void ecall_finish_attestation(uint8_t *shared_key_msg_input, uint32_t shared_key_msg_size) { try { + (void) shared_key_msg_size; oe_shared_key_msg_t *shared_key_msg = (oe_shared_key_msg_t *)shared_key_msg_input; uint8_t shared_key_plaintext[SGX_AESGCM_KEY_SIZE]; size_t shared_key_plaintext_size = sizeof(shared_key_plaintext); - bool ret = g_crypto.decrypt(shared_key_msg->shared_key_ciphertext, shared_key_msg_size, + bool ret = g_crypto.decrypt(shared_key_msg->shared_key_ciphertext, OE_SHARED_KEY_CIPHERTEXT_SIZE, shared_key_plaintext, &shared_key_plaintext_size); if (!ret) { ocall_throw("shared key decryption failed"); } - set_shared_key(shared_key_plaintext, shared_key_plaintext_size); +// set_shared_key(shared_key_plaintext, shared_key_plaintext_size); } catch (const std::runtime_error &e) { ocall_throw(e.what()); } @@ -316,3 +343,177 @@ void ecall_generate_report(uint8_t **report_msg_data, size_t *report_msg_data_si } oe_free_report(report); } + +//////////////////////////////////// Generate Shared Key Begin ////////////////////////////////////// + +static Attestation attestation(&g_crypto); + +void ecall_get_public_key(uint8_t **report_msg_data, + size_t* report_msg_data_size) { + +#ifndef SIMULATE + oe_uuid_t sgx_local_uuid = {OE_FORMAT_UUID_SGX_LOCAL_ATTESTATION}; + oe_uuid_t* format_id = &sgx_local_uuid; + + uint8_t* format_settings = NULL; + size_t format_settings_size = 0; + + if (!attestation.get_format_settings( + format_id, + &format_settings, + &format_settings_size)) { + ocall_throw("Unable to get enclave format settings"); + } +#endif + + uint8_t pem_public_key[512]; + size_t public_key_size = sizeof(pem_public_key); + uint8_t* evidence = nullptr; + size_t evidence_size = 0; + + g_crypto.retrieve_public_key(pem_public_key); + +#ifndef SIMULATE + if (attestation.generate_attestation_evidence( + format_id, + format_settings, + format_settings_size, + pem_public_key, + public_key_size, + &evidence, + &evidence_size) == false) { + ocall_throw("Unable to retrieve enclave evidence"); + } + + if (!attestation.attest_attestation_evidence(format_id, evidence, evidence_size, pem_public_key, public_key_size)) { + ocall_throw("Unable to verify FRESH attestation!"); + } +#endif + + // The report msg includes the public key, the size of the evidence, and the evidence itself + *report_msg_data_size = public_key_size + sizeof(evidence_size) + evidence_size; + *report_msg_data = (uint8_t*)oe_host_malloc(*report_msg_data_size); + + memcpy_s(*report_msg_data, public_key_size, pem_public_key, public_key_size); + memcpy_s(*report_msg_data + public_key_size, sizeof(size_t), &evidence_size, sizeof(evidence_size)); + + memcpy_s(*report_msg_data + public_key_size + sizeof(size_t), evidence_size, evidence, evidence_size); + +} + +void ecall_get_list_encrypted(uint8_t * pk_list, + uint32_t pk_list_size, + uint8_t * sk_list, + uint32_t sk_list_size) { + + // Guard against encrypting or overwriting enclave memory + assert(oe_is_outside_enclave(pk_list, pk_list_size) == 1); + assert(oe_is_outside_enclave(sk_list, sk_list_size) == 1); + __builtin_ia32_lfence(); + + (void) sk_list_size; + + try { + // Generate a random value used for key + // Size of shared key is 16 from ServiceProvider - LC_AESGCM_KEY_SIZE + // For now SGX_AESGCM_KEY_SIZE is also 16, so will just use that for now + + unsigned char secret_key[SGX_AESGCM_KEY_SIZE] = {0}; + mbedtls_read_rand(secret_key, SGX_AESGCM_KEY_SIZE); + + uint8_t public_key[OE_PUBLIC_KEY_SIZE] = {}; + uint8_t *pk_pointer = pk_list; + + unsigned char encrypted_sharedkey[OE_SHARED_KEY_CIPHERTEXT_SIZE]; + size_t encrypted_sharedkey_size = sizeof(encrypted_sharedkey); + + uint8_t *sk_pointer = sk_list; + + size_t evidence_size[1] = {}; + +#ifndef SIMULATE + oe_uuid_t sgx_local_uuid = {OE_FORMAT_UUID_SGX_LOCAL_ATTESTATION}; + oe_uuid_t* format_id = &sgx_local_uuid; + + uint8_t* format_settings = NULL; + size_t format_settings_size = 0; +#endif + + while (pk_pointer < pk_list + pk_list_size) { + +#ifndef SIMULATE + if (!attestation.get_format_settings( + format_id, + &format_settings, + &format_settings_size)) { + ocall_throw("Unable to get enclave format settings"); + } +#endif + + // Read public key, size of evidence, and evidence + memcpy_s(public_key, OE_PUBLIC_KEY_SIZE, pk_pointer, OE_PUBLIC_KEY_SIZE); + +#ifndef SIMULATE + memcpy_s(evidence_size, sizeof(evidence_size), pk_pointer + OE_PUBLIC_KEY_SIZE, sizeof(size_t)); + uint8_t evidence[evidence_size[0]] = {}; + memcpy_s(evidence, evidence_size[0], pk_pointer + OE_PUBLIC_KEY_SIZE + sizeof(size_t), evidence_size[0]); + + // Verify the provided public key is valid + if (!attestation.attest_attestation_evidence(format_id, evidence, evidence_size[0], public_key, sizeof(public_key))) { + std::cout << "get_list_encrypted - unable to verify attestation evidence" << std::endl; + ocall_throw("Unable to verify attestation evidence"); + } +#endif + + g_crypto.encrypt(public_key, + secret_key, + SGX_AESGCM_KEY_SIZE, + encrypted_sharedkey, + &encrypted_sharedkey_size); + memcpy_s(sk_pointer, OE_SHARED_KEY_CIPHERTEXT_SIZE, encrypted_sharedkey, OE_SHARED_KEY_CIPHERTEXT_SIZE); + + pk_pointer += OE_PUBLIC_KEY_SIZE + sizeof(size_t) + evidence_size[0]; + sk_pointer += OE_SHARED_KEY_CIPHERTEXT_SIZE; + } + } catch (const std::runtime_error &e) { + ocall_throw(e.what()); + } + +} + +void ecall_finish_shared_key(uint8_t *sk_list, + uint32_t sk_list_size, + uint8_t *sk, + uint32_t sk_size) { + + (void) sk; + (void) sk_size; + + uint8_t *sk_pointer = sk_list; + + uint8_t secret_key[SGX_AESGCM_KEY_SIZE] = {0}; + size_t sk_length = sizeof(secret_key); + assert(sk_length == sk_size); + + while (sk_pointer < sk_list + sk_list_size) { + uint8_t encrypted_sharedkey[OE_SHARED_KEY_CIPHERTEXT_SIZE]; + size_t encrypted_sharedkey_size = sizeof(encrypted_sharedkey); + + memcpy_s(encrypted_sharedkey, encrypted_sharedkey_size, sk_pointer, OE_SHARED_KEY_CIPHERTEXT_SIZE); + + try { + bool ret = g_crypto.decrypt(encrypted_sharedkey, encrypted_sharedkey_size, secret_key, &sk_length); + if (ret) {break;} // Decryption was successful to obtain secret key + } catch (const std::runtime_error &e) { + ocall_throw(e.what()); + } + + sk_pointer += OE_SHARED_KEY_CIPHERTEXT_SIZE; + } + + set_shared_key(secret_key, sk_size); + + memcpy(sk, secret_key, SGX_AESGCM_KEY_SIZE); +} + +//////////////////////////////////// Generate Shared Key End ////////////////////////////////////// diff --git a/src/enclave/Enclave/Enclave.edl b/src/enclave/Enclave/Enclave.edl index 1789ff2b64..ee12ab1b29 100644 --- a/src/enclave/Enclave/Enclave.edl +++ b/src/enclave/Enclave/Enclave.edl @@ -23,6 +23,10 @@ enclave { [user_check] uint8_t *plaintext, uint32_t length, [user_check] uint8_t *ciphertext, uint32_t cipher_length); + public void ecall_decrypt( + [user_check] uint8_t *ciphertext, uint32_t cipher_length, + [user_check] uint8_t *plaintext, uint32_t plaintext_length); + public void ecall_sample( [user_check] uint8_t *input_rows, size_t input_rows_length, [out] uint8_t **output_rows, [out] size_t *output_rows_length); @@ -90,6 +94,18 @@ enclave { public void ecall_finish_attestation( [in,size=msg4_size] uint8_t *msg4, uint32_t msg4_size); + + public void ecall_get_public_key( + [out] uint8_t** msg1, + [out] size_t* msg1_size); + + public void ecall_get_list_encrypted( + [user_check] uint8_t *pk_list, uint32_t length, + [user_check] uint8_t *sk_list, uint32_t cipher_length); + + public void ecall_finish_shared_key( + [in,size=msg4_size] uint8_t *msg4, uint32_t msg4_size, + [user_check] uint8_t *secret_key, uint32_t secret_length); }; untrusted { diff --git a/src/enclave/Enclave/FlatbuffersReaders.cpp b/src/enclave/Enclave/FlatbuffersReaders.cpp index 74e5269a1e..c049c946b0 100644 --- a/src/enclave/Enclave/FlatbuffersReaders.cpp +++ b/src/enclave/Enclave/FlatbuffersReaders.cpp @@ -5,11 +5,9 @@ void EncryptedBlockToRowReader::reset(const tuix::EncryptedBlock *encrypted_bloc const size_t rows_len = dec_size(encrypted_block->enc_rows()->size()); rows_buf.reset(new uint8_t[rows_len]); - decrypt(encrypted_block->enc_rows()->data(), encrypted_block->enc_rows()->size(), - rows_buf.get()); + decrypt(encrypted_block->enc_rows()->data(), encrypted_block->enc_rows()->size(), rows_buf.get()); BufferRefView buf(rows_buf.get(), rows_len); buf.verify(); - rows = buf.root(); if (rows->rows()->size() != num_rows) { throw std::runtime_error(std::string("EncryptedBlock claimed to contain ") + diff --git a/src/enclave/ServiceProvider/SP.h b/src/enclave/ServiceProvider/SP.h index 0c5623e408..f7b7c0522c 100644 --- a/src/enclave/ServiceProvider/SP.h +++ b/src/enclave/ServiceProvider/SP.h @@ -6,7 +6,7 @@ extern "C" { #endif JNIEXPORT void JNICALL Java_edu_berkeley_cs_rise_opaque_execution_SP_Init(JNIEnv *, jobject, - jbyteArray, jstring); + jstring); JNIEXPORT void JNICALL Java_edu_berkeley_cs_rise_opaque_execution_SP_SPProcMsg0(JNIEnv *, jobject, jbyteArray); diff --git a/src/enclave/ServiceProvider/ServiceProvider.cpp b/src/enclave/ServiceProvider/ServiceProvider.cpp index 6ed9ba9e48..2b0c18d9cc 100644 --- a/src/enclave/ServiceProvider/ServiceProvider.cpp +++ b/src/enclave/ServiceProvider/ServiceProvider.cpp @@ -115,10 +115,6 @@ void ServiceProvider::load_private_key(const std::string &filename) { EVP_PKEY_free(pkey); } -void ServiceProvider::set_shared_key(const uint8_t *shared_key) { - memcpy(this->shared_key, shared_key, LC_AESGCM_KEY_SIZE); -} - void ServiceProvider::export_public_key_code(const std::string &filename) { std::ofstream file(filename.c_str()); @@ -238,6 +234,7 @@ ServiceProvider::process_enclave_report(oe_report_msg_t *report_msg, int ret; unsigned char encrypted_sharedkey[OE_SHARED_KEY_CIPHERTEXT_SIZE]; size_t encrypted_sharedkey_size = sizeof(encrypted_sharedkey); + std::unique_ptr shared_key_msg(new oe_shared_key_msg_t); EVP_PKEY *pkey = buffer_to_public_key((char *)report_msg->public_key, -1); diff --git a/src/enclave/ServiceProvider/ServiceProvider.h b/src/enclave/ServiceProvider/ServiceProvider.h index ad582a0b9a..539ecdb4dd 100644 --- a/src/enclave/ServiceProvider/ServiceProvider.h +++ b/src/enclave/ServiceProvider/ServiceProvider.h @@ -21,7 +21,7 @@ class ServiceProvider { * the enclaves if attestation succeeds. */ void set_shared_key(const uint8_t *shared_key); - + /** * After calling load_private_key, write the corresponding public key as a C++ * header file. This file should be compiled into the enclave. @@ -42,6 +42,7 @@ class ServiceProvider { lc_ec256_private_t sp_priv_key; uint8_t shared_key[LC_AESGCM_KEY_SIZE]; + std::string spid; std::unique_ptr ias; diff --git a/src/enclave/ServiceProvider/ServiceProviderJNI.cpp b/src/enclave/ServiceProvider/ServiceProviderJNI.cpp index 6b6f0b7b69..393050d425 100644 --- a/src/enclave/ServiceProvider/ServiceProviderJNI.cpp +++ b/src/enclave/ServiceProvider/ServiceProviderJNI.cpp @@ -18,23 +18,12 @@ void jni_throw(JNIEnv *env, const char *message) { JNIEXPORT void JNICALL Java_edu_berkeley_cs_rise_opaque_execution_SP_Init(JNIEnv *env, jobject obj, - jbyteArray shared_key, jstring intel_cert) { (void)env; (void)obj; - jboolean if_copy = false; - jbyte *shared_key_bytes = env->GetByteArrayElements(shared_key, &if_copy); - const char *intel_cert_str = env->GetStringUTFChars(intel_cert, nullptr); - try { - service_provider.set_shared_key(reinterpret_cast(shared_key_bytes)); - } catch (const std::runtime_error &e) { - jni_throw(env, e.what()); - } - - env->ReleaseByteArrayElements(shared_key, shared_key_bytes, 0); env->ReleaseStringUTFChars(intel_cert, intel_cert_str); } diff --git a/src/main/scala/edu/berkeley/cs/rise/opaque/LA.scala b/src/main/scala/edu/berkeley/cs/rise/opaque/LA.scala new file mode 100644 index 0000000000..fb67f414bf --- /dev/null +++ b/src/main/scala/edu/berkeley/cs/rise/opaque/LA.scala @@ -0,0 +1,83 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package edu.berkeley.cs.rise.opaque + +import org.apache.spark.SparkContext +import org.apache.spark.internal.Logging + +import Array.concat + +// Helper to handle enclave "local attestation" and determine shared key + +object LA extends Logging { + def initLA(sc: SparkContext): Unit = { + + var numExecutors: Int = 1 + var loop: Boolean = true + + if (!sc.isLocal) { + numExecutors = sc.getConf.getInt("spark.executor.instances", -1) + } + + val rdd = sc.parallelize(Seq.fill(numExecutors) { () }, numExecutors) + + // Obtain reports (evidence) with public keys + val msg1s = rdd + .mapPartitions{ (_) => + val (eid, msg1) = Utils.getEvidence() + Iterator((eid, msg1)) + } + .collect + .toMap + + logInfo("Driver obtained enclave public keys and reports") + + // Combine all reports into one large array + var pkArray = Array[Byte]() + for ((k, Some(v)) <- msg1s) { + pkArray = concat(pkArray, v) + } + + // Send list of public keys to enclaves + val encryptedResults = rdd.context.parallelize(Array(pkArray), 1) + .map { publicKeys => + Utils.getListEncrypted(publicKeys) + }.first() + + // Send encrypted secret key to all enclaves + val msg3s = msg1s.map{case (eid, _) => (eid, encryptedResults)} + val setSharedKeyResults = rdd + .mapPartitions { (_) => + val key = Utils.finishSharedKey(msg3s) + + // Giving Utils enclave shared key for now. Will remove later. + Utils.setSharedKey(key) + Iterator((key, true)) + } + .collect + .toMap + + for ((_, ret) <- setSharedKeyResults) { + if (!ret) + throw new OpaqueException("Failed to set shared key") + } + + // Giving Utils enclave shared key for now. Will remove later. + Utils.setSharedKey(setSharedKeyResults.head._1) + } +} diff --git a/src/main/scala/edu/berkeley/cs/rise/opaque/RA.scala b/src/main/scala/edu/berkeley/cs/rise/opaque/RA.scala index 4586ff3973..2536ac4ce3 100644 --- a/src/main/scala/edu/berkeley/cs/rise/opaque/RA.scala +++ b/src/main/scala/edu/berkeley/cs/rise/opaque/RA.scala @@ -38,14 +38,10 @@ object RA extends Logging { val rdd = sc.parallelize(Seq.fill(numExecutors) { () }, numExecutors) val intelCert = Utils.findResource("AttestationReportSigningCACert.pem") + val sp = new SP() - Utils.sharedKey match { - case Some(sharedKey) => - sp.Init(sharedKey, intelCert) - case None => - throw new OpaqueException("Cannot begin attestation without sharedKey.") - } + sp.Init(intelCert) val numAttested = Utils.numAttested // Runs on executors @@ -107,7 +103,9 @@ object RA extends Logging { logInfo( s"RA.run: ${Utils.numEnclaves.value} unattested, ${Utils.numAttested.value} attested" ) + initRA(sc) + LA.initLA(sc) } Thread.sleep(100) } diff --git a/src/main/scala/edu/berkeley/cs/rise/opaque/Utils.scala b/src/main/scala/edu/berkeley/cs/rise/opaque/Utils.scala index 81b85dd385..c4e8d0cc43 100644 --- a/src/main/scala/edu/berkeley/cs/rise/opaque/Utils.scala +++ b/src/main/scala/edu/berkeley/cs/rise/opaque/Utils.scala @@ -249,12 +249,29 @@ object Utils extends Logging { final val GCM_KEY_LENGTH = 32 final val GCM_TAG_LENGTH = 16 + // We do not trust the driver. Encryption and decryption done in enclave only. + // Leaving here, because will eventually encrypt in enclaves + +// def encrypt(data: Array[Byte]): Array[Byte] = { +// val (enclave, eid) = initEnclave() +// enclave.Encrypt(eid, data) +// } + +// def decrypt(data: Array[Byte]): Array[Byte] = { +// val (enclave, eid) = initEnclave() +// enclave.Decrypt(eid, data) + /** * Symmetric key used to encrypt row data. This key is securely sent to the enclaves if * attestation succeeds. For testing/benchmarking, we use a hardcoded key. For all other * cases, the driver SHOULD NOT be able to decrypt anything. */ - var sharedKey: Option[Array[Byte]] = None + var sharedKey: Option[Array[Byte]] = Option(Array.fill[Byte](GCM_KEY_LENGTH)(0)) + + def setSharedKey(key: Array[Byte]): Unit = { + sharedKey = Option(key) + assert(key.size == GCM_KEY_LENGTH) + } def encrypt(data: Array[Byte]): Array[Byte] = sharedKey match { case Some(sharedKey) => @@ -292,6 +309,7 @@ object Utils extends Logging { var loop: Boolean = true def initSQLContext(sqlContext: SQLContext): Unit = { + sqlContext.experimental.extraOptimizations = (Seq(EncryptLocalRelation, ConvertToOpaqueOperators) ++ sqlContext.experimental.extraOptimizations) @@ -315,6 +333,7 @@ object Utils extends Logging { RA.waitForExecutors(sc) RA.attestEnclaves(sc) RA.startThread(sc) + } def initEnclave(): (SGXEnclave, Long) = { @@ -373,6 +392,44 @@ object Utils extends Logging { } } + def getEvidence(): (Long, Option[Array[Byte]]) = { + this.synchronized { + // Only generate evidence if the enclave has already been started AND attested + if (eid != 0L && attested) { + val enclave = new SGXEnclave() + val msg1 = enclave.GetPublicKey(eid) + (eid, Option(msg1)) + } else { + (eid, None) + } + } + } + + def getListEncrypted(evidences: Array[Byte]): Array[Byte] = { + this.synchronized { + if (eid != 0L && attested) { + val enclave = new SGXEnclave() + enclave.GetListEncrypted(eid, evidences) + } else { + // Return empty array + Array[Byte]() + } + } + } + + def finishSharedKey(msg3s: Map[Long, Array[Byte]]): Array[Byte] = { + this.synchronized { + val enclave = new SGXEnclave() + if (msg3s.contains(eid) && attested) { + val msg3 = msg3s(eid) + enclave.FinishSharedKey(eid, msg3s(eid)) + } else { + // Return array of 0s + Array.fill[Byte](GCM_KEY_LENGTH)(0) + } + } + } + def cleanup(spark: SparkSession) { RA.stopThread() spark.stop() diff --git a/src/main/scala/edu/berkeley/cs/rise/opaque/execution/SGXEnclave.scala b/src/main/scala/edu/berkeley/cs/rise/opaque/execution/SGXEnclave.scala index 584b08f208..0d41c725f6 100644 --- a/src/main/scala/edu/berkeley/cs/rise/opaque/execution/SGXEnclave.scala +++ b/src/main/scala/edu/berkeley/cs/rise/opaque/execution/SGXEnclave.scala @@ -84,4 +84,9 @@ class SGXEnclave extends java.io.Serializable { // Remote attestation, enclave side @native def GenerateReport(eid: Long): Array[Byte] @native def FinishAttestation(eid: Long, attResultInput: Array[Byte]): Unit + + // "Local attestation" to determine shared key, enclave side + @native def GetPublicKey(eid: Long): Array[Byte] + @native def GetListEncrypted(eid: Long, publicKeyList: Array[Byte]): Array[Byte] + @native def FinishSharedKey(eid: Long, encryptedKeyList: Array[Byte]): Array[Byte] } diff --git a/src/main/scala/edu/berkeley/cs/rise/opaque/execution/SP.scala b/src/main/scala/edu/berkeley/cs/rise/opaque/execution/SP.scala index b22fac2ecd..91caa4a7ca 100644 --- a/src/main/scala/edu/berkeley/cs/rise/opaque/execution/SP.scala +++ b/src/main/scala/edu/berkeley/cs/rise/opaque/execution/SP.scala @@ -22,7 +22,7 @@ import ch.jodersky.jni.nativeLoader @nativeLoader("ra_jni") class SP extends java.io.Serializable { // Remote attestation, master side - @native def Init(sharedKey: Array[Byte], intelCert: String): Unit + @native def Init(intelCert: String): Unit @native def SPProcMsg0(msg0Input: Array[Byte]): Unit @native def ProcessEnclaveReport(msg1Input: Array[Byte]): Array[Byte] @native def SPProcMsg3(msg3Input: Array[Byte]): Array[Byte] diff --git a/src/main/scala/edu/berkeley/cs/rise/opaque/execution/operators.scala b/src/main/scala/edu/berkeley/cs/rise/opaque/execution/operators.scala index 7d5b7315f2..0e9d7e4d23 100644 --- a/src/main/scala/edu/berkeley/cs/rise/opaque/execution/operators.scala +++ b/src/main/scala/edu/berkeley/cs/rise/opaque/execution/operators.scala @@ -186,6 +186,7 @@ trait OpaqueOperatorExec extends SparkPlan { collectEncrypted().flatMap { block => Utils.decryptBlockFlatbuffers(block) } +// collectEncrypted() } override def executeTake(n: Int): Array[InternalRow] = { diff --git a/src/test/scala/edu/berkeley/cs/rise/opaque/OpaqueSpecificSuite.scala b/src/test/scala/edu/berkeley/cs/rise/opaque/OpaqueSpecificSuite.scala index bb75e76ddb..e4ee69569e 100644 --- a/src/test/scala/edu/berkeley/cs/rise/opaque/OpaqueSpecificSuite.scala +++ b/src/test/scala/edu/berkeley/cs/rise/opaque/OpaqueSpecificSuite.scala @@ -37,13 +37,6 @@ class OpaqueSpecificSuite extends OpaqueSuiteBase with SinglePartitionSparkSessi case 2 => "C" } - test("java encryption/decryption") { - val data = Array[Byte](0, 1, 2) - val (enclave, eid) = Utils.initEnclave() - assert(data === Utils.decrypt(Utils.encrypt(data))) - assert(data === Utils.decrypt(enclave.Encrypt(eid, data))) - } - test("cache") { def numCached(ds: Dataset[_]): Int = ds.queryExecution.executedPlan.collect { @@ -166,6 +159,8 @@ class OpaqueSpecificSuite extends OpaqueSuiteBase with SinglePartitionSparkSessi assert(e.getCause.isInstanceOf[OpaqueException]) } + // Honestly, a little confused about this one. Works in manual shell but not build/sbt + // Needed to add enclave share key to Utils on workers. test("encrypted literal") { checkAnswer() { sl => val input = 10 @@ -176,4 +171,13 @@ class OpaqueSpecificSuite extends OpaqueSuiteBase with SinglePartitionSparkSessi words.filter($"id" < decrypt(lit(enc_str), IntegerType)).sort($"id") } } + + test("java encryption/decryption") { + val data = Array[Byte](0, 1, 2) + assert(data === Utils.decrypt(Utils.encrypt(data))) + + // Invalid test: enclave secret key should only be on trusted enclaves. +// val (enclave, eid) = Utils.initEnclave() +// assert(data === Utils.decrypt(enclave.Encrypt(eid, data))) + } } diff --git a/src/test/scala/edu/berkeley/cs/rise/opaque/SharedSparkSessions.scala b/src/test/scala/edu/berkeley/cs/rise/opaque/SharedSparkSessions.scala index 665033a5ce..f389a5d3c4 100644 --- a/src/test/scala/edu/berkeley/cs/rise/opaque/SharedSparkSessions.scala +++ b/src/test/scala/edu/berkeley/cs/rise/opaque/SharedSparkSessions.scala @@ -20,12 +20,19 @@ package edu.berkeley.cs.rise.opaque import org.apache.spark.sql.SparkSession trait SinglePartitionSparkSession { + val executorInstances = 1 def numPartitions = 1 + val spark = SparkSession .builder() - .master("local[*]") + .master(s"local-cluster[$executorInstances, 1, 4096]") .appName("SinglePartitionSuiteSession") + .config("spark.executor.instances", executorInstances) .config("spark.sql.shuffle.partitions", numPartitions) + .config( + "spark.jars", + "target/scala-2.12/opaque_2.12-0.1.jar,target/scala-2.12/opaque_2.12-0.1-tests.jar" + ) .config("spark.opaque.testing.enableSharedKey", true) .getOrCreate() }