From 850018f10213d8781b479811e2d086446c7581ff Mon Sep 17 00:00:00 2001 From: Varsha Date: Fri, 24 Mar 2023 23:12:50 +0000 Subject: [PATCH] ConstantTimeEq proc macro derive --- Cargo.toml | 1 + constant_time_derive/Cargo.toml | 14 +++ constant_time_derive/src/lib.rs | 99 +++++++++++++++++++ core/build/src/lib.rs | 20 ++++ core/sys/types/Cargo.toml | 4 + core/sys/types/build.rs | 30 ++++++ core/types/Cargo.toml | 2 + core/types/src/attestation_key.rs | 101 +++++++++++++++++-- core/types/src/attributes.rs | 28 +++++- core/types/src/config_id.rs | 18 +++- core/types/src/key_request.rs | 34 ++++++- core/types/src/measurement.rs | 32 +++++- core/types/src/quote.rs | 139 ++++++++++++++++++++++++-- core/types/src/report.rs | 98 ++++++++++++++++-- core/types/src/svn.rs | 142 ++++++++++++++++++++++++++- core/types/src/target_info.rs | 42 +++++++- dcap/ql/types/Cargo.toml | 2 + dcap/ql/types/src/lib.rs | 15 ++- dcap/quoteverify/types/Cargo.toml | 2 + dcap/quoteverify/types/src/lib.rs | 15 ++- dcap/sys/types/Cargo.toml | 2 + dcap/sys/types/build.rs | 3 +- dcap/types/Cargo.toml | 1 + dcap/types/src/certification_data.rs | 17 ++-- dcap/types/src/quote3.rs | 3 +- dcap/types/src/quoting_enclave.rs | 27 ++++- tservice/types/Cargo.toml | 2 + tservice/types/src/seal.rs | 35 ++++++- 28 files changed, 876 insertions(+), 52 deletions(-) create mode 100644 constant_time_derive/Cargo.toml create mode 100644 constant_time_derive/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 449bc8aa..81d29dd8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,7 @@ members = [ "capable/sys", "capable/sys/types", "capable/types", + "constant_time_derive", "core/build", "core/sys/types", "core/types", diff --git a/constant_time_derive/Cargo.toml b/constant_time_derive/Cargo.toml new file mode 100644 index 00000000..6e84deed --- /dev/null +++ b/constant_time_derive/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "constant_time_derive" +version = "0.1.0" +edition = "2021" +license = "Apache-2.0" + +[lib] +proc-macro = true + +[dependencies] +proc-macro2 = "1.0.8" +quote = "1.0" +subtle = { version = "2.4.0", default-features = false } +syn = "1.0" diff --git a/constant_time_derive/src/lib.rs b/constant_time_derive/src/lib.rs new file mode 100644 index 00000000..6ea68d53 --- /dev/null +++ b/constant_time_derive/src/lib.rs @@ -0,0 +1,99 @@ +// Copyright (c) 2023 The MobileCoin Foundation + +use proc_macro::TokenStream; +use quote::quote; +use syn::{parse_macro_input, Data, DataEnum, DeriveInput, Fields, GenericParam, Generics}; + +#[proc_macro_derive(ConstantTimeEq)] +pub fn constant_time_eq(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as DeriveInput); + derive_ct_eq(&input) +} +// TODO: Check or remove padding and align decorators on the struct +fn parse_fields(fields: &Fields) -> Result { + match &fields { + Fields::Named(fields_named) => { + let mut token_stream = quote!(); + let mut iter = fields_named.named.iter().peekable(); + + while let Some(field) = iter.next() { + let ident = &field.ident; + match iter.peek() { + None => token_stream.extend(quote! { {self.#ident}.ct_eq(&{other.#ident}) }), + Some(_) => { + token_stream.extend(quote! { {self.#ident}.ct_eq(&{other.#ident}) & }) + } + } + } + Ok(token_stream) + } + Fields::Unnamed(_) => { + let token_stream = quote! { {self.0}.ct_eq({&other.0}) }; + + Ok(token_stream) + } + Fields::Unit => Err("Constant time cannot be derived for unit fields"), + } +} + +fn parse_enum(data_enum: &DataEnum) -> Result { + for variant in data_enum.variants.iter() { + if let Fields::Unnamed(_) = variant.fields { + panic!("Cannot derive ct_eq for fields in enums") + } + } + let token_stream = quote! { + if self == other { + ::subtle::Choice::from(1) + } + else { + ::subtle::Choice::from(0) + } + }; + + Ok(token_stream) +} + +fn parse_data(data: &Data) -> Result { + match data { + Data::Struct(variant_data) => parse_fields(&variant_data.fields), + Data::Enum(data_enum) => parse_enum(data_enum), + Data::Union(..) => Err("Constant time cannot be derived for a union"), + } +} + +fn parse_lifetime(generics: &Generics) -> bool { + for i in generics.params.iter() { + if let GenericParam::Lifetime(_) = i { + return true; + } + } + false +} + +fn derive_ct_eq(input: &DeriveInput) -> TokenStream { + let ident = &input.ident; + let data = &input.data; + let generics = &input.generics; + + let is_lifetime = parse_lifetime(generics); + let ct_eq_stream: proc_macro2::TokenStream = + parse_data(data).expect("Failed to parse DeriveInput data"); + let data_ident = if is_lifetime { + format!("{}<'_>", ident) + } else { + ident.to_string() + }; + let ident_stream: proc_macro2::TokenStream = data_ident.parse().unwrap(); + + let expanded: proc_macro2::TokenStream = quote! { + impl ::subtle::ConstantTimeEq for #ident_stream { + fn ct_eq(&self, other: &Self) -> ::subtle::Choice { + use ::subtle::ConstantTimeEq; + return #ct_eq_stream + } + } + }; + + expanded.into() +} diff --git a/core/build/src/lib.rs b/core/build/src/lib.rs index 56656718..3d8337f2 100644 --- a/core/build/src/lib.rs +++ b/core/build/src/lib.rs @@ -117,6 +117,8 @@ pub struct SgxParseCallbacks { // Dynamically Sized types dynamically_sized_types: Vec, + + constant_time_types: Vec, } impl SgxParseCallbacks { @@ -176,6 +178,20 @@ impl SgxParseCallbacks { self } + /// Types to derive constant time for + /// + /// # Arguments + /// * `default_types` - Types to derive default for. + pub fn derive_constant_time<'a, E, I>(mut self, constant_time_types: I) -> Self + where + I: IntoIterator, + E: ToString + 'a + ?Sized, + { + self.constant_time_types + .extend(constant_time_types.into_iter().map(ToString::to_string)); + self + } + /// Dynamically Sized Types /// /// # Arguments @@ -205,6 +221,10 @@ impl ParseCallbacks for SgxParseCallbacks { attributes.push("Default"); } + if self.constant_time_types.iter().any(|n| *n == name) { + attributes.push("constant_time_derive::ConstantTimeEq"); + } + // The [enum_types] method adds enums to the [copyable_types] if self.copyable_types.iter().any(|n| *n == name) { attributes.push("Copy"); diff --git a/core/sys/types/Cargo.toml b/core/sys/types/Cargo.toml index bec9267a..d36dd5c0 100644 --- a/core/sys/types/Cargo.toml +++ b/core/sys/types/Cargo.toml @@ -14,6 +14,10 @@ rust-version = "1.62.1" [lib] doctest = false +[dependencies] +constant_time_derive = { path = "../../../constant_time_derive", version = "0.1.0" } +subtle = { version = "2.4.0", default-features = false } + [build-dependencies] bindgen = "0.64.0" cargo-emit = "0.2.1" diff --git a/core/sys/types/build.rs b/core/sys/types/build.rs index d652f288..c93b4827 100644 --- a/core/sys/types/build.rs +++ b/core/sys/types/build.rs @@ -99,6 +99,9 @@ fn main() { "sgx_measurement_t", "sgx_report_data_t", "sgx_attributes_t", + "sgx_key_request_t", + "sgx_platform_info_t", + "sgx_basename_t", ]) .dynamically_sized_types(["sgx_quote_t"]) .derive_default([ @@ -108,6 +111,33 @@ fn main() { "sgx_quote_nonce_t", "sgx_update_info_bit_t", "sgx_qe_report_info_t", + ]) + .derive_constant_time([ + "sgx_att_key_id_ext_t", + "sgx_ql_att_key_id_t", + "sgx_measurement_t", + "sgx_attributes_t", + "sgx_key_id_t", + "sgx_config_id_t", + "sgx_key_request_t", + "sgx_qe_report_info_t", + "sgx_platform_info_t", + "sgx_update_info_bit_t", + "sgx_epid_group_id_t", + "sgx_basename_t", + "sgx_quote_nonce_t", + "sgx_report_t", + "sgx_target_info_t", + "sgx_cpu_svn_t", + "sgx_mac_t", + "sgx_report_data_t", + "sgx_isvfamily_id_t", + "sgx_isvext_prod_id_t", + "sgx_prod_id_t", + "sgx_config_svn_t", + "sgx_isv_svn_t", + "sgx_cpu_svn_t", + "sgx_report_body_t", ]); let mut builder = mc_sgx_core_build::sgx_builder() .header("wrapper.h") diff --git a/core/types/Cargo.toml b/core/types/Cargo.toml index ae40c492..b279a0b3 100644 --- a/core/types/Cargo.toml +++ b/core/types/Cargo.toml @@ -18,12 +18,14 @@ alloc = [] [dependencies] bitflags = "2.0.0" +constant_time_derive = { path = "../../constant_time_derive", version = "0.1.0" } displaydoc = { version = "0.2.3", default-features = false } mc-sgx-core-sys-types = { path = "../sys/types", version = "=0.5.1-beta.0" } mc-sgx-util = { path = "../../util", version = "=0.5.1-beta.0" } nom = { version = "7.1.2", default-features = false } rand_core = { version = "0.6.4", default-features = false } serde = { version = "1.0.152", default-features = false, features = ["derive"], optional = true } +subtle = { version = "2.4.0", default-features = false, features = ["i128"] } # `getrandom` is pulled in by `rand_core` we only need to access it directly when registering a custom spng, # `register_custom_getrandom`, which only happens for target_os = none diff --git a/core/types/src/attestation_key.rs b/core/types/src/attestation_key.rs index f7b4bea4..2e5b1969 100644 --- a/core/types/src/attestation_key.rs +++ b/core/types/src/attestation_key.rs @@ -6,6 +6,7 @@ use crate::{ report::{ExtendedProductId, FamilyId}, ConfigId, FfiError, }; +use constant_time_derive::ConstantTimeEq; use mc_sgx_core_sys_types::{sgx_att_key_id_ext_t, sgx_ql_att_key_id_t, sgx_quote_sign_type_t}; #[derive(Debug, Clone, Hash, PartialEq, Eq)] @@ -14,7 +15,7 @@ pub enum MrSignerKeyHash { Sha384([u8; 48]), } -#[derive(Default, Debug, Clone, Hash, PartialEq, Eq)] +#[derive(Default, Debug, Clone, Hash, PartialEq, Eq, ConstantTimeEq)] #[non_exhaustive] #[repr(u16)] pub enum Algorithm { @@ -45,7 +46,7 @@ impl TryFrom for Algorithm { } } -#[derive(Debug, Default, Clone, Hash, PartialEq, Eq)] +#[derive(Debug, Default, Clone, Hash, PartialEq, Eq, ConstantTimeEq)] #[repr(transparent)] pub struct Version(u16); @@ -53,7 +54,7 @@ impl_newtype! { Version, u16; } -#[derive(Debug, Default, Clone, Hash, PartialEq, Eq)] +#[derive(Debug, Default, Clone, Hash, PartialEq, Eq, ConstantTimeEq)] #[repr(transparent)] pub struct Id(u16); @@ -62,7 +63,7 @@ impl_newtype! { } /// The type of quote. Only valid for EPID quotes. -#[derive(Debug, Clone, Hash, PartialEq, Eq)] +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, ConstantTimeEq)] #[non_exhaustive] #[repr(u16)] pub enum QuoteSignatureKind { @@ -88,7 +89,7 @@ impl TryFrom for QuoteSignatureKind { /// Attestation key from th quoting library. contains the quoting enclaves ID /// and the attestation algorithm -#[derive(Default, Debug, Clone, Hash, PartialEq, Eq)] +#[derive(Default, Debug, Clone, Hash, PartialEq, Eq, ConstantTimeEq)] #[repr(transparent)] pub struct AttestationKeyId(sgx_ql_att_key_id_t); @@ -150,7 +151,7 @@ impl_newtype! { } /// Service Provider ID -#[derive(Default, Debug, Clone, Hash, PartialEq, Eq)] +#[derive(Default, Debug, Clone, Hash, PartialEq, Eq, ConstantTimeEq)] #[repr(transparent)] pub struct ServiceProviderId([u8; 16]); @@ -159,7 +160,7 @@ impl_newtype! { } /// Extended Attestation Key ID -#[derive(Debug, Default, Clone, Hash, PartialEq, Eq)] +#[derive(Debug, Default, Clone, Hash, PartialEq, Eq, ConstantTimeEq)] #[repr(transparent)] pub struct ExtendedAttestationKeyId(sgx_att_key_id_ext_t); @@ -187,6 +188,7 @@ impl_newtype! { #[cfg(test)] mod test { use super::*; + use subtle::ConstantTimeEq; use yare::parameterized; #[test] @@ -251,6 +253,91 @@ mod test { assert_eq!(key.algorithm_id().unwrap(), Algorithm::Reserved); } + #[test] + fn ct_eq_extended_attestation_key_id() { + let first = ExtendedAttestationKeyId::default(); + let second = ExtendedAttestationKeyId::default(); + let choice_result = first.ct_eq(&second); + let result: bool = From::from(choice_result); + assert!(result); + } + + #[test] + fn ct_eq_version() { + let first = Version(2); + let second = Version(2); + let choice_result = first.ct_eq(&second); + let result: bool = From::from(choice_result); + assert!(result); + } + + #[test] + fn ct_eq_service_provider_id() { + let first = ServiceProviderId([0u8; 16]); + let second = ServiceProviderId([0u8; 16]); + let choice_result = first.ct_eq(&second); + let result: bool = From::from(choice_result); + assert!(result); + } + + #[test] + fn ct_eq_id() { + let first = Id(4); + let second = Id(4); + let choice_result = first.ct_eq(&second); + let result: bool = From::from(choice_result); + assert!(result); + } + + #[test] + fn ct_eq_sgx_key() { + let sgx_key = sgx_ql_att_key_id_t { + id: 1, + version: 2, + mrsigner_length: 48, + mrsigner: [4u8; 48], + prod_id: 5, + extended_prod_id: [6u8; 16], + config_id: [7u8; 64], + family_id: [8u8; 16], + algorithm_id: Algorithm::Reserved as u32, + }; + let other_sgx_key = sgx_ql_att_key_id_t { + id: 1, + version: 2, + mrsigner_length: 48, + mrsigner: [4u8; 48], + prod_id: 5, + extended_prod_id: [6u8; 16], + config_id: [7u8; 64], + family_id: [8u8; 16], + algorithm_id: Algorithm::Reserved as u32, + }; + let first_key: AttestationKeyId = sgx_key.into(); + let second_key: AttestationKeyId = other_sgx_key.into(); + let choice_result = first_key.ct_eq(&second_key); + let result: bool = From::from(choice_result); + assert!(result); + } + + #[test] + fn ct_eq_algorithm() { + let first = Algorithm::Reserved; + let second = Algorithm::Reserved; + let choice_result = first.ct_eq(&second); + let result: bool = From::from(choice_result); + assert!(result); + } + + #[test] + fn ct_eq_quote_signature_kind() { + let first = QuoteSignatureKind::Linkable; + let second = QuoteSignatureKind::Linkable; + let choice_result = first.ct_eq(&second); + let result: bool = From::from(choice_result); + assert!(result); + } + #[parameterized( nothing = {0}, too_small_256 = {31}, diff --git a/core/types/src/attributes.rs b/core/types/src/attributes.rs index 8c612c51..fe56f180 100644 --- a/core/types/src/attributes.rs +++ b/core/types/src/attributes.rs @@ -3,11 +3,12 @@ //! SGX Attributes types use crate::impl_newtype; +use constant_time_derive::ConstantTimeEq; use mc_sgx_core_sys_types::{sgx_attributes_t, sgx_misc_attribute_t, sgx_misc_select_t}; /// Attributes of the enclave #[repr(transparent)] -#[derive(Default, Debug, Clone, Hash, PartialEq, Eq, Copy)] +#[derive(Default, Debug, Clone, Hash, PartialEq, Eq, Copy, ConstantTimeEq)] pub struct Attributes(sgx_attributes_t); impl_newtype! { Attributes, sgx_attributes_t; @@ -37,7 +38,7 @@ impl Attributes { /// Miscellaneous select bits for target enclave. Reserved for future extension. #[repr(transparent)] -#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Default)] +#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Default, ConstantTimeEq)] pub struct MiscellaneousSelect(sgx_misc_select_t); impl_newtype! { @@ -55,9 +56,30 @@ impl_newtype! { #[cfg(test)] mod test { - extern crate std; use super::*; + use subtle::ConstantTimeEq; use yare::parameterized; + #[test] + fn ct_eq_attributes() { + let first_sgx_attributes = sgx_attributes_t { flags: 1, xfrm: 2 }; + let first: Attributes = first_sgx_attributes.into(); + let second_sgx_attributes = sgx_attributes_t { flags: 1, xfrm: 2 }; + let second: Attributes = second_sgx_attributes.into(); + + let result_choice = first.ct_eq(&second); + let result: bool = From::from(result_choice); + assert!(result); + } + + #[test] + fn ct_eq_miscellaneous_select() { + let first: MiscellaneousSelect = MiscellaneousSelect::from(2); + let second: MiscellaneousSelect = MiscellaneousSelect::from(2); + + let result_choice = first.ct_eq(&second); + let result: bool = From::from(result_choice); + assert!(result); + } #[test] fn sgx_attributes_to_attributes() { diff --git a/core/types/src/config_id.rs b/core/types/src/config_id.rs index 3f83ca21..a5a3c83c 100644 --- a/core/types/src/config_id.rs +++ b/core/types/src/config_id.rs @@ -2,10 +2,11 @@ //! SGX Config ID use crate::impl_newtype; +use constant_time_derive::ConstantTimeEq; use mc_sgx_core_sys_types::{sgx_config_id_t, SGX_CONFIGID_SIZE}; /// Config ID -#[derive(Debug, Clone, Hash, PartialEq, Eq)] +#[derive(Debug, Clone, Hash, PartialEq, Eq, ConstantTimeEq)] #[repr(transparent)] pub struct ConfigId(sgx_config_id_t); @@ -19,3 +20,18 @@ impl Default for ConfigId { Self::from([0; SGX_CONFIGID_SIZE]) } } + +#[cfg(test)] +mod test { + use super::*; + use subtle::ConstantTimeEq; + + #[test] + fn default_extended_attestation_key_id() { + let first_config_id = ConfigId::from([11u8; 64]); + let second_config_id = ConfigId::from([11u8; 64]); + let result_choice = first_config_id.ct_eq(&second_config_id); + let result: bool = From::from(result_choice); + assert!(result); + } +} diff --git a/core/types/src/key_request.rs b/core/types/src/key_request.rs index 69b82d7b..ef2f8f1e 100644 --- a/core/types/src/key_request.rs +++ b/core/types/src/key_request.rs @@ -6,6 +6,7 @@ use crate::{ MiscellaneousSelect, }; use bitflags::bitflags; +use constant_time_derive::ConstantTimeEq; use mc_sgx_core_sys_types::{ sgx_key_128bit_t, sgx_key_id_t, sgx_key_request_t, SGX_KEYID_SIZE, SGX_KEYPOLICY_CONFIGID, SGX_KEYPOLICY_ISVEXTPRODID, SGX_KEYPOLICY_ISVFAMILYID, SGX_KEYPOLICY_MRENCLAVE, @@ -16,7 +17,7 @@ use mc_sgx_core_sys_types::{ use rand_core::{CryptoRng, RngCore}; /// Key ID -#[derive(Default, Debug, Clone, Hash, PartialEq, Eq)] +#[derive(Default, Debug, Clone, Hash, PartialEq, Eq, ConstantTimeEq)] #[repr(transparent)] pub struct KeyId(sgx_key_id_t); @@ -25,7 +26,7 @@ impl_newtype_for_bytestruct! { } /// Key Name -#[derive(Debug, Clone, Hash, PartialEq, Eq)] +#[derive(Debug, Clone, Hash, PartialEq, Eq, ConstantTimeEq)] #[non_exhaustive] #[repr(u16)] pub enum KeyName { @@ -70,7 +71,7 @@ bitflags! { } /// Key request -#[derive(Debug, Clone, Hash, PartialEq, Eq)] +#[derive(Debug, Clone, Hash, PartialEq, Eq, ConstantTimeEq)] #[repr(transparent)] pub struct KeyRequest(sgx_key_request_t); impl_newtype! { @@ -78,7 +79,7 @@ impl_newtype! { } /// A builder for creating a [`KeyRequest`] -#[derive(Debug, Clone, Hash, PartialEq, Eq)] +#[derive(Debug, Clone, Hash, PartialEq, Eq, ConstantTimeEq)] #[repr(transparent)] pub struct KeyRequestBuilder(sgx_key_request_t); @@ -186,7 +187,7 @@ impl KeyRequestBuilder { } } -#[derive(Default, Debug, Clone, Hash, PartialEq, Eq)] +#[derive(Default, Debug, Clone, Hash, PartialEq, Eq, ConstantTimeEq)] pub struct Key128bit(sgx_key_128bit_t); impl_newtype! { @@ -200,6 +201,7 @@ mod test { use super::*; use mc_sgx_core_sys_types::SGX_CPUSVN_SIZE; use rand::{rngs::StdRng, SeedableRng}; + use subtle::ConstantTimeEq; #[test] fn new_key_request_all_zero_except_key_id() { @@ -257,4 +259,26 @@ mod test { let sgx_key: sgx_key_128bit_t = key.into(); assert_eq!(sgx_key, [0u8; 16]); } + + #[test] + fn ct_eq_key_id() { + let first_sgx_key = sgx_key_id_t { id: [1u8; 32] }; + let second_sgx_key = sgx_key_id_t { id: [1u8; 32] }; + let first: KeyId = first_sgx_key.into(); + let second: KeyId = second_sgx_key.into(); + + let result_choice = first.ct_eq(&second); + let result: bool = From::from(result_choice); + assert!(result); + } + + #[test] + fn ct_eq_key_name() { + let first = KeyName::EnclaveInitializationToken; + let second = KeyName::EnclaveInitializationToken; + + let result_choice = first.ct_eq(&second); + let result: bool = From::from(result_choice); + assert!(result); + } } diff --git a/core/types/src/measurement.rs b/core/types/src/measurement.rs index 94b5ed9c..b96a56b4 100644 --- a/core/types/src/measurement.rs +++ b/core/types/src/measurement.rs @@ -5,6 +5,7 @@ //! Different types are used for MrSigner and MrEnclave to prevent misuse. use crate::impl_newtype_for_bytestruct; +use constant_time_derive::ConstantTimeEq; use mc_sgx_core_sys_types::{sgx_measurement_t, SGX_HASH_SIZE}; /// An opaque type for MRENCLAVE values @@ -12,7 +13,8 @@ use mc_sgx_core_sys_types::{sgx_measurement_t, SGX_HASH_SIZE}; /// A MRENCLAVE value is a chained cryptographic hash of the signed /// enclave binary (.so), and the results of the page initialization /// steps which created the enclave's pages. -#[derive(Default, Debug, Clone, Eq, PartialEq)] + +#[derive(Default, Debug, Clone, Eq, PartialEq, ConstantTimeEq)] #[repr(transparent)] pub struct MrEnclave(sgx_measurement_t); @@ -20,7 +22,7 @@ pub struct MrEnclave(sgx_measurement_t); /// /// A MRSIGNER value is a cryptographic hash of the public key an enclave /// was signed with. -#[derive(Default, Debug, Clone, Eq, PartialEq)] +#[derive(Default, Debug, Clone, Eq, PartialEq, ConstantTimeEq)] #[repr(transparent)] pub struct MrSigner(sgx_measurement_t); @@ -32,6 +34,32 @@ impl_newtype_for_bytestruct! { #[cfg(test)] mod test { use super::*; + use subtle::ConstantTimeEq; + + #[test] + fn ct_eq_sgx_mr_enclave() { + let sgx_mr_enclave = sgx_measurement_t { m: [5u8; 32] }; + let other_mr_enclave = sgx_measurement_t { m: [5u8; 32] }; + let first_mr_enclave: MrEnclave = sgx_mr_enclave.into(); + let second_mr_enclave: MrEnclave = other_mr_enclave.into(); + + let choice_result = first_mr_enclave.ct_eq(&second_mr_enclave); + let result: bool = From::from(choice_result); + assert!(result); + } + + #[test] + fn ct_eq_check_sgx_mr_sign() { + let sgx_mr_signer = sgx_measurement_t { m: [9u8; 32] }; + let other_mr_signer = sgx_measurement_t { m: [9u8; 32] }; + + let first_mr_signer: MrSigner = sgx_mr_signer.into(); + let second_mr_sign: MrSigner = other_mr_signer.into(); + + let choice_result = first_mr_signer.ct_eq(&second_mr_sign); + let result: bool = From::from(choice_result); + assert!(result); + } #[test] fn from_sgx_mr_enclave() { diff --git a/core/types/src/quote.rs b/core/types/src/quote.rs index b7134f2f..de65390a 100644 --- a/core/types/src/quote.rs +++ b/core/types/src/quote.rs @@ -5,13 +5,14 @@ use crate::{ attestation_key::QuoteSignatureKind, impl_newtype, impl_newtype_for_bytestruct, report::Report, FfiError, IsvSvn, ReportBody, TargetInfo, }; +use constant_time_derive::ConstantTimeEq; use mc_sgx_core_sys_types::{ sgx_basename_t, sgx_epid_group_id_t, sgx_platform_info_t, sgx_qe_report_info_t, sgx_quote_nonce_t, sgx_quote_sign_type_t, sgx_update_info_bit_t, SGX_PLATFORM_INFO_SIZE, }; /// Quoting Enclave Report Info -#[derive(Default, Debug, Clone, Hash, PartialEq, Eq)] +#[derive(Default, Debug, Clone, Hash, PartialEq, Eq, ConstantTimeEq)] #[repr(transparent)] pub struct QuotingEnclaveReportInfo(sgx_qe_report_info_t); @@ -37,7 +38,7 @@ impl_newtype! { } /// Platform Info -#[derive(Default, Debug, Clone, Hash, PartialEq, Eq)] +#[derive(Default, Debug, Clone, Hash, PartialEq, Eq, ConstantTimeEq)] #[repr(transparent)] pub struct PlatformInfo(sgx_platform_info_t); @@ -46,7 +47,7 @@ impl_newtype_for_bytestruct! { } /// Update Info Bit -#[derive(Default, Debug, Clone, Hash, PartialEq, Eq)] +#[derive(Default, Debug, Clone, Hash, PartialEq, Eq, ConstantTimeEq)] #[repr(transparent)] pub struct UpdateInfoBit(sgx_update_info_bit_t); @@ -72,7 +73,7 @@ impl UpdateInfoBit { } /// EPID Group ID -#[derive(Default, Debug, Clone, Hash, PartialEq, Eq)] +#[derive(Default, Debug, Clone, Hash, PartialEq, Eq, ConstantTimeEq)] #[repr(transparent)] pub struct EpidGroupId(sgx_epid_group_id_t); @@ -83,7 +84,7 @@ impl_newtype! { const BASENAME_SIZE: usize = 32; /// Basename -#[derive(Default, Debug, Clone, Hash, PartialEq, Eq)] +#[derive(Default, Debug, Clone, Hash, PartialEq, Eq, ConstantTimeEq)] #[repr(transparent)] pub struct Basename(sgx_basename_t); @@ -94,7 +95,7 @@ impl_newtype_for_bytestruct! { const NONCE_SIZE: usize = 16; /// Quote Nonce -#[derive(Default, Debug, Clone, Hash, PartialEq, Eq)] +#[derive(Default, Debug, Clone, Hash, PartialEq, Eq, ConstantTimeEq)] #[repr(transparent)] pub struct QuoteNonce(sgx_quote_nonce_t); @@ -105,12 +106,12 @@ impl_newtype_for_bytestruct! { /// The raw bytes representing a quote. /// /// Should not be used directly instead use [`Quote`]. -#[derive(Debug, Clone, Hash, PartialEq, Eq)] +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, ConstantTimeEq)] pub struct RawQuote<'a> { bytes: &'a [u8], } -#[derive(Default, Debug, Clone, Hash, PartialEq, Eq)] +#[derive(Default, Debug, Clone, Hash, PartialEq, Eq, ConstantTimeEq)] #[repr(transparent)] pub struct Version(u16); @@ -192,7 +193,7 @@ impl<'a> From<&'a [u8]> for RawQuote<'a> { } /// Quote -#[derive(Debug, Clone, Hash, PartialEq, Eq)] +#[derive(Debug, Clone, Hash, PartialEq, Eq, ConstantTimeEq)] pub struct Quote<'a>(RawQuote<'a>); impl BaseQuote for Quote<'_> { @@ -219,6 +220,7 @@ mod test { use crate::{report::Report, TargetInfo}; use core::{mem, slice}; use mc_sgx_core_sys_types::{sgx_quote_t, sgx_report_body_t, sgx_report_t, sgx_target_info_t}; + use subtle::ConstantTimeEq; #[allow(unsafe_code)] fn quote_to_bytes(report: sgx_quote_t) -> [u8; mem::size_of::()] { @@ -394,4 +396,123 @@ mod test { report.body.misc_select = 3; assert_eq!(info.report(), Report::from(report)); } + + #[test] + fn ct_eq_quoting_enclave_report_info() { + let nonce = sgx_quote_nonce_t { rand: [1u8; 16] }; + let app_enclave_target_info = sgx_target_info_t { + mr_enclave: Default::default(), + attributes: Default::default(), + reserved1: [1u8; 2], + config_svn: 0, + misc_select: 0, + reserved2: [2u8; 8], + config_id: [1u8; 64], + reserved3: [3u8; 384], + }; + let qe_report = sgx_report_t { + body: Default::default(), + key_id: Default::default(), + mac: [2u8; 16], + }; + let first_report_info = sgx_qe_report_info_t { + nonce, + app_enclave_target_info, + qe_report, + }; + let second_report_info = sgx_qe_report_info_t { + nonce, + app_enclave_target_info, + qe_report, + }; + + let first_report_info: QuotingEnclaveReportInfo = first_report_info.into(); + let second_report_info: QuotingEnclaveReportInfo = second_report_info.into(); + + let choice_result = first_report_info.ct_eq(&second_report_info); + let result: bool = From::from(choice_result); + assert!(result); + } + + #[test] + fn ct_eq_platform_info() { + let info = sgx_platform_info_t { + platform_info: [1u8; 101], + }; + let other_info = sgx_platform_info_t { + platform_info: [1u8; 101], + }; + let first_info: PlatformInfo = info.into(); + let second_info: PlatformInfo = other_info.into(); + + let choice_result = first_info.ct_eq(&second_info); + let result: bool = From::from(choice_result); + + assert!(result); + } + + #[test] + fn ct_eq_update_info() { + let first_info: UpdateInfoBit = UpdateInfoBit::default(); + let second_info: UpdateInfoBit = UpdateInfoBit::default(); + + let choice_result = first_info.ct_eq(&second_info); + let result: bool = From::from(choice_result); + assert!(result); + } + + #[test] + fn ct_eq_epid_groud_id() { + let first_info = EpidGroupId::from([16u8, 0u8, 0u8, 0u8]); + let second_info = EpidGroupId::from([16u8, 0u8, 0u8, 0u8]); + + let choice_result = first_info.ct_eq(&second_info); + let result: bool = From::from(choice_result); + + assert!(result); + } + + #[test] + fn ct_eq_base_name() { + let first_info = Basename::from([17u8; 32]); + let second_info = Basename::from([17u8; 32]); + + let choice_result = first_info.ct_eq(&second_info); + let result: bool = From::from(choice_result); + + assert!(result); + } + + #[test] + fn ct_eq_quote_nonce() { + let first_info = QuoteNonce::from([1u8; 16]); + let second_info = QuoteNonce::from([1u8; 16]); + + let choice_result = first_info.ct_eq(&second_info); + let result: bool = From::from(choice_result); + + assert!(result); + } + + #[test] + fn ct_eq_version() { + let first_info = Version(2); + let second_info = Version(2); + + let choice_result = first_info.ct_eq(&second_info); + let result: bool = From::from(choice_result); + + assert!(result); + } + + #[test] + fn ct_eq_quote() { + let first_quote: Quote = [4u8; 6].as_slice().into(); + let second_quote: Quote = [4u8; 6].as_slice().into(); + + let choice_result = first_quote.ct_eq(&second_quote); + let result: bool = From::from(choice_result); + + assert!(result); + } } diff --git a/core/types/src/report.rs b/core/types/src/report.rs index e905b336..186c35da 100644 --- a/core/types/src/report.rs +++ b/core/types/src/report.rs @@ -5,6 +5,7 @@ use crate::{ config_id::ConfigId, impl_newtype, impl_newtype_for_bytestruct, key_request::KeyId, Attributes, ConfigSvn, CpuSvn, FfiError, IsvSvn, MiscellaneousSelect, MrEnclave, MrSigner, }; +use constant_time_derive::ConstantTimeEq; use core::ops::BitAnd; use mc_sgx_core_sys_types::{ sgx_isvext_prod_id_t, sgx_isvfamily_id_t, sgx_mac_t, sgx_prod_id_t, sgx_report_body_t, @@ -17,7 +18,7 @@ use nom::bytes::complete::take; use nom::number::complete::{le_u16, le_u32, le_u64}; /// MAC -#[derive(Default, Debug, Clone, Hash, PartialEq, Eq)] +#[derive(Default, Debug, Clone, Hash, PartialEq, Eq, ConstantTimeEq)] #[repr(transparent)] pub struct Mac(sgx_mac_t); @@ -26,7 +27,7 @@ impl_newtype! { } /// Report Data -#[derive(Default, Debug, Clone, Hash, PartialEq, Eq)] +#[derive(Default, Debug, Clone, Hash, PartialEq, Eq, ConstantTimeEq)] #[repr(transparent)] pub struct ReportData(sgx_report_data_t); @@ -59,7 +60,7 @@ impl BitAnd for ReportData { } /// ISV Family ID -#[derive(Debug, Clone, Hash, PartialEq, Eq, Default)] +#[derive(Debug, Clone, Hash, PartialEq, Eq, Default, ConstantTimeEq)] #[repr(transparent)] pub struct FamilyId(sgx_isvfamily_id_t); @@ -68,7 +69,7 @@ impl_newtype! { } /// Extended Product ID -#[derive(Debug, Clone, Hash, PartialEq, Eq, Default)] +#[derive(Debug, Clone, Hash, PartialEq, Eq, Default, ConstantTimeEq)] #[repr(transparent)] pub struct ExtendedProductId(sgx_isvext_prod_id_t); @@ -77,7 +78,7 @@ impl_newtype! { } /// ISV Product ID -#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Default)] +#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Default, ConstantTimeEq)] #[repr(transparent)] pub struct IsvProductId(sgx_prod_id_t); @@ -87,7 +88,7 @@ impl_newtype! { /// The main body of a report from SGX #[repr(transparent)] -#[derive(Debug, Clone, Hash, PartialEq, Eq, Default)] +#[derive(Debug, Clone, Hash, PartialEq, Eq, Default, ConstantTimeEq)] pub struct ReportBody(sgx_report_body_t); impl ReportBody { @@ -222,7 +223,7 @@ impl TryFrom<&[u8]> for ReportBody { /// An enclave Report #[repr(transparent)] -#[derive(Default, Debug, Clone, Hash, PartialEq, Eq)] +#[derive(Default, Debug, Clone, Hash, PartialEq, Eq, ConstantTimeEq)] pub struct Report(sgx_report_t); impl Report { @@ -254,6 +255,7 @@ mod test { use crate::{key_request::KeyId, MrEnclave, MrSigner}; use core::{mem, slice}; use mc_sgx_core_sys_types::{SGX_KEYID_SIZE, SGX_MAC_SIZE}; + use subtle::ConstantTimeEq; use yare::parameterized; fn report_body_1() -> sgx_report_body_t { @@ -508,4 +510,86 @@ mod test { ReportData::from(expected) ); } + + #[test] + fn ct_eq_quote() { + let first = Mac([5u8; 16]); + let second = Mac([5u8; 16]); + + let choice_result = first.ct_eq(&second); + let result: bool = From::from(choice_result); + + assert!(result); + } + + #[test] + fn ct_eq_platform_info() { + let report_data = sgx_report_data_t { d: [1u8; 64] }; + let other_report_data = sgx_report_data_t { d: [1u8; 64] }; + let first_info: ReportData = report_data.into(); + let second_info: ReportData = other_report_data.into(); + + let choice_result = first_info.ct_eq(&second_info); + let result: bool = From::from(choice_result); + + assert!(result); + } + + #[test] + fn ct_eq_family_id() { + let first = FamilyId::from([8u8; 16]); + let second = FamilyId::from([8u8; 16]); + + let choice_result = first.ct_eq(&second); + let result: bool = From::from(choice_result); + + assert!(result); + } + + #[test] + fn ct_eq_extended_product_id() { + let first = ExtendedProductId::from([6u8; 16]); + let second = ExtendedProductId::from([6u8; 16]); + + let choice_result = first.ct_eq(&second); + let result: bool = From::from(choice_result); + + assert!(result); + } + + #[test] + fn ct_eq_isv_product_id() { + let first = IsvProductId(12); + let second = IsvProductId(12); + + let choice_result = first.ct_eq(&second); + let result: bool = From::from(choice_result); + + assert!(result); + } + + #[test] + fn ct_eq_report_body() { + let bytes = report_body_to_bytes(report_body_2()); + let other_bytes = report_body_to_bytes(report_body_2()); + + let first = ReportBody::try_from(bytes.as_slice()).unwrap(); + let second = ReportBody::try_from(other_bytes.as_slice()).unwrap(); + + let choice_result = first.ct_eq(&second); + let result: bool = From::from(choice_result); + + assert!(result); + } + + #[test] + fn ct_eq_report() { + let first = Report::default(); + let second = Report::default(); + + let choice_result = first.ct_eq(&second); + let result: bool = From::from(choice_result); + + assert!(result); + } } diff --git a/core/types/src/svn.rs b/core/types/src/svn.rs index 6fa61869..5b30b1b5 100644 --- a/core/types/src/svn.rs +++ b/core/types/src/svn.rs @@ -2,31 +2,167 @@ //! SGX core SVN (Security Version Numbers) use crate::{impl_newtype, impl_newtype_for_bytestruct}; +use constant_time_derive::ConstantTimeEq; use mc_sgx_core_sys_types::{sgx_config_svn_t, sgx_cpu_svn_t, sgx_isv_svn_t, SGX_CPUSVN_SIZE}; +use subtle::{Choice, ConstantTimeGreater, ConstantTimeLess}; /// Config security version number (SVN) #[repr(transparent)] -#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Default)] +#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Default, ConstantTimeEq)] pub struct ConfigSvn(sgx_config_svn_t); impl_newtype! { ConfigSvn, sgx_config_svn_t; } +impl ConstantTimeGreater for ConfigSvn { + fn ct_gt(&self, other: &Self) -> Choice { + self.0.ct_gt(&other.0) + } +} + +impl ConstantTimeLess for ConfigSvn {} + /// Independent software vendor (ISV) security version number (SVN) #[repr(transparent)] -#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Default)] +#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Default, ConstantTimeEq)] pub struct IsvSvn(sgx_isv_svn_t); impl_newtype! { IsvSvn, sgx_isv_svn_t; } +impl ConstantTimeGreater for IsvSvn { + fn ct_gt(&self, other: &Self) -> Choice { + self.0.ct_gt(&other.0) + } +} + +impl ConstantTimeLess for IsvSvn {} + /// CPU security version number (SVN) #[repr(transparent)] -#[derive(Default, Debug, Clone, Hash, PartialEq, Eq)] +#[derive(Default, Debug, Clone, Hash, PartialEq, Eq, ConstantTimeEq)] pub struct CpuSvn(sgx_cpu_svn_t); impl_newtype_for_bytestruct! { CpuSvn, sgx_cpu_svn_t, SGX_CPUSVN_SIZE, svn; } + +impl ConstantTimeGreater for CpuSvn { + fn ct_gt(&self, other: &Self) -> Choice { + if !self.0.svn.len().eq(&other.0.svn.len()) { + return Choice::from(0); + } + let svn_self = u128::from_le_bytes(self.0.svn); + let svn_other = u128::from_le_bytes(other.0.svn); + svn_self.ct_gt(&svn_other) + } +} + +impl ConstantTimeLess for CpuSvn {} + +#[cfg(test)] +mod test { + use super::*; + use subtle::ConstantTimeEq; + + #[test] + fn ct_eq_config_svn() { + let first_info = ConfigSvn(1); + let second_info = ConfigSvn(1); + + let choice_result = first_info.ct_eq(&second_info); + let result: bool = From::from(choice_result); + + assert!(result); + } + + #[test] + fn ct_gt_config_svn() { + let first_info = ConfigSvn(2); + let second_info = ConfigSvn(1); + + let choice_result = first_info.ct_gt(&second_info); + let result: bool = From::from(choice_result); + + assert!(result); + } + + #[test] + fn ct_lt_config_svn() { + let first_info = ConfigSvn(1); + let second_info = ConfigSvn(4); + + let choice_result = first_info.ct_lt(&second_info); + let result: bool = From::from(choice_result); + + assert!(result); + } + + #[test] + fn ct_eq_isv_svn() { + let first_info = IsvSvn(1); + let second_info = IsvSvn(1); + + let choice_result = first_info.ct_eq(&second_info); + let result: bool = From::from(choice_result); + + assert!(result); + } + + #[test] + fn ct_gt_isv_svn() { + let first_info = IsvSvn(3); + let second_info = IsvSvn(1); + + let choice_result = first_info.ct_gt(&second_info); + let result: bool = From::from(choice_result); + + assert!(result); + } + + #[test] + fn ct_lt_isv_svn() { + let first_info = IsvSvn(1); + let second_info = IsvSvn(4); + + let choice_result = first_info.ct_lt(&second_info); + let result: bool = From::from(choice_result); + + assert!(result); + } + + #[test] + fn ct_eq_cpu_svn() { + let first_info = CpuSvn(sgx_cpu_svn_t { svn: [1u8; 16] }); + let second_info = CpuSvn(sgx_cpu_svn_t { svn: [1u8; 16] }); + + let choice_result = first_info.ct_eq(&second_info); + let result: bool = From::from(choice_result); + + assert!(result); + } + + #[test] + fn ct_gt_cpu_svn() { + let first_info = CpuSvn(sgx_cpu_svn_t { svn: [2u8; 16] }); + let second_info = CpuSvn(sgx_cpu_svn_t { svn: [1u8; 16] }); + + let choice_result = first_info.ct_gt(&second_info); + let result: bool = From::from(choice_result); + + assert!(result); + } + + #[test] + fn ct_lt_cpu_svn() { + let first_info = CpuSvn(sgx_cpu_svn_t { svn: [1u8; 16] }); + let second_info = CpuSvn(sgx_cpu_svn_t { svn: [4u8; 16] }); + + let choice_result = first_info.ct_lt(&second_info); + let result: bool = From::from(choice_result); + + assert!(result); + } +} diff --git a/core/types/src/target_info.rs b/core/types/src/target_info.rs index 2e4fe20e..033a111f 100644 --- a/core/types/src/target_info.rs +++ b/core/types/src/target_info.rs @@ -4,11 +4,12 @@ use crate::{ config_id::ConfigId, impl_newtype, Attributes, ConfigSvn, MiscellaneousSelect, MrEnclave, }; +use constant_time_derive::ConstantTimeEq; use mc_sgx_core_sys_types::sgx_target_info_t; /// The target info #[repr(transparent)] -#[derive(Default, Debug, Clone, Hash, PartialEq, Eq)] +#[derive(Default, Debug, Clone, Hash, PartialEq, Eq, ConstantTimeEq)] pub struct TargetInfo(sgx_target_info_t); impl TargetInfo { @@ -49,6 +50,7 @@ mod test { SGX_CONFIGID_SIZE, SGX_HASH_SIZE, SGX_TARGET_INFO_RESERVED1_BYTES, SGX_TARGET_INFO_RESERVED2_BYTES, SGX_TARGET_INFO_RESERVED3_BYTES, }; + use subtle::ConstantTimeEq; #[test] fn default_target_info() { @@ -89,4 +91,42 @@ mod test { assert_eq!(info.miscellaneous_select(), MiscellaneousSelect::from(6)); assert_eq!(info.config_id(), ConfigId::from([8; SGX_CONFIGID_SIZE])); } + + #[test] + fn ct_eq_target_info_t() { + let first_info = sgx_target_info_t { + mr_enclave: MrEnclave::from([2u8; MrEnclave::SIZE]).into(), + attributes: Attributes::default() + .set_flags(2) + .set_extended_features_mask(3) + .into(), + reserved1: [4u8; SGX_TARGET_INFO_RESERVED1_BYTES], + config_svn: 5, + misc_select: 6, + reserved2: [7u8; SGX_TARGET_INFO_RESERVED2_BYTES], + config_id: [8u8; SGX_CONFIGID_SIZE], + reserved3: [9u8; SGX_TARGET_INFO_RESERVED3_BYTES], + }; + let second_info = sgx_target_info_t { + mr_enclave: MrEnclave::from([2u8; MrEnclave::SIZE]).into(), + attributes: Attributes::default() + .set_flags(2) + .set_extended_features_mask(3) + .into(), + reserved1: [4u8; SGX_TARGET_INFO_RESERVED1_BYTES], + config_svn: 5, + misc_select: 6, + reserved2: [7u8; SGX_TARGET_INFO_RESERVED2_BYTES], + config_id: [8u8; SGX_CONFIGID_SIZE], + reserved3: [9u8; SGX_TARGET_INFO_RESERVED3_BYTES], + }; + + let first: TargetInfo = first_info.into(); + let second: TargetInfo = second_info.into(); + + let choice_result = first.ct_eq(&second); + let result: bool = From::from(choice_result); + + assert!(result); + } } diff --git a/dcap/ql/types/Cargo.toml b/dcap/ql/types/Cargo.toml index 2e7dc779..aacab952 100644 --- a/dcap/ql/types/Cargo.toml +++ b/dcap/ql/types/Cargo.toml @@ -12,8 +12,10 @@ repository = "https://github.com/mobilecoinfoundation/sgx" rust-version = "1.62.1" [dependencies] +constant_time_derive = { path = "../../../constant_time_derive", version = "0.1.0" } mc-sgx-core-types = { path = "../../../core/types", version = "=0.5.1-beta.0" } mc-sgx-dcap-ql-sys-types = { path = "../sys/types", version = "=0.5.1-beta.0" } +subtle = { version = "2.4.0", default-features = false } [dev-dependencies] yare = "1.0.2" diff --git a/dcap/ql/types/src/lib.rs b/dcap/ql/types/src/lib.rs index 40f3cc38..b87fed3a 100644 --- a/dcap/ql/types/src/lib.rs +++ b/dcap/ql/types/src/lib.rs @@ -4,12 +4,13 @@ #![no_std] #![deny(missing_docs, missing_debug_implementations, unsafe_code)] +use constant_time_derive::ConstantTimeEq; use mc_sgx_core_types::FfiError; use mc_sgx_dcap_ql_sys_types::sgx_ql_path_type_t; /// Paths (location and filename) to be override the default entries. #[non_exhaustive] -#[derive(Eq, PartialEq, Debug)] +#[derive(Eq, PartialEq, Debug, ConstantTimeEq)] pub enum PathKind { /// Quoting Enclave (QE3) QuotingEnclave, @@ -51,6 +52,7 @@ mod test { use yare::parameterized; extern crate std; use super::*; + use subtle::ConstantTimeEq; #[parameterized( qe3 = { sgx_ql_path_type_t::SGX_QL_QE3_PATH, PathKind::QuotingEnclave }, @@ -78,4 +80,15 @@ mod test { let result = PathKind::try_from(sgx_ql_path_type_t(4)); assert!(result.is_err()); } + + #[test] + fn ct_eq_path_kind() { + let first = PathKind::try_from(sgx_ql_path_type_t(1)); + let second = PathKind::try_from(sgx_ql_path_type_t(1)); + + let choice_result = first.unwrap().ct_eq(&second.unwrap()); + let result: bool = From::from(choice_result); + + assert!(result); + } } diff --git a/dcap/quoteverify/types/Cargo.toml b/dcap/quoteverify/types/Cargo.toml index c02e3f3b..fab5de6d 100644 --- a/dcap/quoteverify/types/Cargo.toml +++ b/dcap/quoteverify/types/Cargo.toml @@ -12,8 +12,10 @@ repository = "https://github.com/mobilecoinfoundation/sgx" rust-version = "1.62.1" [dependencies] +constant_time_derive = { path = "../../../constant_time_derive", version = "0.1.0" } mc-sgx-core-types = { path = "../../../core/types", version = "=0.5.1-beta.0" } mc-sgx-dcap-quoteverify-sys-types = { path = "../sys/types", version = "=0.5.1-beta.0" } +subtle = { version = "2.4.0", default-features = false } [dev-dependencies] yare = "1.0.2" diff --git a/dcap/quoteverify/types/src/lib.rs b/dcap/quoteverify/types/src/lib.rs index b1b2b65c..ea333e01 100644 --- a/dcap/quoteverify/types/src/lib.rs +++ b/dcap/quoteverify/types/src/lib.rs @@ -3,11 +3,12 @@ #![doc = include_str!("../README.md")] #![no_std] +use constant_time_derive::ConstantTimeEq; use mc_sgx_core_types::FfiError; use mc_sgx_dcap_quoteverify_sys_types::sgx_qv_path_type_t; #[non_exhaustive] -#[derive(Eq, PartialEq, Debug)] +#[derive(Eq, PartialEq, Debug, ConstantTimeEq)] pub enum PathKind { QuoteVerificationEnclave, QuoteProviderLibrary, @@ -36,6 +37,7 @@ impl From for sgx_qv_path_type_t { #[cfg(test)] mod test { + use subtle::ConstantTimeEq; use yare::parameterized; extern crate std; use super::*; @@ -62,4 +64,15 @@ mod test { let result = PathKind::try_from(sgx_qv_path_type_t(2)); assert!(result.is_err()); } + + #[test] + fn ct_eq_path_kind() { + let first = PathKind::try_from(sgx_qv_path_type_t(1)); + let second = PathKind::try_from(sgx_qv_path_type_t(1)); + + let choice_result = first.unwrap().ct_eq(&second.unwrap()); + let result: bool = From::from(choice_result); + + assert!(result); + } } diff --git a/dcap/sys/types/Cargo.toml b/dcap/sys/types/Cargo.toml index be8de5a3..8baee10d 100644 --- a/dcap/sys/types/Cargo.toml +++ b/dcap/sys/types/Cargo.toml @@ -15,7 +15,9 @@ rust-version = "1.62.1" doctest = false [dependencies] +constant_time_derive = { path = "../../../constant_time_derive", version = "0.1.0" } mc-sgx-core-sys-types = { path = "../../../core/sys/types", version = "=0.5.1-beta.0" } +subtle = { version = "2.4.0", default-features = false } [build-dependencies] bindgen = "0.64.0" diff --git a/dcap/sys/types/build.rs b/dcap/sys/types/build.rs index f0056ed3..17d9daf0 100644 --- a/dcap/sys/types/build.rs +++ b/dcap/sys/types/build.rs @@ -71,7 +71,8 @@ fn main() { "sgx_ql_att_key_id_param_t", "sgx_ql_att_id_list_t", ]) - .derive_default(["sgx_ql_qe_report_info_t"]); + .derive_default(["sgx_ql_qe_report_info_t"]) + .derive_constant_time(["sgx_ql_qe_report_info_t"]); let mut builder = mc_sgx_core_build::sgx_builder() .header("wrapper.h") .parse_callbacks(Box::new(callback)) diff --git a/dcap/types/Cargo.toml b/dcap/types/Cargo.toml index 782747c4..e851cfd8 100644 --- a/dcap/types/Cargo.toml +++ b/dcap/types/Cargo.toml @@ -17,6 +17,7 @@ serde = ["dep:serde"] alloc = [] [dependencies] +constant_time_derive = { path = "../../constant_time_derive", version = "0.1.0" } displaydoc = { version = "0.2.3", default-features = false } mc-sgx-core-types = { path = "../../core/types", version = "=0.5.1-beta.0" } mc-sgx-dcap-sys-types = { path = "../sys/types", version = "=0.5.1-beta.0" } diff --git a/dcap/types/src/certification_data.rs b/dcap/types/src/certification_data.rs index b3af4e5d..ebbf045d 100644 --- a/dcap/types/src/certification_data.rs +++ b/dcap/types/src/certification_data.rs @@ -4,6 +4,7 @@ use crate::quote3::{le_u16, le_u32}; use crate::Quote3Error; +use constant_time_derive::ConstantTimeEq; /// The minimum size of a byte array to contain a [`CertificationData`] /// The 2(type) + 4(size) for QE certification data @@ -118,7 +119,7 @@ trait CertificationDataKind { const KIND: u16; } -#[derive(Debug, Clone, Hash, PartialEq, Eq)] +#[derive(Debug, Clone, Hash, PartialEq, Eq, ConstantTimeEq)] /// Contains the following data: /// - Platform provisioning ID (PPID) /// - CPU security version number (CPUSVN) @@ -130,7 +131,7 @@ impl<'a> CertificationDataKind for Ppid<'a> { const KIND: u16 = 1; } -#[derive(Debug, Clone, Hash, PartialEq, Eq)] +#[derive(Debug, Clone, Hash, PartialEq, Eq, ConstantTimeEq)] /// Contains the following data encrypted with RSA 2048: /// - Platform provisioning ID (PPID) /// - CPU security version number (CPUSVN) @@ -142,7 +143,7 @@ impl<'a> CertificationDataKind for PpidEncryptedRsa2048<'a> { const KIND: u16 = 2; } -#[derive(Debug, Clone, Hash, PartialEq, Eq)] +#[derive(Debug, Clone, Hash, PartialEq, Eq, ConstantTimeEq)] /// Contains the following data encrypted with RSA 3072: /// - Platform provisioning ID (PPID) /// - CPU security version number (CPUSVN) @@ -154,7 +155,7 @@ impl<'a> CertificationDataKind for PpidEncryptedRsa3072<'a> { const KIND: u16 = 3; } -#[derive(Debug, Clone, Hash, PartialEq, Eq)] +#[derive(Debug, Clone, Hash, PartialEq, Eq, ConstantTimeEq)] /// Contains the provisioning certification key (PCK) leaf certificate pub struct Pck<'a>(&'a [u8]); @@ -162,7 +163,7 @@ impl<'a> CertificationDataKind for Pck<'a> { const KIND: u16 = 4; } -#[derive(Debug, Clone, Hash, PartialEq, Eq)] +#[derive(Debug, Clone, Hash, PartialEq, Eq, ConstantTimeEq)] /// Contains the certificate chain for the provisioning certification key /// (PCK). pub struct PckCertificateChain<'a> { @@ -184,7 +185,7 @@ impl<'a> IntoIterator for &'a PckCertificateChain<'a> { } } -#[derive(Debug, Clone, Hash, PartialEq, Eq)] +#[derive(Debug, Clone, Hash, PartialEq, Eq, ConstantTimeEq)] /// ECDSA signature auxiliary data of an Intel SGX quote /// See `sgx_ql_cert_key_type_t::ECDSA_SIG_AUX_DATA` pub struct EcdsaSignatureAuxData<'a>(&'a [u8]); @@ -193,7 +194,7 @@ impl<'a> CertificationDataKind for EcdsaSignatureAuxData<'a> { const KIND: u16 = 6; } -#[derive(Debug, Clone, Hash, PartialEq, Eq)] +#[derive(Debug, Clone, Hash, PartialEq, Eq, ConstantTimeEq)] /// Platform manifest pub struct PlatformManifest<'a>(&'a [u8]); @@ -204,7 +205,7 @@ impl<'a> CertificationDataKind for PlatformManifest<'a> { const BEGIN_PEM: &[u8] = b"-----BEGIN "; const END_PEM: &[u8] = b"-----END "; -#[derive(Debug)] +#[derive(Debug, ConstantTimeEq)] pub struct PemIterator<'a> { pem_data: &'a [u8], } diff --git a/dcap/types/src/quote3.rs b/dcap/types/src/quote3.rs index 48471a1f..4ab4361b 100644 --- a/dcap/types/src/quote3.rs +++ b/dcap/types/src/quote3.rs @@ -6,6 +6,7 @@ use crate::certification_data::{CertificationData, MIN_CERT_DATA_SIZE}; use crate::Quote3Error; #[cfg(feature = "alloc")] use alloc::vec::Vec; +use constant_time_derive::ConstantTimeEq; use core::cmp::Ordering; use core::hash::{Hash, Hasher}; use core::mem; @@ -269,7 +270,7 @@ impl<'a> SignatureData<'a> { /// /// Table 8 of /// . -#[derive(Debug, Clone, Hash, PartialEq, Eq)] +#[derive(Debug, Clone, Hash, PartialEq, Eq, ConstantTimeEq)] struct AuthenticationData<'a> { // The `data` field as described in the QuoteLibReference. // The length of this *will* equal the `size` field as described in the diff --git a/dcap/types/src/quoting_enclave.rs b/dcap/types/src/quoting_enclave.rs index 97301c34..01672624 100644 --- a/dcap/types/src/quoting_enclave.rs +++ b/dcap/types/src/quoting_enclave.rs @@ -2,12 +2,13 @@ //! Types specific to the quoting enclave +use constant_time_derive::ConstantTimeEq; use mc_sgx_core_types::{impl_newtype, QuoteNonce, Report, TargetInfo}; use mc_sgx_dcap_sys_types::sgx_ql_qe_report_info_t; /// Report info for the Quoting Enclave #[repr(transparent)] -#[derive(Default, Debug, Clone, Hash, PartialEq, Eq)] +#[derive(Default, Debug, Clone, Hash, PartialEq, Eq, ConstantTimeEq)] pub struct ReportInfo(sgx_ql_qe_report_info_t); impl_newtype! { @@ -35,6 +36,7 @@ impl ReportInfo { mod test { use super::*; use mc_sgx_core_sys_types::{sgx_quote_nonce_t, sgx_report_t, sgx_target_info_t}; + use subtle::ConstantTimeEq; #[test] fn default_report_info() { @@ -64,4 +66,27 @@ mod test { assert_eq!(info.target_info(), target_info.into()); assert_eq!(info.nonce(), nonce.into()); } + + #[test] + fn ct_eq_report_info_t() { + let mut report = sgx_report_t::default(); + report.body.cpu_svn.svn[0] = 1; + let mut target_info = sgx_target_info_t::default(); + target_info.mr_enclave.m[0] = 2; + let mut nonce = sgx_quote_nonce_t::default(); + nonce.rand[0] = 3; + let info = sgx_ql_qe_report_info_t { + qe_report: report, + app_enclave_target_info: target_info, + nonce, + }; + + let first: ReportInfo = info.into(); + let second: ReportInfo = info.into(); + + let choice_result = first.ct_eq(&second); + let result: bool = From::from(choice_result); + + assert!(result); + } } diff --git a/tservice/types/Cargo.toml b/tservice/types/Cargo.toml index ed34b5c3..57b3e0a4 100644 --- a/tservice/types/Cargo.toml +++ b/tservice/types/Cargo.toml @@ -18,8 +18,10 @@ alloc = [] test-utils = [] [dependencies] +constant_time_derive = { path = "../../constant_time_derive", version = "0.1.0" } mc-sgx-core-types = { path = "../../core/types", version = "=0.5.1-beta.0" } mc-sgx-tservice-sys-types = { path = "../sys/types", version = "=0.5.1-beta.0" } +subtle = { version = "2.4.0", default-features = false } [dev-dependencies] yare = "1.0.2" diff --git a/tservice/types/src/seal.rs b/tservice/types/src/seal.rs index d4004f62..4e23b480 100644 --- a/tservice/types/src/seal.rs +++ b/tservice/types/src/seal.rs @@ -4,9 +4,11 @@ #[cfg(feature = "alloc")] use alloc::vec::Vec; +use constant_time_derive::ConstantTimeEq; use core::{mem, result::Result as CoreResult}; use mc_sgx_core_types::FfiError; use mc_sgx_tservice_sys_types::{sgx_aes_gcm_data_t, sgx_sealed_data_t}; +use subtle::{Choice, ConstantTimeEq}; pub type Result = CoreResult; @@ -14,7 +16,7 @@ pub type Result = CoreResult; /// /// Wraps up a `&[u8]` since [`mc-sgx-tservice-sys-types::sgx_aes_gcm_data_t`] /// is a dynamically sized type -#[derive(Debug, Clone, Hash, PartialEq, Eq)] +#[derive(Debug, Clone, Hash, PartialEq, Eq, ConstantTimeEq)] struct AesGcmData<'a> { bytes: &'a [u8], } @@ -61,6 +63,12 @@ pub struct Sealed { bytes: T, } +impl ConstantTimeEq for Sealed<&[u8]> { + fn ct_eq(&self, other: &Self) -> Choice { + self.as_ref().ct_eq(other.as_ref()) + } +} + // Unable to do // ```rust // impl TryFrom for Sealed; @@ -267,4 +275,29 @@ mod test { Err(FfiError::InvalidInputLength) ); } + + #[test] + fn ct_eq_aes_gcm_data() { + let bytes = aes_gcm_data_to_bytes(sgx_aes_gcm_data_t::default(), b"1234"); + let size = mem::size_of::() + b"1234".len(); + assert!(AesGcmData::try_from(&bytes[..size]).is_ok()); + let first_quote = AesGcmData::try_from(&bytes[..size]); + let second_quote = AesGcmData::try_from(&bytes[..size]); + + let choice_result = first_quote.unwrap().ct_eq(&second_quote.unwrap()); + let result: bool = From::from(choice_result); + + assert!(result); + } + + #[test] + fn ct_eq_sealed() { + let bytes = test_utils::sealed_data_to_bytes(sgx_sealed_data_t::default(), b"", None); + let size = mem::size_of::(); + let first = Sealed::try_from(&bytes[..size]); + let second = Sealed::try_from(&bytes[..size]); + let choice_result = first.unwrap().ct_eq(&second.unwrap()); + let result: bool = From::from(choice_result); + assert!(result); + } }