extern crate alloc;
use alloc::vec;
use alloc::vec::Vec;
use core::mem::size_of;
use uguid::Guid;
use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
use crate::efivar::types::EfiVar;
use crate::guids::{EfiCertPkcs7, RawGuid};
use crate::types::EfiTime;
pub const WIN_CERT_TYPE_EFI_GUID: u16 = 0x0EF1;
#[repr(C)]
#[derive(Debug, Clone, Copy, FromBytes, IntoBytes, KnownLayout, Immutable)]
pub struct EfiVarAuth2Header {
pub time: EfiTime,
pub length: u32,
pub revision: u16,
pub certtype: u16,
pub guid: RawGuid,
}
pub struct EfiVarAuth2<'a> {
header: EfiVarAuth2Header,
pkcs7: &'a [u8],
data: &'a [u8],
}
impl<'a> EfiVarAuth2<'a> {
pub fn new_from_bytes(auth: &'a [u8]) -> Option<Self> {
let (header, _) = EfiVarAuth2Header::read_from_prefix(auth).ok()?;
if header.revision != 0x0200 || header.certtype != WIN_CERT_TYPE_EFI_GUID {
return None;
}
let guid: Guid = header.guid.into();
if guid != EfiCertPkcs7 {
return None;
};
let pkcs7_offset = size_of::<EfiVarAuth2Header>();
let data_offset = 16 + header.length as usize;
let pkcs7 = auth.get(pkcs7_offset..data_offset)?;
let data = auth.get(data_offset..)?;
Some(Self {
header,
pkcs7,
data,
})
}
pub fn new_from_data(time: &EfiTime, data: &'a [u8]) -> Self {
let length = size_of::<EfiVarAuth2Header>() as u32 - 16;
let header = EfiVarAuth2Header {
time: *time,
length,
revision: 0x0200,
certtype: WIN_CERT_TYPE_EFI_GUID,
guid: EfiCertPkcs7.into(),
};
Self {
header,
pkcs7: &[],
data,
}
}
pub fn get_pkcs7(&self) -> Vec<u8> {
let wrap: [u8; 13] = [
0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02, 0xa0, 0x82,
];
if self.pkcs7.is_empty() {
return self.pkcs7.to_vec();
}
if self.pkcs7.get(4..17) == Some(&wrap) {
return self.pkcs7.to_vec();
}
let data_len_inner = self.pkcs7.len() as u16;
let data_len_outer = data_len_inner + 15;
let mut pkcs7 = vec![0x30, 0x82];
pkcs7.extend_from_slice(&data_len_outer.to_be_bytes());
pkcs7.extend_from_slice(&wrap);
pkcs7.extend_from_slice(&data_len_inner.to_be_bytes());
pkcs7.extend_from_slice(self.pkcs7);
pkcs7
}
pub fn get_signed_data(&self, variable: &EfiVar) -> Vec<u8> {
let mut sdata: Vec<u8> = Vec::new();
let name_bytes: Vec<u8> = variable
.name()
.encode_utf16()
.flat_map(|u| u.to_le_bytes())
.collect();
sdata.extend_from_slice(&name_bytes);
sdata.extend_from_slice(&variable.guid().to_bytes());
let attr: u32 = variable.attr().into();
sdata.extend_from_slice(&attr.to_le_bytes());
sdata.extend_from_slice(self.header.time.as_bytes());
sdata.extend_from_slice(self.data);
sdata
}
pub fn header(&self) -> &EfiVarAuth2Header {
&self.header
}
pub fn data(&self) -> &[u8] {
self.data
}
}
impl From<&EfiVarAuth2<'_>> for Vec<u8> {
fn from(authvar: &EfiVarAuth2) -> Vec<u8> {
let mut auth = Vec::new();
auth.extend_from_slice(authvar.header.as_bytes());
auth.extend_from_slice(authvar.pkcs7);
auth.extend_from_slice(authvar.data);
auth
}
}
pub fn auth_to_esl(auth: &[u8]) -> Option<(EfiTime, &[u8])> {
let authvar = EfiVarAuth2::new_from_bytes(auth)?;
Some((authvar.header.time, authvar.data))
}
pub fn esl_to_auth(time: &EfiTime, esl: &[u8]) -> Vec<u8> {
let authvar = &EfiVarAuth2::new_from_data(time, esl);
authvar.into()
}