• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2023, The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 //! # Generic Boot Loader (gbl) Library
16 //!
17 //! TODO: b/312610098 - add documentation.
18 //!
19 //! The intended users of this library are firmware, bootloader, and bring-up teams at OEMs and SOC
20 //! Vendors
21 //!
22 //! This library is `no_std` as it is intended for use in bootloaders that typically will not
23 //! support the Rust standard library. However, it does require `alloc` with a global allocator,
24 //! currently used for:
25 //! * libavb
26 //! * kernel decompression
27 
28 #![cfg_attr(not(any(test, android_dylib)), no_std)]
29 // TODO: b/312610985 - return warning for unused partitions
30 #![allow(async_fn_in_trait)]
31 // Needed for MaybeUninit::fill() experimental API
32 #![feature(maybe_uninit_fill)]
33 extern crate avb;
34 extern crate core;
35 extern crate gbl_storage;
36 extern crate spin;
37 extern crate zbi;
38 
39 use avb::{HashtreeErrorMode, SlotVerifyData, SlotVerifyFlags};
40 use core::ffi::CStr;
41 use core::marker::PhantomData;
42 
43 pub mod android_boot;
44 pub mod boot_mode;
45 pub mod boot_reason;
46 pub mod constants;
47 pub mod decompress;
48 pub mod device_tree;
49 pub mod error;
50 pub mod fastboot;
51 pub mod fuchsia_boot;
52 pub mod gbl_avb;
53 pub mod ops;
54 pub mod partition;
55 
56 /// The 'slots' module, containing types and traits for
57 /// querying and modifying slotted boot behavior.
58 pub mod slots;
59 
60 mod image_buffer;
61 
62 use slots::{BootTarget, BootToken, Cursor, SuffixBytes};
63 
64 pub use avb::Descriptor;
65 pub use boot_mode::BootMode;
66 pub use boot_reason::KnownBootReason;
67 pub use error::{IntegrationError, Result};
68 use liberror::Error;
69 pub use ops::{GblOps, Os};
70 
71 /// GBL object that provides implementation of helpers for boot process.
72 pub struct Gbl<'a, 'd, G>
73 where
74     G: GblOps<'a, 'd>,
75 {
76     ops: &'a mut G,
77     boot_token: Option<BootToken>,
78     _get_image_buffer_lifetime: PhantomData<&'d ()>,
79 }
80 
81 // TODO(b/312610985): Investigate whether to deprecate this and remove this allow.
82 #[allow(unused_variables)]
83 impl<'a, 'f, G> Gbl<'a, 'f, G>
84 where
85     G: GblOps<'a, 'f>,
86 {
87     /// Returns a new [Gbl] object.
88     ///
89     /// # Arguments
90     /// * `ops` - the [GblOps] callbacks to use
new(ops: &'a mut G) -> Self91     pub fn new(ops: &'a mut G) -> Self {
92         Self { ops, boot_token: Some(BootToken(())), _get_image_buffer_lifetime: PhantomData }
93     }
94 
95     /// Verify + Load Image Into memory
96     ///
97     /// Load from disk, validate with AVB
98     ///
99     /// # Arguments
100     /// * `avb_ops` - implementation for `avb::Ops`
101     /// * `partitions_to_verify` - names of all the partitions to verify with libavb.
102     /// * `slot_verify_flags` - AVB slot verification flags
103     /// * `boot_target` - [Optional] Boot Target
104     ///
105     /// # Returns
106     /// * `Ok(SlotVerifyData)` - avb verification data
107     /// * `Err(Error)` - on failure
load_and_verify_image<'b>( &mut self, avb_ops: &mut impl avb::Ops<'b>, partitions_to_verify: &[&CStr], slot_verify_flags: SlotVerifyFlags, boot_target: Option<BootTarget>, ) -> Result<SlotVerifyData<'b>>108     pub fn load_and_verify_image<'b>(
109         &mut self,
110         avb_ops: &mut impl avb::Ops<'b>,
111         partitions_to_verify: &[&CStr],
112         slot_verify_flags: SlotVerifyFlags,
113         boot_target: Option<BootTarget>,
114     ) -> Result<SlotVerifyData<'b>> {
115         let bytes: SuffixBytes =
116             if let Some(tgt) = boot_target { tgt.suffix().into() } else { Default::default() };
117 
118         let avb_suffix = CStr::from_bytes_until_nul(&bytes).map_err(Error::from)?;
119 
120         Ok(avb::slot_verify(
121             avb_ops,
122             partitions_to_verify,
123             Some(avb_suffix),
124             slot_verify_flags,
125             HashtreeErrorMode::AVB_HASHTREE_ERROR_MODE_EIO,
126         )
127         .map_err(|v| v.without_verify_data())?)
128     }
129 
130     /// Load Slot Manager Interface
131     ///
132     /// The default implementation loads from the `durable_boot` partition
133     /// and writes changes back on the destruction of the cursor.
134     ///
135     /// # Returns
136     ///
137     /// * `Ok(Cursor)` - Cursor object that manages a Manager
138     /// * `Err(Error)` - on failure
load_slot_interface( &'a mut self, persist: &'a mut dyn FnMut(&mut [u8]) -> core::result::Result<(), Error>, ) -> Result<Cursor<'a>>139     pub fn load_slot_interface(
140         &'a mut self,
141         persist: &'a mut dyn FnMut(&mut [u8]) -> core::result::Result<(), Error>,
142     ) -> Result<Cursor<'a>> {
143         let boot_token = self.boot_token.take().ok_or(Error::OperationProhibited)?;
144         self.ops.load_slot_interface(persist, boot_token)
145     }
146 }
147 
148 #[cfg(test)]
149 mod tests {
150     extern crate avb_sysdeps;
151     extern crate avb_test;
152     extern crate libc_deps_posix;
153 
154     use super::*;
155     use crate::ops::test::FakeGblOps;
156     use avb::{CertPermanentAttributes, SlotVerifyError};
157     use avb_test::{FakeVbmetaKey, TestOps};
158     use libutils::aligned_offset;
159     use std::{
160         fs,
161         ops::{Deref, DerefMut},
162         path::Path,
163     };
164     use zerocopy::FromBytes;
165 
166     // Helper object for allocating aligned buffer.
167     pub(crate) struct AlignedBuffer {
168         buffer: Vec<u8>,
169         size: usize,
170         alignment: usize,
171     }
172 
173     impl AlignedBuffer {
174         /// Allocates a buffer.
new(size: usize, alignment: usize) -> Self175         pub(crate) fn new(size: usize, alignment: usize) -> Self {
176             Self { buffer: vec![0u8; alignment + size - 1], size, alignment }
177         }
178 
179         /// Allocates a buffer and initializes with data.
new_with_data(data: &[u8], alignment: usize) -> Self180         pub(crate) fn new_with_data(data: &[u8], alignment: usize) -> Self {
181             let mut res = Self::new(data.len(), alignment);
182             res.clone_from_slice(data);
183             res
184         }
185     }
186 
187     impl Deref for AlignedBuffer {
188         type Target = [u8];
189 
deref(&self) -> &Self::Target190         fn deref(&self) -> &Self::Target {
191             let off = aligned_offset(&self.buffer, self.alignment).unwrap();
192             &self.buffer[off..][..self.size]
193         }
194     }
195 
196     impl DerefMut for AlignedBuffer {
deref_mut(&mut self) -> &mut Self::Target197         fn deref_mut(&mut self) -> &mut Self::Target {
198             let off = aligned_offset(&self.buffer, self.alignment).unwrap();
199             &mut self.buffer[off..][..self.size]
200         }
201     }
202 
203     const TEST_ZIRCON_PARTITION_NAME: &str = "zircon_a";
204     const TEST_ZIRCON_PARTITION_NAME_CSTR: &CStr = c"zircon_a";
205     const TEST_ZIRCON_IMAGE_PATH: &str = "zircon_a.zbi";
206     const TEST_ZIRCON_VBMETA_PATH: &str = "zircon_a.vbmeta";
207     const TEST_ZIRCON_VBMETA_CERT_PATH: &str = "zircon_a.vbmeta.cert";
208     const TEST_PUBLIC_KEY_PATH: &str = "testkey_rsa4096_pub.bin";
209     const TEST_PERMANENT_ATTRIBUTES_PATH: &str = "cert_permanent_attributes.bin";
210     const TEST_PERMANENT_ATTRIBUTES_HASH_PATH: &str = "cert_permanent_attributes.hash";
211     const TEST_BAD_PERMANENT_ATTRIBUTES_PATH: &str = "cert_permanent_attributes.bad.bin";
212     const TEST_BAD_PERMANENT_ATTRIBUTES_HASH_PATH: &str = "cert_permanent_attributes.bad.hash";
213     const TEST_VBMETA_ROLLBACK_LOCATION: usize = 0; // Default value, we don't explicitly set this.
214     pub const TEST_CERT_PIK_VERSION: u64 = 42;
215     pub const TEST_CERT_PSK_VERSION: u64 = 42;
216 
217     /// Returns the contents of a test data file.
218     ///
219     /// Panicks if the requested file cannot be read.
220     ///
221     /// # Arguments
222     /// * `path`: file path relative to libgbl's `testdata/` directory.
testdata(path: &str) -> Vec<u8>223     fn testdata(path: &str) -> Vec<u8> {
224         let full_path = Path::new("external/gbl/libgbl/testdata").join(path);
225         fs::read(full_path).unwrap()
226     }
227 
228     /// Creates and returns a configured avb `TestOps`.
229     ///
230     /// The initial state will verify successfully with:
231     /// * a valid vbmeta image in the `vbmeta` partition, containing a hash descriptor for the
232     ///   `TEST_ZIRCON_PARTITION_NAME` partition
233     /// * an image in the `TEST_ZIRCON_PARTITION_NAME` partition matching the vbmeta hash
234     /// * no preloaded partition data
235     /// * a public key matching the vbmeta image
236     /// * a valid vbmeta rollback index
237     /// * a locked bootloader state
238     ///
239     /// The caller can modify any of this state as needed for their particular test.
test_avb_ops() -> TestOps<'static>240     fn test_avb_ops() -> TestOps<'static> {
241         let mut avb_ops = TestOps::default();
242 
243         avb_ops.add_partition(TEST_ZIRCON_PARTITION_NAME, testdata(TEST_ZIRCON_IMAGE_PATH));
244         avb_ops.add_partition("vbmeta", testdata(TEST_ZIRCON_VBMETA_PATH));
245         avb_ops.default_vbmeta_key = Some(FakeVbmetaKey::Avb {
246             public_key: testdata(TEST_PUBLIC_KEY_PATH),
247             public_key_metadata: None,
248         });
249         avb_ops.rollbacks.insert(TEST_VBMETA_ROLLBACK_LOCATION, Ok(0));
250         avb_ops.unlock_state = Ok(false);
251 
252         avb_ops
253     }
254 
255     /// Similar to `test_avb_ops()`, but with the avb_cert extension enabled.
test_avb_cert_ops() -> TestOps<'static>256     fn test_avb_cert_ops() -> TestOps<'static> {
257         let mut avb_ops = test_avb_ops();
258 
259         // Replace vbmeta with the cert-signed version.
260         avb_ops.add_partition("vbmeta", testdata(TEST_ZIRCON_VBMETA_CERT_PATH));
261 
262         // Tell `avb_ops` to use cert APIs and to route the default key through cert validation.
263         avb_ops.use_cert = true;
264         avb_ops.default_vbmeta_key = Some(FakeVbmetaKey::Cert);
265 
266         // Add the permanent attributes.
267         let perm_attr_bytes = testdata(TEST_PERMANENT_ATTRIBUTES_PATH);
268         let perm_attr_hash = testdata(TEST_PERMANENT_ATTRIBUTES_HASH_PATH);
269         avb_ops.cert_permanent_attributes =
270             Some(CertPermanentAttributes::read_from(&perm_attr_bytes[..]).unwrap());
271         avb_ops.cert_permanent_attributes_hash = Some(perm_attr_hash.try_into().unwrap());
272 
273         // Add the rollbacks for the cert keys.
274         avb_ops.rollbacks.insert(avb::CERT_PIK_VERSION_LOCATION, Ok(TEST_CERT_PIK_VERSION));
275         avb_ops.rollbacks.insert(avb::CERT_PSK_VERSION_LOCATION, Ok(TEST_CERT_PSK_VERSION));
276 
277         avb_ops
278     }
279 
280     #[test]
test_load_and_verify_image_success()281     fn test_load_and_verify_image_success() {
282         let mut gbl_ops = FakeGblOps::default();
283         let mut gbl = Gbl::new(&mut gbl_ops);
284         let mut avb_ops = test_avb_ops();
285 
286         let res = gbl.load_and_verify_image(
287             &mut avb_ops,
288             &mut [&TEST_ZIRCON_PARTITION_NAME_CSTR],
289             SlotVerifyFlags::AVB_SLOT_VERIFY_FLAGS_NONE,
290             None,
291         );
292         assert!(res.is_ok());
293     }
294 
295     #[test]
test_load_and_verify_image_verification_error()296     fn test_load_and_verify_image_verification_error() {
297         let mut gbl_ops = FakeGblOps::default();
298         let mut gbl = Gbl::new(&mut gbl_ops);
299         let mut avb_ops = test_avb_ops();
300 
301         // Modify the kernel image, it should now fail to validate against the vbmeta image.
302         avb_ops.partitions.get_mut(TEST_ZIRCON_PARTITION_NAME).unwrap().contents.as_mut_vec()[0] ^=
303             0x01;
304 
305         let res = gbl.load_and_verify_image(
306             &mut avb_ops,
307             &mut [&TEST_ZIRCON_PARTITION_NAME_CSTR],
308             SlotVerifyFlags::AVB_SLOT_VERIFY_FLAGS_NONE,
309             None,
310         );
311         assert_eq!(
312             res.unwrap_err(),
313             IntegrationError::AvbSlotVerifyError(SlotVerifyError::Verification(None))
314         );
315     }
316 
317     #[test]
test_load_and_verify_image_io_error()318     fn test_load_and_verify_image_io_error() {
319         let mut gbl_ops = FakeGblOps::default();
320         let mut gbl = Gbl::new(&mut gbl_ops);
321         let mut avb_ops = test_avb_ops();
322 
323         // Erase the fake rollbacks, which will result in an I/O error when attempting to access.
324         avb_ops.rollbacks.clear();
325 
326         let res = gbl.load_and_verify_image(
327             &mut avb_ops,
328             &mut [&TEST_ZIRCON_PARTITION_NAME_CSTR],
329             SlotVerifyFlags::AVB_SLOT_VERIFY_FLAGS_NONE,
330             None,
331         );
332         assert_eq!(res.unwrap_err(), IntegrationError::AvbSlotVerifyError(SlotVerifyError::Io));
333     }
334 
335     #[test]
test_load_and_verify_image_with_cert_success()336     fn test_load_and_verify_image_with_cert_success() {
337         let mut gbl_ops = FakeGblOps::default();
338         let mut gbl = Gbl::new(&mut gbl_ops);
339         let mut avb_ops = test_avb_cert_ops();
340 
341         let res = gbl.load_and_verify_image(
342             &mut avb_ops,
343             &mut [&TEST_ZIRCON_PARTITION_NAME_CSTR],
344             SlotVerifyFlags::AVB_SLOT_VERIFY_FLAGS_NONE,
345             None,
346         );
347         assert!(res.is_ok());
348     }
349 
350     #[test]
test_load_and_verify_image_with_cert_permanent_attribute_mismatch_error()351     fn test_load_and_verify_image_with_cert_permanent_attribute_mismatch_error() {
352         let mut gbl_ops = FakeGblOps::default();
353         let mut gbl = Gbl::new(&mut gbl_ops);
354         let mut avb_ops = test_avb_cert_ops();
355 
356         // Swap in the corrupted permanent attributes, which should cause the vbmeta image to fail
357         // validation due to key mismatch.
358         let perm_attr_bytes = testdata(TEST_BAD_PERMANENT_ATTRIBUTES_PATH);
359         let perm_attr_hash = testdata(TEST_BAD_PERMANENT_ATTRIBUTES_HASH_PATH);
360         avb_ops.cert_permanent_attributes =
361             Some(CertPermanentAttributes::read_from(&perm_attr_bytes[..]).unwrap());
362         avb_ops.cert_permanent_attributes_hash = Some(perm_attr_hash.try_into().unwrap());
363 
364         let res = gbl.load_and_verify_image(
365             &mut avb_ops,
366             &mut [&TEST_ZIRCON_PARTITION_NAME_CSTR],
367             SlotVerifyFlags::AVB_SLOT_VERIFY_FLAGS_NONE,
368             None,
369         );
370         assert_eq!(
371             res.unwrap_err(),
372             IntegrationError::AvbSlotVerifyError(SlotVerifyError::PublicKeyRejected(None))
373         );
374     }
375 }
376