1#![expect(missing_docs)]
7#![forbid(unsafe_code)]
8
9use guid::Guid;
10use mesh_protobuf::Protobuf;
11use thiserror::Error;
12use uefi_specs::uefi::nvram::vars::EFI_GLOBAL_VARIABLE;
13
14pub mod delta;
15
16#[derive(Debug, Clone, Protobuf)]
18#[mesh(transparent)]
19pub struct BaseTemplateJson(Vec<u8>);
20
21impl BaseTemplateJson {
22 pub fn as_bytes(&self) -> &[u8] {
24 &self.0
25 }
26}
27
28impl From<Vec<u8>> for BaseTemplateJson {
29 fn from(json: Vec<u8>) -> Self {
30 Self(json)
31 }
32}
33
34#[derive(Debug, Clone, Protobuf)]
39pub struct BaseTemplate {
40 pub json: BaseTemplateJson,
41}
42
43#[derive(Debug, Clone, Protobuf)]
45#[mesh(transparent)]
46pub struct UefiVarsDeltaJson(Vec<u8>);
47
48impl UefiVarsDeltaJson {
49 pub fn as_bytes(&self) -> &[u8] {
51 &self.0
52 }
53}
54
55impl From<Vec<u8>> for UefiVarsDeltaJson {
56 fn from(json: Vec<u8>) -> Self {
57 Self(json)
58 }
59}
60
61#[derive(Debug, Default, Clone, Protobuf)]
63pub struct UefiVars {
64 pub signatures: Option<Signatures>,
66 pub non_signature_vars: Vec<(String, UefiVar)>,
68}
69
70#[derive(Debug, Default, Clone, Protobuf)]
72#[mesh(transparent)]
73pub struct BaseTemplateVars(UefiVars);
74
75#[derive(Debug)]
77pub struct FinalVars(UefiVars);
78
79impl From<UefiVars> for BaseTemplateVars {
80 fn from(vars: UefiVars) -> Self {
81 Self(vars)
82 }
83}
84
85impl BaseTemplateVars {
86 pub fn signatures(&self) -> Option<&Signatures> {
88 self.0.signatures.as_ref()
89 }
90}
91
92impl FinalVars {
93 pub fn resolve(
95 base_template: Option<BaseTemplateVars>,
96 custom_template_delta: Option<delta::UefiVarsDelta>,
97 ) -> Result<Self, ApplyDeltaError> {
98 let base_vars = base_template.map_or_else(UefiVars::default, |base| base.0);
99 let final_vars = match custom_template_delta {
100 Some(delta) => base_vars.apply_delta(delta)?,
101 None => base_vars,
102 };
103 Ok(Self(final_vars))
104 }
105
106 pub fn into_uefi_vars(self) -> UefiVars {
108 self.0
109 }
110}
111
112#[derive(Debug, Clone, Protobuf)]
113pub struct Signatures {
114 pub pk: Signature,
115 pub kek: Vec<Signature>,
116 pub db: Vec<Signature>,
117 pub dbx: Vec<Signature>,
118 pub moklist: Vec<Signature>,
119 pub moklistx: Vec<Signature>,
120}
121
122#[derive(Debug, Clone, Protobuf)]
123pub enum Signature {
124 X509(Vec<X509Cert>),
125 Sha256(Vec<Sha256Digest>),
126}
127
128#[derive(Debug, Clone, Protobuf)]
129pub struct UefiVar {
130 pub guid: Guid,
131 pub attr: u32,
132 pub value: Vec<u8>,
133}
134
135#[derive(Clone, Protobuf)]
136pub struct X509Cert(pub Vec<u8>);
137
138impl std::fmt::Debug for X509Cert {
139 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
140 f.debug_tuple("X509Cert").field(&"[..]").finish()
141 }
142}
143
144#[derive(Clone, Protobuf)]
145pub struct Sha256Digest(pub [u8; 32]);
146
147impl std::fmt::Debug for Sha256Digest {
148 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
149 f.debug_tuple("Sha256Digest")
150 .field(&self.0.map(|b| format!("{:02x?}", b)).join(""))
151 .finish()
152 }
153}
154
155#[derive(Debug, Error)]
156pub enum ApplyDeltaError {
157 #[error("cannot Append if no base signatures are provided")]
158 AppendWithoutBase,
159 #[error("cannot use \"Default\" variable type if no base signatures are provided")]
160 DefaultWithoutBase,
161 #[error("cannot set restricted variable: {name}:{guid}")]
162 RestrictedUefiVar { name: String, guid: Guid },
163}
164
165impl UefiVars {
166 pub fn new() -> UefiVars {
168 UefiVars::default()
169 }
170
171 pub fn apply_delta(self, delta: delta::UefiVarsDelta) -> Result<UefiVars, ApplyDeltaError> {
173 use delta::SignatureDelta;
174 use delta::SignatureDeltaVec;
175 use delta::SignaturesAppend;
176 use delta::SignaturesDelta;
177 use delta::SignaturesReplace;
178
179 let signatures = match (self.signatures, delta.signatures) {
180 (None, SignaturesDelta::Append(..)) => {
181 return Err(ApplyDeltaError::AppendWithoutBase);
182 }
183 (
184 None,
185 SignaturesDelta::Replace(SignaturesReplace {
186 pk,
187 kek,
188 db,
189 dbx,
190 moklist,
191 moklistx,
192 }),
193 ) => {
194 fn deny_default(sig_delta: SignatureDelta) -> Result<Signature, ApplyDeltaError> {
195 match sig_delta {
196 SignatureDelta::Sig(sig) => Ok(sig),
197 SignatureDelta::Default => Err(ApplyDeltaError::DefaultWithoutBase),
198 }
199 }
200
201 fn deny_default_vec(
202 sig_delta_vec: SignatureDeltaVec,
203 ) -> Result<Vec<Signature>, ApplyDeltaError> {
204 match sig_delta_vec {
205 SignatureDeltaVec::Sigs(sig) => Ok(sig),
206 SignatureDeltaVec::Default => Err(ApplyDeltaError::DefaultWithoutBase),
207 }
208 }
209
210 Signatures {
211 pk: deny_default(pk)?,
212 kek: deny_default_vec(kek)?,
213 db: deny_default_vec(db)?,
214 dbx: deny_default_vec(dbx)?,
215 moklist: moklist
216 .map(deny_default_vec)
217 .transpose()?
218 .unwrap_or_default(),
219 moklistx: moklistx
220 .map(deny_default_vec)
221 .transpose()?
222 .unwrap_or_default(),
223 }
224 }
225 (
226 Some(Signatures {
227 pk,
228 mut kek,
229 mut db,
230 mut dbx,
231 mut moklist,
232 mut moklistx,
233 }),
234 sig_delta,
235 ) => match sig_delta {
236 SignaturesDelta::Append(SignaturesAppend {
237 kek: append_kek,
238 db: append_db,
239 dbx: append_dbx,
240 moklist: append_moklist,
241 moklistx: append_moklistx,
242 }) => {
243 if let Some(append_kek) = append_kek {
244 kek.extend(append_kek);
245 }
246
247 if let Some(append_db) = append_db {
248 db.extend(append_db);
249 }
250
251 if let Some(append_dbx) = append_dbx {
252 dbx.extend(append_dbx);
253 }
254
255 if let Some(append_moklist) = append_moklist {
256 moklist.extend(append_moklist)
257 }
258
259 if let Some(append_moklistx) = append_moklistx {
260 moklistx.extend(append_moklistx)
261 }
262
263 Signatures {
264 pk,
265 kek,
266 db,
267 dbx,
268 moklist,
269 moklistx,
270 }
271 }
272 SignaturesDelta::Replace(SignaturesReplace {
273 pk: replace_pk,
274 kek: replace_kek,
275 db: replace_db,
276 dbx: replace_dbx,
277 moklist: replace_moklist,
278 moklistx: replace_moklistx,
279 }) => {
280 fn replace_default(sig_delta: SignatureDelta, base: Signature) -> Signature {
281 match sig_delta {
282 SignatureDelta::Sig(sig) => sig,
283 SignatureDelta::Default => base,
284 }
285 }
286
287 fn replace_default_vec(
288 sig_delta_vec: SignatureDeltaVec,
289 base: Vec<Signature>,
290 ) -> Vec<Signature> {
291 match sig_delta_vec {
292 SignatureDeltaVec::Sigs(sigs) => sigs,
293 SignatureDeltaVec::Default => base,
294 }
295 }
296
297 fn replace_default_option_vec(
298 sig_delta_vec: Option<SignatureDeltaVec>,
299 base: Vec<Signature>,
300 ) -> Vec<Signature> {
301 match sig_delta_vec {
302 Some(SignatureDeltaVec::Sigs(sigs)) => sigs,
303 Some(SignatureDeltaVec::Default) | None => base,
304 }
305 }
306
307 Signatures {
308 pk: replace_default(replace_pk, pk),
309 kek: replace_default_vec(replace_kek, kek),
310 db: replace_default_vec(replace_db, db),
311 dbx: replace_default_vec(replace_dbx, dbx),
312 moklist: replace_default_option_vec(replace_moklist, moklist),
313 moklistx: replace_default_option_vec(replace_moklistx, moklistx),
314 }
315 }
316 },
317 };
318
319 let mut non_signature_vars = self.non_signature_vars;
320
321 'outer: for (new_key, new_val) in delta.non_signature_vars {
323 if new_key == "dbDefault" && new_val.guid == EFI_GLOBAL_VARIABLE {
324 return Err(ApplyDeltaError::RestrictedUefiVar {
325 name: new_key,
326 guid: new_val.guid,
327 });
328 }
329
330 for (old_key, old_val) in &mut non_signature_vars {
331 if *old_key == new_key {
332 *old_val = new_val;
333 continue 'outer;
334 }
335 }
336 non_signature_vars.push((new_key, new_val));
337 }
338
339 Ok(UefiVars {
340 signatures: Some(signatures),
341 non_signature_vars,
342 })
343 }
344}
345
346#[cfg(test)]
347mod tests {
348 use super::*;
349 use crate::delta::SignatureDelta;
350 use crate::delta::SignatureDeltaVec;
351 use crate::delta::SignaturesAppend;
352 use crate::delta::SignaturesDelta;
353 use crate::delta::SignaturesReplace;
354 use crate::delta::UefiVarsDelta;
355
356 #[test]
357 fn no_templates_resolve_to_empty_final_vars() {
358 let vars = FinalVars::resolve(None, None).unwrap().into_uefi_vars();
359
360 assert!(vars.signatures.is_none());
361 assert!(vars.non_signature_vars.is_empty());
362 }
363
364 #[test]
365 fn append_without_base_fails() {
366 let delta = UefiVarsDelta {
367 signatures: SignaturesDelta::Append(SignaturesAppend {
368 kek: None,
369 db: None,
370 dbx: None,
371 moklist: None,
372 moklistx: None,
373 }),
374 non_signature_vars: Vec::new(),
375 };
376
377 assert!(matches!(
378 FinalVars::resolve(None, Some(delta)),
379 Err(ApplyDeltaError::AppendWithoutBase)
380 ));
381 }
382
383 #[test]
384 fn default_without_base_fails() {
385 let delta = UefiVarsDelta {
386 signatures: SignaturesDelta::Replace(SignaturesReplace {
387 pk: SignatureDelta::Default,
388 kek: SignatureDeltaVec::Default,
389 db: SignatureDeltaVec::Default,
390 dbx: SignatureDeltaVec::Default,
391 moklist: None,
392 moklistx: None,
393 }),
394 non_signature_vars: Vec::new(),
395 };
396
397 assert!(matches!(
398 FinalVars::resolve(None, Some(delta)),
399 Err(ApplyDeltaError::DefaultWithoutBase)
400 ));
401 }
402
403 #[test]
404 fn restricted_uefi_var_fails() {
405 let base_template = UefiVars {
406 signatures: Some(Signatures {
407 pk: Signature::X509(Vec::new()),
408 kek: Vec::new(),
409 db: Vec::new(),
410 dbx: Vec::new(),
411 moklist: Vec::new(),
412 moklistx: Vec::new(),
413 }),
414 non_signature_vars: Vec::new(),
415 }
416 .into();
417 let delta = UefiVarsDelta {
418 signatures: SignaturesDelta::Append(SignaturesAppend {
419 kek: None,
420 db: None,
421 dbx: None,
422 moklist: None,
423 moklistx: None,
424 }),
425 non_signature_vars: vec![(
426 "dbDefault".into(),
427 UefiVar {
428 guid: EFI_GLOBAL_VARIABLE,
429 attr: 0,
430 value: Vec::new(),
431 },
432 )],
433 };
434
435 assert!(matches!(
436 FinalVars::resolve(Some(base_template), Some(delta)),
437 Err(ApplyDeltaError::RestrictedUefiVar { name, guid })
438 if name == "dbDefault" && guid == EFI_GLOBAL_VARIABLE
439 ));
440 }
441}