use super::{Quote, QuoteError};
use openssl::pkey::{PKey, Public};
use openssl::{hash::MessageDigest, sha::Sha256, sign::Verifier};
use thiserror::Error;
use tss_esapi::structures::{Attest, AttestInfo};
use tss_esapi::traits::UnMarshall;
#[non_exhaustive]
#[derive(Error, Debug)]
pub enum VerifyError {
#[error("tss error")]
Tss(#[from] tss_esapi::Error),
#[error("openssl error")]
OpenSsl(#[from] openssl::error::ErrorStack),
#[error("quote is not signed by key")]
SignatureMismatch,
#[error("nonce mismatch")]
NonceMismatch,
#[error("quote error")]
Quote(#[from] QuoteError),
#[error("pcr mismatch")]
PcrMismatch,
}
impl Quote {
pub fn verify(&self, pub_key: &PKey<Public>, nonce: &[u8]) -> Result<(), VerifyError> {
self.verify_signature(pub_key)?;
let quote_nonce = &self.nonce()?;
if nonce != quote_nonce {
return Err(VerifyError::NonceMismatch);
}
self.verify_pcrs()?;
Ok(())
}
pub fn verify_signature(&self, pub_key: &PKey<Public>) -> Result<(), VerifyError> {
let mut verifier = Verifier::new(MessageDigest::sha256(), pub_key)?;
verifier.update(&self.message)?;
let is_verified = verifier.verify(&self.signature)?;
if !is_verified {
return Err(VerifyError::SignatureMismatch);
}
Ok(())
}
pub fn verify_pcrs(&self) -> Result<(), VerifyError> {
let attest = Attest::unmarshall(&self.message)?;
let AttestInfo::Quote { info } = attest.attested() else {
return Err(VerifyError::Quote(QuoteError::NotAQuote));
};
let pcr_digest = info.pcr_digest();
let mut hasher = Sha256::new();
for pcr in self.pcrs.iter() {
hasher.update(pcr);
}
let digest = hasher.finish();
if digest[..] != pcr_digest[..] {
return Err(VerifyError::PcrMismatch);
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[cfg(feature = "verifier")]
#[test]
fn test_quote_validation() {
let pem = include_bytes!("../../test/akpub.pem");
let pkey = PKey::public_key_from_pem(pem).unwrap();
let quote_bytes = include_bytes!("../../test/quote.bin");
let quote: Quote = bincode::deserialize(quote_bytes).unwrap();
let nonce = "challenge".as_bytes().to_vec();
let result = quote.verify(&pkey, &nonce);
assert!(result.is_ok(), "Quote verification should not fail");
let mut wrong_quote = quote.clone();
wrong_quote.signature.reverse();
let result = wrong_quote.verify(&pkey, &nonce);
let error = result.unwrap_err();
assert!(
matches!(error, VerifyError::SignatureMismatch),
"Expected signature mismatch"
);
let nonce = vec![1, 2, 3, 4];
let result = quote.verify(&pkey, &nonce);
let error = result.unwrap_err();
assert!(
matches!(error, VerifyError::NonceMismatch),
"Expected nonce verification error"
);
}
#[test]
fn test_pcr_values() {
let quote_bytes = include_bytes!("../../test/quote.bin");
let quote: Quote = bincode::deserialize(quote_bytes).unwrap();
let result = quote.verify_pcrs();
assert!(result.is_ok(), "PCR verification should not fail");
}
}