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