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, SlotVerifyError, 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 mod overlap;
62
63 use slots::{BootTarget, BootToken, Cursor, OneShot, SuffixBytes, UnbootableReason};
64
65 pub use avb::Descriptor;
66 pub use boot_mode::BootMode;
67 pub use boot_reason::KnownBootReason;
68 pub use error::{IntegrationError, Result};
69 use liberror::Error;
70 pub use ops::{GblOps, Os};
71
72 use overlap::is_overlap;
73
74 // TODO: b/312607649 - Replace placeholders with actual structures: https://r.android.com/2721974, etc
75 /// TODO: b/312607649 - placeholder type
76 pub struct Partition {}
77 /// TODO: b/312607649 - placeholder type
78 pub struct InfoStruct {}
79
80 /// Structure representing partition and optional address it is required to be loaded.
81 /// If no address is provided GBL will use default one.
82 pub struct PartitionRamMap<'b, 'c> {
83 /// Partition details
84 pub partition: &'b Partition,
85
86 /// Optional memory region to load partitions.
87 /// If it's not provided default values will be used.
88 pub address: Option<&'c mut [u8]>,
89 }
90
91 /// Boot Image in memory
92 #[allow(dead_code)]
93 pub struct BootImage<'a>(&'a mut [u8]);
94
95 /// Vendor Boot Image in memory
96 pub struct VendorBootImage<'a>(&'a mut [u8]);
97
98 /// Init Boot Image in memory
99 pub struct InitBootImage<'a>(&'a mut [u8]);
100
101 /// Kernel Image in memory
102 #[allow(dead_code)]
103 pub struct KernelImage<'a>(&'a mut [u8]);
104
105 /// Ramdisk in memory
106 pub struct Ramdisk<'a>(&'a mut [u8]);
107 /// Bootconfig in memory
108 #[allow(dead_code)]
109 pub struct Bootconfig<'a>(&'a mut [u8]);
110 /// DTB in memory
111 #[allow(dead_code)]
112 pub struct Dtb<'a>(&'a mut [u8]);
113
114 /// Create Boot Image from corresponding partition for `partitions_ram_map` and `avb_descriptors`
115 /// lists
get_boot_image<'a: 'b, 'b: 'c, 'c, 'd>( partitions_ram_map: &'a mut [PartitionRamMap<'b, 'c>], ) -> (Option<BootImage<'c>>, &'a mut [PartitionRamMap<'b, 'c>])116 pub fn get_boot_image<'a: 'b, 'b: 'c, 'c, 'd>(
117 partitions_ram_map: &'a mut [PartitionRamMap<'b, 'c>],
118 ) -> (Option<BootImage<'c>>, &'a mut [PartitionRamMap<'b, 'c>]) {
119 match partitions_ram_map.len() {
120 0 => (None, partitions_ram_map),
121 _ => {
122 let (partition_map, tail) = partitions_ram_map.split_first_mut().unwrap();
123 (partition_map.address.take().map(BootImage), tail)
124 }
125 }
126 }
127
128 /// Create Vendor Boot Image from corresponding partition for `partitions_ram_map` and
129 /// `avb_descriptors` lists
get_vendor_boot_image<'a: 'b, 'b: 'c, 'c, 'd>( partitions_ram_map: &'a mut [PartitionRamMap<'b, 'c>], ) -> (Option<VendorBootImage<'c>>, &'a mut [PartitionRamMap<'b, 'c>])130 pub fn get_vendor_boot_image<'a: 'b, 'b: 'c, 'c, 'd>(
131 partitions_ram_map: &'a mut [PartitionRamMap<'b, 'c>],
132 ) -> (Option<VendorBootImage<'c>>, &'a mut [PartitionRamMap<'b, 'c>]) {
133 match partitions_ram_map.len() {
134 0 => (None, partitions_ram_map),
135 _ => {
136 let (partition_map, tail) = partitions_ram_map.split_first_mut().unwrap();
137 (partition_map.address.take().map(VendorBootImage), tail)
138 }
139 }
140 }
141
142 /// Create Init Boot Image from corresponding partition for `partitions` and `avb_descriptors` lists
get_init_boot_image<'a: 'b, 'b: 'c, 'c, 'd>( partitions_ram_map: &'a mut [PartitionRamMap<'b, 'c>], ) -> (Option<InitBootImage<'c>>, &'a mut [PartitionRamMap<'b, 'c>])143 pub fn get_init_boot_image<'a: 'b, 'b: 'c, 'c, 'd>(
144 partitions_ram_map: &'a mut [PartitionRamMap<'b, 'c>],
145 ) -> (Option<InitBootImage<'c>>, &'a mut [PartitionRamMap<'b, 'c>]) {
146 match partitions_ram_map.len() {
147 0 => (None, partitions_ram_map),
148 _ => {
149 let (partition_map, tail) = partitions_ram_map.split_first_mut().unwrap();
150 (partition_map.address.take().map(InitBootImage), tail)
151 }
152 }
153 }
154
155 /// Create separate image types from [avb::Descriptor]
get_images<'a: 'b, 'b: 'c, 'c, 'd>( partitions_ram_map: &'a mut [PartitionRamMap<'b, 'c>], ) -> ( Option<BootImage<'c>>, Option<InitBootImage<'c>>, Option<VendorBootImage<'c>>, &'a mut [PartitionRamMap<'b, 'c>], )156 pub fn get_images<'a: 'b, 'b: 'c, 'c, 'd>(
157 partitions_ram_map: &'a mut [PartitionRamMap<'b, 'c>],
158 ) -> (
159 Option<BootImage<'c>>,
160 Option<InitBootImage<'c>>,
161 Option<VendorBootImage<'c>>,
162 &'a mut [PartitionRamMap<'b, 'c>],
163 ) {
164 let (boot_image, partitions_ram_map) = get_boot_image(partitions_ram_map);
165 let (init_boot_image, partitions_ram_map) = get_init_boot_image(partitions_ram_map);
166 let (vendor_boot_image, partitions_ram_map) = get_vendor_boot_image(partitions_ram_map);
167 (boot_image, init_boot_image, vendor_boot_image, partitions_ram_map)
168 }
169
170 /// GBL object that provides implementation of helpers for boot process.
171 pub struct Gbl<'a, 'd, G>
172 where
173 G: GblOps<'a, 'd>,
174 {
175 ops: &'a mut G,
176 boot_token: Option<BootToken>,
177 _get_image_buffer_lifetime: PhantomData<&'d ()>,
178 }
179
180 // TODO(b/312610985): Investigate whether to deprecate this and remove this allow.
181 #[allow(unused_variables)]
182 impl<'a, 'f, G> Gbl<'a, 'f, G>
183 where
184 G: GblOps<'a, 'f>,
185 {
186 /// Returns a new [Gbl] object.
187 ///
188 /// # Arguments
189 /// * `ops` - the [GblOps] callbacks to use
new(ops: &'a mut G) -> Self190 pub fn new(ops: &'a mut G) -> Self {
191 Self { ops, boot_token: Some(BootToken(())), _get_image_buffer_lifetime: PhantomData }
192 }
193
194 /// Verify + Load Image Into memory
195 ///
196 /// Load from disk, validate with AVB
197 ///
198 /// # Arguments
199 /// * `avb_ops` - implementation for `avb::Ops`
200 /// * `partitions_to_verify` - names of all the partitions to verify with libavb.
201 /// * `slot_verify_flags` - AVB slot verification flags
202 /// * `boot_target` - [Optional] Boot Target
203 ///
204 /// # Returns
205 /// * `Ok(SlotVerifyData)` - avb verification data
206 /// * `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>>207 pub fn load_and_verify_image<'b>(
208 &mut self,
209 avb_ops: &mut impl avb::Ops<'b>,
210 partitions_to_verify: &[&CStr],
211 slot_verify_flags: SlotVerifyFlags,
212 boot_target: Option<BootTarget>,
213 ) -> Result<SlotVerifyData<'b>> {
214 let bytes: SuffixBytes =
215 if let Some(tgt) = boot_target { tgt.suffix().into() } else { Default::default() };
216
217 let avb_suffix = CStr::from_bytes_until_nul(&bytes).map_err(Error::from)?;
218
219 Ok(avb::slot_verify(
220 avb_ops,
221 partitions_to_verify,
222 Some(avb_suffix),
223 slot_verify_flags,
224 HashtreeErrorMode::AVB_HASHTREE_ERROR_MODE_EIO,
225 )
226 .map_err(|v| v.without_verify_data())?)
227 }
228
229 /// Load Slot Manager Interface
230 ///
231 /// The default implementation loads from the `durable_boot` partition
232 /// and writes changes back on the destruction of the cursor.
233 ///
234 /// # Returns
235 ///
236 /// * `Ok(Cursor)` - Cursor object that manages a Manager
237 /// * `Err(Error)` - on failure
load_slot_interface( &'a mut self, persist: &'a mut dyn FnMut(&mut [u8]) -> core::result::Result<(), Error>, ) -> Result<Cursor<'a>>238 pub fn load_slot_interface(
239 &'a mut self,
240 persist: &'a mut dyn FnMut(&mut [u8]) -> core::result::Result<(), Error>,
241 ) -> Result<Cursor<'a>> {
242 let boot_token = self.boot_token.take().ok_or(Error::OperationProhibited)?;
243 self.ops.load_slot_interface(persist, boot_token)
244 }
245
246 /// Info Load
247 ///
248 /// Unpack boot image in RAM
249 ///
250 /// # Arguments
251 /// * `boot_image_buffer` - Buffer that contains (Optionally Verified) Boot Image
252 /// * `boot_mode` - Boot Mode
253 /// * `boot_target` - [Optional] Boot Target
254 ///
255 /// # Returns
256 ///
257 /// * `Ok(InfoStruct)` - Info Struct (Concatenated kernel commandline - includes slot,
258 /// bootconfig selection, normal_mode, Concatenated bootconfig) on success
259 /// * `Err(Error)` - on failure
unpack_boot_image( &self, boot_image_buffer: &BootImage, boot_target: Option<BootTarget>, ) -> Result<InfoStruct>260 pub fn unpack_boot_image(
261 &self,
262 boot_image_buffer: &BootImage,
263 boot_target: Option<BootTarget>,
264 ) -> Result<InfoStruct> {
265 unimplemented!();
266 }
267
268 /// Kernel Load
269 ///
270 /// Prepare kernel in RAM for booting
271 ///
272 /// # Arguments
273 /// * `info` - Info Struct from Info Load
274 /// * `image_buffer` - Buffer that contains (Verified) Boot Image
275 /// * `load_buffer` - Kernel Load buffer
276 ///
277 /// # Returns
278 ///
279 /// * `Ok(())` - on success
280 /// * `Err(Error)` - on failure
kernel_load<'b>( &self, info: &InfoStruct, image_buffer: BootImage, load_buffer: &'b mut [u8], ) -> Result<KernelImage<'b>>281 pub fn kernel_load<'b>(
282 &self,
283 info: &InfoStruct,
284 image_buffer: BootImage,
285 load_buffer: &'b mut [u8],
286 ) -> Result<KernelImage<'b>> {
287 unimplemented!();
288 }
289
290 /// Ramdisk + Bootconfig Load
291 ///
292 /// Kernel Load
293 /// (Could break this into a RD and Bootconfig specific function each, TBD)
294 /// Prepare ramdisk/bootconfig in RAM for booting
295 ///
296 /// # Arguments
297 /// * `info` - Info Struct from Info Load
298 /// * `vendor_boot_image` - Buffer that contains (Verified) Vendor Boot Image
299 /// * `init_boot_image` - Buffer that contains (Verified) Init Boot Image
300 /// * `ramdisk_load_buffer` - Ramdisk Load buffer (not compressed). It will be filled with
301 /// a concatenation of `vendor_boot_image`, `init_boot_image` and bootconfig at the end.
302 ///
303 /// # Returns
304 ///
305 /// * `Ok(&str)` - on success returns Kernel command line
306 /// * `Err(Error)` - on failure
ramdisk_bootconfig_load( &self, info: &InfoStruct, vendor_boot_image: &VendorBootImage, init_boot_image: &InitBootImage, ramdisk: &mut Ramdisk, ) -> Result<&'static str>307 pub fn ramdisk_bootconfig_load(
308 &self,
309 info: &InfoStruct,
310 vendor_boot_image: &VendorBootImage,
311 init_boot_image: &InitBootImage,
312 ramdisk: &mut Ramdisk,
313 ) -> Result<&'static str> {
314 unimplemented!();
315 }
316
317 /// DTB Update And Load
318 ///
319 /// Prepare DTB in RAM for booting
320 ///
321 /// # Arguments
322 /// * `info` - Info Struct from Info Load
323 /// * `vendor_boot_image_buffer` - Buffer that contains (Verified) Vendor Boot Image
324 ///
325 /// # Returns
326 ///
327 /// * `Ok()` - on success
328 /// * `Err(Error)` - on failure
dtb_update_and_load( &self, info: &InfoStruct, vendor_boot_image_buffer: VendorBootImage, ) -> Result<Dtb>329 pub fn dtb_update_and_load(
330 &self,
331 info: &InfoStruct,
332 vendor_boot_image_buffer: VendorBootImage,
333 ) -> Result<Dtb> {
334 unimplemented!();
335 }
336
337 /// Kernel Jump
338 ///
339 ///
340 /// # Arguments
341 /// * `kernel_load_buffer` - Kernel Load buffer
342 /// * `ramdisk_bootconfi_load_buffer` - Concatenated Ramdisk, (Bootconfig if present) Load
343 /// buffer
344 /// * `dtb_load_buffer` - DTB Load buffer
345 /// * `boot_token` - Consumable boot token
346 ///
347 /// # Returns
348 ///
349 /// * doesn't return on success
350 /// * `Err(Error)` - on failure
351 // Nevertype could be used here when it is stable https://github.com/serde-rs/serde/issues/812
kernel_jump( &self, kernel_load_buffer: KernelImage, ramdisk_load_buffer: Ramdisk, dtb_load_buffer: Dtb, boot_token: BootToken, ) -> Result<()>352 pub fn kernel_jump(
353 &self,
354 kernel_load_buffer: KernelImage,
355 ramdisk_load_buffer: Ramdisk,
356 dtb_load_buffer: Dtb,
357 boot_token: BootToken,
358 ) -> Result<()> {
359 unimplemented!();
360 }
361
362 /// Load, verify, and boot
363 ///
364 /// Wrapper around the above functions for devices that don't need custom behavior between each
365 /// step
366 ///
367 /// Warning: If the call to load_verify_boot fails, the device MUST
368 /// be restarted in order to make forward boot progress.
369 /// Callers MAY log the error, enter an interactive mode,
370 /// or take other actions before rebooting.
371 ///
372 /// # Arguments
373 /// * `avb_ops` - implementation for `avb::Ops` that would be borrowed in result to prevent
374 /// changes to partitions until it is out of scope.
375 /// * `partitions_to_verify` - names of all the partitions to verify with libavb.
376 /// * `partitions_ram_map` - Partitions to verify and optional address for them to be loaded.
377 /// * `slot_verify_flags` - AVB slot verification flags
378 /// * `slot_cursor` - Cursor object that manages interactions with boot slot management
379 /// * `kernel_load_buffer` - Buffer for loading the kernel.
380 /// * `ramdisk_load_buffer` - Buffer for loading the ramdisk.
381 /// * `fdt` - Buffer containing a flattened device tree blob.
382 ///
383 /// # Returns
384 /// * doesn't return on success
385 /// * `Err(Error)` - on failure
386 // Nevertype could be used here when it is stable https://github.com/serde-rs/serde/issues/812
387 #[allow(clippy::too_many_arguments)]
load_verify_boot<'b: 'c, 'c, 'd: 'b>( &mut self, avb_ops: &mut impl avb::Ops<'b>, partitions_to_verify: &[&CStr], partitions_ram_map: &'d mut [PartitionRamMap<'b, 'c>], slot_verify_flags: SlotVerifyFlags, slot_cursor: Cursor, kernel_load_buffer: &mut [u8], ramdisk_load_buffer: &mut [u8], fdt: &mut [u8], ) -> Result<()>388 pub fn load_verify_boot<'b: 'c, 'c, 'd: 'b>(
389 &mut self,
390 avb_ops: &mut impl avb::Ops<'b>,
391 partitions_to_verify: &[&CStr],
392 partitions_ram_map: &'d mut [PartitionRamMap<'b, 'c>],
393 slot_verify_flags: SlotVerifyFlags,
394 slot_cursor: Cursor,
395 kernel_load_buffer: &mut [u8],
396 ramdisk_load_buffer: &mut [u8],
397 fdt: &mut [u8],
398 ) -> Result<()> {
399 let dtb = Dtb(&mut fdt[..]);
400 let mut ramdisk = Ramdisk(ramdisk_load_buffer);
401
402 // Call the inner method which consumes the cursor
403 // in order to properly manager cursor lifetime
404 // and cleanup.
405 let (kernel_image, token) = self.lvb_inner(
406 avb_ops,
407 &mut ramdisk,
408 kernel_load_buffer,
409 partitions_to_verify,
410 partitions_ram_map,
411 slot_verify_flags,
412 slot_cursor,
413 )?;
414
415 self.kernel_jump(kernel_image, ramdisk, dtb, token)
416 }
417
is_unrecoverable_error(error: &IntegrationError) -> bool418 fn is_unrecoverable_error(error: &IntegrationError) -> bool {
419 // Note: these ifs are nested instead of chained because multiple
420 // expressions in an if-let is an unstable features
421 if let IntegrationError::AvbSlotVerifyError(ref avb_error) = error {
422 // These are the AVB errors that are not recoverable on a subsequent attempt.
423 // If necessary in the future, this helper function can be moved to the GblOps trait
424 // and customized for platform specific behavior.
425 if matches!(
426 avb_error,
427 SlotVerifyError::Verification(_)
428 | SlotVerifyError::PublicKeyRejected
429 | SlotVerifyError::RollbackIndex
430 ) {
431 return true;
432 }
433 }
434 false
435 }
436
lvb_inner<'b: 'c, 'c, 'd: 'b, 'e>( &mut self, avb_ops: &mut impl avb::Ops<'b>, ramdisk: &mut Ramdisk, kernel_load_buffer: &'e mut [u8], partitions_to_verify: &[&CStr], partitions_ram_map: &'d mut [PartitionRamMap<'b, 'c>], slot_verify_flags: SlotVerifyFlags, slot_cursor: Cursor, ) -> Result<(KernelImage<'e>, BootToken)>437 fn lvb_inner<'b: 'c, 'c, 'd: 'b, 'e>(
438 &mut self,
439 avb_ops: &mut impl avb::Ops<'b>,
440 ramdisk: &mut Ramdisk,
441 kernel_load_buffer: &'e mut [u8],
442 partitions_to_verify: &[&CStr],
443 partitions_ram_map: &'d mut [PartitionRamMap<'b, 'c>],
444 slot_verify_flags: SlotVerifyFlags,
445 slot_cursor: Cursor,
446 ) -> Result<(KernelImage<'e>, BootToken)> {
447 let oneshot_status = slot_cursor.ctx.get_oneshot_status();
448 slot_cursor.ctx.clear_oneshot_status();
449
450 let boot_target = match oneshot_status {
451 None | Some(OneShot::Bootloader) => slot_cursor.ctx.get_boot_target()?,
452 Some(OneShot::Continue(recovery)) => BootTarget::Recovery(recovery),
453 };
454
455 let verify_data = self
456 .load_and_verify_image(
457 avb_ops,
458 partitions_to_verify,
459 slot_verify_flags,
460 Some(boot_target),
461 )
462 .map_err(|e: IntegrationError| {
463 if let BootTarget::NormalBoot(slot) = boot_target {
464 if Self::is_unrecoverable_error(&e) {
465 let _ = slot_cursor.ctx.set_slot_unbootable(
466 slot.suffix,
467 UnbootableReason::VerificationFailure,
468 );
469 } else {
470 // Note: the call to mark_boot_attempt will fail if any of the following occur:
471 // * the target was already Unbootable before the call to load_and_verify_image
472 // * policy, I/O, or other errors in mark_boot_attempt
473 //
474 // We don't really care about those circumstances.
475 // The call here is a best effort attempt to decrement tries remaining.
476 let _ = slot_cursor.ctx.mark_boot_attempt();
477 }
478 }
479 e
480 })?;
481
482 let (boot_image, init_boot_image, vendor_boot_image, _) = get_images(partitions_ram_map);
483 let boot_image = boot_image.ok_or(Error::MissingImage)?;
484 let vendor_boot_image = vendor_boot_image.ok_or(Error::MissingImage)?;
485 let init_boot_image = init_boot_image.ok_or(Error::MissingImage)?;
486
487 if is_overlap(&[
488 boot_image.0,
489 vendor_boot_image.0,
490 init_boot_image.0,
491 &ramdisk.0,
492 kernel_load_buffer,
493 ]) {
494 return Err(IntegrationError::UnificationError(Error::BufferOverlap));
495 }
496
497 let info_struct = self.unpack_boot_image(&boot_image, Some(boot_target))?;
498
499 let kernel_image = self.kernel_load(&info_struct, boot_image, kernel_load_buffer)?;
500
501 let cmd_line = self.ramdisk_bootconfig_load(
502 &info_struct,
503 &vendor_boot_image,
504 &init_boot_image,
505 ramdisk,
506 )?;
507
508 self.dtb_update_and_load(&info_struct, vendor_boot_image)?;
509
510 let token = slot_cursor.ctx.mark_boot_attempt().map_err(|_| Error::OperationProhibited)?;
511
512 Ok((kernel_image, token))
513 }
514 }
515
516 #[cfg(test)]
517 mod tests {
518 extern crate avb_sysdeps;
519 extern crate avb_test;
520 use super::*;
521 use crate::ops::test::FakeGblOps;
522 use avb::{CertPermanentAttributes, SlotVerifyError};
523 use avb_test::{FakeVbmetaKey, TestOps};
524 use std::{fs, path::Path};
525 use zerocopy::FromBytes;
526
527 const TEST_ZIRCON_PARTITION_NAME: &str = "zircon_a";
528 const TEST_ZIRCON_PARTITION_NAME_CSTR: &CStr = c"zircon_a";
529 const TEST_ZIRCON_IMAGE_PATH: &str = "zircon_a.zbi";
530 const TEST_ZIRCON_VBMETA_PATH: &str = "zircon_a.vbmeta";
531 const TEST_ZIRCON_VBMETA_CERT_PATH: &str = "zircon_a.vbmeta.cert";
532 const TEST_PUBLIC_KEY_PATH: &str = "testkey_rsa4096_pub.bin";
533 const TEST_PERMANENT_ATTRIBUTES_PATH: &str = "cert_permanent_attributes.bin";
534 const TEST_PERMANENT_ATTRIBUTES_HASH_PATH: &str = "cert_permanent_attributes.hash";
535 const TEST_BAD_PERMANENT_ATTRIBUTES_PATH: &str = "cert_permanent_attributes.bad.bin";
536 const TEST_BAD_PERMANENT_ATTRIBUTES_HASH_PATH: &str = "cert_permanent_attributes.bad.hash";
537 const TEST_VBMETA_ROLLBACK_LOCATION: usize = 0; // Default value, we don't explicitly set this.
538 pub const TEST_CERT_PIK_VERSION: u64 = 42;
539 pub const TEST_CERT_PSK_VERSION: u64 = 42;
540
541 /// Returns the contents of a test data file.
542 ///
543 /// Panicks if the requested file cannot be read.
544 ///
545 /// # Arguments
546 /// * `path`: file path relative to libgbl's `testdata/` directory.
testdata(path: &str) -> Vec<u8>547 fn testdata(path: &str) -> Vec<u8> {
548 let full_path = Path::new("external/gbl/libgbl/testdata").join(path);
549 fs::read(full_path).unwrap()
550 }
551
552 /// Creates and returns a configured avb `TestOps`.
553 ///
554 /// The initial state will verify successfully with:
555 /// * a valid vbmeta image in the `vbmeta` partition, containing a hash descriptor for the
556 /// `TEST_ZIRCON_PARTITION_NAME` partition
557 /// * an image in the `TEST_ZIRCON_PARTITION_NAME` partition matching the vbmeta hash
558 /// * no preloaded partition data
559 /// * a public key matching the vbmeta image
560 /// * a valid vbmeta rollback index
561 /// * a locked bootloader state
562 ///
563 /// The caller can modify any of this state as needed for their particular test.
test_avb_ops() -> TestOps<'static>564 fn test_avb_ops() -> TestOps<'static> {
565 let mut avb_ops = TestOps::default();
566
567 avb_ops.add_partition(TEST_ZIRCON_PARTITION_NAME, testdata(TEST_ZIRCON_IMAGE_PATH));
568 avb_ops.add_partition("vbmeta", testdata(TEST_ZIRCON_VBMETA_PATH));
569 avb_ops.default_vbmeta_key = Some(FakeVbmetaKey::Avb {
570 public_key: testdata(TEST_PUBLIC_KEY_PATH),
571 public_key_metadata: None,
572 });
573 avb_ops.rollbacks.insert(TEST_VBMETA_ROLLBACK_LOCATION, Ok(0));
574 avb_ops.unlock_state = Ok(false);
575
576 avb_ops
577 }
578
579 /// Similar to `test_avb_ops()`, but with the avb_cert extension enabled.
test_avb_cert_ops() -> TestOps<'static>580 fn test_avb_cert_ops() -> TestOps<'static> {
581 let mut avb_ops = test_avb_ops();
582
583 // Replace vbmeta with the cert-signed version.
584 avb_ops.add_partition("vbmeta", testdata(TEST_ZIRCON_VBMETA_CERT_PATH));
585
586 // Tell `avb_ops` to use cert APIs and to route the default key through cert validation.
587 avb_ops.use_cert = true;
588 avb_ops.default_vbmeta_key = Some(FakeVbmetaKey::Cert);
589
590 // Add the permanent attributes.
591 let perm_attr_bytes = testdata(TEST_PERMANENT_ATTRIBUTES_PATH);
592 let perm_attr_hash = testdata(TEST_PERMANENT_ATTRIBUTES_HASH_PATH);
593 avb_ops.cert_permanent_attributes =
594 Some(CertPermanentAttributes::read_from(&perm_attr_bytes[..]).unwrap());
595 avb_ops.cert_permanent_attributes_hash = Some(perm_attr_hash.try_into().unwrap());
596
597 // Add the rollbacks for the cert keys.
598 avb_ops.rollbacks.insert(avb::CERT_PIK_VERSION_LOCATION, Ok(TEST_CERT_PIK_VERSION));
599 avb_ops.rollbacks.insert(avb::CERT_PSK_VERSION_LOCATION, Ok(TEST_CERT_PSK_VERSION));
600
601 avb_ops
602 }
603
604 #[test]
test_load_and_verify_image_success()605 fn test_load_and_verify_image_success() {
606 let mut gbl_ops = FakeGblOps::default();
607 let mut gbl = Gbl::new(&mut gbl_ops);
608 let mut avb_ops = test_avb_ops();
609
610 let res = gbl.load_and_verify_image(
611 &mut avb_ops,
612 &mut [&TEST_ZIRCON_PARTITION_NAME_CSTR],
613 SlotVerifyFlags::AVB_SLOT_VERIFY_FLAGS_NONE,
614 None,
615 );
616 assert!(res.is_ok());
617 }
618
619 #[test]
test_load_and_verify_image_verification_error()620 fn test_load_and_verify_image_verification_error() {
621 let mut gbl_ops = FakeGblOps::default();
622 let mut gbl = Gbl::new(&mut gbl_ops);
623 let mut avb_ops = test_avb_ops();
624
625 // Modify the kernel image, it should now fail to validate against the vbmeta image.
626 avb_ops.partitions.get_mut(TEST_ZIRCON_PARTITION_NAME).unwrap().contents.as_mut_vec()[0] ^=
627 0x01;
628
629 let res = gbl.load_and_verify_image(
630 &mut avb_ops,
631 &mut [&TEST_ZIRCON_PARTITION_NAME_CSTR],
632 SlotVerifyFlags::AVB_SLOT_VERIFY_FLAGS_NONE,
633 None,
634 );
635 assert_eq!(
636 res.unwrap_err(),
637 IntegrationError::AvbSlotVerifyError(SlotVerifyError::Verification(None))
638 );
639 }
640
641 #[test]
test_load_and_verify_image_io_error()642 fn test_load_and_verify_image_io_error() {
643 let mut gbl_ops = FakeGblOps::default();
644 let mut gbl = Gbl::new(&mut gbl_ops);
645 let mut avb_ops = test_avb_ops();
646
647 // Erase the fake rollbacks, which will result in an I/O error when attempting to access.
648 avb_ops.rollbacks.clear();
649
650 let res = gbl.load_and_verify_image(
651 &mut avb_ops,
652 &mut [&TEST_ZIRCON_PARTITION_NAME_CSTR],
653 SlotVerifyFlags::AVB_SLOT_VERIFY_FLAGS_NONE,
654 None,
655 );
656 assert_eq!(res.unwrap_err(), IntegrationError::AvbSlotVerifyError(SlotVerifyError::Io));
657 }
658
659 #[test]
test_load_and_verify_image_with_cert_success()660 fn test_load_and_verify_image_with_cert_success() {
661 let mut gbl_ops = FakeGblOps::default();
662 let mut gbl = Gbl::new(&mut gbl_ops);
663 let mut avb_ops = test_avb_cert_ops();
664
665 let res = gbl.load_and_verify_image(
666 &mut avb_ops,
667 &mut [&TEST_ZIRCON_PARTITION_NAME_CSTR],
668 SlotVerifyFlags::AVB_SLOT_VERIFY_FLAGS_NONE,
669 None,
670 );
671 assert!(res.is_ok());
672 }
673
674 #[test]
test_load_and_verify_image_with_cert_permanent_attribute_mismatch_error()675 fn test_load_and_verify_image_with_cert_permanent_attribute_mismatch_error() {
676 let mut gbl_ops = FakeGblOps::default();
677 let mut gbl = Gbl::new(&mut gbl_ops);
678 let mut avb_ops = test_avb_cert_ops();
679
680 // Swap in the corrupted permanent attributes, which should cause the vbmeta image to fail
681 // validation due to key mismatch.
682 let perm_attr_bytes = testdata(TEST_BAD_PERMANENT_ATTRIBUTES_PATH);
683 let perm_attr_hash = testdata(TEST_BAD_PERMANENT_ATTRIBUTES_HASH_PATH);
684 avb_ops.cert_permanent_attributes =
685 Some(CertPermanentAttributes::read_from(&perm_attr_bytes[..]).unwrap());
686 avb_ops.cert_permanent_attributes_hash = Some(perm_attr_hash.try_into().unwrap());
687
688 let res = gbl.load_and_verify_image(
689 &mut avb_ops,
690 &mut [&TEST_ZIRCON_PARTITION_NAME_CSTR],
691 SlotVerifyFlags::AVB_SLOT_VERIFY_FLAGS_NONE,
692 None,
693 );
694 assert_eq!(
695 res.unwrap_err(),
696 IntegrationError::AvbSlotVerifyError(SlotVerifyError::PublicKeyRejected)
697 );
698 }
699 }
700