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