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

Generate DCAP quotes in enclaves #3612

Merged
merged 5 commits into from
Oct 16, 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
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,
nick-mobilecoin marked this conversation as resolved.
Show resolved Hide resolved

_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();

nick-mobilecoin marked this conversation as resolved.
Show resolved Hide resolved
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]);
nick-mobilecoin marked this conversation as resolved.
Show resolved Hide resolved
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
Loading