1use guid::Guid;
10use std::borrow::Cow;
11use std::collections::BTreeSet;
12use thiserror::Error;
13use uefi_specs::uefi::nvram::signature_list::EFI_CERT_SHA256_GUID;
14use uefi_specs::uefi::nvram::signature_list::EFI_CERT_X509_GUID;
15use uefi_specs::uefi::nvram::signature_list::EFI_SIGNATURE_DATA;
16use uefi_specs::uefi::nvram::signature_list::EFI_SIGNATURE_LIST;
17use zerocopy::FromBytes;
18use zerocopy::IntoBytes;
19
20#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
21pub struct X509Data<'a>(pub Cow<'a, [u8]>);
22
23#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
24pub struct Sha256Data<'a>(pub Cow<'a, [u8; 32]>);
25
26pub enum SignatureDataPayload<'a> {
28 X509(&'a [u8]),
29 Sha256(&'a [u8; 32]),
30}
31
32#[derive(Debug, PartialEq, Eq)]
34pub struct SignatureData<T> {
35 pub header: EFI_SIGNATURE_DATA,
36 pub data: T,
37}
38
39impl SignatureData<Sha256Data<'_>> {
40 pub fn new_sha256(owner: Guid, data: Cow<'_, [u8; 32]>) -> SignatureData<Sha256Data<'_>> {
42 SignatureData {
43 header: EFI_SIGNATURE_DATA {
44 signature_owner: owner,
45 },
46 data: Sha256Data(data),
47 }
48 }
49}
50
51impl SignatureData<X509Data<'_>> {
52 pub fn new_x509(owner: Guid, data: Cow<'_, [u8]>) -> SignatureData<X509Data<'_>> {
54 SignatureData {
55 header: EFI_SIGNATURE_DATA {
56 signature_owner: owner,
57 },
58 data: X509Data(data),
59 }
60 }
61}
62
63impl SignatureData<Sha256Data<'_>> {
64 fn extend_as_spec_signature_data(&self, v: &mut Vec<u8>) {
65 v.extend(self.header.as_bytes());
66 v.extend(self.data.0.as_bytes());
67 }
68}
69
70impl SignatureData<X509Data<'_>> {
71 fn extend_as_spec_signature_data(&self, v: &mut Vec<u8>) {
72 v.extend(self.header.as_bytes());
73 v.extend(self.data.0.as_bytes());
74 }
75}
76
77#[derive(Debug, PartialEq, Eq)]
79pub enum SignatureList<'a> {
80 Sha256(Vec<SignatureData<Sha256Data<'a>>>),
81 X509(SignatureData<X509Data<'a>>),
87}
88
89impl SignatureList<'_> {
90 pub fn extend_as_spec_signature_list(&self, res: &mut Vec<u8>) {
92 let (signature_type, sig_data_size, multiplier) = match &self {
93 SignatureList::Sha256(sigs) => (EFI_CERT_SHA256_GUID, 32, sigs.len()),
94 SignatureList::X509(sig) => (EFI_CERT_X509_GUID, sig.data.0.len(), 1),
95 };
96
97 let signature_size = size_of::<EFI_SIGNATURE_DATA>() + sig_data_size;
98
99 let header = EFI_SIGNATURE_LIST {
100 signature_type,
101 signature_list_size: (size_of::<EFI_SIGNATURE_LIST>() + (signature_size * multiplier))
102 as u32,
103 signature_header_size: 0, signature_size: signature_size as u32,
105 };
106
107 res.extend(header.as_bytes());
108 match self {
109 SignatureList::Sha256(sigs) => {
110 for sig in sigs {
111 sig.extend_as_spec_signature_data(res)
112 }
113 }
114 SignatureList::X509(sig) => sig.extend_as_spec_signature_data(res),
115 }
116 }
117}
118
119#[derive(Debug, Error)]
121pub enum ParseError {
122 #[error("could not read signature list header")]
123 InvalidHeader,
124 #[error("unsupported signature type: {0}")]
125 UnsupportedSignatureType(Guid),
126 #[error("buffer contains less data than specified in EFI_SIGNATURE_LIST header")]
127 TruncatedData,
128
129 #[error("invalid signature_size specified for sha256 (expected 32 + 16, got {0})")]
130 Sha256InvalidSigSize(u32),
131 #[error("unexpected end of buffer while reading sha256 EFI_SIGNATURE_DATA header")]
132 Sha256InvalidHeader,
133 #[error("unexpected end of buffer while reading sha256 EFI_SIGNATURE_DATA data")]
134 Sha256TruncatedData,
135
136 #[error("invalid signature_size specified for x509 (expected {0}, got {1})")]
137 X509InvalidSigSize(u32, u32),
138 #[error("unexpected end of buffer while reading x509 EFI_SIGNATURE_DATA header")]
139 X509InvalidHeader,
140}
141
142pub struct ParseSignatureLists<'a> {
144 buf: &'a [u8],
145}
146
147impl<'a> ParseSignatureLists<'a> {
148 pub fn new(buf: &'a [u8]) -> ParseSignatureLists<'a> {
150 ParseSignatureLists { buf }
151 }
152
153 pub fn collect_signature_lists<F>(
159 self,
160 mut filter: F,
161 ) -> Result<Vec<SignatureList<'a>>, ParseError>
162 where
163 F: FnMut(EFI_SIGNATURE_DATA, SignatureDataPayload<'_>) -> bool,
164 {
165 let mut lists = Vec::new();
166
167 for list in self {
168 let list = list?;
169 let list = match list {
170 ParseSignatureList::X509(mut certs) => {
171 let cert = certs.next().unwrap()?;
172 assert!(certs.next().is_none());
173
174 if !filter(cert.header, SignatureDataPayload::X509(&cert.data.0)) {
175 continue;
176 }
177
178 SignatureList::X509(cert)
179 }
180 ParseSignatureList::Sha256(sigs) => {
181 let mut list = Vec::new();
182 for sig in sigs {
183 let sig = sig?;
184
185 if !filter(sig.header, SignatureDataPayload::Sha256(&sig.data.0)) {
186 continue;
187 }
188
189 list.push(sig);
190 }
191
192 if list.is_empty() {
195 continue;
196 }
197
198 SignatureList::Sha256(list)
199 }
200 };
201 lists.push(list)
202 }
203
204 Ok(lists)
205 }
206
207 pub fn collect_signature_set(
209 self,
210 ) -> Result<BTreeSet<(EFI_SIGNATURE_DATA, Cow<'a, [u8]>)>, ParseError> {
211 let mut sig_set = BTreeSet::new();
212
213 for list in self {
214 let list = list?;
215 match list {
216 ParseSignatureList::X509(mut certs) => {
217 let cert = certs.next().unwrap()?;
218 assert!(certs.next().is_none());
219 sig_set.insert((cert.header, cert.data.0));
220 }
221 ParseSignatureList::Sha256(sigs) => {
222 for sig in sigs {
223 let sig = sig?;
224 sig_set.insert((
225 sig.header,
226 match sig.data.0 {
227 Cow::Borrowed(a) => Cow::Borrowed(a),
228 Cow::Owned(a) => Cow::Owned(a.to_vec()),
229 },
230 ));
231 }
232 }
233 };
234 }
235
236 Ok(sig_set)
237 }
238
239 fn next_inner(&mut self) -> Result<Option<ParseSignatureList<'a>>, ParseError> {
240 if self.buf.is_empty() {
241 return Ok(None);
242 }
243
244 let (
245 EFI_SIGNATURE_LIST {
246 signature_type,
247 signature_list_size,
248 signature_header_size: _,
249 signature_size,
250 },
251 buf,
252 ) = EFI_SIGNATURE_LIST::read_from_prefix(self.buf)
253 .map_err(|_| ParseError::InvalidHeader)?; let expected_data_len = signature_list_size as usize - size_of::<EFI_SIGNATURE_LIST>();
256 if buf.len() < expected_data_len {
257 return Err(ParseError::TruncatedData);
258 }
259 let (data, buf) = buf.split_at(expected_data_len);
260
261 let res = match signature_type {
262 EFI_CERT_SHA256_GUID => {
263 ParseSignatureList::Sha256(ParseSignatureSha256::new(data, signature_size)?)
264 }
265 EFI_CERT_X509_GUID => {
266 ParseSignatureList::X509(ParseSignatureX509::new(data, signature_size)?)
267 }
268 sig => return Err(ParseError::UnsupportedSignatureType(sig)),
269 };
270
271 self.buf = buf;
272
273 Ok(Some(res))
274 }
275}
276
277impl<'a> Iterator for ParseSignatureLists<'a> {
278 type Item = Result<ParseSignatureList<'a>, ParseError>;
279
280 fn next(&mut self) -> Option<Result<ParseSignatureList<'a>, ParseError>> {
281 self.next_inner().transpose()
282 }
283}
284
285pub enum ParseSignatureList<'a> {
287 X509(ParseSignatureX509<'a>),
288 Sha256(ParseSignatureSha256<'a>),
289}
290
291pub struct ParseSignatureX509<'a> {
293 buf: &'a [u8],
294}
295
296impl<'a> ParseSignatureX509<'a> {
297 fn new(buf: &'a [u8], signature_size: u32) -> Result<ParseSignatureX509<'a>, ParseError> {
298 if buf.len() != signature_size as usize {
299 return Err(ParseError::X509InvalidSigSize(
300 signature_size,
301 buf.len() as u32,
302 ));
303 }
304
305 Ok(ParseSignatureX509 { buf })
306 }
307
308 fn next_inner(&mut self) -> Result<Option<SignatureData<X509Data<'a>>>, ParseError> {
312 if self.buf.is_empty() {
313 return Ok(None);
314 }
315
316 let (header, buf) = EFI_SIGNATURE_DATA::read_from_prefix(self.buf)
317 .map_err(|_| ParseError::X509InvalidHeader)?; let val: Cow<'a, [u8]> = buf.into();
320 let res = SignatureData::new_x509(header.signature_owner, val);
321
322 self.buf = &[];
323 Ok(Some(res))
324 }
325}
326
327impl<'a> Iterator for ParseSignatureX509<'a> {
328 type Item = Result<SignatureData<X509Data<'a>>, ParseError>;
329
330 fn next(&mut self) -> Option<Result<SignatureData<X509Data<'a>>, ParseError>> {
331 self.next_inner().transpose()
332 }
333}
334
335pub struct ParseSignatureSha256<'a> {
337 buf: &'a [u8],
338}
339
340impl<'a> ParseSignatureSha256<'a> {
341 fn new(buf: &'a [u8], signature_size: u32) -> Result<ParseSignatureSha256<'a>, ParseError> {
342 let expected_signature_size = 32 + size_of::<EFI_SIGNATURE_DATA>();
343
344 if signature_size != expected_signature_size as u32 {
345 return Err(ParseError::Sha256InvalidSigSize(signature_size));
346 }
347
348 if buf.len() % expected_signature_size != 0 {
351 return Err(ParseError::Sha256TruncatedData);
352 }
353
354 Ok(ParseSignatureSha256 { buf })
355 }
356
357 fn next_inner(&mut self) -> Result<Option<SignatureData<Sha256Data<'a>>>, ParseError> {
358 if self.buf.is_empty() {
359 return Ok(None);
360 }
361
362 let (header, buf) =
363 EFI_SIGNATURE_DATA::read_from_prefix(self.buf).expect("buf size validated in `new`"); let expected_data_len = 32;
366 assert!(buf.len() >= expected_data_len, "validated in new()");
367 let (signature, buf) = buf.split_at(expected_data_len);
368
369 let val: &'a [u8; 32] = signature.try_into().unwrap();
370 let res = SignatureData::new_sha256(header.signature_owner, Cow::Borrowed(val));
371
372 self.buf = buf;
373 Ok(Some(res))
374 }
375}
376
377impl<'a> Iterator for ParseSignatureSha256<'a> {
378 type Item = Result<SignatureData<Sha256Data<'a>>, ParseError>;
379
380 fn next(&mut self) -> Option<Result<SignatureData<Sha256Data<'a>>, ParseError>> {
381 self.next_inner().transpose()
382 }
383}
384
385#[cfg(test)]
386mod test {
387 use super::*;
388
389 const OWNER_1: Guid = Guid {
390 data1: 1,
391 data2: 0,
392 data3: 0,
393 data4: [0, 0, 0, 0, 0, 0, 0, 0],
394 };
395
396 const OWNER_2: Guid = Guid {
397 data1: 2,
398 data2: 0,
399 data3: 0,
400 data4: [0, 0, 0, 0, 0, 0, 0, 0],
401 };
402
403 fn test_data() -> Vec<SignatureList<'static>> {
404 vec![
405 SignatureList::Sha256(vec![
406 SignatureData::new_sha256(OWNER_1, Cow::Owned([0; 32])),
407 SignatureData::new_sha256(OWNER_2, Cow::Owned([1; 32])),
408 SignatureData::new_sha256(OWNER_1, Cow::Owned([2; 32])),
409 ]),
410 SignatureList::X509(SignatureData::new_x509(
411 OWNER_2,
412 b"some cert data"[..].into(),
413 )),
414 SignatureList::Sha256(vec![
415 SignatureData::new_sha256(OWNER_1, Cow::Owned([0; 32])),
416 SignatureData::new_sha256(OWNER_2, Cow::Owned([1; 32])),
417 ]),
418 SignatureList::X509(SignatureData::new_x509(
419 OWNER_1,
420 b"more cert data"[..].into(),
421 )),
422 ]
423 }
424
425 fn test_data_no_owner_1() -> Vec<SignatureList<'static>> {
426 vec![
427 SignatureList::Sha256(vec![SignatureData::new_sha256(
428 OWNER_2,
429 Cow::Owned([1; 32]),
430 )]),
431 SignatureList::X509(SignatureData::new_x509(
432 OWNER_2,
433 b"some cert data"[..].into(),
434 )),
435 SignatureList::Sha256(vec![SignatureData::new_sha256(
436 OWNER_2,
437 Cow::Owned([1; 32]),
438 )]),
439 ]
440 }
441
442 fn dump_to_vec(lists: Vec<SignatureList<'_>>) -> Vec<u8> {
443 let mut buf = Vec::new();
444 for l in &lists {
445 l.extend_as_spec_signature_list(&mut buf)
446 }
447 buf
448 }
449
450 #[test]
451 fn roundtrip() {
452 let lists = test_data();
453
454 let mut buf = Vec::new();
456 for l in &lists {
457 l.extend_as_spec_signature_list(&mut buf)
458 }
459
460 let new_lists = ParseSignatureLists::new(&buf)
462 .collect_signature_lists(|_, _| true)
463 .unwrap();
464
465 assert_eq!(lists, new_lists);
466 }
467
468 #[test]
469 fn filter() {
470 let lists = test_data();
471 let lists_no_owner_1 = test_data_no_owner_1();
472
473 let lists_buf = dump_to_vec(lists);
474 let new_lists_no_owner_1 = ParseSignatureLists::new(&lists_buf)
475 .collect_signature_lists(|header, _| header.signature_owner != OWNER_1)
476 .unwrap();
477
478 assert_eq!(lists_no_owner_1, new_lists_no_owner_1);
479 }
480}