#[cfg(feature = "non-pdk")]
use anyhow::Result;
use borsh::{BorshDeserialize, BorshSerialize};
#[cfg(feature = "non-pdk")]
use fastcrypto::{hash, hash::HashFunction};
#[cfg(feature = "non-pdk")]
use ring::signature::{Ed25519KeyPair, KeyPair, Signature};
use serde::{Deserialize, Serialize};
use serde_big_array::BigArray;
use crate::{tee_types::AttestationEvidence, PublicKey};
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, BorshSerialize, BorshDeserialize)]
pub struct AttestationReport {
pub inner: AttestationReportInner,
#[serde(with = "BigArray")]
pub signature: [u8; 64],
pub platform_attestation: Option<AttestationEvidence>,
}
#[cfg(feature = "non-pdk")]
impl AttestationReport {
pub fn compute_report_hash(inner: &AttestationReportInner) -> [u8; 32] {
let mut hash = hash::Sha256::new();
hash.update(inner.verification_key);
hash.update::<&[u8]>(inner.wrapping_key.as_ref());
hash.finalize().digest
}
pub fn verify_signature(
&self,
digest: &[u8],
) -> Result<VerifiedAttestation, VerificationError> {
ring::signature::UnparsedPublicKey::new(
&ring::signature::ED25519,
&self.inner.verification_key,
)
.verify(digest, &self.signature)
.map_err(|_| VerificationError::SignatureVerification)?;
Ok(VerifiedAttestation::new_verified_dangerous(
self.inner.clone(),
))
}
}
impl Default for AttestationReport {
fn default() -> Self {
Self {
inner: AttestationReportInner::default(),
signature: [0; 64],
platform_attestation: None,
}
}
}
#[derive(
Debug, Clone, Eq, PartialEq, Default, Serialize, Deserialize, BorshSerialize, BorshDeserialize,
)]
pub struct AttestationReportInner {
pub verification_key: [u8; 32],
pub wrapping_key: PublicKey,
}
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub struct VerifiedAttestation {
inner: AttestationReportInner,
}
impl VerifiedAttestation {
pub fn new_verified_dangerous(inner: AttestationReportInner) -> Self {
Self { inner }
}
pub fn verification_key(&self) -> [u8; 32] {
self.inner.verification_key
}
pub fn wrapping_key(&self) -> PublicKey {
self.inner.wrapping_key
}
}
#[derive(Debug, thiserror::Error)]
pub enum VerificationError {
#[error("Failed to verify attestation report's signature")]
SignatureVerification,
#[error("Invalid platform attestation evidence")]
InvalidPlatformAttestation,
#[error("Platform attestation verification failed: {0}")]
PlatformAttestationVerificationFailed(String),
}
#[cfg(feature = "non-pdk")]
pub fn ed25519_signature_to_slice(signature: &Signature) -> [u8; 64] {
let bytes = signature.as_ref();
if bytes.len() != 64 {
unreachable!(
"Ed25519 signatures are always 64 bytes, but got {}",
bytes.len()
);
}
bytes.try_into().unwrap()
}
#[cfg(feature = "non-pdk")]
pub fn ed25519_keypair_to_pubkey_slice(keypair: &Ed25519KeyPair) -> [u8; 32] {
let pubkey = keypair.public_key().as_ref();
if pubkey.len() != 32 {
unreachable!(
"Ed25519 public keys are always 32 bytes, but got {}",
pubkey.len()
);
}
pubkey.try_into().unwrap()
}
#[cfg(all(test, feature = "non-pdk"))]
mod tests {
use rcgen::PKCS_ED25519;
use ring::signature::Ed25519KeyPair;
use super::*;
use crate::tee_types::TeeType;
const TEST_WRAPPING_KEY_BYTES: [u8; 32] = [42u8; 32];
fn test_wrapping_key() -> PublicKey {
PublicKey::from_bytes(TEST_WRAPPING_KEY_BYTES)
}
fn generate_test_keypair() -> Ed25519KeyPair {
let key_pair_der = rcgen::KeyPair::generate_for(&PKCS_ED25519)
.unwrap()
.serialize_der();
Ed25519KeyPair::from_pkcs8(&key_pair_der).unwrap()
}
#[test]
fn test_attestation_report_with_platform_attestation_constructor() {
let key_pair = generate_test_keypair();
let public_key_bytes = ed25519_keypair_to_pubkey_slice(&key_pair);
let platform_evidence = AttestationEvidence {
tee_type: TeeType::SevSnp,
evidence_data: vec![1, 2, 3, 4, 5], };
let inner = AttestationReportInner {
verification_key: public_key_bytes,
wrapping_key: test_wrapping_key(),
};
let digest = AttestationReport::compute_report_hash(&inner);
let signature = ed25519_signature_to_slice(&key_pair.sign(&digest));
let attestation_report = AttestationReport {
inner,
signature,
platform_attestation: Some(platform_evidence.clone()),
};
assert!(attestation_report.platform_attestation.is_some());
let platform_attestation = attestation_report.platform_attestation.unwrap();
assert_eq!(platform_attestation.tee_type, platform_evidence.tee_type);
assert_eq!(
platform_attestation.evidence_data,
platform_evidence.evidence_data
);
}
}