blob: bf0edd5500f9570244f4b9b1999810d8845884bd [file] [log] [blame]
// 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::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 (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) = repeat_trng_rnd(chunk.len())?;
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) = repeat_trng_rnd(remainder.len())?;
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(())
}
fn repeat_trng_rnd(n_bytes: usize) -> hvc::trng::Result<hvc::TrngRng64Entropy> {
let bits = usize::try_from(u8::BITS).unwrap();
let n_bits = (n_bytes * bits).try_into().unwrap();
loop {
match hvc::trng_rnd64(n_bits) {
Err(hvc::trng::Error::NoEntropy) => continue,
res => return res,
}
}
}
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)
}
#[no_mangle]
extern "C" fn CRYPTO_sysrand(out: *mut u8, req: usize) {
// 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);
}