• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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     /// Get the raw VBMeta image.
data(&self) -> &[u8]152     pub fn data(&self) -> &[u8] {
153         &self.data
154     }
155 }
156 
157 /// Verify the data as a VBMeta image, translating errors that arise.
verify_vbmeta_image(data: &[u8]) -> Result<(), VbMetaImageVerificationError>158 fn verify_vbmeta_image(data: &[u8]) -> Result<(), VbMetaImageVerificationError> {
159     // SAFETY: the function only reads from the provided data and the NULL pointers disable the
160     // output arguments.
161     let res = unsafe { avb_vbmeta_image_verify(data.as_ptr(), data.len(), null_mut(), null_mut()) };
162     match res {
163         AvbVBMetaVerifyResult::AVB_VBMETA_VERIFY_RESULT_OK
164         | AvbVBMetaVerifyResult::AVB_VBMETA_VERIFY_RESULT_OK_NOT_SIGNED => Ok(()),
165         AvbVBMetaVerifyResult::AVB_VBMETA_VERIFY_RESULT_INVALID_VBMETA_HEADER => {
166             Err(VbMetaImageParseError::InvalidHeader.into())
167         }
168         AvbVBMetaVerifyResult::AVB_VBMETA_VERIFY_RESULT_UNSUPPORTED_VERSION => {
169             Err(VbMetaImageParseError::UnsupportedVersion.into())
170         }
171         AvbVBMetaVerifyResult::AVB_VBMETA_VERIFY_RESULT_HASH_MISMATCH => {
172             Err(VbMetaImageVerificationError::HashMismatch)
173         }
174         AvbVBMetaVerifyResult::AVB_VBMETA_VERIFY_RESULT_SIGNATURE_MISMATCH => {
175             Err(VbMetaImageVerificationError::SignatureMismatch)
176         }
177     }
178 }
179 
180 /// 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>181 fn read_avb_footer<R: Read + Seek>(image: &mut R) -> Result<AvbFooter, VbMetaImageParseError> {
182     image.seek(SeekFrom::Current(-(size_of::<AvbFooter>() as i64)))?;
183     let mut raw_footer = [0u8; size_of::<AvbFooter>()];
184     image.read_exact(&mut raw_footer)?;
185     // SAFETY: the slice is the same size as the struct which only contains simple data types.
186     let mut footer = unsafe { transmute::<[u8; size_of::<AvbFooter>()], AvbFooter>(raw_footer) };
187     // SAFETY: the function updates the struct in-place.
188     if unsafe { avb_footer_validate_and_byteswap(&footer, &mut footer) } {
189         Ok(footer)
190     } else {
191         Err(VbMetaImageParseError::InvalidFooter)
192     }
193 }
194 
195 #[cfg(test)]
196 mod tests {
197     use super::*;
198     use anyhow::{Context, Result};
199     use std::fs::{self, OpenOptions};
200     use std::os::unix::fs::FileExt;
201     use std::process::Command;
202     use tempfile::TempDir;
203 
204     #[test]
unsigned_image_does_not_have_public_key() -> Result<()>205     fn unsigned_image_does_not_have_public_key() -> Result<()> {
206         let test_dir = TempDir::new().unwrap();
207         let test_file = test_dir.path().join("test.img");
208         let mut cmd = Command::new("./avbtool");
209         cmd.args([
210             "make_vbmeta_image",
211             "--output",
212             test_file.to_str().unwrap(),
213             "--algorithm",
214             "NONE",
215         ]);
216         let status = cmd.status().context("make_vbmeta_image")?;
217         assert!(status.success());
218         let vbmeta = VbMetaImage::verify_path(test_file).context("verify_path")?;
219         assert!(vbmeta.public_key().is_none());
220         Ok(())
221     }
222 
signed_image_has_valid_vbmeta(algorithm: &str, key: &str) -> Result<()>223     fn signed_image_has_valid_vbmeta(algorithm: &str, key: &str) -> Result<()> {
224         let test_dir = TempDir::new().unwrap();
225         let test_file = test_dir.path().join("test.img");
226         let mut cmd = Command::new("./avbtool");
227         cmd.args([
228             "make_vbmeta_image",
229             "--output",
230             test_file.to_str().unwrap(),
231             "--algorithm",
232             algorithm,
233             "--key",
234             key,
235         ]);
236         let status = cmd.status().context("make_vbmeta_image")?;
237         assert!(status.success());
238         let vbmeta = VbMetaImage::verify_path(&test_file).context("verify_path")?;
239 
240         // The image should contain the public part of the key pair.
241         let pubkey = vbmeta.public_key().unwrap();
242         let test_pubkey_file = test_dir.path().join("test.pubkey");
243         let mut cmd = Command::new("./avbtool");
244         cmd.args([
245             "extract_public_key",
246             "--key",
247             key,
248             "--output",
249             test_pubkey_file.to_str().unwrap(),
250         ]);
251         let status = cmd.status().context("extract_public_key")?;
252         assert!(status.success());
253         assert_eq!(pubkey, fs::read(test_pubkey_file).context("read public key")?);
254 
255         // Flip a byte to make verification fail.
256         let file = OpenOptions::new()
257             .read(true)
258             .write(true)
259             .open(&test_file)
260             .context("open image to flip byte")?;
261         let mut data = [0; 1];
262         file.read_exact_at(&mut data, 81).context("read byte from image to flip")?;
263         data[0] = !data[0];
264         file.write_all_at(&data, 81).context("write flipped byte to image")?;
265         assert!(matches!(
266             VbMetaImage::verify_path(test_file),
267             Err(VbMetaImageVerificationError::HashMismatch)
268         ));
269         Ok(())
270     }
271 
272     #[test]
test_rsa2048_signed_image() -> Result<()>273     fn test_rsa2048_signed_image() -> Result<()> {
274         signed_image_has_valid_vbmeta("SHA256_RSA2048", "data/testkey_rsa2048.pem")
275     }
276 
277     #[test]
test_rsa4096_signed_image() -> Result<()>278     fn test_rsa4096_signed_image() -> Result<()> {
279         signed_image_has_valid_vbmeta("SHA256_RSA4096", "data/testkey_rsa4096.pem")
280     }
281 
282     #[test]
test_rsa8192_signed_image() -> Result<()>283     fn test_rsa8192_signed_image() -> Result<()> {
284         signed_image_has_valid_vbmeta("SHA256_RSA8192", "data/testkey_rsa8192.pem")
285     }
286 }
287