Crypto
WASM
TESTS
This commit is contained in:
Alex Emmet 2026-06-25 22:08:44 +02:00
commit 687e6f9642
49 changed files with 6272 additions and 366 deletions

View file

@ -33,9 +33,9 @@ impl ChaCha20Poly1305 {
#[cfg(feature = "chacha20poly1305")]
impl AeadEncrypt for ChaCha20Poly1305 {
fn encrypt(&self, plaintext: &[u8], aad: &[u8]) -> Result<Vec<u8>, CryptoError> {
use chacha20poly1305::aead::{Aead, KeyInit, Payload};
use chacha20poly1305::XChaCha20Poly1305;
use chacha20poly1305::XNonce;
use chacha20poly1305::aead::{Aead, KeyInit, Payload};
let key = chacha20poly1305::Key::from_slice(&self.key);
let cipher = XChaCha20Poly1305::new(key);
@ -63,9 +63,9 @@ impl AeadEncrypt for ChaCha20Poly1305 {
#[cfg(feature = "chacha20poly1305")]
impl AeadDecrypt for ChaCha20Poly1305 {
fn decrypt(&self, ciphertext: &[u8], aad: &[u8]) -> Result<Vec<u8>, CryptoError> {
use chacha20poly1305::aead::{Aead, KeyInit, Payload};
use chacha20poly1305::XChaCha20Poly1305;
use chacha20poly1305::XNonce;
use chacha20poly1305::aead::{Aead, KeyInit, Payload};
if ciphertext.len() < 24 {
return Err(CryptoError::InvalidNonceLength);
@ -76,10 +76,7 @@ impl AeadDecrypt for ChaCha20Poly1305 {
let cipher = XChaCha20Poly1305::new(key);
let nonce_ref = XNonce::from_slice(nonce);
let payload = Payload {
msg: ct,
aad,
};
let payload = Payload { msg: ct, aad };
cipher
.decrypt(nonce_ref, payload)
@ -109,9 +106,9 @@ impl Aes256Gcm {
#[cfg(feature = "aes-gcm")]
impl AeadEncrypt for Aes256Gcm {
fn encrypt(&self, plaintext: &[u8], aad: &[u8]) -> Result<Vec<u8>, CryptoError> {
use aes_gcm::aead::{Aead, KeyInit, Payload};
use aes_gcm::Aes256Gcm as AesGcmInner;
use aes_gcm::Nonce;
use aes_gcm::aead::{Aead, KeyInit, Payload};
let key = aes_gcm::Key::<AesGcmInner>::from_slice(&self.key);
let cipher = AesGcmInner::new(key);
@ -139,9 +136,9 @@ impl AeadEncrypt for Aes256Gcm {
#[cfg(feature = "aes-gcm")]
impl AeadDecrypt for Aes256Gcm {
fn decrypt(&self, ciphertext: &[u8], aad: &[u8]) -> Result<Vec<u8>, CryptoError> {
use aes_gcm::aead::{Aead, KeyInit, Payload};
use aes_gcm::Aes256Gcm as AesGcmInner;
use aes_gcm::Nonce;
use aes_gcm::aead::{Aead, KeyInit, Payload};
if ciphertext.len() < 12 {
return Err(CryptoError::InvalidNonceLength);
@ -152,10 +149,7 @@ impl AeadDecrypt for Aes256Gcm {
let cipher = AesGcmInner::new(key);
let nonce_ref = Nonce::from_slice(nonce);
let payload = Payload {
msg: ct,
aad,
};
let payload = Payload { msg: ct, aad };
cipher
.decrypt(nonce_ref, payload)

View file

@ -13,6 +13,12 @@ pub fn sha256_double(data: &[u8]) -> [u8; 32] {
pub struct Sha256Hasher(sha2::Sha256);
impl Default for Sha256Hasher {
fn default() -> Self {
Self::new()
}
}
impl Sha256Hasher {
pub fn new() -> Self {
Self(sha2::Sha256::new())

View file

@ -196,5 +196,3 @@ pub fn decrypt_multi(
}
Err(CryptoError::DecryptionFailed)
}

View file

@ -12,8 +12,7 @@ pub struct HybridKem;
#[cfg(feature = "mlkem-tls")]
impl HybridKem {
pub fn generate_keypair() -> (KemPrivateKey, KemPublicKey) {
let (ek, dk) =
mlkem_tls::X25519MlKem768::keygen(&mut rand_core::OsRng);
let (ek, dk) = mlkem_tls::X25519MlKem768::keygen(&mut rand_core::OsRng);
(
KemPrivateKey::new(dk.as_bytes().to_vec()),
KemPublicKey::new(ek.as_bytes().to_vec()),
@ -23,8 +22,7 @@ impl HybridKem {
pub fn encapsulate(recipient_pk: &KemPublicKey) -> Result<Encapsulated, CryptoError> {
let ek = mlkem_tls::EncapsKey768::try_from(recipient_pk.as_bytes())
.map_err(|_| CryptoError::KemEncapsulationFailed)?;
let (ct, ss) =
mlkem_tls::X25519MlKem768::encapsulate(&ek, &mut rand_core::OsRng);
let (ct, ss) = mlkem_tls::X25519MlKem768::encapsulate(&ek, &mut rand_core::OsRng);
Ok(Encapsulated {
ciphertext: ct.as_bytes().to_vec(),
shared_secret: ss.as_bytes().to_vec(),

View file

@ -36,16 +36,16 @@ pub use aead::ChaCha20Poly1305;
pub use aead::Aes256Gcm;
#[cfg(feature = "ed25519-dalek")]
pub use sign::{verify_ed25519, Ed25519Signer, SignatureScheme};
pub use sign::{Ed25519Signer, SignatureScheme, verify_ed25519};
#[cfg(feature = "ml-dsa")]
pub use sign::{verify_ml_dsa, MlDsaSigner};
pub use sign::{MlDsaSigner, verify_ml_dsa};
#[cfg(all(feature = "ed25519-dalek", feature = "ml-dsa"))]
pub use sign::{sign_dual, DualSignature};
pub use sign::{DualSignature, sign_dual};
#[cfg(feature = "sha2")]
pub use hash::{sha256, sha256_double, Sha256Hasher};
pub use hash::{Sha256Hasher, sha256, sha256_double};
#[cfg(feature = "hkdf")]
pub use kdf::{derive_encryption_key, hkdf_expand, hkdf_extract};
@ -59,7 +59,7 @@ pub use enc::EncryptionType;
pub use enc::{decrypt_with, encrypt_for};
#[cfg(all(feature = "mlkem-tls", feature = "chacha20poly1305", feature = "hkdf"))]
pub use helper::{decrypt_multi, encrypt_multi, MultiEncryptedMessage, RecipientEntry};
pub use helper::{MultiEncryptedMessage, RecipientEntry, decrypt_multi, encrypt_multi};
/* ================================ TESTS ================================ */
#[cfg(test)]
@ -148,12 +148,7 @@ mod tests {
let (ed_signer, _, _) = Ed25519Signer::generate();
let (ml_signer, _, _) = MlDsaSigner::generate();
let dual = sign_dual(ed_signer.signing_key(), ml_signer.signing_key(), b"msg").unwrap();
dual
.verify(
ed_signer.verifying_key(),
ml_signer.verifying_key(),
b"msg",
)
dual.verify(ed_signer.verifying_key(), ml_signer.verifying_key(), b"msg")
.unwrap();
}
@ -165,9 +160,14 @@ mod tests {
let (ed_signer, _, _) = Ed25519Signer::generate();
let (ml_signer, _, _) = MlDsaSigner::generate();
let dual = sign_dual(ed_signer.signing_key(), ml_signer.signing_key(), b"msg").unwrap();
assert!(dual
.verify(ed_signer.verifying_key(), ml_signer.verifying_key(), b"wrong")
.is_err());
assert!(
dual.verify(
ed_signer.verifying_key(),
ml_signer.verifying_key(),
b"wrong"
)
.is_err()
);
}
#[cfg(feature = "hkdf")]
@ -255,9 +255,18 @@ mod tests {
let kr = Keyring::generate();
let bytes = kr.to_bytes();
let loaded = Keyring::from_bytes(&bytes).unwrap();
assert_eq!(kr.kem_public_key.as_bytes(), loaded.kem_public_key.as_bytes());
assert_eq!(kr.sig_pq_public_key.as_bytes(), loaded.sig_pq_public_key.as_bytes());
assert_eq!(kr.sig_cl_public_key.as_bytes(), loaded.sig_cl_public_key.as_bytes());
assert_eq!(
kr.kem_public_key.as_bytes(),
loaded.kem_public_key.as_bytes()
);
assert_eq!(
kr.sig_pq_public_key.as_bytes(),
loaded.sig_pq_public_key.as_bytes()
);
assert_eq!(
kr.sig_cl_public_key.as_bytes(),
loaded.sig_cl_public_key.as_bytes()
);
}
#[cfg(all(feature = "mlkem-tls", feature = "ml-dsa", feature = "ed25519-dalek"))]
@ -267,7 +276,10 @@ mod tests {
let bundle = kr.public_key_bundle();
let bytes = bundle.as_bytes();
let loaded = PublicKeyBundle::from_bytes(&bytes).unwrap();
assert_eq!(bundle.kem_public_key.as_bytes(), loaded.kem_public_key.as_bytes());
assert_eq!(
bundle.kem_public_key.as_bytes(),
loaded.kem_public_key.as_bytes()
);
assert_eq!(
bundle.sig_pq_public_key.as_bytes(),
loaded.sig_pq_public_key.as_bytes()
@ -290,8 +302,8 @@ mod tests {
#[cfg(all(feature = "mlkem-tls", feature = "chacha20poly1305", feature = "hkdf"))]
#[test]
fn encrypt_multi_roundtrip() {
use crate::helper::{decrypt_multi, encrypt_multi};
use crate::keypair::Keyring;
use crate::helper::{encrypt_multi, decrypt_multi};
let kr = Keyring::generate();
let entities = vec![kr.public_key_bundle()];

View file

@ -172,7 +172,9 @@ impl MlDsaSigner {
impl SignatureScheme for MlDsaSigner {
fn sign(&self, msg: &[u8]) -> Result<Vec<u8>, CryptoError> {
use ml_dsa::Signer;
let signature = self.secret.try_sign(msg)
let signature = self
.secret
.try_sign(msg)
.map_err(|_| CryptoError::SigningFailed)?;
Ok(signature.encode().to_vec())
}
@ -181,7 +183,8 @@ impl SignatureScheme for MlDsaSigner {
use ml_dsa::Verifier;
let sig = ml_dsa::Signature::<ml_dsa::MlDsa65>::try_from(signature)
.map_err(|_| CryptoError::InvalidSignature)?;
self.public.verify(msg, &sig)
self.public
.verify(msg, &sig)
.map_err(|_| CryptoError::VerificationFailed)
}
}