blob: 2789a60d2a279eefbda7cebaca9ec96dfca84f0c [file] [log] [blame]
// Copyright 2021, 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.
//! Implements utility functions and types for diced and the dice HAL.
use android_hardware_security_dice::aidl::android::hardware::security::dice::{
Bcc::Bcc, BccHandover::BccHandover, InputValues::InputValues as BinderInputValues,
Mode::Mode as BinderMode,
};
use anyhow::{anyhow, Context, Result};
use diced_open_dice as dice;
use diced_open_dice::DiceArtifacts;
use keystore2_crypto::ZVec;
use std::convert::TryInto;
/// Converts the `InputValues` from the binder to the `InputValues` type in `diced_open_dice` crate.
pub fn to_dice_input_values(input: &BinderInputValues) -> dice::InputValues {
if input.authorityDescriptor.is_some() {
unimplemented!("Authority descriptor is not yet implemented in the current library.");
}
dice::InputValues::new(
input.codeHash,
dice::Config::Descriptor(input.config.desc.as_slice()),
input.authorityHash,
to_dice_mode(input.mode),
input.hidden,
)
}
fn to_dice_mode(binder_mode: BinderMode) -> dice::DiceMode {
match binder_mode {
BinderMode::NOT_INITIALIZED => dice::DiceMode::kDiceModeNotInitialized,
BinderMode::NORMAL => dice::DiceMode::kDiceModeNormal,
BinderMode::DEBUG => dice::DiceMode::kDiceModeDebug,
BinderMode::RECOVERY => dice::DiceMode::kDiceModeMaintenance,
_ => dice::DiceMode::kDiceModeNotInitialized,
}
}
/// Initializes an aidl defined BccHandover object with the arguments `cdi_attest`, `cdi_seal`,
/// and `bcc`.
pub fn make_bcc_handover(
cdi_attest: &[u8; dice::CDI_SIZE],
cdi_seal: &[u8; dice::CDI_SIZE],
bcc: &[u8],
) -> Result<BccHandover> {
Ok(BccHandover { cdiAttest: *cdi_attest, cdiSeal: *cdi_seal, bcc: Bcc { data: bcc.to_vec() } })
}
/// ResidentArtifacts stores a set of dice artifacts comprising CDI_ATTEST, CDI_SEAL,
/// and the BCC formatted attestation certificate chain. The sensitive secrets are
/// stored in zeroing vectors, and it implements functionality to perform DICE
/// derivation steps using libopen-dice-cbor.
/// TODO(b/268322533): Remove this struct with the unused HAL service dice
pub struct ResidentArtifacts {
cdi_attest: ZVec,
cdi_seal: ZVec,
bcc: Vec<u8>,
}
impl TryFrom<dice::OwnedDiceArtifacts> for ResidentArtifacts {
type Error = anyhow::Error;
fn try_from(dice_artifacts: dice::OwnedDiceArtifacts) -> Result<Self, Self::Error> {
Ok(ResidentArtifacts {
cdi_attest: dice_artifacts.cdi_attest().to_vec().try_into()?,
cdi_seal: dice_artifacts.cdi_seal().to_vec().try_into()?,
bcc: dice_artifacts.bcc().ok_or_else(|| anyhow!("bcc is none"))?.to_vec(),
})
}
}
impl ResidentArtifacts {
/// Create a ResidentArtifacts object. The parameters ensure that the stored secrets
/// can only have the appropriate size, so that subsequent casts to array references
/// cannot fail.
pub fn new(
cdi_attest: &[u8; dice::CDI_SIZE],
cdi_seal: &[u8; dice::CDI_SIZE],
bcc: &[u8],
) -> Result<Self> {
Ok(ResidentArtifacts {
cdi_attest: cdi_attest[..]
.try_into()
.context("In ResidentArtifacts::new: Trying to convert cdi_attest to ZVec.")?,
cdi_seal: cdi_seal[..]
.try_into()
.context("In ResidentArtifacts::new: Trying to convert cdi_seal to ZVec.")?,
bcc: bcc.to_vec(),
})
}
/// Creates a ResidentArtifacts object from another one implementing the DiceArtifacts
/// trait. Like `new` this function can only create artifacts of appropriate size
/// because DiceArtifacts returns array references of appropriate size.
pub fn new_from<T: DiceArtifacts + ?Sized>(artifacts: &T) -> Result<Self> {
Ok(ResidentArtifacts {
cdi_attest: artifacts.cdi_attest().to_vec().try_into()?,
cdi_seal: artifacts.cdi_seal().to_vec().try_into()?,
bcc: artifacts.bcc().ok_or_else(|| anyhow!("bcc is none"))?.to_vec(),
})
}
/// Attempts to clone the artifacts. This operation is fallible due to the fallible
/// nature of ZVec.
pub fn try_clone(&self) -> Result<Self> {
Ok(ResidentArtifacts {
cdi_attest: self
.cdi_attest
.try_clone()
.context("In ResidentArtifacts::new: Trying to clone cdi_attest.")?,
cdi_seal: self
.cdi_seal
.try_clone()
.context("In ResidentArtifacts::new: Trying to clone cdi_seal.")?,
bcc: self.bcc.clone(),
})
}
/// Deconstruct the Artifacts into a tuple.
/// (CDI_ATTEST, CDI_SEAL, BCC)
pub fn into_tuple(self) -> (ZVec, ZVec, Vec<u8>) {
let ResidentArtifacts { cdi_attest, cdi_seal, bcc } = self;
(cdi_attest, cdi_seal, bcc)
}
fn execute_step(self, input_values: &BinderInputValues) -> Result<Self> {
let ResidentArtifacts { cdi_attest, cdi_seal, bcc } = self;
dice::retry_bcc_main_flow(
cdi_attest[..].try_into().with_context(|| {
format!("Trying to convert cdi_attest. (length: {})", cdi_attest.len())
})?,
cdi_seal[..].try_into().with_context(|| {
format!("Trying to convert cdi_seal. (length: {})", cdi_seal.len())
})?,
&bcc,
&to_dice_input_values(input_values),
)
.context("In ResidentArtifacts::execute_step:")?
.try_into()
}
/// Iterate through the iterator of dice input values performing one
/// BCC main flow step on each element.
pub fn execute_steps<'a, I>(self, input_values: I) -> Result<Self>
where
I: IntoIterator<Item = &'a BinderInputValues>,
{
input_values
.into_iter()
.try_fold(self, |acc, input| acc.execute_step(input))
.context("In ResidentArtifacts::execute_step:")
}
}
/// Implement this trait to provide read and write access to a secure artifact
/// storage that can be used by the ResidentHal implementation.
pub trait UpdatableDiceArtifacts {
/// With artifacts provides access to the stored artifacts for the duration
/// of the function call by means of calling the callback.
fn with_artifacts<F, T>(&self, f: F) -> Result<T>
where
F: FnOnce(&dyn DiceArtifacts) -> Result<T>;
/// Consumes the object and returns a an updated version of itself.
fn update(self, new_artifacts: &impl DiceArtifacts) -> Result<Self>
where
Self: Sized;
}
impl DiceArtifacts for ResidentArtifacts {
fn cdi_attest(&self) -> &[u8; dice::CDI_SIZE] {
self.cdi_attest[..].try_into().unwrap()
}
fn cdi_seal(&self) -> &[u8; dice::CDI_SIZE] {
self.cdi_seal[..].try_into().unwrap()
}
fn bcc(&self) -> Option<&[u8]> {
Some(&self.bcc)
}
}
/// This submodule implements a limited set of CBOR generation functionality. Essentially,
/// a cbor header generator and some convenience functions for number and BSTR encoding.
pub mod cbor {
use anyhow::{anyhow, Context, Result};
use std::convert::TryInto;
use std::io::Write;
/// CBOR encodes a positive number.
pub fn encode_number(n: u64, buffer: &mut dyn Write) -> Result<()> {
encode_header(0, n, buffer)
}
/// CBOR encodes a binary string.
pub fn encode_bstr(bstr: &[u8], buffer: &mut dyn Write) -> Result<()> {
encode_header(
2,
bstr.len().try_into().context("In encode_bstr: Failed to convert usize to u64.")?,
buffer,
)
.context("In encode_bstr: While writing header.")?;
let written = buffer.write(bstr).context("In encode_bstr: While writing payload.")?;
if written != bstr.len() {
return Err(anyhow!("In encode_bstr: Buffer too small. ({}, {})", written, bstr.len()));
}
Ok(())
}
/// Formats a CBOR header. `t` is the type, and n is the header argument.
pub fn encode_header(t: u8, n: u64, buffer: &mut dyn Write) -> Result<()> {
match n {
n if n < 24 => {
let written =
buffer.write(&u8::to_be_bytes((t << 5) | (n as u8 & 0x1F))).with_context(
|| format!("In encode_header: Failed to write header ({}, {})", t, n),
)?;
if written != 1 {
return Err(anyhow!("In encode_header: Buffer to small. ({}, {})", t, n));
}
}
n if n <= 0xFF => {
let written =
buffer.write(&u8::to_be_bytes((t << 5) | (24u8 & 0x1F))).with_context(
|| format!("In encode_header: Failed to write header ({}, {})", t, n),
)?;
if written != 1 {
return Err(anyhow!("In encode_header: Buffer to small. ({}, {})", t, n));
}
let written = buffer.write(&u8::to_be_bytes(n as u8)).with_context(|| {
format!("In encode_header: Failed to write size ({}, {})", t, n)
})?;
if written != 1 {
return Err(anyhow!(
"In encode_header while writing size: Buffer to small. ({}, {})",
t,
n
));
}
}
n if n <= 0xFFFF => {
let written =
buffer.write(&u8::to_be_bytes((t << 5) | (25u8 & 0x1F))).with_context(
|| format!("In encode_header: Failed to write header ({}, {})", t, n),
)?;
if written != 1 {
return Err(anyhow!("In encode_header: Buffer to small. ({}, {})", t, n));
}
let written = buffer.write(&u16::to_be_bytes(n as u16)).with_context(|| {
format!("In encode_header: Failed to write size ({}, {})", t, n)
})?;
if written != 2 {
return Err(anyhow!(
"In encode_header while writing size: Buffer to small. ({}, {})",
t,
n
));
}
}
n if n <= 0xFFFFFFFF => {
let written =
buffer.write(&u8::to_be_bytes((t << 5) | (26u8 & 0x1F))).with_context(
|| format!("In encode_header: Failed to write header ({}, {})", t, n),
)?;
if written != 1 {
return Err(anyhow!("In encode_header: Buffer to small. ({}, {})", t, n));
}
let written = buffer.write(&u32::to_be_bytes(n as u32)).with_context(|| {
format!("In encode_header: Failed to write size ({}, {})", t, n)
})?;
if written != 4 {
return Err(anyhow!(
"In encode_header while writing size: Buffer to small. ({}, {})",
t,
n
));
}
}
n => {
let written =
buffer.write(&u8::to_be_bytes((t << 5) | (27u8 & 0x1F))).with_context(
|| format!("In encode_header: Failed to write header ({}, {})", t, n),
)?;
if written != 1 {
return Err(anyhow!("In encode_header: Buffer to small. ({}, {})", t, n));
}
let written = buffer.write(&u64::to_be_bytes(n)).with_context(|| {
format!("In encode_header: Failed to write size ({}, {})", t, n)
})?;
if written != 8 {
return Err(anyhow!(
"In encode_header while writing size: Buffer to small. ({}, {})",
t,
n
));
}
}
}
Ok(())
}
#[cfg(test)]
mod test {
use super::*;
fn encode_header_helper(t: u8, n: u64) -> Vec<u8> {
let mut b: Vec<u8> = vec![];
encode_header(t, n, &mut b).unwrap();
b
}
#[test]
fn encode_header_test() {
assert_eq!(&encode_header_helper(0, 0), &[0b000_00000]);
assert_eq!(&encode_header_helper(0, 23), &[0b000_10111]);
assert_eq!(&encode_header_helper(0, 24), &[0b000_11000, 24]);
assert_eq!(&encode_header_helper(0, 0xff), &[0b000_11000, 0xff]);
assert_eq!(&encode_header_helper(0, 0x100), &[0b000_11001, 0x01, 0x00]);
assert_eq!(&encode_header_helper(0, 0xffff), &[0b000_11001, 0xff, 0xff]);
assert_eq!(&encode_header_helper(0, 0x10000), &[0b000_11010, 0x00, 0x01, 0x00, 0x00]);
assert_eq!(
&encode_header_helper(0, 0xffffffff),
&[0b000_11010, 0xff, 0xff, 0xff, 0xff]
);
assert_eq!(
&encode_header_helper(0, 0x100000000),
&[0b000_11011, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00]
);
assert_eq!(
&encode_header_helper(0, 0xffffffffffffffff),
&[0b000_11011, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]
);
}
}
}