Skip to main content

aranya_crypto/test_util/
mod.rs

1//! Utilities for testing [`Engine`][crate::Engine],
2//! [`CipherSuite`], and cryptography implementations.
3//!
4//! If you implement any traits in this crate it is **very
5//! highly** recommended that you use these tests.
6
7#![allow(clippy::arithmetic_side_effects)]
8#![allow(clippy::panic)]
9#![allow(clippy::unwrap_used)]
10#![cfg(any(test, feature = "test_util"))]
11#![cfg_attr(docsrs, doc(cfg(feature = "test_util")))]
12#![forbid(unsafe_code)]
13
14pub mod ciphersuite;
15pub mod engine;
16
17use core::marker::PhantomData;
18
19pub use ciphersuite::test_ciphersuite;
20use derive_where::derive_where;
21pub use engine::test_engine;
22pub use spideroak_crypto::test_util::{
23    aead::{self, test_aead},
24    hash::{self, test_hash},
25    hpke::{self, test_hpke},
26    kdf::{self, test_kdf},
27    mac::{self, test_mac},
28    signer::{self, test_signer},
29    vectors,
30};
31use spideroak_crypto::{
32    self as crypto,
33    aead::{IndCca2, Lifetime, OpenError, SealError},
34    csprng::{Csprng, Random},
35    hpke::{AeadId, HpkeAead, HpkeKdf, KdfId},
36    import::{ExportError, Import, ImportError},
37    kdf::{KdfError, Prk},
38    keys::{InvalidKey, PublicKey, SecretKey, SecretKeyBytes},
39    oid::{Identified, Oid},
40    signer::{PkError, Signature, SignerError, SigningKey, VerifyingKey},
41    subtle::{Choice, ConstantTimeEq},
42    typenum::U32,
43    zeroize::ZeroizeOnDrop,
44};
45
46use crate::{Aead, Hash, Kdf, Kem, Mac, Signer, ciphersuite::CipherSuite};
47
48#[macro_export]
49#[doc(hidden)]
50macro_rules! __apply {
51    ($callback:ident, $($tt:tt),* $(,)?) => {
52        $(
53            $callback!($tt);
54        )*
55    };
56}
57pub use __apply;
58
59/// Like [`assert_eq!`], but for [`Choice`].
60#[macro_export]
61macro_rules! assert_ct_eq {
62    ($lhs:expr, $rhs:expr) => {
63        assert!(bool::from($crate::subtle::ConstantTimeEq::ct_eq(&$lhs, &$rhs)))
64    };
65    ($lhs:expr, $rhs:expr, ) => {
66        $crate::assert_ct_eq!($lhs, $rhs)
67    };
68    ($lhs:expr, $rhs:expr, $($args:tt)+) => {
69        assert!(bool::from($crate::subtle::ConstantTimeEq::ct_eq(&$lhs, &$rhs)), $($args)+)
70    };
71}
72pub(super) use assert_ct_eq;
73
74/// Like [`assert_ne!`], but for [`Choice`].
75#[macro_export]
76macro_rules! assert_ct_ne {
77    ($lhs:expr, $rhs:expr) => {
78        assert!(bool::from($crate::subtle::ConstantTimeEq::ct_ne(&$lhs, &$rhs)))
79    };
80    ($lhs:expr, $rhs:expr, ) => {
81        $crate::assert_ct_ne!($lhs, $rhs)
82    };
83    ($lhs:expr, $rhs:expr, $($args:tt)+) => {
84        assert!(bool::from($crate::subtle::ConstantTimeEq::ct_ne(&$lhs, &$rhs)), $($args)+)
85    };
86}
87pub(super) use assert_ct_ne;
88
89/// A shim that declares `OS_hardware_rand` for doctests.
90#[macro_export]
91#[doc(hidden)]
92macro_rules! __doctest_os_hardware_rand {
93    () => {
94        #[cfg(feature = "trng")]
95        #[unsafe(no_mangle)]
96        extern "C" fn OS_hardware_rand() -> u32 {
97            use rand::RngCore;
98            rand::rngs::OsRng.next_u32()
99        }
100    };
101}
102
103/// An [`Aead`] that that uses the default trait methods.
104pub struct AeadWithDefaults<T>(T);
105
106impl<T: Aead> crypto::aead::Aead for AeadWithDefaults<T> {
107    const LIFETIME: Lifetime = T::LIFETIME;
108
109    type KeySize = T::KeySize;
110    const KEY_SIZE: usize = T::KEY_SIZE;
111
112    type NonceSize = T::NonceSize;
113    const NONCE_SIZE: usize = T::NONCE_SIZE;
114
115    type Overhead = T::Overhead;
116    const OVERHEAD: usize = T::OVERHEAD;
117
118    const MAX_PLAINTEXT_SIZE: u64 = T::MAX_PLAINTEXT_SIZE;
119    const MAX_ADDITIONAL_DATA_SIZE: u64 = T::MAX_ADDITIONAL_DATA_SIZE;
120    const MAX_CIPHERTEXT_SIZE: u64 = T::MAX_CIPHERTEXT_SIZE;
121
122    type Key = T::Key;
123
124    fn new(key: &Self::Key) -> Self {
125        Self(T::new(key))
126    }
127
128    fn seal_in_place(
129        &self,
130        nonce: &[u8],
131        data: &mut [u8],
132        tag: &mut [u8],
133        additional_data: &[u8],
134    ) -> Result<(), SealError> {
135        self.0.seal_in_place(nonce, data, tag, additional_data)
136    }
137
138    fn open_in_place(
139        &self,
140        nonce: &[u8],
141        data: &mut [u8],
142        tag: &[u8],
143        additional_data: &[u8],
144    ) -> Result<(), OpenError> {
145        self.0.open_in_place(nonce, data, tag, additional_data)
146    }
147}
148
149impl<T: Aead> IndCca2 for AeadWithDefaults<T> {}
150
151impl<T: Aead> HpkeAead for AeadWithDefaults<T> {
152    const ID: AeadId = T::ID;
153}
154
155impl<T: Aead> Identified for AeadWithDefaults<T> {
156    const OID: &Oid = T::OID;
157}
158
159/// A [`Kdf`] that that uses the default trait methods.
160pub struct KdfWithDefaults<T>(PhantomData<T>);
161
162impl<T: Kdf> crypto::kdf::Kdf for KdfWithDefaults<T> {
163    type MaxOutput = T::MaxOutput;
164
165    type PrkSize = T::PrkSize;
166
167    fn extract_multi<'a, I>(ikm: I, salt: &[u8]) -> Prk<Self::PrkSize>
168    where
169        I: IntoIterator<Item = &'a [u8]>,
170    {
171        T::extract_multi(ikm, salt)
172    }
173
174    fn expand_multi<'a, I>(
175        out: &mut [u8],
176        prk: &Prk<Self::PrkSize>,
177        info: I,
178    ) -> Result<(), KdfError>
179    where
180        I: IntoIterator<Item = &'a [u8], IntoIter: Clone>,
181    {
182        T::expand_multi(out, prk, info)
183    }
184}
185
186impl<T: Kdf> HpkeKdf for KdfWithDefaults<T> {
187    const ID: KdfId = T::ID;
188}
189
190impl<T: Kdf> Identified for KdfWithDefaults<T> {
191    const OID: &'static Oid = T::OID;
192}
193
194/// A [`Mac`] that that uses the default trait methods.
195#[derive(Clone)]
196pub struct MacWithDefaults<T>(T);
197
198impl<T: Mac> crypto::mac::Mac for MacWithDefaults<T> {
199    type Tag = T::Tag;
200    type TagSize = T::TagSize;
201
202    type Key = T::Key;
203    type KeySize = T::KeySize;
204    type MinKeySize = T::MinKeySize;
205
206    fn new(key: &Self::Key) -> Self {
207        Self(T::new(key))
208    }
209
210    fn try_new(key: &[u8]) -> Result<Self, InvalidKey> {
211        Ok(Self(T::try_new(key)?))
212    }
213
214    fn update(&mut self, data: &[u8]) {
215        self.0.update(data);
216    }
217
218    fn tag(self) -> Self::Tag {
219        self.0.tag()
220    }
221}
222
223impl<T: Mac> Identified for MacWithDefaults<T> {
224    const OID: &Oid = T::OID;
225}
226
227/// A [`Signer`] that that uses the default trait methods.
228pub struct SignerWithDefaults<T: ?Sized>(T);
229
230impl<T: Signer + ?Sized> crypto::signer::Signer for SignerWithDefaults<T> {
231    type SigningKey = SigningKeyWithDefaults<T>;
232    type VerifyingKey = VerifyingKeyWithDefaults<T>;
233    type Signature = SignatureWithDefaults<T>;
234}
235
236impl<T: Signer + ?Sized> Identified for SignerWithDefaults<T> {
237    const OID: &Oid = T::OID;
238}
239
240/// A [`SigningKey`] that uses the default trait methods.
241#[derive_where(Clone)]
242pub struct SigningKeyWithDefaults<T: Signer + ?Sized>(T::SigningKey);
243
244impl<T: Signer + ?Sized> SigningKey<SignerWithDefaults<T>> for SigningKeyWithDefaults<T> {
245    fn sign(&self, msg: &[u8]) -> Result<SignatureWithDefaults<T>, SignerError> {
246        Ok(SignatureWithDefaults(self.0.sign(msg)?))
247    }
248
249    fn public(&self) -> Result<VerifyingKeyWithDefaults<T>, PkError> {
250        Ok(VerifyingKeyWithDefaults(self.0.public()?))
251    }
252}
253
254impl<T: Signer + ?Sized> SecretKey for SigningKeyWithDefaults<T> {
255    type Size = <T::SigningKey as SecretKey>::Size;
256
257    fn try_export_secret(&self) -> Result<SecretKeyBytes<Self::Size>, ExportError> {
258        self.0.try_export_secret()
259    }
260}
261
262impl<T: Signer + ?Sized> Random for SigningKeyWithDefaults<T> {
263    fn random<R: Csprng>(rng: R) -> Self {
264        Self(T::SigningKey::random(rng))
265    }
266}
267
268impl<T: Signer + ?Sized> ConstantTimeEq for SigningKeyWithDefaults<T> {
269    fn ct_eq(&self, other: &Self) -> Choice {
270        ConstantTimeEq::ct_eq(&self.0, &other.0)
271    }
272}
273
274impl<'a, T: Signer + ?Sized> Import<&'a [u8]> for SigningKeyWithDefaults<T> {
275    fn import(data: &'a [u8]) -> Result<Self, ImportError> {
276        Ok(Self(T::SigningKey::import(data)?))
277    }
278}
279
280impl<T: Signer + ?Sized> ZeroizeOnDrop for SigningKeyWithDefaults<T> {}
281
282/// A [`VerifyingKey`] that uses the default trait methods.
283#[derive_where(Clone, Debug, PartialEq, Eq)]
284pub struct VerifyingKeyWithDefaults<T: Signer + ?Sized>(T::VerifyingKey);
285
286impl<T: Signer + ?Sized> VerifyingKey<SignerWithDefaults<T>> for VerifyingKeyWithDefaults<T> {
287    fn verify(&self, msg: &[u8], sig: &SignatureWithDefaults<T>) -> Result<(), SignerError> {
288        self.0.verify(msg, &sig.0)
289    }
290}
291
292impl<T: Signer + ?Sized> PublicKey for VerifyingKeyWithDefaults<T> {
293    type Data = <T::VerifyingKey as PublicKey>::Data;
294
295    fn export(&self) -> Self::Data {
296        self.0.export()
297    }
298}
299
300impl<'a, T: Signer + ?Sized> Import<&'a [u8]> for VerifyingKeyWithDefaults<T> {
301    fn import(data: &'a [u8]) -> Result<Self, ImportError> {
302        Ok(Self(T::VerifyingKey::import(data)?))
303    }
304}
305
306/// `Signer::Signature` that uses the default trait methods.
307#[derive_where(Clone, Debug)]
308pub struct SignatureWithDefaults<T: Signer + ?Sized>(T::Signature);
309
310impl<T: Signer + ?Sized> Signature<SignerWithDefaults<T>> for SignatureWithDefaults<T> {
311    type Data = <T::Signature as Signature<T>>::Data;
312
313    fn export(&self) -> Self::Data {
314        self.0.export()
315    }
316}
317
318impl<'a, T: Signer + ?Sized> Import<&'a [u8]> for SignatureWithDefaults<T> {
319    fn import(data: &'a [u8]) -> Result<Self, ImportError> {
320        Ok(Self(T::Signature::import(data)?))
321    }
322}
323
324/// A test [`CipherSuite`].
325pub struct TestCs<A, H, F, K, M, S>(PhantomData<(A, H, F, K, M, S)>);
326
327impl<A, H, F, K, M, S> CipherSuite for TestCs<A, H, F, K, M, S>
328where
329    A: Aead,
330    H: Hash<DigestSize = U32>,
331    F: Kdf,
332    K: Kem,
333    M: Mac,
334    S: Signer,
335{
336    type Aead = A;
337    type Hash = H;
338    type Kdf = F;
339    type Kem = K;
340    type Mac = M;
341    type Signer = S;
342}