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 14, 2022
1 parent 5fb350a commit 3d8f726
Show file tree
Hide file tree
Showing 14 changed files with 840 additions and 62 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,8 @@ 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"] }
toml = "0.5"

[target.'cfg(not(target_os = "wasi"))'.dependencies]
tokio = { version = "^1.21.2", features = ["rt-multi-thread", "macros"], default-features = 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"
132 changes: 132 additions & 0 deletions crates/sgx_validation/src/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
// 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 `mrenclave` in the report body.
/// This is the hash of the Enclave environment after the Enarx binary is loaded
/// but before any workload is loaded, so this is a hash of the Enarx binary
/// in memory.
#[serde(default)]
#[serde(deserialize_with = "from_hex")]
pub enarx_hash: Option<Vec<Vec<u8>>>,

/// Values for `features`.
#[serde(default)]
#[serde(deserialize_with = "from_features")]
pub features: Option<u64>,

/// Minimum value allowed for `cpusvn`.
pub cpu_svn: Option<Vec<u8>>,

/// Minimum value for `isv_svn`.
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 added crates/sgx_validation/src/icelake.signed.csr
Binary file not shown.
83 changes: 56 additions & 27 deletions crates/sgx_validation/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
// 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};
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]);
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 @@ -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)
Expand Down
Loading

0 comments on commit 3d8f726

Please sign in to comment.