1 // Copyright 2024, 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 use crate::{
16 fuchsia_boot::{zbi_split_unused_buffer, zircon_part_name, SlotIndex},
17 gbl_avb::ops::GblAvbOps,
18 gbl_print, GblOps, Result as GblResult,
19 };
20 use avb::{slot_verify, Descriptor, HashtreeErrorMode, Ops as _, SlotVerifyFlags};
21 use zbi::ZbiContainer;
22 use zerocopy::SplitByteSliceMut;
23
24 /// Verifies a loaded ZBI kernel.
25 ///
26 /// # Arguments
27 ///
28 /// * glb_ops - GblOps implementation
29 /// * slot - slot to verify
30 /// * slot_booted_successfully - if true, roll back indexes will be increased
31 /// * zbi_kernel - preloaded kernel to verify
32 /// * zbi_items - vbmeta items will be appended to this ZbiContainer
zircon_verify_kernel<'a, 'b, 'c, B: SplitByteSliceMut + PartialEq>( gbl_ops: &mut impl GblOps<'b, 'c>, slot: Option<SlotIndex>, slot_booted_successfully: bool, zbi_kernel: &'a mut [u8], zbi_items: &mut ZbiContainer<B>, ) -> GblResult<()>33 pub(crate) fn zircon_verify_kernel<'a, 'b, 'c, B: SplitByteSliceMut + PartialEq>(
34 gbl_ops: &mut impl GblOps<'b, 'c>,
35 slot: Option<SlotIndex>,
36 slot_booted_successfully: bool,
37 zbi_kernel: &'a mut [u8],
38 zbi_items: &mut ZbiContainer<B>,
39 ) -> GblResult<()> {
40 // Copy ZBI items after kernel first. Because ordering matters, and new items should override
41 // older ones.
42 // TODO(b/379778252) It is not as efficient as moving kernel since ZBI items would contain file
43 // system and be bigger than kernel.
44 copy_items_after_kernel(zbi_kernel, zbi_items)?;
45
46 let (kernel, _) = zbi_split_unused_buffer(&mut zbi_kernel[..])?;
47
48 // Verifies the kernel.
49 let part = zircon_part_name(slot);
50 let slotless_part = zircon_part_name(None);
51 let preloaded = [(slotless_part, &kernel[..])];
52 let mut avb_ops = GblAvbOps::new(gbl_ops, slot, &preloaded[..], true);
53
54 // Determines verify flags and error mode.
55 let unlocked = avb_ops.read_is_device_unlocked()?;
56 let mode = HashtreeErrorMode::AVB_HASHTREE_ERROR_MODE_EIO; // Don't care for fuchsia
57 let flag = match unlocked {
58 true => SlotVerifyFlags::AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR,
59 _ => SlotVerifyFlags::AVB_SLOT_VERIFY_FLAGS_NONE,
60 };
61
62 // TODO(b/334962583): Supports optional additional partitions to verify.
63 let verify_res = slot_verify(&mut avb_ops, &[c"zircon"], slot.map(|s| s.into()), flag, mode);
64 let verified_success = verify_res.is_ok();
65 let verify_data = match verify_res {
66 Ok(ref v) => {
67 gbl_print!(avb_ops.gbl_ops, "{} successfully verified.\r\n", part);
68 v
69 }
70 Err(ref e) if e.verification_data().is_some() && unlocked => {
71 gbl_print!(avb_ops.gbl_ops, "Verification failed. Device is unlocked. Ignore.\r\n");
72 e.verification_data().unwrap()
73 }
74 Err(_) if unlocked => {
75 gbl_print!(
76 avb_ops.gbl_ops,
77 "Verification failed. No valid verify metadata. \
78 Device is unlocked. Ignore.\r\n"
79 );
80 return Ok(());
81 }
82 Err(e) => {
83 gbl_print!(avb_ops.gbl_ops, "Verification failed {:?}.\r\n", e);
84 return Err(e.without_verify_data().into());
85 }
86 };
87
88 // Collects ZBI items from vbmetadata and appends to the `zbi_items`.
89 for vbmeta_data in verify_data.vbmeta_data() {
90 for prop in vbmeta_data.descriptors()?.iter().filter_map(|d| match d {
91 Descriptor::Property(p) if p.key.starts_with("zbi") => Some(p),
92 _ => None,
93 }) {
94 zbi_items.extend_unaligned(prop.value)?;
95 }
96 }
97
98 // Increases rollback indices if the slot has successfully booted.
99 if verified_success && slot_booted_successfully {
100 for (loc, val) in verify_data.rollback_indexes().iter().enumerate() {
101 if *val > 0 && avb_ops.read_rollback_index(loc)? != *val {
102 avb_ops.write_rollback_index(loc, *val)?;
103 }
104 }
105
106 // Increases rollback index values for Fuchsia key version locations.
107 for key_version in avb_ops.key_versions {
108 match key_version {
109 Some((loc, rollback)) if avb_ops.read_rollback_index(loc)? != rollback => {
110 avb_ops.write_rollback_index(loc, rollback)?;
111 }
112 _ => {}
113 }
114 }
115 }
116
117 Ok(())
118 }
119
120 /// Copy ZBI items following kernel to separate container.
copy_items_after_kernel<'a, B: SplitByteSliceMut + PartialEq>( zbi_kernel: &'a mut [u8], zbi_items: &mut ZbiContainer<B>, ) -> GblResult<()>121 pub fn copy_items_after_kernel<'a, B: SplitByteSliceMut + PartialEq>(
122 zbi_kernel: &'a mut [u8],
123 zbi_items: &mut ZbiContainer<B>,
124 ) -> GblResult<()> {
125 let zbi_container = ZbiContainer::parse(&mut zbi_kernel[..])?;
126 let mut items_iter = zbi_container.iter();
127 items_iter.next(); // Skip first kernel item
128 zbi_items.extend_items(items_iter)?;
129 Ok(())
130 }
131
132 #[cfg(test)]
133 mod test {
134 use super::*;
135 use crate::{
136 fuchsia_boot::{
137 test::{
138 append_cmd_line, corrupt_data, create_gbl_ops, create_storage, normalize_zbi,
139 read_test_data, ZIRCON_A_ZBI_FILE,
140 },
141 ZIRCON_KERNEL_ALIGN,
142 },
143 tests::AlignedBuffer,
144 };
145 use avb_bindgen::{AVB_CERT_PIK_VERSION_LOCATION, AVB_CERT_PSK_VERSION_LOCATION};
146 use zbi::ZBI_ALIGNMENT_USIZE;
147
148 // The cert test keys were both generated with rollback version 42.
149 const TEST_CERT_PIK_VERSION: u64 = 42;
150 const TEST_CERT_PSK_VERSION: u64 = 42;
151
152 #[test]
test_verify_success()153 fn test_verify_success() {
154 let storage = create_storage();
155 let mut ops = create_gbl_ops(&storage);
156
157 let expect_rollback = ops.avb_ops.rollbacks.clone();
158 let zbi = &read_test_data(ZIRCON_A_ZBI_FILE);
159 let mut load_buffer = AlignedBuffer::new(zbi.len(), ZIRCON_KERNEL_ALIGN);
160 let mut zbi_items_buffer = AlignedBuffer::new(1024, ZBI_ALIGNMENT_USIZE);
161 let mut zbi_items = ZbiContainer::new(&mut zbi_items_buffer[..]).unwrap();
162 load_buffer[..zbi.len()].clone_from_slice(zbi);
163 zircon_verify_kernel(&mut ops, Some(SlotIndex::A), false, &mut load_buffer, &mut zbi_items)
164 .unwrap();
165
166 // Verifies that vbmeta ZBI items are appended. Non-zbi items are ignored.
167 let mut expected_zbi_items = AlignedBuffer::new(zbi.len() + 1024, 8);
168 let _ = ZbiContainer::new(&mut expected_zbi_items[..]).unwrap();
169 append_cmd_line(&mut expected_zbi_items, b"vb_prop_0=val\0");
170 append_cmd_line(&mut expected_zbi_items, b"vb_prop_1=val\0");
171 assert_eq!(normalize_zbi(&zbi_items_buffer), normalize_zbi(&expected_zbi_items));
172
173 // Slot is not successful, rollback index should not be updated.
174 assert_eq!(expect_rollback, ops.avb_ops.rollbacks);
175 }
176
177 #[test]
test_verify_update_rollback_index_for_successful_slot()178 fn test_verify_update_rollback_index_for_successful_slot() {
179 let storage = create_storage();
180 let mut ops = create_gbl_ops(&storage);
181
182 let zbi = &read_test_data(ZIRCON_A_ZBI_FILE);
183 let mut load_buffer = AlignedBuffer::new(zbi.len(), ZIRCON_KERNEL_ALIGN);
184 load_buffer[..zbi.len()].clone_from_slice(zbi);
185 let mut zbi_items_buffer = AlignedBuffer::new(1024, ZBI_ALIGNMENT_USIZE);
186 let mut zbi_items = ZbiContainer::new(&mut zbi_items_buffer[..]).unwrap();
187 zircon_verify_kernel(&mut ops, Some(SlotIndex::A), true, &mut load_buffer, &mut zbi_items)
188 .unwrap();
189
190 // Slot is successful, rollback index should be updated.
191 // vbmeta_a has rollback index value 2 at location 1.
192 assert_eq!(
193 ops.avb_ops.rollbacks,
194 [
195 (1, Ok(2)),
196 (
197 usize::try_from(AVB_CERT_PSK_VERSION_LOCATION).unwrap(),
198 Ok(TEST_CERT_PSK_VERSION)
199 ),
200 (
201 usize::try_from(AVB_CERT_PIK_VERSION_LOCATION).unwrap(),
202 Ok(TEST_CERT_PIK_VERSION)
203 )
204 ]
205 .into()
206 );
207 }
208
209 #[test]
test_verify_failed_on_corrupted_image()210 fn test_verify_failed_on_corrupted_image() {
211 let storage = create_storage();
212 let mut ops = create_gbl_ops(&storage);
213
214 let expect_rollback = ops.avb_ops.rollbacks.clone();
215 let zbi = &read_test_data(ZIRCON_A_ZBI_FILE);
216 let mut load_buffer = AlignedBuffer::new(zbi.len(), ZIRCON_KERNEL_ALIGN);
217 load_buffer[..zbi.len()].clone_from_slice(zbi);
218 let mut zbi_items_buffer = AlignedBuffer::new(1024, ZBI_ALIGNMENT_USIZE);
219 let mut zbi_items = ZbiContainer::new(&mut zbi_items_buffer[..]).unwrap();
220 // Corrupts a random kernel bytes. Skips pass two ZBI headers.
221 load_buffer[64] = !load_buffer[64];
222 let expect_load = load_buffer.to_vec();
223 assert!(zircon_verify_kernel(
224 &mut ops,
225 Some(SlotIndex::A),
226 true,
227 &mut load_buffer,
228 &mut zbi_items
229 )
230 .is_err());
231 // Failed while device is locked. ZBI items should not be appended.
232 assert_eq!(expect_load, &load_buffer[..]);
233 // Rollback index should not be updated on verification failure.
234 assert_eq!(expect_rollback, ops.avb_ops.rollbacks);
235 }
236
237 #[test]
test_verify_failed_on_corrupted_vbmetadata()238 fn test_verify_failed_on_corrupted_vbmetadata() {
239 let storage = create_storage();
240 let mut ops = create_gbl_ops(&storage);
241
242 let expect_rollback = ops.avb_ops.rollbacks.clone();
243 let zbi = &read_test_data(ZIRCON_A_ZBI_FILE);
244 let mut load = AlignedBuffer::new(zbi.len(), ZIRCON_KERNEL_ALIGN);
245 load[..zbi.len()].clone_from_slice(zbi);
246 let mut zbi_items_buffer = AlignedBuffer::new(1024, ZBI_ALIGNMENT_USIZE);
247 let mut zbi_items = ZbiContainer::new(&mut zbi_items_buffer[..]).unwrap();
248 let expect_load = load.to_vec();
249 // Corrupts vbmetadata
250 corrupt_data(&mut ops, "vbmeta_a");
251 assert!(zircon_verify_kernel(
252 &mut ops,
253 Some(SlotIndex::A),
254 true,
255 &mut load,
256 &mut zbi_items
257 )
258 .is_err());
259 // Failed while device is locked. ZBI items should not be appended.
260 assert_eq!(expect_load, &load[..]);
261 // Rollback index should not be updated on verification failure.
262 assert_eq!(expect_rollback, ops.avb_ops.rollbacks);
263 }
264
265 #[test]
test_verify_failed_on_rollback_protection()266 fn test_verify_failed_on_rollback_protection() {
267 let storage = create_storage();
268 let mut ops = create_gbl_ops(&storage);
269
270 let zbi = &read_test_data(ZIRCON_A_ZBI_FILE);
271 let mut load_buffer = AlignedBuffer::new(zbi.len(), ZIRCON_KERNEL_ALIGN);
272 load_buffer[..zbi.len()].clone_from_slice(zbi);
273 let mut zbi_items_buffer = AlignedBuffer::new(1024, ZBI_ALIGNMENT_USIZE);
274 let mut zbi_items = ZbiContainer::new(&mut zbi_items_buffer[..]).unwrap();
275 let expect_load = load_buffer.to_vec();
276 // vbmeta_a has rollback index value 2 at location 1. Setting min rollback value of 3 should
277 // cause rollback protection failure.
278 ops.avb_ops.rollbacks.insert(1, Ok(3));
279 let expect_rollback = ops.avb_ops.rollbacks.clone();
280 assert!(zircon_verify_kernel(
281 &mut ops,
282 Some(SlotIndex::A),
283 true,
284 &mut load_buffer,
285 &mut zbi_items
286 )
287 .is_err());
288 // Failed while device is locked. ZBI items should not be appended.
289 assert_eq!(expect_load, &load_buffer[..]);
290 // Rollback index should not be updated on verification failure.
291 assert_eq!(expect_rollback, ops.avb_ops.rollbacks);
292 }
293
294 #[test]
test_verify_failure_when_unlocked()295 fn test_verify_failure_when_unlocked() {
296 let storage = create_storage();
297 let mut ops = create_gbl_ops(&storage);
298
299 ops.avb_ops.unlock_state = Ok(true);
300 let expect_rollback = ops.avb_ops.rollbacks.clone();
301
302 let zbi = &read_test_data(ZIRCON_A_ZBI_FILE);
303 let mut load_buffer = AlignedBuffer::new(zbi.len(), ZIRCON_KERNEL_ALIGN);
304 load_buffer[..zbi.len()].clone_from_slice(zbi);
305 let mut zbi_items_buffer = AlignedBuffer::new(1024, ZBI_ALIGNMENT_USIZE);
306 let mut zbi_items = ZbiContainer::new(&mut zbi_items_buffer[..]).unwrap();
307 // Corrupts a random kernel bytes. Skips pass two ZBI headers.
308 load_buffer[64] = !load_buffer[64];
309 // Verification should proceeds OK.
310 zircon_verify_kernel(&mut ops, Some(SlotIndex::A), true, &mut load_buffer, &mut zbi_items)
311 .unwrap();
312 // Verifies that vbmeta ZBI items are appended as long as unlocked.
313 let mut expected_zbi_items = AlignedBuffer::new(load_buffer.len(), ZBI_ALIGNMENT_USIZE);
314 let _ = ZbiContainer::new(&mut expected_zbi_items[..]).unwrap();
315 append_cmd_line(&mut expected_zbi_items, b"vb_prop_0=val\0");
316 append_cmd_line(&mut expected_zbi_items, b"vb_prop_1=val\0");
317 assert_eq!(normalize_zbi(&zbi_items_buffer), normalize_zbi(&expected_zbi_items));
318 // Rollback index should not be updated in any failure cases, even when unlocked.
319 assert_eq!(expect_rollback, ops.avb_ops.rollbacks);
320 }
321
322 #[test]
test_copy_items_after_kernel()323 fn test_copy_items_after_kernel() {
324 let zbi = &read_test_data(ZIRCON_A_ZBI_FILE);
325 let mut load_buffer = AlignedBuffer::new(zbi.len() + 1024, ZIRCON_KERNEL_ALIGN);
326 load_buffer[..zbi.len()].clone_from_slice(zbi);
327 // Add items that will be copied
328 append_cmd_line(&mut load_buffer, b"vb_prop_0=val\0");
329 append_cmd_line(&mut load_buffer, b"vb_prop_1=val\0");
330
331 // Create ZBI items container that contain 1 element
332 let mut zbi_items_buffer = AlignedBuffer::new(1024, ZBI_ALIGNMENT_USIZE);
333 let _ = ZbiContainer::new(&mut zbi_items_buffer[..]).unwrap();
334 append_cmd_line(&mut zbi_items_buffer, b"vb_prop_2=val\0");
335 let mut zbi_items = ZbiContainer::parse(&mut zbi_items_buffer[..]).unwrap();
336
337 // Verifies that ZBI items are appended
338 let mut expected_zbi_items = AlignedBuffer::new(load_buffer.len(), ZBI_ALIGNMENT_USIZE);
339 let _ = ZbiContainer::new(&mut expected_zbi_items[..]).unwrap();
340 append_cmd_line(&mut expected_zbi_items, b"vb_prop_2=val\0");
341 append_cmd_line(&mut expected_zbi_items, b"vb_prop_0=val\0");
342 append_cmd_line(&mut expected_zbi_items, b"vb_prop_1=val\0");
343
344 copy_items_after_kernel(&mut load_buffer, &mut zbi_items).unwrap();
345 assert_eq!(normalize_zbi(&zbi_items_buffer), normalize_zbi(&expected_zbi_items));
346 }
347
348 #[test]
test_verify_failure_by_corrupted_vbmetadata_unlocked()349 fn test_verify_failure_by_corrupted_vbmetadata_unlocked() {
350 let storage = create_storage();
351 let mut ops = create_gbl_ops(&storage);
352
353 ops.avb_ops.unlock_state = Ok(true);
354 let expect_rollback = ops.avb_ops.rollbacks.clone();
355 let zbi = &read_test_data(ZIRCON_A_ZBI_FILE);
356 let mut load_buffer = AlignedBuffer::new(zbi.len(), ZIRCON_KERNEL_ALIGN);
357 load_buffer[..zbi.len()].clone_from_slice(zbi);
358 let mut zbi_items_buffer = AlignedBuffer::new(1024, ZBI_ALIGNMENT_USIZE);
359 let mut zbi_items = ZbiContainer::new(&mut zbi_items_buffer[..]).unwrap();
360 let expect_load = load_buffer.to_vec();
361 // Corrupts vbmetadata
362 corrupt_data(&mut ops, "vbmeta_a");
363 zircon_verify_kernel(&mut ops, Some(SlotIndex::A), true, &mut load_buffer, &mut zbi_items)
364 .unwrap();
365 // Unlocked but vbmetadata is invalid so no ZBI items should be appended.
366 assert_eq!(expect_load, &load_buffer[..]);
367 // Rollback index should not be updated on verification failure.
368 assert_eq!(expect_rollback, ops.avb_ops.rollbacks);
369 }
370 }
371