Skip to content

Commit

Permalink
feat: configuration for attestation
Browse files Browse the repository at this point in the history
Signed-off-by: Richard Zak <richard@profian.com>
  • Loading branch information
rjzak committed Nov 11, 2022
1 parent 5fb350a commit 6627512
Show file tree
Hide file tree
Showing 14 changed files with 799 additions and 52 deletions.
32 changes: 32 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ anyhow = { version = "^1.0.66", default-features = false }
base64 = { version = "^0.13.1", default-features = false }
mime = { version = "^0.3.16", default-features = false }
confargs = { version = "^0.1.3", default-features = false }
serde = { version = "1.0", features = ["derive"] }

[target.'cfg(not(target_os = "wasi"))'.dependencies]
tokio = { version = "^1.21.2", features = ["rt-multi-thread", "macros"], default-features = false }
Expand All @@ -41,6 +42,7 @@ http = { version = "^0.2.6", default-features = false }
memoffset = { version = "0.7.1", default-features = false }
rstest = { version = "0.15", default-features = false }
testaso = { version = "0.1", default-features = false }
toml = "0.5"

[profile.release]
incremental = false
Expand Down
3 changes: 3 additions & 0 deletions crates/sgx_validation/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ description = "Intel SGX Attestation validation library for Steward"
cryptography = { path = "../cryptography" }
anyhow = { version = "^1.0.55", default-features = false }
der = { version = "0.6", features = ["std"], default-features = false }
hex = "0.4"
serde = { version = "1.0", features = ["derive", "std"] }
sgx = { version = "0.5.0", default-features = false }

[dev-dependencies]
testaso = { version = "0.1", default-features = false }
toml = "0.5"
130 changes: 130 additions & 0 deletions crates/sgx_validation/src/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
// SPDX-FileCopyrightText: 2022 Profian Inc. <opensource@profian.com>
// 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 = flags | Features::CET;
}
"Debug" => {
flags = flags | Features::DEBUG;
}
"Eint_Key" => {
flags = flags | Features::EINIT_KEY;
}
"KSS" => {
flags = flags | Features::KSS;
}
"Provisioning_Key" => {
flags = flags | Features::PROVISIONING_KEY;
}
_ => return Err(D::Error::custom(format!("unknown flag '{}'", flag))),
}
}

Ok(Some(flags.bits()))
}

impl Into<Features> for &Config {
fn into(self) -> Features {
match self.features {
Some(f) => Features::from_bits_truncate(f),
None => Features::empty(),
}
}
}

impl Config {
pub fn features(&self) -> Features {
self.into()
}
}

#[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 added crates/sgx_validation/src/icelake.signed.csr
Binary file not shown.
79 changes: 54 additions & 25 deletions crates/sgx_validation/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
// SPDX-FileCopyrightText: 2022 Profian Inc. <opensource@profian.com>
// 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};
Expand All @@ -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)?;
Expand All @@ -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"));
}
Expand Down Expand Up @@ -85,33 +93,54 @@ impl Sgx {
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)
Expand Down
Loading

0 comments on commit 6627512

Please sign in to comment.