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:
parent
db14ada78b
commit
8edf72eafa
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
|
@ -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];
|
||||||
|
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)?;
|
||||||
|
|
Loading…
Reference in New Issue