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