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
This commit is contained in:
Pierre-Clément Tosi 2022-12-06 16:02:57 +00:00
parent db14ada78b
commit 8edf72eafa
5 changed files with 106 additions and 11 deletions

88
libs/dice/src/bcc.rs Normal file
View File

@ -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
}
}

View File

@ -28,8 +28,13 @@ use open_dice_cbor_bindgen::DiceResult_kDiceResultInvalidInput as DICE_RESULT_IN
use open_dice_cbor_bindgen::DiceResult_kDiceResultOk as DICE_RESULT_OK; use open_dice_cbor_bindgen::DiceResult_kDiceResultOk as DICE_RESULT_OK;
use open_dice_cbor_bindgen::DiceResult_kDiceResultPlatformError as DICE_RESULT_PLATFORM_ERROR; 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; 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. /// Array type of hashes used by DICE.
pub type Hash = [u8; HASH_SIZE]; pub type Hash = [u8; HASH_SIZE];

View File

@ -15,6 +15,7 @@ rust_ffi_static {
"libaarch64_paging", "libaarch64_paging",
"libavb_nostd", "libavb_nostd",
"libbuddy_system_allocator", "libbuddy_system_allocator",
"libdice_nostd",
"liblibfdt", "liblibfdt",
"liblog_rust_nostd", "liblog_rust_nostd",
"libpvmfw_embedded_key", "libpvmfw_embedded_key",

View File

@ -24,6 +24,7 @@ use crate::mmu;
use core::arch::asm; use core::arch::asm;
use core::num::NonZeroUsize; use core::num::NonZeroUsize;
use core::slice; use core::slice;
use dice::bcc::Handover;
use log::debug; use log::debug;
use log::error; use log::error;
use log::info; use log::info;
@ -228,8 +229,9 @@ fn main_wrapper(fdt: usize, payload: usize, payload_size: usize) -> Result<(), R
RebootReason::InvalidConfig RebootReason::InvalidConfig
})?; })?;
let bcc = appended.get_bcc_mut().ok_or_else(|| { let bcc_slice = appended.get_bcc_mut();
error!("Invalid BCC"); let bcc = Handover::new(bcc_slice).map_err(|e| {
error!("Invalid BCC Handover: {e:?}");
RebootReason::InvalidBcc RebootReason::InvalidBcc
})?; })?;
@ -243,7 +245,7 @@ fn main_wrapper(fdt: usize, payload: usize, payload_size: usize) -> Result<(), R
let slices = MemorySlices::new(fdt, payload, payload_size, &mut memory)?; let slices = MemorySlices::new(fdt, payload, payload_size, &mut memory)?;
// This wrapper allows main() to be blissfully ignorant of platform details. // This wrapper allows main() to be blissfully ignorant of platform details.
crate::main(slices.fdt, slices.kernel, slices.ramdisk, bcc, &mut memory)?; crate::main(slices.fdt, slices.kernel, slices.ramdisk, &bcc, &mut memory)?;
// TODO: Overwrite BCC before jumping to payload to avoid leaking our sealing key. // TODO: Overwrite BCC before jumping to payload to avoid leaking our sealing key.
@ -366,12 +368,10 @@ impl<'a> AppendedPayload<'a> {
} }
} }
fn get_bcc_mut(&mut self) -> Option<&mut [u8]> { fn get_bcc_mut(&mut self) -> &mut [u8] {
let bcc = match self { match self {
Self::LegacyBcc(ref mut bcc) => bcc, Self::LegacyBcc(ref mut bcc) => bcc,
Self::Config(ref mut cfg) => cfg.get_bcc_mut(), Self::Config(ref mut cfg) => cfg.get_bcc_mut(),
}; }
// TODO(b/256148034): return None if BccHandoverParse(bcc) != kDiceResultOk.
Some(bcc)
} }
} }

View File

@ -39,14 +39,15 @@ use crate::{
}; };
use avb::PUBLIC_KEY; use avb::PUBLIC_KEY;
use avb_nostd::verify_image; use avb_nostd::verify_image;
use dice::bcc;
use libfdt::Fdt; use libfdt::Fdt;
use log::{debug, error, info}; use log::{debug, error, info, trace};
fn main( fn main(
fdt: &Fdt, fdt: &Fdt,
signed_kernel: &[u8], signed_kernel: &[u8],
ramdisk: Option<&[u8]>, ramdisk: Option<&[u8]>,
bcc: &[u8], bcc: &bcc::Handover,
memory: &mut MemoryTracker, memory: &mut MemoryTracker,
) -> Result<(), RebootReason> { ) -> Result<(), RebootReason> {
info!("pVM firmware"); info!("pVM firmware");
@ -57,7 +58,7 @@ fn main(
} else { } else {
debug!("Ramdisk: None"); debug!("Ramdisk: None");
} }
debug!("BCC: {:?} ({:#x} bytes)", bcc.as_ptr(), bcc.len()); trace!("BCC: {bcc:x?}");
// Set up PCI bus for VirtIO devices. // Set up PCI bus for VirtIO devices.
let pci_node = pci_node(fdt)?; let pci_node = pci_node(fdt)?;