diff --git a/Cargo.lock b/Cargo.lock index 0e9e2382..1e00688e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -51,6 +51,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + [[package]] name = "android_system_properties" version = "0.1.5" @@ -457,18 +463,17 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.23" +version = "0.4.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" +checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" dependencies = [ + "android-tzdata", "iana-time-zone", "js-sys", - "num-integer", "num-traits", "serde", - "time 0.1.44", "wasm-bindgen", - "winapi", + "windows-targets 0.52.6", ] [[package]] @@ -2750,6 +2755,7 @@ dependencies = [ "anyhow", "b64-ct", "base16", + "chrono", "dcap-ql", "failure", "hex 0.4.3", @@ -2763,6 +2769,7 @@ dependencies = [ "serde_json", "sgx-isa 0.4.1", "sgx_pkix 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tempdir", "yasna 0.3.2", ] diff --git a/intel-sgx/pcs/Cargo.toml b/intel-sgx/pcs/Cargo.toml index a33045e2..efad498d 100644 --- a/intel-sgx/pcs/Cargo.toml +++ b/intel-sgx/pcs/Cargo.toml @@ -19,6 +19,7 @@ categories = ["os", "hardware-support"] [dependencies] +chrono = { version = "0.4.39", features = ["now"] } dcap-ql = { version = "0.4.0", path = "../dcap-ql", default-features = false } sgx-isa = { version = "0.4.1", path = "../sgx-isa", default-features = true } pkix = "0.2.0" @@ -38,6 +39,7 @@ mbedtls = { version = "0.12.3", features = ["std", "time"], default-features = f [dev-dependencies] hex = "0.4.2" +tempdir = "0.3.7" [features] verify = ["anyhow", "mbedtls", "mbedtls/x509"] diff --git a/intel-sgx/pcs/src/iso8601.rs b/intel-sgx/pcs/src/iso8601.rs new file mode 100644 index 00000000..1bbf5161 --- /dev/null +++ b/intel-sgx/pcs/src/iso8601.rs @@ -0,0 +1,19 @@ +use chrono::{DateTime, NaiveDateTime, Utc}; +use serde::{Deserialize, Deserializer}; +use serde::de; + +const ISO8601_FORMAT: &'static str = "%Y-%m-%dT%H:%M:%SZ"; + +pub fn serialize(timestamp: &DateTime, serializer: S) -> ::std::result::Result + where + S: ::serde::Serializer, +{ + let timestamp = timestamp.format(ISO8601_FORMAT).to_string(); + serializer.serialize_str(×tamp) +} + +pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result, D::Error> { + let timestamp = String::deserialize(deserializer)?; + let timestamp = NaiveDateTime::parse_from_str(×tamp, &ISO8601_FORMAT).map_err(de::Error::custom)?; + Ok(timestamp.and_utc()) +} diff --git a/intel-sgx/pcs/src/lib.rs b/intel-sgx/pcs/src/lib.rs index 87ee0275..ae33364f 100644 --- a/intel-sgx/pcs/src/lib.rs +++ b/intel-sgx/pcs/src/lib.rs @@ -32,6 +32,7 @@ pub use crate::qe_identity::{QeIdentity, QeIdentitySigned}; pub use crate::tcb_info::{Fmspc, TcbInfo, TcbData}; mod io; +mod iso8601; mod pckcrl; mod pckcrt; mod pckid; @@ -89,6 +90,9 @@ quick_error! { InvalidQe3Id(err: MbedError){ display("Invalid QE3 ID: {}", err) } + Qe3NotValid(err: String){ + display("Invalid QE3: {}", err) + } InvalidFormatQe3Identity{ display("Invalid QE3 Identity format") } @@ -123,12 +127,12 @@ quick_error! { pub trait VerificationType {} -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct Verified; impl VerificationType for Verified {} -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct Unverified; impl VerificationType for Unverified {} diff --git a/intel-sgx/pcs/src/qe_identity.rs b/intel-sgx/pcs/src/qe_identity.rs index 451374fb..e5fe0a6a 100644 --- a/intel-sgx/pcs/src/qe_identity.rs +++ b/intel-sgx/pcs/src/qe_identity.rs @@ -9,6 +9,7 @@ use std::convert::TryInto; use std::marker::PhantomData; use std::path::PathBuf; +use chrono::{DateTime, Utc}; use serde::{de, Deserialize, Deserializer, Serialize}; use serde_json::value::RawValue; use sgx_isa::{Attributes, Miscselect}; @@ -47,8 +48,10 @@ impl TcbLevel { pub struct QeIdentity { version: u16, id: String, - issue_date: String, - next_update: String, + #[serde(with = "crate::iso8601")] + issue_date: DateTime, + #[serde(with = "crate::iso8601")] + next_update: DateTime, tcb_evaluation_data_number: u64, #[serde(deserialize_with = "miscselect_deserializer", serialize_with = "miscselect_serializer")] miscselect: Miscselect, @@ -79,8 +82,10 @@ impl<'de> Deserialize<'de> for QeIdentity { struct Dummy { version: u16, id: String, - issue_date: String, - next_update: String, + #[serde(with = "crate::iso8601")] + issue_date: DateTime, + #[serde(with = "crate::iso8601")] + next_update: DateTime, tcb_evaluation_data_number: u64, #[serde(deserialize_with = "miscselect_deserializer", serialize_with = "miscselect_serializer")] miscselect: Miscselect, @@ -339,9 +344,20 @@ impl QeIdentitySigned { tcb_levels, type_: PhantomData, } = serde_json::from_str(&self.raw_enclave_identity).map_err(|e| Error::ParseError(e))?; + if version != 2 { return Err(Error::UnknownQeIdentityVersion(version)); } + + let now = Utc::now(); + if now < issue_date { + return Err(Error::Qe3NotValid(format!("QE3 only valid from {}", issue_date))) + } + + if next_update < now { + return Err(Error::Qe3NotValid(format!("QE3 expired on {}", next_update))) + } + Ok(QeIdentity:: { version, id, @@ -363,6 +379,7 @@ impl QeIdentitySigned { #[cfg(feature = "verify")] #[cfg(test)] mod tests { + use crate::Error; #[cfg(not(target_env = "sgx"))] use crate::qe_identity::QeIdentitySigned; @@ -373,7 +390,10 @@ mod tests { let root_cert = include_bytes!("../tests/data/root_SGX_CA_der.cert"); let root_certs = [&root_cert[..]]; - assert!(qe_id.verify(&root_certs).is_ok()); + match qe_id.verify(&root_certs) { + Err(Error::Qe3NotValid(msg)) => assert_eq!(msg, "QE3 expired on 2020-06-17 17:49:21 UTC"), + e => assert!(false, "wrong result: {:?}", e), + } } #[test] diff --git a/intel-sgx/pcs/src/tcb_info.rs b/intel-sgx/pcs/src/tcb_info.rs index 87a52e7c..4a383613 100644 --- a/intel-sgx/pcs/src/tcb_info.rs +++ b/intel-sgx/pcs/src/tcb_info.rs @@ -9,6 +9,7 @@ use std::convert::TryFrom; use std::marker::PhantomData; use std::path::PathBuf; +use chrono::{DateTime, Utc}; use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; use serde_json::value::RawValue; #[cfg(feature = "verify")] @@ -135,8 +136,8 @@ fn sgx_platform() -> Platform { pub struct TcbData { id: Platform, version: u16, - issue_date: String, - next_update: String, + issue_date: DateTime, + next_update: DateTime, fmspc: Fmspc, pce_id: String, tcb_type: u16, @@ -156,8 +157,10 @@ impl<'de> Deserialize<'de> for TcbData { #[serde(default = "sgx_platform")] id: Platform, version: u16, - issue_date: String, - next_update: String, + #[serde(with = "crate::iso8601")] + issue_date: DateTime, + #[serde(with = "crate::iso8601")] + next_update: DateTime, fmspc: Fmspc, pce_id: String, tcb_type: u16, @@ -346,6 +349,14 @@ impl TcbInfo { .. } = TcbData::parse(&self.raw_tcb_info)?; + let now = Utc::now(); + if now < issue_date { + return Err(Error::InvalidTcbInfo(format!("TCB Info only valid from {}", issue_date))) + } + if next_update < now { + return Err(Error::InvalidTcbInfo(format!("TCB Info expired on {}", next_update))) + } + Ok(TcbData:: { id, version, @@ -370,8 +381,10 @@ impl TcbInfo { mod tests { #[cfg(not(target_env = "sgx"))] use { + crate::Error, crate::tcb_info::{Fmspc, TcbInfo}, std::convert::TryFrom, + tempdir::TempDir, }; #[test] @@ -381,7 +394,17 @@ mod tests { TcbInfo::restore("./tests/data/", &Fmspc::try_from("00906ea10000").expect("static fmspc")).expect("validated"); let root_certificate = include_bytes!("../tests/data/root_SGX_CA_der.cert"); let root_certificates = [&root_certificate[..]]; - assert!(info.verify(&root_certificates).is_ok()); + match info.verify(&root_certificates) { + Err(Error::InvalidTcbInfo(msg)) => assert_eq!(msg, String::from("TCB Info expired on 2020-06-17 17:49:24 UTC")), + e => assert!(false, "wrong result: {:?}", e), + } + + // Test serialization/deserialization + let temp_dir = TempDir::new("tempdir").unwrap(); + let path = temp_dir.path().as_os_str().to_str().unwrap(); + info.store(&path).unwrap(); + let info2 = TcbInfo::restore(&path, &Fmspc::try_from("00906ea10000").expect("static fmspc")).unwrap(); + assert_eq!(info, info2); } #[test]