corda/sgx-jvm/remote-attestation/attestation-host/native/remote-attestation.cpp
Chris Rankin c545a58c1d
Remote Attestation Phase 2 (#235)
* Initial host server skeleton.
* Create IASProxy project, and skeleton for attestation host.
* Fix up tests
* Extend attestation host skeleton, and make test ports configurable.
* Enhance MockIAS to make pseManifestStatus optional.
* Make IASProxy endpoints asynchronous.
* Add sub-modules for challenger and for common code.
* Create integration test for host's provisioning endpoint.
* Flesh out attestation challenger WAR.
* Package refactoring, to be more Java9 friendly.
* Refactor more messages into attestation-common.
* Remove our private key from the repository.
* Declare an empty PSE Manifest to be invalid.
* Fix basic integration test issues for challenger and host.
* Integrate keystore scripts into the build properly.
* Name keystore targets explicitly for Gradle.
* Allow HTTP conversation between Challenger, Host and ISV using session ID.
* Add MockHost for challenger's integration tests.
* Reconcile HTTP port numbers between Phase1 and Phase2 components.
* Remove elements that can be inherited from root project.
* Add placeholder README.
* Add convenient extension functions to ObjectMapper.
* Extend integration test coverage for challenger/host/isv.
* Catch IOException from HttpClient for challenger.
* Integrate host sub-module with remote-attestation project.
* Begin integrating host/enclave code from Phase I.
* Rename challenger's HTTP endpoint.
* Generate keystore for challenger "on the fly".
* Add native JNI code for accessing the SGX enclave.
* Point Gradle to the correct enclave object.
* Fixes for generating a Quote for this enclave.
* Return the IAS report to the challenger for verification.
* Begin populating the challenger's AttestationResponse message.
* Enable the challenger to pass encrypted secrets into the enclave.
* Align challenger, host and isv ports.
* Refactor challenger as a fat-jar application.
* AttestationResponse is not shared, so refactor into challenger.
* Move HttpClientContext objects into HttpClient blocks.
* Remove unused Message2 and Message3 objects.
* Add realistic dummy value for reportID from IAS.
* Small tidy-up on attestation host.
* First set of review comments.
* Add missing exception message.
* Update location of environment file.
* Use empty mock revocation lists by default.
* Improve logging and add "happy path" test for provisioning secrets.
* Update Gradle files so that we can run attestation-host from IntelliJ.
* The platformInfo field from IAS can be null, so allow this.
Also protect other JNI pointer parameters from NPE.
* Allow Gradle to build hardware enclave.
2017-12-22 14:42:42 +00:00

286 lines
8.7 KiB
C++

#include <string.h>
#include <unistd.h>
#include <sgx_uae_service.h>
#include "enclave_u.h"
#include "logging.hpp"
#include "remote-attestation.hpp"
// Initialize the remote attestation.
sgx_status_t initialize_remote_attestation(
// Inputs
sgx_enclave_id_t enclave_id,
bool use_platform_services,
sgx_ec256_public_t *key_challenger,
// Outputs
sgx_ra_context_t *context
) {
sgx_status_t ret;
// Perform ECALL into the application enclave to initialize the remote
// attestation. The resulting attestation context will be stored in the
// variable referenced by the `context` parameter.
sgx_status_t _ret = initializeRemoteAttestation(
enclave_id, &ret, use_platform_services, key_challenger, context
);
LOG(enclave_id, _ret | ret, *context, "initialize_remote_attestation()");
// If the ECALL itself failed, report why. Otherwise, return the status of
// the underlying function call.
return (SGX_SUCCESS != _ret) ? _ret : ret;
}
// Clean up and finalize the remote attestation process.
sgx_status_t finalize_remote_attestation(
sgx_enclave_id_t enclave_id,
sgx_ra_context_t context
) {
sgx_status_t ret;
// Perform ECALL into the application enclave to close the current
// attestation context and tidy up.
sgx_status_t _ret = finalizeRemoteAttestation(enclave_id, &ret, context);
LOG(enclave_id, _ret | ret, context, "finalize_remote_attestation()");
// If the ECALL itself failed, report why. Otherwise, return the status of
// the underlying function call.
return (SGX_SUCCESS != _ret) ? _ret : ret;
}
// Retrieve the application enclave's public key and the platform's group
// identifier.
sgx_status_t get_public_key_and_group_identifier(
// Inputs
sgx_enclave_id_t enclave_id,
sgx_ra_context_t context,
// Outputs
sgx_ec256_public_t *public_key,
sgx_epid_group_id_t *group_id,
// Retry logic
int max_retry_count,
unsigned int retry_wait_in_secs
) {
sgx_status_t ret;
sgx_ra_msg1_t message;
// It is generally recommended that the caller should wait (typically
// several seconds to tens of seconds) and retry `sgx_ra_get_msg1()` if
// `SGX_ERROR_BUSY` is returned.
int retry_count = max_retry_count;
while (retry_count-- >= 0) {
// Using an ECALL proxy to `sgx_ra_get_ga()` in the `sgx_tkey_exchange`
// library to retrieve the public key of the application enclave.
ret = sgx_ra_get_msg1(context, enclave_id, sgx_ra_get_ga, &message);
LOG(enclave_id, ret, context, "sgx_ra_get_msg1()");
if (SGX_ERROR_BUSY == ret) {
// Wait before retrying...
sleep(retry_wait_in_secs);
} else if (SGX_SUCCESS != ret) {
return ret;
} else {
break;
}
}
// Store the public key; components X and Y, each 256 bits long.
if (NULL != public_key) {
memcpy(public_key, &message.g_a, sizeof(sgx_ec256_public_t));
}
// Store the EPID group identifier. Note, this is not the same as the
// extended group identifier.
if (NULL != group_id) {
memcpy(group_id, &message.gid, sizeof(sgx_epid_group_id_t));
}
return ret;
}
// Process details received from challenger via the service provider, and
// generate quote.
sgx_status_t process_challenger_details_and_generate_quote(
// Inputs
sgx_enclave_id_t enclave_id,
sgx_ra_context_t context,
sgx_ec256_public_t *challenger_public_key,
sgx_spid_t *service_provider_id,
uint16_t quote_type,
uint16_t key_derivation_function,
sgx_ec256_signature_t *signature,
sgx_mac_t *challenger_mac,
uint32_t revocation_list_size,
uint8_t *revocation_list,
// Outputs
sgx_mac_t *enclave_mac,
sgx_ec256_public_t *enclave_public_key,
sgx_ps_sec_prop_desc_t *security_properties,
uint8_t **quote,
size_t *quote_size,
// Retry logic
int max_retry_count,
unsigned int retry_wait_in_secs
) {
sgx_status_t ret = SGX_SUCCESS;
size_t msg_in_size = sizeof(sgx_ra_msg2_t) + revocation_list_size;
sgx_ra_msg2_t *msg_in = (sgx_ra_msg2_t*)malloc(msg_in_size);
sgx_ra_msg3_t *msg_out = NULL;
uint32_t msg_out_size;
if (NULL == msg_in) {
return SGX_ERROR_OUT_OF_MEMORY;
}
// Populate input message (message 2 in the Intel attestation flow).
memcpy(&msg_in->g_b, challenger_public_key, sizeof(sgx_ec256_public_t));
memcpy(&msg_in->spid, service_provider_id, sizeof(sgx_spid_t));
msg_in->quote_type = quote_type;
msg_in->kdf_id = key_derivation_function;
memcpy(&msg_in->sign_gb_ga, signature, sizeof(sgx_ec256_signature_t));
memcpy(&msg_in->mac, challenger_mac, sizeof(sgx_mac_t));
msg_in->sig_rl_size = revocation_list_size;
if (revocation_list_size > 0) {
memcpy(&msg_in->sig_rl, revocation_list, revocation_list_size);
}
// Nullify outputs.
*quote = NULL;
// It is generally recommended that the caller should wait (typically
// several seconds to tens of seconds) and retry `sgx_ra_proc_msg2()` if
// `SGX_ERROR_BUSY` is returned.
int retry_count = max_retry_count;
while (retry_count-- >= 0) {
// Using an ECALL proxy to `sgx_ra_proc_msg2_trusted()` in the
// `sgx_tkey_exchange` library to process the incoming details from the
// challenger, and `sgx_ra_get_msg3_trusted()` in the same library to
// generate the quote.
ret = sgx_ra_proc_msg2(
context,
enclave_id,
sgx_ra_proc_msg2_trusted,
sgx_ra_get_msg3_trusted,
msg_in,
sizeof(sgx_ra_msg2_t) + revocation_list_size,
&msg_out,
&msg_out_size
);
LOG(enclave_id, ret, context, "sgx_ra_proc_msg2()");
if (SGX_ERROR_BUSY == ret) {
// Wait before retrying...
sleep(retry_wait_in_secs);
} else {
break;
}
}
// Populate outputs from the returned message structure.
if (NULL != msg_out) {
memcpy(enclave_mac, &msg_out->mac, sizeof(sgx_mac_t));
memcpy(enclave_public_key, &msg_out->g_a, sizeof(sgx_ec256_public_t));
size_t z_sec_prop = sizeof(sgx_ps_sec_prop_desc_t);
memcpy(security_properties, &msg_out->ps_sec_prop, z_sec_prop);
}
// Populate the quote structure.
if (NULL != msg_out) {
*quote_size = msg_out_size - offsetof(sgx_ra_msg3_t, quote);
*quote = (uint8_t*)malloc(*quote_size);
if (NULL != quote) {
memcpy(*quote, &msg_out->quote, *quote_size);
}
} else {
*quote = NULL;
}
// The output message is generated by the library and thus has to be freed
// upon completion.
free(msg_out);
// Allocated due to the variable size revocation list. Free up the
// temporary structure.
free(msg_in);
// Check if the malloc() call for the output quote failed above; if it did,
// it was due to an out-of-memory condition.
if (NULL == quote && SGX_SUCCESS == ret) {
return SGX_ERROR_OUT_OF_MEMORY;
}
return ret;
}
sgx_status_t verify_attestation_response(
// Inputs
sgx_enclave_id_t enclave_id,
sgx_ra_context_t context,
uint8_t *message,
size_t message_size,
uint8_t *cmac,
size_t cmac_size,
uint8_t *secret,
size_t secret_size,
uint8_t *gcm_iv,
uint8_t *gcm_mac,
size_t gcm_mac_size,
// Outputs
uint8_t *sealed_secret,
size_t *sealed_secret_size,
sgx_status_t *cmac_status
) {
// Check the generated CMAC from the service provider.
sgx_status_t ret = SGX_SUCCESS;
sgx_status_t _ret = verifyCMAC(
enclave_id, &ret, context, message, message_size, cmac, cmac_size
);
*cmac_status = ret;
LOG(enclave_id, _ret, context, "verify_cmac() = %x", (uint32_t)ret);
// Abort if call failed. Otherwise, forward the outcome to the caller.
if (SGX_SUCCESS != _ret) {
return _ret;
}
// Try to decrypt and verify the attestation response.
_ret = verifyAttestationResponse(
enclave_id, &ret, context, secret, secret_size,
gcm_iv, gcm_mac, gcm_mac_size,
sealed_secret, sizeof(sgx_sealed_data_t) + secret_size
);
LOG(
enclave_id, _ret, context,
"verify_attestation_response() = %x",
(uint32_t)ret
);
// Abort if unable to verify attestation response.
if (SGX_SUCCESS != (_ret | ret)) {
return (SGX_SUCCESS != _ret) ? _ret : ret;
}
// Return sealed secret if requested. The buffer is populated by the ECALL
// above, if sealed_secret is non-null.
if (NULL != sealed_secret_size) {
*sealed_secret_size = sizeof(sgx_sealed_data_t) + secret_size;
}
return SGX_SUCCESS;
}