pvmfw: Obtain entropy through the SMCCC TRNG
Implement a driver for the SMCCC TRNG and use it to generate the DICE
salt and AEAD nonce.
Bug: 262393451
Test: atest MicrodroidHostTests
Change-Id: Ie85e9196760779d665511bc0c9a2719d00a5eb81
diff --git a/pvmfw/src/entry.rs b/pvmfw/src/entry.rs
index 964ded1..a1cd2a1 100644
--- a/pvmfw/src/entry.rs
+++ b/pvmfw/src/entry.rs
@@ -22,6 +22,7 @@
use crate::memory::MemoryTracker;
use crate::mmio_guard;
use crate::mmu;
+use crate::rand;
use core::arch::asm;
use core::num::NonZeroUsize;
use core::slice;
@@ -252,6 +253,11 @@
let mut memory = MemoryTracker::new(page_table);
let slices = MemorySlices::new(fdt, payload, payload_size, &mut memory)?;
+ rand::init().map_err(|e| {
+ error!("Failed to initialize rand: {e}");
+ RebootReason::InternalError
+ })?;
+
// This wrapper allows main() to be blissfully ignorant of platform details.
crate::main(slices.fdt, slices.kernel, slices.ramdisk, bcc_slice, &mut memory)?;
diff --git a/pvmfw/src/hvc.rs b/pvmfw/src/hvc.rs
index dc99303..319ff9d 100644
--- a/pvmfw/src/hvc.rs
+++ b/pvmfw/src/hvc.rs
@@ -14,9 +14,19 @@
//! Wrappers around calls to the hypervisor.
+pub mod trng;
+
use crate::smccc::{self, checked_hvc64, checked_hvc64_expect_zero};
use log::info;
+const ARM_SMCCC_TRNG_VERSION: u32 = 0x8400_0050;
+#[allow(dead_code)]
+const ARM_SMCCC_TRNG_FEATURES: u32 = 0x8400_0051;
+#[allow(dead_code)]
+const ARM_SMCCC_TRNG_GET_UUID: u32 = 0x8400_0052;
+#[allow(dead_code)]
+const ARM_SMCCC_TRNG_RND32: u32 = 0x8400_0053;
+const ARM_SMCCC_TRNG_RND64: u32 = 0xc400_0053;
const ARM_SMCCC_KVM_FUNC_HYP_MEMINFO: u32 = 0xc6000002;
const ARM_SMCCC_KVM_FUNC_MEM_SHARE: u32 = 0xc6000003;
const ARM_SMCCC_KVM_FUNC_MEM_UNSHARE: u32 = 0xc6000004;
@@ -94,3 +104,22 @@
x => x,
}
}
+
+/// Returns the (major, minor) version tuple, as defined by the SMCCC TRNG.
+pub fn trng_version() -> trng::Result<(u16, u16)> {
+ let args = [0u64; 17];
+
+ let version = trng::hvc64(ARM_SMCCC_TRNG_VERSION, args)?[0];
+ Ok(((version >> 16) as u16, version as u16))
+}
+
+pub type TrngRng64Entropy = (u64, u64, u64);
+
+pub fn trng_rnd64(nbits: u64) -> trng::Result<TrngRng64Entropy> {
+ let mut args = [0u64; 17];
+ args[0] = nbits;
+
+ let regs = trng::hvc64_expect_zero(ARM_SMCCC_TRNG_RND64, args)?;
+
+ Ok((regs[1], regs[2], regs[3]))
+}
diff --git a/pvmfw/src/hvc/trng.rs b/pvmfw/src/hvc/trng.rs
new file mode 100644
index 0000000..d347693
--- /dev/null
+++ b/pvmfw/src/hvc/trng.rs
@@ -0,0 +1,65 @@
+// 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.
+
+use crate::smccc;
+use core::fmt;
+use core::result;
+
+/// Standard SMCCC TRNG error values as described in DEN 0098 1.0 REL0.
+#[derive(Debug, Clone)]
+pub enum Error {
+ /// The call is not supported by the implementation.
+ NotSupported,
+ /// One of the call parameters has a non-supported value.
+ InvalidParameter,
+ /// Call returned without the requested entropy bits.
+ NoEntropy,
+ /// Negative values indicate error.
+ Unknown(i64),
+ /// The call returned a positive value when 0 was expected.
+ Unexpected(u64),
+}
+
+impl fmt::Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ Self::NotSupported => write!(f, "SMCCC TRNG call not supported"),
+ Self::InvalidParameter => write!(f, "SMCCC TRNG call received non-supported value"),
+ Self::NoEntropy => write!(f, "SMCCC TRNG call returned no entropy"),
+ Self::Unexpected(v) => write!(f, "Unexpected SMCCC TRNG return value {} ({0:#x})", v),
+ Self::Unknown(e) => write!(f, "Unknown SMCCC TRNG return value {} ({0:#x})", e),
+ }
+ }
+}
+
+pub type Result<T> = result::Result<T, Error>;
+
+pub fn hvc64(function: u32, args: [u64; 17]) -> Result<[u64; 18]> {
+ let res = smccc::hvc64(function, args);
+ match res[0] as i64 {
+ ret if ret >= 0 => Ok(res),
+ -1 => Err(Error::NotSupported),
+ -2 => Err(Error::InvalidParameter),
+ -3 => Err(Error::NoEntropy),
+ ret => Err(Error::Unknown(ret)),
+ }
+}
+
+pub fn hvc64_expect_zero(function: u32, args: [u64; 17]) -> Result<[u64; 18]> {
+ let res = hvc64(function, args)?;
+ match res[0] {
+ 0 => Ok(res),
+ v => Err(Error::Unexpected(v)),
+ }
+}
diff --git a/pvmfw/src/instance.rs b/pvmfw/src/instance.rs
index 6a54623..fbf2040 100644
--- a/pvmfw/src/instance.rs
+++ b/pvmfw/src/instance.rs
@@ -22,6 +22,7 @@
use crate::gpt::Partition;
use crate::gpt::Partitions;
use crate::helpers::ceiling_div;
+use crate::rand;
use crate::virtio::pci::VirtIOBlkIterator;
use core::fmt;
use core::mem::size_of;
@@ -38,6 +39,8 @@
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.
@@ -69,6 +72,7 @@
}
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 {
@@ -129,7 +133,7 @@
}
}
PvmfwEntry::New { header_index } => {
- let salt = [0; size_of::<Hidden>()]; // TODO(b/262393451): Generate using TRNG.
+ let salt = rand::random_array().map_err(Error::FailedSaltGeneration)?;
let entry_body = EntryBody::new(dice_inputs, &salt);
let body = entry_body.as_ref();
diff --git a/pvmfw/src/rand.rs b/pvmfw/src/rand.rs
index 2824cbd..a53cac6 100644
--- a/pvmfw/src/rand.rs
+++ b/pvmfw/src/rand.rs
@@ -12,6 +12,80 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+use crate::hvc;
+use core::fmt;
+use core::mem::size_of;
+
+pub enum Error {
+ /// Error during SMCCC TRNG call.
+ Trng(hvc::trng::Error),
+ /// Unsupported SMCCC TRNG version.
+ UnsupportedVersion((u16, u16)),
+}
+
+impl From<hvc::trng::Error> for Error {
+ fn from(e: hvc::trng::Error) -> Self {
+ Self::Trng(e)
+ }
+}
+
+pub type Result<T> = core::result::Result<T, Error>;
+
+impl fmt::Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ Self::Trng(e) => write!(f, "SMCCC TRNG error: {e}"),
+ Self::UnsupportedVersion((x, y)) => {
+ write!(f, "Unsupported SMCCC TRNG version v{x}.{y}")
+ }
+ }
+ }
+}
+
+/// Configure the source of entropy.
+pub fn init() -> Result<()> {
+ match hvc::trng_version()? {
+ (1, _) => Ok(()),
+ version => Err(Error::UnsupportedVersion(version)),
+ }
+}
+
+fn fill_with_entropy(s: &mut [u8]) -> Result<()> {
+ const MAX_BYTES_PER_CALL: usize = size_of::<hvc::TrngRng64Entropy>();
+ let bits = usize::try_from(u8::BITS).unwrap();
+
+ let (aligned, remainder) = s.split_at_mut(s.len() - s.len() % MAX_BYTES_PER_CALL);
+
+ for chunk in aligned.chunks_exact_mut(MAX_BYTES_PER_CALL) {
+ let (r, s, t) = hvc::trng_rnd64((chunk.len() * bits).try_into().unwrap())?;
+
+ let mut words = chunk.chunks_exact_mut(size_of::<u64>());
+ words.next().unwrap().clone_from_slice(&t.to_ne_bytes());
+ words.next().unwrap().clone_from_slice(&s.to_ne_bytes());
+ words.next().unwrap().clone_from_slice(&r.to_ne_bytes());
+ }
+
+ if !remainder.is_empty() {
+ let mut entropy = [0; MAX_BYTES_PER_CALL];
+ let (r, s, t) = hvc::trng_rnd64((remainder.len() * bits).try_into().unwrap())?;
+
+ let mut words = entropy.chunks_exact_mut(size_of::<u64>());
+ words.next().unwrap().clone_from_slice(&t.to_ne_bytes());
+ words.next().unwrap().clone_from_slice(&s.to_ne_bytes());
+ words.next().unwrap().clone_from_slice(&r.to_ne_bytes());
+
+ remainder.clone_from_slice(&entropy[..remainder.len()]);
+ }
+
+ Ok(())
+}
+
+pub fn random_array<const N: usize>() -> Result<[u8; N]> {
+ let mut arr = [0; N];
+ fill_with_entropy(&mut arr)?;
+ Ok(arr)
+}
+
#[no_mangle]
extern "C" fn CRYPTO_sysrand_for_seed(out: *mut u8, req: usize) {
CRYPTO_sysrand(out, req)
@@ -19,5 +93,7 @@
#[no_mangle]
extern "C" fn CRYPTO_sysrand(out: *mut u8, req: usize) {
- #![allow(unused_variables)] // TODO(b/262393451): Fill with actual entropy.
+ // SAFETY - We need to assume that out points to valid memory of size req.
+ let s = unsafe { core::slice::from_raw_parts_mut(out, req) };
+ let _ = fill_with_entropy(s);
}
diff --git a/pvmfw/src/smccc.rs b/pvmfw/src/smccc.rs
index f92c076..ccf2680 100644
--- a/pvmfw/src/smccc.rs
+++ b/pvmfw/src/smccc.rs
@@ -16,7 +16,7 @@
// TODO(b/245889995): use psci-0.1.1 crate
#[inline(always)]
-fn hvc64(function: u32, args: [u64; 17]) -> [u64; 18] {
+pub fn hvc64(function: u32, args: [u64; 17]) -> [u64; 18] {
#[cfg(target_arch = "aarch64")]
unsafe {
let mut ret = [0; 18];