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 //! libavb_rs verification tests.
16
17 use crate::{
18 build_test_ops_one_image_one_vbmeta,
19 test_data::*,
20 test_ops::{FakeVbmetaKey, TestOps},
21 verify_one_image_one_vbmeta,
22 };
23 use avb::{
24 slot_verify, ChainPartitionDescriptor, ChainPartitionDescriptorFlags, Descriptor,
25 HashDescriptor, HashDescriptorFlags, HashtreeDescriptor, HashtreeDescriptorFlags,
26 HashtreeErrorMode, IoError, KernelCommandlineDescriptor, KernelCommandlineDescriptorFlags,
27 PropertyDescriptor, SlotVerifyData, SlotVerifyError, SlotVerifyFlags, SlotVerifyResult,
28 };
29 use hex::decode;
30 use std::{ffi::CString, fs};
31 #[cfg(feature = "uuid")]
32 use uuid::uuid;
33
34 /// Initializes a `TestOps` object such that verification will succeed on `TEST_PARTITION_NAME` and
35 /// `TEST_PARTITION_2_NAME`.
build_test_ops_two_images_one_vbmeta<'a>() -> TestOps<'a>36 fn build_test_ops_two_images_one_vbmeta<'a>() -> TestOps<'a> {
37 let mut ops = build_test_ops_one_image_one_vbmeta();
38 // Add in the contents of the second partition and overwrite the vbmeta partition to
39 // include both partition descriptors.
40 ops.add_partition(TEST_PARTITION_2_NAME, fs::read(TEST_IMAGE_PATH).unwrap());
41 ops.add_partition("vbmeta", fs::read(TEST_VBMETA_2_PARTITIONS_PATH).unwrap());
42 ops
43 }
44
45 /// Calls `slot_verify()` for both test partitions.
verify_two_images<'a>(ops: &mut TestOps<'a>) -> SlotVerifyResult<'a, SlotVerifyData<'a>>46 fn verify_two_images<'a>(ops: &mut TestOps<'a>) -> SlotVerifyResult<'a, SlotVerifyData<'a>> {
47 slot_verify(
48 ops,
49 &[
50 &CString::new(TEST_PARTITION_NAME).unwrap(),
51 &CString::new(TEST_PARTITION_2_NAME).unwrap(),
52 ],
53 None,
54 SlotVerifyFlags::AVB_SLOT_VERIFY_FLAGS_NONE,
55 HashtreeErrorMode::AVB_HASHTREE_ERROR_MODE_EIO,
56 )
57 }
58
59 /// Initializes a `TestOps` object such that verification will succeed on the `boot` partition with
60 /// a combined image + vbmeta.
build_test_ops_boot_partition<'a>() -> TestOps<'a>61 fn build_test_ops_boot_partition<'a>() -> TestOps<'a> {
62 let mut ops = build_test_ops_one_image_one_vbmeta();
63 ops.partitions.clear();
64 ops.add_partition(
65 "boot",
66 fs::read(TEST_IMAGE_WITH_VBMETA_FOOTER_FOR_BOOT_PATH).unwrap(),
67 );
68 ops
69 }
70
71 /// Calls `slot_verify()` using standard args for `build_test_ops_boot_partition()` setup.
verify_boot_partition<'a>(ops: &mut TestOps<'a>) -> SlotVerifyResult<'a, SlotVerifyData<'a>>72 fn verify_boot_partition<'a>(ops: &mut TestOps<'a>) -> SlotVerifyResult<'a, SlotVerifyData<'a>> {
73 slot_verify(
74 ops,
75 &[&CString::new("boot").unwrap()],
76 None,
77 // libavb has some special-case handling to automatically detect a combined image + vbmeta
78 // in the `boot` partition; don't pass the `AVB_SLOT_VERIFY_FLAGS_NO_VBMETA_PARTITION` flag
79 // so we can test this behavior.
80 SlotVerifyFlags::AVB_SLOT_VERIFY_FLAGS_NONE,
81 HashtreeErrorMode::AVB_HASHTREE_ERROR_MODE_EIO,
82 )
83 }
84
85 /// Initializes a `TestOps` object such that verification will succeed on
86 /// `TEST_PARTITION_PERSISTENT_DIGEST_NAME`.
build_test_ops_persistent_digest<'a>(image: Vec<u8>) -> TestOps<'a>87 fn build_test_ops_persistent_digest<'a>(image: Vec<u8>) -> TestOps<'a> {
88 let mut ops = build_test_ops_one_image_one_vbmeta();
89 ops.partitions.clear();
90 // Use the vbmeta image with the persistent digest descriptor.
91 ops.add_partition(
92 "vbmeta",
93 fs::read(TEST_VBMETA_PERSISTENT_DIGEST_PATH).unwrap(),
94 );
95 // Register the image contents to be stored via persistent digest.
96 ops.add_partition(TEST_PARTITION_PERSISTENT_DIGEST_NAME, image);
97 ops
98 }
99
100 /// Calls `slot_verify()` using standard args for `build_test_ops_persistent_digest()` setup.
verify_persistent_digest<'a>(ops: &mut TestOps<'a>) -> SlotVerifyResult<'a, SlotVerifyData<'a>>101 fn verify_persistent_digest<'a>(ops: &mut TestOps<'a>) -> SlotVerifyResult<'a, SlotVerifyData<'a>> {
102 slot_verify(
103 ops,
104 &[&CString::new(TEST_PARTITION_PERSISTENT_DIGEST_NAME).unwrap()],
105 None,
106 SlotVerifyFlags::AVB_SLOT_VERIFY_FLAGS_NONE,
107 HashtreeErrorMode::AVB_HASHTREE_ERROR_MODE_EIO,
108 )
109 }
110
111 /// Modifies the partition contents by flipping a bit.
modify_partition_contents(ops: &mut TestOps, partition: &str)112 fn modify_partition_contents(ops: &mut TestOps, partition: &str) {
113 ops.partitions
114 .get_mut(partition)
115 .unwrap()
116 .contents
117 .as_mut_vec()[0] ^= 0x01;
118 }
119
120 /// Returns the persistent value name for `TEST_PARTITION_PERSISTENT_DIGEST_NAME`.
persistent_digest_value_name() -> String121 fn persistent_digest_value_name() -> String {
122 // This exact format is a libavb implementation detail but is unlikely to change. If it does
123 // just update this format to match.
124 format!("avb.persistent_digest.{TEST_PARTITION_PERSISTENT_DIGEST_NAME}")
125 }
126
127 #[test]
one_image_one_vbmeta_passes_verification_with_correct_data()128 fn one_image_one_vbmeta_passes_verification_with_correct_data() {
129 let mut ops = build_test_ops_one_image_one_vbmeta();
130
131 let result = verify_one_image_one_vbmeta(&mut ops);
132
133 // Make sure the resulting `SlotVerifyData` looks correct.
134 let data = result.unwrap();
135 assert_eq!(data.ab_suffix().to_bytes(), b"");
136 // We don't care about the exact commandline, just search for a substring we know will
137 // exist to make sure the commandline is being provided to the caller correctly.
138 assert!(data
139 .cmdline()
140 .to_str()
141 .unwrap()
142 .contains("androidboot.vbmeta.device_state=locked"));
143 assert_eq!(data.rollback_indexes(), &[0; 32]);
144 assert_eq!(
145 data.resolved_hashtree_error_mode(),
146 HashtreeErrorMode::AVB_HASHTREE_ERROR_MODE_EIO
147 );
148
149 // Check the `VbmetaData` struct looks correct.
150 assert_eq!(data.vbmeta_data().len(), 1);
151 let vbmeta_data = &data.vbmeta_data()[0];
152 assert_eq!(vbmeta_data.partition_name().to_str().unwrap(), "vbmeta");
153 assert_eq!(vbmeta_data.data(), fs::read(TEST_VBMETA_PATH).unwrap());
154 assert_eq!(vbmeta_data.verify_result(), Ok(()));
155
156 // Check the `PartitionData` struct looks correct.
157 assert_eq!(data.partition_data().len(), 1);
158 let partition_data = &data.partition_data()[0];
159 assert_eq!(
160 partition_data.partition_name().to_str().unwrap(),
161 TEST_PARTITION_NAME
162 );
163 assert_eq!(partition_data.data(), fs::read(TEST_IMAGE_PATH).unwrap());
164 assert!(!partition_data.preloaded());
165 assert!(partition_data.verify_result().is_ok());
166 }
167
168 #[test]
preloaded_image_passes_verification()169 fn preloaded_image_passes_verification() {
170 let mut ops = build_test_ops_one_image_one_vbmeta();
171 // Use preloaded data instead for the test partition.
172 let preloaded = fs::read(TEST_IMAGE_PATH).unwrap();
173 ops.add_preloaded_partition(TEST_PARTITION_NAME, &preloaded);
174
175 let result = verify_one_image_one_vbmeta(&mut ops);
176
177 let data = result.unwrap();
178 let partition_data = &data.partition_data()[0];
179 assert!(partition_data.preloaded());
180 }
181
182 // When all images are loaded from disk (rather than preloaded), libavb allocates memory itself for
183 // the data, so there is no shared ownership; the returned verification data owns the image data
184 // and can hold onto it even after the `ops` goes away.
185 #[test]
verification_data_from_disk_can_outlive_ops()186 fn verification_data_from_disk_can_outlive_ops() {
187 let result = {
188 let mut ops = build_test_ops_one_image_one_vbmeta();
189 verify_one_image_one_vbmeta(&mut ops)
190 };
191
192 let data = result.unwrap();
193
194 // The verification data owns the images and we can still access them.
195 assert_eq!(
196 data.partition_data()[0].data(),
197 fs::read(TEST_IMAGE_PATH).unwrap()
198 );
199 }
200
201 // When preloaded data is passed into ops but outlives it, we can also continue to access it from
202 // the verification data after the ops goes away. The ops was only borrowing it, and now the
203 // verification data continues to borrow it.
204 #[test]
verification_data_preloaded_can_outlive_ops()205 fn verification_data_preloaded_can_outlive_ops() {
206 let preloaded = fs::read(TEST_IMAGE_PATH).unwrap();
207
208 let result = {
209 let mut ops = build_test_ops_one_image_one_vbmeta();
210 ops.add_preloaded_partition(TEST_PARTITION_NAME, &preloaded);
211 verify_one_image_one_vbmeta(&mut ops)
212 };
213
214 let data = result.unwrap();
215
216 // The verification data is borrowing the preloaded images and we can still access them.
217 assert_eq!(data.partition_data()[0].data(), preloaded);
218 }
219
220 // When preloaded data is passed into ops but also goes out of scope, the verification data loses
221 // access to it, violating lifetime rules.
222 //
223 // Our lifetimes *must* be configured such that this does not compile, since `result` is borrowing
224 // `preloaded` which has gone out of scope.
225 //
226 // TODO: figure out how to make a compile-fail test; for now we just have to manually test by
227 // un-commenting the code.
228 // #[test]
229 // fn verification_data_preloaded_cannot_outlive_result() {
230 // let result = {
231 // let preloaded = fs::read(TEST_IMAGE_PATH).unwrap();
232 // let mut ops = build_test_ops_one_image_one_vbmeta();
233 // ops.add_preloaded_partition(TEST_PARTITION_NAME, &preloaded);
234 // verify_one_image_one_vbmeta(&mut ops)
235 // };
236 // result.unwrap();
237 // }
238
239 #[test]
slotted_partition_passes_verification()240 fn slotted_partition_passes_verification() {
241 let mut ops = build_test_ops_one_image_one_vbmeta();
242 // Move the partitions to a "_c" slot.
243 ops.partitions.clear();
244 ops.add_partition(
245 TEST_PARTITION_SLOT_C_NAME,
246 fs::read(TEST_IMAGE_PATH).unwrap(),
247 );
248 ops.add_partition("vbmeta_c", fs::read(TEST_VBMETA_PATH).unwrap());
249
250 let result = slot_verify(
251 &mut ops,
252 &[&CString::new(TEST_PARTITION_NAME).unwrap()],
253 Some(&CString::new("_c").unwrap()),
254 SlotVerifyFlags::AVB_SLOT_VERIFY_FLAGS_NONE,
255 HashtreeErrorMode::AVB_HASHTREE_ERROR_MODE_EIO,
256 );
257
258 let data = result.unwrap();
259 assert_eq!(data.ab_suffix().to_bytes(), b"_c");
260 }
261
262 #[test]
two_images_one_vbmeta_passes_verification()263 fn two_images_one_vbmeta_passes_verification() {
264 let mut ops = build_test_ops_two_images_one_vbmeta();
265
266 let result = verify_two_images(&mut ops);
267
268 // We should still only have 1 `VbmetaData` since we only used 1 vbmeta image, but it
269 // signed 2 partitions so we should have 2 `PartitionData` objects.
270 let data = result.unwrap();
271 assert_eq!(data.vbmeta_data().len(), 1);
272 assert_eq!(data.partition_data().len(), 2);
273 assert_eq!(
274 data.partition_data()[0].partition_name().to_str().unwrap(),
275 TEST_PARTITION_NAME
276 );
277 assert_eq!(
278 data.partition_data()[1].partition_name().to_str().unwrap(),
279 TEST_PARTITION_2_NAME
280 );
281 }
282
283 #[test]
combined_image_vbmeta_partition_passes_verification()284 fn combined_image_vbmeta_partition_passes_verification() {
285 let mut ops = build_test_ops_one_image_one_vbmeta();
286 ops.partitions.clear();
287 // Register the single combined image + vbmeta in `TEST_PARTITION_NAME`.
288 ops.add_partition(
289 TEST_PARTITION_NAME,
290 fs::read(TEST_IMAGE_WITH_VBMETA_FOOTER_PATH).unwrap(),
291 );
292 // For a combined image it should not attempt to use the default "vbmeta" key, instead we
293 // register the public key specifically for this partition.
294 ops.default_vbmeta_key = None;
295 ops.vbmeta_keys_for_partition.insert(
296 TEST_PARTITION_NAME,
297 (
298 FakeVbmetaKey::Avb {
299 public_key: fs::read(TEST_PUBLIC_KEY_PATH).unwrap(),
300 public_key_metadata: None,
301 },
302 TEST_VBMETA_ROLLBACK_LOCATION as u32,
303 ),
304 );
305
306 let result = slot_verify(
307 &mut ops,
308 &[&CString::new(TEST_PARTITION_NAME).unwrap()],
309 None,
310 // Tell libavb that the vbmeta image is embedded, not in its own partition.
311 SlotVerifyFlags::AVB_SLOT_VERIFY_FLAGS_NO_VBMETA_PARTITION,
312 HashtreeErrorMode::AVB_HASHTREE_ERROR_MODE_EIO,
313 );
314
315 let data = result.unwrap();
316
317 // Vbmeta should indicate that it came from `TEST_PARTITION_NAME`.
318 assert_eq!(data.vbmeta_data().len(), 1);
319 let vbmeta_data = &data.vbmeta_data()[0];
320 assert_eq!(
321 vbmeta_data.partition_name().to_str().unwrap(),
322 TEST_PARTITION_NAME
323 );
324
325 // Partition should indicate that it came from `TEST_PARTITION_NAME`, but only contain the
326 // image contents.
327 assert_eq!(data.partition_data().len(), 1);
328 let partition_data = &data.partition_data()[0];
329 assert_eq!(
330 partition_data.partition_name().to_str().unwrap(),
331 TEST_PARTITION_NAME
332 );
333 assert_eq!(partition_data.data(), fs::read(TEST_IMAGE_PATH).unwrap());
334 }
335
336 // Validate the custom behavior if the combined image + vbmeta live in the `boot` partition.
337 #[test]
vbmeta_with_boot_partition_passes_verification()338 fn vbmeta_with_boot_partition_passes_verification() {
339 let mut ops = build_test_ops_boot_partition();
340
341 let result = verify_boot_partition(&mut ops);
342
343 let data = result.unwrap();
344
345 // Vbmeta should indicate that it came from `boot`.
346 assert_eq!(data.vbmeta_data().len(), 1);
347 let vbmeta_data = &data.vbmeta_data()[0];
348 assert_eq!(vbmeta_data.partition_name().to_str().unwrap(), "boot");
349
350 // Partition should indicate that it came from `boot`, but only contain the image contents.
351 assert_eq!(data.partition_data().len(), 1);
352 let partition_data = &data.partition_data()[0];
353 assert_eq!(partition_data.partition_name().to_str().unwrap(), "boot");
354 assert_eq!(partition_data.data(), fs::read(TEST_IMAGE_PATH).unwrap());
355 }
356
357 #[test]
persistent_digest_verification_updates_persistent_value()358 fn persistent_digest_verification_updates_persistent_value() {
359 // With persistent digests, the image hash isn't stored in the descriptor, but is instead
360 // calculated on-demand and stored into a named persistent value. So our test image can contain
361 // anything, but does have to match the size indicated by the descriptor.
362 let image_contents = vec![0xAAu8; TEST_IMAGE_SIZE];
363 let mut ops = build_test_ops_persistent_digest(image_contents.clone());
364
365 {
366 let result = verify_persistent_digest(&mut ops);
367 let data = result.unwrap();
368 assert_eq!(data.partition_data()[0].data(), image_contents);
369 } // Drop `result` here so it releases `ops` and we can use it again.
370
371 assert!(ops
372 .persistent_values
373 .contains_key(&persistent_digest_value_name()));
374 }
375
376 #[cfg(feature = "uuid")]
377 #[test]
successful_verification_substitutes_partition_guid()378 fn successful_verification_substitutes_partition_guid() {
379 let mut ops = build_test_ops_one_image_one_vbmeta();
380 ops.partitions.get_mut("vbmeta").unwrap().uuid = uuid!("01234567-89ab-cdef-0123-456789abcdef");
381
382 let result = verify_one_image_one_vbmeta(&mut ops);
383
384 let data = result.unwrap();
385 assert!(data
386 .cmdline()
387 .to_str()
388 .unwrap()
389 .contains("androidboot.vbmeta.device=PARTUUID=01234567-89ab-cdef-0123-456789abcdef"));
390 }
391
392 #[cfg(feature = "uuid")]
393 #[test]
successful_verification_substitutes_boot_partition_guid()394 fn successful_verification_substitutes_boot_partition_guid() {
395 let mut ops = build_test_ops_boot_partition();
396 ops.partitions.get_mut("boot").unwrap().uuid = uuid!("01234567-89ab-cdef-0123-456789abcdef");
397
398 let result = verify_boot_partition(&mut ops);
399
400 let data = result.unwrap();
401 // In this case libavb substitutes the `boot` partition GUID in for `vbmeta`.
402 assert!(data
403 .cmdline()
404 .to_str()
405 .unwrap()
406 .contains("androidboot.vbmeta.device=PARTUUID=01234567-89ab-cdef-0123-456789abcdef"));
407 }
408
409 #[test]
corrupted_image_fails_verification()410 fn corrupted_image_fails_verification() {
411 let mut ops = build_test_ops_one_image_one_vbmeta();
412 modify_partition_contents(&mut ops, TEST_PARTITION_NAME);
413
414 let result = verify_one_image_one_vbmeta(&mut ops);
415
416 let error = result.unwrap_err();
417 assert!(matches!(error, SlotVerifyError::Verification(None)));
418 }
419
420 #[test]
read_partition_callback_error_fails_verification()421 fn read_partition_callback_error_fails_verification() {
422 let mut ops = build_test_ops_one_image_one_vbmeta();
423 ops.partitions.remove(TEST_PARTITION_NAME);
424
425 let result = verify_one_image_one_vbmeta(&mut ops);
426
427 let error = result.unwrap_err();
428 assert!(matches!(error, SlotVerifyError::Io));
429 }
430
431 #[test]
undersized_partition_fails_verification()432 fn undersized_partition_fails_verification() {
433 let mut ops = build_test_ops_one_image_one_vbmeta();
434 ops.partitions
435 .get_mut(TEST_PARTITION_NAME)
436 .unwrap()
437 .contents
438 .as_mut_vec()
439 .pop();
440
441 let result = verify_one_image_one_vbmeta(&mut ops);
442
443 let error = result.unwrap_err();
444 assert!(matches!(error, SlotVerifyError::Io));
445 }
446
447 #[test]
corrupted_vbmeta_fails_verification()448 fn corrupted_vbmeta_fails_verification() {
449 let mut ops = build_test_ops_one_image_one_vbmeta();
450 modify_partition_contents(&mut ops, "vbmeta");
451
452 let result = verify_one_image_one_vbmeta(&mut ops);
453
454 let error = result.unwrap_err();
455 assert!(matches!(error, SlotVerifyError::InvalidMetadata));
456 }
457
458 #[test]
rollback_violation_fails_verification()459 fn rollback_violation_fails_verification() {
460 let mut ops = build_test_ops_one_image_one_vbmeta();
461 // Device with rollback = 1 should refuse to boot image with rollback = 0.
462 ops.rollbacks.insert(TEST_VBMETA_ROLLBACK_LOCATION, Ok(1));
463
464 let result = verify_one_image_one_vbmeta(&mut ops);
465
466 let error = result.unwrap_err();
467 assert!(matches!(error, SlotVerifyError::RollbackIndex));
468 }
469
470 #[test]
rollback_callback_error_fails_verification()471 fn rollback_callback_error_fails_verification() {
472 let mut ops = build_test_ops_one_image_one_vbmeta();
473 ops.rollbacks.clear();
474
475 let result = verify_one_image_one_vbmeta(&mut ops);
476
477 let error = result.unwrap_err();
478 assert!(matches!(error, SlotVerifyError::Io));
479 }
480
481 #[test]
untrusted_vbmeta_keys_fails_verification()482 fn untrusted_vbmeta_keys_fails_verification() {
483 let mut ops = build_test_ops_one_image_one_vbmeta();
484 ops.default_vbmeta_key = Some(FakeVbmetaKey::Avb {
485 public_key: b"not_the_key".into(),
486 public_key_metadata: None,
487 });
488
489 let result = verify_one_image_one_vbmeta(&mut ops);
490
491 let error = result.unwrap_err();
492 assert!(matches!(error, SlotVerifyError::PublicKeyRejected));
493 }
494
495 #[test]
vbmeta_keys_callback_error_fails_verification()496 fn vbmeta_keys_callback_error_fails_verification() {
497 let mut ops = build_test_ops_one_image_one_vbmeta();
498 ops.default_vbmeta_key = None;
499
500 let result = verify_one_image_one_vbmeta(&mut ops);
501
502 let error = result.unwrap_err();
503 assert!(matches!(error, SlotVerifyError::Io));
504 }
505
506 #[test]
unlock_state_callback_error_fails_verification()507 fn unlock_state_callback_error_fails_verification() {
508 let mut ops = build_test_ops_one_image_one_vbmeta();
509 ops.unlock_state = Err(IoError::Io);
510
511 let result = verify_one_image_one_vbmeta(&mut ops);
512
513 let error = result.unwrap_err();
514 assert!(matches!(error, SlotVerifyError::Io));
515 }
516
517 #[test]
persistent_digest_mismatch_fails_verification()518 fn persistent_digest_mismatch_fails_verification() {
519 let image_contents = vec![0xAAu8; TEST_IMAGE_SIZE];
520 let mut ops = build_test_ops_persistent_digest(image_contents.clone());
521 // Put in an incorrect persistent digest; `slot_verify()` should detect the mismatch and fail.
522 ops.add_persistent_value(&persistent_digest_value_name(), Ok(b"incorrect_digest"));
523 // Make a copy so we can verify the persistent values don't change on failure.
524 let original_persistent_values = ops.persistent_values.clone();
525
526 assert!(verify_persistent_digest(&mut ops).is_err());
527
528 // Persistent value should be unchanged.
529 assert_eq!(ops.persistent_values, original_persistent_values);
530 }
531
532 #[test]
persistent_digest_callback_error_fails_verification()533 fn persistent_digest_callback_error_fails_verification() {
534 let image_contents = vec![0xAAu8; TEST_IMAGE_SIZE];
535 let mut ops = build_test_ops_persistent_digest(image_contents.clone());
536 ops.add_persistent_value(&persistent_digest_value_name(), Err(IoError::NoSuchValue));
537
538 let result = verify_persistent_digest(&mut ops);
539
540 let error = result.unwrap_err();
541 assert!(matches!(error, SlotVerifyError::Io));
542 }
543
544 #[test]
corrupted_image_with_allow_verification_error_flag_fails_verification_with_data()545 fn corrupted_image_with_allow_verification_error_flag_fails_verification_with_data() {
546 let mut ops = build_test_ops_one_image_one_vbmeta();
547 modify_partition_contents(&mut ops, TEST_PARTITION_NAME);
548
549 let result = slot_verify(
550 &mut ops,
551 &[&CString::new(TEST_PARTITION_NAME).unwrap()],
552 None,
553 // Pass the flag to allow verification errors.
554 SlotVerifyFlags::AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR,
555 HashtreeErrorMode::AVB_HASHTREE_ERROR_MODE_EIO,
556 );
557
558 // Verification should fail, but with the `AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR` flag
559 // it should give us back the verification data.
560 let error = result.unwrap_err();
561 let data = match error {
562 SlotVerifyError::Verification(Some(data)) => data,
563 _ => panic!("Expected verification data to exist"),
564 };
565
566 // vbmeta verification should have succeeded since that image was still correct.
567 assert_eq!(data.vbmeta_data().len(), 1);
568 assert_eq!(data.vbmeta_data()[0].verify_result(), Ok(()));
569 // Partition verification should have failed since we modified the image.
570 assert_eq!(data.partition_data().len(), 1);
571 assert!(matches!(
572 data.partition_data()[0].verify_result(),
573 Err(SlotVerifyError::Verification(None))
574 ));
575 }
576
577 #[test]
one_image_one_vbmeta_verification_data_display()578 fn one_image_one_vbmeta_verification_data_display() {
579 let mut ops = build_test_ops_one_image_one_vbmeta();
580
581 let result = verify_one_image_one_vbmeta(&mut ops);
582
583 let data = result.unwrap();
584 assert_eq!(
585 format!("{data}"),
586 r#"slot: "", vbmeta: ["vbmeta": Ok(())], images: ["test_part": Ok(())]"#
587 );
588 }
589
590 #[test]
preloaded_image_verification_data_display()591 fn preloaded_image_verification_data_display() {
592 let mut ops = build_test_ops_one_image_one_vbmeta();
593 let preloaded = fs::read(TEST_IMAGE_PATH).unwrap();
594 ops.add_preloaded_partition(TEST_PARTITION_NAME, &preloaded);
595
596 let result = verify_one_image_one_vbmeta(&mut ops);
597
598 let data = result.unwrap();
599 assert_eq!(
600 format!("{data}"),
601 r#"slot: "", vbmeta: ["vbmeta": Ok(())], images: ["test_part"(p): Ok(())]"#
602 );
603 }
604
605 #[test]
two_images_one_vbmeta_verification_data_display()606 fn two_images_one_vbmeta_verification_data_display() {
607 let mut ops = build_test_ops_two_images_one_vbmeta();
608
609 let result = verify_two_images(&mut ops);
610
611 let data = result.unwrap();
612 assert_eq!(
613 format!("{data}"),
614 r#"slot: "", vbmeta: ["vbmeta": Ok(())], images: ["test_part": Ok(()), "test_part_2": Ok(())]"#
615 );
616 }
617
618 #[test]
corrupted_image_verification_data_display()619 fn corrupted_image_verification_data_display() {
620 let mut ops = build_test_ops_one_image_one_vbmeta();
621 modify_partition_contents(&mut ops, TEST_PARTITION_NAME);
622
623 let result = slot_verify(
624 &mut ops,
625 &[&CString::new(TEST_PARTITION_NAME).unwrap()],
626 None,
627 SlotVerifyFlags::AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR,
628 HashtreeErrorMode::AVB_HASHTREE_ERROR_MODE_EIO,
629 );
630
631 let error = result.unwrap_err();
632 let data = match error {
633 SlotVerifyError::Verification(Some(data)) => data,
634 _ => panic!("Expected verification data to exist"),
635 };
636 assert_eq!(
637 format!("{data}"),
638 r#"slot: "", vbmeta: ["vbmeta": Ok(())], images: ["test_part": Err(Verification(None))]"#
639 );
640 }
641
642 #[test]
one_image_gives_single_descriptor()643 fn one_image_gives_single_descriptor() {
644 let mut ops = build_test_ops_one_image_one_vbmeta();
645
646 let result = verify_one_image_one_vbmeta(&mut ops);
647
648 let data = result.unwrap();
649 assert_eq!(data.vbmeta_data()[0].descriptors().unwrap().len(), 1);
650 }
651
652 #[test]
two_images_gives_two_descriptors()653 fn two_images_gives_two_descriptors() {
654 let mut ops = build_test_ops_two_images_one_vbmeta();
655
656 let result = verify_two_images(&mut ops);
657
658 let data = result.unwrap();
659 assert_eq!(data.vbmeta_data()[0].descriptors().unwrap().len(), 2);
660 }
661
662 /// Runs verification on the given contents and checks for a resulting descriptor.
663 ///
664 /// This test helper performs the following steps:
665 ///
666 /// 1. set up a `TestOps` for the default test image/vbmeta
667 /// 2. replace the vbmeta image with the contents at `vbmeta_path`
668 /// 3. run verification
669 /// 4. check that the given `descriptor` exists in the verification data
verify_and_find_descriptor(vbmeta_path: &str, expected_descriptor: &Descriptor)670 fn verify_and_find_descriptor(vbmeta_path: &str, expected_descriptor: &Descriptor) {
671 let mut ops = build_test_ops_one_image_one_vbmeta();
672
673 // Replace the vbmeta image with the requested variation.
674 ops.add_partition("vbmeta", fs::read(vbmeta_path).unwrap());
675
676 let result = verify_one_image_one_vbmeta(&mut ops);
677
678 let data = result.unwrap();
679 let descriptors = &data.vbmeta_data()[0].descriptors().unwrap();
680 assert!(descriptors.contains(expected_descriptor));
681 }
682
683 #[test]
verify_hash_descriptor()684 fn verify_hash_descriptor() {
685 verify_and_find_descriptor(
686 // The standard vbmeta image should contain the hash descriptor.
687 TEST_VBMETA_PATH,
688 &Descriptor::Hash(HashDescriptor {
689 image_size: TEST_IMAGE_SIZE as u64,
690 hash_algorithm: TEST_IMAGE_HASH_ALGO,
691 flags: HashDescriptorFlags(0),
692 partition_name: TEST_PARTITION_NAME,
693 salt: &decode(TEST_IMAGE_SALT_HEX).unwrap(),
694 digest: &decode(TEST_IMAGE_DIGEST_HEX).unwrap(),
695 }),
696 );
697 }
698
699 #[test]
verify_property_descriptor()700 fn verify_property_descriptor() {
701 verify_and_find_descriptor(
702 TEST_VBMETA_WITH_PROPERTY_PATH,
703 &Descriptor::Property(PropertyDescriptor {
704 key: TEST_PROPERTY_KEY,
705 value: TEST_PROPERTY_VALUE,
706 }),
707 );
708 }
709
710 #[test]
verify_hashtree_descriptor()711 fn verify_hashtree_descriptor() {
712 verify_and_find_descriptor(
713 TEST_VBMETA_WITH_HASHTREE_PATH,
714 &Descriptor::Hashtree(HashtreeDescriptor {
715 dm_verity_version: 1,
716 image_size: TEST_IMAGE_SIZE as u64,
717 tree_offset: TEST_IMAGE_SIZE as u64,
718 tree_size: 4096,
719 data_block_size: 4096,
720 hash_block_size: 4096,
721 fec_num_roots: 0,
722 fec_offset: 0,
723 fec_size: 0,
724 hash_algorithm: TEST_HASHTREE_ALGORITHM,
725 flags: HashtreeDescriptorFlags(0),
726 partition_name: TEST_PARTITION_HASH_TREE_NAME,
727 salt: &decode(TEST_HASHTREE_SALT_HEX).unwrap(),
728 root_digest: &decode(TEST_HASHTREE_DIGEST_HEX).unwrap(),
729 }),
730 );
731 }
732
733 #[test]
verify_kernel_commandline_descriptor()734 fn verify_kernel_commandline_descriptor() {
735 verify_and_find_descriptor(
736 TEST_VBMETA_WITH_COMMANDLINE_PATH,
737 &Descriptor::KernelCommandline(KernelCommandlineDescriptor {
738 flags: KernelCommandlineDescriptorFlags(0),
739 commandline: TEST_KERNEL_COMMANDLINE,
740 }),
741 );
742 }
743
744 #[test]
verify_chain_partition_descriptor()745 fn verify_chain_partition_descriptor() {
746 let mut ops = build_test_ops_two_images_one_vbmeta();
747
748 // Set up the fake ops to contain:
749 // * the default test image in TEST_PARTITION_NAME
750 // * a signed test image with vbmeta footer in TEST_PARTITION_2_NAME
751 // * a vbmeta image in "vbmeta" which:
752 // * signs the default TEST_PARTITION_NAME image
753 // * chains to TEST_PARTITION_2_NAME
754 //
755 // Since this is an unusual configuration, it's simpler to just set it up manually here
756 // rather than try to adapt `verify_and_find_descriptor()` for this one case.
757 ops.add_partition(
758 "vbmeta",
759 fs::read(TEST_VBMETA_WITH_CHAINED_PARTITION_PATH).unwrap(),
760 );
761 // Replace the chained partition with the combined image + vbmeta footer.
762 ops.add_partition(
763 TEST_PARTITION_2_NAME,
764 fs::read(TEST_IMAGE_WITH_VBMETA_FOOTER_FOR_TEST_PART_2).unwrap(),
765 );
766 // Add the rollback index for the chained partition's location.
767 ops.rollbacks.insert(
768 TEST_CHAINED_PARTITION_ROLLBACK_LOCATION,
769 Ok(TEST_CHAINED_PARTITION_ROLLBACK_INDEX),
770 );
771
772 let result = verify_two_images(&mut ops);
773
774 let data = result.unwrap();
775 // We should have two vbmeta images - one from the "vbmeta" partition, the other embedded
776 // in the footer of TEST_PARTITION_2_NAME.
777 let vbmetas = data.vbmeta_data();
778 assert_eq!(vbmetas.len(), 2);
779 // Search for the main vbmeta so we don't assume any particular order.
780 let main_vbmeta = vbmetas
781 .iter()
782 .find(|v| v.partition_name().to_str().unwrap() == "vbmeta")
783 .unwrap();
784
785 // The main vbmeta should contain the chain descriptor.
786 let expected = ChainPartitionDescriptor {
787 rollback_index_location: TEST_CHAINED_PARTITION_ROLLBACK_LOCATION as u32,
788 partition_name: TEST_PARTITION_2_NAME,
789 public_key: &fs::read(TEST_PUBLIC_KEY_RSA8192_PATH).unwrap(),
790 flags: ChainPartitionDescriptorFlags(0),
791 };
792 assert!(main_vbmeta
793 .descriptors()
794 .unwrap()
795 .contains(&Descriptor::ChainPartition(expected)));
796 }
797
798 #[test]
verify_get_property_value()799 fn verify_get_property_value() {
800 let mut ops = build_test_ops_one_image_one_vbmeta();
801 ops.add_partition("vbmeta", fs::read(TEST_VBMETA_WITH_PROPERTY_PATH).unwrap());
802
803 let data = verify_one_image_one_vbmeta(&mut ops).unwrap();
804
805 assert_eq!(
806 data.vbmeta_data()[0].get_property_value(TEST_PROPERTY_KEY),
807 Some(TEST_PROPERTY_VALUE),
808 "Expected valid buffer for the given key"
809 );
810 }
811
812 #[test]
verify_get_property_value_not_found()813 fn verify_get_property_value_not_found() {
814 let mut ops = build_test_ops_one_image_one_vbmeta();
815 ops.add_partition("vbmeta", fs::read(TEST_VBMETA_WITH_PROPERTY_PATH).unwrap());
816
817 let data = verify_one_image_one_vbmeta(&mut ops).unwrap();
818
819 assert_eq!(
820 data.vbmeta_data()[0].get_property_value("test_prop_doesnt_exist"),
821 None,
822 "Expected property not found for not existing key"
823 );
824 }
825