Skip to content
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
20 changes: 10 additions & 10 deletions 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 Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ members = [
]

[workspace.package]
version = "1.6.0"
version = "1.6.1"
edition = "2021"
license = "BUSL-1.1" # "Business Source License 1.1"
license-file = "LICENSE"
Expand Down
70 changes: 16 additions & 54 deletions crate/tpm_quote/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
use crate::key::{get_key_from_persistent_handle, TPM_AK_NVINDEX};
use error::Error;
use policy::TpmPolicy;
use sha2::Digest;
use std::convert::TryInto;
use tss_esapi::{
interface_types::algorithm::HashingAlgorithm,
structures::{
Attest, AttestInfo, PcrSelectionListBuilder, PcrSlot, Public, QuoteInfo, Signature,
},
structures::{Attest, AttestInfo, PcrSelectionListBuilder, PcrSlot, Public, Signature},
traits::{Marshall, UnMarshall},
Context,
};
use verify::verify_quote_signature;
use verify::{verify_pcr_value, verify_quote_policy, verify_quote_signature};

pub mod command;
pub mod convert;
Expand Down Expand Up @@ -85,6 +82,20 @@ pub fn get_quote(
))
}

/// Extract the digest of PCRs in TPM `quote`.
///
/// # Returns
///
/// Either [`Vec<u8>`] or [`Error`].
pub fn get_pcr_digest_from_quote(quote: &[u8]) -> Result<Vec<u8>, Error> {
let attestation_data = Attest::unmarshall(quote)?;
let AttestInfo::Quote { info: quote_info } = attestation_data.attested() else {
return Err(Error::QuoteError("unexpected attestion type".to_owned()));
};

Ok(quote_info.pcr_digest().to_owned().to_vec())
}

/// Verify signature of the `quote` with `public_key`` and `nonce`.
///
/// # Returns
Expand Down Expand Up @@ -127,55 +138,6 @@ pub fn verify_quote(
Ok(())
}

/// Verify the quote against expected values
pub(crate) fn verify_quote_policy(
attestation_data: &Attest,
policy: &TpmPolicy,
) -> Result<(), Error> {
if let Some(reset_count) = policy.reset_count {
if attestation_data.clock_info().reset_count() != reset_count {
return Err(Error::VerificationError(format!(
"Attestation reset count '{}' is not equal to the set value '{}'",
attestation_data.clock_info().reset_count(),
reset_count
)));
}
}

if let Some(restart_count) = policy.restart_count {
if attestation_data.clock_info().restart_count() != restart_count {
return Err(Error::VerificationError(format!(
"Attestation restart count '{}' is not equal to the set value '{}'",
attestation_data.clock_info().restart_count(),
restart_count
)));
}
}

Ok(())
}

/// Verify the digest of the `pcr_value`.
///
/// # Returns
///
/// Either [`()`] or [`Error`].
pub fn verify_pcr_value(quote_info: &QuoteInfo, pcr_value: &[u8]) -> Result<(), Error> {
let hpcr_value: [u8; 32] = quote_info.pcr_digest().to_owned().try_into()?;

let expected_hpcr_value = sha2::Sha256::digest(pcr_value).to_vec();
if expected_hpcr_value != hpcr_value[..] {
return Err(Error::VerificationError(format!(
"Bad Hash(PCR digest) in quote '{}', expected: '{}' from '{}'",
hex::encode(hpcr_value),
hex::encode(expected_hpcr_value),
hex::encode(pcr_value),
)));
}

Ok(())
}

#[cfg(test)]
mod tests {
use std::str::FromStr;
Expand Down
52 changes: 50 additions & 2 deletions crate/tpm_quote/src/verify.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
use crate::error::Error;
use crate::{error::Error, policy::TpmPolicy};

use std::convert::TryInto;

use sha2::Digest;

use p256::ecdsa::{signature::Verifier, VerifyingKey};
use tss_esapi::{
interface_types::{
algorithm::HashingAlgorithm, ecc::EccCurve, structure_tags::AttestationType,
},
structures::{Attest, EccScheme, Public, Signature},
structures::{Attest, EccScheme, Public, QuoteInfo, Signature},
traits::Marshall,
};

/// Verify signature of TPM attestation with public key `pk`.
pub(crate) fn verify_quote_signature(
attestation_data: &Attest,
signature: &Signature,
Expand Down Expand Up @@ -107,3 +110,48 @@ pub(crate) fn verify_quote_signature(

Ok(nonce)
}

/// Verify the quote against expected values in `TpmPolicy`.
pub(crate) fn verify_quote_policy(
attestation_data: &Attest,
policy: &TpmPolicy,
) -> Result<(), Error> {
if let Some(reset_count) = policy.reset_count {
if attestation_data.clock_info().reset_count() != reset_count {
return Err(Error::VerificationError(format!(
"Attestation reset count '{}' is not equal to the set value '{}'",
attestation_data.clock_info().reset_count(),
reset_count
)));
}
}

if let Some(restart_count) = policy.restart_count {
if attestation_data.clock_info().restart_count() != restart_count {
return Err(Error::VerificationError(format!(
"Attestation restart count '{}' is not equal to the set value '{}'",
attestation_data.clock_info().restart_count(),
restart_count
)));
}
}

Ok(())
}

/// Verify the digest of PCRs in `QuoteInfo` against expected `pcr_value`.
pub(crate) fn verify_pcr_value(quote_info: &QuoteInfo, pcr_value: &[u8]) -> Result<(), Error> {
let hpcr_value: [u8; 32] = quote_info.pcr_digest().to_owned().try_into()?;

let expected_hpcr_value = sha2::Sha256::digest(pcr_value).to_vec();
if expected_hpcr_value != hpcr_value[..] {
return Err(Error::VerificationError(format!(
"Bad Hash(PCR digest) in quote '{}', expected: '{}' from '{}'",
hex::encode(hpcr_value),
hex::encode(expected_hpcr_value),
hex::encode(pcr_value),
)));
}

Ok(())
}
Loading