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 _, SlotVerifyError, SlotVerifyFlags};
21 use core::ffi::CStr;
22 use zbi::ZbiContainer;
23 use zerocopy::ByteSliceMut;
24
25 /// Helper for getting the A/B/R suffix.
slot_suffix(slot: Option<SlotIndex>) -> Option<&'static CStr>26 fn slot_suffix(slot: Option<SlotIndex>) -> Option<&'static CStr> {
27 Some(match slot? {
28 SlotIndex::A => c"_a",
29 SlotIndex::B => c"_b",
30 SlotIndex::R => c"_r",
31 })
32 }
33
34 /// Verifies a loaded ZBI kernel.
35 ///
36 /// # Arguments
37 ///
38 /// * glb_ops - GblOps implementation
39 /// * slot - slot to verify
40 /// * slot_booted_successfully - if true, roll back indexes will be increased
41 /// * zbi_kernel - preloaded kernel to verify
42 /// * zbi_items - vbmeta items will be appended to this ZbiContainer
zircon_verify_kernel<'a, 'b, 'c, B: ByteSliceMut + 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<()>43 pub(crate) fn zircon_verify_kernel<'a, 'b, 'c, B: ByteSliceMut + PartialEq>(
44 gbl_ops: &mut impl GblOps<'b, 'c>,
45 slot: Option<SlotIndex>,
46 slot_booted_successfully: bool,
47 zbi_kernel: &'a mut [u8],
48 zbi_items: &mut ZbiContainer<B>,
49 ) -> GblResult<()> {
50 let (kernel, _) = zbi_split_unused_buffer(&mut zbi_kernel[..])?;
51
52 // Verifies the kernel.
53 let part = zircon_part_name(slot);
54 let preloaded = [(part, &kernel[..])];
55 let mut avb_ops = GblAvbOps::new(gbl_ops, &preloaded[..], true);
56
57 // Determines verify flags and error mode.
58 let unlocked = avb_ops.read_is_device_unlocked()?;
59 let mode = HashtreeErrorMode::AVB_HASHTREE_ERROR_MODE_EIO; // Don't care for fuchsia
60 let flag = match unlocked {
61 true => SlotVerifyFlags::AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR,
62 _ => SlotVerifyFlags::AVB_SLOT_VERIFY_FLAGS_NONE,
63 };
64
65 // TODO(b/334962583): Supports optional additional partitions to verify.
66 let verify_res = slot_verify(&mut avb_ops, &[c"zircon"], slot_suffix(slot), flag, mode);
67 let verified_success = verify_res.is_ok();
68 let verify_data = match verify_res {
69 Ok(v) => {
70 gbl_print!(avb_ops.gbl_ops, "{} successfully verified.\r\n", part);
71 v
72 }
73 Err(SlotVerifyError::Verification(Some(v))) if unlocked => {
74 gbl_print!(avb_ops.gbl_ops, "Verification failed. Device is unlocked. Ignore.\r\n");
75 v
76 }
77 Err(_) if unlocked => {
78 gbl_print!(
79 avb_ops.gbl_ops,
80 "Verification failed. No valid verify metadata. \
81 Device is unlocked. Ignore.\r\n"
82 );
83 return Ok(());
84 }
85 Err(e) => {
86 gbl_print!(avb_ops.gbl_ops, "Verification failed {:?}.\r\n", e);
87 return Err(e.without_verify_data().into());
88 }
89 };
90
91 // Collects ZBI items from vbmetadata and appends to the `zbi_items`.
92 for vbmeta_data in verify_data.vbmeta_data() {
93 for prop in vbmeta_data.descriptors()?.iter().filter_map(|d| match d {
94 Descriptor::Property(p) if p.key.starts_with("zbi") => Some(p),
95 _ => None,
96 }) {
97 zbi_items.extend_unaligned(prop.value)?;
98 }
99 }
100
101 // Increases rollback indices if the slot has successfully booted.
102 if verified_success && slot_booted_successfully {
103 for (loc, val) in verify_data.rollback_indexes().iter().enumerate() {
104 if *val > 0 && avb_ops.read_rollback_index(loc)? != *val {
105 avb_ops.write_rollback_index(loc, *val)?;
106 }
107 }
108
109 // Increases rollback index values for Fuchsia key version locations.
110 for key_version in avb_ops.key_versions {
111 match key_version {
112 Some((loc, rollback)) if avb_ops.read_rollback_index(loc)? != rollback => {
113 avb_ops.write_rollback_index(loc, rollback)?;
114 }
115 _ => {}
116 }
117 }
118 }
119
120 Ok(())
121 }
122
123 /// Copy ZBI items following kernel to separate container.
copy_items_after_kernel<'a, B: ByteSliceMut + PartialEq>( zbi_kernel: &'a mut [u8], zbi_items: &mut ZbiContainer<B>, ) -> GblResult<()>124 pub fn copy_items_after_kernel<'a, B: ByteSliceMut + PartialEq>(
125 zbi_kernel: &'a mut [u8],
126 zbi_items: &mut ZbiContainer<B>,
127 ) -> GblResult<()> {
128 let zbi_container = ZbiContainer::parse(&mut zbi_kernel[..])?;
129 let mut items_iter = zbi_container.iter();
130 items_iter.next(); // Skip first kernel item
131 zbi_items.extend_items(items_iter)?;
132 Ok(())
133 }
134
135 #[cfg(test)]
136 mod test {
137 use super::*;
138 use crate::fuchsia_boot::{
139 test::{
140 append_cmd_line, corrupt_data, create_gbl_ops, create_storage, normalize_zbi,
141 read_test_data, AlignedBuffer, ZIRCON_A_ZBI_FILE,
142 },
143 ZIRCON_KERNEL_ALIGN,
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