blob: 881069648ca7dd0c12e1c28c76a5f2c750e0703d [file] [log] [blame]
Alice Wang28cbcf12022-12-01 07:58:28 +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
Alice Wangf3d96b12022-12-15 13:10:47 +000015//! This module handles the pvmfw payload verification.
Alice Wang28cbcf12022-12-01 07:58:28 +000016
Alice Wang167ab3f2023-01-23 13:39:25 +000017use crate::ops::{Ops, Payload};
Alice Wang8077a862023-01-18 16:06:37 +000018use crate::partition::PartitionName;
David Pursella7c727b2023-08-14 16:24:40 -070019use crate::PvmfwVerifyError;
Alice Wangab0d0202023-05-17 08:07:41 +000020use alloc::vec::Vec;
David Pursellc420c8b2024-01-17 10:52:19 -080021use avb::{
Pierre-Clément Tosi2c63f302024-11-26 13:37:16 +000022 Descriptor, DescriptorError, DescriptorResult, HashDescriptor, PartitionData, SlotVerifyError,
23 SlotVerifyNoDataResult, VbmetaData,
David Pursellc420c8b2024-01-17 10:52:19 -080024};
Pierre-Clément Tosi9fbbaf32024-11-26 14:00:01 +000025use core::str;
Alice Wang28cbcf12022-12-01 07:58:28 +000026
David Pursellb59bcc42023-11-10 16:59:19 -080027// We use this for the rollback_index field if SlotVerifyData has empty rollback_indexes
Shikha Panwara26f16a2023-09-27 09:39:00 +000028const DEFAULT_ROLLBACK_INDEX: u64 = 0;
29
David Pursellc420c8b2024-01-17 10:52:19 -080030/// SHA256 digest type for kernel and initrd.
31pub type Digest = [u8; 32];
32
Alice Wang1f0add02023-01-23 16:22:53 +000033/// Verified data returned when the payload verification succeeds.
Pierre-Clément Tosi81ca0802023-02-14 10:41:38 +000034#[derive(Debug, PartialEq, Eq)]
Pierre-Clément Tosif58f3a32023-02-02 16:24:23 +000035pub struct VerifiedBootData<'a> {
Alice Wang1f0add02023-01-23 16:22:53 +000036 /// DebugLevel of the VM.
37 pub debug_level: DebugLevel,
38 /// Kernel digest.
39 pub kernel_digest: Digest,
40 /// Initrd digest if initrd exists.
41 pub initrd_digest: Option<Digest>,
Pierre-Clément Tosif58f3a32023-02-02 16:24:23 +000042 /// Trusted public key.
43 pub public_key: &'a [u8],
Alice Wangab0d0202023-05-17 08:07:41 +000044 /// VM capabilities.
45 pub capabilities: Vec<Capability>,
Shikha Panwara26f16a2023-09-27 09:39:00 +000046 /// Rollback index of kernel.
47 pub rollback_index: u64,
Pierre-Clément Tosi938b4fb2024-11-26 12:59:47 +000048 /// Page size of kernel, if present.
49 pub page_size: Option<usize>,
Alice Wang1f0add02023-01-23 16:22:53 +000050}
51
Shikha Panwar4a0651d2023-09-28 13:06:13 +000052impl VerifiedBootData<'_> {
53 /// Returns whether the kernel have the given capability
54 pub fn has_capability(&self, cap: Capability) -> bool {
55 self.capabilities.contains(&cap)
56 }
57}
58
Alice Wang5c1a7562023-01-13 17:19:57 +000059/// This enum corresponds to the `DebugLevel` in `VirtualMachineConfig`.
Alice Wang1f0add02023-01-23 16:22:53 +000060#[derive(Clone, Copy, Debug, PartialEq, Eq)]
Alice Wang5c1a7562023-01-13 17:19:57 +000061pub enum DebugLevel {
62 /// Not debuggable at all.
63 None,
64 /// Fully debuggable.
65 Full,
66}
67
Alice Wangab0d0202023-05-17 08:07:41 +000068/// VM Capability.
Shikha Panwar4a0651d2023-09-28 13:06:13 +000069#[derive(Copy, Clone, Debug, PartialEq, Eq)]
Alice Wangab0d0202023-05-17 08:07:41 +000070pub enum Capability {
71 /// Remote attestation.
72 RemoteAttest,
Shikha Panwar4a0651d2023-09-28 13:06:13 +000073 /// Secretkeeper protected secrets.
74 SecretkeeperProtection,
Alice Wangfe0b9762024-11-21 14:47:54 +000075 /// Trusty security VM.
76 TrustySecurityVm,
Nikolina Ilic57ba9c42024-10-01 09:50:48 +000077 /// UEFI support for booting guest kernel.
78 SupportsUefiBoot,
79 /// (internal)
80 #[allow(non_camel_case_types)] // TODO: Use mem::variant_count once stable.
81 _VARIANT_COUNT,
Alice Wangab0d0202023-05-17 08:07:41 +000082}
83
84impl Capability {
David Pursellc420c8b2024-01-17 10:52:19 -080085 const KEY: &'static str = "com.android.virt.cap";
Chris Wailes98f18232023-12-07 12:04:21 -080086 const REMOTE_ATTEST: &'static [u8] = b"remote_attest";
Alice Wangfe0b9762024-11-21 14:47:54 +000087 const TRUSTY_SECURITY_VM: &'static [u8] = b"trusty_security_vm";
Chris Wailes98f18232023-12-07 12:04:21 -080088 const SECRETKEEPER_PROTECTION: &'static [u8] = b"secretkeeper_protection";
Alice Wangab0d0202023-05-17 08:07:41 +000089 const SEPARATOR: u8 = b'|';
Nikolina Ilic57ba9c42024-10-01 09:50:48 +000090 const SUPPORTS_UEFI_BOOT: &'static [u8] = b"supports_uefi_boot";
91 /// Number of supported capabilites.
92 pub const COUNT: usize = Self::_VARIANT_COUNT as usize;
Alice Wangab0d0202023-05-17 08:07:41 +000093
David Pursellc420c8b2024-01-17 10:52:19 -080094 /// Returns the capabilities indicated in `descriptor`, or error if the descriptor has
95 /// unexpected contents.
Pierre-Clément Tosi2c63f302024-11-26 13:37:16 +000096 fn get_capabilities(vbmeta_data: &VbmetaData) -> Result<Vec<Self>, PvmfwVerifyError> {
97 let Some(value) = vbmeta_data.get_property_value(Self::KEY) else {
98 return Ok(Vec::new());
99 };
David Pursellc420c8b2024-01-17 10:52:19 -0800100
Alice Wangab0d0202023-05-17 08:07:41 +0000101 let mut res = Vec::new();
102
Pierre-Clément Tosi2c63f302024-11-26 13:37:16 +0000103 for v in value.split(|b| *b == Self::SEPARATOR) {
Alice Wangab0d0202023-05-17 08:07:41 +0000104 let cap = match v {
105 Self::REMOTE_ATTEST => Self::RemoteAttest,
Alice Wangfe0b9762024-11-21 14:47:54 +0000106 Self::TRUSTY_SECURITY_VM => Self::TrustySecurityVm,
Shikha Panwar4a0651d2023-09-28 13:06:13 +0000107 Self::SECRETKEEPER_PROTECTION => Self::SecretkeeperProtection,
Nikolina Ilic57ba9c42024-10-01 09:50:48 +0000108 Self::SUPPORTS_UEFI_BOOT => Self::SupportsUefiBoot,
David Pursella7c727b2023-08-14 16:24:40 -0700109 _ => return Err(PvmfwVerifyError::UnknownVbmetaProperty),
Alice Wangab0d0202023-05-17 08:07:41 +0000110 };
111 if res.contains(&cap) {
David Pursellb59bcc42023-11-10 16:59:19 -0800112 return Err(SlotVerifyError::InvalidMetadata.into());
Alice Wangab0d0202023-05-17 08:07:41 +0000113 }
114 res.push(cap);
115 }
116 Ok(res)
117 }
118}
119
David Pursellb59bcc42023-11-10 16:59:19 -0800120fn verify_only_one_vbmeta_exists(vbmeta_data: &[VbmetaData]) -> SlotVerifyNoDataResult<()> {
121 if vbmeta_data.len() == 1 {
Alice Wangd3f28ae2023-01-25 10:41:40 +0000122 Ok(())
123 } else {
David Pursellb59bcc42023-11-10 16:59:19 -0800124 Err(SlotVerifyError::InvalidMetadata)
Alice Wangd3f28ae2023-01-25 10:41:40 +0000125 }
126}
127
David Pursellb59bcc42023-11-10 16:59:19 -0800128fn verify_vbmeta_is_from_kernel_partition(vbmeta_image: &VbmetaData) -> SlotVerifyNoDataResult<()> {
129 match vbmeta_image.partition_name().try_into() {
Alice Wang9dfb2962023-01-18 10:01:34 +0000130 Ok(PartitionName::Kernel) => Ok(()),
David Pursellb59bcc42023-11-10 16:59:19 -0800131 _ => Err(SlotVerifyError::InvalidMetadata),
Alice Wang9dfb2962023-01-18 10:01:34 +0000132 }
133}
134
Alice Wang75d05632023-01-25 13:31:18 +0000135fn verify_loaded_partition_has_expected_length(
David Pursellb59bcc42023-11-10 16:59:19 -0800136 loaded_partitions: &[PartitionData],
Alice Wang75d05632023-01-25 13:31:18 +0000137 partition_name: PartitionName,
138 expected_len: usize,
David Pursellb59bcc42023-11-10 16:59:19 -0800139) -> SlotVerifyNoDataResult<()> {
Alice Wang75d05632023-01-25 13:31:18 +0000140 if loaded_partitions.len() != 1 {
141 // Only one partition should be loaded in each verify result.
David Pursellb59bcc42023-11-10 16:59:19 -0800142 return Err(SlotVerifyError::Io);
Alice Wang75d05632023-01-25 13:31:18 +0000143 }
David Pursellb59bcc42023-11-10 16:59:19 -0800144 let loaded_partition = &loaded_partitions[0];
145 if !PartitionName::try_from(loaded_partition.partition_name())
Alice Wang75d05632023-01-25 13:31:18 +0000146 .map_or(false, |p| p == partition_name)
147 {
148 // Only the requested partition should be loaded.
David Pursellb59bcc42023-11-10 16:59:19 -0800149 return Err(SlotVerifyError::Io);
Alice Wang75d05632023-01-25 13:31:18 +0000150 }
David Pursellb59bcc42023-11-10 16:59:19 -0800151 if loaded_partition.data().len() == expected_len {
Alice Wang75d05632023-01-25 13:31:18 +0000152 Ok(())
153 } else {
David Pursellb59bcc42023-11-10 16:59:19 -0800154 Err(SlotVerifyError::Verification(None))
Alice Wang75d05632023-01-25 13:31:18 +0000155 }
156}
157
David Pursellc420c8b2024-01-17 10:52:19 -0800158/// Hash descriptors extracted from a vbmeta image.
159///
160/// We always have a kernel hash descriptor and may have initrd normal or debug descriptors.
161struct HashDescriptors<'a> {
162 kernel: &'a HashDescriptor<'a>,
163 initrd_normal: Option<&'a HashDescriptor<'a>>,
164 initrd_debug: Option<&'a HashDescriptor<'a>>,
165}
166
167impl<'a> HashDescriptors<'a> {
168 /// Extracts the hash descriptors from all vbmeta descriptors. Any unexpected hash descriptor
169 /// is an error.
170 fn get(descriptors: &'a [Descriptor<'a>]) -> DescriptorResult<Self> {
171 let mut kernel = None;
172 let mut initrd_normal = None;
173 let mut initrd_debug = None;
174
175 for descriptor in descriptors.iter().filter_map(|d| match d {
176 Descriptor::Hash(h) => Some(h),
177 _ => None,
178 }) {
179 let target = match descriptor
180 .partition_name
181 .as_bytes()
182 .try_into()
183 .map_err(|_| DescriptorError::InvalidContents)?
184 {
185 PartitionName::Kernel => &mut kernel,
186 PartitionName::InitrdNormal => &mut initrd_normal,
187 PartitionName::InitrdDebug => &mut initrd_debug,
188 };
189
190 if target.is_some() {
191 // Duplicates of the same partition name is an error.
192 return Err(DescriptorError::InvalidContents);
193 }
194 target.replace(descriptor);
195 }
196
197 // Kernel is required, the others are optional.
198 Ok(Self {
199 kernel: kernel.ok_or(DescriptorError::InvalidContents)?,
200 initrd_normal,
201 initrd_debug,
202 })
203 }
204
205 /// Returns an error if either initrd descriptor exists.
206 fn verify_no_initrd(&self) -> Result<(), PvmfwVerifyError> {
207 match self.initrd_normal.or(self.initrd_debug) {
208 Some(_) => Err(SlotVerifyError::InvalidMetadata.into()),
209 None => Ok(()),
210 }
211 }
212}
213
214/// Returns a copy of the SHA256 digest in `descriptor`, or error if the sizes don't match.
215fn copy_digest(descriptor: &HashDescriptor) -> SlotVerifyNoDataResult<Digest> {
216 let mut digest = Digest::default();
217 if descriptor.digest.len() != digest.len() {
218 return Err(SlotVerifyError::InvalidMetadata);
219 }
220 digest.clone_from_slice(descriptor.digest);
221 Ok(digest)
Alice Wangab0d0202023-05-17 08:07:41 +0000222}
223
Pierre-Clément Tosi9fbbaf32024-11-26 14:00:01 +0000224/// Returns the indicated payload page size, if present.
225fn read_page_size(vbmeta_data: &VbmetaData) -> Result<Option<usize>, PvmfwVerifyError> {
226 let Some(property) = vbmeta_data.get_property_value("com.android.virt.page_size") else {
227 return Ok(None);
228 };
229 let size = str::from_utf8(property)
230 .or(Err(PvmfwVerifyError::InvalidPageSize))?
231 .parse::<usize>()
232 .or(Err(PvmfwVerifyError::InvalidPageSize))?
233 .checked_mul(1024)
234 // TODO(stable(unsigned_is_multiple_of)): use .is_multiple_of()
235 .filter(|sz| sz % (4 << 10) == 0 && *sz != 0)
236 .ok_or(PvmfwVerifyError::InvalidPageSize)?;
237
238 Ok(Some(size))
239}
240
David Pursellb59bcc42023-11-10 16:59:19 -0800241/// Verifies the given initrd partition, and checks that the resulting contents looks like expected.
242fn verify_initrd(
243 ops: &mut Ops,
244 partition_name: PartitionName,
245 expected_initrd: &[u8],
246) -> SlotVerifyNoDataResult<()> {
247 let result =
248 ops.verify_partition(partition_name.as_cstr()).map_err(|e| e.without_verify_data())?;
249 verify_loaded_partition_has_expected_length(
250 result.partition_data(),
251 partition_name,
252 expected_initrd.len(),
253 )
254}
255
Alice Wangf3d96b12022-12-15 13:10:47 +0000256/// Verifies the payload (signed kernel + initrd) against the trusted public key.
Pierre-Clément Tosif58f3a32023-02-02 16:24:23 +0000257pub fn verify_payload<'a>(
Alice Wang6b486f12023-01-06 13:12:16 +0000258 kernel: &[u8],
259 initrd: Option<&[u8]>,
Pierre-Clément Tosif58f3a32023-02-02 16:24:23 +0000260 trusted_public_key: &'a [u8],
David Pursella7c727b2023-08-14 16:24:40 -0700261) -> Result<VerifiedBootData<'a>, PvmfwVerifyError> {
David Pursellb59bcc42023-11-10 16:59:19 -0800262 let payload = Payload::new(kernel, initrd, trusted_public_key);
263 let mut ops = Ops::new(&payload);
Alice Wang167ab3f2023-01-23 13:39:25 +0000264 let kernel_verify_result = ops.verify_partition(PartitionName::Kernel.as_cstr())?;
Alice Wang75d05632023-01-25 13:31:18 +0000265
David Pursellb59bcc42023-11-10 16:59:19 -0800266 let vbmeta_images = kernel_verify_result.vbmeta_data();
Shikha Panwara26f16a2023-09-27 09:39:00 +0000267 // TODO(b/302093437): Use explicit rollback_index_location instead of default
268 // location (first element).
269 let rollback_index =
270 *kernel_verify_result.rollback_indexes().first().unwrap_or(&DEFAULT_ROLLBACK_INDEX);
Alice Wangd3f28ae2023-01-25 10:41:40 +0000271 verify_only_one_vbmeta_exists(vbmeta_images)?;
David Pursellb59bcc42023-11-10 16:59:19 -0800272 let vbmeta_image = &vbmeta_images[0];
273 verify_vbmeta_is_from_kernel_partition(vbmeta_image)?;
David Pursellc420c8b2024-01-17 10:52:19 -0800274 let descriptors = vbmeta_image.descriptors()?;
275 let hash_descriptors = HashDescriptors::get(&descriptors)?;
Pierre-Clément Tosi2c63f302024-11-26 13:37:16 +0000276 let capabilities = Capability::get_capabilities(vbmeta_image)?;
Pierre-Clément Tosi9fbbaf32024-11-26 14:00:01 +0000277 let page_size = read_page_size(vbmeta_image)?;
Alice Wangd3f28ae2023-01-25 10:41:40 +0000278
Alice Wang167ab3f2023-01-23 13:39:25 +0000279 if initrd.is_none() {
David Pursellc420c8b2024-01-17 10:52:19 -0800280 hash_descriptors.verify_no_initrd()?;
Alice Wang1f0add02023-01-23 16:22:53 +0000281 return Ok(VerifiedBootData {
282 debug_level: DebugLevel::None,
David Pursellc420c8b2024-01-17 10:52:19 -0800283 kernel_digest: copy_digest(hash_descriptors.kernel)?,
Alice Wang1f0add02023-01-23 16:22:53 +0000284 initrd_digest: None,
Pierre-Clément Tosif58f3a32023-02-02 16:24:23 +0000285 public_key: trusted_public_key,
Alice Wangab0d0202023-05-17 08:07:41 +0000286 capabilities,
Shikha Panwara26f16a2023-09-27 09:39:00 +0000287 rollback_index,
Pierre-Clément Tosi938b4fb2024-11-26 12:59:47 +0000288 page_size,
Alice Wang1f0add02023-01-23 16:22:53 +0000289 });
Alice Wang86383df2023-01-11 10:03:56 +0000290 }
Alice Wang5c1a7562023-01-13 17:19:57 +0000291
Alice Wang75d05632023-01-25 13:31:18 +0000292 let initrd = initrd.unwrap();
David Pursellc420c8b2024-01-17 10:52:19 -0800293 let (debug_level, initrd_descriptor) =
David Pursell09bfc852024-02-02 11:24:24 -0800294 if verify_initrd(&mut ops, PartitionName::InitrdNormal, initrd).is_ok() {
David Pursellc420c8b2024-01-17 10:52:19 -0800295 (DebugLevel::None, hash_descriptors.initrd_normal)
David Pursell09bfc852024-02-02 11:24:24 -0800296 } else if verify_initrd(&mut ops, PartitionName::InitrdDebug, initrd).is_ok() {
David Pursellc420c8b2024-01-17 10:52:19 -0800297 (DebugLevel::Full, hash_descriptors.initrd_debug)
Alice Wang75d05632023-01-25 13:31:18 +0000298 } else {
David Pursellb59bcc42023-11-10 16:59:19 -0800299 return Err(SlotVerifyError::Verification(None).into());
Alice Wang75d05632023-01-25 13:31:18 +0000300 };
David Pursellc420c8b2024-01-17 10:52:19 -0800301 let initrd_descriptor = initrd_descriptor.ok_or(DescriptorError::InvalidContents)?;
Alice Wang1f0add02023-01-23 16:22:53 +0000302 Ok(VerifiedBootData {
303 debug_level,
David Pursellc420c8b2024-01-17 10:52:19 -0800304 kernel_digest: copy_digest(hash_descriptors.kernel)?,
305 initrd_digest: Some(copy_digest(initrd_descriptor)?),
Pierre-Clément Tosif58f3a32023-02-02 16:24:23 +0000306 public_key: trusted_public_key,
Alice Wangab0d0202023-05-17 08:07:41 +0000307 capabilities,
Shikha Panwara26f16a2023-09-27 09:39:00 +0000308 rollback_index,
Pierre-Clément Tosi938b4fb2024-11-26 12:59:47 +0000309 page_size,
Alice Wang1f0add02023-01-23 16:22:53 +0000310 })
Alice Wang28cbcf12022-12-01 07:58:28 +0000311}