mirror of
https://github.com/corda/corda.git
synced 2024-12-29 09:18:58 +00:00
c545a58c1d
* 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.
220 lines
6.6 KiB
C++
220 lines
6.6 KiB
C++
#include <cstring>
|
|
#include <sgx_tcrypto.h>
|
|
#include <sgx_tkey_exchange.h>
|
|
|
|
#include "enclave_t.h"
|
|
|
|
#define CHECKED(expr) { \
|
|
sgx_status_t _status = (expr); \
|
|
if (SGX_SUCCESS != _status) { return _status; } \
|
|
}
|
|
|
|
#define MAX_SECRET_SIZE 128
|
|
|
|
extern "C" {
|
|
|
|
static const uint8_t safe_empty[] = {};
|
|
|
|
// === Initialization and Finalization =======================================
|
|
|
|
static inline sgx_status_t create_pse_session(
|
|
bool use_platform_services
|
|
) {
|
|
// If desired, try up to three times to establish a PSE session.
|
|
sgx_status_t status = SGX_SUCCESS;
|
|
if (use_platform_services) {
|
|
int retry_count = 3;
|
|
do { status = sgx_create_pse_session(); }
|
|
while (SGX_ERROR_BUSY == status && --retry_count);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
static inline sgx_status_t close_pse_session(
|
|
bool use_platform_services
|
|
) {
|
|
// If a PSE session was created, close it properly.
|
|
sgx_status_t status = SGX_SUCCESS;
|
|
if (use_platform_services) {
|
|
status = sgx_close_pse_session();
|
|
}
|
|
return status;
|
|
}
|
|
|
|
// Initialize the remote attestation process.
|
|
sgx_status_t initializeRemoteAttestation(
|
|
bool use_platform_services,
|
|
const sgx_ec256_public_t *challenger_key,
|
|
sgx_ra_context_t *context
|
|
) {
|
|
sgx_status_t status;
|
|
|
|
// Abort if the public key of the challenger and/or the output context
|
|
// variable is not provided.
|
|
if (NULL == challenger_key || NULL == context) {
|
|
return SGX_ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
// If desired, try to establish a PSE session.
|
|
CHECKED(create_pse_session(use_platform_services));
|
|
|
|
// Initialize the remote attestation and key exchange process, and place
|
|
// the resulting context in `context`.
|
|
status = sgx_ra_init(challenger_key, use_platform_services, context);
|
|
|
|
// If a PSE session was created, close it properly.
|
|
CHECKED(close_pse_session(use_platform_services));
|
|
return status;
|
|
}
|
|
|
|
// Clean up and finalize the remote attestation process.
|
|
sgx_status_t finalizeRemoteAttestation(
|
|
sgx_ra_context_t context
|
|
) {
|
|
// Release the remote attestation and key exchange context after the
|
|
// process has been completed.
|
|
return sgx_ra_close(context);
|
|
}
|
|
|
|
// === Remote Attestation Verification =======================================
|
|
|
|
/*
|
|
* This function compares `len` bytes from buffer `b1` and `b2` in constant
|
|
* time to protect against side-channel attacks. For sensitive code running
|
|
* inside an enclave, this function is preferred over `memcmp`.
|
|
*/
|
|
static int consttime_memequal(const void *b1, const void *b2, size_t len) {
|
|
// Written by Matthias Drochner <drochner@NetBSD.org>. Public domain.
|
|
const unsigned char
|
|
*c1 = (unsigned char*)b1,
|
|
*c2 = (unsigned char*)b2;
|
|
unsigned int res = 0;
|
|
|
|
while (len--) res |= *c1++ ^ *c2++;
|
|
|
|
/*
|
|
* Map 0 to 1 and [1, 256) to 0 using only constant-time
|
|
* arithmetic.
|
|
*
|
|
* This is not simply `!res' because although many CPUs support branchless
|
|
* conditional moves and many compilers will take advantage of them,
|
|
* certain compilers generate branches on certain CPUs for `!res'.
|
|
*/
|
|
return (1 & ((res - 1) >> 8));
|
|
}
|
|
|
|
// Verify CMAC from the challenger to protect against spoofed results.
|
|
sgx_status_t verifyCMAC(
|
|
sgx_ra_context_t context,
|
|
const uint8_t *message,
|
|
size_t message_size,
|
|
const uint8_t *cmac,
|
|
size_t cmac_size
|
|
) {
|
|
// Check inputs.
|
|
if (sizeof(sgx_mac_t) != cmac_size || NULL == cmac) {
|
|
return SGX_ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (UINT32_MAX < message_size || ((NULL == message) && (message_size > 0))) {
|
|
return SGX_ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
// Get negotiated MK key of remote attestation and key exchange session.
|
|
sgx_ec_key_128bit_t mk_key = { 0 };
|
|
CHECKED(sgx_ra_get_keys(context, SGX_RA_KEY_MK, &mk_key));
|
|
|
|
// Perform 128-bit CMAC hash over the first four bytes of the status
|
|
// obtained from the challenger.
|
|
uint8_t computed_cmac[SGX_CMAC_MAC_SIZE] = { 0 };
|
|
const uint8_t* safe_message = (message == NULL) ? safe_empty : message;
|
|
CHECKED(sgx_rijndael128_cmac_msg(
|
|
&mk_key, safe_message, message_size, &computed_cmac
|
|
));
|
|
|
|
// Compare the computed CMAC-SMK with the provided one.
|
|
if (0 == consttime_memequal(computed_cmac, cmac, sizeof(computed_cmac))) {
|
|
return SGX_ERROR_MAC_MISMATCH;
|
|
}
|
|
|
|
// Can further test number of uses of the secret data, and require
|
|
// re-attestation after X uses... But, for now, we're happy!
|
|
return SGX_SUCCESS;
|
|
}
|
|
|
|
// Verify attestation response from the challenger.
|
|
sgx_status_t verifyAttestationResponse(
|
|
sgx_ra_context_t context,
|
|
const uint8_t *secret,
|
|
size_t secret_size,
|
|
const uint8_t *gcm_iv,
|
|
const uint8_t *gcm_mac,
|
|
size_t gcm_mac_size,
|
|
uint8_t *sealed_secret,
|
|
size_t sealed_secret_size
|
|
) {
|
|
// Check inputs.
|
|
if (secret_size > MAX_SECRET_SIZE || NULL == secret) {
|
|
return SGX_ERROR_INVALID_PARAMETER;
|
|
}
|
|
if (gcm_mac_size != SGX_AESGCM_MAC_SIZE || NULL == gcm_mac) {
|
|
return SGX_ERROR_INVALID_PARAMETER;
|
|
}
|
|
if (sealed_secret_size - sizeof(sgx_sealed_data_t) > MAX_SECRET_SIZE) {
|
|
return SGX_ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
// Get negotiated SK key of remote attestation and key exchange session.
|
|
sgx_ec_key_128bit_t sk_key;
|
|
CHECKED(sgx_ra_get_keys(context, SGX_RA_KEY_SK, &sk_key));
|
|
|
|
// Decrypt using Rijndael AES-GCM.
|
|
uint8_t *decrypted_secret = (uint8_t*)malloc(secret_size);
|
|
CHECKED(sgx_rijndael128GCM_decrypt(
|
|
&sk_key, secret, secret_size, decrypted_secret, gcm_iv,
|
|
SGX_AESGCM_IV_SIZE, NULL, 0, (sgx_aes_gcm_128bit_tag_t*)gcm_mac
|
|
));
|
|
|
|
// Return sealed secret if requested.
|
|
if (NULL != sealed_secret && secret_size <= sealed_secret_size) {
|
|
// Seal the secret so that it can be returned to the untrusted
|
|
// environment.
|
|
CHECKED(sgx_seal_data(
|
|
0, NULL, secret_size, decrypted_secret,
|
|
sealed_secret_size, (sgx_sealed_data_t*)sealed_secret
|
|
));
|
|
}
|
|
|
|
// Free temporary memory.
|
|
free(decrypted_secret);
|
|
|
|
return SGX_SUCCESS;
|
|
}
|
|
|
|
// Check whether the sealed secret is unsealable or not.
|
|
sgx_status_t unsealSecret(
|
|
uint8_t *sealed_secret,
|
|
size_t sealed_secret_size
|
|
) {
|
|
// Allocate temporary buffer for the output of the operation.
|
|
uint8_t *buffer = (uint8_t*)malloc(sealed_secret_size);
|
|
uint32_t buffer_size = sealed_secret_size;
|
|
|
|
if (NULL == buffer) {
|
|
return SGX_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
// Attempt to unseal the secret.
|
|
sgx_status_t status = sgx_unseal_data(
|
|
(sgx_sealed_data_t*)sealed_secret, NULL, NULL,
|
|
buffer, &buffer_size
|
|
);
|
|
|
|
// Free up the temporary memory buffer.
|
|
free(buffer);
|
|
|
|
return status;
|
|
}
|
|
|
|
}
|