virtfw_libefi/efivar/
auth.rs1extern crate alloc;
3use alloc::vec;
4use alloc::vec::Vec;
5
6use core::mem::size_of;
7use uguid::Guid;
8use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
9
10use crate::efivar::types::EfiVar;
11use crate::guids::{EfiCertPkcs7, RawGuid};
12use crate::types::EfiTime;
13
14pub const WIN_CERT_TYPE_EFI_GUID: u16 = 0x0EF1;
15
16#[repr(C)]
18#[derive(Debug, Clone, Copy, FromBytes, IntoBytes, KnownLayout, Immutable)]
19pub struct EfiVarAuth2Header {
20 pub time: EfiTime,
21 pub length: u32,
22 pub revision: u16,
23 pub certtype: u16,
24 pub guid: RawGuid,
25}
26
27pub struct EfiVarAuth2<'a> {
28 header: EfiVarAuth2Header,
29 pkcs7: &'a [u8],
30 data: &'a [u8],
31}
32
33impl<'a> EfiVarAuth2<'a> {
34 pub fn new_from_bytes(auth: &'a [u8]) -> Option<Self> {
35 let (header, _) = EfiVarAuth2Header::read_from_prefix(auth).ok()?;
36 if header.revision != 0x0200 || header.certtype != WIN_CERT_TYPE_EFI_GUID {
37 return None;
38 }
39
40 let guid: Guid = header.guid.into();
41 if guid != EfiCertPkcs7 {
42 return None;
43 };
44
45 let pkcs7_offset = size_of::<EfiVarAuth2Header>();
46 let data_offset = 16 + header.length as usize;
47 let pkcs7 = auth.get(pkcs7_offset..data_offset)?;
48 let data = auth.get(data_offset..)?;
49
50 Some(Self {
51 header,
52 pkcs7,
53 data,
54 })
55 }
56
57 pub fn new_from_data(time: &EfiTime, data: &'a [u8]) -> Self {
58 let length = size_of::<EfiVarAuth2Header>() as u32 - 16;
59 let header = EfiVarAuth2Header {
60 time: *time,
61 length,
62 revision: 0x0200,
63 certtype: WIN_CERT_TYPE_EFI_GUID,
64 guid: EfiCertPkcs7.into(),
65 };
66
67 Self {
68 header,
69 pkcs7: &[],
70 data,
71 }
72 }
73
74 pub fn get_pkcs7(&self) -> Vec<u8> {
76 let wrap: [u8; 13] = [
77 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02, 0xa0, 0x82,
78 ];
79
80 if self.pkcs7.is_empty() {
81 return self.pkcs7.to_vec();
82 }
83 if self.pkcs7.get(4..17) == Some(&wrap) {
84 return self.pkcs7.to_vec();
85 }
86
87 let data_len_inner = self.pkcs7.len() as u16;
88 let data_len_outer = data_len_inner + 15;
89
90 let mut pkcs7 = vec![0x30, 0x82];
95 pkcs7.extend_from_slice(&data_len_outer.to_be_bytes());
96 pkcs7.extend_from_slice(&wrap);
97 pkcs7.extend_from_slice(&data_len_inner.to_be_bytes());
98 pkcs7.extend_from_slice(self.pkcs7);
99 pkcs7
100 }
101
102 pub fn get_signed_data(&self, variable: &EfiVar) -> Vec<u8> {
104 let mut sdata: Vec<u8> = Vec::new();
105
106 let name_bytes: Vec<u8> = variable
108 .name()
109 .encode_utf16()
110 .flat_map(|u| u.to_le_bytes())
111 .collect();
112 sdata.extend_from_slice(&name_bytes);
113
114 sdata.extend_from_slice(&variable.guid().to_bytes());
116
117 let attr: u32 = variable.attr().into();
119 sdata.extend_from_slice(&attr.to_le_bytes());
120
121 sdata.extend_from_slice(self.header.time.as_bytes());
123
124 sdata.extend_from_slice(self.data);
126
127 sdata
128 }
129
130 pub fn header(&self) -> &EfiVarAuth2Header {
131 &self.header
132 }
133
134 pub fn data(&self) -> &[u8] {
135 self.data
136 }
137}
138
139impl From<&EfiVarAuth2<'_>> for Vec<u8> {
140 fn from(authvar: &EfiVarAuth2) -> Vec<u8> {
141 let mut auth = Vec::new();
142 auth.extend_from_slice(authvar.header.as_bytes());
143 auth.extend_from_slice(authvar.pkcs7);
144 auth.extend_from_slice(authvar.data);
145 auth
146 }
147}
148
149pub fn auth_to_esl(auth: &[u8]) -> Option<(EfiTime, &[u8])> {
151 let authvar = EfiVarAuth2::new_from_bytes(auth)?;
152 Some((authvar.header.time, authvar.data))
153}
154
155pub fn esl_to_auth(time: &EfiTime, esl: &[u8]) -> Vec<u8> {
157 let authvar = &EfiVarAuth2::new_from_data(time, esl);
158 authvar.into()
159}