corda/sgx-jvm/remote-attestation/enclave/enclave.cpp
Tommy Lillehagen 83d6a248a8
ENT-970 - SGX remote attestation host (#173)
* ENT-970 - SGX remote attestation host
 * Remote attestation enclave
 * Client for the remote attestation host
    * Communicates with ISV / RA server, which in turn communicates with
      the Intel Attestation Service
    * Native library bridging the client code running on the JVM with
      the native bits controlling and communicating with the enclave

* ENT-970 - Address comments from code review
* ENT-970 - More updates addressing review comments
* ENT-970 - Integrate with root Gradle project for SGX
2017-12-13 17:45:33 +00:00

217 lines
6.5 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" {
// === 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,
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,
uint8_t *message,
size_t message_size,
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) {
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 };
CHECKED(sgx_rijndael128_cmac_msg(
&mk_key, 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,
uint8_t *secret,
size_t secret_size,
uint8_t *gcm_iv,
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;
}
}