| // 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) |
| } |