[pvmfw] Use AEAD wrapper from libbssl_avf
This cl replaces the AEAD bssl wrappers in pvmfw with the ones in
libbssl_avf. The wrappers in libbssl_avf have undergone thorough
testing, and the context is cleaned and freed when it is dropped.
This cl decreases the size of pvmfw.bin from 532512 bytes to
511648 bytes.
Bug: 302286887
Test: atest MicrodroidHostTests
Change-Id: Iba12469410dd1069fb9c48b666010ff158cc1327
diff --git a/libs/bssl/src/aead.rs b/libs/bssl/src/aead.rs
index e0c9fbb..1ac2c22 100644
--- a/libs/bssl/src/aead.rs
+++ b/libs/bssl/src/aead.rs
@@ -18,8 +18,8 @@
use bssl_avf_error::{ApiName, Result};
use bssl_ffi::{
EVP_AEAD_CTX_free, EVP_AEAD_CTX_new, EVP_AEAD_CTX_open, EVP_AEAD_CTX_seal,
- EVP_AEAD_max_overhead, EVP_AEAD_nonce_length, EVP_aead_aes_256_gcm, EVP_AEAD, EVP_AEAD_CTX,
- EVP_AEAD_DEFAULT_TAG_LENGTH,
+ EVP_AEAD_max_overhead, EVP_AEAD_nonce_length, EVP_aead_aes_256_gcm,
+ EVP_aead_aes_256_gcm_randnonce, EVP_AEAD, EVP_AEAD_CTX, EVP_AEAD_DEFAULT_TAG_LENGTH,
};
use core::ptr::NonNull;
@@ -51,6 +51,17 @@
Self(unsafe { &*p })
}
+ /// AES-256 in Galois Counter Mode with internal nonce generation.
+ /// The 12-byte nonce is appended to the tag and is generated internally.
+ pub fn aes_256_gcm_randnonce() -> Self {
+ // SAFETY: This function does not access any Rust variables and simply returns
+ // a pointer to the static variable in BoringSSL.
+ let p = unsafe { EVP_aead_aes_256_gcm_randnonce() };
+ // SAFETY: The returned pointer should always be valid and points to a static
+ // `EVP_AEAD`.
+ Self(unsafe { &*p })
+ }
+
/// Returns the maximum number of additional bytes added by the act of sealing data.
pub fn max_overhead(&self) -> usize {
// SAFETY: This function only reads from self.
diff --git a/pvmfw/src/crypto.rs b/pvmfw/src/crypto.rs
deleted file mode 100644
index 8f31553..0000000
--- a/pvmfw/src/crypto.rs
+++ /dev/null
@@ -1,271 +0,0 @@
-// Copyright 2023, 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 BoringSSL/OpenSSL symbols.
-
-use core::convert::AsRef;
-use core::ffi::{c_char, c_int, CStr};
-use core::fmt;
-use core::mem::MaybeUninit;
-use core::num::NonZeroU32;
-use core::ptr;
-
-use bssl_ffi::CRYPTO_library_init;
-use bssl_ffi::ERR_get_error_line;
-use bssl_ffi::ERR_lib_error_string;
-use bssl_ffi::ERR_reason_error_string;
-use bssl_ffi::EVP_AEAD_CTX_aead;
-use bssl_ffi::EVP_AEAD_CTX_init;
-use bssl_ffi::EVP_AEAD_CTX_open;
-use bssl_ffi::EVP_AEAD_CTX_seal;
-use bssl_ffi::EVP_AEAD_max_overhead;
-use bssl_ffi::EVP_aead_aes_256_gcm_randnonce;
-use bssl_ffi::EVP_AEAD;
-use bssl_ffi::EVP_AEAD_CTX;
-use cstr::cstr;
-
-#[derive(Debug)]
-pub struct Error {
- packed: NonZeroU32,
- file: Option<&'static CStr>,
- line: c_int,
-}
-
-impl Error {
- fn get() -> Option<Self> {
- let mut file = ptr::null();
- let mut line = 0;
- // SAFETY: The function writes to the provided pointers, which are valid because they come
- // from references. It doesn't retain them after it returns.
- let packed = unsafe { ERR_get_error_line(&mut file, &mut line) };
-
- let packed = packed.try_into().ok()?;
- // SAFETY: Any non-NULL result is expected to point to a global const C string.
- let file = unsafe { as_static_cstr(file) };
-
- Some(Self { packed, file, line })
- }
-
- fn packed_value(&self) -> u32 {
- self.packed.get()
- }
-
- fn library_name(&self) -> Option<&'static CStr> {
- // SAFETY: Call to a pure function.
- let name = unsafe { ERR_lib_error_string(self.packed_value()) };
- // SAFETY: Any non-NULL result is expected to point to a global const C string.
- unsafe { as_static_cstr(name) }
- }
-
- fn reason(&self) -> Option<&'static CStr> {
- // SAFETY: Call to a pure function.
- let reason = unsafe { ERR_reason_error_string(self.packed_value()) };
- // SAFETY: Any non-NULL result is expected to point to a global const C string.
- unsafe { as_static_cstr(reason) }
- }
-}
-
-impl fmt::Display for Error {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- let packed = self.packed_value();
- let library = self.library_name().unwrap_or(cstr!("{unknown library}")).to_str().unwrap();
- let reason = self.reason().unwrap_or(cstr!("{unknown reason}")).to_str().unwrap();
- let file = self.file.unwrap_or(cstr!("??")).to_str().unwrap();
- let line = self.line;
-
- write!(f, "{file}:{line}: {library}: {reason} ({packed:#x})")
- }
-}
-
-#[derive(Copy, Clone)]
-pub struct ErrorIterator {}
-
-impl Iterator for ErrorIterator {
- type Item = Error;
-
- fn next(&mut self) -> Option<Self::Item> {
- Self::Item::get()
- }
-}
-
-pub type Result<T> = core::result::Result<T, ErrorIterator>;
-
-#[repr(transparent)]
-pub struct Aead(EVP_AEAD);
-
-impl Aead {
- pub fn aes_256_gcm_randnonce() -> Option<&'static Self> {
- // SAFETY: Returned pointer is checked below.
- let aead = unsafe { EVP_aead_aes_256_gcm_randnonce() };
- if aead.is_null() {
- None
- } else {
- // SAFETY: We assume that the non-NULL value points to a valid and static EVP_AEAD.
- Some(unsafe { &*(aead as *const _) })
- }
- }
-
- pub fn max_overhead(&self) -> usize {
- // SAFETY: Function should only read from self.
- unsafe { EVP_AEAD_max_overhead(self.as_ref() as *const _) }
- }
-}
-
-#[repr(transparent)]
-pub struct AeadCtx(EVP_AEAD_CTX);
-
-impl AeadCtx {
- pub fn new_aes_256_gcm_randnonce(key: &[u8]) -> Result<Self> {
- let aead = Aead::aes_256_gcm_randnonce().unwrap();
-
- Self::new(aead, key)
- }
-
- fn new(aead: &'static Aead, key: &[u8]) -> Result<Self> {
- const DEFAULT_TAG_LENGTH: usize = 0;
- let engine = ptr::null_mut(); // Use default implementation.
- let mut ctx = MaybeUninit::zeroed();
- // SAFETY: Initialize the EVP_AEAD_CTX with const pointers to the AEAD and key.
- let result = unsafe {
- EVP_AEAD_CTX_init(
- ctx.as_mut_ptr(),
- aead.as_ref() as *const _,
- key.as_ptr(),
- key.len(),
- DEFAULT_TAG_LENGTH,
- engine,
- )
- };
-
- if result == 1 {
- // SAFETY: We assume that the non-NULL value points to a valid and static EVP_AEAD.
- Ok(Self(unsafe { ctx.assume_init() }))
- } else {
- Err(ErrorIterator {})
- }
- }
-
- pub fn aead(&self) -> Option<&'static Aead> {
- // SAFETY: The function should only read from self.
- let aead = unsafe { EVP_AEAD_CTX_aead(self.as_ref() as *const _) };
- if aead.is_null() {
- None
- } else {
- // SAFETY: We assume that the non-NULL value points to a valid and static EVP_AEAD.
- Some(unsafe { &*(aead as *const _) })
- }
- }
-
- pub fn open<'b>(&self, out: &'b mut [u8], data: &[u8]) -> Result<&'b mut [u8]> {
- let nonce = ptr::null_mut();
- let nonce_len = 0;
- let ad = ptr::null_mut();
- let ad_len = 0;
- let mut out_len = MaybeUninit::uninit();
- // SAFETY: The function should only read from self and write to out (at most the provided
- // number of bytes) and out_len while reading from data (at most the provided number of
- // bytes), ignoring any NULL input.
- let result = unsafe {
- EVP_AEAD_CTX_open(
- self.as_ref() as *const _,
- out.as_mut_ptr(),
- out_len.as_mut_ptr(),
- out.len(),
- nonce,
- nonce_len,
- data.as_ptr(),
- data.len(),
- ad,
- ad_len,
- )
- };
-
- if result == 1 {
- // SAFETY: Any value written to out_len could be a valid usize. The value itself is
- // validated as being a proper slice length by panicking in the following indexing
- // otherwise.
- let out_len = unsafe { out_len.assume_init() };
- Ok(&mut out[..out_len])
- } else {
- Err(ErrorIterator {})
- }
- }
-
- pub fn seal<'b>(&self, out: &'b mut [u8], data: &[u8]) -> Result<&'b mut [u8]> {
- let nonce = ptr::null_mut();
- let nonce_len = 0;
- let ad = ptr::null_mut();
- let ad_len = 0;
- let mut out_len = MaybeUninit::uninit();
- // SAFETY: The function should only read from self and write to out (at most the provided
- // number of bytes) while reading from data (at most the provided number of bytes),
- // ignoring any NULL input.
- let result = unsafe {
- EVP_AEAD_CTX_seal(
- self.as_ref() as *const _,
- out.as_mut_ptr(),
- out_len.as_mut_ptr(),
- out.len(),
- nonce,
- nonce_len,
- data.as_ptr(),
- data.len(),
- ad,
- ad_len,
- )
- };
-
- if result == 1 {
- // SAFETY: Any value written to out_len could be a valid usize. The value itself is
- // validated as being a proper slice length by panicking in the following indexing
- // otherwise.
- let out_len = unsafe { out_len.assume_init() };
- Ok(&mut out[..out_len])
- } else {
- Err(ErrorIterator {})
- }
- }
-}
-
-/// Cast a C string pointer to a static non-mutable reference.
-///
-/// # Safety
-///
-/// The caller needs to ensure that the pointer is null or points to a valid C string and that the
-/// C lifetime of the string is compatible with a static Rust lifetime.
-unsafe fn as_static_cstr(p: *const c_char) -> Option<&'static CStr> {
- if p.is_null() {
- None
- } else {
- // Safety: Safe given the requirements of this function.
- Some(unsafe { CStr::from_ptr(p) })
- }
-}
-
-impl AsRef<EVP_AEAD> for Aead {
- fn as_ref(&self) -> &EVP_AEAD {
- &self.0
- }
-}
-
-impl AsRef<EVP_AEAD_CTX> for AeadCtx {
- fn as_ref(&self) -> &EVP_AEAD_CTX {
- &self.0
- }
-}
-
-pub fn init() {
- // SAFETY: Configures the internal state of the library - may be called multiple times.
- unsafe { CRYPTO_library_init() }
-}
diff --git a/pvmfw/src/entry.rs b/pvmfw/src/entry.rs
index ed73bc9..65c8f26 100644
--- a/pvmfw/src/entry.rs
+++ b/pvmfw/src/entry.rs
@@ -15,9 +15,9 @@
//! Low-level entry and exit points of pvmfw.
use crate::config;
-use crate::crypto;
use crate::fdt;
use crate::memory;
+use bssl_ffi::CRYPTO_library_init;
use core::arch::asm;
use core::mem::{drop, size_of};
use core::num::NonZeroUsize;
@@ -196,7 +196,12 @@
// - only access non-pvmfw memory once (and while) it has been mapped
log::set_max_level(LevelFilter::Info);
- crypto::init();
+ // TODO(https://crbug.com/boringssl/35): Remove this init when BoringSSL can handle this
+ // internally.
+ // SAFETY: Configures the internal state of the library - may be called multiple times.
+ unsafe {
+ CRYPTO_library_init();
+ }
let page_table = memory::init_page_table().map_err(|e| {
error!("Failed to set up the dynamic page tables: {e}");
diff --git a/pvmfw/src/instance.rs b/pvmfw/src/instance.rs
index f2cd6a3..28e9ca3 100644
--- a/pvmfw/src/instance.rs
+++ b/pvmfw/src/instance.rs
@@ -14,13 +14,11 @@
//! Support for reading and writing to the instance.img.
-use crate::crypto;
-use crate::crypto::AeadCtx;
use crate::dice::PartialInputs;
use crate::gpt;
use crate::gpt::Partition;
use crate::gpt::Partitions;
-use bssl_avf::{self, hkdf, Digester};
+use bssl_avf::{self, hkdf, Aead, AeadContext, Digester};
use core::fmt;
use core::mem::size_of;
use diced_open_dice::DiceMode;
@@ -40,12 +38,8 @@
pub enum Error {
/// Unexpected I/O error while accessing the underlying disk.
FailedIo(gpt::Error),
- /// Failed to decrypt the entry.
- FailedOpen(crypto::ErrorIterator),
/// Failed to generate a random salt to be stored.
FailedSaltGeneration(rand::Error),
- /// Failed to encrypt the entry.
- FailedSeal(crypto::ErrorIterator),
/// Impossible to create a new instance.img entry.
InstanceImageFull,
/// Badly formatted instance.img header block.
@@ -72,21 +66,7 @@
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::FailedIo(e) => write!(f, "Failed I/O to disk: {e}"),
- Self::FailedOpen(e_iter) => {
- writeln!(f, "Failed to open the instance.img partition:")?;
- for e in *e_iter {
- writeln!(f, "\t{e}")?;
- }
- Ok(())
- }
Self::FailedSaltGeneration(e) => write!(f, "Failed to generate salt: {e}"),
- Self::FailedSeal(e_iter) => {
- writeln!(f, "Failed to seal the instance.img partition:")?;
- for e in *e_iter {
- writeln!(f, "\t{e}")?;
- }
- Ok(())
- }
Self::InstanceImageFull => write!(f, "Failed to obtain a free instance.img partition"),
Self::InvalidInstanceImageHeader => write!(f, "instance.img header is invalid"),
Self::MissingInstanceImage => write!(f, "Failed to find the instance.img partition"),
@@ -124,6 +104,13 @@
trace!("Found pvmfw instance.img entry: {entry:?}");
let key = hkdf::<32>(secret, /* salt= */ &[], b"vm-instance", Digester::sha512())?;
+ let tag_len = None;
+ let aead_ctx = AeadContext::new(Aead::aes_256_gcm_randnonce(), key.as_slice(), tag_len)?;
+ let ad = &[];
+ // The nonce is generated internally for `aes_256_gcm_randnonce`, so no additional
+ // nonce is required.
+ let nonce = &[];
+
let mut blk = [0; BLK_SIZE];
match entry {
PvmfwEntry::Existing { header_index, payload_size } => {
@@ -136,9 +123,7 @@
let payload = &blk[..payload_size];
let mut entry = [0; size_of::<EntryBody>()];
- let aead =
- AeadCtx::new_aes_256_gcm_randnonce(key.as_slice()).map_err(Error::FailedOpen)?;
- let decrypted = aead.open(&mut entry, payload).map_err(Error::FailedOpen)?;
+ let decrypted = aead_ctx.open(payload, nonce, ad, &mut entry)?;
let body = EntryBody::read_from(decrypted).unwrap();
if body.code_hash != dice_inputs.code_hash {
@@ -155,12 +140,10 @@
let salt = rand::random_array().map_err(Error::FailedSaltGeneration)?;
let body = EntryBody::new(dice_inputs, &salt);
- let aead =
- AeadCtx::new_aes_256_gcm_randnonce(key.as_slice()).map_err(Error::FailedSeal)?;
// We currently only support single-blk entries.
let plaintext = body.as_bytes();
- assert!(plaintext.len() + aead.aead().unwrap().max_overhead() < blk.len());
- let encrypted = aead.seal(&mut blk, plaintext).map_err(Error::FailedSeal)?;
+ assert!(plaintext.len() + aead_ctx.aead().max_overhead() < blk.len());
+ let encrypted = aead_ctx.seal(plaintext, nonce, ad, &mut blk)?;
let payload_size = encrypted.len();
let payload_index = header_index + 1;
instance_img.write_block(payload_index, &blk).map_err(Error::FailedIo)?;
diff --git a/pvmfw/src/main.rs b/pvmfw/src/main.rs
index 8aa5274..8d872d4 100644
--- a/pvmfw/src/main.rs
+++ b/pvmfw/src/main.rs
@@ -22,7 +22,6 @@
mod bcc;
mod bootargs;
mod config;
-mod crypto;
mod device_assignment;
mod dice;
mod entry;