• 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 //! This module handles the pvmfw payload verification.
16 
17 use crate::ops::{Ops, Payload};
18 use crate::partition::PartitionName;
19 use crate::PvmfwVerifyError;
20 use alloc::{string::String, vec::Vec};
21 use avb::{
22     Descriptor, DescriptorError, DescriptorResult, HashDescriptor, PartitionData, SlotVerifyError,
23     SlotVerifyNoDataResult, VbmetaData,
24 };
25 use core::str;
26 
27 // We use this for the rollback_index field if SlotVerifyData has empty rollback_indexes
28 const DEFAULT_ROLLBACK_INDEX: u64 = 0;
29 
30 /// SHA256 digest type for kernel and initrd.
31 pub type Digest = [u8; 32];
32 
33 /// Verified data returned when the payload verification succeeds.
34 #[derive(Debug, PartialEq, Eq)]
35 pub struct VerifiedBootData<'a> {
36     /// 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>,
42     /// Trusted public key.
43     pub public_key: &'a [u8],
44     /// VM capabilities.
45     pub capabilities: Vec<Capability>,
46     /// Rollback index of kernel.
47     pub rollback_index: u64,
48     /// Page size of kernel, if present.
49     pub page_size: Option<usize>,
50     /// Name of the guest payload, if present.
51     pub name: Option<String>,
52 }
53 
54 impl VerifiedBootData<'_> {
55     /// Returns whether the kernel have the given capability
has_capability(&self, cap: Capability) -> bool56     pub fn has_capability(&self, cap: Capability) -> bool {
57         self.capabilities.contains(&cap)
58     }
59 }
60 
61 /// This enum corresponds to the `DebugLevel` in `VirtualMachineConfig`.
62 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
63 pub enum DebugLevel {
64     /// Not debuggable at all.
65     None,
66     /// Fully debuggable.
67     Full,
68 }
69 
70 /// VM Capability.
71 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
72 pub enum Capability {
73     /// Remote attestation.
74     RemoteAttest,
75     /// Secretkeeper protected secrets.
76     SecretkeeperProtection,
77     /// Trusty security VM.
78     TrustySecurityVm,
79     /// UEFI support for booting guest kernel.
80     SupportsUefiBoot,
81     /// (internal)
82     #[allow(non_camel_case_types)] // TODO: Use mem::variant_count once stable.
83     _VARIANT_COUNT,
84 }
85 
86 impl Capability {
87     const KEY: &'static str = "com.android.virt.cap";
88     const REMOTE_ATTEST: &'static [u8] = b"remote_attest";
89     const TRUSTY_SECURITY_VM: &'static [u8] = b"trusty_security_vm";
90     const SECRETKEEPER_PROTECTION: &'static [u8] = b"secretkeeper_protection";
91     const SEPARATOR: u8 = b'|';
92     const SUPPORTS_UEFI_BOOT: &'static [u8] = b"supports_uefi_boot";
93     /// Number of supported capabilites.
94     pub const COUNT: usize = Self::_VARIANT_COUNT as usize;
95 
96     /// Returns the capabilities indicated in `descriptor`, or error if the descriptor has
97     /// unexpected contents.
get_capabilities(vbmeta_data: &VbmetaData) -> Result<Vec<Self>, PvmfwVerifyError>98     fn get_capabilities(vbmeta_data: &VbmetaData) -> Result<Vec<Self>, PvmfwVerifyError> {
99         let Some(value) = vbmeta_data.get_property_value(Self::KEY) else {
100             return Ok(Vec::new());
101         };
102 
103         let mut res = Vec::new();
104 
105         for v in value.split(|b| *b == Self::SEPARATOR) {
106             let cap = match v {
107                 Self::REMOTE_ATTEST => Self::RemoteAttest,
108                 Self::TRUSTY_SECURITY_VM => Self::TrustySecurityVm,
109                 Self::SECRETKEEPER_PROTECTION => Self::SecretkeeperProtection,
110                 Self::SUPPORTS_UEFI_BOOT => Self::SupportsUefiBoot,
111                 _ => return Err(PvmfwVerifyError::UnknownVbmetaProperty),
112             };
113             if res.contains(&cap) {
114                 return Err(SlotVerifyError::InvalidMetadata.into());
115             }
116             res.push(cap);
117         }
118         Ok(res)
119     }
120 }
121 
verify_only_one_vbmeta_exists(vbmeta_data: &[VbmetaData]) -> SlotVerifyNoDataResult<()>122 fn verify_only_one_vbmeta_exists(vbmeta_data: &[VbmetaData]) -> SlotVerifyNoDataResult<()> {
123     if vbmeta_data.len() == 1 {
124         Ok(())
125     } else {
126         Err(SlotVerifyError::InvalidMetadata)
127     }
128 }
129 
verify_vbmeta_is_from_kernel_partition(vbmeta_image: &VbmetaData) -> SlotVerifyNoDataResult<()>130 fn verify_vbmeta_is_from_kernel_partition(vbmeta_image: &VbmetaData) -> SlotVerifyNoDataResult<()> {
131     match vbmeta_image.partition_name().try_into() {
132         Ok(PartitionName::Kernel) => Ok(()),
133         _ => Err(SlotVerifyError::InvalidMetadata),
134     }
135 }
136 
verify_loaded_partition_has_expected_length( loaded_partitions: &[PartitionData], partition_name: PartitionName, expected_len: usize, ) -> SlotVerifyNoDataResult<()>137 fn verify_loaded_partition_has_expected_length(
138     loaded_partitions: &[PartitionData],
139     partition_name: PartitionName,
140     expected_len: usize,
141 ) -> SlotVerifyNoDataResult<()> {
142     if loaded_partitions.len() != 1 {
143         // Only one partition should be loaded in each verify result.
144         return Err(SlotVerifyError::Io);
145     }
146     let loaded_partition = &loaded_partitions[0];
147     if !PartitionName::try_from(loaded_partition.partition_name())
148         .map_or(false, |p| p == partition_name)
149     {
150         // Only the requested partition should be loaded.
151         return Err(SlotVerifyError::Io);
152     }
153     if loaded_partition.data().len() == expected_len {
154         Ok(())
155     } else {
156         Err(SlotVerifyError::Verification(None))
157     }
158 }
159 
160 /// Hash descriptors extracted from a vbmeta image.
161 ///
162 /// We always have a kernel hash descriptor and may have initrd normal or debug descriptors.
163 struct HashDescriptors<'a> {
164     kernel: &'a HashDescriptor<'a>,
165     initrd_normal: Option<&'a HashDescriptor<'a>>,
166     initrd_debug: Option<&'a HashDescriptor<'a>>,
167 }
168 
169 impl<'a> HashDescriptors<'a> {
170     /// Extracts the hash descriptors from all vbmeta descriptors. Any unexpected hash descriptor
171     /// is an error.
get(descriptors: &'a [Descriptor<'a>]) -> DescriptorResult<Self>172     fn get(descriptors: &'a [Descriptor<'a>]) -> DescriptorResult<Self> {
173         let mut kernel = None;
174         let mut initrd_normal = None;
175         let mut initrd_debug = None;
176 
177         for descriptor in descriptors.iter().filter_map(|d| match d {
178             Descriptor::Hash(h) => Some(h),
179             _ => None,
180         }) {
181             let target = match descriptor
182                 .partition_name
183                 .as_bytes()
184                 .try_into()
185                 .map_err(|_| DescriptorError::InvalidContents)?
186             {
187                 PartitionName::Kernel => &mut kernel,
188                 PartitionName::InitrdNormal => &mut initrd_normal,
189                 PartitionName::InitrdDebug => &mut initrd_debug,
190             };
191 
192             if target.is_some() {
193                 // Duplicates of the same partition name is an error.
194                 return Err(DescriptorError::InvalidContents);
195             }
196             target.replace(descriptor);
197         }
198 
199         // Kernel is required, the others are optional.
200         Ok(Self {
201             kernel: kernel.ok_or(DescriptorError::InvalidContents)?,
202             initrd_normal,
203             initrd_debug,
204         })
205     }
206 
207     /// Returns an error if either initrd descriptor exists.
verify_no_initrd(&self) -> Result<(), PvmfwVerifyError>208     fn verify_no_initrd(&self) -> Result<(), PvmfwVerifyError> {
209         match self.initrd_normal.or(self.initrd_debug) {
210             Some(_) => Err(SlotVerifyError::InvalidMetadata.into()),
211             None => Ok(()),
212         }
213     }
214 }
215 
216 /// Returns a copy of the SHA256 digest in `descriptor`, or error if the sizes don't match.
copy_digest(descriptor: &HashDescriptor) -> SlotVerifyNoDataResult<Digest>217 fn copy_digest(descriptor: &HashDescriptor) -> SlotVerifyNoDataResult<Digest> {
218     let mut digest = Digest::default();
219     if descriptor.digest.len() != digest.len() {
220         return Err(SlotVerifyError::InvalidMetadata);
221     }
222     digest.clone_from_slice(descriptor.digest);
223     Ok(digest)
224 }
225 
226 /// Returns the indicated payload page size, if present.
read_page_size(vbmeta_data: &VbmetaData) -> Result<Option<usize>, PvmfwVerifyError>227 fn read_page_size(vbmeta_data: &VbmetaData) -> Result<Option<usize>, PvmfwVerifyError> {
228     let Some(property) = vbmeta_data.get_property_value("com.android.virt.page_size") else {
229         return Ok(None);
230     };
231     let size = str::from_utf8(property)
232         .or(Err(PvmfwVerifyError::InvalidPageSize))?
233         .parse::<usize>()
234         .or(Err(PvmfwVerifyError::InvalidPageSize))?
235         .checked_mul(1024)
236         // TODO(stable(unsigned_is_multiple_of)): use .is_multiple_of()
237         .filter(|sz| sz % (4 << 10) == 0 && *sz != 0)
238         .ok_or(PvmfwVerifyError::InvalidPageSize)?;
239 
240     Ok(Some(size))
241 }
242 
243 /// Returns the indicated payload name, if present.
read_name(vbmeta_data: &VbmetaData) -> Result<Option<String>, PvmfwVerifyError>244 fn read_name(vbmeta_data: &VbmetaData) -> Result<Option<String>, PvmfwVerifyError> {
245     let Some(property) = vbmeta_data.get_property_value("com.android.virt.name") else {
246         return Ok(None);
247     };
248     let name = str::from_utf8(property).map_err(|_| PvmfwVerifyError::InvalidVmName)?;
249     if name.is_empty() {
250         return Err(PvmfwVerifyError::InvalidVmName);
251     }
252     Ok(Some(name.into()))
253 }
254 
255 /// Verifies the given initrd partition, and checks that the resulting contents looks like expected.
verify_initrd( ops: &mut Ops, partition_name: PartitionName, expected_initrd: &[u8], ) -> SlotVerifyNoDataResult<()>256 fn verify_initrd(
257     ops: &mut Ops,
258     partition_name: PartitionName,
259     expected_initrd: &[u8],
260 ) -> SlotVerifyNoDataResult<()> {
261     let result =
262         ops.verify_partition(partition_name.as_cstr()).map_err(|e| e.without_verify_data())?;
263     verify_loaded_partition_has_expected_length(
264         result.partition_data(),
265         partition_name,
266         expected_initrd.len(),
267     )
268 }
269 
270 /// Verifies the payload (signed kernel + initrd) against the trusted public key.
verify_payload<'a>( kernel: &[u8], initrd: Option<&[u8]>, trusted_public_key: &'a [u8], ) -> Result<VerifiedBootData<'a>, PvmfwVerifyError>271 pub fn verify_payload<'a>(
272     kernel: &[u8],
273     initrd: Option<&[u8]>,
274     trusted_public_key: &'a [u8],
275 ) -> Result<VerifiedBootData<'a>, PvmfwVerifyError> {
276     let payload = Payload::new(kernel, initrd, trusted_public_key);
277     let mut ops = Ops::new(&payload);
278     let kernel_verify_result = ops.verify_partition(PartitionName::Kernel.as_cstr())?;
279 
280     let vbmeta_images = kernel_verify_result.vbmeta_data();
281     // TODO(b/302093437): Use explicit rollback_index_location instead of default
282     // location (first element).
283     let rollback_index =
284         *kernel_verify_result.rollback_indexes().first().unwrap_or(&DEFAULT_ROLLBACK_INDEX);
285     verify_only_one_vbmeta_exists(vbmeta_images)?;
286     let vbmeta_image = &vbmeta_images[0];
287     verify_vbmeta_is_from_kernel_partition(vbmeta_image)?;
288     let descriptors = vbmeta_image.descriptors()?;
289     let hash_descriptors = HashDescriptors::get(&descriptors)?;
290     let capabilities = Capability::get_capabilities(vbmeta_image)?;
291     let page_size = read_page_size(vbmeta_image)?;
292     let name = read_name(vbmeta_image)?;
293 
294     if initrd.is_none() {
295         hash_descriptors.verify_no_initrd()?;
296         return Ok(VerifiedBootData {
297             debug_level: DebugLevel::None,
298             kernel_digest: copy_digest(hash_descriptors.kernel)?,
299             initrd_digest: None,
300             public_key: trusted_public_key,
301             capabilities,
302             rollback_index,
303             page_size,
304             name,
305         });
306     }
307 
308     let initrd = initrd.unwrap();
309     let (debug_level, initrd_descriptor) =
310         if verify_initrd(&mut ops, PartitionName::InitrdNormal, initrd).is_ok() {
311             (DebugLevel::None, hash_descriptors.initrd_normal)
312         } else if verify_initrd(&mut ops, PartitionName::InitrdDebug, initrd).is_ok() {
313             (DebugLevel::Full, hash_descriptors.initrd_debug)
314         } else {
315             return Err(SlotVerifyError::Verification(None).into());
316         };
317     let initrd_descriptor = initrd_descriptor.ok_or(DescriptorError::InvalidContents)?;
318     Ok(VerifiedBootData {
319         debug_level,
320         kernel_digest: copy_digest(hash_descriptors.kernel)?,
321         initrd_digest: Some(copy_digest(initrd_descriptor)?),
322         public_key: trusted_public_key,
323         capabilities,
324         rollback_index,
325         page_size,
326         name,
327     })
328 }
329