blob: bcc7e2a605f8d102b86663563993c45155ed49e6 [file] [log] [blame]
// 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..
//! # Diffie–Hellman (DH) Session Establishment Functions
//!
//! These functions allow an ISV to establish secure session between two enclaves using the EC DH Key exchange protocol.
//!
use crate::ecp::*;
use alloc::boxed::Box;
use alloc::slice;
use alloc::vec::Vec;
use core::mem;
use core::ptr;
use sgx_tcrypto::*;
use sgx_trts::memeq::ConsttimeMemEq;
use sgx_trts::trts::*;
use sgx_tse::*;
use sgx_types::marker::ContiguousMemory;
use sgx_types::*;
const AES_CMAC_KDF_ID: [u8; 2] = [1, 0];
pub type SgxDhMsg1 = sgx_dh_msg1_t;
pub type SgxDhMsg2 = sgx_dh_msg2_t;
/// Type for message body of the MSG3 structure used in DH secure session establishment.
#[derive(Clone, Default)]
pub struct SgxDhMsg3Body {
pub report: sgx_report_t,
pub additional_prop: Box<[u8]>,
}
/// Type for MSG3 used in DH secure session establishment.
#[derive(Clone, Default)]
pub struct SgxDhMsg3 {
pub cmac: [u8; SGX_DH_MAC_SIZE],
pub msg3_body: SgxDhMsg3Body,
}
impl SgxDhMsg3 {
///
/// Create a SgxDhMsg3 with default values.
///
pub fn new() -> SgxDhMsg3 {
SgxDhMsg3::default()
}
///
/// Calculate the size of sgx_dh_msg3_t converted from SgxDhMsg3, really add the size of struct sgx_dh_msg3_t and msg3_body.additional_prop.
///
/// # Return value
///
/// The size of sgx_dh_msg3_t needed.
///
pub fn calc_raw_sealed_data_size(&self) -> u32 {
let max = u32::MAX;
let dh_msg3_size = mem::size_of::<sgx_dh_msg3_t>();
let additional_prop_len = self.msg3_body.additional_prop.len();
if additional_prop_len > (max as usize) - dh_msg3_size {
return max;
}
(dh_msg3_size + additional_prop_len) as u32
}
///
/// Convert SgxDhMsg3 to sgx_dh_msg3_t, this is an unsafe function.
///
/// # Parameters
///
/// **p**
///
/// The pointer of a sgx_dh_msg3_t buffer to save the buffer of SgxDhMsg3.
///
/// **len**
///
/// The size of the sgx_dh_msg3_t buffer.
///
/// # Return value
///
/// **Some(*mut sgx_dh_msg3_t)**
///
/// Indicates the conversion is successfully. The return value is the mutable pointer of sgx_dh_msg3_t.
///
/// **None**
///
/// The parameters p and len are not available for the conversion.
///
pub unsafe fn to_raw_dh_msg3_t(
&self,
p: *mut sgx_dh_msg3_t,
len: u32,
) -> Option<*mut sgx_dh_msg3_t> {
if p.is_null() {
return None;
}
if !rsgx_raw_is_within_enclave(p as *mut u8, len as usize) {
return None;
}
let additional_prop_len = self.msg3_body.additional_prop.len();
let dh_msg3_size = mem::size_of::<sgx_dh_msg3_t>();
if additional_prop_len > u32::MAX as usize - dh_msg3_size {
return None;
}
if len < (dh_msg3_size + additional_prop_len) as u32 {
return None;
}
let dh_msg3 = &mut *p;
dh_msg3.cmac = self.cmac;
dh_msg3.msg3_body.report = self.msg3_body.report;
dh_msg3.msg3_body.additional_prop_length = additional_prop_len as u32;
if additional_prop_len > 0 {
let raw_msg3 = slice::from_raw_parts_mut(p as *mut u8, len as usize);
raw_msg3[dh_msg3_size..].copy_from_slice(&self.msg3_body.additional_prop);
}
Some(p)
}
///
/// Convert sgx_dh_msg3_t to SgxDhMsg3, this is an unsafe function.
///
/// # Parameters
///
/// **p**
///
/// The pointer of a sgx_dh_msg3_t buffer.
///
/// **len**
///
/// The size of the sgx_dh_msg3_t buffer.
///
/// # Return value
///
/// **Some(SgxDhMsg3)**
///
/// Indicates the conversion is successfully. The return value is SgxDhMsg3.
///
/// **None**
///
/// The parameters p and len are not available for the conversion.
///
pub unsafe fn from_raw_dh_msg3_t(p: *mut sgx_dh_msg3_t, len: u32) -> Option<SgxDhMsg3> {
if p.is_null() {
return None;
}
if !rsgx_raw_is_within_enclave(p as *mut u8, len as usize) {
return None;
}
let raw_msg3 = &*p;
let additional_prop_len = raw_msg3.msg3_body.additional_prop_length;
let dh_msg3_size = mem::size_of::<sgx_dh_msg3_t>() as u32;
if additional_prop_len > u32::MAX - dh_msg3_size {
return None;
}
if len < dh_msg3_size + additional_prop_len {
return None;
}
let mut dh_msg3 = SgxDhMsg3::default();
dh_msg3.cmac = raw_msg3.cmac;
dh_msg3.msg3_body.report = raw_msg3.msg3_body.report;
if additional_prop_len > 0 {
let mut additional_prop: Vec<u8> = vec![0_u8; additional_prop_len as usize];
let ptr_additional_prop = p.offset(1) as *const u8;
ptr::copy_nonoverlapping(
ptr_additional_prop,
additional_prop.as_mut_ptr(),
additional_prop_len as usize,
);
dh_msg3.msg3_body.additional_prop = additional_prop.into_boxed_slice();
}
Some(dh_msg3)
}
}
#[derive(Copy, Clone, PartialEq, Eq)]
enum SgxDhSessionState {
SGX_DH_SESSION_STATE_ERROR,
SGX_DH_SESSION_STATE_RESET,
SGX_DH_SESSION_RESPONDER_WAIT_M2,
SGX_DH_SESSION_INITIATOR_WAIT_M1,
SGX_DH_SESSION_INITIATOR_WAIT_M3,
SGX_DH_SESSION_ACTIVE,
}
/// DH secure session responder
#[derive(Copy, Clone)]
pub struct SgxDhResponder {
state: SgxDhSessionState,
prv_key: sgx_align_ec256_private_t,
pub_key: sgx_ec256_public_t,
smk_aek: sgx_align_key_128bit_t,
shared_key: sgx_align_ec256_dh_shared_t,
}
impl Default for SgxDhResponder {
fn default() -> SgxDhResponder {
SgxDhResponder {
state: SgxDhSessionState::SGX_DH_SESSION_STATE_RESET,
prv_key: sgx_align_ec256_private_t::default(),
pub_key: sgx_ec256_public_t::default(),
smk_aek: sgx_align_key_128bit_t::default(),
shared_key: sgx_align_ec256_dh_shared_t::default(),
}
}
}
unsafe impl ContiguousMemory for SgxDhResponder {}
impl SgxDhResponder {
///
/// Initialize DH secure session responder.
///
/// Indicates role of responder the caller plays in the secure session establishment.
///
/// The value of role of the responder of the session establishment must be `SGX_DH_SESSION_RESPONDER`.
///
/// # Requirements
///
/// Library: libsgx_tservice.a or libsgx_tservice_sim.a (simulation)
///
pub fn init_session() -> SgxDhResponder {
Self::default()
}
///
/// Generates MSG1 for the responder of DH secure session establishment and records ECC key pair in session structure.
///
/// # Requirements
///
/// Library: libsgx_tservice.a or libsgx_tservice_sim.a (simulation)
///
/// # Parameters
///
/// **msg1**
///
/// A pointer to an SgxDhMsg1 msg1 buffer. The buffer holding the msg1
/// message, which is referenced by this parameter, must be within the enclave.
/// The DH msg1 contains the responder’s public key and report based target
/// info.
///
/// # Errors
///
/// **SGX_ERROR_INVALID_PARAMETER**
///
/// Any of the input parameters is incorrect.
///
/// **SGX_ERROR_INVALID_STATE**
///
/// The API is invoked in incorrect order or state.
///
/// **SGX_ERROR_OUT_OF_MEMORY**
///
/// The enclave is out of memory.
///
/// **SGX_ERROR_UNEXPECTED**
///
/// An unexpected error occurred.
///
pub fn gen_msg1(&mut self, msg1: &mut SgxDhMsg1) -> SgxError {
if !rsgx_data_is_within_enclave(self) {
return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
}
if !rsgx_data_is_within_enclave(msg1) {
return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
}
if self.state != SgxDhSessionState::SGX_DH_SESSION_STATE_RESET {
*self = Self::default();
return Err(sgx_status_t::SGX_ERROR_INVALID_STATE);
}
let error = self.dh_generate_message1(msg1);
if let Err(mut ret) = error {
*self = Self::default();
if ret != sgx_status_t::SGX_ERROR_OUT_OF_MEMORY {
ret = sgx_status_t::SGX_ERROR_UNEXPECTED;
}
return Err(ret);
}
self.state = SgxDhSessionState::SGX_DH_SESSION_RESPONDER_WAIT_M2;
Ok(())
}
///
/// The responder handles msg2 sent by initiator and then derives AEK, updates session information and generates msg3.
///
/// # Requirements
///
/// Library: libsgx_tservice.a or libsgx_tservice_sim.a (simulation)
///
/// # Parameters
///
/// **msg2**
///
/// Point to dh message 2 buffer generated by session initiator, and the buffer must be in enclave address space.
///
/// **msg3**
///
/// Point to dh message 3 buffer generated by session responder in this function, and the buffer must be in enclave address space.
///
/// **aek**
///
/// A pointer that points to instance of sgx_key_128bit_t. The aek is derived as follows:
///
/// ```
/// KDK := CMAC(key0, LittleEndian(gab x-coordinate))
/// AEK = AES-CMAC(KDK, 0x01||"AEK"||0x00||0x80||0x00)
/// ```
/// The key0 used in the key extraction operation is 16 bytes of 0x00. The plain
/// text used in the AES-CMAC calculation of the KDK is the Diffie-Hellman shared
/// secret elliptic curve field element in Little Endian format.The plain text used
/// in the AEK calculation includes:
///
/// * a counter (0x01)
///
/// * a label: the ASCII representation of the string 'AEK' in Little Endian format
///
/// * a bit length (0x80)
///
/// **initiator_identity**
///
/// A pointer that points to instance of sgx_dh_session_enclave_identity_t.
/// Identity information of initiator includes isv svn, isv product id, the
/// enclave attributes, MRSIGNER, and MRENCLAVE. The buffer must be in
/// enclave address space. The caller should check the identity of the peer and
/// decide whether to trust the peer and use the aek.
///
/// # Errors
///
/// **SGX_ERROR_INVALID_PARAMETER**
///
/// Any of the input parameters is incorrect.
///
/// **SGX_ERROR_INVALID_STATE**
///
/// The API is invoked in incorrect order or state.
///
/// **SGX_ERROR_KDF_MISMATCH**
///
/// Indicates the key derivation function does not match.
///
/// **SGX_ERROR_OUT_OF_MEMORY**
///
/// The enclave is out of memory.
///
/// **SGX_ERROR_UNEXPECTED**
///
/// An unexpected error occurred.
///
pub fn proc_msg2(
&mut self,
msg2: &SgxDhMsg2,
msg3: &mut SgxDhMsg3,
aek: &mut sgx_key_128bit_t,
initiator_identity: &mut sgx_dh_session_enclave_identity_t,
) -> SgxError {
if !rsgx_data_is_within_enclave(self) {
return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
}
if !rsgx_data_is_within_enclave(msg2)
|| !rsgx_data_is_within_enclave(aek)
|| !rsgx_data_is_within_enclave(initiator_identity)
|| !rsgx_raw_is_within_enclave(
msg3 as *const _ as *const u8,
mem::size_of::<SgxDhMsg3>(),
)
{
*self = Self::default();
self.state = SgxDhSessionState::SGX_DH_SESSION_STATE_ERROR;
return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
}
if msg3.msg3_body.additional_prop.len() > 0
&& (!(rsgx_slice_is_within_enclave(&msg3.msg3_body.additional_prop))
|| (msg3.msg3_body.additional_prop.len()
> (u32::MAX as usize) - mem::size_of::<sgx_dh_msg3_t>()))
{
*self = Self::default();
self.state = SgxDhSessionState::SGX_DH_SESSION_STATE_ERROR;
return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
}
if self.state != SgxDhSessionState::SGX_DH_SESSION_RESPONDER_WAIT_M2 {
*self = Self::default();
self.state = SgxDhSessionState::SGX_DH_SESSION_STATE_ERROR;
return Err(sgx_status_t::SGX_ERROR_INVALID_STATE);
}
let ecc_state = SgxEccHandle::new();
ecc_state.open().map_err(|ret| self.set_error(ret))?;
self.shared_key = ecc_state
.compute_align_shared_dhkey(&self.prv_key.key, &msg2.g_b)
.map_err(|ret| self.set_error(ret))?;
self.smk_aek =
derive_key(&self.shared_key.key, &EC_SMK_LABEL).map_err(|ret| self.set_error(ret))?;
#[cfg(feature = "use_lav2")]
self.lav2_verify_message2(msg2)
.map_err(|ret| self.set_error(ret))?;
#[cfg(not(feature = "use_lav2"))]
self.dh_verify_message2(msg2)
.map_err(|ret| self.set_error(ret))?;
initiator_identity.isv_svn = msg2.report.body.isv_svn;
initiator_identity.isv_prod_id = msg2.report.body.isv_prod_id;
initiator_identity.attributes = msg2.report.body.attributes;
initiator_identity.mr_signer = msg2.report.body.mr_signer;
initiator_identity.mr_enclave = msg2.report.body.mr_enclave;
#[cfg(feature = "use_lav2")]
self.lav2_generate_message3(msg2, msg3)
.map_err(|ret| self.set_error(ret))?;
#[cfg(not(feature = "use_lav2"))]
self.dh_generate_message3(msg2, msg3)
.map_err(|ret| self.set_error(ret))?;
let align_aek =
derive_key(&self.shared_key.key, &EC_AEK_LABEL).map_err(|ret| self.set_error(ret))?;
*aek = align_aek.key;
*self = Self::default();
self.state = SgxDhSessionState::SGX_DH_SESSION_ACTIVE;
Ok(())
}
fn dh_generate_message1(&mut self, msg1: &mut SgxDhMsg1) -> SgxError {
msg1.target = Default::default();
msg1.g_a = Default::default();
let mut target = sgx_target_info_t::default();
let report_data = sgx_report_data_t::default();
let report = rsgx_create_report(&target, &report_data)?;
SGX_LAV2_PROTO_SPEC.make_target_info(&report, &mut target)?;
let ecc_state = SgxEccHandle::new();
ecc_state.open()?;
let (prv_key, pub_key) = ecc_state.create_align_key_pair()?;
self.prv_key = prv_key;
self.pub_key = pub_key;
msg1.g_a = pub_key;
msg1.target = target;
Ok(())
}
fn dh_verify_message2(&self, msg2: &SgxDhMsg2) -> SgxError {
let kdf_id =
&msg2.report.body.report_data.d[SGX_SHA256_HASH_SIZE..SGX_SHA256_HASH_SIZE + 2];
let data_hash = &msg2.report.body.report_data.d[..SGX_SHA256_HASH_SIZE];
if !kdf_id.eq(&AES_CMAC_KDF_ID) {
return Err(sgx_status_t::SGX_ERROR_KDF_MISMATCH);
}
let report = msg2.report;
let data_mac = rsgx_rijndael128_cmac_msg(&self.smk_aek.key, &report)?;
if !data_mac.consttime_memeq(&msg2.cmac) {
return Err(sgx_status_t::SGX_ERROR_MAC_MISMATCH);
}
rsgx_verify_report(&report)?;
let sha_handle = SgxShaHandle::new();
sha_handle.init()?;
sha_handle.update_msg(&self.pub_key)?;
sha_handle.update_msg(&msg2.g_b)?;
let msg_hash = sha_handle.get_hash()?;
if !msg_hash.eq(data_hash) {
return Err(sgx_status_t::SGX_ERROR_MAC_MISMATCH);
}
Ok(())
}
fn lav2_verify_message2(&self, msg2: &SgxDhMsg2) -> SgxError {
let sha_handle = SgxShaHandle::new();
sha_handle.init()?;
sha_handle.update_msg(&msg2.report.body.report_data)?;
sha_handle.update_msg(&msg2.g_b)?;
let msg_hash = sha_handle.get_hash()?;
let mut report = msg2.report;
report.body.report_data = sgx_report_data_t::default();
report.body.report_data.d[..SGX_SHA256_HASH_SIZE].copy_from_slice(&msg_hash);
rsgx_verify_report(&report)?;
let data_mac = rsgx_rijndael128_cmac_msg(&self.smk_aek.key, &msg2.g_b)?;
if !data_mac.consttime_memeq(&msg2.cmac) {
return Err(sgx_status_t::SGX_ERROR_MAC_MISMATCH);
}
let proto_spec =
unsafe { SgxLAv2ProtoSpec::from_report_data(&msg2.report.body.report_data) };
if (!proto_spec.signature.eq(&SGX_LAV2_PROTO_SPEC.signature))
|| (proto_spec.ver != SGX_LAV2_PROTO_SPEC.ver)
{
return Err(sgx_status_t::SGX_ERROR_UNEXPECTED);
}
Ok(())
}
fn dh_generate_message3(&self, msg2: &SgxDhMsg2, msg3: &mut SgxDhMsg3) -> SgxError {
msg3.cmac = Default::default();
msg3.msg3_body.report = Default::default();
let sha_handle = SgxShaHandle::new();
sha_handle.init()?;
sha_handle.update_msg(&msg2.g_b)?;
sha_handle.update_msg(&self.pub_key)?;
let msg_hash = sha_handle.get_hash()?;
let mut target = sgx_target_info_t::default();
let mut report_data = sgx_report_data_t::default();
let report = msg2.report;
report_data.d[..SGX_SHA256_HASH_SIZE].copy_from_slice(&msg_hash);
SGX_LAV2_PROTO_SPEC.make_target_info(&report, &mut target)?;
msg3.msg3_body.report = rsgx_create_report(&target, &report_data)?;
let add_prop_len = msg3.msg3_body.additional_prop.len() as u32;
let cmac_handle = SgxCmacHandle::new();
cmac_handle.init(&self.smk_aek.key)?;
cmac_handle.update_msg(&msg3.msg3_body.report)?;
cmac_handle.update_msg(&add_prop_len)?;
if add_prop_len > 0 {
cmac_handle.update_slice(&msg3.msg3_body.additional_prop)?;
}
msg3.cmac = cmac_handle.get_hash()?;
Ok(())
}
fn lav2_generate_message3(&self, msg2: &SgxDhMsg2, msg3: &mut SgxDhMsg3) -> SgxError {
msg3.cmac = Default::default();
msg3.msg3_body.report = Default::default();
let proto_spec =
unsafe { SgxLAv2ProtoSpec::from_report_data(&msg2.report.body.report_data) };
let mut target = sgx_target_info_t::default();
let mut report_data = sgx_report_data_t::default();
let report = msg2.report;
let sha_handle = SgxShaHandle::new();
sha_handle.init()?;
sha_handle.update_msg(&self.pub_key)?;
sha_handle.update_msg(&proto_spec)?;
let msg_hash = sha_handle.get_hash()?;
report_data.d[..SGX_SHA256_HASH_SIZE].copy_from_slice(&msg_hash);
SGX_LAV2_PROTO_SPEC.make_target_info(&report, &mut target)?;
msg3.msg3_body.report = rsgx_create_report(&target, &report_data)?;
let cmac_handle = SgxCmacHandle::new();
cmac_handle.init(&self.smk_aek.key)?;
if msg3.msg3_body.additional_prop.len() > 0 {
cmac_handle.update_slice(&msg3.msg3_body.additional_prop)?;
}
cmac_handle.update_msg(&self.pub_key)?;
msg3.cmac = cmac_handle.get_hash()?;
Ok(())
}
fn set_error(&mut self, sgx_ret: sgx_status_t) -> sgx_status_t {
*self = Self::default();
self.state = SgxDhSessionState::SGX_DH_SESSION_STATE_ERROR;
match sgx_ret {
sgx_status_t::SGX_ERROR_OUT_OF_MEMORY => sgx_status_t::SGX_ERROR_OUT_OF_MEMORY,
sgx_status_t::SGX_ERROR_KDF_MISMATCH => sgx_status_t::SGX_ERROR_KDF_MISMATCH,
_ => sgx_status_t::SGX_ERROR_UNEXPECTED,
}
}
}
/// DH secure session Initiator
#[derive(Copy, Clone)]
pub struct SgxDhInitiator {
state: SgxDhSessionState,
smk_aek: sgx_align_key_128bit_t,
pub_key: sgx_ec256_public_t,
peer_pub_key: sgx_ec256_public_t,
shared_key: sgx_align_ec256_dh_shared_t,
}
impl Default for SgxDhInitiator {
fn default() -> SgxDhInitiator {
SgxDhInitiator {
state: SgxDhSessionState::SGX_DH_SESSION_INITIATOR_WAIT_M1,
smk_aek: sgx_align_key_128bit_t::default(),
pub_key: sgx_ec256_public_t::default(),
peer_pub_key: sgx_ec256_public_t::default(),
shared_key: sgx_align_ec256_dh_shared_t::default(),
}
}
}
unsafe impl ContiguousMemory for SgxDhInitiator {}
impl SgxDhInitiator {
///
/// Initialize DH secure session Initiator.
///
/// Indicates role of initiator the caller plays in the secure session establishment.
///
/// The value of role of the initiator of the session establishment must be `SGX_DH_SESSION_INITIATOR`.
///
/// # Requirements
///
/// Library: libsgx_tservice.a or libsgx_tservice_sim.a (simulation)
///
pub fn init_session() -> SgxDhInitiator {
Self::default()
}
///
/// The initiator of DH secure session establishment handles msg1 sent by responder and then generates msg2,
/// and records initiator’s ECC key pair in DH session structure.
///
/// # Requirements
///
/// Library: libsgx_tservice.a or libsgx_tservice_sim.a (simulation)
///
/// # Parameters
///
/// **msg1**
///
/// Point to dh message 1 buffer generated by session responder, and the buffer must be in enclave address space.
///
/// **msg2**
///
/// Point to dh message 2 buffer, and the buffer must be in enclave address space.
///
/// # Errors
///
/// **SGX_ERROR_INVALID_PARAMETER**
///
/// Any of the input parameters is incorrect.
///
/// **SGX_ERROR_INVALID_STATE**
///
/// The API is invoked in incorrect order or state.
///
/// **SGX_ERROR_OUT_OF_MEMORY**
///
/// The enclave is out of memory.
///
/// **SGX_ERROR_UNEXPECTED**
///
/// An unexpected error occurred.
///
pub fn proc_msg1(&mut self, msg1: &SgxDhMsg1, msg2: &mut SgxDhMsg2) -> SgxError {
if !rsgx_data_is_within_enclave(self) {
return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
}
if !rsgx_data_is_within_enclave(msg1) || !rsgx_data_is_within_enclave(msg2) {
*self = Self::default();
self.state = SgxDhSessionState::SGX_DH_SESSION_STATE_ERROR;
return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
}
if self.state != SgxDhSessionState::SGX_DH_SESSION_INITIATOR_WAIT_M1 {
*self = Self::default();
self.state = SgxDhSessionState::SGX_DH_SESSION_STATE_ERROR;
return Err(sgx_status_t::SGX_ERROR_INVALID_STATE);
}
let ecc_state = SgxEccHandle::new();
ecc_state.open().map_err(|ret| self.set_error(ret))?;
let (mut prv_key, pub_key) = ecc_state
.create_align_key_pair()
.map_err(|ret| self.set_error(ret))?;
self.shared_key = ecc_state
.compute_align_shared_dhkey(&prv_key.key, &msg1.g_a)
.map_err(|ret| self.set_error(ret))?;
prv_key = sgx_align_ec256_private_t::default();
self.pub_key = pub_key;
self.smk_aek =
derive_key(&self.shared_key.key, &EC_SMK_LABEL).map_err(|ret| self.set_error(ret))?;
#[cfg(feature = "use_lav2")]
self.lav2_generate_message2(msg1, msg2)
.map_err(|ret| self.set_error(ret))?;
#[cfg(not(feature = "use_lav2"))]
self.dh_generate_message2(msg1, msg2)
.map_err(|ret| self.set_error(ret))?;
self.peer_pub_key = msg1.g_a;
self.state = SgxDhSessionState::SGX_DH_SESSION_INITIATOR_WAIT_M3;
Ok(())
}
///
/// The initiator handles msg3 sent by responder and then derives AEK, updates
/// session information and gets responder’s identity information.
///
/// # Requirements
///
/// Library: libsgx_tservice.a or libsgx_tservice_sim.a (simulation)
///
/// # Parameters
///
/// **msg3**
///
/// Point to dh message 3 buffer generated by session responder, and the buffer must be in enclave address space.
///
/// **aek**
///
/// A pointer that points to instance of sgx_key_128bit_t. The aek is derived as follows:
///
/// ```
/// KDK:= CMAC(key0, LittleEndian(gab x-coordinate))
/// AEK = AES-CMAC(KDK, 0x01||"AEK"||0x00||0x80||0x00)
/// ```
///
/// The key0 used in the key extraction operation is 16 bytes of 0x00. The plain
/// text used in the AES-CMAC calculation of the KDK is the Diffie-Hellman shared
/// secret elliptic curve field element in Little Endian format.
/// The plain text used in the AEK calculation includes:
///
/// * a counter (0x01)
///
/// * a label: the ASCII representation of the string 'AEK' in Little Endian format
///
/// * a bit length (0x80)
///
/// **responder_identity**
///
/// Identity information of responder including isv svn, isv product id, the enclave
/// attributes, MRSIGNER, and MRENCLAVE. The buffer must be in enclave address space.
/// The caller should check the identity of the peer and decide whether to trust the
/// peer and use the aek or the msg3_body.additional_prop field of msg3.
///
/// # Errors
///
/// **SGX_ERROR_INVALID_PARAMETER**
///
/// Any of the input parameters is incorrect.
///
/// **SGX_ERROR_INVALID_STATE**
///
/// The API is invoked in incorrect order or state.
///
/// **SGX_ERROR_OUT_OF_MEMORY**
///
/// The enclave is out of memory.
///
/// **SGX_ERROR_UNEXPECTED**
///
/// An unexpected error occurred.
///
pub fn proc_msg3(
&mut self,
msg3: &SgxDhMsg3,
aek: &mut sgx_key_128bit_t,
responder_identity: &mut sgx_dh_session_enclave_identity_t,
) -> SgxError {
if !rsgx_data_is_within_enclave(self) {
return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
}
if !rsgx_raw_is_within_enclave(msg3 as *const _ as *const u8, mem::size_of::<SgxDhMsg3>())
|| !rsgx_data_is_within_enclave(aek)
|| !rsgx_data_is_within_enclave(responder_identity)
{
*self = Self::default();
self.state = SgxDhSessionState::SGX_DH_SESSION_STATE_ERROR;
return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
}
if msg3.msg3_body.additional_prop.len() > 0
&& (!rsgx_slice_is_within_enclave(&msg3.msg3_body.additional_prop)
|| (msg3.msg3_body.additional_prop.len()
> (u32::MAX as usize) - mem::size_of::<sgx_dh_msg3_t>()))
{
*self = Self::default();
self.state = SgxDhSessionState::SGX_DH_SESSION_STATE_ERROR;
return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
}
if self.state != SgxDhSessionState::SGX_DH_SESSION_INITIATOR_WAIT_M3 {
*self = Self::default();
self.state = SgxDhSessionState::SGX_DH_SESSION_STATE_ERROR;
return Err(sgx_status_t::SGX_ERROR_INVALID_STATE);
}
#[cfg(feature = "use_lav2")]
self.lav2_verify_message3(msg3).map_err(|ret| self.set_error(ret))?;
#[cfg(not(feature = "use_lav2"))]
self.dh_verify_message3(msg3).map_err(|ret| self.set_error(ret))?;
let align_aek =
derive_key(&self.shared_key.key, &EC_AEK_LABEL).map_err(|ret| self.set_error(ret))?;
*aek = align_aek.key;
*self = Self::default();
self.state = SgxDhSessionState::SGX_DH_SESSION_ACTIVE;
responder_identity.cpu_svn = msg3.msg3_body.report.body.cpu_svn;
responder_identity.misc_select = msg3.msg3_body.report.body.misc_select;
responder_identity.isv_svn = msg3.msg3_body.report.body.isv_svn;
responder_identity.isv_prod_id = msg3.msg3_body.report.body.isv_prod_id;
responder_identity.attributes = msg3.msg3_body.report.body.attributes;
responder_identity.mr_signer = msg3.msg3_body.report.body.mr_signer;
responder_identity.mr_enclave = msg3.msg3_body.report.body.mr_enclave;
Ok(())
}
fn dh_generate_message2(&self, msg1: &SgxDhMsg1, msg2: &mut SgxDhMsg2) -> SgxError {
msg2.report = Default::default();
msg2.cmac = Default::default();
msg2.g_b = self.pub_key;
let sha_handle = SgxShaHandle::new();
sha_handle.init()?;
sha_handle.update_msg(&msg1.g_a)?;
sha_handle.update_msg(&msg2.g_b)?;
let msg_hash = sha_handle.get_hash()?;
let mut report_data = sgx_report_data_t::default();
report_data.d[..SGX_SHA256_HASH_SIZE].copy_from_slice(&msg_hash);
report_data.d[SGX_SHA256_HASH_SIZE..SGX_SHA256_HASH_SIZE + 2]
.copy_from_slice(&AES_CMAC_KDF_ID);
let target = msg1.target;
msg2.report = rsgx_create_report(&target, &report_data)?;
let report = msg2.report;
msg2.cmac = rsgx_rijndael128_cmac_msg(&self.smk_aek.key, &report)?;
Ok(())
}
fn lav2_generate_message2(&self, msg1: &SgxDhMsg1, msg2: &mut SgxDhMsg2) -> SgxError {
msg2.report = Default::default();
msg2.cmac = Default::default();
msg2.g_b = self.pub_key;
let sha_handle = SgxShaHandle::new();
sha_handle.init()?;
sha_handle.update_msg(&SGX_LAV2_PROTO_SPEC)?;
sha_handle.update_msg(&msg2.g_b)?;
let msg_hash = sha_handle.get_hash()?;
let target = msg1.target;
let mut report_data = sgx_report_data_t::default();
report_data.d[..SGX_SHA256_HASH_SIZE].copy_from_slice(&msg_hash);
msg2.report = rsgx_create_report(&target, &report_data)?;
// Replace report_data with proto_spec
unsafe {
msg2.report.body.report_data = SGX_LAV2_PROTO_SPEC.to_report_data();
}
msg2.cmac = rsgx_rijndael128_cmac_msg(&self.smk_aek.key, &msg2.g_b)?;
Ok(())
}
fn dh_verify_message3(&self, msg3: &SgxDhMsg3) -> SgxError {
let add_prop_len = msg3.msg3_body.additional_prop.len() as u32;
let cmac_handle = SgxCmacHandle::new();
cmac_handle.init(&self.smk_aek.key)?;
cmac_handle.update_msg(&msg3.msg3_body.report)?;
cmac_handle.update_msg(&add_prop_len)?;
if add_prop_len > 0 {
cmac_handle.update_slice(&msg3.msg3_body.additional_prop)?;
}
let data_mac = cmac_handle.get_hash()?;
if !data_mac.consttime_memeq(&msg3.cmac) {
return Err(sgx_status_t::SGX_ERROR_MAC_MISMATCH);
}
rsgx_verify_report(&msg3.msg3_body.report)?;
let sha_handle = SgxShaHandle::new();
sha_handle.init()?;
sha_handle.update_msg(&self.pub_key)?;
sha_handle.update_msg(&self.peer_pub_key)?;
let msg_hash = sha_handle.get_hash()?;
let data_hash = &msg3.msg3_body.report.body.report_data.d[..SGX_SHA256_HASH_SIZE];
if !msg_hash.eq(data_hash) {
return Err(sgx_status_t::SGX_ERROR_MAC_MISMATCH);
}
Ok(())
}
fn lav2_verify_message3(&self, msg3: &SgxDhMsg3) -> SgxError {
let sha_handle = SgxShaHandle::new();
sha_handle.init()?;
sha_handle.update_msg(&self.peer_pub_key)?;
sha_handle.update_msg(&SGX_LAV2_PROTO_SPEC)?;
let msg_hash = sha_handle.get_hash()?;
let mut report = msg3.msg3_body.report;
report.body.report_data = sgx_report_data_t::default();
report.body.report_data.d[..SGX_SHA256_HASH_SIZE].copy_from_slice(&msg_hash);
if !&report.body.report_data.d[..].eq(&msg3.msg3_body.report.body.report_data.d[..]) {
return Err(sgx_status_t::SGX_ERROR_UNEXPECTED);
}
rsgx_verify_report(&report)?;
let cmac_handle = SgxCmacHandle::new();
cmac_handle.init(&self.smk_aek.key)?;
if msg3.msg3_body.additional_prop.len() > 0 {
cmac_handle.update_slice(&msg3.msg3_body.additional_prop)?;
}
cmac_handle.update_msg(&self.peer_pub_key)?;
let data_mac = cmac_handle.get_hash()?;
if !data_mac.consttime_memeq(&msg3.cmac) {
return Err(sgx_status_t::SGX_ERROR_MAC_MISMATCH);
}
Ok(())
}
fn set_error(&mut self, sgx_ret: sgx_status_t) -> sgx_status_t {
*self = Self::default();
self.state = SgxDhSessionState::SGX_DH_SESSION_STATE_ERROR;
match sgx_ret {
sgx_status_t::SGX_ERROR_OUT_OF_MEMORY => sgx_status_t::SGX_ERROR_OUT_OF_MEMORY,
_ => sgx_status_t::SGX_ERROR_UNEXPECTED,
}
}
}
#[derive(Copy, Clone, Default)]
struct SgxLAv2ProtoSpec {
signature: [u8; 6],
ver: u8,
rev: u8,
target_spec: [u16; 28],
}
unsafe impl ContiguousMemory for SgxLAv2ProtoSpec {}
impl SgxLAv2ProtoSpec {
pub unsafe fn to_report_data(&self) -> sgx_report_data_t {
mem::transmute::<SgxLAv2ProtoSpec, sgx_report_data_t>(*self)
}
pub unsafe fn from_report_data(data: &sgx_report_data_t) -> SgxLAv2ProtoSpec {
mem::transmute::<sgx_report_data_t, SgxLAv2ProtoSpec>(*data)
}
pub fn ts_count(&self) -> u16 {
self.target_spec[0] >> 8
}
pub fn is_valid(&self) -> bool {
self.ver == 2 && self.rev == 0 && self.target_spec[0] as u8 == 0 && self.ts_count() < 28
}
pub fn make_target_info(&self, rpt: &sgx_report_t, ti: &mut sgx_target_info_t) -> SgxError {
if !self.is_valid() {
return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER);
}
let d = ti as *mut sgx_target_info_t as *mut u8;
let f = rpt as *const sgx_report_t as *const u8;
rsgx_lfence();
let mut to: i32 = 0;
for i in 1..(self.ts_count() + 1) as usize {
let size: i32 = 1 << (self.target_spec[i] & 0xF);
to += size - 1;
to &= -size;
if (to + size) as usize > mem::size_of::<sgx_target_info_t>() {
return Err(sgx_status_t::SGX_ERROR_UNEXPECTED);
}
let from: i32 = (self.target_spec[i] >> 4) as i32;
if from >= 0 {
if (from + size) as usize > mem::size_of::<sgx_report_t>() {
return Err(sgx_status_t::SGX_ERROR_UNEXPECTED);
}
unsafe {
ptr::copy_nonoverlapping(
f.offset(from as isize),
d.offset(to as isize),
size as usize,
);
}
} else {
if from == -1 {
break;
} else {
return Err(sgx_status_t::SGX_ERROR_UNEXPECTED);
}
}
to += size;
}
Ok(())
}
}
const SGX_LAV2_PROTO_SPEC: SgxLAv2ProtoSpec = SgxLAv2ProtoSpec {
signature: [0x53, 0x47, 0x58, 0x20, 0x4C, 0x41], // "SGX LA"
ver: 2,
rev: 0,
target_spec: [
0x0600, // target_spec count & revision
0x0405, // MRENCLAVE
0x0304, // ATTRIBUTES
0x0140, // CET_ATTRIBUTES
0x1041, // CONFIGSVN
0x0102, // MISCSELECT
0x0C06, // CONFIGID
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
],
};
pub fn rsgx_self_target() -> SgxResult<sgx_target_info_t> {
let mut target_info = sgx_target_info_t::default();
let report = rsgx_self_report();
SGX_LAV2_PROTO_SPEC
.make_target_info(&report, &mut target_info)
.map(|_| target_info)
}