1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 mod utils;
18 
19 use anyhow::{anyhow, Result};
20 use avb::{DescriptorError, SlotVerifyError};
21 use avb_bindgen::{AvbFooter, AvbVBMetaImageHeader};
22 use pvmfw_avb::{verify_payload, Capability, DebugLevel, PvmfwVerifyError, VerifiedBootData};
23 use std::{
24     fs,
25     mem::{offset_of, size_of},
26     ptr,
27 };
28 use utils::*;
29 
30 const TEST_IMG_WITH_ONE_HASHDESC_PATH: &str = "test_image_with_one_hashdesc.img";
31 const TEST_IMG_WITH_ROLLBACK_INDEX_5: &str = "test_image_with_rollback_index_5.img";
32 const TEST_IMG_WITH_PROP_DESC_PATH: &str = "test_image_with_prop_desc.img";
33 const TEST_IMG_WITH_SERVICE_VM_PROP_PATH: &str = "test_image_with_service_vm_prop.img";
34 const TEST_IMG_WITH_UNKNOWN_VM_TYPE_PROP_PATH: &str = "test_image_with_unknown_vm_type_prop.img";
35 const TEST_IMG_WITH_MULTIPLE_PROPS_PATH: &str = "test_image_with_multiple_props.img";
36 const TEST_IMG_WITH_DUPLICATED_CAP_PATH: &str = "test_image_with_duplicated_capability.img";
37 const TEST_IMG_WITH_NON_INITRD_HASHDESC_PATH: &str = "test_image_with_non_initrd_hashdesc.img";
38 const TEST_IMG_WITH_INITRD_AND_NON_INITRD_DESC_PATH: &str =
39     "test_image_with_initrd_and_non_initrd_desc.img";
40 const TEST_IMG_WITH_MULTIPLE_CAPABILITIES: &str = "test_image_with_multiple_capabilities.img";
41 const TEST_IMG_WITH_ALL_CAPABILITIES: &str = "test_image_with_all_capabilities.img";
42 const UNSIGNED_TEST_IMG_PATH: &str = "unsigned_test.img";
43 
44 const RANDOM_FOOTER_POS: usize = 30;
45 
46 /// This test uses the Microdroid payload compiled on the fly to check that
47 /// the latest payload can be verified successfully.
48 #[test]
latest_normal_payload_passes_verification() -> Result<()>49 fn latest_normal_payload_passes_verification() -> Result<()> {
50     assert_latest_payload_verification_passes(
51         &load_latest_initrd_normal()?,
52         b"initrd_normal",
53         DebugLevel::None,
54         None,
55     )
56 }
57 
58 #[test]
latest_trusty_security_vm_kernel_passes_verification() -> Result<()>59 fn latest_trusty_security_vm_kernel_passes_verification() -> Result<()> {
60     let salt = b"trusty_security_vm_salt";
61     let expected_rollback_index = 1;
62     assert_payload_without_initrd_passes_verification(
63         &load_latest_trusty_security_vm_signed_kernel()?,
64         salt,
65         expected_rollback_index,
66         vec![Capability::TrustySecurityVm],
67         None,
68     )
69 }
70 
71 #[test]
latest_debug_payload_passes_verification() -> Result<()>72 fn latest_debug_payload_passes_verification() -> Result<()> {
73     assert_latest_payload_verification_passes(
74         &load_latest_initrd_debug()?,
75         b"initrd_debug",
76         DebugLevel::Full,
77         None,
78     )
79 }
80 
81 #[test]
payload_expecting_no_initrd_passes_verification_with_no_initrd() -> Result<()>82 fn payload_expecting_no_initrd_passes_verification_with_no_initrd() -> Result<()> {
83     let public_key = load_trusted_public_key()?;
84     let verified_boot_data = verify_payload(
85         &fs::read(TEST_IMG_WITH_ONE_HASHDESC_PATH)?,
86         /* initrd= */ None,
87         &public_key,
88     )
89     .map_err(|e| anyhow!("Verification failed. Error: {}", e))?;
90 
91     let kernel_digest = hash(&[&hex::decode("1111")?, &fs::read(UNSIGNED_TEST_IMG_PATH)?]);
92     let expected_boot_data = VerifiedBootData {
93         debug_level: DebugLevel::None,
94         kernel_digest,
95         initrd_digest: None,
96         public_key: &public_key,
97         capabilities: vec![],
98         rollback_index: 0,
99         page_size: None,
100     };
101     assert_eq!(expected_boot_data, verified_boot_data);
102 
103     Ok(())
104 }
105 
106 #[test]
payload_with_non_initrd_descriptor_fails_verification_with_no_initrd() -> Result<()>107 fn payload_with_non_initrd_descriptor_fails_verification_with_no_initrd() -> Result<()> {
108     assert_payload_verification_fails(
109         &fs::read(TEST_IMG_WITH_NON_INITRD_HASHDESC_PATH)?,
110         /* initrd= */ None,
111         &load_trusted_public_key()?,
112         PvmfwVerifyError::InvalidDescriptors(DescriptorError::InvalidContents),
113     )
114 }
115 
116 #[test]
payload_with_non_initrd_descriptor_fails_verification_with_initrd() -> Result<()>117 fn payload_with_non_initrd_descriptor_fails_verification_with_initrd() -> Result<()> {
118     assert_payload_verification_with_initrd_fails(
119         &fs::read(TEST_IMG_WITH_INITRD_AND_NON_INITRD_DESC_PATH)?,
120         &load_latest_initrd_normal()?,
121         &load_trusted_public_key()?,
122         PvmfwVerifyError::InvalidDescriptors(DescriptorError::InvalidContents),
123     )
124 }
125 
126 #[test]
payload_expecting_no_initrd_passes_verification_with_service_vm_prop() -> Result<()>127 fn payload_expecting_no_initrd_passes_verification_with_service_vm_prop() -> Result<()> {
128     let public_key = load_trusted_public_key()?;
129     let verified_boot_data = verify_payload(
130         &fs::read(TEST_IMG_WITH_SERVICE_VM_PROP_PATH)?,
131         /* initrd= */ None,
132         &public_key,
133     )
134     .map_err(|e| anyhow!("Verification failed. Error: {}", e))?;
135 
136     let kernel_digest = hash(&[&hex::decode("2131")?, &fs::read(UNSIGNED_TEST_IMG_PATH)?]);
137     let expected_boot_data = VerifiedBootData {
138         debug_level: DebugLevel::None,
139         kernel_digest,
140         initrd_digest: None,
141         public_key: &public_key,
142         capabilities: vec![Capability::RemoteAttest],
143         rollback_index: 0,
144         page_size: None,
145     };
146     assert_eq!(expected_boot_data, verified_boot_data);
147 
148     Ok(())
149 }
150 
151 #[test]
payload_with_unknown_vm_type_fails_verification_with_no_initrd() -> Result<()>152 fn payload_with_unknown_vm_type_fails_verification_with_no_initrd() -> Result<()> {
153     assert_payload_verification_fails(
154         &fs::read(TEST_IMG_WITH_UNKNOWN_VM_TYPE_PROP_PATH)?,
155         /* initrd= */ None,
156         &load_trusted_public_key()?,
157         PvmfwVerifyError::UnknownVbmetaProperty,
158     )
159 }
160 
161 #[test]
payload_with_multiple_props_fails_verification_with_no_initrd() -> Result<()>162 fn payload_with_multiple_props_fails_verification_with_no_initrd() -> Result<()> {
163     assert_payload_verification_fails(
164         &fs::read(TEST_IMG_WITH_MULTIPLE_PROPS_PATH)?,
165         /* initrd= */ None,
166         &load_trusted_public_key()?,
167         PvmfwVerifyError::InvalidDescriptors(DescriptorError::InvalidContents),
168     )
169 }
170 
171 #[test]
payload_with_duplicated_capability_fails_verification_with_no_initrd() -> Result<()>172 fn payload_with_duplicated_capability_fails_verification_with_no_initrd() -> Result<()> {
173     assert_payload_verification_fails(
174         &fs::read(TEST_IMG_WITH_DUPLICATED_CAP_PATH)?,
175         /* initrd= */ None,
176         &load_trusted_public_key()?,
177         SlotVerifyError::InvalidMetadata.into(),
178     )
179 }
180 
181 #[test]
payload_with_prop_descriptor_fails_verification_with_no_initrd() -> Result<()>182 fn payload_with_prop_descriptor_fails_verification_with_no_initrd() -> Result<()> {
183     assert_payload_verification_fails(
184         &fs::read(TEST_IMG_WITH_PROP_DESC_PATH)?,
185         /* initrd= */ None,
186         &load_trusted_public_key()?,
187         PvmfwVerifyError::UnknownVbmetaProperty,
188     )
189 }
190 
191 #[test]
payload_expecting_initrd_fails_verification_with_no_initrd() -> Result<()>192 fn payload_expecting_initrd_fails_verification_with_no_initrd() -> Result<()> {
193     assert_payload_verification_fails(
194         &load_latest_signed_kernel()?,
195         /* initrd= */ None,
196         &load_trusted_public_key()?,
197         SlotVerifyError::InvalidMetadata.into(),
198     )
199 }
200 
201 #[test]
payload_with_empty_public_key_fails_verification() -> Result<()>202 fn payload_with_empty_public_key_fails_verification() -> Result<()> {
203     assert_payload_verification_with_initrd_fails(
204         &load_latest_signed_kernel()?,
205         &load_latest_initrd_normal()?,
206         /* trusted_public_key= */ &[0u8; 0],
207         SlotVerifyError::PublicKeyRejected.into(),
208     )
209 }
210 
211 #[test]
payload_with_an_invalid_public_key_fails_verification() -> Result<()>212 fn payload_with_an_invalid_public_key_fails_verification() -> Result<()> {
213     assert_payload_verification_with_initrd_fails(
214         &load_latest_signed_kernel()?,
215         &load_latest_initrd_normal()?,
216         /* trusted_public_key= */ &[0u8; 512],
217         SlotVerifyError::PublicKeyRejected.into(),
218     )
219 }
220 
221 #[test]
payload_with_a_different_valid_public_key_fails_verification() -> Result<()>222 fn payload_with_a_different_valid_public_key_fails_verification() -> Result<()> {
223     assert_payload_verification_with_initrd_fails(
224         &load_latest_signed_kernel()?,
225         &load_latest_initrd_normal()?,
226         &fs::read(PUBLIC_KEY_RSA2048_PATH)?,
227         SlotVerifyError::PublicKeyRejected.into(),
228     )
229 }
230 
231 #[test]
payload_with_an_invalid_initrd_fails_verification() -> Result<()>232 fn payload_with_an_invalid_initrd_fails_verification() -> Result<()> {
233     assert_payload_verification_with_initrd_fails(
234         &load_latest_signed_kernel()?,
235         /* initrd= */ &fs::read(UNSIGNED_TEST_IMG_PATH)?,
236         &load_trusted_public_key()?,
237         SlotVerifyError::Verification(None).into(),
238     )
239 }
240 
241 #[test]
unsigned_kernel_fails_verification() -> Result<()>242 fn unsigned_kernel_fails_verification() -> Result<()> {
243     assert_payload_verification_with_initrd_fails(
244         &fs::read(UNSIGNED_TEST_IMG_PATH)?,
245         &load_latest_initrd_normal()?,
246         &load_trusted_public_key()?,
247         SlotVerifyError::Io.into(),
248     )
249 }
250 
251 #[test]
tampered_kernel_fails_verification() -> Result<()>252 fn tampered_kernel_fails_verification() -> Result<()> {
253     let mut kernel = load_latest_signed_kernel()?;
254     kernel[1] = !kernel[1]; // Flip the bits
255 
256     assert_payload_verification_with_initrd_fails(
257         &kernel,
258         &load_latest_initrd_normal()?,
259         &load_trusted_public_key()?,
260         SlotVerifyError::Verification(None).into(),
261     )
262 }
263 
264 #[test]
kernel_footer_with_vbmeta_offset_overwritten_fails_verification() -> Result<()>265 fn kernel_footer_with_vbmeta_offset_overwritten_fails_verification() -> Result<()> {
266     // Arrange.
267     let mut kernel = load_latest_signed_kernel()?;
268     let footer_offset = get_avb_footer_offset(&kernel)?;
269     let vbmeta_offset_offset = footer_offset + offset_of!(AvbFooter, vbmeta_offset);
270     let vbmeta_offset_bytes = vbmeta_offset_offset..(vbmeta_offset_offset + size_of::<u64>());
271 
272     let test_values = [kernel.len(), usize::MAX];
273     for value in test_values {
274         let value = u64::try_from(value).unwrap();
275         // Act.
276         kernel[vbmeta_offset_bytes.clone()].copy_from_slice(&value.to_be_bytes());
277         // footer is unaligned; copy vbmeta_offset to local variable
278         let vbmeta_offset = extract_avb_footer(&kernel)?.vbmeta_offset;
279         assert_eq!(vbmeta_offset, value);
280 
281         // Assert.
282         assert_payload_verification_with_initrd_fails(
283             &kernel,
284             &load_latest_initrd_normal()?,
285             &load_trusted_public_key()?,
286             SlotVerifyError::Io.into(),
287         )?;
288     }
289     Ok(())
290 }
291 
292 #[test]
tampered_kernel_footer_fails_verification() -> Result<()>293 fn tampered_kernel_footer_fails_verification() -> Result<()> {
294     let mut kernel = load_latest_signed_kernel()?;
295     let avb_footer_index = kernel.len() - size_of::<AvbFooter>() + RANDOM_FOOTER_POS;
296     kernel[avb_footer_index] = !kernel[avb_footer_index];
297 
298     assert_payload_verification_with_initrd_fails(
299         &kernel,
300         &load_latest_initrd_normal()?,
301         &load_trusted_public_key()?,
302         SlotVerifyError::InvalidMetadata.into(),
303     )
304 }
305 
306 #[test]
extended_initrd_fails_verification() -> Result<()>307 fn extended_initrd_fails_verification() -> Result<()> {
308     let mut initrd = load_latest_initrd_normal()?;
309     initrd.extend(b"androidboot.vbmeta.digest=1111");
310 
311     assert_payload_verification_with_initrd_fails(
312         &load_latest_signed_kernel()?,
313         &initrd,
314         &load_trusted_public_key()?,
315         SlotVerifyError::Verification(None).into(),
316     )
317 }
318 
319 #[test]
tampered_vbmeta_fails_verification() -> Result<()>320 fn tampered_vbmeta_fails_verification() -> Result<()> {
321     let mut kernel = load_latest_signed_kernel()?;
322     let footer = extract_avb_footer(&kernel)?;
323     let vbmeta_index: usize = (footer.vbmeta_offset + 1).try_into()?;
324 
325     kernel[vbmeta_index] = !kernel[vbmeta_index]; // Flip the bits
326 
327     assert_payload_verification_with_initrd_fails(
328         &kernel,
329         &load_latest_initrd_normal()?,
330         &load_trusted_public_key()?,
331         SlotVerifyError::InvalidMetadata.into(),
332     )
333 }
334 
335 #[test]
vbmeta_with_public_key_overwritten_fails_verification() -> Result<()>336 fn vbmeta_with_public_key_overwritten_fails_verification() -> Result<()> {
337     let mut kernel = load_latest_signed_kernel()?;
338     let footer = extract_avb_footer(&kernel)?;
339     let vbmeta_header = extract_vbmeta_header(&kernel, &footer)?;
340     let public_key_offset = footer.vbmeta_offset as usize
341         + size_of::<AvbVBMetaImageHeader>()
342         + vbmeta_header.authentication_data_block_size as usize
343         + vbmeta_header.public_key_offset as usize;
344     let public_key_size: usize = vbmeta_header.public_key_size.try_into()?;
345     let empty_public_key = vec![0u8; public_key_size];
346 
347     kernel[public_key_offset..(public_key_offset + public_key_size)]
348         .copy_from_slice(&empty_public_key);
349 
350     assert_payload_verification_with_initrd_fails(
351         &kernel,
352         &load_latest_initrd_normal()?,
353         &empty_public_key,
354         SlotVerifyError::Verification(None).into(),
355     )?;
356     assert_payload_verification_with_initrd_fails(
357         &kernel,
358         &load_latest_initrd_normal()?,
359         &load_trusted_public_key()?,
360         SlotVerifyError::Verification(None).into(),
361     )
362 }
363 
364 #[test]
vbmeta_with_verification_flag_disabled_fails_verification() -> Result<()>365 fn vbmeta_with_verification_flag_disabled_fails_verification() -> Result<()> {
366     // From external/avb/libavb/avb_vbmeta_image.h
367     const AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED: u32 = 2;
368 
369     // Arrange.
370     let mut kernel = load_latest_signed_kernel()?;
371     let footer = extract_avb_footer(&kernel)?;
372     let vbmeta_header = extract_vbmeta_header(&kernel, &footer)?;
373 
374     // vbmeta_header is unaligned; copy flags to local variable
375     let vbmeta_header_flags = vbmeta_header.flags;
376     assert_eq!(0, vbmeta_header_flags, "The disable flag should not be set in the latest kernel.");
377     let flags_addr = ptr::addr_of!(vbmeta_header.flags) as *const u8;
378     // SAFETY: It is safe as both raw pointers `flags_addr` and `vbmeta_header` are not null.
379     let flags_offset = unsafe { flags_addr.offset_from(ptr::addr_of!(vbmeta_header) as *const u8) };
380     let flags_offset = usize::try_from(footer.vbmeta_offset)? + usize::try_from(flags_offset)?;
381 
382     // Act.
383     kernel[flags_offset..(flags_offset + size_of::<u32>())]
384         .copy_from_slice(&AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED.to_be_bytes());
385 
386     // Assert.
387     let vbmeta_header = extract_vbmeta_header(&kernel, &footer)?;
388     // vbmeta_header is unaligned; copy flags to local variable
389     let vbmeta_header_flags = vbmeta_header.flags;
390     assert_eq!(
391         AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED, vbmeta_header_flags,
392         "VBMeta verification flag should be disabled now."
393     );
394     assert_payload_verification_with_initrd_fails(
395         &kernel,
396         &load_latest_initrd_normal()?,
397         &load_trusted_public_key()?,
398         SlotVerifyError::Verification(None).into(),
399     )
400 }
401 
402 #[test]
payload_with_rollback_index() -> Result<()>403 fn payload_with_rollback_index() -> Result<()> {
404     let public_key = load_trusted_public_key()?;
405     let verified_boot_data = verify_payload(
406         &fs::read(TEST_IMG_WITH_ROLLBACK_INDEX_5)?,
407         /* initrd= */ None,
408         &public_key,
409     )
410     .map_err(|e| anyhow!("Verification failed. Error: {}", e))?;
411 
412     let kernel_digest = hash(&[&hex::decode("1211")?, &fs::read(UNSIGNED_TEST_IMG_PATH)?]);
413     let expected_boot_data = VerifiedBootData {
414         debug_level: DebugLevel::None,
415         kernel_digest,
416         initrd_digest: None,
417         public_key: &public_key,
418         capabilities: vec![],
419         rollback_index: 5,
420         page_size: None,
421     };
422     assert_eq!(expected_boot_data, verified_boot_data);
423     Ok(())
424 }
425 
426 #[test]
payload_with_multiple_capabilities() -> Result<()>427 fn payload_with_multiple_capabilities() -> Result<()> {
428     let public_key = load_trusted_public_key()?;
429     let verified_boot_data = verify_payload(
430         &fs::read(TEST_IMG_WITH_MULTIPLE_CAPABILITIES)?,
431         /* initrd= */ None,
432         &public_key,
433     )
434     .map_err(|e| anyhow!("Verification failed. Error: {}", e))?;
435 
436     assert!(verified_boot_data.has_capability(Capability::RemoteAttest));
437     assert!(verified_boot_data.has_capability(Capability::SecretkeeperProtection));
438     Ok(())
439 }
440 
441 #[test]
payload_with_all_capabilities() -> Result<()>442 fn payload_with_all_capabilities() -> Result<()> {
443     let public_key = load_trusted_public_key()?;
444     let verified_boot_data = verify_payload(
445         &fs::read(TEST_IMG_WITH_ALL_CAPABILITIES)?,
446         /* initrd= */ None,
447         &public_key,
448     )
449     .map_err(|e| anyhow!("Verification failed. Error: {}", e))?;
450 
451     assert!(verified_boot_data.has_capability(Capability::RemoteAttest));
452     assert!(verified_boot_data.has_capability(Capability::TrustySecurityVm));
453     assert!(verified_boot_data.has_capability(Capability::SecretkeeperProtection));
454     assert!(verified_boot_data.has_capability(Capability::SupportsUefiBoot));
455     // Fail if this test doesn't actually cover all supported capabilities.
456     assert_eq!(Capability::COUNT, 4);
457 
458     Ok(())
459 }
460