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