blob: a15f699b2d9e47b9bf8cfb6987f55ffbd542e9bf [file] [log] [blame]
Andrew Scull38127252022-06-13 13:11:00 +00001// 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
17mod descriptor;
18
19use avb_bindgen::{
20 avb_footer_validate_and_byteswap, avb_vbmeta_image_header_to_host_byte_order,
Alice Wangdc63fe02022-12-15 08:49:57 +000021 avb_vbmeta_image_verify, AvbAlgorithmType, AvbFooter, AvbVBMetaImageHeader,
22 AvbVBMetaVerifyResult,
Andrew Scull38127252022-06-13 13:11:00 +000023};
24use std::fs::File;
25use std::io::{self, Read, Seek, SeekFrom};
Alice Wang76ad4ca2022-12-13 12:09:58 +000026use std::mem::{size_of, transmute, MaybeUninit};
Andrew Scull38127252022-06-13 13:11:00 +000027use std::path::Path;
28use std::ptr::null_mut;
Andrew Scull38127252022-06-13 13:11:00 +000029use thiserror::Error;
30
31pub use crate::descriptor::{Descriptor, Descriptors};
32
33/// Errors from parsing a VBMeta image.
34#[derive(Debug, Error)]
35pub 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)]
55pub 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,
Andrew Scull38127252022-06-13 13:11:00 +000065}
66
67/// A VBMeta Image.
68pub struct VbMetaImage {
69 header: AvbVBMetaImageHeader,
70 data: Box<[u8]>,
71}
72
73impl VbMetaImage {
74 /// Load and verify a VBMeta image from the given path.
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.
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)?;
Alice Wang285c7582022-12-13 13:44:52 +000089 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)
Andrew Scull38127252022-06-13 13:11:00 +000095 }
Alice Wang285c7582022-12-13 13:44:52 +000096 Err(VbMetaImageParseError::InvalidFooter) => (0, size),
97 Err(e) => {
98 return Err(e.into());
99 }
Andrew Scull38127252022-06-13 13:11:00 +0000100 };
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.
123 pub fn public_key(&self) -> Option<&[u8]> {
Alice Wangdc63fe02022-12-15 08:49:57 +0000124 if self.header.algorithm_type == AvbAlgorithmType::AVB_ALGORITHM_TYPE_NONE as u32 {
Andrew Scull38127252022-06-13 13:11:00 +0000125 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
Alice Wang3356d6d2022-07-18 08:32:10 +0000134 /// 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.
137 pub fn hash(&self) -> Option<&[u8]> {
Alice Wangdc63fe02022-12-15 08:49:57 +0000138 if self.header.algorithm_type == AvbAlgorithmType::AVB_ALGORITHM_TYPE_NONE as u32 {
Alice Wang3356d6d2022-07-18 08:32:10 +0000139 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
Andrew Scull38127252022-06-13 13:11:00 +0000146 /// Get the descriptors of the VBMeta image.
147 pub fn descriptors(&self) -> Result<Descriptors<'_>, VbMetaImageParseError> {
148 Descriptors::from_image(&self.data)
149 }
150
Nikita Ioffeea99b3d2024-03-27 22:20:19 +0000151 /// Returns the rollback_index of the VBMeta image.
152 pub fn rollback_index(&self) -> u64 {
153 self.header.rollback_index
154 }
155
Andrew Scull38127252022-06-13 13:11:00 +0000156 /// Get the raw VBMeta image.
157 pub fn data(&self) -> &[u8] {
158 &self.data
159 }
160}
161
162/// Verify the data as a VBMeta image, translating errors that arise.
163fn 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()) };
Andrew Scull38127252022-06-13 13:11:00 +0000167 match res {
Alice Wangdc63fe02022-12-15 08:49:57 +0000168 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 => {
Andrew Scull38127252022-06-13 13:11:00 +0000171 Err(VbMetaImageParseError::InvalidHeader.into())
172 }
Alice Wangdc63fe02022-12-15 08:49:57 +0000173 AvbVBMetaVerifyResult::AVB_VBMETA_VERIFY_RESULT_UNSUPPORTED_VERSION => {
Andrew Scull38127252022-06-13 13:11:00 +0000174 Err(VbMetaImageParseError::UnsupportedVersion.into())
175 }
Alice Wangdc63fe02022-12-15 08:49:57 +0000176 AvbVBMetaVerifyResult::AVB_VBMETA_VERIFY_RESULT_HASH_MISMATCH => {
Andrew Scull38127252022-06-13 13:11:00 +0000177 Err(VbMetaImageVerificationError::HashMismatch)
178 }
Alice Wangdc63fe02022-12-15 08:49:57 +0000179 AvbVBMetaVerifyResult::AVB_VBMETA_VERIFY_RESULT_SIGNATURE_MISMATCH => {
Andrew Scull38127252022-06-13 13:11:00 +0000180 Err(VbMetaImageVerificationError::SignatureMismatch)
181 }
Andrew Scull38127252022-06-13 13:11:00 +0000182 }
183}
184
185/// Read the AVB footer, if present, given a reader that's positioned at the end of the image.
Alice Wang285c7582022-12-13 13:44:52 +0000186fn read_avb_footer<R: Read + Seek>(image: &mut R) -> Result<AvbFooter, VbMetaImageParseError> {
Andrew Scull38127252022-06-13 13:11:00 +0000187 image.seek(SeekFrom::Current(-(size_of::<AvbFooter>() as i64)))?;
Alice Wang76ad4ca2022-12-13 12:09:58 +0000188 let mut raw_footer = [0u8; size_of::<AvbFooter>()];
189 image.read_exact(&mut raw_footer)?;
Andrew Scull38127252022-06-13 13:11:00 +0000190 // SAFETY: the slice is the same size as the struct which only contains simple data types.
Alice Wang76ad4ca2022-12-13 12:09:58 +0000191 let mut footer = unsafe { transmute::<[u8; size_of::<AvbFooter>()], AvbFooter>(raw_footer) };
Andrew Scull38127252022-06-13 13:11:00 +0000192 // SAFETY: the function updates the struct in-place.
193 if unsafe { avb_footer_validate_and_byteswap(&footer, &mut footer) } {
Alice Wang285c7582022-12-13 13:44:52 +0000194 Ok(footer)
Andrew Scull38127252022-06-13 13:11:00 +0000195 } else {
Alice Wang285c7582022-12-13 13:44:52 +0000196 Err(VbMetaImageParseError::InvalidFooter)
Andrew Scull38127252022-06-13 13:11:00 +0000197 }
198}
199
200#[cfg(test)]
201mod 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]
Alice Wangc3483872022-12-13 12:05:40 +0000210 fn unsigned_image_does_not_have_public_key() -> Result<()> {
Andrew Scull38127252022-06-13 13:11:00 +0000211 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
Alice Wanga3cc9a02022-11-29 09:48:16 +0000228 fn signed_image_has_valid_vbmeta(algorithm: &str, key: &str) -> Result<()> {
Andrew Scull38127252022-06-13 13:11:00 +0000229 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]
278 fn test_rsa2048_signed_image() -> Result<()> {
Alice Wanga3cc9a02022-11-29 09:48:16 +0000279 signed_image_has_valid_vbmeta("SHA256_RSA2048", "data/testkey_rsa2048.pem")
Andrew Scull38127252022-06-13 13:11:00 +0000280 }
281
282 #[test]
283 fn test_rsa4096_signed_image() -> Result<()> {
Alice Wanga3cc9a02022-11-29 09:48:16 +0000284 signed_image_has_valid_vbmeta("SHA256_RSA4096", "data/testkey_rsa4096.pem")
Andrew Scull38127252022-06-13 13:11:00 +0000285 }
286
287 #[test]
288 fn test_rsa8192_signed_image() -> Result<()> {
Alice Wanga3cc9a02022-11-29 09:48:16 +0000289 signed_image_has_valid_vbmeta("SHA256_RSA8192", "data/testkey_rsa8192.pem")
Andrew Scull38127252022-06-13 13:11:00 +0000290 }
Nikita Ioffeea99b3d2024-03-27 22:20:19 +0000291
292 #[test]
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]
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 }
Andrew Scull38127252022-06-13 13:11:00 +0000306}