Skip to main content

wasefire_board_api/
crypto.rs

1// Copyright 2022 Google LLC
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//! Cryptography interface.
16
17#[cfg(feature = "internal-crypto-rng")]
18use core::marker::PhantomData;
19
20#[cfg(feature = "internal-api-crypto-hash")]
21use crypto_common::BlockSizeUser;
22#[cfg(any(feature = "internal-api-crypto-hash", feature = "internal-api-crypto-hmac"))]
23use crypto_common::Output;
24#[cfg(feature = "internal-api-crypto-hmac")]
25use crypto_common::{InvalidLength, KeyInit};
26#[cfg(feature = "internal-api-crypto-hash")]
27use digest::HashMarker;
28#[cfg(feature = "internal-api-crypto-hmac")]
29use digest::MacMarker;
30#[cfg(any(feature = "internal-api-crypto-hash", feature = "internal-api-crypto-hmac"))]
31use digest::{FixedOutput, Update};
32#[cfg(feature = "internal-crypto-rng")]
33use signature::rand_core;
34#[cfg(feature = "internal-api-crypto-hmac")]
35use wasefire_error::Code;
36#[cfg(feature = "internal-with-error")]
37use wasefire_sync::TakeCell;
38
39#[cfg(feature = "internal-with-error")]
40use crate::Error;
41#[cfg(any(feature = "internal-api-crypto-hash", feature = "internal-api-crypto-hmac"))]
42use crate::Support;
43
44#[cfg(feature = "internal-api-crypto-aead")]
45pub mod aead;
46#[cfg(feature = "internal-api-crypto-cbc")]
47pub mod cbc;
48#[cfg(feature = "internal-api-crypto-ecc")]
49pub mod ecc;
50#[cfg(feature = "internal-api-crypto-ecdh")]
51pub mod ecdh;
52#[cfg(feature = "internal-api-crypto-ecdsa")]
53pub mod ecdsa;
54#[cfg(feature = "api-crypto-ed25519")]
55pub mod ed25519;
56
57/// Cryptography interface.
58pub trait Api: Send {
59    /// AES-128-CCM interface.
60    #[cfg(feature = "api-crypto-aes128-ccm")]
61    type Aes128Ccm: aead::Api<typenum::U16, typenum::U13, Tag = typenum::U4>;
62
63    /// AES-256-CBC interface.
64    #[cfg(feature = "api-crypto-aes256-cbc")]
65    type Aes256Cbc: cbc::Api<typenum::U32, typenum::U16>;
66
67    /// AES-256-GCM interface.
68    #[cfg(feature = "api-crypto-aes256-gcm")]
69    type Aes256Gcm: aead::Api<typenum::U32, typenum::U12>;
70
71    /// Ed25519 interface.
72    #[cfg(feature = "api-crypto-ed25519")]
73    type Ed25519: ed25519::Api;
74
75    /// HMAC-SHA-256 interface.
76    #[cfg(feature = "api-crypto-hmac-sha256")]
77    type HmacSha256: Hmac<KeySize = typenum::U64, OutputSize = typenum::U32>;
78
79    /// HMAC-SHA-384 interface.
80    #[cfg(feature = "api-crypto-hmac-sha384")]
81    type HmacSha384: Hmac<KeySize = typenum::U128, OutputSize = typenum::U48>;
82
83    /// P-256 interface.
84    #[cfg(feature = "api-crypto-p256")]
85    type P256: ecc::Api<typenum::U32>;
86
87    /// P-256 ECDH interface.
88    #[cfg(feature = "api-crypto-p256-ecdh")]
89    type P256Ecdh: ecdh::Api<32>;
90
91    /// P-256 ECDSA interface.
92    #[cfg(feature = "api-crypto-p256-ecdsa")]
93    type P256Ecdsa: ecdsa::Api<32>;
94
95    /// P-384 interface.
96    #[cfg(feature = "api-crypto-p384")]
97    type P384: ecc::Api<typenum::U48>;
98
99    /// P-384 ECDH interface.
100    #[cfg(feature = "api-crypto-p384-ecdh")]
101    type P384Ecdh: ecdh::Api<48>;
102
103    /// P-384 ECDSA interface.
104    #[cfg(feature = "api-crypto-p384-ecdsa")]
105    type P384Ecdsa: ecdsa::Api<48>;
106
107    /// SHA-256 interface.
108    #[cfg(feature = "api-crypto-sha256")]
109    type Sha256: Hash<BlockSize = typenum::U64, OutputSize = typenum::U32>;
110
111    /// SHA-384 interface.
112    #[cfg(feature = "api-crypto-sha384")]
113    type Sha384: Hash<BlockSize = typenum::U128, OutputSize = typenum::U48>;
114}
115
116/// Hash interface.
117#[cfg(feature = "internal-api-crypto-hash")]
118pub trait Hash:
119    Support<bool> + Send + Default + BlockSizeUser + Update + FixedOutput + HashMarker + WithError
120{
121}
122/// HMAC interface.
123#[cfg(feature = "internal-api-crypto-hmac")]
124pub trait Hmac:
125    Support<bool> + Send + KeyInit + Update + FixedOutput + MacMarker + WithError
126{
127}
128
129#[cfg(feature = "internal-api-crypto-hash")]
130impl<T> Hash for T where T: Support<bool>
131        + Send
132        + Default
133        + BlockSizeUser
134        + Update
135        + FixedOutput
136        + HashMarker
137        + WithError
138{
139}
140#[cfg(feature = "internal-api-crypto-hmac")]
141impl<T> Hmac for T where T: Support<bool> + Send + KeyInit + Update + FixedOutput + MacMarker + WithError
142{}
143
144/// Adds error support to operations with an infallible signature.
145#[cfg(feature = "internal-with-error")]
146pub trait WithError {
147    /// Executes a seemingly infallible operation with error support.
148    ///
149    /// The closure may actually call multiple seemingly infaillible operations. Each such call
150    /// should support running after a previous one failed. This funtion returns an error if any
151    /// such call failed.
152    fn with_error<T>(operation: impl FnOnce() -> T) -> Result<T, Error>;
153}
154
155/// Helper trait for infaillible operations.
156#[cfg(feature = "internal-with-error")]
157pub trait NoError {}
158
159#[cfg(feature = "internal-with-error")]
160impl<T: NoError> WithError for T {
161    fn with_error<R>(operation: impl FnOnce() -> R) -> Result<R, Error> {
162        Ok(operation())
163    }
164}
165
166/// Helper struct to implement `WithError`.
167#[cfg(feature = "internal-with-error")]
168pub struct GlobalError(TakeCell<Result<(), Error>>);
169
170#[cfg(feature = "internal-with-error")]
171impl GlobalError {
172    /// Creates an empty global error.
173    #[allow(clippy::new_without_default)]
174    pub const fn new() -> Self {
175        GlobalError(TakeCell::new(None))
176    }
177
178    /// Helper to implement `with_error`.
179    ///
180    /// This will consume the global error if not empty.
181    pub fn with<T>(&self, operation: impl FnOnce() -> T) -> Result<T, Error> {
182        self.0.put(Ok(()));
183        let result = operation();
184        self.0.take().map(|()| result)
185    }
186
187    /// Records an error.
188    ///
189    /// This will overwrite any previous error that was not consumed yet.
190    pub fn record<T>(&self, x: Result<T, Error>) -> Option<T> {
191        x.inspect_err(|e| self.0.with(|x| *x = Err(*e))).ok()
192    }
193}
194
195/// Helper to use the board RNG as a cryptography-secure one.
196#[cfg(feature = "internal-crypto-rng")]
197pub struct CryptoRng<T: crate::Api> {
198    rng: PhantomData<crate::Rng<T>>,
199}
200
201#[cfg(feature = "internal-crypto-rng")]
202impl<T: crate::Api> Default for CryptoRng<T> {
203    fn default() -> Self {
204        CryptoRng { rng: PhantomData }
205    }
206}
207
208#[cfg(feature = "internal-crypto-rng")]
209impl<T: crate::Api> rand_core::RngCore for CryptoRng<T> {
210    fn next_u32(&mut self) -> u32 {
211        rand_core::impls::next_u32_via_fill(self)
212    }
213
214    fn next_u64(&mut self) -> u64 {
215        rand_core::impls::next_u64_via_fill(self)
216    }
217
218    fn fill_bytes(&mut self, dest: &mut [u8]) {
219        self.try_fill_bytes(dest).unwrap()
220    }
221
222    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core::Error> {
223        ERROR
224            .record(<crate::Rng<T> as crate::rng::Api>::fill_bytes(dest))
225            .ok_or_else(|| core::num::NonZeroU32::new(1).unwrap().into())
226    }
227}
228
229#[cfg(feature = "internal-crypto-rng")]
230static ERROR: GlobalError = GlobalError::new();
231
232#[cfg(feature = "internal-crypto-rng")]
233impl<T: crate::Api> rand_core::CryptoRng for CryptoRng<T> {}
234
235#[cfg(feature = "internal-crypto-rng")]
236impl<T: crate::Api> WithError for CryptoRng<T> {
237    fn with_error<R>(operation: impl FnOnce() -> R) -> Result<R, Error> {
238        ERROR.with(operation)
239    }
240}
241
242/// AES-128-CCM interface.
243#[cfg(feature = "api-crypto-aes128-ccm")]
244pub type Aes128Ccm<B> = <super::Crypto<B> as Api>::Aes128Ccm;
245
246/// AES-256-CBC interface.
247#[cfg(feature = "api-crypto-aes256-cbc")]
248pub type Aes256Cbc<B> = <super::Crypto<B> as Api>::Aes256Cbc;
249
250/// AES-256-GCM interface.
251#[cfg(feature = "api-crypto-aes256-gcm")]
252pub type Aes256Gcm<B> = <super::Crypto<B> as Api>::Aes256Gcm;
253
254/// Ed25519 interface.
255#[cfg(feature = "api-crypto-ed25519")]
256pub type Ed25519<B> = <super::Crypto<B> as Api>::Ed25519;
257
258/// HMAC-SHA-256 interface.
259#[cfg(feature = "api-crypto-hmac-sha256")]
260pub type HmacSha256<B> = <super::Crypto<B> as Api>::HmacSha256;
261
262/// HMAC-SHA-384 interface.
263#[cfg(feature = "api-crypto-hmac-sha384")]
264pub type HmacSha384<B> = <super::Crypto<B> as Api>::HmacSha384;
265
266/// P-256 interface.
267#[cfg(feature = "api-crypto-p256")]
268pub type P256<B> = <super::Crypto<B> as Api>::P256;
269
270/// P-256 ECDH interface.
271#[cfg(feature = "api-crypto-p256-ecdh")]
272pub type P256Ecdh<B> = <super::Crypto<B> as Api>::P256Ecdh;
273
274/// P-256 ECDSA interface.
275#[cfg(feature = "api-crypto-p256-ecdsa")]
276pub type P256Ecdsa<B> = <super::Crypto<B> as Api>::P256Ecdsa;
277
278/// P-384 interface.
279#[cfg(feature = "api-crypto-p384")]
280pub type P384<B> = <super::Crypto<B> as Api>::P384;
281
282/// P-384 ECDH interface.
283#[cfg(feature = "api-crypto-p384-ecdh")]
284pub type P384Ecdh<B> = <super::Crypto<B> as Api>::P384Ecdh;
285
286/// P-384 ECDSA interface.
287#[cfg(feature = "api-crypto-p384-ecdsa")]
288pub type P384Ecdsa<B> = <super::Crypto<B> as Api>::P384Ecdsa;
289
290/// SHA-256 interface.
291#[cfg(feature = "api-crypto-sha256")]
292pub type Sha256<B> = <super::Crypto<B> as Api>::Sha256;
293
294/// SHA-384 interface.
295#[cfg(feature = "api-crypto-sha384")]
296pub type Sha384<B> = <super::Crypto<B> as Api>::Sha384;
297
298/// AES-128-CCM interface.
299#[cfg(feature = "software-crypto-aes128-ccm")]
300pub type SoftwareAes128Ccm = ccm::Ccm<aes::Aes128, typenum::U4, typenum::U13>;
301
302/// AES-256-CBC interface.
303#[cfg(feature = "software-crypto-aes256-cbc")]
304pub type SoftwareAes256Cbc = cbc::Software<aes::Aes256>;
305
306/// AES-256-GCM interface.
307#[cfg(feature = "software-crypto-aes256-gcm")]
308pub type SoftwareAes256Gcm = aes_gcm::Aes256Gcm;
309
310/// Ed25519 interface.
311#[cfg(feature = "software-crypto-ed25519")]
312pub type SoftwareEd25519<R> = ed25519::Software<R>;
313
314/// HMAC-SHA-256 interface.
315#[cfg(feature = "software-crypto-hmac-sha256")]
316pub type SoftwareHmacSha256<T> = hmac::SimpleHmac<<T as Api>::Sha256>;
317
318/// HMAC-SHA-384 interface.
319#[cfg(feature = "software-crypto-hmac-sha384")]
320pub type SoftwareHmacSha384<T> = hmac::SimpleHmac<<T as Api>::Sha384>;
321
322/// P-256 interface.
323#[cfg(feature = "software-crypto-p256")]
324pub type SoftwareP256<T> = ecc::Software<p256::NistP256, <T as Api>::Sha256>;
325
326/// P-256 ECDH interface.
327#[cfg(feature = "software-crypto-p256-ecdh")]
328pub type SoftwareP256Ecdh<R> = ecdh::Software<p256::NistP256, R, 32>;
329
330/// P-256 ECDSA interface.
331#[cfg(feature = "software-crypto-p256-ecdsa")]
332pub type SoftwareP256Ecdsa<T, R> = ecdsa::Software<p256::NistP256, <T as Api>::Sha256, R, 32>;
333
334/// P-384 interface.
335#[cfg(feature = "software-crypto-p384")]
336pub type SoftwareP384<T> = ecc::Software<p384::NistP384, <T as Api>::Sha384>;
337
338/// P-384 ECDH interface.
339#[cfg(feature = "software-crypto-p384-ecdh")]
340pub type SoftwareP384Ecdh<R> = ecdh::Software<p384::NistP384, R, 48>;
341
342/// P-384 ECDSA interface.
343#[cfg(feature = "software-crypto-p384-ecdsa")]
344pub type SoftwareP384Ecdsa<T, R> = ecdsa::Software<p384::NistP384, <T as Api>::Sha384, R, 48>;
345
346/// SHA-256 interface.
347#[cfg(feature = "software-crypto-sha256")]
348pub type SoftwareSha256 = sha2::Sha256;
349
350/// SHA-384 interface.
351#[cfg(feature = "software-crypto-sha384")]
352pub type SoftwareSha384 = sha2::Sha384;
353
354#[cfg(feature = "internal-test-software-crypto")]
355mod _test_software_crypto {
356    use super::*;
357
358    macro_rules! test {
359        ($Type:ident $($Param:ident)* $([$($where:tt)*])?; $Final:path) => {
360            #[allow(dead_code, non_snake_case)]
361            fn $Type<$($Param),*>() $(where $($where)*)? {
362                fn assert<T: $Final>() {}
363                assert::<$Type<$($Param),*>>();
364            }
365        };
366    }
367
368    #[cfg(feature = "software-crypto-aes128-ccm")]
369    test!(SoftwareAes128Ccm; aead::Api<typenum::U16, typenum::U13, Tag = typenum::U4>);
370    #[cfg(feature = "software-crypto-aes256-cbc")]
371    test!(SoftwareAes256Cbc; cbc::Api<typenum::U32, typenum::U16>);
372    #[cfg(feature = "software-crypto-aes256-gcm")]
373    test!(SoftwareAes256Gcm; aead::Api<typenum::U32, typenum::U12>);
374    #[cfg(feature = "software-crypto-ed25519")]
375    test!(SoftwareEd25519 R [R: Default + rand_core::CryptoRngCore + WithError + Send];
376          ed25519::Api);
377    #[cfg(feature = "software-crypto-hmac-sha256")]
378    test!(SoftwareHmacSha256 T [T: Api]; Hmac<KeySize = typenum::U64, OutputSize = typenum::U32>);
379    #[cfg(feature = "software-crypto-hmac-sha384")]
380    test!(SoftwareHmacSha384 T [T: Api]; Hmac<KeySize = typenum::U128, OutputSize = typenum::U48>);
381    #[cfg(feature = "software-crypto-p256")]
382    test!(SoftwareP256 T [T: Api, T::Sha256: digest::FixedOutputReset]; ecc::Api<typenum::U32>);
383    #[cfg(feature = "software-crypto-p256-ecdh")]
384    test!(SoftwareP256Ecdh R [R: Default + rand_core::CryptoRngCore + WithError + Send];
385          ecdh::Api<32>);
386    #[cfg(feature = "software-crypto-p256-ecdsa")]
387    test!(SoftwareP256Ecdsa T R [T: Api, T::Sha256: digest::FixedOutputReset,
388                                 R: Default + rand_core::CryptoRngCore + WithError + Send];
389          ecdsa::Api<32>);
390    #[cfg(feature = "software-crypto-p384")]
391    test!(SoftwareP384 T [T: Api, T::Sha384: digest::FixedOutputReset]; ecc::Api<typenum::U48>);
392    #[cfg(feature = "software-crypto-p384-ecdh")]
393    test!(SoftwareP384Ecdh R [R: Default + rand_core::CryptoRngCore + WithError + Send];
394          ecdh::Api<48>);
395    #[cfg(feature = "software-crypto-p384-ecdsa")]
396    test!(SoftwareP384Ecdsa T R [T: Api, T::Sha384: digest::FixedOutputReset,
397                                 R: Default + rand_core::CryptoRngCore + WithError + Send];
398          ecdsa::Api<48>);
399    #[cfg(feature = "software-crypto-sha256")]
400    test!(SoftwareSha256[]; Hash<BlockSize = typenum::U64, OutputSize = typenum::U32>);
401    #[cfg(feature = "software-crypto-sha384")]
402    test!(SoftwareSha384[]; Hash<BlockSize = typenum::U128, OutputSize = typenum::U48>);
403}
404
405#[cfg(feature = "software-crypto-sha256")]
406impl crate::Supported for sha2::Sha256 {}
407
408#[cfg(feature = "software-crypto-sha384")]
409impl crate::Supported for sha2::Sha384 {}
410
411#[cfg(feature = "internal-software-crypto-hmac")]
412impl<D> Support<bool> for hmac::SimpleHmac<D>
413where D: Support<bool> + Default + BlockSizeUser + Update + FixedOutput + HashMarker + WithError
414{
415    const SUPPORT: bool = D::SUPPORT;
416}
417
418#[cfg(feature = "software-crypto-sha256")]
419impl NoError for sha2::Sha256 {}
420
421#[cfg(feature = "software-crypto-sha384")]
422impl NoError for sha2::Sha384 {}
423
424#[cfg(feature = "internal-software-crypto-hmac")]
425impl<D> WithError for hmac::SimpleHmac<D>
426where D: Support<bool> + Default + BlockSizeUser + Update + FixedOutput + HashMarker + WithError
427{
428    fn with_error<T>(operation: impl FnOnce() -> T) -> Result<T, Error> {
429        D::with_error(operation)
430    }
431}
432
433/// Hash wrapper with error support.
434#[cfg(feature = "internal-api-crypto-hash")]
435pub struct HashApi<T: Hash>(T);
436
437/// HMAC wrapper with error support.
438#[cfg(feature = "internal-api-crypto-hmac")]
439pub struct HmacApi<T: Hmac>(T);
440
441#[cfg(feature = "internal-api-crypto-hash")]
442impl<T: Hash> HashApi<T> {
443    /// Creates a hash wrapper.
444    pub fn new() -> Result<Self, Error> {
445        T::with_error(|| T::default()).map(HashApi)
446    }
447
448    /// Updates the hash with the provided data.
449    pub fn update(&mut self, data: &[u8]) -> Result<(), Error> {
450        T::with_error(|| self.0.update(data))
451    }
452
453    /// Finalizes the hash to the provided output.
454    pub fn finalize_into(self, out: &mut Output<T>) -> Result<(), Error> {
455        T::with_error(|| self.0.finalize_into(out))
456    }
457}
458
459#[cfg(feature = "internal-api-crypto-hmac")]
460impl<T: Hmac> HmacApi<T> {
461    /// Creates an HMAC wrapper.
462    pub fn new(key: &[u8]) -> Result<Self, Error> {
463        match T::with_error(|| T::new_from_slice(key)) {
464            Ok(Ok(x)) => Ok(HmacApi(x)),
465            Ok(Err(InvalidLength)) => Err(Error::user(Code::InvalidLength)),
466            Err(e) => Err(e),
467        }
468    }
469
470    /// Updates the HMAC with the provided data.
471    pub fn update(&mut self, data: &[u8]) -> Result<(), Error> {
472        T::with_error(|| self.0.update(data))
473    }
474
475    /// Finalizes the HMAC to the provided output.
476    pub fn finalize_into(self, out: &mut Output<T>) -> Result<(), Error> {
477        T::with_error(|| self.0.finalize_into(out))
478    }
479}