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::{fs, mem::size_of, ptr};
24 use utils::*;
25 
26 const TEST_IMG_WITH_ONE_HASHDESC_PATH: &str = "test_image_with_one_hashdesc.img";
27 const TEST_IMG_WITH_ROLLBACK_INDEX_5: &str = "test_image_with_rollback_index_5.img";
28 const TEST_IMG_WITH_PROP_DESC_PATH: &str = "test_image_with_prop_desc.img";
29 const TEST_IMG_WITH_SERVICE_VM_PROP_PATH: &str = "test_image_with_service_vm_prop.img";
30 const TEST_IMG_WITH_UNKNOWN_VM_TYPE_PROP_PATH: &str = "test_image_with_unknown_vm_type_prop.img";
31 const TEST_IMG_WITH_MULTIPLE_PROPS_PATH: &str = "test_image_with_multiple_props.img";
32 const TEST_IMG_WITH_DUPLICATED_CAP_PATH: &str = "test_image_with_duplicated_capability.img";
33 const TEST_IMG_WITH_NON_INITRD_HASHDESC_PATH: &str = "test_image_with_non_initrd_hashdesc.img";
34 const TEST_IMG_WITH_INITRD_AND_NON_INITRD_DESC_PATH: &str =
35     "test_image_with_initrd_and_non_initrd_desc.img";
36 const TEST_IMG_WITH_MULTIPLE_CAPABILITIES: &str = "test_image_with_multiple_capabilities.img";
37 const UNSIGNED_TEST_IMG_PATH: &str = "unsigned_test.img";
38 
39 const RANDOM_FOOTER_POS: usize = 30;
40 
41 /// This test uses the Microdroid payload compiled on the fly to check that
42 /// the latest payload can be verified successfully.
43 #[test]
latest_normal_payload_passes_verification() -> Result<()>44 fn latest_normal_payload_passes_verification() -> Result<()> {
45     assert_latest_payload_verification_passes(
46         &load_latest_initrd_normal()?,
47         b"initrd_normal",
48         DebugLevel::None,
49     )
50 }
51 
52 #[test]
latest_debug_payload_passes_verification() -> Result<()>53 fn latest_debug_payload_passes_verification() -> Result<()> {
54     assert_latest_payload_verification_passes(
55         &load_latest_initrd_debug()?,
56         b"initrd_debug",
57         DebugLevel::Full,
58     )
59 }
60 
61 #[test]
payload_expecting_no_initrd_passes_verification_with_no_initrd() -> Result<()>62 fn payload_expecting_no_initrd_passes_verification_with_no_initrd() -> Result<()> {
63     let public_key = load_trusted_public_key()?;
64     let verified_boot_data = verify_payload(
65         &fs::read(TEST_IMG_WITH_ONE_HASHDESC_PATH)?,
66         /* initrd= */ None,
67         &public_key,
68     )
69     .map_err(|e| anyhow!("Verification failed. Error: {}", e))?;
70 
71     let kernel_digest = hash(&[&hex::decode("1111")?, &fs::read(UNSIGNED_TEST_IMG_PATH)?]);
72     let expected_boot_data = VerifiedBootData {
73         debug_level: DebugLevel::None,
74         kernel_digest,
75         initrd_digest: None,
76         public_key: &public_key,
77         capabilities: vec![],
78         rollback_index: 0,
79     };
80     assert_eq!(expected_boot_data, verified_boot_data);
81 
82     Ok(())
83 }
84 
85 #[test]
payload_with_non_initrd_descriptor_fails_verification_with_no_initrd() -> Result<()>86 fn payload_with_non_initrd_descriptor_fails_verification_with_no_initrd() -> Result<()> {
87     assert_payload_verification_fails(
88         &fs::read(TEST_IMG_WITH_NON_INITRD_HASHDESC_PATH)?,
89         /* initrd= */ None,
90         &load_trusted_public_key()?,
91         PvmfwVerifyError::InvalidDescriptors(DescriptorError::InvalidContents),
92     )
93 }
94 
95 #[test]
payload_with_non_initrd_descriptor_fails_verification_with_initrd() -> Result<()>96 fn payload_with_non_initrd_descriptor_fails_verification_with_initrd() -> Result<()> {
97     assert_payload_verification_with_initrd_fails(
98         &fs::read(TEST_IMG_WITH_INITRD_AND_NON_INITRD_DESC_PATH)?,
99         &load_latest_initrd_normal()?,
100         &load_trusted_public_key()?,
101         PvmfwVerifyError::InvalidDescriptors(DescriptorError::InvalidContents),
102     )
103 }
104 
105 #[test]
payload_expecting_no_initrd_passes_verification_with_service_vm_prop() -> Result<()>106 fn payload_expecting_no_initrd_passes_verification_with_service_vm_prop() -> Result<()> {
107     let public_key = load_trusted_public_key()?;
108     let verified_boot_data = verify_payload(
109         &fs::read(TEST_IMG_WITH_SERVICE_VM_PROP_PATH)?,
110         /* initrd= */ None,
111         &public_key,
112     )
113     .map_err(|e| anyhow!("Verification failed. Error: {}", e))?;
114 
115     let kernel_digest = hash(&[&hex::decode("2131")?, &fs::read(UNSIGNED_TEST_IMG_PATH)?]);
116     let expected_boot_data = VerifiedBootData {
117         debug_level: DebugLevel::None,
118         kernel_digest,
119         initrd_digest: None,
120         public_key: &public_key,
121         capabilities: vec![Capability::RemoteAttest],
122         rollback_index: 0,
123     };
124     assert_eq!(expected_boot_data, verified_boot_data);
125 
126     Ok(())
127 }
128 
129 #[test]
payload_with_unknown_vm_type_fails_verification_with_no_initrd() -> Result<()>130 fn payload_with_unknown_vm_type_fails_verification_with_no_initrd() -> Result<()> {
131     assert_payload_verification_fails(
132         &fs::read(TEST_IMG_WITH_UNKNOWN_VM_TYPE_PROP_PATH)?,
133         /* initrd= */ None,
134         &load_trusted_public_key()?,
135         PvmfwVerifyError::UnknownVbmetaProperty,
136     )
137 }
138 
139 #[test]
payload_with_multiple_props_fails_verification_with_no_initrd() -> Result<()>140 fn payload_with_multiple_props_fails_verification_with_no_initrd() -> Result<()> {
141     assert_payload_verification_fails(
142         &fs::read(TEST_IMG_WITH_MULTIPLE_PROPS_PATH)?,
143         /* initrd= */ None,
144         &load_trusted_public_key()?,
145         PvmfwVerifyError::InvalidDescriptors(DescriptorError::InvalidContents),
146     )
147 }
148 
149 #[test]
payload_with_duplicated_capability_fails_verification_with_no_initrd() -> Result<()>150 fn payload_with_duplicated_capability_fails_verification_with_no_initrd() -> Result<()> {
151     assert_payload_verification_fails(
152         &fs::read(TEST_IMG_WITH_DUPLICATED_CAP_PATH)?,
153         /* initrd= */ None,
154         &load_trusted_public_key()?,
155         SlotVerifyError::InvalidMetadata.into(),
156     )
157 }
158 
159 #[test]
payload_with_prop_descriptor_fails_verification_with_no_initrd() -> Result<()>160 fn payload_with_prop_descriptor_fails_verification_with_no_initrd() -> Result<()> {
161     assert_payload_verification_fails(
162         &fs::read(TEST_IMG_WITH_PROP_DESC_PATH)?,
163         /* initrd= */ None,
164         &load_trusted_public_key()?,
165         PvmfwVerifyError::UnknownVbmetaProperty,
166     )
167 }
168 
169 #[test]
payload_expecting_initrd_fails_verification_with_no_initrd() -> Result<()>170 fn payload_expecting_initrd_fails_verification_with_no_initrd() -> Result<()> {
171     assert_payload_verification_fails(
172         &load_latest_signed_kernel()?,
173         /* initrd= */ None,
174         &load_trusted_public_key()?,
175         SlotVerifyError::InvalidMetadata.into(),
176     )
177 }
178 
179 #[test]
payload_with_empty_public_key_fails_verification() -> Result<()>180 fn payload_with_empty_public_key_fails_verification() -> Result<()> {
181     assert_payload_verification_with_initrd_fails(
182         &load_latest_signed_kernel()?,
183         &load_latest_initrd_normal()?,
184         /* trusted_public_key= */ &[0u8; 0],
185         SlotVerifyError::PublicKeyRejected.into(),
186     )
187 }
188 
189 #[test]
payload_with_an_invalid_public_key_fails_verification() -> Result<()>190 fn payload_with_an_invalid_public_key_fails_verification() -> Result<()> {
191     assert_payload_verification_with_initrd_fails(
192         &load_latest_signed_kernel()?,
193         &load_latest_initrd_normal()?,
194         /* trusted_public_key= */ &[0u8; 512],
195         SlotVerifyError::PublicKeyRejected.into(),
196     )
197 }
198 
199 #[test]
payload_with_a_different_valid_public_key_fails_verification() -> Result<()>200 fn payload_with_a_different_valid_public_key_fails_verification() -> Result<()> {
201     assert_payload_verification_with_initrd_fails(
202         &load_latest_signed_kernel()?,
203         &load_latest_initrd_normal()?,
204         &fs::read(PUBLIC_KEY_RSA2048_PATH)?,
205         SlotVerifyError::PublicKeyRejected.into(),
206     )
207 }
208 
209 #[test]
payload_with_an_invalid_initrd_fails_verification() -> Result<()>210 fn payload_with_an_invalid_initrd_fails_verification() -> Result<()> {
211     assert_payload_verification_with_initrd_fails(
212         &load_latest_signed_kernel()?,
213         /* initrd= */ &fs::read(UNSIGNED_TEST_IMG_PATH)?,
214         &load_trusted_public_key()?,
215         SlotVerifyError::Verification(None).into(),
216     )
217 }
218 
219 #[test]
unsigned_kernel_fails_verification() -> Result<()>220 fn unsigned_kernel_fails_verification() -> Result<()> {
221     assert_payload_verification_with_initrd_fails(
222         &fs::read(UNSIGNED_TEST_IMG_PATH)?,
223         &load_latest_initrd_normal()?,
224         &load_trusted_public_key()?,
225         SlotVerifyError::Io.into(),
226     )
227 }
228 
229 #[test]
tampered_kernel_fails_verification() -> Result<()>230 fn tampered_kernel_fails_verification() -> Result<()> {
231     let mut kernel = load_latest_signed_kernel()?;
232     kernel[1] = !kernel[1]; // Flip the bits
233 
234     assert_payload_verification_with_initrd_fails(
235         &kernel,
236         &load_latest_initrd_normal()?,
237         &load_trusted_public_key()?,
238         SlotVerifyError::Verification(None).into(),
239     )
240 }
241 
242 #[test]
kernel_footer_with_vbmeta_offset_overwritten_fails_verification() -> Result<()>243 fn kernel_footer_with_vbmeta_offset_overwritten_fails_verification() -> Result<()> {
244     // Arrange.
245     let mut kernel = load_latest_signed_kernel()?;
246     let total_len = kernel.len() as u64;
247     let footer = extract_avb_footer(&kernel)?;
248     assert!(footer.vbmeta_offset < total_len);
249     // TODO: use core::mem::offset_of once stable.
250     let footer_addr = ptr::addr_of!(footer) as *const u8;
251     let vbmeta_offset_addr = ptr::addr_of!(footer.vbmeta_offset) as *const u8;
252     let vbmeta_offset_start =
253         // SAFETY:
254         // - both raw pointers `vbmeta_offset_addr` and `footer_addr` are not null;
255         // - they are both derived from the `footer` object;
256         // - the offset is known from the struct definition to be a small positive number of bytes.
257         unsafe { vbmeta_offset_addr.offset_from(footer_addr) };
258     let footer_start = kernel.len() - size_of::<AvbFooter>();
259     let vbmeta_offset_start = footer_start + usize::try_from(vbmeta_offset_start)?;
260 
261     let wrong_offsets = [total_len, u64::MAX];
262     for &wrong_offset in wrong_offsets.iter() {
263         // Act.
264         kernel[vbmeta_offset_start..(vbmeta_offset_start + size_of::<u64>())]
265             .copy_from_slice(&wrong_offset.to_be_bytes());
266 
267         // Assert.
268         let footer = extract_avb_footer(&kernel)?;
269         // footer is unaligned; copy vbmeta_offset to local variable
270         let vbmeta_offset = footer.vbmeta_offset;
271         assert_eq!(wrong_offset, vbmeta_offset);
272         assert_payload_verification_with_initrd_fails(
273             &kernel,
274             &load_latest_initrd_normal()?,
275             &load_trusted_public_key()?,
276             SlotVerifyError::Io.into(),
277         )?;
278     }
279     Ok(())
280 }
281 
282 #[test]
tampered_kernel_footer_fails_verification() -> Result<()>283 fn tampered_kernel_footer_fails_verification() -> Result<()> {
284     let mut kernel = load_latest_signed_kernel()?;
285     let avb_footer_index = kernel.len() - size_of::<AvbFooter>() + RANDOM_FOOTER_POS;
286     kernel[avb_footer_index] = !kernel[avb_footer_index];
287 
288     assert_payload_verification_with_initrd_fails(
289         &kernel,
290         &load_latest_initrd_normal()?,
291         &load_trusted_public_key()?,
292         SlotVerifyError::InvalidMetadata.into(),
293     )
294 }
295 
296 #[test]
extended_initrd_fails_verification() -> Result<()>297 fn extended_initrd_fails_verification() -> Result<()> {
298     let mut initrd = load_latest_initrd_normal()?;
299     initrd.extend(b"androidboot.vbmeta.digest=1111");
300 
301     assert_payload_verification_with_initrd_fails(
302         &load_latest_signed_kernel()?,
303         &initrd,
304         &load_trusted_public_key()?,
305         SlotVerifyError::Verification(None).into(),
306     )
307 }
308 
309 #[test]
tampered_vbmeta_fails_verification() -> Result<()>310 fn tampered_vbmeta_fails_verification() -> Result<()> {
311     let mut kernel = load_latest_signed_kernel()?;
312     let footer = extract_avb_footer(&kernel)?;
313     let vbmeta_index: usize = (footer.vbmeta_offset + 1).try_into()?;
314 
315     kernel[vbmeta_index] = !kernel[vbmeta_index]; // Flip the bits
316 
317     assert_payload_verification_with_initrd_fails(
318         &kernel,
319         &load_latest_initrd_normal()?,
320         &load_trusted_public_key()?,
321         SlotVerifyError::InvalidMetadata.into(),
322     )
323 }
324 
325 #[test]
vbmeta_with_public_key_overwritten_fails_verification() -> Result<()>326 fn vbmeta_with_public_key_overwritten_fails_verification() -> Result<()> {
327     let mut kernel = load_latest_signed_kernel()?;
328     let footer = extract_avb_footer(&kernel)?;
329     let vbmeta_header = extract_vbmeta_header(&kernel, &footer)?;
330     let public_key_offset = footer.vbmeta_offset as usize
331         + size_of::<AvbVBMetaImageHeader>()
332         + vbmeta_header.authentication_data_block_size as usize
333         + vbmeta_header.public_key_offset as usize;
334     let public_key_size: usize = vbmeta_header.public_key_size.try_into()?;
335     let empty_public_key = vec![0u8; public_key_size];
336 
337     kernel[public_key_offset..(public_key_offset + public_key_size)]
338         .copy_from_slice(&empty_public_key);
339 
340     assert_payload_verification_with_initrd_fails(
341         &kernel,
342         &load_latest_initrd_normal()?,
343         &empty_public_key,
344         SlotVerifyError::Verification(None).into(),
345     )?;
346     assert_payload_verification_with_initrd_fails(
347         &kernel,
348         &load_latest_initrd_normal()?,
349         &load_trusted_public_key()?,
350         SlotVerifyError::Verification(None).into(),
351     )
352 }
353 
354 #[test]
vbmeta_with_verification_flag_disabled_fails_verification() -> Result<()>355 fn vbmeta_with_verification_flag_disabled_fails_verification() -> Result<()> {
356     // From external/avb/libavb/avb_vbmeta_image.h
357     const AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED: u32 = 2;
358 
359     // Arrange.
360     let mut kernel = load_latest_signed_kernel()?;
361     let footer = extract_avb_footer(&kernel)?;
362     let vbmeta_header = extract_vbmeta_header(&kernel, &footer)?;
363 
364     // vbmeta_header is unaligned; copy flags to local variable
365     let vbmeta_header_flags = vbmeta_header.flags;
366     assert_eq!(0, vbmeta_header_flags, "The disable flag should not be set in the latest kernel.");
367     let flags_addr = ptr::addr_of!(vbmeta_header.flags) as *const u8;
368     // SAFETY: It is safe as both raw pointers `flags_addr` and `vbmeta_header` are not null.
369     let flags_offset = unsafe { flags_addr.offset_from(ptr::addr_of!(vbmeta_header) as *const u8) };
370     let flags_offset = usize::try_from(footer.vbmeta_offset)? + usize::try_from(flags_offset)?;
371 
372     // Act.
373     kernel[flags_offset..(flags_offset + size_of::<u32>())]
374         .copy_from_slice(&AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED.to_be_bytes());
375 
376     // Assert.
377     let vbmeta_header = extract_vbmeta_header(&kernel, &footer)?;
378     // vbmeta_header is unaligned; copy flags to local variable
379     let vbmeta_header_flags = vbmeta_header.flags;
380     assert_eq!(
381         AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED, vbmeta_header_flags,
382         "VBMeta verification flag should be disabled now."
383     );
384     assert_payload_verification_with_initrd_fails(
385         &kernel,
386         &load_latest_initrd_normal()?,
387         &load_trusted_public_key()?,
388         SlotVerifyError::Verification(None).into(),
389     )
390 }
391 
392 #[test]
payload_with_rollback_index() -> Result<()>393 fn payload_with_rollback_index() -> Result<()> {
394     let public_key = load_trusted_public_key()?;
395     let verified_boot_data = verify_payload(
396         &fs::read(TEST_IMG_WITH_ROLLBACK_INDEX_5)?,
397         /* initrd= */ None,
398         &public_key,
399     )
400     .map_err(|e| anyhow!("Verification failed. Error: {}", e))?;
401 
402     let kernel_digest = hash(&[&hex::decode("1211")?, &fs::read(UNSIGNED_TEST_IMG_PATH)?]);
403     let expected_boot_data = VerifiedBootData {
404         debug_level: DebugLevel::None,
405         kernel_digest,
406         initrd_digest: None,
407         public_key: &public_key,
408         capabilities: vec![],
409         rollback_index: 5,
410     };
411     assert_eq!(expected_boot_data, verified_boot_data);
412     Ok(())
413 }
414 
415 #[test]
payload_with_multiple_capabilities() -> Result<()>416 fn payload_with_multiple_capabilities() -> Result<()> {
417     let public_key = load_trusted_public_key()?;
418     let verified_boot_data = verify_payload(
419         &fs::read(TEST_IMG_WITH_MULTIPLE_CAPABILITIES)?,
420         /* initrd= */ None,
421         &public_key,
422     )
423     .map_err(|e| anyhow!("Verification failed. Error: {}", e))?;
424 
425     assert!(verified_boot_data.has_capability(Capability::RemoteAttest));
426     assert!(verified_boot_data.has_capability(Capability::SecretkeeperProtection));
427     Ok(())
428 }
429