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