diff --git a/Cargo.lock b/Cargo.lock index 39c6f00..e8fb877 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1019,6 +1019,7 @@ dependencies = [ "spin", "subtle", "tempfile", + "thiserror", "typenum", "wycheproof", "zeroize", @@ -1101,6 +1102,26 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "thiserror" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + [[package]] name = "typenum" version = "1.17.0" diff --git a/crates/crypto/Cargo.toml b/crates/crypto/Cargo.toml index 711737a..44fef35 100644 --- a/crates/crypto/Cargo.toml +++ b/crates/crypto/Cargo.toml @@ -119,6 +119,7 @@ std = [ "sha3-utils/std", "spin?/std", "subtle/std", + "thiserror/std", "zeroize/std", ] @@ -175,6 +176,7 @@ sha2 = { version = "0.10", default-features = false } sha3-utils = { version = "0.3.0", default-features = false } spin = { workspace = true, default-features = false, features = ["mutex", "once", "spin_mutex"], optional = true } subtle = { version = "2.5", default-features = false, features = ["core_hint_black_box"] } +thiserror = { version = "2", default-features = false } typenum = { version = "1", default-features = false, features = ["const-generics"] } # Only pulled into non-dev builds if `test_util` is enabled. It # won't bloat release builds, though, since users of this crate diff --git a/crates/crypto/src/aead.rs b/crates/crypto/src/aead.rs index 02c47b4..0951d1b 100644 --- a/crates/crypto/src/aead.rs +++ b/crates/crypto/src/aead.rs @@ -27,7 +27,7 @@ pub use crate::hpke::AeadId; use crate::{ csprng::{Csprng, Random}, kdf::{Expand, Kdf, KdfError, Prk}, - keys::{raw_key, SecretKey, SecretKeyBytes}, + keys::{raw_key, FixedLength, SecretKey, SecretKeyBytes}, util::const_assert, zeroize::Zeroize, }; @@ -410,7 +410,7 @@ pub trait Aead { }; /// The key used by the [`Aead`]. - type Key: SecretKey; + type Key: SecretKey + FixedLength; /// Creates a new [`Aead`]. fn new(key: &Self::Key) -> Self; @@ -539,7 +539,7 @@ pub trait Aead { /// Shorthand which the compiler does not understand without /// a good amount of hand holding. -pub type KeyData = SecretKeyBytes<<::Key as SecretKey>::Size>; +pub type KeyData = SecretKeyBytes<<::Key as FixedLength>::Size>; /// An authentication tag. pub type Tag = GenericArray::Overhead>; @@ -1347,15 +1347,18 @@ mod committing { // The nonce length is fixed, so use // HMAC(K || N || A)[1 : k] per Theorem 3.2. let tag = { - let key = $crate::keys::SecretKey::try_export_secret(&self.key)?; - let mut hmac = $crate::hmac::Hmac::<$hash>::new(key.as_bytes()); + let bytes = $crate::keys::SecretKey::try_export_secret(&self.key)?; + let key = $crate::hmac::HmacKey::<$hash>::new( + $crate::keys::RawSecretBytes::raw_secret_bytes(&bytes), + ); + let mut hmac = $crate::hmac::Hmac::<$hash>::new(&key); hmac.update(nonce); hmac.update(ad); hmac.tag() }; let mut key_bytes = $crate::generic_array::GenericArray::< u8, - <<$inner as $crate::aead::Aead>::Key as $crate::keys::SecretKey>::Size, + <<$inner as $crate::aead::Aead>::Key as $crate::keys::FixedLength>::Size, >::default(); let k = ::core::cmp::min(tag.len(), key_bytes.as_slice().len()); key_bytes diff --git a/crates/crypto/src/bearssl.rs b/crates/crypto/src/bearssl.rs index 64901b0..de48b6c 100644 --- a/crates/crypto/src/bearssl.rs +++ b/crates/crypto/src/bearssl.rs @@ -30,15 +30,16 @@ use crate::{ Lifetime, OpenError, SealError, }, asn1::{max_sig_len, raw_sig_len, RawSig, Sig}, - csprng::Csprng, + block::BlockSize, + csprng::{Csprng, Random}, ec::{Curve, Curve25519, Scalar, Secp256r1, Secp384r1, Secp521r1, Uncompressed}, - hash::{Block, Digest, Hash, HashId}, + hash::{Digest, Hash, HashId}, hex::ToHex, hkdf::hkdf_impl, hmac::hmac_impl, import::{ExportError, Import, ImportError}, kem::{dhkem_impl, DecapKey, Ecdh, EcdhError, EncapKey, SharedSecret}, - keys::{PublicKey, SecretKey, SecretKeyBytes}, + keys::{FixedLength, PublicKey, SecretKey, SecretKeyBytes}, signer::{PkError, Signer, SignerError, SignerId, SigningKey, VerifyingKey}, zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing}, }; @@ -403,7 +404,20 @@ macro_rules! ecdh_impl { } impl SecretKey for $sk { - fn new(rng: &mut R) -> Self { + type Secret = SecretKeyBytes<<$curve as Curve>::ScalarSize>; + + #[inline] + fn try_export_secret(&self) -> Result { + Ok(SecretKeyBytes::new(self.kbuf.0.into())) + } + } + + impl FixedLength for $sk { + type Size = <$curve as Curve>::ScalarSize; + } + + impl Random for $sk { + fn random(rng: &mut R) -> Self { // We don't know what `rng` is, so construct our // own. let mut rng = RngWrapper::new(rng); @@ -436,13 +450,6 @@ macro_rules! ecdh_impl { } Self { kbuf } } - - type Size = <$curve as Curve>::ScalarSize; - - #[inline] - fn try_export_secret(&self) -> Result, ExportError> { - Ok(SecretKeyBytes::new(self.kbuf.0.into())) - } } impl ConstantTimeEq for $sk { @@ -458,10 +465,10 @@ macro_rules! ecdh_impl { } } - impl Import::Size>> for $sk { + impl Import::Size>> for $sk { #[inline] fn import( - data: SecretKeyBytes<::Size>, + data: SecretKeyBytes<::Size>, ) -> Result { Self::import(data.as_bytes()) } @@ -703,8 +710,21 @@ macro_rules! ecdsa_impl { } impl SecretKey for $sk { + type Secret = SecretKeyBytes<<$curve as Curve>::ScalarSize>; + #[inline] - fn new(rng: &mut R) -> Self { + fn try_export_secret(&self) -> Result { + Ok(SecretKeyBytes::new(self.kbuf.0.into())) + } + } + + impl FixedLength for $sk { + type Size = <$curve as Curve>::ScalarSize; + } + + impl Random for $sk { + #[inline] + fn random(rng: &mut R) -> Self { // We don't know what `rng` is, so construct our // own. let mut rng = RngWrapper::new(rng); @@ -744,13 +764,6 @@ macro_rules! ecdsa_impl { Self { kbuf } } - - type Size = <$curve as Curve>::ScalarSize; - - #[inline] - fn try_export_secret(&self) -> Result, ExportError> { - Ok(SecretKeyBytes::new(self.kbuf.0.into())) - } } #[cfg(test)] @@ -973,9 +986,6 @@ macro_rules! hash_impl { type DigestSize = U<{ $digest_size as usize }>; const DIGEST_SIZE: usize = $digest_size as usize; - const BLOCK_SIZE: usize = $block_size; - type Block = Block<{ Self::BLOCK_SIZE }>; - #[inline] fn new() -> Self { let mut ctx = $ctx::default(); @@ -1004,6 +1014,10 @@ macro_rules! hash_impl { out } } + + impl BlockSize for $name { + type BlockSize = U<{ $block_size }>; + } }; } hash_impl!( diff --git a/crates/crypto/src/block.rs b/crates/crypto/src/block.rs new file mode 100644 index 0000000..e152eaa --- /dev/null +++ b/crates/crypto/src/block.rs @@ -0,0 +1,17 @@ +//! Operations on blocks. + +#![forbid(unsafe_code)] + +use generic_array::{ArrayLength, GenericArray}; + +/// Implemented by types that operate on blocks. +/// +/// For example, block ciphers or the Merkle-Damgård +/// construction. +pub trait BlockSize { + /// The size in bytes of the block. + type BlockSize: ArrayLength; +} + +/// A block. +pub type Block = GenericArray::BlockSize>; diff --git a/crates/crypto/src/ed25519.rs b/crates/crypto/src/ed25519.rs index 6145f53..99f8e02 100644 --- a/crates/crypto/src/ed25519.rs +++ b/crates/crypto/src/ed25519.rs @@ -16,12 +16,12 @@ use subtle::{Choice, ConstantTimeEq}; use typenum::U32; use crate::{ - csprng::Csprng, + csprng::{Csprng, Random}, hex::ToHex, import::{try_import, ExportError, Import, ImportError}, - keys::{PublicKey, SecretKey, SecretKeyBytes}, + keys::{FixedLength, PublicKey, SecretKey, SecretKeyBytes}, signer::{self, PkError, Signer, SignerError, SignerId}, - zeroize::{ZeroizeOnDrop, Zeroizing}, + zeroize::ZeroizeOnDrop, }; /// EdDSA using Ed25519. @@ -68,14 +68,6 @@ impl signer::SigningKey for SigningKey { } impl SecretKey for SigningKey { - type Size = U32; - - fn new(rng: &mut R) -> Self { - let mut sk = dalek::SecretKey::default(); - rng.fill_bytes(&mut sk); - Self(dalek::SigningKey::from_bytes(&sk)) - } - type Secret = SecretKeyBytes; #[inline] @@ -84,11 +76,21 @@ impl SecretKey for SigningKey { } } +impl FixedLength for SigningKey { + type Size = U32; +} + +impl Random for SigningKey { + fn random(rng: &mut R) -> Self { + let mut sk = dalek::SecretKey::default(); + rng.fill_bytes(&mut sk); + Self(dalek::SigningKey::from_bytes(&sk)) + } +} + impl ConstantTimeEq for SigningKey { fn ct_eq(&self, other: &Self) -> Choice { - let lhs = Zeroizing::new(self.0.to_bytes()); - let rhs = Zeroizing::new(other.0.to_bytes()); - ConstantTimeEq::ct_eq(lhs.as_ref(), rhs.as_ref()) + self.0.ct_eq(&other.0) } } diff --git a/crates/crypto/src/hash.rs b/crates/crypto/src/hash.rs index ddda652..4a7e63d 100644 --- a/crates/crypto/src/hash.rs +++ b/crates/crypto/src/hash.rs @@ -3,7 +3,6 @@ #![forbid(unsafe_code)] use core::{ - borrow::{Borrow, BorrowMut}, fmt::{self, Debug}, num::NonZeroU16, ops::{Deref, DerefMut}, @@ -66,12 +65,6 @@ pub trait Hash: Clone { /// Shorthand for [`DigestSize`][Self::DigestSize]. const DIGEST_SIZE: usize = Self::DigestSize::USIZE; - /// The size in bytes of a [`Self::Block`]. - const BLOCK_SIZE: usize; - - /// An individual block. - type Block: Borrow<[u8]> + BorrowMut<[u8]> + Default + Clone; - /// Creates a new [`Hash`]. fn new() -> Self; @@ -199,31 +192,6 @@ impl ConstantTimeEq for Digest { } } -/// An hash function block. -#[derive(Clone)] -pub struct Block([u8; N]); - -impl Default for Block { - #[inline] - fn default() -> Self { - Self([0u8; N]) - } -} - -impl Borrow<[u8]> for Block { - #[inline] - fn borrow(&self) -> &[u8] { - self.0.borrow() - } -} - -impl BorrowMut<[u8]> for Block { - #[inline] - fn borrow_mut(&mut self) -> &mut [u8] { - self.0.borrow_mut() - } -} - /// A cryptographic hash over a set of strings such that each /// element is unambiguously encoded per [NIST SP 800-185]. /// diff --git a/crates/crypto/src/hkdf.rs b/crates/crypto/src/hkdf.rs index 42419e2..184ee50 100644 --- a/crates/crypto/src/hkdf.rs +++ b/crates/crypto/src/hkdf.rs @@ -11,8 +11,9 @@ use generic_array::GenericArray; use typenum::{Prod, U255}; use crate::{ + block::BlockSize, hash::Hash, - hmac::{Hmac, Tag}, + hmac::{Hmac, HmacKey, Tag}, kdf::{KdfError, Prk}, keys::SecretKeyBytes, }; @@ -23,7 +24,7 @@ pub type MaxOutput = Prod; /// HKDF for some hash `H`. pub struct Hkdf(PhantomData); -impl Hkdf { +impl Hkdf { /// The maximum nuumber of bytes that can be expanded by /// [`Self::expand`] and [`Self::expand_multi`]. pub const MAX_OUTPUT: usize = 255 * H::DIGEST_SIZE; @@ -55,11 +56,15 @@ impl Hkdf { // salt: optional salt value (a non-secret random value); // if not provided, it is set to a string of HashLen // zeros. - let zero = GenericArray::::default(); - let salt = if salt.is_empty() { &zero } else { salt }; + let salt = if salt.is_empty() { + let zero = GenericArray::::default(); + HmacKey::new(zero.as_slice()) + } else { + HmacKey::new(salt) + }; // PRK = HMAC-Hash(salt, IKM) - let prk = Hmac::::mac_multi(salt, ikm).into_array(); + let prk = Hmac::::mac_multi(&salt, ikm).into_array(); Prk::new(SecretKeyBytes::new(prk)) } @@ -110,7 +115,8 @@ impl Hkdf { return Err(KdfError::OutputTooLong); } - let expander = Hmac::::new(prk.as_bytes()); + let key = HmacKey::::new(prk.as_bytes()); + let expander = Hmac::::new(&key); let info = info.into_iter(); let mut prev: Option> = None; @@ -138,9 +144,10 @@ impl Hkdf { /// /// ```rust /// use spideroak_crypto::{ -/// hash::{Block, Digest, Hash, HashId}, +/// block::BlockSize, +/// hash::{Digest, Hash, HashId}, /// hkdf_impl, -/// typenum::U32, +/// typenum::{U32, U64}, /// }; /// /// #[derive(Clone)] @@ -149,8 +156,6 @@ impl Hkdf { /// impl Hash for Sha256 { /// const ID: HashId = HashId::Sha256; /// type DigestSize = U32; -/// type Block = Block<64>; -/// const BLOCK_SIZE: usize = 64; /// fn new() -> Self { /// Self /// } @@ -162,6 +167,10 @@ impl Hkdf { /// } /// } /// +/// impl BlockSize for Sha256 { +/// type BlockSize = U64; +/// } +/// /// hkdf_impl!(HkdfSha256, "HMAC-SHA-256", Sha256); /// ``` #[macro_export] diff --git a/crates/crypto/src/hmac.rs b/crates/crypto/src/hmac.rs index 48bb5b4..457f8aa 100644 --- a/crates/crypto/src/hmac.rs +++ b/crates/crypto/src/hmac.rs @@ -2,23 +2,20 @@ //! //! [FIPS PUB 198-1]: https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.198-1.pdf -use core::{ - borrow::{Borrow, BorrowMut}, - cmp, - marker::PhantomData, - mem, -}; +#![forbid(unsafe_code)] + +use core::cmp; use generic_array::{ArrayLength, GenericArray, LengthError}; use subtle::{Choice, ConstantTimeEq}; -use typenum::Unsigned; use crate::{ - csprng::Csprng, + block::{Block, BlockSize}, + csprng::{Csprng, Random}, hash::{Digest, Hash}, - import::{ExportError, Import, ImportError, InvalidSizeError}, - keys::{RawSecretBytes, SecretKey, SecretKeyBytes}, - zeroize::ZeroizeOnDrop, + import::{ExportError, Import, ImportError}, + keys::{FixedLength, SecretKey, SecretKeyBytes}, + zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing}, }; /// HMAC per [FIPS PUB 198-1] for some hash `H`. @@ -32,37 +29,24 @@ pub struct Hmac { opad: H, } -impl Hmac { +impl Hmac { /// Creates an HMAC using the provided `key`. - pub fn new(key: &[u8]) -> Self { - let mut key = { - let mut tmp = H::Block::default(); - let tmp_len = tmp.borrow().len(); - if key.len() <= tmp_len { - // Steps 1 and 3 - tmp.borrow_mut()[..key.len()].copy_from_slice(key); - } else { - // Step 2 - let d = H::hash(key); - let n = cmp::min(d.len(), tmp_len); - tmp.borrow_mut()[..n].copy_from_slice(&d[..n]); - }; - tmp - }; + pub fn new(key: &HmacKey) -> Self { + let mut key = Zeroizing::new(key.0.clone()); // Step 4: K_0 ^ ipad (0x36) - for v in key.borrow_mut() { + for v in key.iter_mut() { *v ^= 0x36; } let mut ipad = H::new(); - ipad.update(key.borrow()); + ipad.update(key.as_slice()); // Step 7: K_0 ^ opad (0x5c) - for v in key.borrow_mut() { + for v in key.iter_mut() { *v ^= 0x36 ^ 0x5c; } let mut opad = H::new(); - opad.update(key.borrow()); + opad.update(key.as_slice()); Self { ipad, opad } } @@ -83,7 +67,7 @@ impl Hmac { } /// Computes the single-shot tag from `data` using `key`. - pub fn mac_multi(key: &[u8], data: I) -> Tag + pub fn mac_multi(key: &HmacKey, data: I) -> Tag where I: IntoIterator, I::Item: AsRef<[u8]>, @@ -167,52 +151,74 @@ impl<'a, N: ArrayLength> TryFrom<&'a [u8]> for Tag { /// An [`Hmac`] key. #[repr(transparent)] -pub struct HmacKey { - _l: PhantomData, - key: [u8], +pub struct HmacKey(Block); + +impl HmacKey { + /// Creates an `HmacKey`. + pub fn new(key: &[u8]) -> Self { + let mut out = Block::::default(); + if key.len() <= out.len() { + // Steps 1 and 3 + out[..key.len()].copy_from_slice(key); + } else { + // Step 2 + let d = H::hash(key); + let n = cmp::min(d.len(), out.len()); + out[..n].copy_from_slice(&d[..n]); + }; + Self(out) + } } -impl<'a, L: ArrayLength> SecretKey for HmacKey { - type Size = L; - fn new(_rng: &mut R) -> Self { - todo!() +impl Clone for HmacKey { + #[inline] + fn clone(&self) -> Self { + Self(self.0.clone()) } +} - type Secret = &'a [u8]; +impl SecretKey for HmacKey { + type Secret = SecretKeyBytes; + + /// Exports the raw key. + /// + /// Note: this returns `h(k)` if the original `k` passed to + /// [`HmacKey::new`] was longer than the block size of `h`. + #[inline] fn try_export_secret(&self) -> Result { - Ok(&self.key) + Ok(SecretKeyBytes::new(self.0.clone())) } } -impl Import<&[u8]> for &HmacKey { +impl FixedLength for HmacKey { + type Size = H::BlockSize; +} + +impl Random for HmacKey { + fn random(rng: &mut R) -> Self { + Self(Block::::random(rng)) + } +} + +impl Import<&[u8]> for HmacKey { + #[inline] fn import(data: &[u8]) -> Result { - if data.len() < L::USIZE { - Err(ImportError::InvalidSize(InvalidSizeError { - got: data.len(), - // TODO(eric): `usize::MAX` is not the correct - // upper bound here. - want: L::USIZE..usize::MAX, - })) - } else { - // SAFETY: `&[u8]` and `Self` have the same layout in - // memory. - let key = unsafe { mem::transmute::<&[u8], Self>(data) }; - Ok(key) - } + Ok(Self::new(data)) } } -impl ConstantTimeEq for &HmacKey { +impl ConstantTimeEq for HmacKey { #[inline] fn ct_eq(&self, other: &Self) -> Choice { - self.key.ct_eq(&other.key) + self.0.ct_eq(&other.0) } } -impl ZeroizeOnDrop for HmacKey {} -impl Drop for HmacKey { +impl ZeroizeOnDrop for HmacKey {} +impl Drop for HmacKey { + #[inline] fn drop(&mut self) { - // TODO + self.0.zeroize(); } } @@ -222,9 +228,10 @@ impl Drop for HmacKey { /// /// ```rust /// use spideroak_crypto::{ -/// hash::{Block, Digest, Hash, HashId}, +/// block::BlockSize, +/// hash::{Digest, Hash, HashId}, /// hmac_impl, -/// typenum::U32, +/// typenum::{U32, U64}, /// }; /// /// #[derive(Clone)] @@ -233,8 +240,6 @@ impl Drop for HmacKey { /// impl Hash for Sha256 { /// const ID: HashId = HashId::Sha256; /// type DigestSize = U32; -/// type Block = Block<64>; -/// const BLOCK_SIZE: usize = 64; /// fn new() -> Self { /// Self /// } @@ -246,7 +251,11 @@ impl Drop for HmacKey { /// } /// } /// -/// hmac_impl!(HmacSha256, "HMAC-SHA-256", Sha256, HmacSha256Key); +/// impl BlockSize for Sha256 { +/// type BlockSize = U64; +/// } +/// +/// hmac_impl!(HmacSha256, "HMAC-SHA-256", Sha256); /// ``` #[macro_export] macro_rules! hmac_impl { @@ -258,17 +267,19 @@ macro_rules! hmac_impl { impl $crate::mac::Mac for $name { const ID: $crate::mac::MacId = $crate::mac::MacId::$name; - // Setting len(K) = L ensures that we're always in - // [L, B]. - type Key = $crate::hmac::HmacKey; - type KeySize = <$hash as $crate::hash::Hash>::DigestSize; type Tag = $crate::hmac::Tag; type TagSize = <$hash as $crate::hash::Hash>::DigestSize; + type Key = $crate::hmac::HmacKey<$hash>; + type MinKeySize = <$hash as $crate::hash::Hash>::DigestSize; + fn new(key: &Self::Key) -> Self { - let key = $crate::keys::RawSecretBytes::raw_secret_bytes(key); Self($crate::hmac::Hmac::new(key)) } + fn try_new(key: &[u8]) -> ::core::result::Result { + let key = $crate::hmac::HmacKey::<$hash>::new(key); + Ok(Self::new(&key)) + } fn update(&mut self, data: &[u8]) { self.0.update(data) @@ -289,9 +300,9 @@ mod tests { () => { use crate::test_util::test_mac; - hmac_impl!(HmacSha256, "HMAC-SHA256", Sha256, HmacSha256Key); - hmac_impl!(HmacSha384, "HMAC-SHA384", Sha384, HmacSha384Key); - hmac_impl!(HmacSha512, "HMAC-SHA512", Sha512, HmacSha512Key); + hmac_impl!(HmacSha256, "HMAC-SHA256", Sha256); + hmac_impl!(HmacSha384, "HMAC-SHA384", Sha384); + hmac_impl!(HmacSha512, "HMAC-SHA512", Sha512); test_mac!(hmac_sha256, HmacSha256, MacTest::HmacSha256); test_mac!(hmac_sha384, HmacSha384, MacTest::HmacSha384); diff --git a/crates/crypto/src/hpke.rs b/crates/crypto/src/hpke.rs index 2eb58a8..0371db5 100644 --- a/crates/crypto/src/hpke.rs +++ b/crates/crypto/src/hpke.rs @@ -646,7 +646,10 @@ pub struct SendCtx { export: ExportCtx, } -impl SendCtx { +impl SendCtx +where + A::Key: for<'a> Import<&'a [u8]>, +{ /// The size in bytes of the overhead added to the plaintext. pub const OVERHEAD: usize = SealCtx::::OVERHEAD; @@ -689,7 +692,9 @@ impl SendCtx { ) -> Result { self.seal_ctx()?.seal_in_place(data, tag, additional_data) } +} +impl SendCtx { /// Exports a secret from the encryption context. pub fn export(&self, context: &[u8]) -> Result where @@ -726,7 +731,10 @@ impl SealCtx { key: &KeyData, base_nonce: &Nonce, seq: Seq, - ) -> Result { + ) -> Result + where + A::Key: for<'a> Import<&'a [u8]>, + { let key = A::Key::import(key.as_bytes())?; Ok(Self { aead: A::new(&key), @@ -789,7 +797,10 @@ pub struct RecvCtx { export: ExportCtx, } -impl RecvCtx { +impl RecvCtx +where + A::Key: for<'a> Import<&'a [u8]>, +{ /// The size in bytes of the overhead added to the plaintext. pub const OVERHEAD: usize = OpenCtx::::OVERHEAD; @@ -861,7 +872,9 @@ impl RecvCtx { self.open_ctx()? .open_in_place_at(data, tag, additional_data, seq) } +} +impl RecvCtx { /// Exports a secret from the encryption context. pub fn export(&self, context: &[u8]) -> Result where @@ -898,7 +911,10 @@ impl OpenCtx { key: &KeyData, base_nonce: &Nonce, seq: Seq, - ) -> Result { + ) -> Result + where + A::Key: for<'a> Import<&'a [u8]>, + { let key = A::Key::import(key.as_bytes())?; Ok(Self { aead: A::new(&key), diff --git a/crates/crypto/src/import.rs b/crates/crypto/src/import.rs index 8290253..5903c86 100644 --- a/crates/crypto/src/import.rs +++ b/crates/crypto/src/import.rs @@ -165,6 +165,14 @@ pub trait Import: Sized { fn import(data: T) -> Result; } +/// TODO +pub trait Export { + /// TODO + type Secret; + /// TODO + fn try_export_secret(&self) -> Result; +} + /// An error that occurs while exporting secret key material. #[derive(Debug, Eq, PartialEq)] pub enum ExportError { diff --git a/crates/crypto/src/kem.rs b/crates/crypto/src/kem.rs index 75df7c0..3ac669f 100644 --- a/crates/crypto/src/kem.rs +++ b/crates/crypto/src/kem.rs @@ -13,10 +13,10 @@ use core::{ #[doc(inline)] pub use crate::hpke::KemId; use crate::{ - csprng::Csprng, + csprng::{Csprng, Random}, import::{Import, ImportError}, kdf::{Kdf, KdfError, Prk}, - keys::{PublicKey, SecretKey}, + keys::{FixedLength, PublicKey, SecretKey}, signer::PkError, zeroize::ZeroizeOnDrop, }; @@ -191,7 +191,7 @@ pub trait Kem { } /// An asymmetric private key used to decapsulate keys. -pub trait DecapKey: SecretKey { +pub trait DecapKey: SecretKey + FixedLength + Random { /// The corresponding public key. type EncapKey: EncapKey; @@ -350,7 +350,7 @@ impl DhKem { rng: &mut R, pkR: &E::PublicKey, ) -> Result<(Prk, PubKeyData), KemError> { - let skE = E::PrivateKey::new(rng); + let skE = E::PrivateKey::random(rng); self.encap_deterministically(pkR, skE) } @@ -413,7 +413,7 @@ impl DhKem { pkR: &E::PublicKey, skS: &E::PrivateKey, ) -> Result<(Prk, PubKeyData), KemError> { - let skE = E::PrivateKey::new(rng); + let skE = E::PrivateKey::random(rng); self.auth_encap_deterministically(pkR, skS, skE) } diff --git a/crates/crypto/src/keys.rs b/crates/crypto/src/keys.rs index cd0cba7..1ac3e0c 100644 --- a/crates/crypto/src/keys.rs +++ b/crates/crypto/src/keys.rs @@ -4,7 +4,7 @@ use core::{borrow::Borrow, fmt::Debug, iter::IntoIterator, mem, result::Result}; use generic_array::{ArrayLength, GenericArray, IntoArrayLength}; use subtle::{Choice, ConstantTimeEq}; -use typenum::{generic_const_mappings::Const, IsLess, Unsigned, U65536}; +use typenum::{generic_const_mappings::Const, IsLess, U65536}; use crate::{ csprng::{Csprng, Random}, @@ -17,18 +17,7 @@ use crate::{ /// /// Secret keys are either symmetric keys (e.g., for AES) or /// asymmetric private keys (e.g., for ECDH). -pub trait SecretKey: Clone + ConstantTimeEq + for<'a> Import<&'a [u8]> + ZeroizeOnDrop { - /// Creates a random key, possibly using entropy from `rng`. - /// - /// Implementations are free to ignore `rng` and callers must - /// not rely on this function reading from `rng`. - fn new(rng: &mut R) -> Self; - - /// The size of the key. - type Size: ArrayLength + 'static; - /// Shorthand for [`Size`][Self::Size]; - const SIZE: usize = Self::Size::USIZE; - +pub trait SecretKey: Clone + ConstantTimeEq + ZeroizeOnDrop { /// The raw secret. type Secret: RawSecretBytes; @@ -36,6 +25,12 @@ pub trait SecretKey: Clone + ConstantTimeEq + for<'a> Import<&'a [u8]> + Zeroize fn try_export_secret(&self) -> Result; } +/// A fixed-length secret key. +pub trait FixedLength: SecretKey> { + /// The size in octets of the key. + type Size: ArrayLength + 'static; +} + /// Provides access to a secret's byte encoding. pub trait RawSecretBytes { /// Returns the secret's byte encoding. @@ -45,7 +40,7 @@ pub trait RawSecretBytes { impl RawSecretBytes for &T { #[inline] fn raw_secret_bytes(&self) -> &[u8] { - (&**self).raw_secret_bytes() + (**self).raw_secret_bytes() } } @@ -224,15 +219,8 @@ macro_rules! raw_key { } } - impl $crate::keys::SecretKey for $name - { + impl $crate::keys::SecretKey for $name { type Secret = $crate::keys::SecretKeyBytes; - type Size = N; - - #[inline] - fn new(rng: &mut R) -> Self { - Self($crate::csprng::Random::random(rng)) - } #[inline] fn try_export_secret(&self) -> ::core::result::Result< @@ -243,6 +231,10 @@ macro_rules! raw_key { } } + impl $crate::keys::FixedLength for $name { + type Size = N; + } + impl $crate::csprng::Random for $name { fn random(rng: &mut R) -> Self { let sk = <$crate::keys::SecretKeyBytes as $crate::csprng::Random>::random(rng); @@ -309,3 +301,9 @@ macro_rules! raw_key { }; } pub(crate) use raw_key; + +/// The provided key is invalid. +// TODO(eric): move this somewhere else. +#[derive(Copy, Clone, Debug, Eq, PartialEq, thiserror::Error)] +#[error("invalid key length")] +pub struct InvalidKey(()); diff --git a/crates/crypto/src/lib.rs b/crates/crypto/src/lib.rs index 77feed9..9693171 100644 --- a/crates/crypto/src/lib.rs +++ b/crates/crypto/src/lib.rs @@ -17,6 +17,7 @@ pub(crate) use spideroak_crypto_derive::AlgId; pub mod aead; pub mod asn1; pub mod bearssl; +pub mod block; pub mod csprng; pub mod default; pub mod ec; diff --git a/crates/crypto/src/mac.rs b/crates/crypto/src/mac.rs index 09d37c8..5c13d96 100644 --- a/crates/crypto/src/mac.rs +++ b/crates/crypto/src/mac.rs @@ -11,11 +11,11 @@ use generic_array::{ArrayLength, GenericArray}; use subtle::{Choice, ConstantTimeEq}; use typenum::{ type_operators::{IsGreaterOrEqual, IsLess}, - U16, U32, U48, U64, U65536, + U32, U48, U64, U65536, }; use crate::{ - keys::{raw_key, SecretKey}, + keys::{raw_key, InvalidKey, SecretKey}, AlgId, }; @@ -79,19 +79,23 @@ pub trait Mac: Clone + Sized { type Tag: ConstantTimeEq; /// The size in octets of a tag used by this [`Mac`]. /// - /// Must be at least 32 octets and less than 2³² octets. + /// Must be at least 32 octets and less than 2¹⁶ octets. type TagSize: ArrayLength + IsGreaterOrEqual + IsLess + 'static; /// The key used by the [`Mac`]. - type Key: SecretKey; - /// The size in octets of a key used by this [`Mac`]. + type Key: SecretKey; + /// The minimum allowed size in octets of a key used by this + /// [`Mac`]. /// - /// Must be at least 16 octets and less than 2¹⁶ octets. - type KeySize: ArrayLength + IsGreaterOrEqual + IsLess + 'static; + /// Must be at least 32 octets and less than 2¹⁶ octets. + type MinKeySize: ArrayLength + IsGreaterOrEqual + IsLess + 'static; /// Creates a new [`Mac`]. fn new(key: &Self::Key) -> Self; + /// Attempts to create a new [`Mac`]. + fn try_new(key: &[u8]) -> Result; + /// Adds `data` to the running tag. fn update(&mut self, data: &[u8]); diff --git a/crates/crypto/src/rust.rs b/crates/crypto/src/rust.rs index 6fd358e..1cdcbd7 100644 --- a/crates/crypto/src/rust.rs +++ b/crates/crypto/src/rust.rs @@ -29,15 +29,16 @@ use crate::{ check_open_in_place_params, check_seal_in_place_params, Aead, AeadId, AeadKey, IndCca2, Lifetime, OpenError, SealError, }, - csprng::Csprng, + block::BlockSize, + csprng::{Csprng, Random}, ec::{Curve, Secp256r1, Secp384r1}, - hash::{Block, Digest, Hash, HashId}, + hash::{Digest, Hash, HashId}, hex, hkdf::hkdf_impl, hmac::hmac_impl, import::{try_from_slice, ExportError, Import, ImportError}, kem::{dhkem_impl, DecapKey, Ecdh, EcdhError, EncapKey}, - keys::{PublicKey, SecretKey, SecretKeyBytes}, + keys::{FixedLength, PublicKey, SecretKey, SecretKeyBytes}, signer::{Signature, Signer, SignerError, SignerId, SigningKey, VerifyingKey}, zeroize::ZeroizeOnDrop, }; @@ -232,14 +233,6 @@ macro_rules! ecdh_impl { } impl SecretKey for $sk { - type Size = FieldBytesSize<$curve>; - - #[inline] - fn new(rng: &mut R) -> Self { - let sk = NonZeroScalar::random(&mut RngWrapper(rng)); - Self(sk) - } - type Secret = SecretKeyBytes>; #[inline] @@ -250,6 +243,18 @@ macro_rules! ecdh_impl { } } + impl FixedLength for $sk { + type Size = FieldBytesSize<$curve>; + } + + impl Random for $sk { + #[inline] + fn random(rng: &mut R) -> Self { + let sk = NonZeroScalar::random(&mut RngWrapper(rng)); + Self(sk) + } + } + #[cfg(test)] impl fmt::Debug for $sk { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -371,14 +376,6 @@ macro_rules! ecdsa_impl { } impl SecretKey for $sk { - type Size = FieldBytesSize<$curve>; - - #[inline] - fn new(rng: &mut R) -> Self { - let sk = ecdsa::SigningKey::random(&mut RngWrapper(rng)); - Self(sk) - } - type Secret = SecretKeyBytes>; #[inline] @@ -389,6 +386,18 @@ macro_rules! ecdsa_impl { } } + impl FixedLength for $sk { + type Size = FieldBytesSize<$curve>; + } + + impl Random for $sk { + #[inline] + fn random(rng: &mut R) -> Self { + let sk = ecdsa::SigningKey::random(&mut RngWrapper(rng)); + Self(sk) + } + } + #[cfg(test)] impl fmt::Debug for $sk { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -508,9 +517,6 @@ macro_rules! hash_impl { const DIGEST_SIZE: usize = <::OutputSize as Unsigned>::USIZE; - const BLOCK_SIZE: usize = ::BlockSize::USIZE; - type Block = Block<{ Self::BLOCK_SIZE }>; - #[inline] fn new() -> Self { Self(::new()) @@ -531,6 +537,10 @@ macro_rules! hash_impl { Digest::from_array(::digest(data).into()) } } + + impl BlockSize for $name { + type BlockSize = ::BlockSize; + } }; } hash_impl!(Sha256, "SHA-256"); diff --git a/crates/crypto/src/signer.rs b/crates/crypto/src/signer.rs index e86dbbd..fea80ac 100644 --- a/crates/crypto/src/signer.rs +++ b/crates/crypto/src/signer.rs @@ -13,8 +13,9 @@ use buggy::Bug; use crate::{ asn1::EncodingError, + csprng::Random, import::Import, - keys::{PublicKey, SecretKey}, + keys::{FixedLength, PublicKey, SecretKey}, AlgId, }; @@ -148,7 +149,7 @@ pub trait Signer { } /// An asymmetric secret key used to create digital signatures. -pub trait SigningKey: SecretKey { +pub trait SigningKey: SecretKey + FixedLength + Random { /// Returns the signature over `msg`, which must NOT be /// pre-hashed. fn sign(&self, msg: &[u8]) -> Result; diff --git a/crates/crypto/src/test_util/aead.rs b/crates/crypto/src/test_util/aead.rs index f8ef790..b9a8cad 100644 --- a/crates/crypto/src/test_util/aead.rs +++ b/crates/crypto/src/test_util/aead.rs @@ -9,8 +9,7 @@ use more_asserts::assert_ge; use super::{assert_all_zero, assert_ct_ne}; use crate::{ aead::{Aead, Nonce, OpenError}, - csprng::Csprng, - keys::SecretKey, + csprng::{Csprng, Random}, }; /// Invokes `callback` for each AEAD test. @@ -103,7 +102,12 @@ const GOLDEN: &[u8] = b"hello, world!"; const AD: &[u8] = b"some additional data"; /// A basic positive test. -pub fn test_basic(_rng: &mut R) { +pub fn test_basic(_rng: &mut R) +where + A: Aead, + A::Key: Random, + R: Csprng, +{ // The minimum key size is 128 bits. assert_ge!(A::KEY_SIZE, 16); // Must be at least 2^32-1. @@ -118,15 +122,25 @@ pub fn test_basic(_rng: &mut R) { } /// Tests that `Aead::Key::new` returns unique keys. -pub fn test_new_key(rng: &mut R) { - let k1 = A::Key::new(rng); - let k2 = A::Key::new(rng); +pub fn test_new_key(rng: &mut R) +where + A: Aead, + A::Key: Random, + R: Csprng, +{ + let k1 = A::Key::random(rng); + let k2 = A::Key::random(rng); assert_ct_ne!(k1, k2); } /// A round-trip positive test. -pub fn test_round_trip(rng: &mut R) { - let key = A::Key::new(rng); +pub fn test_round_trip(rng: &mut R) +where + A: Aead, + A::Key: Random, + R: Csprng, +{ + let key = A::Key::random(rng); let nonce = Nonce::::default(); assert_all_zero!(nonce); @@ -149,8 +163,13 @@ pub fn test_round_trip(rng: &mut R) { } /// An in-place round-trip positive test. -pub fn test_in_place_round_trip(rng: &mut R) { - let key = A::Key::new(rng); +pub fn test_in_place_round_trip(rng: &mut R) +where + A: Aead, + A::Key: Random, + R: Csprng, +{ + let key = A::Key::random(rng); let nonce = Nonce::::default(); assert_all_zero!(nonce); @@ -176,12 +195,17 @@ pub fn test_in_place_round_trip(rng: &mut R) { } /// Decryption should fail with an incorrect key. -pub fn test_bad_key(rng: &mut R) { +pub fn test_bad_key(rng: &mut R) +where + A: Aead, + A::Key: Random, + R: Csprng, +{ let nonce = Nonce::::default(); assert_all_zero!(nonce); let ciphertext = { - let key = A::Key::new(rng); + let key = A::Key::random(rng); let mut dst = vec![0u8; GOLDEN.len() + A::OVERHEAD]; A::new(&key) @@ -190,7 +214,7 @@ pub fn test_bad_key(rng: &mut R) { dst }; - let key = A::Key::new(rng); + let key = A::Key::random(rng); let mut dst = vec![0u8; ciphertext.len() - A::OVERHEAD]; let err = A::new(&key) .open(&mut dst[..], nonce.as_ref(), &ciphertext, AD) @@ -199,8 +223,13 @@ pub fn test_bad_key(rng: &mut R) { } /// Decryption should fail with an incorrect nonce. -pub fn test_bad_nonce(rng: &mut R) { - let key = A::Key::new(rng); +pub fn test_bad_nonce(rng: &mut R) +where + A: Aead, + A::Key: Random, + R: Csprng, +{ + let key = A::Key::random(rng); let ciphertext = { let mut nonce = Nonce::::default(); @@ -226,8 +255,13 @@ pub fn test_bad_nonce(rng: &mut R) { } /// Decryption should fail with a modified AD. -pub fn test_bad_ad(rng: &mut R) { - let key = A::Key::new(rng); +pub fn test_bad_ad(rng: &mut R) +where + A: Aead, + A::Key: Random, + R: Csprng, +{ + let key = A::Key::random(rng); let nonce = Nonce::::default(); assert_all_zero!(nonce); @@ -247,8 +281,13 @@ pub fn test_bad_ad(rng: &mut R) { } /// Decryption should fail with a modified ciphertext. -pub fn test_bad_ciphertext(rng: &mut R) { - let key = A::Key::new(rng); +pub fn test_bad_ciphertext(rng: &mut R) +where + A: Aead, + A::Key: Random, + R: Csprng, +{ + let key = A::Key::random(rng); let nonce = Nonce::::default(); assert_all_zero!(nonce); @@ -271,8 +310,13 @@ pub fn test_bad_ciphertext(rng: &mut R) { /// Decryption should fail with a modified authentication /// tag. -pub fn test_bad_tag(rng: &mut R) { - let key = A::Key::new(rng); +pub fn test_bad_tag(rng: &mut R) +where + A: Aead, + A::Key: Random, + R: Csprng, +{ + let key = A::Key::random(rng); let nonce = Nonce::::default(); assert_all_zero!(nonce); diff --git a/crates/crypto/src/test_util/hpke.rs b/crates/crypto/src/test_util/hpke.rs index 9cc15cd..231f109 100644 --- a/crates/crypto/src/test_util/hpke.rs +++ b/crates/crypto/src/test_util/hpke.rs @@ -9,11 +9,11 @@ use typenum::U64; use crate::{ aead::{Aead, IndCca2}, - csprng::Csprng, + csprng::{Csprng, Random}, hpke::{Hpke, Mode, OpenCtx, SealCtx}, + import::Import, kdf::{Expand, Kdf, KdfError, Prk}, kem::{DecapKey, Kem}, - keys::SecretKey, }; /// Invokes `callback` for each HPKE test. @@ -125,12 +125,15 @@ pub use test_hpke; /// Tests the full encryption-decryption cycle. #[allow(non_snake_case)] -pub fn test_round_trip(rng: &mut R) { +pub fn test_round_trip(rng: &mut R) +where + A::Key: for<'a> Import<&'a [u8]>, +{ const GOLDEN: &[u8] = b"some plaintext"; const AD: &[u8] = b"some additional data"; const INFO: &[u8] = b"some contextual binding"; - let skR = K::DecapKey::new(rng); + let skR = K::DecapKey::random(rng); let pkR = skR.public().expect("encap key should be valid"); let (enc, mut send) = Hpke::::setup_send(rng, Mode::Base, &pkR, INFO) @@ -160,7 +163,7 @@ pub fn test_round_trip(rng: &mut R pub fn test_export(rng: &mut R) { const INFO: &[u8] = b"some contextual binding"; - let skR = K::DecapKey::new(rng); + let skR = K::DecapKey::random(rng); let pkR = skR.public().expect("encap key should be valid"); let (enc, send) = Hpke::::setup_send(rng, Mode::Base, &pkR, INFO) diff --git a/crates/crypto/src/test_util/mac.rs b/crates/crypto/src/test_util/mac.rs index e1343bf..2ba4405 100644 --- a/crates/crypto/src/test_util/mac.rs +++ b/crates/crypto/src/test_util/mac.rs @@ -1,7 +1,12 @@ //! [`Mac`] tests. +use subtle::ConstantTimeEq; + use super::{assert_ct_eq, assert_ct_ne}; -use crate::{csprng::Csprng, keys::SecretKey, mac::Mac}; +use crate::{ + csprng::{Csprng, Random}, + mac::Mac, +}; /// Invokes `callback` for each MAC test. /// @@ -84,16 +89,26 @@ pub use test_mac; const DATA: &[u8] = b"hello, world!"; /// Basic positive test. -pub fn test_default(rng: &mut R) { - let key = T::Key::new(rng); +pub fn test_default(rng: &mut R) +where + T: Mac, + T::Key: Random, + R: Csprng, +{ + let key = Random::random(rng); let tag1 = T::mac(&key, DATA); let tag2 = T::mac(&key, DATA); assert_ct_eq!(tag1, tag2, "tags should be the same"); } /// Tests that [`Mac::update`] is the same as [`Mac::mac`]. -pub fn test_update(rng: &mut R) { - let key = T::Key::new(rng); +pub fn test_update(rng: &mut R) +where + T: Mac, + T::Key: Random, + R: Csprng, +{ + let key = Random::random(rng); let tag1 = T::mac(&key, DATA); let tag2 = { let mut h = T::new(&key); @@ -106,8 +121,13 @@ pub fn test_update(rng: &mut R) { } /// Test [`Mac::verify`]. -pub fn test_verify(rng: &mut R) { - let key = T::Key::new(rng); +pub fn test_verify(rng: &mut R) +where + T: Mac, + T::Key: Random, + R: Csprng, +{ + let key = Random::random(rng); let tag1 = T::mac(&key, DATA); let mut h = T::new(&key); @@ -118,9 +138,14 @@ pub fn test_verify(rng: &mut R) { } /// Negative tests for different keys. -pub fn test_different_keys(rng: &mut R) { - let key1 = T::Key::new(rng); - let key2 = T::Key::new(rng); +pub fn test_different_keys(rng: &mut R) +where + T: Mac, + T::Key: ConstantTimeEq + Random, + R: Csprng, +{ + let key1 = Random::random(rng); + let key2 = Random::random(rng); assert_ct_ne!(key1, key2, "keys should differ"); let tag1 = T::mac(&key1, DATA); @@ -129,8 +154,13 @@ pub fn test_different_keys(rng: &mut R) { } /// Negative test for MACs of different data. -pub fn test_different_data(rng: &mut R) { - let key = T::Key::new(rng); +pub fn test_different_data(rng: &mut R) +where + T: Mac, + T::Key: Random, + R: Csprng, +{ + let key = Random::random(rng); let tag1 = T::mac(&key, b"hello"); let tag2 = T::mac(&key, b"world"); assert_ct_ne!(tag1, tag2, "tags should differ"); diff --git a/crates/crypto/src/test_util/mod.rs b/crates/crypto/src/test_util/mod.rs index cfb5bea..8948d2f 100644 --- a/crates/crypto/src/test_util/mod.rs +++ b/crates/crypto/src/test_util/mod.rs @@ -33,10 +33,10 @@ use zeroize::ZeroizeOnDrop; use crate::{ aead::{Aead, AeadId, Lifetime, OpenError, SealError}, - csprng::Csprng, + csprng::{Csprng, Random}, import::{ExportError, Import, ImportError}, kdf::{Kdf, KdfError, KdfId, Prk}, - keys::{PublicKey, SecretKey}, + keys::{FixedLength, InvalidKey, PublicKey, SecretKey}, mac::{Mac, MacId}, signer::{Signature, Signer, SignerError, SignerId, SigningKey, VerifyingKey}, }; @@ -194,11 +194,14 @@ impl Mac for MacWithDefaults { type TagSize = T::TagSize; type Key = T::Key; - type KeySize = T::KeySize; + type MinKeySize = T::MinKeySize; fn new(key: &Self::Key) -> Self { Self(T::new(key)) } + fn try_new(key: &[u8]) -> Result { + Ok(Self(T::try_new(key)?)) + } fn update(&mut self, data: &[u8]) { self.0.update(data) @@ -234,25 +237,36 @@ impl SigningKey> for SigningKeyWithDef } impl SecretKey for SigningKeyWithDefaults { - type Size = ::Size; - - fn new(rng: &mut R) -> Self { - Self(T::SigningKey::new(rng)) - } - type Secret = ::Secret; fn try_export_secret(&self) -> Result { self.0.try_export_secret() } } +impl FixedLength for SigningKeyWithDefaults { + type Size = ::Size; +} + +impl Random for SigningKeyWithDefaults +where + T: Signer + ?Sized, + T::SigningKey: Random, +{ + fn random(rng: &mut R) -> Self { + Self(::random(rng)) + } +} + impl ConstantTimeEq for SigningKeyWithDefaults { fn ct_eq(&self, other: &Self) -> Choice { ConstantTimeEq::ct_eq(&self.0, &other.0) } } -impl<'a, T: Signer + ?Sized> Import<&'a [u8]> for SigningKeyWithDefaults { +impl<'a, T: Signer + ?Sized> Import<&'a [u8]> for SigningKeyWithDefaults +where + T::SigningKey: Import<&'a [u8]>, +{ fn import(data: &'a [u8]) -> Result { Ok(Self(T::SigningKey::import(data)?)) } diff --git a/crates/crypto/src/test_util/signer.rs b/crates/crypto/src/test_util/signer.rs index 4619059..86a0649 100644 --- a/crates/crypto/src/test_util/signer.rs +++ b/crates/crypto/src/test_util/signer.rs @@ -7,8 +7,9 @@ use core::borrow::Borrow; use super::{assert_ct_eq, assert_ct_ne}; use crate::{ - csprng::Csprng, - keys::{RawSecretBytes, SecretKey}, + csprng::{Csprng, Random}, + import::Import, + keys::RawSecretBytes, signer::{Signer, SigningKey, VerifyingKey}, }; @@ -120,7 +121,7 @@ pub use test_signer; /// The base positive test. pub fn test_default(rng: &mut R) { const MSG: &[u8] = b"hello, world!"; - let sk = T::SigningKey::new(rng); + let sk = T::SigningKey::random(rng); let sig = sk.sign(MSG).expect("unable to create signature"); sk.public() .expect("signing key should be valid") @@ -131,11 +132,14 @@ pub fn test_default(rng: &mut R) { /// Test `Signer::SigningKey::ct_eq`. /// /// It also tests `Signer::SigningKey::import`. -pub fn test_sk_ct_eq(rng: &mut R) { - let sk1 = T::SigningKey::new(rng); - let sk2 = T::SigningKey::new(rng); - - fn same_key>(k: K) { +pub fn test_sk_ct_eq(rng: &mut R) +where + T::SigningKey: for<'a> Import<&'a [u8]>, +{ + let sk1 = T::SigningKey::random(rng); + let sk2 = T::SigningKey::random(rng); + + fn same_key + for<'a> Import<&'a [u8]>>(k: K) { let data = match k.try_export_secret() { Ok(data) => data, Err(_) => { @@ -159,10 +163,10 @@ pub fn test_sk_ct_eq(rng: &mut R) { /// /// It also tests `Signer::VerifyingKey::import`. pub fn test_pk_eq(rng: &mut R) { - let pk1 = T::SigningKey::new(rng) + let pk1 = T::SigningKey::random(rng) .public() .expect("signing key should be valid"); - let pk2 = T::SigningKey::new(rng) + let pk2 = T::SigningKey::random(rng) .public() .expect("signing key should be valid"); @@ -181,7 +185,7 @@ pub fn test_pk_eq(rng: &mut R) { /// [`SigningKey::public`] should always return the same key. pub fn test_public(rng: &mut R) { - let sk = T::SigningKey::new(rng); + let sk = T::SigningKey::random(rng); assert_eq!(sk.public(), sk.public()); } @@ -201,7 +205,7 @@ pub fn test_batch_simple_good(rng: &mut R) { let (pks, sigs): (Vec<_>, Vec<_>) = MSGS .iter() .map(|msg| { - let sk = T::SigningKey::new(rng); + let sk = T::SigningKey::random(rng); let sig = sk.sign(msg).expect("should not fail"); (sk.public().expect("signer key should be valid"), sig) }) @@ -225,7 +229,7 @@ pub fn test_batch_simple_bad(rng: &mut R) { let (pks, sigs): (Vec<_>, Vec<_>) = msgs .iter() .map(|msg| { - let sk = T::SigningKey::new(rng); + let sk = T::SigningKey::random(rng); let sig = sk.sign(msg).expect("should not fail"); (sk.public().expect("signing key should be valid"), sig) }) diff --git a/crates/crypto/src/test_util/vectors.rs b/crates/crypto/src/test_util/vectors.rs index 748d78e..e0a45a8 100644 --- a/crates/crypto/src/test_util/vectors.rs +++ b/crates/crypto/src/test_util/vectors.rs @@ -222,12 +222,18 @@ pub mod hpke { /// Tests an [`Aead`] against Project Wycheproof test vectors. /// /// It tests both `A` and [`AeadWithDefaults`]. -pub fn test_aead(name: AeadTest) { +pub fn test_aead(name: AeadTest) +where + A::Key: for<'a> Import<&'a [u8]>, +{ test_aead_inner::(name); test_aead_inner::>(name); } -fn test_aead_inner(name: AeadTest) { +fn test_aead_inner(name: AeadTest) +where + A::Key: for<'a> Import<&'a [u8]>, +{ let set = aead::TestSet::load(name).expect("should be able to load tests"); for g in &set.test_groups { if g.nonce_size / 8 != A::NONCE_SIZE @@ -304,7 +310,10 @@ fn test_aead_inner(name: AeadTest) { /// Tests an [`Ecdh`] against Project Wycheproof test /// vectors. -pub fn test_ecdh(name: EcdhTest) { +pub fn test_ecdh(name: EcdhTest) +where + T::PrivateKey: for<'a> Import<&'a [u8]>, +{ let set = ecdh::TestSet::load(name).expect("should be able to load tests"); for g in &set.test_groups { for tc in &g.tests { @@ -457,8 +466,11 @@ fn test_hkdf_inner(name: HkdfTest) { pub fn test_hpke(name: HpkeTest) where K: Kem, + K::DecapKey: for<'a> Import<&'a [u8]>, + K::EncapKey: for<'a> Import<&'a [u8]>, F: Kdf, A: Aead + IndCca2, + A::Key: for<'a> Import<&'a [u8]>, { let set = hpke::TestSet::load(name).expect("should be able to load tests"); for (i, g) in set.test_groups.iter().enumerate() { @@ -533,9 +545,9 @@ where /// Tests a [`Mac`] against Project Wycheproof test vectors. /// /// It tests both `T` and [`MacWithDefaults`]. -pub fn test_mac(name: MacTest) +pub fn test_mac(name: MacTest) where - T::Key: ConstantTimeEq, + T: Mac, T::Tag: for<'a> TryFrom<&'a [u8]>, { test_mac_inner::(name); @@ -544,7 +556,7 @@ where fn test_mac_inner(name: MacTest) where - T::Key: ConstantTimeEq, + T: Mac, T::Tag: for<'a> TryFrom<&'a [u8]>, { let set = mac::TestSet::load(name).expect("should be able to load tests"); @@ -558,12 +570,11 @@ where Err(_) => continue, }; - let key = match T::Key::import(&tc.key[..]) { + let mut h = match T::try_new(&tc.key[..]) { Ok(h) => h, // Skip insecure keys. Err(_) => continue, }; - let mut h = T::new(&key); // Update one character at a time. for c in tc.msg.iter() {