-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Richard Zak <[email protected]>
- Loading branch information
Showing
14 changed files
with
790 additions
and
55 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
// SPDX-FileCopyrightText: 2022 Profian Inc. <[email protected]> | ||
// SPDX-License-Identifier: AGPL-3.0-only | ||
|
||
use serde::de::Error; | ||
use serde::{Deserialize, Deserializer, Serialize}; | ||
use sgx::parameters::Features; | ||
|
||
#[derive(Clone, Deserialize, Debug, Default, Serialize)] | ||
#[serde(deny_unknown_fields)] | ||
pub struct Config { | ||
/// Values for `mrsigner` in the report body. | ||
/// This is the list of public keys which have signed the Enarx binary. | ||
#[serde(default)] | ||
#[serde(deserialize_with = "from_hex")] | ||
pub enarx_signer: Option<Vec<Vec<u8>>>, | ||
|
||
/// Values for `features`. | ||
#[serde(default)] | ||
#[serde(deserialize_with = "from_features")] | ||
pub features: Option<u64>, | ||
|
||
/// Value allowed for `cpusvn`. | ||
pub cpu_svn: Option<Vec<u8>>, | ||
|
||
/// Value for `isv_svn`, do not allow versions below this. | ||
pub enclave_security_version: Option<u16>, | ||
|
||
/// Value for `isv_prodid`, do not allow versions below this. | ||
pub enclave_product_id: Option<u16>, | ||
} | ||
|
||
fn from_hex<'de, D>(deserializer: D) -> Result<Option<Vec<Vec<u8>>>, D::Error> | ||
where | ||
D: Deserializer<'de>, | ||
{ | ||
let s: Vec<&str> = Deserialize::deserialize(deserializer)?; | ||
|
||
let mut outer_vec = Vec::new(); | ||
for hash_string in s { | ||
outer_vec.push(hex::decode(hash_string).map_err(|_| Error::custom("invalid hex"))?); | ||
} | ||
|
||
Ok(Some(outer_vec)) | ||
} | ||
|
||
fn from_features<'de, D>(deserializer: D) -> Result<Option<u64>, D::Error> | ||
where | ||
D: Deserializer<'de>, | ||
{ | ||
let s: &str = Deserialize::deserialize(deserializer)?; | ||
|
||
let mut flags = Features::empty(); | ||
|
||
flags |= Features::INIT; // Must be set | ||
flags |= Features::MODE64BIT; // Isn't everything 64-bit? | ||
|
||
for flag in s.to_string().split('|') { | ||
match flag.trim() { | ||
"CET" => { | ||
flags |= Features::CET; | ||
} | ||
"Debug" => { | ||
flags |= Features::DEBUG; | ||
} | ||
"Eint_Key" => { | ||
flags |= Features::EINIT_KEY; | ||
} | ||
"KSS" => { | ||
flags |= Features::KSS; | ||
} | ||
"Provisioning_Key" => { | ||
flags |= Features::PROVISIONING_KEY; | ||
} | ||
_ => return Err(D::Error::custom(format!("unknown flag '{}'", flag))), | ||
} | ||
} | ||
|
||
Ok(Some(flags.bits())) | ||
} | ||
|
||
impl Config { | ||
pub fn features(&self) -> Features { | ||
match self.features { | ||
Some(f) => Features::from_bits_truncate(f), | ||
None => Features::empty(), | ||
} | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use crate::config::Config; | ||
|
||
#[test] | ||
fn test_empty_config() { | ||
let config_raw = r#" | ||
"#; | ||
|
||
let config_obj: Config = toml::from_str(config_raw).expect("Couldn't deserialize"); | ||
assert!(config_obj.enarx_signer.is_none()); | ||
assert!(config_obj.enclave_security_version.is_none()); | ||
assert!(config_obj.cpu_svn.is_none()); | ||
} | ||
|
||
#[test] | ||
fn test_list_of_hashes() { | ||
let config_raw = r#" | ||
enarx_signer = ["1234567890", "00112233445566778899"] | ||
"#; | ||
|
||
let config_obj: Config = toml::from_str(config_raw).expect("Couldn't deserialize"); | ||
assert!(config_obj.enarx_signer.is_some()); | ||
assert_eq!(config_obj.enarx_signer.clone().unwrap().len(), 2); | ||
assert_eq!( | ||
config_obj.enarx_signer.clone().unwrap().first().unwrap(), | ||
&hex::decode("1234567890").unwrap() | ||
); | ||
assert_eq!( | ||
config_obj.enarx_signer.unwrap().get(1).unwrap(), | ||
&hex::decode("00112233445566778899").unwrap() | ||
); | ||
assert!(config_obj.cpu_svn.is_none()); | ||
} | ||
} |
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,19 +1,21 @@ | ||
// SPDX-FileCopyrightText: 2022 Profian Inc. <[email protected]> | ||
// SPDX-License-Identifier: AGPL-3.0-only | ||
|
||
mod quote; | ||
pub mod config; | ||
pub mod quote; | ||
|
||
use cryptography::ext::*; | ||
use quote::traits::ParseBytes; | ||
|
||
use std::fmt::Debug; | ||
|
||
use crate::config::Config; | ||
use anyhow::{anyhow, Result}; | ||
use cryptography::const_oid::ObjectIdentifier; | ||
use cryptography::sha2::{Digest, Sha256}; | ||
use cryptography::x509::{ext::Extension, request::CertReqInfo, Certificate, TbsCertificate}; | ||
use der::{Decode, Encode}; | ||
use sgx::parameters::{Attributes, MiscSelect}; | ||
use sgx::parameters::MiscSelect; | ||
|
||
#[derive(Clone, Debug)] | ||
pub struct Sgx([Certificate<'static>; 1]); | ||
|
@@ -29,7 +31,7 @@ impl Sgx { | |
pub const OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.3.6.1.4.1.58270.1.2"); | ||
pub const ATT: bool = true; | ||
|
||
fn trusted<'c>(&'c self, chain: &'c [Certificate<'c>]) -> Result<&'c TbsCertificate<'c>> { | ||
pub fn trusted<'c>(&'c self, chain: &'c [Certificate<'c>]) -> Result<&'c TbsCertificate<'c>> { | ||
let mut signer = &self.0[0].tbs_certificate; | ||
for cert in self.0.iter().chain(chain.iter()) { | ||
signer = signer.verify_crt(cert)?; | ||
|
@@ -38,7 +40,13 @@ impl Sgx { | |
Ok(signer) | ||
} | ||
|
||
pub fn verify(&self, cri: &CertReqInfo<'_>, ext: &Extension<'_>, dbg: bool) -> Result<bool> { | ||
pub fn verify( | ||
&self, | ||
cri: &CertReqInfo<'_>, | ||
ext: &Extension<'_>, | ||
config: Option<&Config>, | ||
dbg: bool, | ||
) -> Result<bool> { | ||
if ext.critical { | ||
return Err(anyhow!("sgx extension cannot be critical")); | ||
} | ||
|
@@ -80,38 +88,59 @@ impl Sgx { | |
|
||
if !dbg { | ||
// TODO: Validate that the certification request came from an SGX enclave. | ||
let hash = Sha256::digest(&cri.public_key.to_vec()?); | ||
let hash = Sha256::digest(cri.public_key.to_vec()?); | ||
if hash.as_slice() != &rpt.reportdata[..hash.as_slice().len()] { | ||
return Err(anyhow!("sgx report data is invalid")); | ||
} | ||
|
||
if rpt.mrenclave != [0u8; 32] { | ||
return Err(anyhow!("untrusted enarx runtime")); | ||
} | ||
|
||
if rpt.mrsigner != [0u8; 32] { | ||
return Err(anyhow!("untrusted enarx signer")); | ||
} | ||
|
||
if rpt.cpusvn != [0u8; 16] { | ||
return Err(anyhow!("untrusted cpu")); | ||
} | ||
|
||
if rpt.attributes() != Attributes::default() { | ||
return Err(anyhow!("untrusted attributes")); | ||
if config.is_some() { | ||
let config_ref = config.as_ref().unwrap(); | ||
if config_ref.enarx_signer.is_some() { | ||
let mut signed = false; | ||
for signer in config_ref.enarx_signer.as_ref().unwrap() { | ||
if rpt.mrsigner == signer.as_slice() { | ||
signed = true; | ||
break; | ||
} | ||
} | ||
if !signed { | ||
return Err(anyhow!("untrusted enarx signer")); | ||
} | ||
} | ||
|
||
if config_ref.cpu_svn.is_some() { | ||
let conf_version = config_ref.cpu_svn.as_ref().unwrap(); | ||
for index in 0..rpt.cpusvn.len() { | ||
if rpt.cpusvn.get(index).unwrap() < conf_version.get(index).unwrap() { | ||
return Err(anyhow!("untrusted cpu")); | ||
} | ||
} | ||
} | ||
|
||
if config_ref.enclave_product_id.is_some() | ||
&& rpt.enclave_product_id() != config_ref.enclave_product_id.unwrap() | ||
{ | ||
return Err(anyhow!("untrusted enclave product id")); | ||
} | ||
|
||
if config_ref.enclave_security_version.is_some() | ||
&& rpt.enclave_security_version() < config_ref.enclave_security_version.unwrap() | ||
{ | ||
return Err(anyhow!("untrusted enclave")); | ||
} | ||
|
||
if rpt | ||
.attributes() | ||
.features() | ||
.intersects(config_ref.features()) | ||
{ | ||
return Err(anyhow!("untrusted features")); | ||
} | ||
} | ||
|
||
if rpt.misc_select() != MiscSelect::default() { | ||
return Err(anyhow!("untrusted misc select")); | ||
} | ||
|
||
if rpt.enclave_product_id() != u16::MAX { | ||
return Err(anyhow!("untrusted enclave product id")); | ||
} | ||
|
||
if rpt.enclave_security_version() < u16::MAX { | ||
return Err(anyhow!("untrusted enclave")); | ||
} | ||
} | ||
|
||
Ok(false) | ||
|
Oops, something went wrong.