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/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);
}