1 // Copyright 2023, The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 //! Functions and drivers for obtaining true entropy.
16 
17 use crate::hvc;
18 use core::fmt;
19 use core::mem::size_of;
20 use smccc::{self, Hvc};
21 use zerocopy::AsBytes as _;
22 
23 type Entropy = [u8; size_of::<u64>() * 3];
24 
25 /// Error type for rand operations.
26 pub enum Error {
27     /// No source of entropy found.
28     NoEntropySource,
29     /// Error during architectural SMCCC call.
30     Smccc(smccc::arch::Error),
31     /// Error during SMCCC TRNG call.
32     Trng(hvc::trng::Error),
33     /// Unsupported SMCCC version.
34     UnsupportedSmcccVersion(smccc::arch::Version),
35     /// Unsupported SMCCC TRNG version.
36     UnsupportedTrngVersion(hvc::trng::Version),
37 }
38 
39 impl From<smccc::arch::Error> for Error {
from(e: smccc::arch::Error) -> Self40     fn from(e: smccc::arch::Error) -> Self {
41         Self::Smccc(e)
42     }
43 }
44 
45 impl From<hvc::trng::Error> for Error {
from(e: hvc::trng::Error) -> Self46     fn from(e: hvc::trng::Error) -> Self {
47         Self::Trng(e)
48     }
49 }
50 
51 /// Result type for rand operations.
52 pub type Result<T> = core::result::Result<T, Error>;
53 
54 impl fmt::Display for Error {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result55     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
56         match self {
57             Self::NoEntropySource => write!(f, "No source of entropy available"),
58             Self::Smccc(e) => write!(f, "Architectural SMCCC error: {e}"),
59             Self::Trng(e) => write!(f, "SMCCC TRNG error: {e}"),
60             Self::UnsupportedSmcccVersion(v) => write!(f, "Unsupported SMCCC version {v}"),
61             Self::UnsupportedTrngVersion(v) => write!(f, "Unsupported SMCCC TRNG version {v}"),
62         }
63     }
64 }
65 
66 impl fmt::Debug for Error {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result67     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
68         write!(f, "{self}")
69     }
70 }
71 
72 /// Configure the source of entropy.
init() -> Result<()>73 pub(crate) fn init() -> Result<()> {
74     // SMCCC TRNG requires SMCCC v1.1.
75     match smccc::arch::version::<Hvc>()? {
76         smccc::arch::Version { major: 1, minor } if minor >= 1 => (),
77         version => return Err(Error::UnsupportedSmcccVersion(version)),
78     }
79 
80     // TRNG_RND requires SMCCC TRNG v1.0.
81     match hvc::trng_version()? {
82         hvc::trng::Version { major: 1, minor: _ } => (),
83         version => return Err(Error::UnsupportedTrngVersion(version)),
84     }
85 
86     // TRNG_RND64 doesn't define any special capabilities so ignore the successful result.
87     let _ = hvc::trng_features(hvc::ARM_SMCCC_TRNG_RND64).map_err(|e| {
88         if e == hvc::trng::Error::NotSupported {
89             // SMCCC TRNG is currently our only source of entropy.
90             Error::NoEntropySource
91         } else {
92             e.into()
93         }
94     })?;
95 
96     Ok(())
97 }
98 
99 /// Fills a slice of bytes with true entropy.
fill_with_entropy(s: &mut [u8]) -> Result<()>100 pub fn fill_with_entropy(s: &mut [u8]) -> Result<()> {
101     const MAX_BYTES_PER_CALL: usize = size_of::<Entropy>();
102 
103     for chunk in s.chunks_mut(MAX_BYTES_PER_CALL) {
104         let entropy = repeat_trng_rnd(chunk.len())?;
105         chunk.clone_from_slice(&entropy[..chunk.len()]);
106     }
107 
108     Ok(())
109 }
110 
111 /// Returns an array where the first `n_bytes` bytes hold entropy.
112 ///
113 /// The rest of the array should be ignored.
repeat_trng_rnd(n_bytes: usize) -> Result<Entropy>114 fn repeat_trng_rnd(n_bytes: usize) -> Result<Entropy> {
115     loop {
116         if let Some(entropy) = rnd64(n_bytes)? {
117             return Ok(entropy);
118         }
119     }
120 }
121 
122 /// Returns an array where the first `n_bytes` bytes hold entropy, if available.
123 ///
124 /// The rest of the array should be ignored.
rnd64(n_bytes: usize) -> Result<Option<Entropy>>125 fn rnd64(n_bytes: usize) -> Result<Option<Entropy>> {
126     let bits = usize::try_from(u8::BITS).unwrap();
127     let result = hvc::trng_rnd64((n_bytes * bits).try_into().unwrap());
128     let entropy = if matches!(result, Err(hvc::trng::Error::NoEntropy)) {
129         None
130     } else {
131         let r = result?;
132         // From the SMCCC TRNG:
133         //
134         //     A MAX_BITS-bits wide value (Entropy) is returned across X1 to X3.
135         //     The requested conditioned entropy is returned in Entropy[N-1:0].
136         //
137         //             X1     Entropy[191:128]
138         //             X2     Entropy[127:64]
139         //             X3     Entropy[63:0]
140         //
141         //     The bits in Entropy[MAX_BITS-1:N] are 0.
142         let reordered = [r[2].to_le(), r[1].to_le(), r[0].to_le()];
143 
144         Some(reordered.as_bytes().try_into().unwrap())
145     };
146 
147     Ok(entropy)
148 }
149 
150 /// Generate an array of fixed-size initialized with true-random bytes.
random_array<const N: usize>() -> Result<[u8; N]>151 pub fn random_array<const N: usize>() -> Result<[u8; N]> {
152     let mut arr = [0; N];
153     fill_with_entropy(&mut arr)?;
154     Ok(arr)
155 }
156