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