pvmfw: Validate input BCC handover
Ensure that the BCC contained in the configuration data is properly
formatted as a "BCC Handover" [1] i.e. a CBOR-encoded map
BccHandover = {
1 : bstr .size 32, ; CDI_Attest
2 : bstr .size 32, ; CDI_Seal
3 : Bcc, ; Certificate chain
}
If not, abort the pVM boot.
[1]: https://pigweed.googlesource.com/open-dice/+/72ad792c4d9ffffef3412801b5da46568a6b3197/src/android/bcc.c#260
Bug: 256827715
Test: atest MicrodroidHostTests
Change-Id: Ibade0ebd1e50d912a59b32c1282022aa46235501
diff --git a/libs/dice/src/bcc.rs b/libs/dice/src/bcc.rs
new file mode 100644
index 0000000..849dfa0
--- /dev/null
+++ b/libs/dice/src/bcc.rs
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//! Wrapper around dice/android/bcc.h.
+
+use core::mem;
+use core::ptr;
+
+use open_dice_bcc_bindgen::BccHandoverParse;
+
+use crate::check_call;
+use crate::Cdi;
+use crate::Error;
+use crate::Result;
+
+/// Boot Chain Certificate handover format combining the BCC and CDIs in a single CBOR object.
+#[derive(Clone, Debug)]
+pub struct Handover<'a> {
+ /// Attestation CDI.
+ pub cdi_attest: &'a Cdi,
+ /// Sealing CDI.
+ pub cdi_seal: &'a Cdi,
+ /// Boot Chain Certificate (optional).
+ pub bcc: Option<&'a [u8]>,
+}
+
+impl<'a> Handover<'a> {
+ /// Validates and extracts the fields of a BCC handover buffer.
+ pub fn new(buffer: &'a [u8]) -> Result<Self> {
+ let mut cdi_attest: *const u8 = ptr::null();
+ let mut cdi_seal: *const u8 = ptr::null();
+ let mut bcc: *const u8 = ptr::null();
+ let mut bcc_size: usize = 0;
+
+ // SAFETY - The buffer is only read and never stored and the returned pointers should all
+ // point within the address range of the buffer or be NULL.
+ check_call(unsafe {
+ BccHandoverParse(
+ buffer.as_ptr(),
+ buffer.len(),
+ &mut cdi_attest as *mut *const u8,
+ &mut cdi_seal as *mut *const u8,
+ &mut bcc as *mut *const u8,
+ &mut bcc_size as *mut usize,
+ )
+ })?;
+
+ let cdi_attest = {
+ let i = index_from_ptr(buffer, cdi_attest).ok_or(Error::PlatformError)?;
+ let s = buffer.get(i..(i + mem::size_of::<Cdi>())).ok_or(Error::PlatformError)?;
+ s.try_into().map_err(|_| Error::PlatformError)?
+ };
+ let cdi_seal = {
+ let i = index_from_ptr(buffer, cdi_seal).ok_or(Error::PlatformError)?;
+ let s = buffer.get(i..(i + mem::size_of::<Cdi>())).ok_or(Error::PlatformError)?;
+ s.try_into().map_err(|_| Error::PlatformError)?
+ };
+ let bcc = if bcc.is_null() {
+ None
+ } else {
+ let i = index_from_ptr(buffer, bcc).ok_or(Error::PlatformError)?;
+ Some(buffer.get(i..(i + bcc_size)).ok_or(Error::PlatformError)?)
+ };
+
+ Ok(Self { cdi_attest, cdi_seal, bcc })
+ }
+}
+
+fn index_from_ptr(slice: &[u8], pointer: *const u8) -> Option<usize> {
+ if slice.as_ptr_range().contains(&pointer) {
+ (pointer as usize).checked_sub(slice.as_ptr() as usize)
+ } else {
+ None
+ }
+}
diff --git a/libs/dice/src/lib.rs b/libs/dice/src/lib.rs
index 862f3e9..43d167f 100644
--- a/libs/dice/src/lib.rs
+++ b/libs/dice/src/lib.rs
@@ -28,8 +28,13 @@
use open_dice_cbor_bindgen::DiceResult_kDiceResultOk as DICE_RESULT_OK;
use open_dice_cbor_bindgen::DiceResult_kDiceResultPlatformError as DICE_RESULT_PLATFORM_ERROR;
+pub mod bcc;
+
+const CDI_SIZE: usize = open_dice_cbor_bindgen::DICE_CDI_SIZE as usize;
const HASH_SIZE: usize = open_dice_cbor_bindgen::DICE_HASH_SIZE as usize;
+/// Array type of CDIs.
+pub type Cdi = [u8; CDI_SIZE];
/// Array type of hashes used by DICE.
pub type Hash = [u8; HASH_SIZE];