xref: /aosp_15_r20/external/avb/rust/tests/verify_tests.rs (revision d289c2ba6de359471b23d594623b906876bc48a0)
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