mirror of
https://github.com/intel/linux-sgx
synced 2026-06-08 14:49:32 +00:00
26c458905b
Signed-off-by: Zhang Lili <lili.z.zhang@intel.com>
868 lines
32 KiB
C++
868 lines
32 KiB
C++
/*
|
|
* Copyright (C) 2011-2021 Intel Corporation. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* * Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in
|
|
* the documentation and/or other materials provided with the
|
|
* distribution.
|
|
* * Neither the name of Intel Corporation nor the names of its
|
|
* contributors may be used to endorse or promote products derived
|
|
* from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
*/
|
|
|
|
|
|
|
|
#include "service_provider.h"
|
|
|
|
#include "sample_libcrypto.h"
|
|
|
|
#include "ecp.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stddef.h>
|
|
#include <time.h>
|
|
#include <string.h>
|
|
#include "ias_ra.h"
|
|
|
|
#ifndef SAFE_FREE
|
|
#define SAFE_FREE(ptr) {if (NULL != (ptr)) {free(ptr); (ptr) = NULL;}}
|
|
#endif
|
|
|
|
// This is supported extended epid group of SP. SP can support more than one
|
|
// extended epid group with different extended epid group id and credentials.
|
|
static const sample_extended_epid_group g_extended_epid_groups[] = {
|
|
{
|
|
0,
|
|
ias_enroll,
|
|
ias_get_sigrl,
|
|
ias_verify_attestation_evidence
|
|
}
|
|
};
|
|
|
|
// This is the private EC key of SP, the corresponding public EC key is
|
|
// hard coded in isv_enclave. It is based on NIST P-256 curve.
|
|
static const sample_ec256_private_t g_sp_priv_key = {
|
|
{
|
|
0x90, 0xe7, 0x6c, 0xbb, 0x2d, 0x52, 0xa1, 0xce,
|
|
0x3b, 0x66, 0xde, 0x11, 0x43, 0x9c, 0x87, 0xec,
|
|
0x1f, 0x86, 0x6a, 0x3b, 0x65, 0xb6, 0xae, 0xea,
|
|
0xad, 0x57, 0x34, 0x53, 0xd1, 0x03, 0x8c, 0x01
|
|
}
|
|
};
|
|
|
|
// This is the public EC key of SP, this key is hard coded in isv_enclave.
|
|
// It is based on NIST P-256 curve. Not used in the SP code.
|
|
static const sample_ec_pub_t g_sp_pub_key = {
|
|
{
|
|
0x72, 0x12, 0x8a, 0x7a, 0x17, 0x52, 0x6e, 0xbf,
|
|
0x85, 0xd0, 0x3a, 0x62, 0x37, 0x30, 0xae, 0xad,
|
|
0x3e, 0x3d, 0xaa, 0xee, 0x9c, 0x60, 0x73, 0x1d,
|
|
0xb0, 0x5b, 0xe8, 0x62, 0x1c, 0x4b, 0xeb, 0x38
|
|
},
|
|
{
|
|
0xd4, 0x81, 0x40, 0xd9, 0x50, 0xe2, 0x57, 0x7b,
|
|
0x26, 0xee, 0xb7, 0x41, 0xe7, 0xc6, 0x14, 0xe2,
|
|
0x24, 0xb7, 0xbd, 0xc9, 0x03, 0xf2, 0x9a, 0x28,
|
|
0xa8, 0x3c, 0xc8, 0x10, 0x11, 0x14, 0x5e, 0x06
|
|
}
|
|
};
|
|
|
|
const uint8_t g_ecdsa_p256_att_key_id_list[] = {
|
|
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x8c, 0x4f,
|
|
0x57, 0x75, 0xd7, 0x96, 0x50, 0x3e, 0x96, 0x13,
|
|
0x7f, 0x77, 0xc6, 0x8a, 0x82, 0x9a, 0x00, 0x56,
|
|
0xac, 0x8d, 0xed, 0x70, 0x14, 0x0b, 0x08, 0x1b,
|
|
0x09, 0x44, 0x90, 0xc5, 0x7b, 0xff, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
};
|
|
const uint8_t g_epid_unlinkable_att_key_id_list[] = {
|
|
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0xec, 0x15,
|
|
0xb1, 0x07, 0x87, 0xd2, 0xf8, 0x46, 0x67, 0xce,
|
|
0xb0, 0xb5, 0x98, 0xff, 0xc4, 0x4a, 0x1f, 0x1c,
|
|
0xb8, 0x0f, 0x67, 0x0a, 0xae, 0x5d, 0xf9, 0xe8,
|
|
0xfa, 0x9f, 0x63, 0x76, 0xe1, 0xf8, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
};
|
|
|
|
|
|
// This is a context data structure used on SP side
|
|
typedef struct _sp_db_item_t
|
|
{
|
|
sample_ec_pub_t g_a;
|
|
sample_ec_pub_t g_b;
|
|
sample_ec_key_128bit_t vk_key;// Shared secret key for the REPORT_DATA
|
|
sample_ec_key_128bit_t mk_key;// Shared secret key for generating MAC's
|
|
sample_ec_key_128bit_t sk_key;// Shared secret key for encryption
|
|
sample_ec_key_128bit_t smk_key;// Used only for SIGMA protocol
|
|
sample_ec_priv_t b;
|
|
sample_ps_sec_prop_desc_t ps_sec_prop;
|
|
}sp_db_item_t;
|
|
static sp_db_item_t g_sp_db;
|
|
|
|
static const sample_extended_epid_group* g_sp_extended_epid_group_id= NULL;
|
|
static bool g_is_sp_registered = false;
|
|
static bool g_return_ecdsa_att_key_id = true;
|
|
static int g_sp_credentials = 0;
|
|
static int g_authentication_token = 0;
|
|
|
|
uint8_t g_secret[] = {0,1,2,3,4,5,6,7};
|
|
|
|
sample_spid_t g_spid;
|
|
|
|
|
|
// Verify message 0 then configure extended epid group.
|
|
int sp_ra_proc_msg0_req(const sample_ra_msg0_t *p_msg0,
|
|
uint32_t msg0_size,
|
|
ra_samp_response_header_t **pp_msg0_resp)
|
|
{
|
|
int ret = -1;
|
|
uint32_t msg0_resp_size = 0;
|
|
ra_samp_response_header_t* p_msg0_resp_full = NULL;
|
|
|
|
if (!p_msg0 ||
|
|
!pp_msg0_resp ||
|
|
(msg0_size != sizeof(sample_ra_msg0_t)))
|
|
{
|
|
return -1;
|
|
}
|
|
uint32_t extended_epid_group_id = p_msg0->extended_epid_group_id;
|
|
|
|
// Check to see if we have registered with the attestation server yet?
|
|
if (!g_is_sp_registered ||
|
|
(g_sp_extended_epid_group_id != NULL && g_sp_extended_epid_group_id->extended_epid_group_id != extended_epid_group_id))
|
|
{
|
|
// Check to see if the extended_epid_group_id is supported?
|
|
ret = SP_UNSUPPORTED_EXTENDED_EPID_GROUP;
|
|
for (size_t i = 0; i < sizeof(g_extended_epid_groups) / sizeof(sample_extended_epid_group); i++)
|
|
{
|
|
if (g_extended_epid_groups[i].extended_epid_group_id == extended_epid_group_id)
|
|
{
|
|
g_sp_extended_epid_group_id = &(g_extended_epid_groups[i]);
|
|
// In the product, the SP will establish a mutually
|
|
// authenticated SSL channel. During the enrollment process, the ISV
|
|
// registers it exchanges TLS certs with attestation server and obtains an SPID and
|
|
// Report Key from the attestation server.
|
|
// For a product attestation server, enrollment is an offline process. See the 'on-boarding'
|
|
// documentation to get the information required. The enrollment process is
|
|
// simulated by a call in this sample.
|
|
ret = g_sp_extended_epid_group_id->enroll(g_sp_credentials, &g_spid,
|
|
&g_authentication_token);
|
|
if (0 != ret)
|
|
{
|
|
ret = SP_IAS_FAILED;
|
|
goto CLEANUP;
|
|
}
|
|
|
|
g_is_sp_registered = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Return ECDSA attestation key id first
|
|
if (g_return_ecdsa_att_key_id)
|
|
{
|
|
msg0_resp_size = (uint32_t)sizeof(g_ecdsa_p256_att_key_id_list);
|
|
// Next time we will return EPID attestation key id
|
|
}
|
|
else // Return EPID attestation key id
|
|
{
|
|
msg0_resp_size = (uint32_t)sizeof(g_epid_unlinkable_att_key_id_list);
|
|
}
|
|
|
|
p_msg0_resp_full = (ra_samp_response_header_t*)malloc(msg0_resp_size
|
|
+ sizeof(ra_samp_response_header_t));
|
|
if(!p_msg0_resp_full)
|
|
{
|
|
fprintf(stderr, "\nError, out of memory in [%s].", __FUNCTION__);
|
|
ret = SP_INTERNAL_ERROR;
|
|
}
|
|
else
|
|
{
|
|
memset(p_msg0_resp_full, 0, msg0_resp_size + sizeof(ra_samp_response_header_t));
|
|
|
|
if (g_return_ecdsa_att_key_id)
|
|
{
|
|
memcpy_s(p_msg0_resp_full->body, msg0_resp_size,
|
|
g_ecdsa_p256_att_key_id_list, msg0_resp_size);
|
|
g_return_ecdsa_att_key_id = false;
|
|
}
|
|
else // Return EPID attestation key id
|
|
{
|
|
memcpy_s(p_msg0_resp_full->body, msg0_resp_size,
|
|
g_epid_unlinkable_att_key_id_list, msg0_resp_size);
|
|
}
|
|
p_msg0_resp_full->type = TYPE_RA_MSG0;
|
|
p_msg0_resp_full->size = msg0_resp_size;
|
|
// The simulated message0 always passes. This would need to be set
|
|
// accordingly in a real service provider implementation.
|
|
p_msg0_resp_full->status[0] = 0;
|
|
p_msg0_resp_full->status[1] = 0;
|
|
ret = SP_OK;
|
|
}
|
|
|
|
CLEANUP:
|
|
if(ret)
|
|
{
|
|
*pp_msg0_resp = NULL;
|
|
SAFE_FREE(p_msg0_resp_full);
|
|
}
|
|
else
|
|
{
|
|
// Freed by the network simulator in ra_free_network_response_buffer
|
|
*pp_msg0_resp = p_msg0_resp_full;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Verify message 1 then generate and return message 2 to isv.
|
|
int sp_ra_proc_msg1_req(const sample_ra_msg1_t *p_msg1,
|
|
uint32_t msg1_size,
|
|
ra_samp_response_header_t **pp_msg2)
|
|
{
|
|
int ret = 0;
|
|
ra_samp_response_header_t* p_msg2_full = NULL;
|
|
sample_ra_msg2_t *p_msg2 = NULL;
|
|
sample_ecc_state_handle_t ecc_state = NULL;
|
|
sample_status_t sample_ret = SAMPLE_SUCCESS;
|
|
bool derive_ret = false;
|
|
|
|
if(!p_msg1 ||
|
|
!pp_msg2 ||
|
|
(msg1_size != sizeof(sample_ra_msg1_t)))
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
// Check to see if we have registered?
|
|
if (!g_is_sp_registered)
|
|
{
|
|
return SP_UNSUPPORTED_EXTENDED_EPID_GROUP;
|
|
}
|
|
|
|
do
|
|
{
|
|
// Get the sig_rl from attestation server using GID.
|
|
// GID is Base-16 encoded of EPID GID in little-endian format.
|
|
// In the product, the SP and attestation server uses an established channel for
|
|
// communication.
|
|
uint8_t* sig_rl;
|
|
uint32_t sig_rl_size = 0;
|
|
|
|
// The product interface uses a REST based message to get the SigRL.
|
|
//! Refer to the attestation server API for more information on how to communicate to
|
|
//! the real attestation server.
|
|
ret = g_sp_extended_epid_group_id->get_sigrl(p_msg1->gid, &sig_rl_size, &sig_rl);
|
|
if(0 != ret)
|
|
{
|
|
fprintf(stderr, "\nError, ias_get_sigrl [%s].", __FUNCTION__);
|
|
ret = SP_IAS_FAILED;
|
|
break;
|
|
}
|
|
|
|
// Need to save the client's public ECDH key to local storage
|
|
if (memcpy_s(&g_sp_db.g_a, sizeof(g_sp_db.g_a), &p_msg1->g_a,
|
|
sizeof(p_msg1->g_a)))
|
|
{
|
|
fprintf(stderr, "\nError, cannot do memcpy in [%s].", __FUNCTION__);
|
|
ret = SP_INTERNAL_ERROR;
|
|
break;
|
|
}
|
|
|
|
// Generate the Service providers ECDH key pair.
|
|
sample_ret = sample_ecc256_open_context(&ecc_state);
|
|
if(SAMPLE_SUCCESS != sample_ret)
|
|
{
|
|
fprintf(stderr, "\nError, cannot get ECC context in [%s].",
|
|
__FUNCTION__);
|
|
ret = -1;
|
|
break;
|
|
}
|
|
sample_ec256_public_t pub_key = {{0},{0}};
|
|
sample_ec256_private_t priv_key = {{0}};
|
|
sample_ret = sample_ecc256_create_key_pair(&priv_key, &pub_key,
|
|
ecc_state);
|
|
if(SAMPLE_SUCCESS != sample_ret)
|
|
{
|
|
fprintf(stderr, "\nError, cannot generate key pair in [%s].",
|
|
__FUNCTION__);
|
|
ret = SP_INTERNAL_ERROR;
|
|
break;
|
|
}
|
|
|
|
// Need to save the SP ECDH key pair to local storage.
|
|
if(memcpy_s(&g_sp_db.b, sizeof(g_sp_db.b), &priv_key,sizeof(priv_key))
|
|
|| memcpy_s(&g_sp_db.g_b, sizeof(g_sp_db.g_b),
|
|
&pub_key,sizeof(pub_key)))
|
|
{
|
|
fprintf(stderr, "\nError, cannot do memcpy in [%s].", __FUNCTION__);
|
|
ret = SP_INTERNAL_ERROR;
|
|
break;
|
|
}
|
|
|
|
// Generate the client/SP shared secret
|
|
sample_ec_dh_shared_t dh_key = {{0}};
|
|
sample_ret = sample_ecc256_compute_shared_dhkey(&priv_key,
|
|
(sample_ec256_public_t *)&p_msg1->g_a,
|
|
(sample_ec256_dh_shared_t *)&dh_key,
|
|
ecc_state);
|
|
if(SAMPLE_SUCCESS != sample_ret)
|
|
{
|
|
fprintf(stderr, "\nError, compute share key fail in [%s].",
|
|
__FUNCTION__);
|
|
ret = SP_INTERNAL_ERROR;
|
|
break;
|
|
}
|
|
|
|
#ifdef SUPPLIED_KEY_DERIVATION
|
|
|
|
// smk is only needed for msg2 generation.
|
|
derive_ret = derive_key(&dh_key, SAMPLE_DERIVE_KEY_SMK_SK,
|
|
&g_sp_db.smk_key, &g_sp_db.sk_key);
|
|
if(derive_ret != true)
|
|
{
|
|
fprintf(stderr, "\nError, derive key fail in [%s].", __FUNCTION__);
|
|
ret = SP_INTERNAL_ERROR;
|
|
break;
|
|
}
|
|
|
|
// The rest of the keys are the shared secrets for future communication.
|
|
derive_ret = derive_key(&dh_key, SAMPLE_DERIVE_KEY_MK_VK,
|
|
&g_sp_db.mk_key, &g_sp_db.vk_key);
|
|
if(derive_ret != true)
|
|
{
|
|
fprintf(stderr, "\nError, derive key fail in [%s].", __FUNCTION__);
|
|
ret = SP_INTERNAL_ERROR;
|
|
break;
|
|
}
|
|
#else
|
|
// smk is only needed for msg2 generation.
|
|
derive_ret = derive_key(&dh_key, SAMPLE_DERIVE_KEY_SMK,
|
|
&g_sp_db.smk_key);
|
|
if(derive_ret != true)
|
|
{
|
|
fprintf(stderr, "\nError, derive key fail in [%s].", __FUNCTION__);
|
|
ret = SP_INTERNAL_ERROR;
|
|
break;
|
|
}
|
|
|
|
// The rest of the keys are the shared secrets for future communication.
|
|
derive_ret = derive_key(&dh_key, SAMPLE_DERIVE_KEY_MK,
|
|
&g_sp_db.mk_key);
|
|
if(derive_ret != true)
|
|
{
|
|
fprintf(stderr, "\nError, derive key fail in [%s].", __FUNCTION__);
|
|
ret = SP_INTERNAL_ERROR;
|
|
break;
|
|
}
|
|
|
|
derive_ret = derive_key(&dh_key, SAMPLE_DERIVE_KEY_SK,
|
|
&g_sp_db.sk_key);
|
|
if(derive_ret != true)
|
|
{
|
|
fprintf(stderr, "\nError, derive key fail in [%s].", __FUNCTION__);
|
|
ret = SP_INTERNAL_ERROR;
|
|
break;
|
|
}
|
|
|
|
derive_ret = derive_key(&dh_key, SAMPLE_DERIVE_KEY_VK,
|
|
&g_sp_db.vk_key);
|
|
if(derive_ret != true)
|
|
{
|
|
fprintf(stderr, "\nError, derive key fail in [%s].", __FUNCTION__);
|
|
ret = SP_INTERNAL_ERROR;
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
uint32_t msg2_size = (uint32_t)sizeof(sample_ra_msg2_t) + sig_rl_size;
|
|
p_msg2_full = (ra_samp_response_header_t*)malloc(msg2_size
|
|
+ sizeof(ra_samp_response_header_t));
|
|
if(!p_msg2_full)
|
|
{
|
|
fprintf(stderr, "\nError, out of memory in [%s].", __FUNCTION__);
|
|
ret = SP_INTERNAL_ERROR;
|
|
break;
|
|
}
|
|
memset(p_msg2_full, 0, msg2_size + sizeof(ra_samp_response_header_t));
|
|
p_msg2_full->type = TYPE_RA_MSG2;
|
|
p_msg2_full->size = msg2_size;
|
|
// The simulated message2 always passes. This would need to be set
|
|
// accordingly in a real service provider implementation.
|
|
p_msg2_full->status[0] = 0;
|
|
p_msg2_full->status[1] = 0;
|
|
p_msg2 = (sample_ra_msg2_t *)p_msg2_full->body;
|
|
|
|
// Assemble MSG2
|
|
if(memcpy_s(&p_msg2->g_b, sizeof(p_msg2->g_b), &g_sp_db.g_b,
|
|
sizeof(g_sp_db.g_b)) ||
|
|
memcpy_s(&p_msg2->spid, sizeof(sample_spid_t),
|
|
&g_spid, sizeof(g_spid)))
|
|
{
|
|
fprintf(stderr,"\nError, memcpy failed in [%s].", __FUNCTION__);
|
|
ret = SP_INTERNAL_ERROR;
|
|
break;
|
|
}
|
|
|
|
// The service provider is responsible for selecting the proper EPID
|
|
// signature type and to understand the implications of the choice!
|
|
p_msg2->quote_type = SAMPLE_QUOTE_LINKABLE_SIGNATURE;
|
|
|
|
#ifdef SUPPLIED_KEY_DERIVATION
|
|
//isv defined key derivation function id
|
|
#define ISV_KDF_ID 2
|
|
p_msg2->kdf_id = ISV_KDF_ID;
|
|
#else
|
|
p_msg2->kdf_id = SAMPLE_AES_CMAC_KDF_ID;
|
|
#endif
|
|
// Create gb_ga
|
|
sample_ec_pub_t gb_ga[2];
|
|
if(memcpy_s(&gb_ga[0], sizeof(gb_ga[0]), &g_sp_db.g_b,
|
|
sizeof(g_sp_db.g_b))
|
|
|| memcpy_s(&gb_ga[1], sizeof(gb_ga[1]), &g_sp_db.g_a,
|
|
sizeof(g_sp_db.g_a)))
|
|
{
|
|
fprintf(stderr,"\nError, memcpy failed in [%s].", __FUNCTION__);
|
|
ret = SP_INTERNAL_ERROR;
|
|
break;
|
|
}
|
|
|
|
// Sign gb_ga
|
|
sample_ret = sample_ecdsa_sign((uint8_t *)&gb_ga, sizeof(gb_ga),
|
|
(sample_ec256_private_t *)&g_sp_priv_key,
|
|
(sample_ec256_signature_t *)&p_msg2->sign_gb_ga,
|
|
ecc_state);
|
|
if(SAMPLE_SUCCESS != sample_ret)
|
|
{
|
|
fprintf(stderr, "\nError, sign ga_gb fail in [%s].", __FUNCTION__);
|
|
ret = SP_INTERNAL_ERROR;
|
|
break;
|
|
}
|
|
|
|
// Generate the CMACsmk for gb||SPID||TYPE||KDF_ID||Sigsp(gb,ga)
|
|
uint8_t mac[SAMPLE_EC_MAC_SIZE] = {0};
|
|
uint32_t cmac_size = offsetof(sample_ra_msg2_t, mac);
|
|
sample_ret = sample_rijndael128_cmac_msg(&g_sp_db.smk_key,
|
|
(uint8_t *)&p_msg2->g_b, cmac_size, &mac);
|
|
if(SAMPLE_SUCCESS != sample_ret)
|
|
{
|
|
fprintf(stderr, "\nError, cmac fail in [%s].", __FUNCTION__);
|
|
ret = SP_INTERNAL_ERROR;
|
|
break;
|
|
}
|
|
if(memcpy_s(&p_msg2->mac, sizeof(p_msg2->mac), mac, sizeof(mac)))
|
|
{
|
|
fprintf(stderr,"\nError, memcpy failed in [%s].", __FUNCTION__);
|
|
ret = SP_INTERNAL_ERROR;
|
|
break;
|
|
}
|
|
|
|
if(memcpy_s(&p_msg2->sig_rl[0], sig_rl_size, sig_rl, sig_rl_size))
|
|
{
|
|
fprintf(stderr,"\nError, memcpy failed in [%s].", __FUNCTION__);
|
|
ret = SP_INTERNAL_ERROR;
|
|
break;
|
|
}
|
|
p_msg2->sig_rl_size = sig_rl_size;
|
|
|
|
}while(0);
|
|
|
|
if(ret)
|
|
{
|
|
*pp_msg2 = NULL;
|
|
SAFE_FREE(p_msg2_full);
|
|
}
|
|
else
|
|
{
|
|
// Freed by the network simulator in ra_free_network_response_buffer
|
|
*pp_msg2 = p_msg2_full;
|
|
}
|
|
|
|
if(ecc_state)
|
|
{
|
|
sample_ecc256_close_context(ecc_state);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Process remote attestation message 3
|
|
int sp_ra_proc_msg3_req(const sample_ra_msg3_t *p_msg3,
|
|
uint32_t msg3_size,
|
|
ra_samp_response_header_t **pp_att_result_msg)
|
|
{
|
|
int ret = 0;
|
|
sample_status_t sample_ret = SAMPLE_SUCCESS;
|
|
const uint8_t *p_msg3_cmaced = NULL;
|
|
const sample_quote_t *p_quote = NULL;
|
|
sample_sha_state_handle_t sha_handle = NULL;
|
|
sample_report_data_t report_data = {0};
|
|
sample_ra_att_result_msg_t *p_att_result_msg = NULL;
|
|
ra_samp_response_header_t* p_att_result_msg_full = NULL;
|
|
uint32_t i;
|
|
|
|
if((!p_msg3) ||
|
|
(msg3_size < sizeof(sample_ra_msg3_t)) ||
|
|
(!pp_att_result_msg))
|
|
{
|
|
return SP_INTERNAL_ERROR;
|
|
}
|
|
|
|
// Check to see if we have registered?
|
|
if (!g_is_sp_registered)
|
|
{
|
|
return SP_UNSUPPORTED_EXTENDED_EPID_GROUP;
|
|
}
|
|
do
|
|
{
|
|
// Compare g_a in message 3 with local g_a.
|
|
ret = memcmp(&g_sp_db.g_a, &p_msg3->g_a, sizeof(sample_ec_pub_t));
|
|
if(ret)
|
|
{
|
|
fprintf(stderr, "\nError, g_a is not same [%s].", __FUNCTION__);
|
|
ret = SP_PROTOCOL_ERROR;
|
|
break;
|
|
}
|
|
//Make sure that msg3_size is bigger than sample_mac_t.
|
|
uint32_t mac_size = msg3_size - (uint32_t)sizeof(sample_mac_t);
|
|
p_msg3_cmaced = reinterpret_cast<const uint8_t*>(p_msg3);
|
|
p_msg3_cmaced += sizeof(sample_mac_t);
|
|
|
|
// Verify the message mac using SMK
|
|
sample_cmac_128bit_tag_t mac = {0};
|
|
sample_ret = sample_rijndael128_cmac_msg(&g_sp_db.smk_key,
|
|
p_msg3_cmaced,
|
|
mac_size,
|
|
&mac);
|
|
if(SAMPLE_SUCCESS != sample_ret)
|
|
{
|
|
fprintf(stderr, "\nError, cmac fail in [%s].", __FUNCTION__);
|
|
ret = SP_INTERNAL_ERROR;
|
|
break;
|
|
}
|
|
// In real implementation, should use a time safe version of memcmp here,
|
|
// in order to avoid side channel attack.
|
|
ret = memcmp(&p_msg3->mac, mac, sizeof(mac));
|
|
if(ret)
|
|
{
|
|
fprintf(stderr, "\nError, verify cmac fail [%s].", __FUNCTION__);
|
|
ret = SP_INTEGRITY_FAILED;
|
|
break;
|
|
}
|
|
|
|
if(memcpy_s(&g_sp_db.ps_sec_prop, sizeof(g_sp_db.ps_sec_prop),
|
|
&p_msg3->ps_sec_prop, sizeof(p_msg3->ps_sec_prop)))
|
|
{
|
|
fprintf(stderr,"\nError, memcpy failed in [%s].", __FUNCTION__);
|
|
ret = SP_INTERNAL_ERROR;
|
|
break;
|
|
}
|
|
|
|
p_quote = (const sample_quote_t*)p_msg3->quote;
|
|
|
|
// Check the quote version if needed. Only check the Quote.version field if the enclave
|
|
// identity fields have changed or the size of the quote has changed. The version may
|
|
// change without affecting the legacy fields or size of the quote structure.
|
|
//if(p_quote->version < ACCEPTED_QUOTE_VERSION)
|
|
//{
|
|
// fprintf(stderr,"\nError, quote version is too old.", __FUNCTION__);
|
|
// ret = SP_QUOTE_VERSION_ERROR;
|
|
// break;
|
|
//}
|
|
|
|
// Verify the report_data in the Quote matches the expected value.
|
|
// The first 32 bytes of report_data are SHA256 HASH of {ga|gb|vk}.
|
|
// The second 32 bytes of report_data are set to zero.
|
|
sample_ret = sample_sha256_init(&sha_handle);
|
|
if(sample_ret != SAMPLE_SUCCESS)
|
|
{
|
|
fprintf(stderr,"\nError, init hash failed in [%s].", __FUNCTION__);
|
|
ret = SP_INTERNAL_ERROR;
|
|
break;
|
|
}
|
|
sample_ret = sample_sha256_update((uint8_t *)&(g_sp_db.g_a),
|
|
sizeof(g_sp_db.g_a), sha_handle);
|
|
if(sample_ret != SAMPLE_SUCCESS)
|
|
{
|
|
fprintf(stderr,"\nError, udpate hash failed in [%s].",
|
|
__FUNCTION__);
|
|
ret = SP_INTERNAL_ERROR;
|
|
break;
|
|
}
|
|
sample_ret = sample_sha256_update((uint8_t *)&(g_sp_db.g_b),
|
|
sizeof(g_sp_db.g_b), sha_handle);
|
|
if(sample_ret != SAMPLE_SUCCESS)
|
|
{
|
|
fprintf(stderr,"\nError, udpate hash failed in [%s].",
|
|
__FUNCTION__);
|
|
ret = SP_INTERNAL_ERROR;
|
|
break;
|
|
}
|
|
sample_ret = sample_sha256_update((uint8_t *)&(g_sp_db.vk_key),
|
|
sizeof(g_sp_db.vk_key), sha_handle);
|
|
if(sample_ret != SAMPLE_SUCCESS)
|
|
{
|
|
fprintf(stderr,"\nError, udpate hash failed in [%s].",
|
|
__FUNCTION__);
|
|
ret = SP_INTERNAL_ERROR;
|
|
break;
|
|
}
|
|
sample_ret = sample_sha256_get_hash(sha_handle,
|
|
(sample_sha256_hash_t *)&report_data);
|
|
if(sample_ret != SAMPLE_SUCCESS)
|
|
{
|
|
fprintf(stderr,"\nError, Get hash failed in [%s].", __FUNCTION__);
|
|
ret = SP_INTERNAL_ERROR;
|
|
break;
|
|
}
|
|
ret = memcmp((uint8_t *)&report_data,
|
|
&(p_quote->report_body.report_data),
|
|
sizeof(report_data));
|
|
if(ret)
|
|
{
|
|
fprintf(stderr, "\nError, verify hash fail [%s].", __FUNCTION__);
|
|
ret = SP_INTEGRITY_FAILED;
|
|
break;
|
|
}
|
|
|
|
// Verify Enclave policy (an attestation server may provide an API for this if we
|
|
// registered an Enclave policy)
|
|
|
|
// Verify quote with attestation server.
|
|
// In the product, an attestation server could use a REST message and JSON formatting to request
|
|
// attestation Quote verification. The sample only simulates this interface.
|
|
//! Please refer to the attestation server API for more details on this interface.
|
|
ias_att_report_t attestation_report;
|
|
memset(&attestation_report, 0, sizeof(attestation_report));
|
|
ret = g_sp_extended_epid_group_id->verify_attestation_evidence(p_quote, NULL,
|
|
&attestation_report);
|
|
if(0 != ret)
|
|
{
|
|
ret = SP_IAS_FAILED;
|
|
break;
|
|
}
|
|
FILE* OUTPUT = stdout;
|
|
fprintf(OUTPUT, "\n\n\tAttestation Report:");
|
|
fprintf(OUTPUT, "\n\tid: 0x%0x.", attestation_report.id);
|
|
fprintf(OUTPUT, "\n\tstatus: %d.", attestation_report.status);
|
|
fprintf(OUTPUT, "\n\trevocation_reason: %u.",
|
|
attestation_report.revocation_reason);
|
|
// attestation_report.info_blob;
|
|
fprintf(OUTPUT, "\n\tpse_status: %d.", attestation_report.pse_status);
|
|
// Note: This sample always assumes the PIB is sent by attestation server. In the product
|
|
// implementation, the attestation server could only send the PIB for certain attestation
|
|
// report statuses. A product SP implementation needs to handle cases
|
|
// where the PIB is zero length.
|
|
|
|
// Respond the client with the results of the attestation.
|
|
uint32_t att_result_msg_size = sizeof(sample_ra_att_result_msg_t);
|
|
p_att_result_msg_full =
|
|
(ra_samp_response_header_t*)malloc(att_result_msg_size
|
|
+ sizeof(ra_samp_response_header_t) + sizeof(g_secret));
|
|
if(!p_att_result_msg_full)
|
|
{
|
|
fprintf(stderr, "\nError, out of memory in [%s].", __FUNCTION__);
|
|
ret = SP_INTERNAL_ERROR;
|
|
break;
|
|
}
|
|
memset(p_att_result_msg_full, 0, att_result_msg_size
|
|
+ sizeof(ra_samp_response_header_t) + sizeof(g_secret));
|
|
p_att_result_msg_full->type = TYPE_RA_ATT_RESULT;
|
|
p_att_result_msg_full->size = att_result_msg_size;
|
|
if(IAS_QUOTE_OK != attestation_report.status)
|
|
{
|
|
p_att_result_msg_full->status[0] = 0xFF;
|
|
}
|
|
if(IAS_PSE_OK != attestation_report.pse_status)
|
|
{
|
|
p_att_result_msg_full->status[1] = 0xFF;
|
|
}
|
|
|
|
p_att_result_msg =
|
|
(sample_ra_att_result_msg_t *)p_att_result_msg_full->body;
|
|
|
|
// In a product implementation of attestation server, the HTTP response header itself could have
|
|
// an RK based signature that the service provider needs to check here.
|
|
|
|
// The platform_info_blob signature will be verified by the client
|
|
// when sent. No need to have the Service Provider to check it. The SP
|
|
// should pass it down to the application for further analysis.
|
|
|
|
fprintf(OUTPUT, "\n\n\tEnclave Report:");
|
|
fprintf(OUTPUT, "\n\tSignature Type: 0x%x", p_quote->sign_type);
|
|
fprintf(OUTPUT, "\n\tSignature Basename: ");
|
|
for(i=0; i<sizeof(p_quote->basename.name) && p_quote->basename.name[i];
|
|
i++)
|
|
{
|
|
fprintf(OUTPUT, "%c", p_quote->basename.name[i]);
|
|
}
|
|
#ifdef __x86_64__
|
|
fprintf(OUTPUT, "\n\tattributes.flags: 0x%0lx",
|
|
p_quote->report_body.attributes.flags);
|
|
fprintf(OUTPUT, "\n\tattributes.xfrm: 0x%0lx",
|
|
p_quote->report_body.attributes.xfrm);
|
|
#else
|
|
fprintf(OUTPUT, "\n\tattributes.flags: 0x%0llx",
|
|
p_quote->report_body.attributes.flags);
|
|
fprintf(OUTPUT, "\n\tattributes.xfrm: 0x%0llx",
|
|
p_quote->report_body.attributes.xfrm);
|
|
#endif
|
|
fprintf(OUTPUT, "\n\tmr_enclave: ");
|
|
for(i=0;i<sizeof(sample_measurement_t);i++)
|
|
{
|
|
|
|
fprintf(OUTPUT, "%02x",p_quote->report_body.mr_enclave[i]);
|
|
|
|
//fprintf(stderr, "%02x",p_quote->report_body.mr_enclave.m[i]);
|
|
|
|
}
|
|
fprintf(OUTPUT, "\n\tmr_signer: ");
|
|
for(i=0;i<sizeof(sample_measurement_t);i++)
|
|
{
|
|
|
|
fprintf(OUTPUT, "%02x",p_quote->report_body.mr_signer[i]);
|
|
|
|
//fprintf(stderr, "%02x",p_quote->report_body.mr_signer.m[i]);
|
|
|
|
}
|
|
fprintf(OUTPUT, "\n\tisv_prod_id: 0x%0x",
|
|
p_quote->report_body.isv_prod_id);
|
|
fprintf(OUTPUT, "\n\tisv_svn: 0x%0x",p_quote->report_body.isv_svn);
|
|
fprintf(OUTPUT, "\n");
|
|
|
|
// A product service provider needs to verify that its enclave properties
|
|
// match what is expected. The SP needs to check these values before
|
|
// trusting the enclave. For the sample, we always pass the policy check.
|
|
// Attestation server only verifies the quote structure and signature. It does not
|
|
// check the identity of the enclave.
|
|
bool isv_policy_passed = true;
|
|
|
|
// Assemble Attestation Result Message
|
|
// Note, this is a structure copy. We don't copy the policy reports
|
|
// right now.
|
|
p_att_result_msg->platform_info_blob = attestation_report.info_blob;
|
|
|
|
// Generate mac based on the mk key.
|
|
mac_size = sizeof(ias_platform_info_blob_t);
|
|
sample_ret = sample_rijndael128_cmac_msg(&g_sp_db.mk_key,
|
|
(const uint8_t*)&p_att_result_msg->platform_info_blob,
|
|
mac_size,
|
|
&p_att_result_msg->mac);
|
|
if(SAMPLE_SUCCESS != sample_ret)
|
|
{
|
|
fprintf(stderr, "\nError, cmac fail in [%s].", __FUNCTION__);
|
|
ret = SP_INTERNAL_ERROR;
|
|
break;
|
|
}
|
|
|
|
// Generate shared secret and encrypt it with SK, if attestation passed.
|
|
uint8_t aes_gcm_iv[SAMPLE_SP_IV_SIZE] = {0};
|
|
p_att_result_msg->secret.payload_size = sizeof(g_secret);
|
|
if((IAS_QUOTE_OK == attestation_report.status) &&
|
|
(IAS_PSE_OK == attestation_report.pse_status) &&
|
|
(isv_policy_passed == true))
|
|
{
|
|
ret = sample_rijndael128GCM_encrypt(&g_sp_db.sk_key,
|
|
&g_secret[0],
|
|
p_att_result_msg->secret.payload_size,
|
|
p_att_result_msg->secret.payload,
|
|
&aes_gcm_iv[0],
|
|
SAMPLE_SP_IV_SIZE,
|
|
NULL,
|
|
0,
|
|
&p_att_result_msg->secret.payload_tag);
|
|
}
|
|
}while(0);
|
|
|
|
if(ret)
|
|
{
|
|
*pp_att_result_msg = NULL;
|
|
SAFE_FREE(p_att_result_msg_full);
|
|
}
|
|
else
|
|
{
|
|
// Freed by the network simulator in ra_free_network_response_buffer
|
|
*pp_att_result_msg = p_att_result_msg_full;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
|
|
|