1 // Copyright 2024, 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 use crate::{
16 gbl_avb::{
17 ops::{GblAvbOps, AVB_DIGEST_KEY},
18 state::{BootStateColor, KeyValidationStatus},
19 },
20 gbl_print, gbl_println, GblOps, Result,
21 };
22 use abr::SlotIndex;
23 use arrayvec::ArrayVec;
24 use avb::{slot_verify, HashtreeErrorMode, Ops as _, SlotVerifyFlags};
25 use bootparams::{bootconfig::BootConfigBuilder, entry::CommandlineParser};
26 use core::{ffi::CStr, fmt::Write};
27 use liberror::Error;
28
29 // Maximum number of partition allowed for verification.
30 //
31 // The value is randomly chosen for now. We can update it as we see more usecases.
32 const MAX_NUM_PARTITION: usize = 16;
33
34 // Type alias for ArrayVec of size `MAX_NUM_PARTITION`:
35 type ArrayMaxParts<T> = ArrayVec<T, MAX_NUM_PARTITION>;
36
37 /// A container holding partitions for libavb verification
38 pub(crate) struct PartitionsToVerify<'a> {
39 partitions: ArrayMaxParts<&'a CStr>,
40 preloaded: ArrayMaxParts<(&'a str, &'a [u8])>,
41 }
42
43 impl<'a> PartitionsToVerify<'a> {
44 /// Appends a partition to verify
45 #[cfg(test)]
try_push(&mut self, name: &'a CStr) -> Result<()>46 pub fn try_push(&mut self, name: &'a CStr) -> Result<()> {
47 self.partitions.try_push(name).or(Err(Error::TooManyPartitions(MAX_NUM_PARTITION)))?;
48 Ok(())
49 }
50
51 /// Appends a partition, along with its preloaded data
try_push_preloaded(&mut self, name: &'a CStr, data: &'a [u8]) -> Result<()>52 pub fn try_push_preloaded(&mut self, name: &'a CStr, data: &'a [u8]) -> Result<()> {
53 let err = Err(Error::TooManyPartitions(MAX_NUM_PARTITION));
54 self.partitions.try_push(name).or(err)?;
55 self.preloaded.try_push((name.to_str().unwrap(), data)).or(err)?;
56 Ok(())
57 }
58
59 /// Appends partitions, along with preloaded data
try_extend_preloaded(&mut self, partitions: &PartitionsToVerify<'a>) -> Result<()>60 pub fn try_extend_preloaded(&mut self, partitions: &PartitionsToVerify<'a>) -> Result<()> {
61 let err = Err(Error::TooManyPartitions(MAX_NUM_PARTITION));
62 self.partitions.try_extend_from_slice(partitions.partitions()).or(err)?;
63 self.preloaded.try_extend_from_slice(partitions.preloaded()).or(err)?;
64 Ok(())
65 }
66
partitions(&self) -> &[&'a CStr]67 fn partitions(&self) -> &[&'a CStr] {
68 &self.partitions
69 }
70
preloaded(&self) -> &[(&'a str, &'a [u8])]71 fn preloaded(&self) -> &[(&'a str, &'a [u8])] {
72 &self.preloaded
73 }
74 }
75
76 impl<'a> Default for PartitionsToVerify<'a> {
default() -> Self77 fn default() -> Self {
78 Self { partitions: ArrayMaxParts::new(), preloaded: ArrayMaxParts::new() }
79 }
80 }
81
82 /// Android verified boot flow.
83 ///
84 /// All relevant images from disk must be preloaded and provided as `partitions`; in its final
85 /// state `ops` will provide the necessary callbacks for where the images should go in RAM and
86 /// which ones are preloaded.
87 ///
88 /// # Arguments
89 /// * `ops`: [GblOps] providing device-specific backend.
90 /// * `slot`: The slot index.
91 /// * `partitions`: [PartitionsToVerify] providing pre-loaded partitions.
92 /// * `bootconfig_builder`: object to write the bootconfig data into.
93 ///
94 /// # Returns
95 /// `()` on success. Returns an error if verification process failed and boot cannot
96 /// continue, or if parsing the command line or updating the boot configuration fail.
avb_verify_slot<'a, 'b, 'c>( ops: &mut impl GblOps<'a, 'b>, slot: u8, partitions: &PartitionsToVerify<'c>, bootconfig_builder: &mut BootConfigBuilder, ) -> Result<()>97 pub(crate) fn avb_verify_slot<'a, 'b, 'c>(
98 ops: &mut impl GblOps<'a, 'b>,
99 slot: u8,
100 partitions: &PartitionsToVerify<'c>,
101 bootconfig_builder: &mut BootConfigBuilder,
102 ) -> Result<()> {
103 let slot = match slot {
104 0 => SlotIndex::A,
105 1 => SlotIndex::B,
106 _ => {
107 gbl_println!(ops, "AVB: Invalid slot index: {slot}");
108 return Err(Error::InvalidInput.into());
109 }
110 };
111
112 let mut avb_ops = GblAvbOps::new(ops, Some(slot), partitions.preloaded(), false);
113 let unlocked = avb_ops.read_is_device_unlocked()?;
114 let verify_result = slot_verify(
115 &mut avb_ops,
116 partitions.partitions(),
117 Some(slot.into()),
118 // TODO(b/337846185): Pass AVB_SLOT_VERIFY_FLAGS_RESTART_CAUSED_BY_HASHTREE_CORRUPTION in
119 // case verity corruption is detected by HLOS.
120 match unlocked {
121 true => SlotVerifyFlags::AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR,
122 _ => SlotVerifyFlags::AVB_SLOT_VERIFY_FLAGS_NONE,
123 },
124 // TODO(b/337846185): For demo, we use the same setting as Cuttlefish u-boot.
125 // Pass AVB_HASHTREE_ERROR_MODE_MANAGED_RESTART_AND_EIO and handle EIO.
126 HashtreeErrorMode::AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE,
127 );
128 let (color, verify_data) = match verify_result {
129 Ok(ref verify_data) => {
130 let color = match unlocked {
131 false
132 if avb_ops.key_validation_status()? == KeyValidationStatus::ValidCustomKey =>
133 {
134 BootStateColor::Yellow
135 }
136 false => BootStateColor::Green,
137 true => BootStateColor::Orange,
138 };
139
140 gbl_println!(
141 avb_ops.gbl_ops,
142 "AVB verification passed. Device is unlocked: {unlocked}. Color: {color}"
143 );
144
145 (color, Some(verify_data))
146 }
147 // Non-fatal error, can continue booting since verify_data is available.
148 Err(ref e) if e.verification_data().is_some() && unlocked => {
149 let color = BootStateColor::Orange;
150
151 gbl_println!(
152 avb_ops.gbl_ops,
153 "AVB verification failed with {e}. Device is unlocked: {unlocked}. Color: {color}. \
154 Continue current boot attempt."
155 );
156
157 (color, Some(e.verification_data().unwrap()))
158 }
159 // Fatal error. Cannot boot.
160 Err(ref e) => {
161 let color = BootStateColor::Red;
162
163 gbl_println!(
164 avb_ops.gbl_ops,
165 "AVB verification failed with {e}. Device is unlocked: {unlocked}. Color: {color}. \
166 Cannot continue boot."
167 );
168
169 (color, None)
170 }
171 };
172
173 // Gets digest from the result command line.
174 let mut digest = None;
175 if let Some(ref verify_data) = verify_data {
176 for entry in CommandlineParser::new(verify_data.cmdline().to_str().unwrap()) {
177 let entry = entry?;
178 if entry.key == AVB_DIGEST_KEY {
179 digest = entry.value;
180 }
181 write!(bootconfig_builder, "{}\n", entry).or(Err(Error::BufferTooSmall(None)))?;
182 }
183 }
184
185 // Allowes FW to handle verification result.
186 avb_ops.handle_verification_result(verify_data, color, digest)?;
187
188 match color {
189 BootStateColor::Red => Err(verify_result.unwrap_err().without_verify_data().into()),
190 _ => {
191 write!(bootconfig_builder, "androidboot.verifiedbootstate={}\n", color)
192 .or(Err(Error::BufferTooSmall(None)))?;
193
194 Ok(())
195 }
196 }
197 }
198
199 #[cfg(test)]
200 mod test {
201 use super::*;
202 use crate::{
203 android_boot::load::tests::{
204 dump_bootconfig, make_bootconfig, read_test_data, read_test_data_as_str,
205 AvbResultBootconfigBuilder, TEST_PUBLIC_KEY_DIGEST,
206 },
207 ops::test::{FakeGblOps, FakeGblOpsStorage},
208 IntegrationError::AvbIoError,
209 };
210 use avb::{IoError, SlotVerifyError};
211 use std::{collections::HashMap, ffi::CStr};
212
213 /// Helper for testing avb_verify_slot
test_avb_verify_slot<'a>( partitions: &[(&CStr, &str)], partitions_to_verify: &PartitionsToVerify<'a>, device_unlocked: std::result::Result<bool, avb::IoError>, rollback_result: std::result::Result<u64, avb::IoError>, slot: u8, expected_reported_color: Option<BootStateColor>, expected_bootconfig: &[u8], ) -> Result<()>214 fn test_avb_verify_slot<'a>(
215 partitions: &[(&CStr, &str)],
216 partitions_to_verify: &PartitionsToVerify<'a>,
217 device_unlocked: std::result::Result<bool, avb::IoError>,
218 rollback_result: std::result::Result<u64, avb::IoError>,
219 slot: u8,
220 expected_reported_color: Option<BootStateColor>,
221 expected_bootconfig: &[u8],
222 ) -> Result<()> {
223 let mut storage = FakeGblOpsStorage::default();
224 for (part, file) in partitions {
225 storage.add_raw_device(part, read_test_data(file));
226 }
227 let mut ops = FakeGblOps::new(&storage);
228 ops.avb_ops.unlock_state = device_unlocked;
229 ops.avb_ops.rollbacks = HashMap::from([(1, rollback_result)]);
230 let mut out_color = None;
231 let mut handler = |color,
232 _: Option<&CStr>,
233 _: Option<&[u8]>,
234 _: Option<&[u8]>,
235 _: Option<&[u8]>,
236 _: Option<&[u8]>,
237 _: Option<&[u8]>,
238 _: Option<&[u8]>| {
239 out_color = Some(color);
240 Ok(())
241 };
242 ops.avb_handle_verification_result = Some(&mut handler);
243 ops.avb_key_validation_status = Some(Ok(KeyValidationStatus::Valid));
244
245 let mut bootconfig_buffer = vec![0u8; 512 * 1024];
246 let mut bootconfig_builder = BootConfigBuilder::new(&mut bootconfig_buffer).unwrap();
247 let verify_result =
248 avb_verify_slot(&mut ops, slot, partitions_to_verify, &mut bootconfig_builder);
249 let bootconfig_bytes = bootconfig_builder.config_bytes();
250
251 assert_eq!(out_color, expected_reported_color);
252 assert_eq!(
253 bootconfig_bytes,
254 expected_bootconfig,
255 "\nexpect: \n{}\nactual: \n{}\n",
256 dump_bootconfig(expected_bootconfig),
257 dump_bootconfig(bootconfig_bytes),
258 );
259
260 verify_result
261 }
262
263 #[test]
test_avb_verify_slot_success()264 fn test_avb_verify_slot_success() {
265 let mut partitions_to_verify = PartitionsToVerify::default();
266 partitions_to_verify.try_push(c"boot").unwrap();
267 partitions_to_verify.try_push(c"init_boot").unwrap();
268 partitions_to_verify.try_push(c"vendor_boot").unwrap();
269 let partitions_data = [
270 (c"boot_a", "boot_no_ramdisk_v4_a.img"),
271 (c"init_boot_a", "init_boot_a.img"),
272 (c"vendor_boot_a", "vendor_boot_v4_a.img"),
273 (c"vbmeta_a", "vbmeta_v4_v4_init_boot_a.img"),
274 ];
275 let expected_bootconfig = AvbResultBootconfigBuilder::new()
276 .vbmeta_size(read_test_data("vbmeta_v4_v4_init_boot_a.img").len())
277 .digest(
278 read_test_data_as_str("vbmeta_v4_v4_init_boot_a.digest.txt")
279 .strip_suffix("\n")
280 .unwrap(),
281 )
282 .partition_digest(
283 "boot",
284 read_test_data_as_str("vbmeta_v4_v4_init_boot_a.boot.digest.txt")
285 .strip_suffix("\n")
286 .unwrap(),
287 )
288 .partition_digest(
289 "init_boot",
290 read_test_data_as_str("vbmeta_v4_v4_init_boot_a.init_boot.digest.txt")
291 .strip_suffix("\n")
292 .unwrap(),
293 )
294 .partition_digest(
295 "vendor_boot",
296 read_test_data_as_str("vbmeta_v4_v4_init_boot_a.vendor_boot.digest.txt")
297 .strip_suffix("\n")
298 .unwrap(),
299 )
300 .public_key_digest(TEST_PUBLIC_KEY_DIGEST)
301 .build();
302
303 assert_eq!(
304 test_avb_verify_slot(
305 &partitions_data,
306 &partitions_to_verify,
307 // Unlocked result
308 Ok(false),
309 // Rollback index result
310 Ok(0),
311 // Slot
312 0,
313 // Expected color
314 Some(BootStateColor::Green),
315 // Expected bootcofnig
316 &expected_bootconfig,
317 ),
318 Ok(()),
319 );
320 }
321
322 #[test]
test_avb_verify_slot_from_preloaded_success()323 fn test_avb_verify_slot_from_preloaded_success() {
324 let boot = read_test_data("boot_no_ramdisk_v4_a.img");
325 let init_boot = read_test_data("init_boot_a.img");
326 let vendor_boot = read_test_data("vendor_boot_v4_a.img");
327
328 let mut partitions_to_verify = PartitionsToVerify::default();
329 partitions_to_verify.try_push_preloaded(c"boot", &boot).unwrap();
330 partitions_to_verify.try_push_preloaded(c"init_boot", &init_boot).unwrap();
331 partitions_to_verify.try_push_preloaded(c"vendor_boot", &vendor_boot).unwrap();
332 let partitions_data = [
333 // Required images aren't presented. Have to rely on preloaded.
334 (c"vbmeta_a", "vbmeta_v4_v4_init_boot_a.img"),
335 ];
336 let expected_bootconfig = AvbResultBootconfigBuilder::new()
337 .vbmeta_size(read_test_data("vbmeta_v4_v4_init_boot_a.img").len())
338 .digest(
339 read_test_data_as_str("vbmeta_v4_v4_init_boot_a.digest.txt")
340 .strip_suffix("\n")
341 .unwrap(),
342 )
343 .partition_digest(
344 "boot",
345 read_test_data_as_str("vbmeta_v4_v4_init_boot_a.boot.digest.txt")
346 .strip_suffix("\n")
347 .unwrap(),
348 )
349 .partition_digest(
350 "init_boot",
351 read_test_data_as_str("vbmeta_v4_v4_init_boot_a.init_boot.digest.txt")
352 .strip_suffix("\n")
353 .unwrap(),
354 )
355 .partition_digest(
356 "vendor_boot",
357 read_test_data_as_str("vbmeta_v4_v4_init_boot_a.vendor_boot.digest.txt")
358 .strip_suffix("\n")
359 .unwrap(),
360 )
361 .public_key_digest(TEST_PUBLIC_KEY_DIGEST)
362 .build();
363
364 assert_eq!(
365 test_avb_verify_slot(
366 &partitions_data,
367 &partitions_to_verify,
368 // Unlocked result
369 Ok(false),
370 // Rollback index result
371 Ok(0),
372 // Slot
373 0,
374 // Expected color
375 Some(BootStateColor::Green),
376 // Expected bootcofnig
377 &expected_bootconfig,
378 ),
379 Ok(()),
380 );
381 }
382
383 #[test]
test_avb_verify_slot_success_unlocked()384 fn test_avb_verify_slot_success_unlocked() {
385 let mut partitions_to_verify = PartitionsToVerify::default();
386 partitions_to_verify.try_push(c"boot").unwrap();
387 partitions_to_verify.try_push(c"init_boot").unwrap();
388 partitions_to_verify.try_push(c"vendor_boot").unwrap();
389 let partitions_data = [
390 (c"boot_a", "boot_no_ramdisk_v4_a.img"),
391 (c"init_boot_a", "init_boot_a.img"),
392 (c"vendor_boot_a", "vendor_boot_v4_a.img"),
393 (c"vbmeta_a", "vbmeta_v4_v4_init_boot_a.img"),
394 ];
395 let expected_bootconfig = AvbResultBootconfigBuilder::new()
396 .vbmeta_size(read_test_data("vbmeta_v4_v4_init_boot_a.img").len())
397 .digest(
398 read_test_data_as_str("vbmeta_v4_v4_init_boot_a.digest.txt")
399 .strip_suffix("\n")
400 .unwrap(),
401 )
402 .partition_digest(
403 "boot",
404 read_test_data_as_str("vbmeta_v4_v4_init_boot_a.boot.digest.txt")
405 .strip_suffix("\n")
406 .unwrap(),
407 )
408 .partition_digest(
409 "init_boot",
410 read_test_data_as_str("vbmeta_v4_v4_init_boot_a.init_boot.digest.txt")
411 .strip_suffix("\n")
412 .unwrap(),
413 )
414 .partition_digest(
415 "vendor_boot",
416 read_test_data_as_str("vbmeta_v4_v4_init_boot_a.vendor_boot.digest.txt")
417 .strip_suffix("\n")
418 .unwrap(),
419 )
420 .public_key_digest(TEST_PUBLIC_KEY_DIGEST)
421 .color(BootStateColor::Orange)
422 .unlocked(true)
423 .build();
424
425 assert_eq!(
426 test_avb_verify_slot(
427 &partitions_data,
428 &partitions_to_verify,
429 // Unlocked result
430 Ok(true),
431 // Rollback index result
432 Ok(0),
433 // Slot
434 0,
435 // Expected color
436 Some(BootStateColor::Orange),
437 // Expected bootconfig
438 &expected_bootconfig,
439 ),
440 Ok(()),
441 );
442 }
443
444 #[test]
test_avb_verify_slot_verification_failed_unlocked()445 fn test_avb_verify_slot_verification_failed_unlocked() {
446 let mut partitions_to_verify = PartitionsToVerify::default();
447 partitions_to_verify.try_push(c"boot").unwrap();
448 partitions_to_verify.try_push(c"init_boot").unwrap();
449 partitions_to_verify.try_push(c"vendor_boot").unwrap();
450 let partitions_data = [
451 (c"boot_a", "boot_no_ramdisk_v4_a.img"),
452 (c"init_boot_a", "init_boot_a.img"),
453 (c"vendor_boot_a", "vendor_boot_v4_a.img"),
454 (c"vbmeta_a", "vbmeta_v4_v4_init_boot_a.img"),
455 ];
456 let expected_bootconfig = AvbResultBootconfigBuilder::new()
457 .vbmeta_size(read_test_data("vbmeta_v4_v4_init_boot_a.img").len())
458 .digest(
459 read_test_data_as_str("vbmeta_v4_v4_init_boot_a.digest.txt")
460 .strip_suffix("\n")
461 .unwrap(),
462 )
463 .partition_digest(
464 "boot",
465 read_test_data_as_str("vbmeta_v4_v4_init_boot_a.boot.digest.txt")
466 .strip_suffix("\n")
467 .unwrap(),
468 )
469 .partition_digest(
470 "init_boot",
471 read_test_data_as_str("vbmeta_v4_v4_init_boot_a.init_boot.digest.txt")
472 .strip_suffix("\n")
473 .unwrap(),
474 )
475 .partition_digest(
476 "vendor_boot",
477 read_test_data_as_str("vbmeta_v4_v4_init_boot_a.vendor_boot.digest.txt")
478 .strip_suffix("\n")
479 .unwrap(),
480 )
481 .public_key_digest(TEST_PUBLIC_KEY_DIGEST)
482 .color(BootStateColor::Orange)
483 .unlocked(true)
484 .build();
485
486 assert_eq!(
487 test_avb_verify_slot(
488 &partitions_data,
489 &partitions_to_verify,
490 // Unlocked result
491 Ok(true),
492 // Rollback index result
493 Ok(0),
494 // Slot
495 0,
496 // Expected color
497 Some(BootStateColor::Orange),
498 // Expected bootconfig
499 &expected_bootconfig,
500 ),
501 // Device is unlocked, so can continue boot
502 Ok(()),
503 );
504 }
505
506 #[test]
test_avb_verify_slot_verification_fatal_failed_unlocked()507 fn test_avb_verify_slot_verification_fatal_failed_unlocked() {
508 let mut partitions_to_verify = PartitionsToVerify::default();
509 partitions_to_verify.try_push(c"boot").unwrap();
510 partitions_to_verify.try_push(c"init_boot").unwrap();
511 partitions_to_verify.try_push(c"vendor_boot").unwrap();
512 let partitions_data = [
513 (c"boot_a", "boot_no_ramdisk_v4_a.img"),
514 (c"init_boot_a", "init_boot_a.img"),
515 (c"vendor_boot_a", "vendor_boot_v4_a.img"),
516 (c"vbmeta_a", "vbmeta_v4_v4_init_boot_a.img"),
517 ];
518 let expected_bootconfig = make_bootconfig("");
519
520 assert_eq!(
521 test_avb_verify_slot(
522 &partitions_data,
523 &partitions_to_verify,
524 // Unlocked result
525 Ok(true),
526 // Get rollback index is failed
527 Err(IoError::NoSuchValue),
528 // Slot
529 0,
530 // Expected color
531 Some(BootStateColor::Red),
532 // Expected bootconfig
533 &expected_bootconfig,
534 ),
535 // Fatal error, so cannot continue boot
536 Err(SlotVerifyError::Io.into()),
537 );
538 }
539
540 #[test]
test_avb_verify_slot_verification_failed_locked()541 fn test_avb_verify_slot_verification_failed_locked() {
542 let mut partitions_to_verify = PartitionsToVerify::default();
543 partitions_to_verify.try_push(c"boot").unwrap();
544 partitions_to_verify.try_push(c"init_boot").unwrap();
545 partitions_to_verify.try_push(c"vendor_boot").unwrap();
546 let partitions_data = [
547 // Wrong boot image, expect verification to fail.
548 (c"boot_a", "boot_v0_a.img"),
549 (c"init_boot_a", "init_boot_a.img"),
550 (c"vendor_boot_a", "vendor_boot_v4_a.img"),
551 (c"vbmeta_a", "vbmeta_v4_v4_init_boot_a.img"),
552 ];
553 let expected_bootconfig = make_bootconfig("");
554
555 assert_eq!(
556 test_avb_verify_slot(
557 &partitions_data,
558 &partitions_to_verify,
559 // Unlocked result
560 Ok(false),
561 // Rollback index result
562 Ok(0),
563 // Slot
564 0,
565 // Expected color
566 Some(BootStateColor::Red),
567 // Expected bootconfig
568 &expected_bootconfig,
569 ),
570 // Cannot continue boot
571 Err(SlotVerifyError::Verification(None).into()),
572 );
573 }
574
575 #[test]
test_avb_verify_slot_verification_failed_obtain_lock_status()576 fn test_avb_verify_slot_verification_failed_obtain_lock_status() {
577 let partitions_to_verify = PartitionsToVerify::default();
578 let expected_bootconfig = make_bootconfig("");
579
580 assert_eq!(
581 test_avb_verify_slot(
582 &[],
583 &partitions_to_verify,
584 // Unlocked result
585 Err(avb::IoError::NoSuchValue),
586 // Rollback index result
587 Ok(0),
588 // Slot
589 0,
590 // Expected color
591 None,
592 // Expected bootconfig
593 &expected_bootconfig,
594 ),
595 // Cannot continue boot
596 Err(AvbIoError(IoError::NoSuchValue)),
597 );
598 }
599 }
600