Skip to main content

spideroak_crypto/test_util/
mod.rs

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