firmware_uefi_custom_vars/
lib.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Types and methods for defining and layering sets of custom UEFI nvram
5//! variables
6
7#![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/// Collection of UEFI nvram variables that that will be injected on first boot.
18#[derive(Debug, Default, Clone, Protobuf)]
19pub struct CustomVars {
20    /// Secure Boot signature vars
21    pub signatures: Option<Signatures>,
22    /// Any additional custom vars
23    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    /// Create a new, blank set of CustomVars.
81    pub fn new() -> CustomVars {
82        CustomVars::default()
83    }
84
85    /// Apply a delta on-top of an existing set of CustomVars.
86    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        // Replace overwritten vars, append new vars
234        '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}