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