blob: 24c4f059d74e67e7f9163a41569e7eb72e174670 [file] [log] [blame]
Jooyung Hanc8deb472021-09-13 13:48:25 +09001// Copyright 2021, 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//! Routines for handling APEX payload
16
17use anyhow::{anyhow, ensure, Result};
18use avb_bindgen::*;
19use std::ffi::{c_void, CStr};
20use std::fs::File;
21use std::io::{Read, Seek, SeekFrom};
22use std::mem::{size_of, zeroed};
23use std::ops::Deref;
24use std::ptr::null_mut;
25use std::slice::{from_raw_parts, from_raw_parts_mut};
26use zip::ZipArchive;
27
28const APEX_PUBKEY_ENTRY: &str = "apex_pubkey";
29const APEX_PAYLOAD_ENTRY: &str = "apex_payload.img";
30
31/// Verification result holds public key and root digest of apex_payload.img
32pub struct ApexVerificationResult {
33 pub public_key: Vec<u8>,
34 pub root_digest: Vec<u8>,
35}
36
37/// Verify APEX payload by AVB verification and return public key and root digest
38pub fn verify(path: &str) -> Result<ApexVerificationResult> {
39 let apex_file = File::open(path)?;
40 let (public_key, image_offset, image_size) = get_public_key_and_image_info(&apex_file)?;
41 let root_digest = verify_vbmeta(apex_file, image_offset, image_size, &public_key)?;
42 Ok(ApexVerificationResult { public_key, root_digest })
43}
44
45fn get_public_key_and_image_info(apex_file: &File) -> Result<(Vec<u8>, u64, u64)> {
46 let mut z = ZipArchive::new(apex_file)?;
47
48 let mut public_key = Vec::new();
49 z.by_name(APEX_PUBKEY_ENTRY)?.read_to_end(&mut public_key)?;
50
51 let (image_offset, image_size) =
52 z.by_name(APEX_PAYLOAD_ENTRY).map(|f| (f.data_start(), f.size()))?;
53
54 Ok((public_key, image_offset, image_size))
55}
56
57// Manual addition of a missing enum
58#[allow(non_camel_case_types, dead_code)]
59#[repr(u8)]
60enum AvbDescriptorTag {
61 AVB_DESCRIPTOR_TAG_PROPERTY = 0,
62 AVB_DESCRIPTOR_TAG_HASHTREE,
63 AVB_DESCRIPTOR_TAG_HASH,
64 AVB_DESCRIPTOR_TAG_KERNEL_CMDLINE,
65 AVB_DESCRIPTOR_TAG_CHAIN_PARTITION,
66}
67
68const FOOTER_SIZE: usize = size_of::<AvbFooter>();
69const HASHTREE_DESCRIPTOR_SIZE: usize = size_of::<AvbHashtreeDescriptor>();
70
71/// Verify VBmeta image and return root digest
72fn verify_vbmeta<R: Read + Seek>(
73 image: R,
74 offset: u64,
75 size: u64,
76 public_key: &[u8],
77) -> Result<Vec<u8>> {
78 let vbmeta = VbMeta::from(image, offset, size)?;
79 vbmeta.verify(public_key)?;
80 for &descriptor in vbmeta.descriptors()?.iter() {
81 if let Ok(hashtree_descriptor) = HashtreeDescriptor::from(descriptor) {
82 return hashtree_descriptor.root_digest();
83 }
84 }
85 Err(anyhow!("HashtreeDescriptor is not found."))
86}
87
88struct VbMeta {
89 data: Vec<u8>,
90}
91
92impl VbMeta {
93 // Read a VbMeta data from a given image
94 fn from<R: Read + Seek>(mut image: R, offset: u64, size: u64) -> Result<VbMeta> {
95 // Get AvbFooter first
96 image.seek(SeekFrom::Start(offset + size - FOOTER_SIZE as u64))?;
97 // SAFETY: AvbDescriptor is a "repr(C,packed)" struct from bindgen
98 let mut footer: AvbFooter = unsafe { zeroed() };
99 // SAFETY: safe to read because of seek(-FOOTER_SIZE) above
100 unsafe {
101 let footer_slice = from_raw_parts_mut(&mut footer as *mut _ as *mut u8, FOOTER_SIZE);
102 image.read_exact(footer_slice)?;
103 ensure!(avb_footer_validate_and_byteswap(&footer, &mut footer));
104 }
105 // Get VbMeta block
106 image.seek(SeekFrom::Start(offset + footer.vbmeta_offset))?;
107 let vbmeta_size = footer.vbmeta_size as usize;
108 let mut data = vec![0u8; vbmeta_size];
109 image.read_exact(&mut data)?;
110 Ok(VbMeta { data })
111 }
112 // Verify VbMeta image. Its enclosed public key should match with a given public key.
113 fn verify(&self, outer_public_key: &[u8]) -> Result<()> {
114 // SAFETY: self.data points to a valid VBMeta data and avb_vbmeta_image_verify should work fine
115 // with it
116 let public_key = unsafe {
117 let mut pk_ptr: *const u8 = null_mut();
118 let mut pk_len: usize = 0;
119 let res = avb_vbmeta_image_verify(
120 self.data.as_ptr(),
121 self.data.len(),
122 &mut pk_ptr,
123 &mut pk_len,
124 );
125 ensure!(
126 res == AvbVBMetaVerifyResult_AVB_VBMETA_VERIFY_RESULT_OK,
127 CStr::from_ptr(avb_vbmeta_verify_result_to_string(res))
128 .to_string_lossy()
129 .into_owned()
130 );
131 from_raw_parts(pk_ptr, pk_len)
132 };
133
134 ensure!(public_key == outer_public_key, "Public key mismatch with a given one.");
135 Ok(())
136 }
137 // Return a slice of AvbDescriptor pointers
138 fn descriptors(&self) -> Result<Descriptors> {
139 let mut num: usize = 0;
140 // SAFETY: ptr will be freed by Descriptor.
141 Ok(unsafe {
142 let ptr = avb_descriptor_get_all(self.data.as_ptr(), self.data.len(), &mut num);
143 ensure!(!ptr.is_null(), "VbMeta has no descriptors.");
144 let all = from_raw_parts(ptr, num);
145 Descriptors { ptr, all }
146 })
147 }
148}
149
150struct HashtreeDescriptor {
151 ptr: *const u8,
152 inner: AvbHashtreeDescriptor,
153}
154
155impl HashtreeDescriptor {
156 fn from(descriptor: *const AvbDescriptor) -> Result<HashtreeDescriptor> {
157 // SAFETY: AvbDescriptor is a "repr(C,packed)" struct from bindgen
158 let mut desc: AvbDescriptor = unsafe { zeroed() };
159 // SAFETY: both points to valid AvbDescriptor pointers
160 unsafe {
161 ensure!(avb_descriptor_validate_and_byteswap(descriptor, &mut desc));
162 }
163 ensure!({ desc.tag } == AvbDescriptorTag::AVB_DESCRIPTOR_TAG_HASHTREE as u64);
164 // SAFETY: AvbHashtreeDescriptor is a "repr(C, packed)" struct from bindgen
165 let mut hashtree_descriptor: AvbHashtreeDescriptor = unsafe { zeroed() };
166 // SAFETY: With tag == AVB_DESCRIPTOR_TAG_HASHTREE, descriptor should point to
167 // a AvbHashtreeDescriptor.
168 unsafe {
169 ensure!(avb_hashtree_descriptor_validate_and_byteswap(
170 descriptor as *const AvbHashtreeDescriptor,
171 &mut hashtree_descriptor,
172 ));
173 }
174 Ok(Self { ptr: descriptor as *const u8, inner: hashtree_descriptor })
175 }
176 fn root_digest(&self) -> Result<Vec<u8>> {
177 // SAFETY: digest_ptr should point to a valid buffer of root_digest_len
178 let root_digest = unsafe {
179 let digest_ptr = self.ptr.offset(
180 HASHTREE_DESCRIPTOR_SIZE as isize
181 + self.inner.partition_name_len as isize
182 + self.inner.salt_len as isize,
183 );
184 from_raw_parts(digest_ptr, self.inner.root_digest_len as usize)
185 };
186 Ok(root_digest.to_owned())
187 }
188}
189
190// Wraps pointer to a heap-allocated array of AvbDescriptor pointers
191struct Descriptors<'a> {
192 ptr: *mut *const AvbDescriptor,
193 all: &'a [*const AvbDescriptor],
194}
195
196// Wrapped pointer should be freed with avb_free.
197impl Drop for Descriptors<'_> {
198 fn drop(&mut self) {
199 // SAFETY: ptr is allocated by avb_descriptor_get_all
200 unsafe { avb_free(self.ptr as *mut c_void) }
201 }
202}
203
204impl<'a> Deref for Descriptors<'a> {
205 type Target = &'a [*const AvbDescriptor];
206 fn deref(&self) -> &Self::Target {
207 &self.all
208 }
209}
210
211#[cfg(test)]
212mod tests {
213 use super::*;
214 fn to_hex_string(buf: &[u8]) -> String {
215 buf.iter().map(|b| format!("{:02x}", b)).collect()
216 }
217 #[test]
218 fn test_open_apex() {
219 let res = verify("tests/data/test.apex").unwrap();
220 assert_eq!(
221 to_hex_string(&res.root_digest),
222 "fe11ab17da0a3a738b54bdc3a13f6139cbdf91ec32f001f8d4bbbf8938e04e39"
223 );
224 }
225}