use crate::device::Device;
use crate::key::{HashAlg, Key};
use crate::{check_rc, sys, Result, TpmError};
use alloc::vec;
use alloc::vec::Vec;
const PCR_COUNT: u32 = 24;
pub struct Attestation {
pub attest: Vec<u8>,
pub sig_alg: u16,
pub signature: Vec<u8>,
}
impl Device {
pub fn certify(
&self,
object: &Key<'_>,
signer: &Key<'_>,
qualifying: &[u8],
) -> Result<Attestation> {
if crate::session::is_active() {
return Err(TpmError(crate::E_SESSION_IN_USE));
}
let is_ecc = signer.alg() == sys::TPM_ALG_ID_T_TPM_ALG_ECC as sys::TPM_ALG_ID;
let mut cin: sys::Certify_In = unsafe { core::mem::zeroed() };
if qualifying.len() > cin.qualifyingData.buffer.len() {
return Err(TpmError(crate::BUFFER_E));
}
let (s0, s1) = unsafe {
(
sys::wolfTPM2_SetAuthHandle(self.ptr(), 0, object.handle_ptr()),
sys::wolfTPM2_SetAuthHandle(self.ptr(), 1, signer.handle_ptr()),
)
};
if s0 != 0 || s1 != 0 {
unsafe {
sys::wolfTPM2_UnsetAuth(self.ptr(), 0);
sys::wolfTPM2_UnsetAuth(self.ptr(), 1);
}
return Err(TpmError(if s0 != 0 { s0 } else { s1 }));
}
cin.objectHandle = object.handle();
cin.signHandle = signer.handle();
cin.inScheme.scheme = sys::TPM_ALG_ID_T_TPM_ALG_NULL as sys::TPMI_ALG_SIG_SCHEME;
cin.qualifyingData.size = qualifying.len() as u16;
cin.qualifyingData.buffer[..qualifying.len()].copy_from_slice(qualifying);
let mut cout: sys::Certify_Out = unsafe { core::mem::zeroed() };
let rc = unsafe { sys::TPM2_Certify(&mut cin, &mut cout) };
unsafe {
sys::wolfTPM2_UnsetAuth(self.ptr(), 0);
sys::wolfTPM2_UnsetAuth(self.ptr(), 1);
}
check_rc(rc)?;
let asz = cout.certifyInfo.size as usize;
let attest = cout.certifyInfo.attestationData[..asz].to_vec();
let signature = extract_signature(is_ecc, &cout.signature);
Ok(Attestation {
attest,
sig_alg: cout.signature.sigAlg,
signature,
})
}
pub fn quote(
&self,
signer: &Key<'_>,
pcr_indices: &[u32],
hash: HashAlg,
qualifying: &[u8],
) -> Result<Attestation> {
let is_ecc = signer.alg() == sys::TPM_ALG_ID_T_TPM_ALG_ECC as sys::TPM_ALG_ID;
if pcr_indices.is_empty() || pcr_indices.iter().any(|&i| i >= PCR_COUNT) {
return Err(TpmError(crate::BUFFER_E));
}
let mut qin: sys::Quote_In = unsafe { core::mem::zeroed() };
if qualifying.len() > qin.qualifyingData.buffer.len() {
return Err(TpmError(crate::BUFFER_E));
}
for &idx in pcr_indices {
unsafe {
sys::TPM2_SetupPCRSel(&mut qin.PCRselect, hash.alg_id(), idx as core::ffi::c_int)
};
}
unsafe { sys::wolfTPM2_SetAuthHandle(self.ptr(), 0, signer.handle_ptr()) };
qin.signHandle = signer.handle();
qin.inScheme.scheme = sys::TPM_ALG_ID_T_TPM_ALG_NULL as sys::TPMI_ALG_SIG_SCHEME;
qin.qualifyingData.size = qualifying.len() as u16;
qin.qualifyingData.buffer[..qualifying.len()].copy_from_slice(qualifying);
let mut qout: sys::Quote_Out = unsafe { core::mem::zeroed() };
let rc = unsafe { sys::TPM2_Quote(&mut qin, &mut qout) };
unsafe { sys::wolfTPM2_UnsetAuth(self.ptr(), 0) };
check_rc(rc)?;
let asz = qout.quoted.size as usize;
let attest = qout.quoted.attestationData[..asz].to_vec();
let signature = extract_signature(is_ecc, &qout.signature);
Ok(Attestation {
attest,
sig_alg: qout.signature.sigAlg,
signature,
})
}
}
const ECC_P256_COORD: usize = 32;
fn extract_signature(is_ecc: bool, sig: &sys::TPMT_SIGNATURE) -> Vec<u8> {
unsafe {
if is_ecc {
let e = &sig.signature.ecdsa;
let coord = (e.signatureR.size as usize)
.max(e.signatureS.size as usize)
.max(ECC_P256_COORD);
let mut v = vec![0u8; coord * 2];
put_left_padded(&mut v[..coord], &e.signatureR.buffer, e.signatureR.size);
put_left_padded(&mut v[coord..], &e.signatureS.buffer, e.signatureS.size);
v
} else {
let r = &sig.signature.rsassa;
r.sig.buffer[..r.sig.size as usize].to_vec()
}
}
}
fn put_left_padded(dst: &mut [u8], src: &[u8], len: u16) {
let n = (len as usize).min(dst.len());
let start = dst.len() - n;
dst[start..].copy_from_slice(&src[..n]);
}