firmware_uefi_custom_vars/
lib.rs1#![expect(missing_docs)]
8#![forbid(unsafe_code)]
9
10use guid::Guid;
11use mesh_protobuf::Protobuf;
12use thiserror::Error;
13use uefi_specs::uefi::nvram::vars::EFI_GLOBAL_VARIABLE;
14
15pub mod delta;
16
17#[derive(Debug, Default, Clone, Protobuf)]
19pub struct CustomVars {
20 pub signatures: Option<Signatures>,
22 pub custom_vars: Vec<(String, CustomVar)>,
24}
25
26#[derive(Debug, Clone, Protobuf)]
27pub struct Signatures {
28 pub pk: Signature,
29 pub kek: Vec<Signature>,
30 pub db: Vec<Signature>,
31 pub dbx: Vec<Signature>,
32 pub moklist: Vec<Signature>,
33 pub moklistx: Vec<Signature>,
34}
35
36#[derive(Debug, Clone, Protobuf)]
37pub enum Signature {
38 X509(Vec<X509Cert>),
39 Sha256(Vec<Sha256Digest>),
40}
41
42#[derive(Debug, Clone, Protobuf)]
43pub struct CustomVar {
44 pub guid: Guid,
45 pub attr: u32,
46 pub value: Vec<u8>,
47}
48
49#[derive(Clone, Protobuf)]
50pub struct X509Cert(pub Vec<u8>);
51
52impl std::fmt::Debug for X509Cert {
53 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
54 f.debug_tuple("X509Cert").field(&"[..]").finish()
55 }
56}
57
58#[derive(Clone, Protobuf)]
59pub struct Sha256Digest(pub [u8; 32]);
60
61impl std::fmt::Debug for Sha256Digest {
62 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
63 f.debug_tuple("Sha256Digest")
64 .field(&self.0.map(|b| format!("{:02x?}", b)).join(""))
65 .finish()
66 }
67}
68
69#[derive(Debug, Error)]
70pub enum ApplyDeltaError {
71 #[error("cannot Append if no base signatures are provided")]
72 AppendWithoutBase,
73 #[error("cannot use \"Default\" variable type if no base signatures are provided")]
74 DefaultWithoutBase,
75 #[error("cannot set restricted variable: {name}:{guid}")]
76 RestrictedCustomVar { name: String, guid: Guid },
77}
78
79impl CustomVars {
80 pub fn new() -> CustomVars {
82 CustomVars::default()
83 }
84
85 pub fn apply_delta(self, delta: delta::CustomVarsDelta) -> Result<CustomVars, ApplyDeltaError> {
87 use delta::SignatureDelta;
88 use delta::SignatureDeltaVec;
89 use delta::SignaturesAppend;
90 use delta::SignaturesDelta;
91 use delta::SignaturesReplace;
92
93 let signatures = match (self.signatures, delta.signatures) {
94 (None, SignaturesDelta::Append(..)) => return Err(ApplyDeltaError::AppendWithoutBase),
95 (
96 None,
97 SignaturesDelta::Replace(SignaturesReplace {
98 pk,
99 kek,
100 db,
101 dbx,
102 moklist,
103 moklistx,
104 }),
105 ) => {
106 fn deny_default(sig_delta: SignatureDelta) -> Result<Signature, ApplyDeltaError> {
107 match sig_delta {
108 SignatureDelta::Sig(sig) => Ok(sig),
109 SignatureDelta::Default => Err(ApplyDeltaError::DefaultWithoutBase),
110 }
111 }
112
113 fn deny_default_vec(
114 sig_delta_vec: SignatureDeltaVec,
115 ) -> Result<Vec<Signature>, ApplyDeltaError> {
116 match sig_delta_vec {
117 SignatureDeltaVec::Sigs(sig) => Ok(sig),
118 SignatureDeltaVec::Default => Err(ApplyDeltaError::DefaultWithoutBase),
119 }
120 }
121
122 Signatures {
123 pk: deny_default(pk)?,
124 kek: deny_default_vec(kek)?,
125 db: deny_default_vec(db)?,
126 dbx: deny_default_vec(dbx)?,
127 moklist: moklist
128 .map(deny_default_vec)
129 .transpose()?
130 .unwrap_or_default(),
131 moklistx: moklistx
132 .map(deny_default_vec)
133 .transpose()?
134 .unwrap_or_default(),
135 }
136 }
137 (
138 Some(Signatures {
139 pk,
140 mut kek,
141 mut db,
142 mut dbx,
143 mut moklist,
144 mut moklistx,
145 }),
146 sig_delta,
147 ) => match sig_delta {
148 SignaturesDelta::Append(SignaturesAppend {
149 kek: append_kek,
150 db: append_db,
151 dbx: append_dbx,
152 moklist: append_moklist,
153 moklistx: append_moklistx,
154 }) => {
155 if let Some(append_kek) = append_kek {
156 kek.extend(append_kek);
157 }
158
159 if let Some(append_db) = append_db {
160 db.extend(append_db);
161 }
162
163 if let Some(append_dbx) = append_dbx {
164 dbx.extend(append_dbx);
165 }
166
167 if let Some(append_moklist) = append_moklist {
168 moklist.extend(append_moklist)
169 }
170
171 if let Some(append_moklistx) = append_moklistx {
172 moklistx.extend(append_moklistx)
173 }
174
175 Signatures {
176 pk,
177 kek,
178 db,
179 dbx,
180 moklist,
181 moklistx,
182 }
183 }
184 SignaturesDelta::Replace(SignaturesReplace {
185 pk: replace_pk,
186 kek: replace_kek,
187 db: replace_db,
188 dbx: replace_dbx,
189 moklist: replace_moklist,
190 moklistx: replace_moklistx,
191 }) => {
192 fn replace_default(sig_delta: SignatureDelta, base: Signature) -> Signature {
193 match sig_delta {
194 SignatureDelta::Sig(sig) => sig,
195 SignatureDelta::Default => base,
196 }
197 }
198
199 fn replace_default_vec(
200 sig_delta_vec: SignatureDeltaVec,
201 base: Vec<Signature>,
202 ) -> Vec<Signature> {
203 match sig_delta_vec {
204 SignatureDeltaVec::Sigs(sigs) => sigs,
205 SignatureDeltaVec::Default => base,
206 }
207 }
208
209 fn replace_default_option_vec(
210 sig_delta_vec: Option<SignatureDeltaVec>,
211 base: Vec<Signature>,
212 ) -> Vec<Signature> {
213 match sig_delta_vec {
214 Some(SignatureDeltaVec::Sigs(sigs)) => sigs,
215 Some(SignatureDeltaVec::Default) | None => base,
216 }
217 }
218
219 Signatures {
220 pk: replace_default(replace_pk, pk),
221 kek: replace_default_vec(replace_kek, kek),
222 db: replace_default_vec(replace_db, db),
223 dbx: replace_default_vec(replace_dbx, dbx),
224 moklist: replace_default_option_vec(replace_moklist, moklist),
225 moklistx: replace_default_option_vec(replace_moklistx, moklistx),
226 }
227 }
228 },
229 };
230
231 let mut custom_vars = self.custom_vars;
232
233 'outer: for (new_key, new_val) in delta.custom_vars {
235 if new_key.as_str() == "dbDefault" && new_val.guid == EFI_GLOBAL_VARIABLE {
236 return Err(ApplyDeltaError::RestrictedCustomVar {
237 name: new_key,
238 guid: new_val.guid,
239 });
240 }
241
242 for (old_key, old_val) in &mut custom_vars {
243 if *old_key == new_key {
244 *old_val = new_val;
245 continue 'outer;
246 }
247 }
248 custom_vars.push((new_key, new_val));
249 }
250
251 Ok(CustomVars {
252 signatures: Some(signatures),
253 custom_vars,
254 })
255 }
256}