blob: 63b09dee94f0159a126440e5d75a64662bf4639b [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 std::fs::File;
Andrew Scull38127252022-06-13 13:11:00 +000018use std::io::{self, Read};
Andrew Scull9cbf2e02022-04-25 18:12:27 +000019use thiserror::Error;
Andrew Scull38127252022-06-13 13:11:00 +000020use vbmeta::VbMetaImage;
Andrew Scull9cbf2e02022-04-25 18:12:27 +000021use zip::result::ZipError;
Jooyung Hanc8deb472021-09-13 13:48:25 +090022use zip::ZipArchive;
23
24const APEX_PUBKEY_ENTRY: &str = "apex_pubkey";
25const APEX_PAYLOAD_ENTRY: &str = "apex_payload.img";
26
Andrew Scull9cbf2e02022-04-25 18:12:27 +000027/// Errors from parsing an APEX.
28#[derive(Debug, Error)]
29pub enum ApexParseError {
30 /// There was an IO error.
31 #[error("IO error")]
32 Io(#[from] io::Error),
33 /// The Zip archive was invalid.
34 #[error("Cannot read zip archive")]
35 InvalidZip(&'static str),
36 /// The apex_pubkey file was missing from the APEX.
37 #[error("APEX doesn't contain apex_pubkey")]
38 PubkeyMissing,
39 /// The apex_payload.img file was missing from the APEX.
40 #[error("APEX doesn't contain apex_payload.img")]
41 PayloadMissing,
Andrew Scull38127252022-06-13 13:11:00 +000042 /// There was no hashtree descriptor in the APEX payload's VBMeta image.
43 #[error("Non-hashtree descriptor found in payload's VBMeta image")]
Andrew Scull9cbf2e02022-04-25 18:12:27 +000044 DescriptorNotHashtree,
Andrew Scull38127252022-06-13 13:11:00 +000045 /// There was an error parsing the APEX payload's VBMeta image.
46 #[error("Could not parse payload's VBMeta image")]
47 PayloadVbmetaError(#[from] vbmeta::VbMetaImageParseError),
Andrew Scull9cbf2e02022-04-25 18:12:27 +000048}
49
50/// Errors from verifying an APEX.
51#[derive(Debug, Error)]
52pub enum ApexVerificationError {
53 /// There was an error parsing the APEX.
54 #[error("Cannot parse APEX file")]
55 ParseError(#[from] ApexParseError),
Andrew Scull38127252022-06-13 13:11:00 +000056 /// There was an error validating the APEX payload's VBMeta image.
57 #[error("Could not parse payload's VBMeta image")]
58 PayloadVbmetaError(#[from] vbmeta::VbMetaImageVerificationError),
59 /// The APEX payload was not verified with the apex_pubkey.
60 #[error("APEX pubkey mismatch")]
61 ApexPubkeyMistmatch,
Andrew Scull9cbf2e02022-04-25 18:12:27 +000062}
63
Jooyung Hanc8deb472021-09-13 13:48:25 +090064/// Verification result holds public key and root digest of apex_payload.img
65pub struct ApexVerificationResult {
Andrew Scull9cbf2e02022-04-25 18:12:27 +000066 /// The public key that verifies the payload signature.
Jooyung Hanc8deb472021-09-13 13:48:25 +090067 pub public_key: Vec<u8>,
Andrew Scull9cbf2e02022-04-25 18:12:27 +000068 /// The root digest of the payload hashtree.
Jooyung Hanc8deb472021-09-13 13:48:25 +090069 pub root_digest: Vec<u8>,
70}
71
72/// Verify APEX payload by AVB verification and return public key and root digest
Andrew Scull9cbf2e02022-04-25 18:12:27 +000073pub fn verify(path: &str) -> Result<ApexVerificationResult, ApexVerificationError> {
74 let apex_file = File::open(path).map_err(ApexParseError::Io)?;
Jooyung Hanc8deb472021-09-13 13:48:25 +090075 let (public_key, image_offset, image_size) = get_public_key_and_image_info(&apex_file)?;
Andrew Scull38127252022-06-13 13:11:00 +000076 let vbmeta = VbMetaImage::verify_reader_region(apex_file, image_offset, image_size)?;
77 let root_digest = find_root_digest(&vbmeta)?;
78 match vbmeta.public_key() {
79 Some(payload_public_key) if public_key == payload_public_key => {
80 Ok(ApexVerificationResult { public_key, root_digest })
81 }
82 _ => Err(ApexVerificationError::ApexPubkeyMistmatch),
83 }
84}
85
86fn find_root_digest(vbmeta: &VbMetaImage) -> Result<Vec<u8>, ApexParseError> {
87 // APEXs use the root digest from the first hashtree descriptor to describe the payload.
88 for descriptor in vbmeta.descriptors()?.iter() {
89 if let vbmeta::Descriptor::Hashtree(_) = descriptor {
90 return Ok(descriptor.to_hashtree()?.root_digest().to_vec());
91 }
92 }
93 Err(ApexParseError::DescriptorNotHashtree)
Jooyung Hanc8deb472021-09-13 13:48:25 +090094}
95
Andrew Scull9cbf2e02022-04-25 18:12:27 +000096fn get_public_key_and_image_info(apex_file: &File) -> Result<(Vec<u8>, u64, u64), ApexParseError> {
97 let mut z = ZipArchive::new(apex_file).map_err(|err| match err {
98 ZipError::Io(err) => ApexParseError::Io(err),
99 ZipError::InvalidArchive(s) | ZipError::UnsupportedArchive(s) => {
100 ApexParseError::InvalidZip(s)
101 }
102 ZipError::FileNotFound => unreachable!(),
103 })?;
Jooyung Hanc8deb472021-09-13 13:48:25 +0900104
105 let mut public_key = Vec::new();
Andrew Scull9cbf2e02022-04-25 18:12:27 +0000106 z.by_name(APEX_PUBKEY_ENTRY)
107 .map_err(|err| match err {
108 ZipError::Io(err) => ApexParseError::Io(err),
109 ZipError::FileNotFound => ApexParseError::PubkeyMissing,
110 ZipError::InvalidArchive(s) | ZipError::UnsupportedArchive(s) => {
111 ApexParseError::InvalidZip(s)
112 }
113 })?
114 .read_to_end(&mut public_key)?;
Jooyung Hanc8deb472021-09-13 13:48:25 +0900115
Andrew Scull9cbf2e02022-04-25 18:12:27 +0000116 let (image_offset, image_size) = z
117 .by_name(APEX_PAYLOAD_ENTRY)
118 .map(|f| (f.data_start(), f.size()))
119 .map_err(|err| match err {
120 ZipError::Io(err) => ApexParseError::Io(err),
121 ZipError::FileNotFound => ApexParseError::PayloadMissing,
122 ZipError::InvalidArchive(s) | ZipError::UnsupportedArchive(s) => {
123 ApexParseError::InvalidZip(s)
124 }
125 })?;
Jooyung Hanc8deb472021-09-13 13:48:25 +0900126
127 Ok((public_key, image_offset, image_size))
128}
129
Jooyung Hanc8deb472021-09-13 13:48:25 +0900130#[cfg(test)]
131mod tests {
132 use super::*;
133 fn to_hex_string(buf: &[u8]) -> String {
134 buf.iter().map(|b| format!("{:02x}", b)).collect()
135 }
136 #[test]
137 fn test_open_apex() {
138 let res = verify("tests/data/test.apex").unwrap();
139 assert_eq!(
140 to_hex_string(&res.root_digest),
141 "fe11ab17da0a3a738b54bdc3a13f6139cbdf91ec32f001f8d4bbbf8938e04e39"
142 );
143 }
144}