From 589a029050b04e18034a0a2e237d29607f0adeb1 Mon Sep 17 00:00:00 2001 From: Herman Date: Tue, 18 May 2021 20:12:34 +0200 Subject: [PATCH] feat(dh): support testing and cleanup module structure --- rtc_tenclave/Cargo.lock | 123 +++++++++ rtc_tenclave/Cargo.toml | 2 + rtc_tenclave/src/dh/mod.rs | 309 +-------------------- rtc_tenclave/src/dh/protected_channel.rs | 21 +- rtc_tenclave/src/dh/sessions.rs | 328 +++++++++++++++++++++++ rtc_tenclave/src/dh/types.rs | 145 ++++++++++ rtc_tenclave/src/lib.rs | 3 +- 7 files changed, 618 insertions(+), 313 deletions(-) create mode 100644 rtc_tenclave/src/dh/sessions.rs create mode 100644 rtc_tenclave/src/dh/types.rs diff --git a/rtc_tenclave/Cargo.lock b/rtc_tenclave/Cargo.lock index 5299bfdc..c0903b4b 100644 --- a/rtc_tenclave/Cargo.lock +++ b/rtc_tenclave/Cargo.lock @@ -1,5 +1,14 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "aho-corasick" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +dependencies = [ + "memchr", +] + [[package]] name = "autocfg" version = "1.0.1" @@ -57,12 +66,39 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "difference" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" + +[[package]] +name = "downcast" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bb454f0228b18c7f4c3b0ebbee346ed9c52e7443b0999cd543ff3571205701d" + +[[package]] +name = "float-cmp" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1267f4ac4f343772758f7b1bdcbe767c218bbab93bb432acbf5162bbf85a6c4" +dependencies = [ + "num-traits", +] + [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "fragile" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69a039c3498dc930fe810151a34ba0c1c70b02b8625035592e74432f678591f2" + [[package]] name = "getrandom" version = "0.1.14" @@ -157,6 +193,45 @@ dependencies = [ "cfg-if 1.0.0", ] +[[package]] +name = "memchr" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc" + +[[package]] +name = "mockall" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18d614ad23f9bb59119b8b5670a85c7ba92c5e9adf4385c81ea00c51c8be33d5" +dependencies = [ + "cfg-if 1.0.0", + "downcast", + "fragile", + "lazy_static", + "mockall_derive", + "predicates", + "predicates-tree", +] + +[[package]] +name = "mockall_derive" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dd4234635bca06fc96c7368d038061e0aae1b00a764dc817e900dc974e3deea" +dependencies = [ + "cfg-if 1.0.0", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "normalize-line-endings" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" + [[package]] name = "num-traits" version = "0.2.14" @@ -191,6 +266,35 @@ version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" +[[package]] +name = "predicates" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f49cfaf7fdaa3bfacc6fa3e7054e65148878354a5cfddcf661df4c851f8021df" +dependencies = [ + "difference", + "float-cmp", + "normalize-line-endings", + "predicates-core", + "regex", +] + +[[package]] +name = "predicates-core" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57e35a3326b75e49aa85f5dc6ec15b41108cf5aee58eabb1f274dd18b73c2451" + +[[package]] +name = "predicates-tree" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15f553275e5721409451eb85e15fd9a860a6e5ab4496eb215987502b5f5391f2" +dependencies = [ + "predicates-core", + "treeline", +] + [[package]] name = "proc-macro2" version = "1.0.26" @@ -394,6 +498,17 @@ dependencies = [ "bitflags", ] +[[package]] +name = "regex" +version = "1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + [[package]] name = "regex-syntax" version = "0.6.25" @@ -430,7 +545,9 @@ version = "0.1.0" dependencies = [ "cfg-if 1.0.0", "hex", + "mockall", "once_cell 1.4.0", + "once_cell 1.7.2", "proptest", "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.7.3 (git+https://github.com/mesalock-linux/rand-sgx?tag=v0.7.3_sgx1.1.3)", @@ -749,6 +866,12 @@ dependencies = [ "syn", ] +[[package]] +name = "treeline" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7f741b240f1a48843f9b8e0444fb55fb2a4ff67293b50a9179dfd5ea67f8d41" + [[package]] name = "unicode-xid" version = "0.2.2" diff --git a/rtc_tenclave/Cargo.toml b/rtc_tenclave/Cargo.toml index 5b62bf9a..a230bbc7 100644 --- a/rtc_tenclave/Cargo.toml +++ b/rtc_tenclave/Cargo.toml @@ -69,10 +69,12 @@ rand_std = { package = "rand", version = "0.7.3" } sgx_ucrypto = { git = "https://github.com/apache/teaclave-sgx-sdk.git" } serde_std = { package = "serde", version = "1.0.0" } serde_json_std = { package = "serde_json", version = "1.0.0" } +once_cell_std = { package = "once_cell", version="1.7.2" } # Test-only dependencies proptest = "1.0.0" tempfile = "3.2.0" +mockall = { version = "0.9.1", features = ["nightly"] } [patch."https://github.com/apache/teaclave-sgx-sdk.git"] sgx_types = { git = "https://github.com/apache/incubator-teaclave-sgx-sdk.git" } diff --git a/rtc_tenclave/src/dh/mod.rs b/rtc_tenclave/src/dh/mod.rs index c4b7e89f..89f66004 100644 --- a/rtc_tenclave/src/dh/mod.rs +++ b/rtc_tenclave/src/dh/mod.rs @@ -1,307 +1,12 @@ mod protected_channel; +mod sessions; +mod types; -use once_cell::sync::OnceCell; -use rtc_types::dh::{ExchangeReportResult, SessionRequestResult}; -use sgx_tdh::{SgxDhInitiator, SgxDhMsg1, SgxDhMsg2, SgxDhMsg3, SgxDhResponder}; +pub use sessions::*; -use sgx_tstd::{ - collections::HashMap, - enclave, - sync::{Arc, SgxMutex, SgxRwLock, SgxRwLockWriteGuard}, -}; -use sgx_types::*; -use std::{mem, ops::Deref}; - -use protected_channel::ProtectedChannel; - -extern "C" { - pub fn rtc_session_request_u( - ret: *mut SessionRequestResult, - src_enclave_id: sgx_enclave_id_t, - dest_enclave_id: sgx_enclave_id_t, - ) -> sgx_status_t; - pub fn rtc_exchange_report_u( - ret: *mut ExchangeReportResult, - src_enclave_id: sgx_enclave_id_t, - dest_enclave_id: sgx_enclave_id_t, - dh_msg2: *const sgx_dh_msg2_t, - ) -> sgx_status_t; - pub fn rtc_end_session_u( - ret: *mut sgx_status_t, - src_enclave_id: sgx_enclave_id_t, - dest_enclave_id: sgx_enclave_id_t, - ) -> sgx_status_t; -} - -#[no_mangle] -pub unsafe extern "C" fn test_dh() { - dh_sessions() - .establish_new(enclave::get_enclave_id()) - .expect("test failed"); -} - -enum AtomicSession { - Closed, - InProgress(SgxDhResponder), - Active(Arc>), -} - -pub struct DhSessions { - sessions: SgxRwLock>>, -} - -impl DhSessions { - fn get(&self, id: &u64) -> Option> { - self.sessions - .read() - .expect("RwLock poisoned") - .get(id) - .map(Clone::clone) +#[cfg(test)] +mod enclave { + pub fn get_enclave_id() -> u64 { + 78 } - - fn lock_write(&self) -> SgxRwLockWriteGuard>> { - self.sessions.write().expect("RwLock poisoned") - } - - pub fn get_active(&self, id: &u64) -> Option>> { - match self.get(id)?.as_ref() { - AtomicSession::Active(x) => Some(x.clone()), - _ => None, - } - } - - fn take_in_progress(&self, id: &u64) -> Option { - let mut sessions = self.lock_write(); - - if matches!(sessions.get(id)?.as_ref(), AtomicSession::InProgress(_)) { - match sessions.remove(id)?.deref() { - AtomicSession::InProgress(responder) => Some(*responder), - _ => unreachable!(), - } - } else { - None - } - } - - pub fn close_session(&self, id: &u64) -> () { - let mut sessions = self.lock_write(); - sessions.insert(*id, Arc::new(AtomicSession::Closed)); - () - } - - /// Creates and sets an active session between this enclave and the enclave with `id` using - /// the `key`. - /// - /// # Valid Operations (It is the responsibility of the caller to ensure these hold) - /// None -> Active = Ok - /// Closed -> Active = Ok - /// InProgress -> Active = Err if it is the same session - /// - In progress value needs to be removed from the map before using it to finalize a session - /// Active -> Active = Err - /// - A session should be closed and then recreated to prevent resetting the nonce counter - /// with the same key. - fn set_active( - &self, - id: u64, - key: sgx_key_128bit_t, - ) -> Result>, sgx_status_t> { - let channel = Arc::new(SgxMutex::new(ProtectedChannel::init(key))); - self.lock_write() - .insert(id, Arc::new(AtomicSession::Active(channel.clone()))); - Ok(channel) - } - - /// Sets an in_progress session for a responding enclave linked to the provided enclave. - /// - /// # Valid Operations (It is the responsibility of the caller to ensure these hold) - /// InProgress -> InProgress = Ok - /// Closed -> InProgress = Ok - /// Active -> In Progress = Ok if keying material differs - fn set_in_progress(&self, id: &u64, responder: SgxDhResponder) -> Result<(), sgx_status_t> { - let _result = self - .lock_write() - .insert(*id, Arc::new(AtomicSession::InProgress(responder))); - - Ok(()) - } - - pub fn establish_new( - &self, - dest_enclave_id: sgx_enclave_id_t, - ) -> Result>, sgx_status_t> { - let this_enclave_id = enclave::get_enclave_id(); - let mut initiator: SgxDhInitiator = SgxDhInitiator::init_session(); - - let dh_msg1 = init_responder_ocall(this_enclave_id, dest_enclave_id)?; - - let mut dh_msg2 = SgxDhMsg2::default(); - initiator.proc_msg1(&dh_msg1, &mut dh_msg2)?; - let dh_msg3 = exchange_report_ocall(this_enclave_id, dest_enclave_id, &dh_msg2)?; - - let mut aek = sgx_align_key_128bit_t::default(); // Session Key - let mut responder_identity = sgx_dh_session_enclave_identity_t::default(); - - // TODO: Verify identity - initiator.proc_msg3(&dh_msg3, &mut aek.key, &mut responder_identity)?; - - self.set_active(dest_enclave_id, aek.key) - } - - pub fn initiate_response( - &self, - src_enclave_id: &sgx_enclave_id_t, - ) -> Result { - let mut responder = SgxDhResponder::init_session(); - - let mut dh_msg1 = SgxDhMsg1::default(); - - responder.gen_msg1(&mut dh_msg1)?; - - self.set_in_progress(src_enclave_id, responder)?; - - Ok(dh_msg1) - } - - pub fn exchange_report( - &self, - src_enclave_id: sgx_enclave_id_t, - dh_msg2: &sgx_dh_msg2_t, - ) -> Result { - let mut responder = self - .take_in_progress(&src_enclave_id) - // TODO: custom error - .ok_or(sgx_status_t::SGX_ERROR_UNEXPECTED)?; - - let mut msg3 = SgxDhMsg3::default(); - let mut aek = sgx_align_key_128bit_t::default(); // Session Key - let mut initiator_identity = sgx_dh_session_enclave_identity_t::default(); - - responder.proc_msg2(dh_msg2, &mut msg3, &mut aek.key, &mut initiator_identity)?; - - // TODO: Verify initiator_identity - - self.set_active(src_enclave_id, aek.key)?; - - Ok(msg3) - } - - pub fn get_or_create_session( - &self, - dest_enclave_id: u64, - ) -> Result>, sgx_status_t> { - if let Some(channel) = self.get_active(&dest_enclave_id) { - Ok(channel) - } else { - self.establish_new(dest_enclave_id) - } - } -} - -fn init_responder_ocall( - this_enclave_id: u64, - dest_enclave_id: u64, -) -> Result { - let mut ret = SessionRequestResult::default(); - - // TODO: Safety - let ocall_res = unsafe { rtc_session_request_u(&mut ret, this_enclave_id, dest_enclave_id) }; - - match ocall_res { - sgx_status_t::SGX_SUCCESS => ret.into(), - err => Err(err), - } - - // dh_sessions().initiate_response(this_enclave_id) -} - -fn exchange_report_ocall( - this_enclave_id: u64, - dest_enclave_id: u64, - dh_msg2: &sgx_dh_msg2_t, -) -> Result { - let mut ret = ExchangeReportResult::default(); - - // TODO: Safety - let ocall_res = - unsafe { rtc_exchange_report_u(&mut ret, this_enclave_id, dest_enclave_id, dh_msg2) }; - - match ocall_res { - sgx_status_t::SGX_SUCCESS => { - let res: Result = ret.into(); - let mut msg3_raw = res?; - - let raw_len = - mem::size_of::() as u32 + msg3_raw.msg3_body.additional_prop_length; - - // TODO: Safety - unsafe { SgxDhMsg3::from_raw_dh_msg3_t(&mut msg3_raw, raw_len) } - .ok_or(sgx_status_t::SGX_ERROR_UNEXPECTED) - } - err => Err(err), - } - - // dh_sessions().exchange_report(this_enclave_id, dh_msg2) -} - -#[no_mangle] -pub extern "C" fn session_request(src_enclave_id: sgx_enclave_id_t) -> SessionRequestResult { - dh_sessions().initiate_response(&src_enclave_id).into() -} - -#[no_mangle] -pub extern "C" fn end_session(src_enclave_id: sgx_enclave_id_t) -> sgx_status_t { - // TODO: Ensure sessions close on both ends? - dh_sessions().close_session(&src_enclave_id); - sgx_status_t::SGX_SUCCESS -} - -#[no_mangle] -pub unsafe extern "C" fn exchange_report( - src_enclave_id: sgx_enclave_id_t, - dh_msg2_ptr: *const sgx_dh_msg2_t, -) -> ExchangeReportResult { - // TODO: Safety - let dh_msg2 = unsafe { &*dh_msg2_ptr }; - - dh_sessions() - .exchange_report(src_enclave_id, dh_msg2) - .map(|msg3| { - let mut msg3_raw = sgx_dh_msg3_t::default(); - unsafe { msg3.to_raw_dh_msg3_t(&mut msg3_raw, msg3.calc_raw_sealed_data_size()) }; - msg3_raw - }) - .into() -} - -// TODO: Integrate using function reference with similar signature or a config obj -fn verify_peer_enclave_trust(peer_identity: &sgx_dh_session_enclave_identity_t) -> Result<(), ()> { - let required_flags = SGX_FLAGS_INITTED; - let denied_flags = SGX_FLAGS_DEBUG; - let expected_mrenclave = [0_u8; SGX_HASH_SIZE]; - let expected_mrsigner = [0_u8; SGX_HASH_SIZE]; - - if peer_identity.attributes.flags & required_flags == required_flags { - return Err(()); - } - if peer_identity.attributes.flags & denied_flags != 0 { - return Err(()); - } - if peer_identity.mr_enclave.m != expected_mrenclave { - return Err(()); - } - if peer_identity.mr_signer.m != expected_mrsigner { - return Err(()); - } - - return Ok(()); -} - -pub fn dh_sessions() -> &'static DhSessions { - // NOTE: Something similar can be done in the OCALL library - // (by storing pointers to data inside the enclave, outside of the enclave) - // TODO: Figure out session timeouts - static DH_SESSIONS: OnceCell = OnceCell::new(); - DH_SESSIONS.get_or_init(|| DhSessions { - sessions: SgxRwLock::new(HashMap::new()), - }) } diff --git a/rtc_tenclave/src/dh/protected_channel.rs b/rtc_tenclave/src/dh/protected_channel.rs index 84f206b1..1eab6b98 100644 --- a/rtc_tenclave/src/dh/protected_channel.rs +++ b/rtc_tenclave/src/dh/protected_channel.rs @@ -1,23 +1,26 @@ use secrecy::{ExposeSecret, Secret}; use sgx_tcrypto::{rsgx_rijndael128GCM_decrypt, rsgx_rijndael128GCM_encrypt}; -use sgx_tstd::enclave; use sgx_types::*; use std::{convert::TryInto, u32}; +use super::types::AlignedKey; + +#[cfg(test)] +use super::enclave; +#[cfg(not(test))] +use sgx_tstd::enclave; + // NIST AES-GCM recommended IV size type GcmNonce = [u8; 12]; pub struct ProtectedChannel { counter: u32, - key: Secret, + key: Secret, } impl ProtectedChannel { - pub fn init(key: sgx_key_128bit_t) -> Self { - Self { - counter: 1, - key: Secret::new(key), - } + pub fn init(key: Secret) -> Self { + Self { counter: 1, key } } pub fn encrypt_message( @@ -29,7 +32,7 @@ impl ProtectedChannel { let mut dst = [0_u8; MESSAGE_SIZE]; let mut mac = sgx_aes_gcm_128bit_tag_t::default(); rsgx_rijndael128GCM_encrypt( - self.key.expose_secret(), + self.key.expose_secret().key(), &plaintext, &nonce, &aad, @@ -51,7 +54,7 @@ impl ProtectedChannel { ) -> Result<[u8; MESSAGE_SIZE], sgx_status_t> { let mut dst = [0_u8; MESSAGE_SIZE]; rsgx_rijndael128GCM_decrypt( - self.key.expose_secret(), + self.key.expose_secret().key(), &message.ciphertext, &message.nonce, &message.aad, diff --git a/rtc_tenclave/src/dh/sessions.rs b/rtc_tenclave/src/dh/sessions.rs new file mode 100644 index 00000000..048fb5f8 --- /dev/null +++ b/rtc_tenclave/src/dh/sessions.rs @@ -0,0 +1,328 @@ +use std::collections::HashMap; +use std::marker::PhantomData; +use std::prelude::v1::*; +use std::sync::Arc; + +use once_cell::sync::OnceCell; +use rtc_types::dh::{ExchangeReportResult, SessionRequestResult}; +use secrecy::Secret; +use sgx_types::*; + +use super::protected_channel::ProtectedChannel; +use super::types::{AlignedKey, RtcDhInitiator, RtcDhResponder}; + +#[cfg(not(test))] +use sgx_tstd::enclave; +#[cfg(not(test))] +use sgx_tstd::sync::{ + SgxMutex as Mutex, SgxRwLock as RwLock, SgxRwLockWriteGuard as RwLockWriteGuard, +}; + +#[cfg(test)] +use super::enclave; +#[cfg(test)] +use std::sync::{Mutex, RwLock, RwLockWriteGuard}; + +extern "C" { + pub fn rtc_session_request_u( + ret: *mut SessionRequestResult, + src_enclave_id: sgx_enclave_id_t, + dest_enclave_id: sgx_enclave_id_t, + ) -> sgx_status_t; + pub fn rtc_exchange_report_u( + ret: *mut ExchangeReportResult, + src_enclave_id: sgx_enclave_id_t, + dest_enclave_id: sgx_enclave_id_t, + dh_msg2: *const sgx_dh_msg2_t, + ) -> sgx_status_t; + pub fn rtc_end_session_u( + ret: *mut sgx_status_t, + src_enclave_id: sgx_enclave_id_t, + dest_enclave_id: sgx_enclave_id_t, + ) -> sgx_status_t; +} + +enum AtomicSession +where + TResp: RtcDhResponder, +{ + Closed, + InProgress(TResp), + Active(Arc>), +} + +pub struct DhSessions +where + TResp: RtcDhResponder, + TInit: RtcDhInitiator, +{ + sessions: RwLock>>>, + _phantom_init: PhantomData, +} + +impl DhSessions +where + TResp: RtcDhResponder, + TInit: RtcDhInitiator, +{ + fn get(&self, id: &u64) -> Option>> { + self.sessions + .read() + .expect("RwLock poisoned") + .get(id) + .map(Clone::clone) + } + + fn lock_write(&self) -> RwLockWriteGuard>>> { + self.sessions.write().expect("RwLock poisoned") + } + + pub fn get_active(&self, id: &u64) -> Option>> { + match self.get(id)?.as_ref() { + AtomicSession::Active(x) => Some(x.clone()), + _ => None, + } + } + + fn take_in_progress(&self, id: &u64) -> Option { + let mut sessions = self.lock_write(); + + if matches!(sessions.get(id)?.as_ref(), AtomicSession::InProgress(_)) { + match Arc::try_unwrap(sessions.remove(id)?) { + Ok(AtomicSession::InProgress(resp)) => Some(resp), + Ok(_) => unreachable!(), + Err(_) => None, + } + } else { + None + } + } + + pub fn close_session(&self, id: &u64) -> () { + let mut sessions = self.lock_write(); + sessions.insert(*id, Arc::new(AtomicSession::Closed)); + () + } + + /// Creates and sets an active session between this enclave and the enclave with `id` using + /// the `key`. + /// + /// # Valid Operations (It is the responsibility of the caller to ensure these hold) + /// None -> Active = Ok + /// Closed -> Active = Ok + /// InProgress -> Active = Err if it is the same session + /// - In progress value needs to be removed from the map before using it to finalize a session + /// Active -> Active = Err + /// - A session should be closed and then recreated to prevent resetting the nonce counter + /// with the same key. + fn set_active( + &self, + id: u64, + key: Secret, + ) -> Result>, sgx_status_t> { + let channel = Arc::new(Mutex::new(ProtectedChannel::init(key))); + self.lock_write() + .insert(id, Arc::new(AtomicSession::Active(channel.clone()))); + Ok(channel) + } + + /// Sets an in_progress session for a responding enclave linked to the provided enclave. + /// + /// # Valid Operations (It is the responsibility of the caller to ensure these hold) + /// InProgress -> InProgress = Ok + /// Closed -> InProgress = Ok + /// Active -> In Progress = Ok if keying material differs + fn set_in_progress(&self, id: &u64, responder: TResp) -> Result<(), sgx_status_t> { + let _result = self + .lock_write() + .insert(*id, Arc::new(AtomicSession::InProgress(responder))); + + Ok(()) + } + + pub fn establish_new( + &self, + dest_enclave_id: sgx_enclave_id_t, + ) -> Result>, sgx_status_t> { + let this_enclave_id = enclave::get_enclave_id(); + let mut initiator = TInit::init_session(); + + let dh_msg1 = init_responder_ocall(this_enclave_id, dest_enclave_id)?; + + let dh_msg2 = initiator.proc_msg1(&dh_msg1)?; + let mut dh_msg3 = exchange_report_ocall(this_enclave_id, dest_enclave_id, &dh_msg2)?; + + // TODO: Verify identity + let dh_values = initiator.proc_msg3(&mut dh_msg3)?; + + self.set_active(dest_enclave_id, dh_values.session_key) + } + + pub fn initiate_response( + &self, + src_enclave_id: &sgx_enclave_id_t, + ) -> Result { + let mut responder = TResp::init_session(); + + let dh_msg1 = responder.gen_msg1()?; + + self.set_in_progress(src_enclave_id, responder)?; + + Ok(dh_msg1) + } + + pub fn exchange_report( + &self, + src_enclave_id: sgx_enclave_id_t, + dh_msg2: &sgx_dh_msg2_t, + ) -> Result { + let mut responder = self + .take_in_progress(&src_enclave_id) + // TODO: custom error + .ok_or(sgx_status_t::SGX_ERROR_UNEXPECTED)?; + + let (msg3, dh_values) = responder.proc_msg2(dh_msg2)?; + + // TODO: Verify initiator_identity + + self.set_active(src_enclave_id, dh_values.session_key)?; + + Ok(msg3) + } + + pub fn get_or_create_session( + &self, + dest_enclave_id: u64, + ) -> Result>, sgx_status_t> { + if let Some(channel) = self.get_active(&dest_enclave_id) { + Ok(channel) + } else { + self.establish_new(dest_enclave_id) + } + } +} + +fn init_responder_ocall( + this_enclave_id: u64, + dest_enclave_id: u64, +) -> Result { + let mut ret = SessionRequestResult::default(); + + // TODO: Safety + let ocall_res = unsafe { rtc_session_request_u(&mut ret, this_enclave_id, dest_enclave_id) }; + + match ocall_res { + sgx_status_t::SGX_SUCCESS => ret.into(), + err => Err(err), + } + + // dh_sessions().initiate_response(this_enclave_id) +} + +fn exchange_report_ocall( + this_enclave_id: u64, + dest_enclave_id: u64, + dh_msg2: &sgx_dh_msg2_t, +) -> Result { + let mut ret = ExchangeReportResult::default(); + + // TODO: Safety + let ocall_res = + unsafe { rtc_exchange_report_u(&mut ret, this_enclave_id, dest_enclave_id, dh_msg2) }; + + match ocall_res { + sgx_status_t::SGX_SUCCESS => ret.into(), + err => Err(err), + } + + // dh_sessions().exchange_report(this_enclave_id, dh_msg2) +} + +#[no_mangle] +pub extern "C" fn session_request(src_enclave_id: sgx_enclave_id_t) -> SessionRequestResult { + dh_sessions().initiate_response(&src_enclave_id).into() +} + +#[no_mangle] +pub extern "C" fn end_session(src_enclave_id: sgx_enclave_id_t) -> sgx_status_t { + // TODO: Ensure sessions close on both ends? + dh_sessions().close_session(&src_enclave_id); + sgx_status_t::SGX_SUCCESS +} + +#[no_mangle] +pub unsafe extern "C" fn exchange_report( + src_enclave_id: sgx_enclave_id_t, + dh_msg2_ptr: *const sgx_dh_msg2_t, +) -> ExchangeReportResult { + // TODO: Safety + let dh_msg2 = unsafe { &*dh_msg2_ptr }; + + dh_sessions() + .exchange_report(src_enclave_id, dh_msg2) + .into() +} + +// TODO: Integrate using function reference with similar signature or a config obj +fn verify_peer_enclave_trust(peer_identity: &sgx_dh_session_enclave_identity_t) -> Result<(), ()> { + let required_flags = SGX_FLAGS_INITTED; + let denied_flags = SGX_FLAGS_DEBUG; + let expected_mrenclave = [0_u8; SGX_HASH_SIZE]; + let expected_mrsigner = [0_u8; SGX_HASH_SIZE]; + + if peer_identity.attributes.flags & required_flags == required_flags { + return Err(()); + } + if peer_identity.attributes.flags & denied_flags != 0 { + return Err(()); + } + if peer_identity.mr_enclave.m != expected_mrenclave { + return Err(()); + } + if peer_identity.mr_signer.m != expected_mrsigner { + return Err(()); + } + + return Ok(()); +} + +#[cfg(not(test))] +pub use sgx_impl::dh_sessions; + +#[cfg(test)] +pub use test::dh_sessions; + +#[cfg(not(test))] +mod sgx_impl { + use super::*; + use sgx_tdh::{SgxDhInitiator, SgxDhResponder}; + + pub fn dh_sessions() -> &'static DhSessions { + // NOTE: Something similar can be done in the OCALL library + // (by storing pointers to data inside the enclave, outside of the enclave) + // TODO: Figure out session timeouts + static DH_SESSIONS: OnceCell> = OnceCell::new(); + DH_SESSIONS.get_or_init(|| DhSessions { + sessions: RwLock::new(HashMap::new()), + _phantom_init: PhantomData::default(), + }) + } +} + +#[cfg(test)] +mod test { + use super::super::types::*; + use super::*; + + pub fn dh_sessions() -> &'static DhSessions { + // NOTE: Something similar can be done in the OCALL library + // (by storing pointers to data inside the enclave, outside of the enclave) + // TODO: Figure out session timeouts + static DH_SESSIONS: OnceCell> = + OnceCell::new(); + DH_SESSIONS.get_or_init(|| DhSessions { + sessions: RwLock::new(HashMap::new()), + _phantom_init: PhantomData::default(), + }) + } +} diff --git a/rtc_tenclave/src/dh/types.rs b/rtc_tenclave/src/dh/types.rs new file mode 100644 index 00000000..e10fd547 --- /dev/null +++ b/rtc_tenclave/src/dh/types.rs @@ -0,0 +1,145 @@ +use secrecy::{Secret, Zeroize}; +use sgx_types::*; + +#[cfg(test)] +use mockall::automock; + +pub struct AlignedKey(sgx_align_key_128bit_t); + +impl AlignedKey { + pub fn new(key: sgx_align_key_128bit_t) -> Secret { + Secret::new(Self(key)) + } + pub fn key(&self) -> &sgx_key_128bit_t { + &self.0.key + } +} + +impl Zeroize for AlignedKey { + fn zeroize(&mut self) { + self.0.key.zeroize() + } +} + +pub struct DhValues { + pub(crate) session_key: Secret, + pub(crate) peer_identity: sgx_dh_session_enclave_identity_t, +} + +#[cfg_attr(test, automock)] +pub trait RtcDhInitiator { + fn init_session() -> Self; + fn proc_msg1(&mut self, msg1: &sgx_dh_msg1_t) -> Result; + + /// Process msg3 and return the DhValues for the session + /// + /// We currently allow no additional prop data, and will return an error + /// if `msg3.msg3_body.additional_prop_length` is greater than 0. This can be changed + /// to a well known constant size in the future if the additional prop needs to be used. + fn proc_msg3(&mut self, msg3: &mut sgx_dh_msg3_t) -> Result; +} +#[cfg_attr(test, automock)] +pub trait RtcDhResponder { + fn init_session() -> Self; + fn gen_msg1(&mut self) -> Result; + + /// Process msg3 and return the DhValues for the session + fn proc_msg2( + &mut self, + msg2: &sgx_dh_msg2_t, + ) -> Result<(sgx_dh_msg3_t, DhValues), sgx_status_t>; +} + +#[cfg(not(test))] +pub mod impl_sgx { + use super::*; + use sgx_tdh::{SgxDhInitiator, SgxDhMsg3, SgxDhResponder}; + use sgx_tstd::mem; + + impl RtcDhInitiator for SgxDhInitiator { + fn init_session() -> Self { + SgxDhInitiator::init_session() + } + + fn proc_msg1(&mut self, msg1: &sgx_dh_msg1_t) -> Result { + let mut msg2 = sgx_dh_msg2_t::default(); + SgxDhInitiator::proc_msg1(self, msg1, &mut msg2)?; + Ok(msg2) + } + + fn proc_msg3(&mut self, msg3: &mut sgx_dh_msg3_t) -> Result { + // Only allow msg3 values with no additional prop data + if msg3.msg3_body.additional_prop_length > 0 { + return Err(sgx_status_t::SGX_ERROR_INVALID_PARAMETER); + } + let mut aek = sgx_align_key_128bit_t::default(); + let mut peer_identity = sgx_dh_session_enclave_identity_t::default(); + + // Safety: + // This should be safe since we don't allow additional prop data, so we don't have to + // worry about memory allocations + let msg3_full = unsafe { + SgxDhMsg3::from_raw_dh_msg3_t(msg3, mem::size_of::() as u32) + } + .ok_or(sgx_status_t::SGX_ERROR_INVALID_PARAMETER)?; + + SgxDhInitiator::proc_msg3(self, &msg3_full, &mut aek.key, &mut peer_identity)?; + Ok(DhValues { + session_key: AlignedKey::new(aek), + peer_identity, + }) + } + } + + impl RtcDhResponder for SgxDhResponder { + fn init_session() -> Self { + SgxDhResponder::init_session() + } + + fn gen_msg1(&mut self) -> Result { + let mut msg1 = sgx_dh_msg1_t::default(); + SgxDhResponder::gen_msg1(self, &mut msg1)?; + Ok(msg1) + } + + fn proc_msg2( + &mut self, + msg2: &sgx_dh_msg2_t, + ) -> Result<(sgx_dh_msg3_t, DhValues), sgx_status_t> { + let mut msg3 = SgxDhMsg3::new(); + let mut aek = sgx_align_key_128bit_t::default(); + let mut peer_identity = sgx_dh_session_enclave_identity_t::default(); + + SgxDhResponder::proc_msg2(self, msg2, &mut msg3, &mut aek.key, &mut peer_identity)?; + + let msg3_len = msg3.calc_raw_sealed_data_size(); + + // Only allow messages with 0 additional prop size + if msg3_len == (mem::size_of::() as u32) { + // This branch should never be reached since we don't use additional prop + // -- + // Normally unreachable code should panic but since this will open up a trivial + // way for untrusted code to trigger a panic (and potentially UB if we unwind over + // the ffi boundary) we should return with an `Err` instead. + return Err(sgx_status_t::SGX_ERROR_UNEXPECTED); + } + + let mut msg3_raw = sgx_dh_msg3_t::default(); + + // Safety: + // This function should be safe since we don't allow additional prop data, so we don't have + // to worry about memory allocations. We also guard against cases where this does not uphold + // to prevent UB. + unsafe { msg3.to_raw_dh_msg3_t(&mut msg3_raw, msg3_len) } + .ok_or(sgx_status_t::SGX_ERROR_UNEXPECTED)?; + + Ok(( + msg3_raw, + DhValues { + session_key: AlignedKey::new(aek), + peer_identity, + }, + )) + } + } +} diff --git a/rtc_tenclave/src/lib.rs b/rtc_tenclave/src/lib.rs index 6cd46cf1..ed21c268 100644 --- a/rtc_tenclave/src/lib.rs +++ b/rtc_tenclave/src/lib.rs @@ -18,11 +18,10 @@ cfg_if::cfg_if! { extern crate sgx_ucrypto as sgx_tcrypto; extern crate serde_std as serde; extern crate serde_json_std as serde_json; + extern crate once_cell_std as once_cell; } } -// TODO: Refactor dh module to allow us to test with it enabled -#[cfg(not(test))] pub mod dh; pub mod crypto;