mtp/crypto/src/lib.rs
Alex Emmet f4118f28ba Host & Client force randomness on each other.
Updated Reserved entry order. Made DataType ID changes easier in future
(this MAY NOT  happen again once in use).
2026-06-26 17:08:48 +02:00

316 lines
9.9 KiB
Rust

pub mod aead;
pub mod auth;
pub mod error;
pub mod keypair;
#[cfg(feature = "sha2")]
pub mod hash;
#[cfg(feature = "hkdf")]
pub mod kdf;
#[cfg(any(feature = "ed25519-dalek", feature = "ml-dsa"))]
pub mod sign;
#[cfg(any(feature = "ed25519-dalek", feature = "ml-dsa"))]
pub use sign::SigAlgorithm;
#[cfg(feature = "mlkem-tls")]
pub mod kem;
pub mod enc;
pub mod helper;
pub use aead::{AeadCipher, AeadDecrypt, AeadEncrypt};
pub use error::CryptoError;
pub use keypair::{
EncryptionPrivateKey, EncryptionPublicKey, KemPrivateKey, KemPublicKey, Keyring,
PublicKeyBundle, SignaturePqPrivateKey, SignaturePqPublicKey, SignaturePrivateKey,
SignaturePublicKey,
};
#[cfg(feature = "chacha20poly1305")]
pub use aead::ChaCha20Poly1305;
#[cfg(feature = "aes-gcm")]
pub use aead::Aes256Gcm;
#[cfg(feature = "ed25519-dalek")]
pub use sign::{Ed25519Signer, SignatureScheme, verify_ed25519};
#[cfg(feature = "ml-dsa")]
pub use sign::{MlDsaSigner, verify_ml_dsa};
#[cfg(all(feature = "ed25519-dalek", feature = "ml-dsa"))]
pub use sign::{DualSignature, sign_dual};
#[cfg(feature = "sha2")]
pub use hash::{Sha256Hasher, sha256, sha256_double};
#[cfg(feature = "hkdf")]
pub use kdf::{derive_encryption_key, hkdf_expand, hkdf_extract};
#[cfg(feature = "mlkem-tls")]
pub use kem::{Encapsulated, HybridKem};
pub use enc::EncryptionType;
#[cfg(all(feature = "mlkem-tls", feature = "hkdf"))]
pub use enc::{decrypt_with, encrypt_for};
#[cfg(all(feature = "mlkem-tls", feature = "chacha20poly1305", feature = "hkdf"))]
pub use helper::{MultiEncryptedMessage, RecipientEntry, decrypt_multi, encrypt_multi};
/* ================================ TESTS ================================ */
#[cfg(test)]
mod tests {
use super::*;
#[cfg(feature = "chacha20poly1305")]
#[test]
fn aead_encrypt_decrypt() {
use crate::aead::{AeadDecrypt, AeadEncrypt, ChaCha20Poly1305};
let key = [0xAB; 32];
let cipher = ChaCha20Poly1305::new(key);
let ct = cipher.encrypt(b"hello world", b"aad").unwrap();
let pt = cipher.decrypt(&ct, b"aad").unwrap();
assert_eq!(pt, b"hello world");
}
#[cfg(feature = "chacha20poly1305")]
#[test]
fn aead_wrong_key_fails() {
use crate::aead::{AeadDecrypt, AeadEncrypt, ChaCha20Poly1305};
let cipher_a = ChaCha20Poly1305::new([0xAB; 32]);
let cipher_b = ChaCha20Poly1305::new([0xCD; 32]);
let ct = cipher_a.encrypt(b"hello", b"").unwrap();
assert!(cipher_b.decrypt(&ct, b"").is_err());
}
#[cfg(feature = "chacha20poly1305")]
#[test]
fn aead_wrong_aad_fails() {
use crate::aead::{AeadDecrypt, AeadEncrypt, ChaCha20Poly1305};
let cipher = ChaCha20Poly1305::new([0xAB; 32]);
let ct = cipher.encrypt(b"hello", b"correct-aad").unwrap();
assert!(cipher.decrypt(&ct, b"wrong-aad").is_err());
}
#[cfg(feature = "ed25519-dalek")]
#[test]
fn ed25519_sign_verify() {
let (signer, sk, pk) = Ed25519Signer::generate();
let msg = b"test message";
let sig = signer.sign(msg).unwrap();
signer.verify(msg, &sig).unwrap();
verify_ed25519(&pk, msg, &sig).unwrap();
let loaded = Ed25519Signer::new(&sk).unwrap();
loaded.verify(msg, &sig).unwrap();
}
#[cfg(feature = "ed25519-dalek")]
#[test]
fn ed25519_wrong_sig_fails() {
let (signer, _, pk) = Ed25519Signer::generate();
let msg = b"test message";
let sig = signer.sign(msg).unwrap();
assert!(verify_ed25519(&pk, b"wrong message", &sig).is_err());
}
#[cfg(feature = "ml-dsa")]
#[test]
fn mldsa_sign_verify() {
let (signer, sk, pk) = MlDsaSigner::generate();
let msg = b"test message";
let sig = signer.sign(msg).unwrap();
signer.verify(msg, &sig).unwrap();
verify_ml_dsa(&pk, msg, &sig).unwrap();
let loaded = MlDsaSigner::new(&sk, &pk).unwrap();
loaded.verify(msg, &sig).unwrap();
}
#[cfg(feature = "ml-dsa")]
#[test]
fn mldsa_wrong_sig_fails() {
let (signer, _, pk) = MlDsaSigner::generate();
let msg = b"test message";
let sig = signer.sign(msg).unwrap();
assert!(verify_ml_dsa(&pk, b"wrong message", &sig).is_err());
}
#[cfg(all(feature = "ed25519-dalek", feature = "ml-dsa"))]
#[test]
fn dual_sign_verify() {
use crate::sign::sign_dual;
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")
.unwrap();
}
#[cfg(all(feature = "ed25519-dalek", feature = "ml-dsa"))]
#[test]
fn dual_wrong_message_fails() {
use crate::sign::sign_dual;
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()
);
}
#[cfg(feature = "hkdf")]
#[test]
fn hkdf_expand_produces_key() {
let key = derive_encryption_key(b"ikm", b"salt", b"context").unwrap();
assert_eq!(key.len(), 32);
let expanded = hkdf_expand(b"ikm", b"salt", b"info", 64).unwrap();
assert_eq!(expanded.len(), 64);
}
#[cfg(feature = "hkdf")]
#[test]
fn hkdf_different_info_different_key() {
let a = derive_encryption_key(b"ikm", b"salt", b"info-a").unwrap();
let b = derive_encryption_key(b"ikm", b"salt", b"info-b").unwrap();
assert_ne!(a, b);
}
#[cfg(feature = "sha2")]
#[test]
fn sha256_hashes() {
let h = sha256(b"hello");
assert_eq!(h.len(), 32);
let h2 = sha256_double(b"hello");
assert_eq!(h2.len(), 32);
assert_ne!(h, h2);
}
#[cfg(feature = "sha2")]
#[test]
fn sha256_deterministic() {
assert_eq!(sha256(b"hello"), sha256(b"hello"));
}
#[cfg(feature = "sha2")]
#[test]
fn sha256_hasher_incremental() {
let mut hasher = Sha256Hasher::new();
hasher.update(b"hel");
hasher.update(b"lo");
let h = hasher.finalize();
assert_eq!(h, sha256(b"hello"));
}
#[test]
fn key_types_roundtrip() {
let data = vec![1u8, 2, 3, 4];
let enc_pk = EncryptionPublicKey::new(data.clone());
assert_eq!(enc_pk.as_bytes(), &data);
let enc_sk = EncryptionPrivateKey::new(data.clone());
assert_eq!(enc_sk.as_bytes(), &data);
let sig_pk = SignaturePublicKey::new(data.clone());
assert_eq!(sig_pk.as_bytes(), &data);
let sig_sk = SignaturePrivateKey::new(data.clone());
assert_eq!(sig_sk.as_bytes(), &data);
let kem_pk = KemPublicKey::new(data.clone());
assert_eq!(kem_pk.as_bytes(), &data);
let kem_sk = KemPrivateKey::new(data.clone());
assert_eq!(kem_sk.as_bytes(), &data);
let sig_pq_pk = SignaturePqPublicKey::new(data.clone());
assert_eq!(sig_pq_pk.as_bytes(), &data);
let sig_pq_sk = SignaturePqPrivateKey::new(data.clone());
assert_eq!(sig_pq_sk.as_bytes(), &data);
}
#[cfg(all(feature = "mlkem-tls", feature = "ml-dsa", feature = "ed25519-dalek"))]
#[test]
fn keyring_generate_and_bundle() {
let kr = Keyring::generate();
let bundle = kr.public_key_bundle();
assert!(!bundle.kem_public_key.as_bytes().is_empty());
assert!(!bundle.sig_pq_public_key.as_bytes().is_empty());
assert!(!bundle.sig_cl_public_key.as_bytes().is_empty());
}
#[cfg(all(feature = "mlkem-tls", feature = "ml-dsa", feature = "ed25519-dalek"))]
#[test]
fn keyring_serialize_roundtrip() {
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()
);
}
#[cfg(all(feature = "mlkem-tls", feature = "ml-dsa", feature = "ed25519-dalek"))]
#[test]
fn public_key_bundle_serialize_roundtrip() {
let kr = Keyring::generate();
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.sig_pq_public_key.as_bytes(),
loaded.sig_pq_public_key.as_bytes()
);
assert_eq!(
bundle.sig_cl_public_key.as_bytes(),
loaded.sig_cl_public_key.as_bytes()
);
}
#[cfg(feature = "mlkem-tls")]
#[test]
fn hybrid_kem_roundtrip() {
let (sk, pk) = HybridKem::generate_keypair();
let enc = HybridKem::encapsulate(&pk).unwrap();
let ss = HybridKem::decapsulate(&sk, &enc.ciphertext).unwrap();
assert_eq!(enc.shared_secret, ss);
}
#[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;
let kr = Keyring::generate();
let entities = vec![kr.public_key_bundle()];
let msg = b"secret data";
let ct = encrypt_multi(msg, b"aad", &entities).unwrap();
let pt = decrypt_multi(&ct, b"aad", &kr).unwrap();
assert_eq!(pt, msg);
}
}