Skip to content

Commit

Permalink
Generate DCAP quotes in enclaves (#3612)
Browse files Browse the repository at this point in the history
The enclaves now generate DCAP specified quotes, `Quote3`.
  • Loading branch information
nick-mobilecoin committed Oct 16, 2023
1 parent 925449b commit c07b5cd
Show file tree
Hide file tree
Showing 79 changed files with 475 additions and 1,023 deletions.
13 changes: 12 additions & 1 deletion Cargo.lock

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

2 changes: 1 addition & 1 deletion attest/ake/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ rand_core = "0.6"
serde = { version = "1.0", default-features = false, features = ["alloc"] }

[dev-dependencies]
mc-attest-net = { path = "../net" }
mc-attest-untrusted = { path = "../untrusted" }
mc-util-encodings = { path = "../../util/encodings" }
mc-util-from-random = { path = "../../util/from-random" }

Expand Down
2 changes: 1 addition & 1 deletion attest/ake/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use mc_crypto_noise::{CipherError, HandshakeError};
use serde::{Deserialize, Serialize};

/// An enumeration of errors which can occur during key exchange
#[derive(Clone, Debug, Deserialize, Display, PartialEq, PartialOrd, Serialize)]
#[derive(Clone, Debug, Deserialize, Display, PartialEq, Serialize)]
pub enum Error {
/// The handshake state could not be initialized: {0}
HandshakeInit(HandshakeError),
Expand Down
24 changes: 12 additions & 12 deletions attest/ake/src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::mealy::{Input as MealyInput, Output as MealyOutput};
use alloc::vec::Vec;
use core::marker::PhantomData;
use der::DateTime;
use mc_attest_core::EvidenceKind;
use mc_attest_core::{DcapEvidence, EvidenceKind};
use mc_attestation_verifier::TrustedIdentity;
use mc_crypto_keys::Kex;
use mc_crypto_noise::{
Expand All @@ -23,8 +23,8 @@ where
{
/// This is the local node's identity key
pub(crate) local_identity: KexAlgo::Private,
/// This is the local node's ias report.
pub(crate) attestation_evidence: EvidenceKind,
/// This is the local node's attestation evidence.
pub(crate) dcap_evidence: DcapEvidence,

_kex: PhantomData<KexAlgo>,
_cipher: PhantomData<Cipher>,
Expand All @@ -38,10 +38,10 @@ where
DigestAlgo: NoiseDigest,
{
/// Create a new input event to initiate a node-to-node channel.
pub fn new(local_identity: KexAlgo::Private, attestation_evidence: EvidenceKind) -> Self {
pub fn new(local_identity: KexAlgo::Private, dcap_evidence: DcapEvidence) -> Self {
Self {
local_identity,
attestation_evidence,
dcap_evidence,
_kex: PhantomData,
_cipher: PhantomData,
_digest: PhantomData,
Expand Down Expand Up @@ -176,7 +176,7 @@ where
/// This is the local node's identity key
pub(crate) local_identity: KexAlgo::Private,
/// This is the local node's attestation evidence.
pub(crate) attestation_evidence: EvidenceKind,
pub(crate) dcap_evidence: DcapEvidence,

/// The auth request input, including payload, if any
pub(crate) data: AuthRequestOutput<HandshakeNX, KexAlgo, Cipher, DigestAlgo>,
Expand All @@ -199,11 +199,11 @@ where
pub fn new(
data: AuthRequestOutput<HandshakeNX, KexAlgo, Cipher, DigestAlgo>,
local_identity: KexAlgo::Private,
attestation_evidence: EvidenceKind,
dcap_evidence: DcapEvidence,
) -> Self {
Self {
local_identity,
attestation_evidence,
dcap_evidence,
data,
}
}
Expand All @@ -223,8 +223,8 @@ where
/// This is the local node's identity key
pub(crate) local_identity: KexAlgo::Private,
/// This is the local node's attestation evidence.
pub(crate) attestation_evidence: EvidenceKind,
/// The identities that the initiator's IAS report must conform to
pub(crate) dcap_evidence: DcapEvidence,
/// The identities that the initiator's attestation evidence must conform to
pub(crate) identities: Vec<TrustedIdentity>,

/// The auth request input, including payload, if any
Expand All @@ -248,12 +248,12 @@ where
pub fn new(
data: AuthRequestOutput<HandshakeIX, KexAlgo, Cipher, DigestAlgo>,
local_identity: KexAlgo::Private,
attestation_evidence: EvidenceKind,
dcap_evidence: DcapEvidence,
identities: impl Into<Vec<TrustedIdentity>>,
) -> Self {
Self {
local_identity,
attestation_evidence,
dcap_evidence,
identities: identities.into(),
data,
}
Expand Down
8 changes: 6 additions & 2 deletions attest/ake/src/initiator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@ use crate::{
AuthPending, AuthRequestOutput, AuthResponseInput, ClientInitiate, Error, NodeInitiate, Ready,
Start, Terminated, Transition, UnverifiedAttestationEvidence,
};
use ::prost::Message;
use alloc::{string::ToString, vec::Vec};
use der::DateTime;
use mc_attest_core::{EvidenceKind, ReportDataMask, VerificationReport};
use mc_attest_verifier::{DcapVerifier, Error as VerifierError, Verifier, DEBUG_ENCLAVE};
use mc_attest_verifier_types::DcapEvidence;
use mc_attest_verifier_types::{prost, DcapEvidence};
use mc_attestation_verifier::{Evidence, TrustedIdentity, VerificationTreeDisplay};
use mc_crypto_keys::{Kex, KexPublic, ReprBytes};
use mc_crypto_noise::{
Expand Down Expand Up @@ -125,7 +126,10 @@ where
None,
)
.map_err(Error::HandshakeInit)?;
let serialized_evidence = input.attestation_evidence.into_bytes();

let prost_evidence = prost::DcapEvidence::try_from(&input.dcap_evidence)
.map_err(|_| Error::AttestationEvidenceSerialization)?;
let serialized_evidence = prost_evidence.encode_to_vec();

parse_handshake_output(
handshake_state
Expand Down
43 changes: 15 additions & 28 deletions attest/ake/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,11 @@ mod test {
//! Unit tests for Attested Key Exchange
use super::*;
use aes_gcm::Aes256Gcm;
use mc_attest_core::{EvidenceKind, Quote};
use mc_attest_net::{Client, RaClient};
use mc_attest_core::Report;
use mc_attest_untrusted::DcapQuotingEnclave;
use mc_attest_verifier_types::{DcapEvidence, EnclaveReportDataContents};
use mc_attestation_verifier::{TrustedIdentity, TrustedMrSignerIdentity};
use mc_crypto_keys::{X25519Private, X25519Public, X25519};
use mc_util_encodings::{FromBase64, ToX64};
use mc_util_from_random::FromRandom;
use rand_core::SeedableRng;
use rand_hc::Hc128Rng;
Expand All @@ -50,37 +50,24 @@ mod test {

#[test]
fn ix_handshake() {
// Read an existing, valid quote
let data = include_str!("../test_data/ok_quote.txt");
let quote = Quote::from_base64(data.trim()).expect("Could not parse quote");

// Create a new identity pubkey for our "enclave"
let mut csprng = Hc128Rng::seed_from_u64(0);
let identity = X25519Private::from_random(&mut csprng);
let pubkey = X25519Public::from(&identity);

// Get the bytes from our quote
let mut quote_data = quote.to_x64_vec();

// Overwrite the cached quote's report_data contents with our pubkey
quote_data[368..400].copy_from_slice(pubkey.as_ref());

// Re-assemble a quote from the munged version
let quote = Quote::try_from(quote_data.as_ref())
.expect("Could not parse quote from modified bytes");

// Sign the forged quote with the sim client
let ra_client = Client::new("").expect("Could not create sim client");
let attestation_evidence = ra_client
.verify_quote(&quote, None)
.expect("Could not sign our bogus report");
let report_data = EnclaveReportDataContents::new([0x2au8; 16].into(), pubkey, [0x36u8; 32]);
let mut report = Report::default();
report.as_mut().body.report_data.d[..32].copy_from_slice(&report_data.sha256());

// TODO: replace with dcap
let attestation_evidence = EvidenceKind::Epid(attestation_evidence);
let quote = DcapQuotingEnclave::quote_report(&report).expect("Failed to create quote");
let collateral = DcapQuotingEnclave::collateral(&quote).expect("Failed to get collateral");
let attestation_evidence = DcapEvidence {
quote,
collateral,
report_data,
};

let report_body = quote
.report_body()
.expect("Could not retrieve report body from cached report");
let report_body = attestation_evidence.quote.app_report_body();

let mr_signer = TrustedIdentity::from(TrustedMrSignerIdentity::new(
report_body.mr_signer(),
Expand Down Expand Up @@ -119,7 +106,7 @@ mod test {
let auth_response_input = AuthResponseInput::new(auth_response_output, identities, None);
let (initiator, _) = initiator
.try_next(&mut csprng, auth_response_input)
.expect("Initiator not process auth response");
.expect("Initiator could not process auth response");

// initiator = ready, responder = ready

Expand Down
75 changes: 38 additions & 37 deletions attest/ake/src/responder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,16 @@ use crate::{
mealy::Transition,
state::{Ready, Start},
};
use alloc::vec::Vec;
use mc_attest_core::{EvidenceKind, ReportDataMask, VerificationReport};
use mc_attest_verifier::{Verifier, DEBUG_ENCLAVE};
use mc_crypto_keys::{Kex, ReprBytes};
use ::prost::Message;
use alloc::{string::ToString, vec::Vec};
use mc_attest_verifier::DcapVerifier;
use mc_attest_verifier_types::{prost, DcapEvidence, EvidenceKind};
use mc_attestation_verifier::{Evidence, VerificationTreeDisplay};
use mc_crypto_keys::Kex;
use mc_crypto_noise::{
HandshakeIX, HandshakeNX, HandshakePattern, HandshakeState, HandshakeStatus, NoiseCipher,
NoiseDigest, ProtocolName,
};
use prost::Message;
use rand_core::{CryptoRng, RngCore};

/// A trait containing default implementations, used to tack repeatable chunks
Expand All @@ -36,7 +37,7 @@ trait ResponderTransitionMixin {
fn handle_response<KexAlgo, Cipher, DigestAlgo>(
csprng: &mut (impl CryptoRng + RngCore),
handshake_state: HandshakeState<KexAlgo, Cipher, DigestAlgo>,
attestatio_evidence: EvidenceKind,
dcap_evidence: DcapEvidence,
) -> Result<(Ready<Cipher>, AuthResponseOutput), Error>
where
KexAlgo: Kex,
Expand Down Expand Up @@ -82,26 +83,23 @@ impl ResponderTransitionMixin for Start {
fn handle_response<KexAlgo, Cipher, DigestAlgo>(
csprng: &mut (impl CryptoRng + RngCore),
handshake_state: HandshakeState<KexAlgo, Cipher, DigestAlgo>,
attestation_evidence: EvidenceKind,
dcap_evidence: DcapEvidence,
) -> Result<(Ready<Cipher>, AuthResponseOutput), Error>
where
KexAlgo: Kex,
Cipher: NoiseCipher,
DigestAlgo: NoiseDigest,
{
// TODO: This will be replaced with dcap evidence serialization.
// This code will never run.
let EvidenceKind::Epid(ias_report) = attestation_evidence else {
return Err(Error::AttestationEvidenceSerialization);
};
// Encrypt the local report for output
let mut report_bytes = Vec::with_capacity(ias_report.encoded_len());
ias_report
.encode(&mut report_bytes)
.expect("Invariant failure, encoded_len insufficient to encode IAS report");
let prost_evidence = prost::DcapEvidence::try_from(&dcap_evidence)
.map_err(|_| Error::AttestationEvidenceSerialization)?;

// We need to send back EvidenceKind for backward compatibility with
// the legacy `EvidenceKind::Epid` version
let evidence = EvidenceKind::Dcap(prost_evidence);
let serialized_evidence = evidence.into_bytes();

let output = handshake_state
.write_message(csprng, &report_bytes)
.write_message(csprng, &serialized_evidence)
.map_err(Error::HandshakeWrite)?;

match output.status {
Expand Down Expand Up @@ -146,27 +144,30 @@ where
)?;

let identities = input.identities;
let mut verifier = Verifier::default();
verifier.identities(&identities).debug(DEBUG_ENCLAVE);

// Parse the received attestation evidence
let remote_report = VerificationReport::decode(payload.as_slice())
// Parse the received DCAP evidence from the other node
let prost_evidence = prost::DcapEvidence::decode(payload.as_slice())
.map_err(|_| Error::AttestationEvidenceDeserialization)?;

let dcap_evidence = DcapEvidence::try_from(&prost_evidence)
.map_err(|_| Error::AttestationEvidenceDeserialization)?;

// Verify using given verifier, and ensure the first 32B of the report data are
// the identity pubkey.
verifier
.report_data(
&handshake_state
.remote_identity()
.ok_or(Error::MissingRemoteIdentity)?
.map_bytes(|bytes| {
ReportDataMask::try_from(bytes).map_err(|_| Error::BadRemoteIdentity)
})?,
)
.verify(&remote_report)?;

Self::handle_response(csprng, handshake_state, input.attestation_evidence)
let DcapEvidence {
quote,
collateral,
report_data,
} = dcap_evidence;

let verifier = DcapVerifier::new(identities, None, report_data);
let evidence = Evidence::new(quote, collateral).map_err(mc_attest_verifier::Error::from)?;

let verification = verifier.verify(&evidence);
if verification.is_failure().into() {
let display_tree = VerificationTreeDisplay::new(&verifier, verification);
return Err(mc_attest_verifier::Error::Verification(display_tree.to_string()).into());
}

Self::handle_response(csprng, handshake_state, input.dcap_evidence)
}
}

Expand Down Expand Up @@ -195,6 +196,6 @@ where
&input.data.data,
input.local_identity,
)?;
Self::handle_response(csprng, handshake_state, input.attestation_evidence)
Self::handle_response(csprng, handshake_state, input.dcap_evidence)
}
}
Loading

0 comments on commit c07b5cd

Please sign in to comment.