Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add TcbRawInfoVerifier #98

Merged
merged 2 commits into from
May 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion verifier/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,11 @@ impl<T> VerificationOutput<T> {
pub fn is_failure(&self) -> Choice {
!self.succeeded
}

/// The value used in the verification.
pub fn value(&self) -> &T {
&self.value
}
}

/// A helper struct for displaying the verification results.
Expand Down Expand Up @@ -107,7 +112,7 @@ impl<'a, V, O> VerificationTreeDisplay<'a, V, O> {
}
}

impl<'a, V: VerificationMessage<O>, O: Clone> Display for VerificationTreeDisplay<'a, V, O> {
impl<'a, V: VerificationMessage<O>, O> Display for VerificationTreeDisplay<'a, V, O> {
nick-mobilecoin marked this conversation as resolved.
Show resolved Hide resolved
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
self.verifier.fmt_padded(f, 0, &self.result)
}
Expand Down
118 changes: 116 additions & 2 deletions verifier/src/tcb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,13 @@
extern crate alloc;

use crate::advisories::{Advisories, AdvisoryStatus};
use crate::{Accessor, VerificationMessage, VerificationOutput, Verifier};
use alloc::string::String;
use alloc::vec::Vec;
use core::fmt::Formatter;
use der::DateTime;
use mc_sgx_dcap_types::{TcbInfo as PckTcb, COMPONENT_SVN_COUNT, FMSPC_SIZE};
use p256::ecdsa::signature::Verifier;
use p256::ecdsa::signature::Verifier as SignatureVerifier;
use p256::ecdsa::{Signature, VerifyingKey};
use serde::Deserialize;
use serde_json::value::RawValue;
Expand Down Expand Up @@ -278,10 +280,80 @@ impl<'a> TryFrom<&'a str> for TcbInfoRaw<'a> {
}
}

/// Verifier for ensuring a raw TCB was signed with the provided key
nick-mobilecoin marked this conversation as resolved.
Show resolved Hide resolved
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct TcbInfoRawVerifier {
key: VerifyingKey,
time: DateTime,
}

impl TcbInfoRawVerifier {
/// Create a new instance.
///
/// The `TcbInfoRawVerifier::verify()` will fail if the signature doesn't
/// match for the `key` or if the `time` is outside the `issueDate` and
/// `nextUpdate` times in the `tcbInfo`.
///
/// # Arguments
/// - `key` - The public key to verify the `tcbInfo` signature with
/// This should be retrieved from the x509 certificate provided in the
/// TCB request from
/// <https://api.trustedservices.intel.com/sgx/certification/v4/tcb?fmspc={}>
/// - `time` - The current system time
/// This is expected to be generated by:
/// ```ignore
/// let time = DateTime::from_system_time(SystemTime::now()).unwrap();
/// ```
/// or equivalent
pub fn new(key: VerifyingKey, time: DateTime) -> Self {
Self { key, time }
}
}

impl<'a, E: Accessor<TcbInfoRaw<'a>>> Verifier<E> for TcbInfoRawVerifier {
type Value = Option<Error>;
fn verify(&self, evidence: &E) -> VerificationOutput<Self::Value> {
let tcb_raw = evidence.get();
let result = tcb_raw.verify(&self.key, self.time);
let is_success = result.is_ok() as u8;

let mut error = None;
if let Err(e) = result {
error = Some(e);
}

VerificationOutput::new(error, is_success.into())
}
}

impl VerificationMessage<Option<Error>> for TcbInfoRawVerifier {
fn fmt_padded(
&self,
f: &mut Formatter<'_>,
pad: usize,
result: &VerificationOutput<Option<Error>>,
) -> core::fmt::Result {
let is_success = result.is_success();
let status = crate::choice_to_status_message(is_success);
write!(f, "{:pad$}{status} ", "")?;

if is_success.into() {
write!(f, "The raw TCB info was verified for the provided key")
} else {
let error = result
.value()
.as_ref()
.expect("Should have an error if not successful");
write!(f, "The raw TCB info could not be verified: {error}")
}
}
}

#[cfg(test)]
mod tests {
use super::*;
use alloc::vec;
use crate::VerificationTreeDisplay;
use alloc::{format, vec};
use p256::ecdsa::VerifyingKey;
use serde_json::value::RawValue;
use x509_cert::{der::DecodePem, Certificate};
Expand Down Expand Up @@ -803,4 +875,46 @@ mod tests {
);
assert_eq!(tcb.is_corresponding_level(&pck_tcb), expected);
}

#[test]
fn tcb_raw_verifier_succeeds() {
let key = tcb_verifying_key();
let tcb_json = include_str!("../data/tests/fmspc_00906ED50000_2023_05_10.json");
let raw_tcb = TcbInfoRaw::try_from(tcb_json).expect("Failed to parse raw TCB");

let time = "2023-06-08T13:43:27Z"
.parse::<DateTime>()
.expect("Failed to parse time");

let verifier = TcbInfoRawVerifier::new(key, time);
let verification = verifier.verify(&raw_tcb);

assert_eq!(verification.is_success().unwrap_u8(), 1);

let displayable = VerificationTreeDisplay::new(&verifier, verification);
let expected = r#"
- [x] The raw TCB info was verified for the provided key"#;
assert_eq!(format!("\n{displayable}"), textwrap::dedent(expected));
}

#[test]
fn tcb_raw_verifier_fails_at_next_update() {
let key = tcb_verifying_key();
let tcb_json = include_str!("../data/tests/fmspc_00906ED50000_2023_05_10.json");
let raw_tcb = TcbInfoRaw::try_from(tcb_json).expect("Failed to parse raw TCB");

let time = "2023-06-09T13:43:27Z"
.parse::<DateTime>()
.expect("Failed to parse time");

let verifier = TcbInfoRawVerifier::new(key, time);
let verification = verifier.verify(&raw_tcb);

assert_eq!(verification.is_success().unwrap_u8(), 0);

let displayable = VerificationTreeDisplay::new(&verifier, verification);
let expected = r#"
- [ ] The raw TCB info could not be verified: TCB info expired"#;
assert_eq!(format!("\n{displayable}"), textwrap::dedent(expected));
}
}