1 // Copyright 2022, 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 //! A library to verify and parse VBMeta images.
16 
17 mod descriptor;
18 
19 use avb_bindgen::{
20     avb_footer_validate_and_byteswap, avb_vbmeta_image_header_to_host_byte_order,
21     avb_vbmeta_image_verify, AvbAlgorithmType, AvbFooter, AvbVBMetaImageHeader,
22     AvbVBMetaVerifyResult,
23 };
24 use std::fs::File;
25 use std::io::{self, Read, Seek, SeekFrom};
26 use std::mem::{size_of, transmute, MaybeUninit};
27 use std::path::Path;
28 use std::ptr::null_mut;
29 use thiserror::Error;
30 
31 pub use crate::descriptor::{Descriptor, Descriptors};
32 
33 /// Errors from parsing a VBMeta image.
34 #[derive(Debug, Error)]
35 pub enum VbMetaImageParseError {
36     /// There was an IO error.
37     #[error("IO error")]
38     Io(#[from] io::Error),
39     /// The image footer was invalid.
40     #[error("Invalid footer")]
41     InvalidFooter,
42     /// The image header was invalid.
43     #[error("Invalid header")]
44     InvalidHeader,
45     /// The image version is not supported.
46     #[error("Unsupported version")]
47     UnsupportedVersion,
48     /// There was an invalid descriptor in the image.
49     #[error("Invalid descriptor ")]
50     InvalidDescriptor,
51 }
52 
53 /// Errors from verifying a VBMeta image.
54 #[derive(Debug, Error)]
55 pub enum VbMetaImageVerificationError {
56     /// There was an error parsing the VBMeta image.
57     #[error("Cannot parse VBMeta image")]
58     ParseError(#[from] VbMetaImageParseError),
59     /// The VBMeta image hash did not validate.
60     #[error("Hash mismatch")]
61     HashMismatch,
62     /// The VBMeta image signature did not validate.
63     #[error("Signature mismatch")]
64     SignatureMismatch,
65 }
66 
67 /// A VBMeta Image.
68 pub struct VbMetaImage {
69     header: AvbVBMetaImageHeader,
70     data: Box<[u8]>,
71 }
72 
73 impl VbMetaImage {
74     /// Load and verify a VBMeta image from the given path.
verify_path<P: AsRef<Path>>(path: P) -> Result<Self, VbMetaImageVerificationError>75     pub fn verify_path<P: AsRef<Path>>(path: P) -> Result<Self, VbMetaImageVerificationError> {
76         let file = File::open(path).map_err(VbMetaImageParseError::Io)?;
77         let size = file.metadata().map_err(VbMetaImageParseError::Io)?.len();
78         Self::verify_reader_region(file, 0, size)
79     }
80 
81     /// Load and verify a VBMeta image from a region within a reader.
verify_reader_region<R: Read + Seek>( mut image: R, offset: u64, size: u64, ) -> Result<Self, VbMetaImageVerificationError>82     pub fn verify_reader_region<R: Read + Seek>(
83         mut image: R,
84         offset: u64,
85         size: u64,
86     ) -> Result<Self, VbMetaImageVerificationError> {
87         // Check for a footer in the image or assume it's an entire VBMeta image.
88         image.seek(SeekFrom::Start(offset + size)).map_err(VbMetaImageParseError::Io)?;
89         let (vbmeta_offset, vbmeta_size) = match read_avb_footer(&mut image) {
90             Ok(footer) => {
91                 if footer.vbmeta_offset > size || footer.vbmeta_size > size - footer.vbmeta_offset {
92                     return Err(VbMetaImageParseError::InvalidFooter.into());
93                 }
94                 (footer.vbmeta_offset, footer.vbmeta_size)
95             }
96             Err(VbMetaImageParseError::InvalidFooter) => (0, size),
97             Err(e) => {
98                 return Err(e.into());
99             }
100         };
101         image.seek(SeekFrom::Start(offset + vbmeta_offset)).map_err(VbMetaImageParseError::Io)?;
102         // Verify the image before examining it to check the size.
103         let mut data = vec![0u8; vbmeta_size as usize];
104         image.read_exact(&mut data).map_err(VbMetaImageParseError::Io)?;
105         verify_vbmeta_image(&data)?;
106         // SAFETY: the image has been verified so we know there is a valid header at the start.
107         let header = unsafe {
108             let mut header = MaybeUninit::uninit();
109             let src = data.as_ptr() as *const _ as *const AvbVBMetaImageHeader;
110             avb_vbmeta_image_header_to_host_byte_order(src, header.as_mut_ptr());
111             header.assume_init()
112         };
113         // Calculate the true size of the verified image data.
114         let vbmeta_size = (size_of::<AvbVBMetaImageHeader>() as u64)
115             + header.authentication_data_block_size
116             + header.auxiliary_data_block_size;
117         data.truncate(vbmeta_size as usize);
118         Ok(Self { header, data: data.into_boxed_slice() })
119     }
120 
121     /// Get the public key that verified the VBMeta image. If the image was not signed, there
122     /// is no such public key.
public_key(&self) -> Option<&[u8]>123     pub fn public_key(&self) -> Option<&[u8]> {
124         if self.header.algorithm_type == AvbAlgorithmType::AVB_ALGORITHM_TYPE_NONE as u32 {
125             return None;
126         }
127         let begin = size_of::<AvbVBMetaImageHeader>()
128             + self.header.authentication_data_block_size as usize
129             + self.header.public_key_offset as usize;
130         let end = begin + self.header.public_key_size as usize;
131         Some(&self.data[begin..end])
132     }
133 
134     /// Get the hash of the verified data in the VBMeta image from the authentication block. If the
135     /// image was not signed, there might not be a hash and, if there is, it's not known to be
136     /// correct.
hash(&self) -> Option<&[u8]>137     pub fn hash(&self) -> Option<&[u8]> {
138         if self.header.algorithm_type == AvbAlgorithmType::AVB_ALGORITHM_TYPE_NONE as u32 {
139             return None;
140         }
141         let begin = size_of::<AvbVBMetaImageHeader>() + self.header.hash_offset as usize;
142         let end = begin + self.header.hash_size as usize;
143         Some(&self.data[begin..end])
144     }
145 
146     /// Get the descriptors of the VBMeta image.
descriptors(&self) -> Result<Descriptors<'_>, VbMetaImageParseError>147     pub fn descriptors(&self) -> Result<Descriptors<'_>, VbMetaImageParseError> {
148         Descriptors::from_image(&self.data)
149     }
150 
151     /// Returns the rollback_index of the VBMeta image.
rollback_index(&self) -> u64152     pub fn rollback_index(&self) -> u64 {
153         self.header.rollback_index
154     }
155 
156     /// Get the raw VBMeta image.
data(&self) -> &[u8]157     pub fn data(&self) -> &[u8] {
158         &self.data
159     }
160 }
161 
162 /// Verify the data as a VBMeta image, translating errors that arise.
verify_vbmeta_image(data: &[u8]) -> Result<(), VbMetaImageVerificationError>163 fn verify_vbmeta_image(data: &[u8]) -> Result<(), VbMetaImageVerificationError> {
164     // SAFETY: the function only reads from the provided data and the NULL pointers disable the
165     // output arguments.
166     let res = unsafe { avb_vbmeta_image_verify(data.as_ptr(), data.len(), null_mut(), null_mut()) };
167     match res {
168         AvbVBMetaVerifyResult::AVB_VBMETA_VERIFY_RESULT_OK
169         | AvbVBMetaVerifyResult::AVB_VBMETA_VERIFY_RESULT_OK_NOT_SIGNED => Ok(()),
170         AvbVBMetaVerifyResult::AVB_VBMETA_VERIFY_RESULT_INVALID_VBMETA_HEADER => {
171             Err(VbMetaImageParseError::InvalidHeader.into())
172         }
173         AvbVBMetaVerifyResult::AVB_VBMETA_VERIFY_RESULT_UNSUPPORTED_VERSION => {
174             Err(VbMetaImageParseError::UnsupportedVersion.into())
175         }
176         AvbVBMetaVerifyResult::AVB_VBMETA_VERIFY_RESULT_HASH_MISMATCH => {
177             Err(VbMetaImageVerificationError::HashMismatch)
178         }
179         AvbVBMetaVerifyResult::AVB_VBMETA_VERIFY_RESULT_SIGNATURE_MISMATCH => {
180             Err(VbMetaImageVerificationError::SignatureMismatch)
181         }
182     }
183 }
184 
185 /// Read the AVB footer, if present, given a reader that's positioned at the end of the image.
read_avb_footer<R: Read + Seek>(image: &mut R) -> Result<AvbFooter, VbMetaImageParseError>186 fn read_avb_footer<R: Read + Seek>(image: &mut R) -> Result<AvbFooter, VbMetaImageParseError> {
187     image.seek(SeekFrom::Current(-(size_of::<AvbFooter>() as i64)))?;
188     let mut raw_footer = [0u8; size_of::<AvbFooter>()];
189     image.read_exact(&mut raw_footer)?;
190     // SAFETY: the slice is the same size as the struct which only contains simple data types.
191     let mut footer = unsafe { transmute::<[u8; size_of::<AvbFooter>()], AvbFooter>(raw_footer) };
192     // SAFETY: the function updates the struct in-place.
193     if unsafe { avb_footer_validate_and_byteswap(&footer, &mut footer) } {
194         Ok(footer)
195     } else {
196         Err(VbMetaImageParseError::InvalidFooter)
197     }
198 }
199 
200 #[cfg(test)]
201 mod tests {
202     use super::*;
203     use anyhow::{Context, Result};
204     use std::fs::{self, OpenOptions};
205     use std::os::unix::fs::FileExt;
206     use std::process::Command;
207     use tempfile::TempDir;
208 
209     #[test]
unsigned_image_does_not_have_public_key() -> Result<()>210     fn unsigned_image_does_not_have_public_key() -> Result<()> {
211         let test_dir = TempDir::new().unwrap();
212         let test_file = test_dir.path().join("test.img");
213         let mut cmd = Command::new("./avbtool");
214         cmd.args([
215             "make_vbmeta_image",
216             "--output",
217             test_file.to_str().unwrap(),
218             "--algorithm",
219             "NONE",
220         ]);
221         let status = cmd.status().context("make_vbmeta_image")?;
222         assert!(status.success());
223         let vbmeta = VbMetaImage::verify_path(test_file).context("verify_path")?;
224         assert!(vbmeta.public_key().is_none());
225         Ok(())
226     }
227 
signed_image_has_valid_vbmeta(algorithm: &str, key: &str) -> Result<()>228     fn signed_image_has_valid_vbmeta(algorithm: &str, key: &str) -> Result<()> {
229         let test_dir = TempDir::new().unwrap();
230         let test_file = test_dir.path().join("test.img");
231         let mut cmd = Command::new("./avbtool");
232         cmd.args([
233             "make_vbmeta_image",
234             "--output",
235             test_file.to_str().unwrap(),
236             "--algorithm",
237             algorithm,
238             "--key",
239             key,
240         ]);
241         let status = cmd.status().context("make_vbmeta_image")?;
242         assert!(status.success());
243         let vbmeta = VbMetaImage::verify_path(&test_file).context("verify_path")?;
244 
245         // The image should contain the public part of the key pair.
246         let pubkey = vbmeta.public_key().unwrap();
247         let test_pubkey_file = test_dir.path().join("test.pubkey");
248         let mut cmd = Command::new("./avbtool");
249         cmd.args([
250             "extract_public_key",
251             "--key",
252             key,
253             "--output",
254             test_pubkey_file.to_str().unwrap(),
255         ]);
256         let status = cmd.status().context("extract_public_key")?;
257         assert!(status.success());
258         assert_eq!(pubkey, fs::read(test_pubkey_file).context("read public key")?);
259 
260         // Flip a byte to make verification fail.
261         let file = OpenOptions::new()
262             .read(true)
263             .write(true)
264             .open(&test_file)
265             .context("open image to flip byte")?;
266         let mut data = [0; 1];
267         file.read_exact_at(&mut data, 81).context("read byte from image to flip")?;
268         data[0] = !data[0];
269         file.write_all_at(&data, 81).context("write flipped byte to image")?;
270         assert!(matches!(
271             VbMetaImage::verify_path(test_file),
272             Err(VbMetaImageVerificationError::HashMismatch)
273         ));
274         Ok(())
275     }
276 
277     #[test]
test_rsa2048_signed_image() -> Result<()>278     fn test_rsa2048_signed_image() -> Result<()> {
279         signed_image_has_valid_vbmeta("SHA256_RSA2048", "data/testkey_rsa2048.pem")
280     }
281 
282     #[test]
test_rsa4096_signed_image() -> Result<()>283     fn test_rsa4096_signed_image() -> Result<()> {
284         signed_image_has_valid_vbmeta("SHA256_RSA4096", "data/testkey_rsa4096.pem")
285     }
286 
287     #[test]
test_rsa8192_signed_image() -> Result<()>288     fn test_rsa8192_signed_image() -> Result<()> {
289         signed_image_has_valid_vbmeta("SHA256_RSA8192", "data/testkey_rsa8192.pem")
290     }
291 
292     #[test]
test_rollback_index() -> Result<()>293     fn test_rollback_index() -> Result<()> {
294         let vbmeta = VbMetaImage::verify_path("test_microdroid_vendor_image.img")?;
295         assert_eq!(5, vbmeta.rollback_index());
296         Ok(())
297     }
298 
299     #[test]
test_rollback_index_default_zero() -> Result<()>300     fn test_rollback_index_default_zero() -> Result<()> {
301         let vbmeta =
302             VbMetaImage::verify_path("test_microdroid_vendor_image_no_rollback_index.img")?;
303         assert_eq!(0, vbmeta.rollback_index());
304         Ok(())
305     }
306 }
307