blob: d53e9078b57edfc791e7a867bec3351f299eb803 [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
Jooyung Hanc8deb472021-09-13 13:48:25 +090017use avb_bindgen::*;
18use std::ffi::{c_void, CStr};
19use std::fs::File;
Andrew Scull9cbf2e02022-04-25 18:12:27 +000020use std::io::{self, Read, Seek, SeekFrom};
Jooyung Hanc8deb472021-09-13 13:48:25 +090021use std::mem::{size_of, zeroed};
22use std::ops::Deref;
23use std::ptr::null_mut;
24use std::slice::{from_raw_parts, from_raw_parts_mut};
Andrew Scull9cbf2e02022-04-25 18:12:27 +000025use thiserror::Error;
26use zip::result::ZipError;
Jooyung Hanc8deb472021-09-13 13:48:25 +090027use zip::ZipArchive;
28
29const APEX_PUBKEY_ENTRY: &str = "apex_pubkey";
30const APEX_PAYLOAD_ENTRY: &str = "apex_payload.img";
31
Andrew Scull9cbf2e02022-04-25 18:12:27 +000032/// Errors from parsing an APEX.
33#[derive(Debug, Error)]
34pub enum ApexParseError {
35 /// There was an IO error.
36 #[error("IO error")]
37 Io(#[from] io::Error),
38 /// The Zip archive was invalid.
39 #[error("Cannot read zip archive")]
40 InvalidZip(&'static str),
41 /// The apex_pubkey file was missing from the APEX.
42 #[error("APEX doesn't contain apex_pubkey")]
43 PubkeyMissing,
44 /// The apex_payload.img file was missing from the APEX.
45 #[error("APEX doesn't contain apex_payload.img")]
46 PayloadMissing,
47 /// The AVB footer in the APEX payload was invalid.
48 #[error("Cannot validate APEX payload AVB footer")]
49 InvalidPayloadAvbFooter,
50 /// There were no descriptors in the APEX payload's AVB footer.
51 #[error("No descriptors found in payload AVB footer")]
52 NoDescriptors,
53 /// There was an invalid descriptor in the APEX payload's AVB footer.
54 #[error("Invalid descriptor found in payload AVB footer")]
55 InvalidDescriptor,
56 /// There was no hashtree descriptor in the APEX payload's AVB footer.
57 #[error("Non-hashtree descriptor found in payload AVB footer")]
58 DescriptorNotHashtree,
59 /// There was an invalid hashtree descriptor in the APEX payload's AVB footer.
60 #[error("Invalid hashtree descriptor found in payload AVB footer")]
61 InvalidHashtreeDescriptor,
62}
63
64/// Errors from verifying an APEX.
65#[derive(Debug, Error)]
66pub enum ApexVerificationError {
67 /// There was an error parsing the APEX.
68 #[error("Cannot parse APEX file")]
69 ParseError(#[from] ApexParseError),
70 /// The APEX payload signature did not validate.
71 #[error("Cannot verify payload signature")]
72 BadPayloadSignature(String),
73 /// The APEX payload was signed with a different key.
74 #[error("Payload is signed with the wrong key")]
75 BadPayloadKey,
76}
77
Jooyung Hanc8deb472021-09-13 13:48:25 +090078/// Verification result holds public key and root digest of apex_payload.img
79pub struct ApexVerificationResult {
Andrew Scull9cbf2e02022-04-25 18:12:27 +000080 /// The public key that verifies the payload signature.
Jooyung Hanc8deb472021-09-13 13:48:25 +090081 pub public_key: Vec<u8>,
Andrew Scull9cbf2e02022-04-25 18:12:27 +000082 /// The root digest of the payload hashtree.
Jooyung Hanc8deb472021-09-13 13:48:25 +090083 pub root_digest: Vec<u8>,
84}
85
86/// Verify APEX payload by AVB verification and return public key and root digest
Andrew Scull9cbf2e02022-04-25 18:12:27 +000087pub fn verify(path: &str) -> Result<ApexVerificationResult, ApexVerificationError> {
88 let apex_file = File::open(path).map_err(ApexParseError::Io)?;
Jooyung Hanc8deb472021-09-13 13:48:25 +090089 let (public_key, image_offset, image_size) = get_public_key_and_image_info(&apex_file)?;
90 let root_digest = verify_vbmeta(apex_file, image_offset, image_size, &public_key)?;
91 Ok(ApexVerificationResult { public_key, root_digest })
92}
93
Andrew Scull9cbf2e02022-04-25 18:12:27 +000094fn get_public_key_and_image_info(apex_file: &File) -> Result<(Vec<u8>, u64, u64), ApexParseError> {
95 let mut z = ZipArchive::new(apex_file).map_err(|err| match err {
96 ZipError::Io(err) => ApexParseError::Io(err),
97 ZipError::InvalidArchive(s) | ZipError::UnsupportedArchive(s) => {
98 ApexParseError::InvalidZip(s)
99 }
100 ZipError::FileNotFound => unreachable!(),
101 })?;
Jooyung Hanc8deb472021-09-13 13:48:25 +0900102
103 let mut public_key = Vec::new();
Andrew Scull9cbf2e02022-04-25 18:12:27 +0000104 z.by_name(APEX_PUBKEY_ENTRY)
105 .map_err(|err| match err {
106 ZipError::Io(err) => ApexParseError::Io(err),
107 ZipError::FileNotFound => ApexParseError::PubkeyMissing,
108 ZipError::InvalidArchive(s) | ZipError::UnsupportedArchive(s) => {
109 ApexParseError::InvalidZip(s)
110 }
111 })?
112 .read_to_end(&mut public_key)?;
Jooyung Hanc8deb472021-09-13 13:48:25 +0900113
Andrew Scull9cbf2e02022-04-25 18:12:27 +0000114 let (image_offset, image_size) = z
115 .by_name(APEX_PAYLOAD_ENTRY)
116 .map(|f| (f.data_start(), f.size()))
117 .map_err(|err| match err {
118 ZipError::Io(err) => ApexParseError::Io(err),
119 ZipError::FileNotFound => ApexParseError::PayloadMissing,
120 ZipError::InvalidArchive(s) | ZipError::UnsupportedArchive(s) => {
121 ApexParseError::InvalidZip(s)
122 }
123 })?;
Jooyung Hanc8deb472021-09-13 13:48:25 +0900124
125 Ok((public_key, image_offset, image_size))
126}
127
128// Manual addition of a missing enum
129#[allow(non_camel_case_types, dead_code)]
130#[repr(u8)]
131enum AvbDescriptorTag {
132 AVB_DESCRIPTOR_TAG_PROPERTY = 0,
133 AVB_DESCRIPTOR_TAG_HASHTREE,
134 AVB_DESCRIPTOR_TAG_HASH,
135 AVB_DESCRIPTOR_TAG_KERNEL_CMDLINE,
136 AVB_DESCRIPTOR_TAG_CHAIN_PARTITION,
137}
138
139const FOOTER_SIZE: usize = size_of::<AvbFooter>();
140const HASHTREE_DESCRIPTOR_SIZE: usize = size_of::<AvbHashtreeDescriptor>();
141
142/// Verify VBmeta image and return root digest
143fn verify_vbmeta<R: Read + Seek>(
144 image: R,
145 offset: u64,
146 size: u64,
147 public_key: &[u8],
Andrew Scull9cbf2e02022-04-25 18:12:27 +0000148) -> Result<Vec<u8>, ApexVerificationError> {
Jooyung Hanc8deb472021-09-13 13:48:25 +0900149 let vbmeta = VbMeta::from(image, offset, size)?;
150 vbmeta.verify(public_key)?;
151 for &descriptor in vbmeta.descriptors()?.iter() {
152 if let Ok(hashtree_descriptor) = HashtreeDescriptor::from(descriptor) {
Andrew Scull9cbf2e02022-04-25 18:12:27 +0000153 return Ok(hashtree_descriptor.root_digest());
Jooyung Hanc8deb472021-09-13 13:48:25 +0900154 }
155 }
Andrew Scull9cbf2e02022-04-25 18:12:27 +0000156 Err(ApexParseError::DescriptorNotHashtree.into())
Jooyung Hanc8deb472021-09-13 13:48:25 +0900157}
158
159struct VbMeta {
160 data: Vec<u8>,
161}
162
163impl VbMeta {
164 // Read a VbMeta data from a given image
Andrew Scull9cbf2e02022-04-25 18:12:27 +0000165 fn from<R: Read + Seek>(
166 mut image: R,
167 offset: u64,
168 size: u64,
169 ) -> Result<VbMeta, ApexParseError> {
Jooyung Hanc8deb472021-09-13 13:48:25 +0900170 // Get AvbFooter first
171 image.seek(SeekFrom::Start(offset + size - FOOTER_SIZE as u64))?;
172 // SAFETY: AvbDescriptor is a "repr(C,packed)" struct from bindgen
173 let mut footer: AvbFooter = unsafe { zeroed() };
174 // SAFETY: safe to read because of seek(-FOOTER_SIZE) above
Andrew Scull9cbf2e02022-04-25 18:12:27 +0000175 let avb_footer_valid = unsafe {
Jooyung Hanc8deb472021-09-13 13:48:25 +0900176 let footer_slice = from_raw_parts_mut(&mut footer as *mut _ as *mut u8, FOOTER_SIZE);
177 image.read_exact(footer_slice)?;
Andrew Scull9cbf2e02022-04-25 18:12:27 +0000178 avb_footer_validate_and_byteswap(&footer, &mut footer)
179 };
180 if !avb_footer_valid {
181 return Err(ApexParseError::InvalidPayloadAvbFooter);
Jooyung Hanc8deb472021-09-13 13:48:25 +0900182 }
183 // Get VbMeta block
184 image.seek(SeekFrom::Start(offset + footer.vbmeta_offset))?;
185 let vbmeta_size = footer.vbmeta_size as usize;
186 let mut data = vec![0u8; vbmeta_size];
187 image.read_exact(&mut data)?;
188 Ok(VbMeta { data })
189 }
190 // Verify VbMeta image. Its enclosed public key should match with a given public key.
Andrew Scull9cbf2e02022-04-25 18:12:27 +0000191 fn verify(&self, outer_public_key: &[u8]) -> Result<(), ApexVerificationError> {
Jooyung Hanc8deb472021-09-13 13:48:25 +0900192 // SAFETY: self.data points to a valid VBMeta data and avb_vbmeta_image_verify should work fine
193 // with it
194 let public_key = unsafe {
195 let mut pk_ptr: *const u8 = null_mut();
196 let mut pk_len: usize = 0;
197 let res = avb_vbmeta_image_verify(
198 self.data.as_ptr(),
199 self.data.len(),
200 &mut pk_ptr,
201 &mut pk_len,
202 );
Andrew Scull9cbf2e02022-04-25 18:12:27 +0000203 if res != AvbVBMetaVerifyResult_AVB_VBMETA_VERIFY_RESULT_OK {
204 return Err(ApexVerificationError::BadPayloadSignature(
205 CStr::from_ptr(avb_vbmeta_verify_result_to_string(res))
206 .to_string_lossy()
207 .into_owned(),
208 ));
209 }
Jooyung Hanc8deb472021-09-13 13:48:25 +0900210 from_raw_parts(pk_ptr, pk_len)
211 };
212
Andrew Scull9cbf2e02022-04-25 18:12:27 +0000213 if public_key != outer_public_key {
214 return Err(ApexVerificationError::BadPayloadKey);
215 }
Jooyung Hanc8deb472021-09-13 13:48:25 +0900216 Ok(())
217 }
218 // Return a slice of AvbDescriptor pointers
Andrew Scull9cbf2e02022-04-25 18:12:27 +0000219 fn descriptors(&self) -> Result<Descriptors, ApexParseError> {
Jooyung Hanc8deb472021-09-13 13:48:25 +0900220 let mut num: usize = 0;
221 // SAFETY: ptr will be freed by Descriptor.
222 Ok(unsafe {
223 let ptr = avb_descriptor_get_all(self.data.as_ptr(), self.data.len(), &mut num);
Andrew Scull9cbf2e02022-04-25 18:12:27 +0000224 if ptr.is_null() {
225 return Err(ApexParseError::NoDescriptors);
226 }
Jooyung Hanc8deb472021-09-13 13:48:25 +0900227 let all = from_raw_parts(ptr, num);
228 Descriptors { ptr, all }
229 })
230 }
231}
232
233struct HashtreeDescriptor {
234 ptr: *const u8,
235 inner: AvbHashtreeDescriptor,
236}
237
238impl HashtreeDescriptor {
Andrew Scull9cbf2e02022-04-25 18:12:27 +0000239 fn from(descriptor: *const AvbDescriptor) -> Result<HashtreeDescriptor, ApexParseError> {
Jooyung Hanc8deb472021-09-13 13:48:25 +0900240 // SAFETY: AvbDescriptor is a "repr(C,packed)" struct from bindgen
241 let mut desc: AvbDescriptor = unsafe { zeroed() };
242 // SAFETY: both points to valid AvbDescriptor pointers
Andrew Scull9cbf2e02022-04-25 18:12:27 +0000243 if !unsafe { avb_descriptor_validate_and_byteswap(descriptor, &mut desc) } {
244 return Err(ApexParseError::InvalidDescriptor);
Jooyung Hanc8deb472021-09-13 13:48:25 +0900245 }
Andrew Scull9cbf2e02022-04-25 18:12:27 +0000246 if desc.tag != AvbDescriptorTag::AVB_DESCRIPTOR_TAG_HASHTREE as u64 {
247 return Err(ApexParseError::DescriptorNotHashtree);
248 }
Jooyung Hanc8deb472021-09-13 13:48:25 +0900249 // SAFETY: AvbHashtreeDescriptor is a "repr(C, packed)" struct from bindgen
250 let mut hashtree_descriptor: AvbHashtreeDescriptor = unsafe { zeroed() };
251 // SAFETY: With tag == AVB_DESCRIPTOR_TAG_HASHTREE, descriptor should point to
252 // a AvbHashtreeDescriptor.
Andrew Scull9cbf2e02022-04-25 18:12:27 +0000253 if !unsafe {
254 avb_hashtree_descriptor_validate_and_byteswap(
Jooyung Hanc8deb472021-09-13 13:48:25 +0900255 descriptor as *const AvbHashtreeDescriptor,
256 &mut hashtree_descriptor,
Andrew Scull9cbf2e02022-04-25 18:12:27 +0000257 )
258 } {
259 return Err(ApexParseError::InvalidHashtreeDescriptor);
Jooyung Hanc8deb472021-09-13 13:48:25 +0900260 }
261 Ok(Self { ptr: descriptor as *const u8, inner: hashtree_descriptor })
262 }
Andrew Scull9cbf2e02022-04-25 18:12:27 +0000263 fn root_digest(&self) -> Vec<u8> {
Jooyung Hanc8deb472021-09-13 13:48:25 +0900264 // SAFETY: digest_ptr should point to a valid buffer of root_digest_len
265 let root_digest = unsafe {
266 let digest_ptr = self.ptr.offset(
267 HASHTREE_DESCRIPTOR_SIZE as isize
268 + self.inner.partition_name_len as isize
269 + self.inner.salt_len as isize,
270 );
271 from_raw_parts(digest_ptr, self.inner.root_digest_len as usize)
272 };
Andrew Scull9cbf2e02022-04-25 18:12:27 +0000273 root_digest.to_owned()
Jooyung Hanc8deb472021-09-13 13:48:25 +0900274 }
275}
276
277// Wraps pointer to a heap-allocated array of AvbDescriptor pointers
278struct Descriptors<'a> {
279 ptr: *mut *const AvbDescriptor,
280 all: &'a [*const AvbDescriptor],
281}
282
283// Wrapped pointer should be freed with avb_free.
284impl Drop for Descriptors<'_> {
285 fn drop(&mut self) {
286 // SAFETY: ptr is allocated by avb_descriptor_get_all
287 unsafe { avb_free(self.ptr as *mut c_void) }
288 }
289}
290
291impl<'a> Deref for Descriptors<'a> {
292 type Target = &'a [*const AvbDescriptor];
293 fn deref(&self) -> &Self::Target {
294 &self.all
295 }
296}
297
298#[cfg(test)]
299mod tests {
300 use super::*;
301 fn to_hex_string(buf: &[u8]) -> String {
302 buf.iter().map(|b| format!("{:02x}", b)).collect()
303 }
304 #[test]
305 fn test_open_apex() {
306 let res = verify("tests/data/test.apex").unwrap();
307 assert_eq!(
308 to_hex_string(&res.root_digest),
309 "fe11ab17da0a3a738b54bdc3a13f6139cbdf91ec32f001f8d4bbbf8938e04e39"
310 );
311 }
312}