uefi_specs/uefi/
nvram.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! UEFI Nvram Variable Services
5
6use crate::uefi::signing::WIN_CERTIFICATE_UEFI_GUID;
7use crate::uefi::time::EFI_TIME;
8use bitfield_struct::bitfield;
9use guid::Guid;
10use zerocopy::FromBytes;
11use zerocopy::Immutable;
12use zerocopy::IntoBytes;
13use zerocopy::KnownLayout;
14
15/// UEFI spec 8.2 - Variable Services
16#[bitfield(u32)]
17#[derive(Eq, PartialEq)]
18pub struct EfiVariableAttributes {
19    pub non_volatile: bool,
20    pub bootservice_access: bool,
21    pub runtime_access: bool,
22    pub hardware_error_record: bool,
23    pub authenticated_write_access: bool,
24    pub time_based_authenticated_write_access: bool,
25    pub append_write: bool,
26    pub enhanced_authenticated_access: bool,
27
28    #[bits(24)]
29    _reserved: u32,
30}
31
32impl EfiVariableAttributes {
33    pub const DEFAULT_ATTRIBUTES: EfiVariableAttributes = EfiVariableAttributes::new()
34        .with_non_volatile(true)
35        .with_bootservice_access(true)
36        .with_runtime_access(true);
37    pub const DEFAULT_ATTRIBUTES_VOLATILE: EfiVariableAttributes = EfiVariableAttributes::new()
38        .with_bootservice_access(true)
39        .with_runtime_access(true);
40    pub const DEFAULT_ATTRIBUTES_TIME_BASED_AUTH: EfiVariableAttributes =
41        Self::DEFAULT_ATTRIBUTES.with_time_based_authenticated_write_access(true);
42
43    pub fn contains_unsupported_bits(&self) -> bool {
44        u32::from(*self)
45            & !u32::from(
46                Self::new()
47                    .with_non_volatile(true)
48                    .with_bootservice_access(true)
49                    .with_runtime_access(true)
50                    .with_hardware_error_record(true)
51                    .with_authenticated_write_access(true)
52                    .with_time_based_authenticated_write_access(true)
53                    .with_append_write(true)
54                    .with_enhanced_authenticated_access(true),
55            )
56            != 0
57    }
58}
59
60/// UEFI spec 8.2
61#[derive(IntoBytes, FromBytes, Immutable, KnownLayout)]
62#[repr(C)]
63pub struct EFI_VARIABLE_AUTHENTICATION_2 {
64    /// Components Pad1, Nanosecond, TimeZone, Daylight and Pad2 shall be set to
65    /// 0. This means that the time shall always be expressed in GMT.
66    pub timestamp: EFI_TIME,
67    /// Provides the authorization for the variable access. Only a CertType of
68    /// EFI_CERT_TYPE_PKCS7_GUID is accepted.
69    pub auth_info: WIN_CERTIFICATE_UEFI_GUID,
70}
71
72impl EFI_VARIABLE_AUTHENTICATION_2 {
73    /// A "dummy" header that doesn't actually include a valid cert.
74    ///
75    /// This header is used during parts of the pre-boot setup to inject
76    /// `TIME_BASED_AUTHENTICATED_WRITE_ACCESS` from within the UEFI device
77    /// itself.
78    pub const DUMMY: Self = {
79        use crate::uefi::signing::EFI_CERT_TYPE_PKCS7_GUID;
80        use crate::uefi::signing::WIN_CERT_TYPE_EFI_GUID;
81        use crate::uefi::signing::WIN_CERTIFICATE;
82
83        EFI_VARIABLE_AUTHENTICATION_2 {
84            timestamp: EFI_TIME::ZEROED,
85            auth_info: WIN_CERTIFICATE_UEFI_GUID {
86                header: WIN_CERTIFICATE {
87                    // `length` includes both the header itself, and the cert
88                    // payload (which has len 0 in the dummy header)
89                    length: size_of::<WIN_CERTIFICATE_UEFI_GUID>() as u32,
90                    revision: 0x0200,
91                    certificate_type: WIN_CERT_TYPE_EFI_GUID,
92                },
93                cert_type: EFI_CERT_TYPE_PKCS7_GUID,
94            },
95        }
96    };
97}
98
99/// UEFI spec 32.4.1
100pub mod signature_list {
101    use guid::Guid;
102    use zerocopy::FromBytes;
103    use zerocopy::Immutable;
104    use zerocopy::IntoBytes;
105    use zerocopy::KnownLayout;
106
107    #[derive(Debug, PartialEq, Eq, IntoBytes, FromBytes, Immutable, KnownLayout)]
108    #[repr(C)]
109    pub struct EFI_SIGNATURE_LIST {
110        /// Type of the signature. GUID signature types are defined in "Related
111        /// Definitions" below.
112        pub signature_type: Guid,
113        /// Total size of the signature list, including this header.
114        pub signature_list_size: u32,
115        /// Size of the signature header which precedes the array of signatures.
116        ///
117        /// > NOTE: a careful reading of the UEFI spec uncovers that this field
118        /// > is _always_ zero. Why? Excellent question.
119        pub signature_header_size: u32,
120        /// Size of each signature. Must be at least the size of EFI_SIGNATURE_DATA.
121        pub signature_size: u32,
122        // Header before the array of signatures. The format of this header is
123        // specified by the SignatureType.
124        //
125        // > NOTE: because SignatureHeaderSize is always zero, this array is
126        // > always zero sized...
127        //
128        // UINT8 SignatureHeader[SignatureHeaderSize];
129        //
130        // An array of signatures. Each signature is SignatureSize bytes in
131        // length. The format of the signature is defined by the SignatureType.
132        //
133        // EFI_SIGNATURE_DATA Signatures[…][SignatureSize];
134    }
135
136    #[derive(
137        Debug,
138        Clone,
139        Copy,
140        PartialEq,
141        Eq,
142        PartialOrd,
143        Ord,
144        IntoBytes,
145        FromBytes,
146        Immutable,
147        KnownLayout,
148    )]
149    #[repr(C)]
150    pub struct EFI_SIGNATURE_DATA {
151        /// An identifier which identifies the agent which added the signature to
152        /// the list.
153        pub signature_owner: Guid,
154        // UINT8 SignatureData[…];
155    }
156
157    pub const EFI_CERT_SHA256_GUID: Guid = guid::guid!("c1c41626-504c-4092-aca9-41f936934328");
158
159    pub const EFI_CERT_X509_GUID: Guid = guid::guid!("a5c059a1-94e4-4aa7-87b5-ab155c2bf072");
160}
161
162/// Check if the specified variable is a secure boot policy variable, as
163/// specified by the UEFI spec in section 3.3 Globally Defined Variables, under
164/// the details of `SetupMode`.
165pub fn is_secure_boot_policy_var(vendor: Guid, name: &ucs2::Ucs2LeSlice) -> bool {
166    let secure_boot_policy_vars = [
167        vars::PK(),
168        vars::KEK(),
169        // TODO: add OsRecoveryOrder, OsRecovery####
170    ];
171
172    let is_secure_boot_policy_var = secure_boot_policy_vars
173        .into_iter()
174        .any(|v| v == (vendor, name));
175
176    is_secure_boot_policy_var || vendor == vars::IMAGE_SECURITY_DATABASE_GUID
177}
178
179/// UEFI spec 3.3 - Table 3-1
180///
181/// Due to the Rust compiler not having built-in support for defining
182/// wide-string literals, and me not wanting to yak-shave a proc macro
183/// implementation that emits valid Utf16LeSlices at compile time, these
184/// "constants" are actually methods that can only be called at runtime.
185pub mod vars {
186    use guid::Guid;
187
188    /// UEFI spec 3.3 - Globally Defined Variables
189    pub const EFI_GLOBAL_VARIABLE: Guid = guid::guid!("8BE4DF61-93CA-11D2-AA0D-00E098032B8C");
190
191    /// UEFI spec 32.6.1 - UEFI Image Variable GUID & Variable Name
192    pub const IMAGE_SECURITY_DATABASE_GUID: Guid =
193        guid::guid!("d719b2cb-3d3a-4596-a3bc-dad00e67656f");
194
195    defn_nvram_var!(SECURE_BOOT = (EFI_GLOBAL_VARIABLE, "SecureBoot"));
196    defn_nvram_var!(SETUP_MODE = (EFI_GLOBAL_VARIABLE, "SetupMode"));
197
198    defn_nvram_var!(PK = (EFI_GLOBAL_VARIABLE, "PK"));
199    defn_nvram_var!(KEK = (EFI_GLOBAL_VARIABLE, "KEK"));
200    defn_nvram_var!(DBDEFAULT = (EFI_GLOBAL_VARIABLE, "dbDefault"));
201
202    defn_nvram_var!(DB = (IMAGE_SECURITY_DATABASE_GUID, "db"));
203    defn_nvram_var!(DBX = (IMAGE_SECURITY_DATABASE_GUID, "dbx"));
204}