Skip to content

Commit

Permalink
Add DICE Attestation Verifier
Browse files Browse the repository at this point in the history
Bug: 374589992
Change-Id: I5d5d3b32754445647eda409051b6c7e7600b45ad
  • Loading branch information
ipetr0v committed Oct 25, 2024
1 parent 361bd00 commit 292b4f0
Show file tree
Hide file tree
Showing 13 changed files with 204 additions and 65 deletions.
5 changes: 1 addition & 4 deletions oak_attestation_integration_tests/tests/attester_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,5 @@ fn dice_attester_generates_correct_dice_chain() {

let test_evidence = dice_attester.quote().expect("couldn't generate the evidence");
let result = verify_dice_chain(&test_evidence);
// TODO: b/368030563 - Make the test check for `Ok` once keys are being stored
// in the Event. Since currently `verify_dice_chain` returns `Err("no
// application keys in evidence")`.
assert!(result.is_err());
assert!(result.is_ok());
}
12 changes: 8 additions & 4 deletions oak_attestation_integration_tests/tests/verifier_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
//

use oak_attestation_integration_tests::{create, Snapshot, SnapshotPath};
use oak_attestation_verification::verifier::{to_attestation_results, verify, verify_dice_chain};
use oak_attestation_verification::verifier::{
to_attestation_results, verify, verify_dice_chain_and_extract_evidence,
};
use oak_proto_rust::oak::attestation::{
self,
v1::{
Expand All @@ -41,7 +43,7 @@ fn verify_mock_dice_chain() {
.expect("failed to create mock attester");
let mock_evidence = mock_attester.quote().expect("couldn't get evidence");

let result = verify_dice_chain(&mock_evidence);
let result = verify_dice_chain_and_extract_evidence(&mock_evidence);

assert!(result.is_ok());
let evidence_values: attestation::v1::extracted_evidence::EvidenceValues =
Expand All @@ -60,7 +62,9 @@ fn get_restricted_kernel_evidence_proto_with_eventlog() -> attestation::v1::Evid

#[test]
fn verify_mock_dice_chain_with_valid_event_log() {
let result = verify_dice_chain(&get_restricted_kernel_evidence_proto_with_eventlog());
let result = verify_dice_chain_and_extract_evidence(
&get_restricted_kernel_evidence_proto_with_eventlog(),
);

assert!(result.is_ok());
let evidence_values: attestation::v1::extracted_evidence::EvidenceValues =
Expand Down Expand Up @@ -98,7 +102,7 @@ fn verify_mock_dice_chain_with_invalid_event_log() {
stage0.kernel_cmdline = format!("evil modification {}", stage0.kernel_cmdline);
*encoded_stage0_event = stage0.encode_to_vec();

let result = verify_dice_chain(&evidence);
let result = verify_dice_chain_and_extract_evidence(&evidence);

assert!(result.is_err());
}
Expand Down
3 changes: 2 additions & 1 deletion oak_attestation_verification/src/policy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ impl Policy for CombinedPolicy {
&self,
event_log: &EventLog,
event_endorsements: &EventEndorsements,
milliseconds_since_epoch: i64,
) -> anyhow::Result<AttestationResults> {
if event_log.encoded_events.len() != event_endorsements.encoded_event_endorsements.len() {
anyhow::bail!(
Expand All @@ -75,7 +76,7 @@ impl Policy for CombinedPolicy {
);
let event_attestation_results = verification_iterator
.map(|(event_policy, event, event_endorsements)| {
event_policy.verify(event, event_endorsements).unwrap_or(
event_policy.verify(event, event_endorsements, milliseconds_since_epoch).unwrap_or(
// TODO: b/366186091 - Use Rust error types for failed attestation.
EventAttestationResults {},
)
Expand Down
4 changes: 2 additions & 2 deletions oak_attestation_verification/src/policy/application.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ impl EventPolicy for ApplicationPolicy {
&self,
encoded_event: &[u8],
encoded_event_endorsement: &[u8],
milliseconds_since_epoch: i64,
) -> anyhow::Result<EventAttestationResults> {
let event = decode_event_proto::<ApplicationLayerData>(
"type.googleapis.com/oak.attestation.v1.ApplicationLayerData",
Expand All @@ -53,8 +54,7 @@ impl EventPolicy for ApplicationPolicy {
)?;

let expected_values = get_application_layer_expected_values(
// TODO: b/369821273 - Add clocks to policies.
0i64,
milliseconds_since_epoch,
Some(&event_endorsements),
&self.reference_values,
)
Expand Down
7 changes: 4 additions & 3 deletions oak_attestation_verification/src/policy/binary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,16 @@ impl EventPolicy for BinaryPolicy {
&self,
encoded_event: &[u8],
_encoded_event_endorsement: &[u8],
milliseconds_since_epoch: i64,
) -> anyhow::Result<EventAttestationResults> {
let event = decode_event_proto::<EventData>(
"type.googleapis.com/oak.attestation.v1.EventData",
encoded_event,
)?;

// TODO: b/369821273 - Add clocks to policies.
let expected_values = get_event_expected_values(0i64, &self.reference_values)
.context("couldn't verify event endosements")?;
let expected_values =
get_event_expected_values(milliseconds_since_epoch, &self.reference_values)
.context("couldn't verify event endosements")?;
compare_event_measurement_digests(&event, &expected_values)
.context("couldn't verify generic event")?;

Expand Down
4 changes: 2 additions & 2 deletions oak_attestation_verification/src/policy/kernel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ impl EventPolicy for KernelPolicy {
&self,
encoded_event: &[u8],
encoded_event_endorsement: &[u8],
milliseconds_since_epoch: i64,
) -> anyhow::Result<EventAttestationResults> {
let event = decode_event_proto::<KernelLayerData>(
"type.googleapis.com/oak.attestation.v1.KernelLayerData",
Expand All @@ -52,8 +53,7 @@ impl EventPolicy for KernelPolicy {
)?;

let expected_values = get_kernel_layer_expected_values(
// TODO: b/369821273 - Add clocks to policies.
0i64,
milliseconds_since_epoch,
Some(&event_endorsements),
&self.reference_values,
)
Expand Down
4 changes: 2 additions & 2 deletions oak_attestation_verification/src/policy/system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ impl EventPolicy for SystemPolicy {
&self,
encoded_event: &[u8],
encoded_event_endorsement: &[u8],
milliseconds_since_epoch: i64,
) -> anyhow::Result<EventAttestationResults> {
let event = decode_event_proto::<SystemLayerData>(
"type.googleapis.com/oak.attestation.v1.SystemLayerData",
Expand All @@ -52,8 +53,7 @@ impl EventPolicy for SystemPolicy {
)?;

let expected_values = get_system_layer_expected_values(
// TODO: b/369821273 - Add clocks to policies.
0i64,
milliseconds_since_epoch,
Some(&event_endorsements),
&self.reference_values,
)
Expand Down
104 changes: 99 additions & 5 deletions oak_attestation_verification/src/verifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,20 @@

//! Provides verification based on evidence, endorsements and reference values.

use alloc::format;
use alloc::{boxed::Box, format};

use anyhow::Context;
use coset::{cwt::ClaimsSet, CborSerializable, CoseKey};
use ecdsa::{signature::Verifier, Signature};
use oak_attestation_verification_types::{
policy::Policy, util::Clock, verifier::AttestationVerifier,
};
use oak_dice::cert::{cose_key_to_verifying_key, get_public_key_from_claims_set};
use oak_proto_rust::oak::attestation::v1::{
attestation_results::Status, endorsements, AttestationResults, Endorsements, EventLog,
Evidence, ExpectedValues, ExtractedEvidence, LayerEvidence, ReferenceValues,
};
use p256::ecdsa::VerifyingKey;

use crate::{
compare::compare_expected_values,
Expand Down Expand Up @@ -57,6 +61,39 @@ pub fn to_attestation_results(
}
}

pub struct AmdSevSnpDiceAttestationVerifier {
policy: Box<dyn Policy>,
clock: Box<dyn Clock>,
}

impl AmdSevSnpDiceAttestationVerifier {
pub fn new(policy: Box<dyn Policy>, clock: Box<dyn Clock>) -> Self {
Self { policy, clock }
}
}

impl AttestationVerifier for AmdSevSnpDiceAttestationVerifier {
fn verify(
&self,
evidence: &Evidence,
endorsements: &Endorsements,
) -> anyhow::Result<AttestationResults> {
// Last layer's certificate authority key is not used to sign anything.
let _ = verify_dice_chain(evidence).context("couldn't verify DICE chain")?;

// Verify event log and event endorsements with corresponding policy.
let event_log = &evidence
.event_log
.as_ref()
.ok_or_else(|| anyhow::anyhow!("event log was not provided"))?;
let event_endorsements = &endorsements
.event_endorsements
.as_ref()
.ok_or_else(|| anyhow::anyhow!("event endorsements were not provided"))?;
self.policy.verify(event_log, event_endorsements, self.clock.get_milliseconds_since_epoch())
}
}

/// Verifies entire setup by forwarding to individual setup types.
///
/// This just fetches expected values using [`expect::get_expected_values`],
Expand Down Expand Up @@ -114,17 +151,75 @@ pub fn verify_with_expected_values(

// Ensure the DICE chain signatures are valid and extract the measurements,
// public keys and other attestation-related data from the DICE chain.
let extracted_evidence = verify_dice_chain(evidence).context("invalid DICE chain")?;
let extracted_evidence =
verify_dice_chain_and_extract_evidence(evidence).context("invalid DICE chain")?;

compare_expected_values(&extracted_evidence, expected_values)
.context("comparing expected values to evidence")?;

Ok(extracted_evidence)
}

/// Verifies signatures of the certificates in the DICE chain and returns last
/// layer's Certificate Authority key if the verification is successful.
pub fn verify_dice_chain(evidence: &Evidence) -> anyhow::Result<VerifyingKey> {
let root_layer_verifying_key = {
let cose_key = {
let root_layer = evidence
.root_layer
.as_ref()
.ok_or_else(|| anyhow::anyhow!("no root layer evidence"))?;
CoseKey::from_slice(&root_layer.eca_public_key).map_err(|_cose_err| {
anyhow::anyhow!("couldn't deserialize root layer public key")
})?
};
cose_key_to_verifying_key(&cose_key).map_err(|msg| anyhow::anyhow!(msg))?
};

// Sequentially verify the layers, eventually retrieving the verifying key of
// the last layer.
let last_layer_verifying_key = evidence
.layers
.iter()
.try_fold(root_layer_verifying_key, |previous_layer_verifying_key, current_layer| {
let cert = coset::CoseSign1::from_slice(&current_layer.eca_certificate)
.map_err(|_cose_err| anyhow::anyhow!("couldn't parse certificate"))?;
cert.verify_signature(ADDITIONAL_DATA, |signature, contents| {
let sig = Signature::from_slice(signature)?;
previous_layer_verifying_key.verify(contents, &sig)
})
.map_err(|error| anyhow::anyhow!(error))?;
let payload = cert.payload.ok_or_else(|| anyhow::anyhow!("no cert payload"))?;
let claims = ClaimsSet::from_slice(&payload)
.map_err(|_cose_err| anyhow::anyhow!("couldn't parse claims set"))?;
let cose_key = get_public_key_from_claims_set(&claims)
.map_err(|msg| anyhow::anyhow!(msg))
.context("couldn't get a public key from claims")?;
cose_key_to_verifying_key(&cose_key)
.map_err(|msg| anyhow::anyhow!(msg))
.context("couldn't convert cose key")
})
.context("couldn't verify DICE chain")?;

// Verify the event log claim for this layer if it exists. This is done for all
// layers here, since the event log is tied uniquely closely to the DICE chain.
if let Some(event_log) = &evidence.event_log {
validate_that_event_log_is_captured_in_dice_layers(event_log, &evidence.layers)
.context("events in log do not match the digests in the dice chain")?
} else {
anyhow::bail!("event log is not present in the evidence");
}

Ok(last_layer_verifying_key)
}

/// Verifies signatures of the certificates in the DICE chain and extracts the
/// evidence values from the certificates if the verification is successful.
pub fn verify_dice_chain(evidence: &Evidence) -> anyhow::Result<ExtractedEvidence> {
// TODO: b/356631464 - Remove this function once all clients use verification
// policies.
pub fn verify_dice_chain_and_extract_evidence(
evidence: &Evidence,
) -> anyhow::Result<ExtractedEvidence> {
let root_layer_verifying_key = {
let cose_key = {
let root_layer = evidence
Expand Down Expand Up @@ -163,8 +258,7 @@ pub fn verify_dice_chain(evidence: &Evidence) -> anyhow::Result<ExtractedEvidenc
})
.context("getting last layer key")?;

// Finally, use the last layer's verification key to verify the application
// keys.
// Use the last layer's verification key to verify the application keys.
{
let appl_keys = evidence
.application_keys
Expand Down
Loading

0 comments on commit 292b4f0

Please sign in to comment.