diff --git a/README.md b/README.md index feb9ef2..78c1489 100644 --- a/README.md +++ b/README.md @@ -8,26 +8,34 @@ [![Crates.io](https://img.shields.io/crates/v/capycrypt?style=flat-square)](https://crates.io/crates/capycrypt) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/drcapybara/capyCRYPT/blob/master/LICENSE.txt) -A complete Rust cryptosystem implementing [NIST FIPS 202](https://nvlpubs.nist.gov/nistpubs/fips/nist.fips.202.pdf) & [NIST FIPS 197](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.197-upd1.pdf) paired to the ed448 Golidlocks curve. +A complete Rust cryptosystem implementing: -## Security -This library is built with love as an academic excercise in cryptographic algorithm design. Despite how awesome and cool it is, it probably shouldn't be used for anything serious. If you find ways to make it even better, open an issue or PR and we'll gladly engage. +- AES: [NIST FIPS 197](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.197-upd1.pdf) +- SHA3: [NIST FIPS 202](https://nvlpubs.nist.gov/nistpubs/fips/nist.fips.202.pdf) +- ML-KEM: [NIST FIPS 203](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.203.ipd.pdf) +- E448: [Ed448-Goldilocks Curve](https://eprint.iacr.org/2015/625.pdf) + +These primitives form the basis of a platform supporting a wide variety of cryptographic operations, which are detailed below. +## Security +This library is built with love as an academic excercise in cryptographic algorithm design. Despite how awesome and cool it is, it probably shouldn't be used for anything serious right now. If you find ways to make it even better, open an issue or PR and we'll gladly engage. ## Features -- **AES:** NIST-Compliant Advanced Encryption Standard (AES) implementation for encrypting and decrypting data. +- **AES:** NIST-Compliant **Advanced Encryption Standard** (AES) implementation for encrypting and decrypting data. -- **Edwards Elliptic Curve:** High-performance, side-channel resistant instance of the [Ed448-Goldilocks](https://crates.io/crates/tiny_ed448_goldilocks) curve for asymmetric operations. +- **Edwards Elliptic Curve:** High-performance, side-channel resistant instance of the **Ed448-Goldilocks** curve for asymmetric operations. -- **SHA-3:** NIST-Compliant Secure Hash Algorithm 3 (SHA-3) implementation for generating cryptographic hash values, symmetric keystreams, and PRNGs. +- **SHA-3:** NIST-Compliant **Secure Hash Algorithm 3** (SHA-3) implementation for generating cryptographic hash values, symmetric keystreams, and PRNGs. +- **ML-KEM 768:** NIST Initial Public Draft (IPD)-Compliant **Module Ring-Learning With Errors Key Encapsulation Mechanism** (ML-KEM) for quantum-safe asymmetric key and message exchange. ## Supported Operations - **Message Digest:** Computes hash of a given message, with adjustable digest lengths. - **MACs:** Computes message authentication code of a given message, with adjustable bit security. - **Shared Secret Key:** Symmetric message encryption and decryption. - **Public Key Cryptography:** Asymmetric message encryption under public key, decryption with secret key. -- **Zero-Knowledge:** Prove knowledge of secret information with Schnorr/ECDHIES signatures. +- **Signatures** Prove and verify knowledge of secret information with Schnorr/ECDHIES signatures. +- **Quantum-Safe Message Exchange:** ML-KEM + SHA3 sponge for quantum-safe symmetric messaging and key exchange. ## Installation Add the following line to your `Cargo.toml` file: @@ -36,85 +44,101 @@ cargo add capycrypt ``` ## Quick Start -### Compute Digest: -```rust -use capycrypt::{Hashable, Message}; -// Hash the empty string -let mut data = Message::new(vec![]); -// Obtained from echo -n "" | openssl dgst -sha3-256 -let expected = "a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a"; -// Compute a SHA3 digest with 128 bits of security -data.compute_sha3_hash(256); -assert!(hex::encode(data.digest.unwrap().to_vec()) == expected); -``` - -### Symmetric Encrypt/Decrypt: +### Quantum-Secure Encrypt/Decrypt: ```rust use capycrypt::{ - Message, - AESEncryptable, - SpongeEncryptable, - sha3::{aux_functions::byte_utils::get_random_bytes} + kem::{encryptable::KEMEncryptable, keypair::kem_keygen}, + sha3::aux_functions::byte_utils::get_random_bytes, + Message, SecParam, }; -// Get a random 128-bit password -let key = get_random_bytes(16); + // Get 5mb random data let mut msg = Message::new(get_random_bytes(5242880)); -// Encrypt the data -msg.aes_encrypt_cbc(&key); -// Decrypt the data -msg.aes_decrypt_cbc(&key); -// Encrypt the data -msg.sha3_encrypt(&pw, 512); -// Decrypt the data -msg.sha3_decrypt(&pw); -// Verify operation success -assert!(msg.op_result.unwrap()); +// Create a new ML-KEM public/private keypair +let (kem_pub_key, kem_priv_key) = kem_keygen(); +// Encrypt the message +assert!(msg.kem_encrypt(&kem_pub_key, SecParam::D256).is_ok()); +// Decrypt and verify +assert!(msg.kem_decrypt(&kem_priv_key).is_ok()); ``` -### Asymmetric Encrypt/Decrypt: +### Elliptic-Curve Encrypt/Decrypt: ```rust use capycrypt::{ - KeyEncryptable, - KeyPair, - Message, - sha3::aux_functions::byte_utils::get_random_bytes + ecc::{encryptable::KeyEncryptable, keypair::KeyPair}, + sha3::aux_functions::byte_utils::get_random_bytes, + Message, SecParam, }; // Get 5mb random data let mut msg = Message::new(get_random_bytes(5242880)); -// Create a new private/public keypair -let key_pair = KeyPair::new(&get_random_bytes(32), "test key".to_string(), 512); +// Create a new elliptic-curve public/private keypair +let key_pair = KeyPair::new( + &get_random_bytes(64), // random password for key + "test key".to_string(), // label + SecParam::D256, // bit-security for key +); // Encrypt the message -msg.key_encrypt(&key_pair.pub_key, 512); -// Decrypt the message -msg.key_decrypt(&key_pair.priv_key); -// Verify -assert!(msg.op_result.unwrap()); +assert!(msg.key_encrypt(&key_pair.pub_key, SecParam::D256).is_ok()); +// Decrypt and verify +assert!(msg.key_decrypt(&key_pair.priv_key).is_ok()); +``` + +### Symmetric Encrypt/Decrypt: +```rust +use capycrypt::{ + aes::encryptable::AesEncryptable, + sha3::{aux_functions::byte_utils::get_random_bytes, + encryptable::SpongeEncryptable}, + Message, SecParam, +}; +// Get a random password +let pw = get_random_bytes(16); +// Get 5mb random data +let mut msg = Message::new(get_random_bytes(5242880)); +// Encrypt the data +assert!(msg.aes_encrypt_ctr(&pw).is_ok()); +// Decrypt the data +assert!(msg.aes_decrypt_ctr(&pw).is_ok()); +// Encrypt the data +assert!(msg.sha3_encrypt(&pw, SecParam::D512).is_ok()); +// Decrypt and verify +assert!(msg.sha3_decrypt(&pw).is_ok()); ``` ### Schnorr Signatures: ```rust use capycrypt::{ - Signable, - KeyPair, - Message, + ecc::{keypair::KeyPair, signable::Signable}, sha3::aux_functions::byte_utils::get_random_bytes, + Message, SecParam, }; // Get random 5mb let mut msg = Message::new(get_random_bytes(5242880)); -// Get a random password -let pw = get_random_bytes(64); -// Generate a signing keypair -let key_pair = KeyPair::new(&pw, "test key".to_string(), 512); -// Sign with 256 bits of security -msg.sign(&key_pair, 512); +// Create a new elliptic-curve public/private keypair +let key_pair = KeyPair::new( + &get_random_bytes(64), // random password for key + "test key".to_string(), // label + SecParam::D256, // bit-security for key +); +// Sign with 128 bits of security +assert!(msg.sign(&key_pair, SecParam::D256).is_ok()); // Verify signature -msg.verify(&key_pair.pub_key); -// Assert correctness -assert!(msg.op_result.unwrap()); +assert!(msg.verify(&key_pair.pub_key).is_ok()); +``` + +### Compute Digest: +```rust +use capycrypt::{sha3::hashable::SpongeHashable, Message, SecParam}; +// Hash the empty string +let mut data = Message::new(vec![]); +// Obtained from echo -n "" | openssl dgst -sha3-256 +let expected = "a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a"; +// Compute a SHA3 digest with 128 bits of security +data.compute_sha3_hash(SecParam::D256); +assert!(hex::encode(data.digest) == expected); ``` ## Performance @@ -122,10 +146,13 @@ This library uses the criterion crate for benches. Running: ```bash cargo bench ``` -conducts benchmarks in order from lowest security to highest. For example, the lowest security configuration available in this library is the pairing of E222 with cSHAKE256, while the highest security offered is E521 paired with cSHAKE512. +conducts benchmarks over parameter sets in order from lowest security to highest. Symmetric operations compare well to openSSL. On an Intel® Core™ i7-10710U × 12, our adaption of in-place keccak from the [XKCP](https://github.com/XKCP/XKCP) achieves a runtime of approximately 20 ms to digest 5mb of random data, vs approximately 17 ms in openSSL. +## (Plausible) Post-Quantum Security +This library pairs ML-KEM under the 768-parameter set to a SHA3-sponge construction for a quantum-safe public-key cryptosystem. It offers theoretic quantum-security through the use of the KEM and sponge primitives, which are both based on problems conjectured to be hard to solve for a quantum adversary. Our construction is non-standard, has not been evaluated by experts, and is unaudited. Our MLKEM library itself is a work in progress and only supports the NIST-II security parameter of 768. Furthermore, the current FIPS 203 IPD is, (as the name indicates), a draft, and final details about secure implementation may be subject to change. Our design currently exists in this library purely as an academic curiosity. Use it at your own risk, we provide no guarantee of safety, reliability, or efficiency. + ## Acknowledgements The authors wish to sincerely thank Dr. Paulo Barreto for the general design of this library as well as the curve functionality. We also wish to extend gratitude to the curve-dalek authors [here](https://github.com/crate-crypto/Ed448-Goldilocks) and [here](https://docs.rs/curve25519-dalek/4.1.1/curve25519_dalek/) for the excellent reference implementations and exemplary instances of rock-solid cryptography. diff --git a/benches/benchmark_e448_224.rs b/benches/benchmark_e448_224.rs index a991fd1..e298ab1 100644 --- a/benches/benchmark_e448_224.rs +++ b/benches/benchmark_e448_224.rs @@ -11,20 +11,20 @@ const BIT_SECURITY: SecParam = D224; /// Symmetric encrypt and decrypt roundtrip fn sym_enc(pw: &[u8], mut msg: Message) { - let _ = msg.sha3_encrypt(pw, &BIT_SECURITY); + let _ = msg.sha3_encrypt(pw, BIT_SECURITY); let _ = msg.sha3_decrypt(pw); } /// Asymmetric encrypt and decrypt roundtrip + keygen fn key_gen_enc_dec(pw: &[u8], mut msg: Message) { - let key_pair = KeyPair::new(pw, "test key".to_string(), &BIT_SECURITY).unwrap(); - let _ = msg.key_encrypt(&key_pair.pub_key, &BIT_SECURITY); + let key_pair = KeyPair::new(pw, "test key".to_string(), BIT_SECURITY); + let _ = msg.key_encrypt(&key_pair.pub_key, BIT_SECURITY); let _ = msg.key_decrypt(&key_pair.priv_key); } /// Signature generation + verification roundtrip pub fn sign_verify(key_pair: KeyPair, mut msg: Message) { - let _ = msg.sign(&key_pair, &BIT_SECURITY); + let _ = msg.sign(&key_pair, BIT_SECURITY); let _ = msg.verify(&key_pair.pub_key); } @@ -32,7 +32,7 @@ fn bench_sign_verify(c: &mut Criterion) { c.bench_function("e448 + SHA3-224 Sign + Verify Roundtrip", |b| { b.iter(|| { sign_verify( - KeyPair::new(&get_random_bytes(16), "test key".to_string(), &BIT_SECURITY).unwrap(), + KeyPair::new(&get_random_bytes(16), "test key".to_string(), BIT_SECURITY), Message::new(get_random_bytes(5242880)), ) }); @@ -54,9 +54,7 @@ fn bench_key_gen_enc_dec(c: &mut Criterion) { c.bench_function("e448 + SHA3-224 Asymmetric enc + dec", |b| { b.iter(|| { key_gen_enc_dec( - &KeyPair::new(&get_random_bytes(32), "test key".to_string(), &BIT_SECURITY) - .unwrap() - .priv_key, + &KeyPair::new(&get_random_bytes(32), "test key".to_string(), BIT_SECURITY).priv_key, Message::new(get_random_bytes(5242880)), ) }); diff --git a/benches/benchmark_e448_512.rs b/benches/benchmark_e448_512.rs index 3c45cfc..9f49d7c 100644 --- a/benches/benchmark_e448_512.rs +++ b/benches/benchmark_e448_512.rs @@ -11,20 +11,20 @@ const BIT_SECURITY: SecParam = D512; /// Symmetric encrypt and decrypt roundtrip fn sym_enc(pw: &[u8], mut msg: Message) { - let _ = msg.sha3_encrypt(pw, &BIT_SECURITY); + let _ = msg.sha3_encrypt(pw, BIT_SECURITY); let _ = msg.sha3_decrypt(pw); } /// Asymmetric encrypt and decrypt roundtrip + keygen fn key_gen_enc_dec(pw: &[u8], mut msg: Message) { - let key_pair = KeyPair::new(pw, "test key".to_string(), &BIT_SECURITY).unwrap(); - let _ = msg.key_encrypt(&key_pair.pub_key, &BIT_SECURITY); + let key_pair = KeyPair::new(pw, "test key".to_string(), BIT_SECURITY); + let _ = msg.key_encrypt(&key_pair.pub_key, BIT_SECURITY); let _ = msg.key_decrypt(&key_pair.priv_key); } /// Signature generation + verification roundtrip pub fn sign_verify(key_pair: KeyPair, mut msg: Message) { - let _ = msg.sign(&key_pair, &BIT_SECURITY); + let _ = msg.sign(&key_pair, BIT_SECURITY); let _ = msg.verify(&key_pair.pub_key); } @@ -32,7 +32,7 @@ fn bench_sign_verify(c: &mut Criterion) { c.bench_function("e448 + SHA3-512 Sign + Verify Roundtrip 5mb", |b| { b.iter(|| { sign_verify( - KeyPair::new(&get_random_bytes(16), "test key".to_string(), &BIT_SECURITY).unwrap(), + KeyPair::new(&get_random_bytes(16), "test key".to_string(), BIT_SECURITY), Message::new(get_random_bytes(5242880)), ) }); @@ -54,9 +54,7 @@ fn bench_key_gen_enc_dec(c: &mut Criterion) { c.bench_function("e448 + SHA3-512 Asymmetric enc + dec 5mb", |b| { b.iter(|| { key_gen_enc_dec( - &KeyPair::new(&get_random_bytes(32), "test key".to_string(), &BIT_SECURITY) - .unwrap() - .priv_key, + &KeyPair::new(&get_random_bytes(32), "test key".to_string(), BIT_SECURITY).priv_key, Message::new(get_random_bytes(5242880)), ) }); diff --git a/benches/benchmark_sha3.rs b/benches/benchmark_sha3.rs index 0f2dc9d..3af1da3 100644 --- a/benches/benchmark_sha3.rs +++ b/benches/benchmark_sha3.rs @@ -1,4 +1,4 @@ -use capycrypt::sha3::hashable::Hashable; +use capycrypt::sha3::hashable::SpongeHashable; use capycrypt::{Message, SecParam}; use capycrypt::sha3::aux_functions::byte_utils::get_random_bytes; @@ -9,7 +9,7 @@ const BIT_SECURITY: SecParam = D256; /// hash 5mb of random data with 128 bits of security fn sha3_digest(mut msg: Message) { - let _ = msg.compute_sha3_hash(&BIT_SECURITY); + msg.compute_sha3_hash(BIT_SECURITY); } fn bench_sha3_digest(c: &mut Criterion) { diff --git a/src/aes/encryptable.rs b/src/aes/encryptable.rs index d9899f5..e790f2c 100644 --- a/src/aes/encryptable.rs +++ b/src/aes/encryptable.rs @@ -35,11 +35,11 @@ impl AesEncryptable for Message { let iv = get_random_bytes(16); let mut ke_ka = iv.clone(); ke_ka.append(&mut key.to_owned()); - let ke_ka = kmac_xof(&ke_ka, &[], 512, "AES", &SecParam::D256)?; + let ke_ka = kmac_xof(&ke_ka, &[], 512, "AES", SecParam::D256); let ke = &ke_ka[..key.len()].to_vec(); // Encryption Key let ka = &ke_ka[key.len()..].to_vec(); // Authentication Key - self.digest = kmac_xof(ka, &self.msg, 512, "AES", &SecParam::D256); + self.digest = kmac_xof(ka, &self.msg, 512, "AES", SecParam::D256); self.sym_nonce = Some(iv.clone()); let key_schedule = AES::new(ke); @@ -81,7 +81,7 @@ impl AesEncryptable for Message { let iv = self.sym_nonce.clone().unwrap(); let mut ke_ka = iv.clone(); ke_ka.append(&mut key.to_owned()); - let ke_ka = kmac_xof(&ke_ka, &[], 512, "AES", &SecParam::D256)?; + let ke_ka = kmac_xof(&ke_ka, &[], 512, "AES", SecParam::D256); let ke = &ke_ka[..key.len()].to_vec(); // Encryption Key let ka = &ke_ka[key.len()..].to_vec(); // Authentication Key @@ -107,12 +107,7 @@ impl AesEncryptable for Message { remove_pcks7_padding(&mut self.msg); - let ver = kmac_xof(ka, &self.msg, 512, "AES", &SecParam::D256)?; - self.op_result = match self.digest.as_mut() { - Ok(digest) if ver == *digest => Ok(()), - Ok(_) => Err(OperationError::OperationResultNotSet), - Err(_) => Err(OperationError::SignatureVerificationFailure), - }; + kmac_xof(ka, &self.msg, 512, "AES", SecParam::D256); Ok(()) } @@ -143,12 +138,12 @@ impl AesEncryptable for Message { let mut ke_ka = iv.clone(); ke_ka.extend_from_slice(&counter_bytes); ke_ka.extend_from_slice(key); - let ke_ka = kmac_xof(&ke_ka, &[], 512, "AES", &SecParam::D256)?; + let ke_ka = kmac_xof(&ke_ka, &[], 512, "AES", SecParam::D256); let (ke, ka) = ke_ka.split_at(key.len()); self.sym_nonce = Some(iv.clone()); - self.digest = Ok(kmac_xof(ka, &self.msg, 512, "AES", &SecParam::D256)?); + self.digest = kmac_xof(ka, &self.msg, 512, "AES", SecParam::D256); let key_schedule = AES::new(ke); @@ -197,7 +192,7 @@ impl AesEncryptable for Message { let mut ke_ka = iv.clone(); ke_ka.extend_from_slice(&counter_bytes); ke_ka.extend_from_slice(key); - let ke_ka = kmac_xof(&ke_ka, &[], 512, "AES", &SecParam::D256)?; + let ke_ka = kmac_xof(&ke_ka, &[], 512, "AES", SecParam::D256); let (ke, ka) = ke_ka.split_at(key.len()); @@ -217,16 +212,7 @@ impl AesEncryptable for Message { xor_blocks(block, &temp); }); - let ver = kmac_xof(ka, &self.msg, 512, "AES", &SecParam::D256)?; - self.op_result = if let Ok(digest) = self.digest.as_ref() { - if digest == &ver { - Ok(()) - } else { - Err(OperationError::AESCTRDecryptionFailure) - } - } else { - Err(OperationError::DigestNotSet) - }; + kmac_xof(ka, &self.msg, 512, "AES", SecParam::D256); Ok(()) } } diff --git a/src/ecc/encryptable.rs b/src/ecc/encryptable.rs index fe08854..102da0e 100644 --- a/src/ecc/encryptable.rs +++ b/src/ecc/encryptable.rs @@ -8,7 +8,7 @@ use crate::{ use tiny_ed448_goldilocks::curve::{extended_edwards::ExtendedPoint, field::scalar::Scalar}; pub trait KeyEncryptable { - fn key_encrypt(&mut self, pub_key: &ExtendedPoint, d: &SecParam) -> Result<(), OperationError>; + fn key_encrypt(&mut self, pub_key: &ExtendedPoint, d: SecParam) -> Result<(), OperationError>; fn key_decrypt(&mut self, pw: &[u8]) -> Result<(), OperationError>; } @@ -28,25 +28,22 @@ impl KeyEncryptable for Message { /// * c ← kmac_xof(ke, “”, |m|, “PKE”) ⊕ m /// * t ← kmac_xof(ka, m, 448, “PKA”) /// ## Arguments: - /// * pub_key: [`EdCurvePoint`] : X coordinate of public key 𝑉 + /// * pub_key: [`ExtendedPoint`] : X coordinate of public key 𝑉 /// * d: u64: Requested security strength in bits. Can only be 224, 256, 384, or 512. #[allow(non_snake_case)] - fn key_encrypt(&mut self, pub_key: &ExtendedPoint, d: &SecParam) -> Result<(), OperationError> { - self.d = Some(*d); - let k = bytes_to_scalar(get_random_bytes(56)).mul_mod(&Scalar::from(4_u64)); + fn key_encrypt(&mut self, pub_key: &ExtendedPoint, d: SecParam) -> Result<(), OperationError> { + self.d = Some(d); + let k = bytes_to_scalar(&get_random_bytes(56)).mul_mod(&Scalar::from(4_u64)); let w = (*pub_key * k).to_affine(); let Z = (ExtendedPoint::generator() * k).to_affine(); - let ke_ka = kmac_xof(&w.x.to_bytes(), &[], 448 * 2, "PK", d)?; + let ke_ka = kmac_xof(&w.x.to_bytes(), &[], 448 * 2, "PK", d); let (ke, ka) = ke_ka.split_at(ke_ka.len() / 2); let t = kmac_xof(ka, &self.msg, 448, "PKA", d); let msg_len = self.msg.len(); - xor_bytes( - &mut self.msg, - &kmac_xof(ke, &[], (msg_len * 8) as u64, "PKE", d)?, - ); + xor_bytes(&mut self.msg, &kmac_xof(ke, &[], msg_len * 8, "PKE", d)); self.digest = t; self.asym_nonce = Some(Z.to_extended()); @@ -58,11 +55,6 @@ impl KeyEncryptable for Message { /// Operates under Schnorr/ECDHIES principle in that shared symmetric key is /// derived from 𝑍. /// - /// ## Assumes: - /// * well-formed encryption - /// * Some(Message.t) - /// * Some(Message.z) - /// /// ## Replaces: /// * `Message.data` with result of decryption. /// * `Message.op_result` with result of comparision of `Message.t` == keyed hash of decryption. @@ -80,32 +72,25 @@ impl KeyEncryptable for Message { #[allow(non_snake_case)] fn key_decrypt(&mut self, pw: &[u8]) -> Result<(), OperationError> { let Z = self.asym_nonce.ok_or(OperationError::SymNonceNotSet)?; - let d = self - .d - .as_ref() - .ok_or(OperationError::SecurityParameterNotSet)?; + let d = self.d.ok_or(OperationError::SecurityParameterNotSet)?; - let s_bytes = kmac_xof(pw, &[], 448, "SK", d)?; - let s = bytes_to_scalar(s_bytes).mul_mod(&Scalar::from(4_u64)); + let s_bytes = kmac_xof(pw, &[], 448, "SK", d); + let s = bytes_to_scalar(&s_bytes).mul_mod(&Scalar::from(4_u64)); let Z = (Z * s).to_affine(); - let ke_ka = kmac_xof(&Z.x.to_bytes(), &[], 448 * 2, "PK", d)?; + let ke_ka = kmac_xof(&Z.x.to_bytes(), &[], 448 * 2, "PK", d); let (ke, ka) = ke_ka.split_at(ke_ka.len() / 2); - let xor_result = kmac_xof(ke, &[], (self.msg.len() * 8) as u64, "PKE", d)?; + let xor_result = kmac_xof(ke, &[], self.msg.len() * 8, "PKE", d); xor_bytes(&mut self.msg, &xor_result); - let t_p = kmac_xof(ka, &self.msg, 448, "PKA", d)?; + let t_p = kmac_xof(ka, &self.msg, 448, "PKA", d); - self.op_result = if self.digest.as_ref() == Ok(&t_p) { + if self.digest == t_p { Ok(()) } else { - // revert back to the encrypted message xor_bytes(&mut self.msg, &xor_result); - Err(OperationError::KeyDecryptionError) - }; - - Ok(()) + } } } diff --git a/src/ecc/keypair.rs b/src/ecc/keypair.rs index 1b50732..afb480b 100644 --- a/src/ecc/keypair.rs +++ b/src/ecc/keypair.rs @@ -8,7 +8,7 @@ use std::fs::File; use std::io::Read; use tiny_ed448_goldilocks::curve::{extended_edwards::ExtendedPoint, field::scalar::Scalar}; -use crate::{sha3, OperationError, SecParam}; +use crate::{sha3, SecParam}; #[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] /// An object containing the fields necessary to represent an asymmetric keypair. @@ -40,16 +40,16 @@ impl KeyPair { /// verification key 𝑉 is hashed together with the message 𝑚 /// and the nonce 𝑈: hash (𝑚, 𝑈, 𝑉) . #[allow(non_snake_case)] - pub fn new(pw: &[u8], owner: String, d: &SecParam) -> Result { - let data = kmac_xof(pw, &[], 448, "SK", d)?; - let s: Scalar = bytes_to_scalar(data).mul_mod(&Scalar::from(4_u64)); + pub fn new(pw: &[u8], owner: String, d: SecParam) -> KeyPair { + let data = kmac_xof(pw, &[], 448, "SK", d); + let s: Scalar = bytes_to_scalar(&data).mul_mod(&Scalar::from(4_u64)); let V = ExtendedPoint::generator() * s; - Ok(KeyPair { + KeyPair { owner, pub_key: V, priv_key: pw.to_vec(), date_created: get_date_and_time_as_string(), - }) + } } /// # KeyPair Saving diff --git a/src/ecc/signable.rs b/src/ecc/signable.rs index 781b468..95512ac 100644 --- a/src/ecc/signable.rs +++ b/src/ecc/signable.rs @@ -11,7 +11,7 @@ use tiny_ed448_goldilocks::curve::{extended_edwards::ExtendedPoint, field::scala use super::keypair::KeyPair; pub trait Signable { - fn sign(&mut self, key: &KeyPair, d: &SecParam) -> Result<(), OperationError>; + fn sign(&mut self, key: &KeyPair, d: SecParam) -> Result<(), OperationError>; fn verify(&mut self, pub_key: &ExtendedPoint) -> Result<(), OperationError>; } @@ -37,27 +37,24 @@ impl Signable for Message { /// ## Arguments: /// * key: &[`KeyPair`], : reference to KeyPair. /// * d: u64: encryption security strength in bits. Can only be 224, 256, 384, or 512. - /// - /// ## Assumes: - /// * Some(key.priv_key) #[allow(non_snake_case)] - fn sign(&mut self, key: &KeyPair, d: &SecParam) -> Result<(), OperationError> { - let s_bytes = kmac_xof(&key.priv_key, &[], 448, "SK", d)?; - let s = bytes_to_scalar(s_bytes).mul_mod(&Scalar::from(4_u64)); + fn sign(&mut self, key: &KeyPair, d: SecParam) -> Result<(), OperationError> { + let s_bytes = kmac_xof(&key.priv_key, &[], 448, "SK", d); + let s = bytes_to_scalar(&s_bytes).mul_mod(&Scalar::from(4_u64)); let s_bytes = scalar_to_bytes(&s); - let k_bytes = kmac_xof(&s_bytes, &self.msg, 448, "N", d)?; - let k = bytes_to_scalar(k_bytes) * Scalar::from(4_u64); + let k_bytes = kmac_xof(&s_bytes, &self.msg, 448, "N", d); + let k = bytes_to_scalar(&k_bytes) * Scalar::from(4_u64); let U = ExtendedPoint::generator() * k; let ux_bytes = U.to_affine().x.to_bytes(); - let h = kmac_xof(&ux_bytes, &self.msg, 448, "T", d)?; - let h_big = bytes_to_scalar(h.clone()); + let h = kmac_xof(&ux_bytes, &self.msg, 448, "T", d); + let h_big = bytes_to_scalar(&h); let z = k - h_big.mul_mod(&s); self.sig = Some(Signature { h, z }); - self.d = Some(*d); + self.d = Some(d); Ok(()) } @@ -76,21 +73,17 @@ impl Signable for Message { #[allow(non_snake_case)] fn verify(&mut self, pub_key: &ExtendedPoint) -> Result<(), OperationError> { let sig = self.sig.as_ref().ok_or(OperationError::SignatureNotSet)?; - let d = self - .d - .as_ref() - .ok_or(OperationError::SecurityParameterNotSet)?; + let d = self.d.ok_or(OperationError::SecurityParameterNotSet)?; - let h_scalar = bytes_to_scalar(sig.h.clone()); + let h_scalar = bytes_to_scalar(&sig.h); let U = ExtendedPoint::generator() * sig.z + (*pub_key * h_scalar); - let h_p = kmac_xof(&U.to_affine().x.to_bytes(), &self.msg, 448, "T", d)?; + let h_p = kmac_xof(&U.to_affine().x.to_bytes(), &self.msg, 448, "T", d); - self.op_result = if h_p == sig.h { + if h_p == sig.h { Ok(()) } else { Err(OperationError::SignatureVerificationFailure) - }; - Ok(()) + } } } diff --git a/src/kem/encryptable.rs b/src/kem/encryptable.rs index 311c0da..b5e9104 100644 --- a/src/kem/encryptable.rs +++ b/src/kem/encryptable.rs @@ -14,33 +14,33 @@ use rand::{thread_rng, RngCore}; use super::keypair::{KEMPrivateKey, KEMPublicKey}; pub trait KEMEncryptable { - fn kem_encrypt(&mut self, key: &KEMPublicKey, d: &SecParam) -> Result<(), OperationError>; + fn kem_encrypt(&mut self, key: &KEMPublicKey, d: SecParam) -> Result<(), OperationError>; fn kem_decrypt(&mut self, key: &KEMPrivateKey) -> Result<(), OperationError>; } impl KEMEncryptable for Message { - fn kem_encrypt(&mut self, key: &KEMPublicKey, d: &SecParam) -> Result<(), OperationError> { - self.d = Some(*d); + fn kem_encrypt(&mut self, key: &KEMPublicKey, d: SecParam) -> Result<(), OperationError> { + self.d = Some(d); let mut rng = thread_rng(); - let mut secret = [0_u8; 32]; + let secret = &mut [0_u8; 32]; // generate a random secret to be used as the shared seed - rng.fill_bytes(&mut secret); + rng.fill_bytes(secret); - let c = k_pke_encrypt::(&secret, &key.ek, &key.rand_bytes); + let c = k_pke_encrypt::(secret, &key.ek, &key.rand_bytes); self.kem_ciphertext = Some(c); let z = get_random_bytes(512); let mut ke_ka = z.clone(); - ke_ka.extend_from_slice(&secret); + ke_ka.extend_from_slice(secret); - let ke_ka = kmac_xof(&ke_ka, &[], 1024, "S", d)?; + let ke_ka = kmac_xof(&ke_ka, &[], 1024, "S", d); let (ke, ka) = ke_ka.split_at(64); self.digest = kmac_xof(ka, &self.msg, 512, "KEMKA", d); - let m = kmac_xof(ke, &[], (self.msg.len() * 8) as u64, "KEMKE", d)?; + let m = kmac_xof(ke, &[], self.msg.len() * 8, "KEMKE", d); xor_bytes(&mut self.msg, &m); self.sym_nonce = Some(z); @@ -55,10 +55,7 @@ impl KEMEncryptable for Message { let dec = k_pke_decrypt::(&key.dk, ciphertext); - let d = self - .d - .as_ref() - .ok_or(OperationError::SecurityParameterNotSet)?; + let d = self.d.ok_or(OperationError::SecurityParameterNotSet)?; let mut z_pw = self .sym_nonce @@ -67,25 +64,19 @@ impl KEMEncryptable for Message { .clone(); z_pw.extend_from_slice(&dec); - let ke_ka = kmac_xof(&z_pw, &[], 1024, "S", d)?; + let ke_ka = kmac_xof(&z_pw, &[], 1024, "S", d); let (ke, ka) = ke_ka.split_at(64); - let m = kmac_xof(ke, &[], (self.msg.len() * 8) as u64, "KEMKE", d)?; + let m = kmac_xof(ke, &[], self.msg.len() * 8, "KEMKE", d); xor_bytes(&mut self.msg, &m); - let new_t = kmac_xof(ka, &self.msg, 512, "KEMKA", d)?; + let new_t = kmac_xof(ka, &self.msg, 512, "KEMKA", d); - self.op_result = if self - .digest - .as_ref() - .map_or(false, |digest| digest == &new_t) - { + if self.digest == new_t { Ok(()) } else { xor_bytes(&mut self.msg, &m); Err(OperationError::SHA3DecryptionFailure) - }; - - Ok(()) + } } } diff --git a/src/lib.rs b/src/lib.rs index 820971c..39a9002 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -72,9 +72,7 @@ pub struct Message { /// Nonce used in asymmetric encryption pub asym_nonce: Option, /// Hash value (also known as message digest) - pub digest: Result, OperationError>, - /// Result of the cryptographic trait - pub op_result: Result<(), OperationError>, + pub digest: Vec, /// Schnorr signatures on the input message pub sig: Option, /// ML-KEM encrypted secret as a byte array @@ -89,8 +87,7 @@ impl Message { d: None, sym_nonce: None, asym_nonce: None, - digest: Ok(vec![]), - op_result: Ok(()), + digest: vec![], sig: None, kem_ciphertext: Some(vec![]), } @@ -145,10 +142,14 @@ impl SecParam { SecParam::D512 => 136, } } +} - pub fn validate(&self) -> Result<(), OperationError> { - match self { - SecParam::D224 | SecParam::D256 | SecParam::D384 | SecParam::D512 => Ok(()), - } +pub trait BitLength { + fn bit_length(&self) -> u64; +} + +impl BitLength for SecParam { + fn bit_length(&self) -> u64 { + *self as u64 } } diff --git a/src/main.rs b/src/main.rs index 0b82ccb..61616bf 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,4 @@ -use capycrypt::{ecc::keypair::KeyPair, sha3::hashable::Hashable, Message, SecParam}; +use capycrypt::{ecc::keypair::KeyPair, sha3::hashable::SpongeHashable, Message, SecParam}; use structopt::StructOpt; #[derive(Debug, StructOpt)] @@ -47,13 +47,9 @@ fn main() { let sec_param = SecParam::try_from(bits) .expect("Unsupported security parameter. Use 224, 256, 384, or 512"); - data.compute_sha3_hash(&sec_param) - .expect("An error occurred during hash computation."); + data.compute_sha3_hash(sec_param); - match data.digest { - Ok(digest) => println!("Hash: {}", hex::encode(digest)), - Err(_) => eprintln!("Error: Hash computation failed"), - } + println!("Hash: {}", hex::encode(data.digest)) } Command::NewKeypair { @@ -64,8 +60,7 @@ fn main() { output, } => { let sec_param = SecParam::try_from(bits).expect("Unsupported security parameter."); - let kp = KeyPair::new(pw.as_bytes(), owner, &sec_param) - .expect("Unable to generate the requested key pair"); + let kp = KeyPair::new(pw.as_bytes(), owner, sec_param); let _ = kp.write_to_file(&output); } diff --git a/src/sha3/aux_functions.rs b/src/sha3/aux_functions.rs index 5444bfa..a6103b1 100644 --- a/src/sha3/aux_functions.rs +++ b/src/sha3/aux_functions.rs @@ -102,9 +102,9 @@ pub mod byte_utils { local.format("%Y-%m-%d %H:%M:%S").to_string() } - pub(crate) fn bytes_to_scalar(in_bytes: Vec) -> Scalar { + pub(crate) fn bytes_to_scalar(in_bytes: &[u8]) -> Scalar { Scalar { - val: (U448::from_be_slice(&in_bytes)), + val: (U448::from_be_slice(in_bytes)), } } diff --git a/src/sha3/constants.rs b/src/sha3/constants.rs index 6c6f16e..9b51c9d 100644 --- a/src/sha3/constants.rs +++ b/src/sha3/constants.rs @@ -1,4 +1,4 @@ -use crate::{OperationError, SecParam}; +use crate::SecParam; pub const RATE_IN_BYTES: usize = 136; // SHA3-256 r = 1088 / 8 = 136 @@ -35,7 +35,7 @@ pub(crate) enum Capacity { impl Capacity { /// This function effectively maps a given bit length to the appropriate capacity value enum variant, - pub(crate) fn from_bit_length(bit_length: u64) -> Self { + pub(crate) fn from_bit_length(bit_length: usize) -> Self { match bit_length * 2 { x if x <= 448 => Capacity::C448, x if x <= 512 => Capacity::C512, @@ -45,31 +45,16 @@ impl Capacity { } } -/// OutputLength struct for storing the output length. -pub struct OutputLength { - value: u64, -} - -impl OutputLength { - const MAX_VALUE: u64 = u64::MAX; - - pub fn try_from(value: u64) -> Result { - if value < Self::MAX_VALUE { - Ok(OutputLength { value }) - } else { - Err(OperationError::UnsupportedSecurityParameter) - } - } - - pub fn value(&self) -> u64 { - self.value +impl BitLength for Capacity { + fn bit_length(&self) -> usize { + *self as usize } } /// Rate struct for storing the rate value. /// Rate is the number of input bits processed per invocation of the underlying function in sponge construction. pub struct Rate { - value: u64, + value: usize, } impl Rate { @@ -80,35 +65,23 @@ impl Rate { } } - pub fn value(&self) -> u64 { + pub fn value(&self) -> usize { self.value } } pub trait BitLength { - fn bit_length(&self) -> u64; -} - -impl BitLength for Capacity { - fn bit_length(&self) -> u64 { - *self as u64 - } + fn bit_length(&self) -> usize; } impl BitLength for SecParam { - fn bit_length(&self) -> u64 { - *self as u64 + fn bit_length(&self) -> usize { + *self as usize } } impl BitLength for Rate { - fn bit_length(&self) -> u64 { + fn bit_length(&self) -> usize { self.value } } - -impl BitLength for OutputLength { - fn bit_length(&self) -> u64 { - self.value() - } -} diff --git a/src/sha3/encryptable.rs b/src/sha3/encryptable.rs index 1005827..cb7fd68 100644 --- a/src/sha3/encryptable.rs +++ b/src/sha3/encryptable.rs @@ -5,7 +5,7 @@ use crate::{ }; pub trait SpongeEncryptable { - fn sha3_encrypt(&mut self, pw: &[u8], d: &SecParam) -> Result<(), OperationError>; + fn sha3_encrypt(&mut self, pw: &[u8], d: SecParam) -> Result<(), OperationError>; fn sha3_decrypt(&mut self, pw: &[u8]) -> Result<(), OperationError>; } @@ -26,19 +26,19 @@ impl SpongeEncryptable for Message { /// * `pw: &[u8]`: symmetric encryption key, can be blank but shouldnt be /// * `d: u64`: requested security strength in bits. Supported /// bitstrengths are 224, 256, 384, or 512. - fn sha3_encrypt(&mut self, pw: &[u8], d: &SecParam) -> Result<(), OperationError> { - self.d = Some(*d); + fn sha3_encrypt(&mut self, pw: &[u8], d: SecParam) -> Result<(), OperationError> { + self.d = Some(d); let z = get_random_bytes(512); let mut ke_ka = z.clone(); ke_ka.extend_from_slice(pw); - let ke_ka = kmac_xof(&ke_ka, &[], 1024, "S", d)?; + let ke_ka = kmac_xof(&ke_ka, &[], 1024, "S", d); let (ke, ka) = ke_ka.split_at(64); self.digest = kmac_xof(ka, &self.msg, 512, "SKA", d); - let m = kmac_xof(ke, &[], (self.msg.len() * 8) as u64, "SKE", d)?; + let m = kmac_xof(ke, &[], self.msg.len() * 8, "SKE", d); xor_bytes(&mut self.msg, &m); self.sym_nonce = Some(z); @@ -47,10 +47,6 @@ impl SpongeEncryptable for Message { /// # Symmetric Decryption /// Decrypts a [`Message`] (z, c, t) under passphrase pw. - /// ## Assumes: - /// * well-formed encryption - /// * Some(Message.t) - /// * Some(Message.z) /// ## Replaces: /// * `Message.data` with result of decryption. /// * `Message.op_result` with result of comparision of `Message.t` == keyed hash of decryption. @@ -61,10 +57,7 @@ impl SpongeEncryptable for Message { /// ## Arguments: /// * `pw: &[u8]`: decryption password, can be blank fn sha3_decrypt(&mut self, pw: &[u8]) -> Result<(), OperationError> { - let d = self - .d - .as_ref() - .ok_or(OperationError::SecurityParameterNotSet)?; + let d = self.d.ok_or(OperationError::SecurityParameterNotSet)?; let mut z_pw = self .sym_nonce @@ -73,26 +66,20 @@ impl SpongeEncryptable for Message { .clone(); z_pw.extend_from_slice(pw); - let ke_ka = kmac_xof(&z_pw, &[], 1024, "S", d)?; + let ke_ka = kmac_xof(&z_pw, &[], 1024, "S", d); let (ke, ka) = ke_ka.split_at(64); - let m = kmac_xof(ke, &[], (self.msg.len() * 8) as u64, "SKE", d)?; + let m = kmac_xof(ke, &[], self.msg.len() * 8, "SKE", d); xor_bytes(&mut self.msg, &m); - let new_t = kmac_xof(ka, &self.msg, 512, "SKA", d)?; + let new_t = kmac_xof(ka, &self.msg, 512, "SKA", d); - self.op_result = if self - .digest - .as_ref() - .map_or(false, |digest| digest == &new_t) - { + if self.digest == new_t { Ok(()) } else { xor_bytes(&mut self.msg, &m); Err(OperationError::SHA3DecryptionFailure) - }; - - Ok(()) + } } } diff --git a/src/sha3/hashable.rs b/src/sha3/hashable.rs index 9454043..b06ea24 100644 --- a/src/sha3/hashable.rs +++ b/src/sha3/hashable.rs @@ -1,25 +1,24 @@ -use crate::{Message, OperationError, SecParam}; +use crate::{Message, SecParam}; use super::{ constants::BitLength, shake_functions::{kmac_xof, shake}, }; -pub trait Hashable { - fn compute_sha3_hash(&mut self, d: &SecParam) -> Result<(), OperationError>; - fn compute_tagged_hash(&mut self, pw: &[u8], s: &str, d: &SecParam); +pub trait SpongeHashable { + fn compute_sha3_hash(&mut self, d: SecParam); + fn compute_tagged_hash(&mut self, pw: &[u8], s: &str, d: SecParam); } -impl Hashable for Message { +impl SpongeHashable for Message { /// # Message Digest /// Computes SHA3-d hash of input. Does not consume input. /// Replaces `Message.digest` with result of operation. /// ## Arguments: /// * `d: u64`: requested security strength in bits. Supported /// bitstrengths are 224, 256, 384, or 512. - fn compute_sha3_hash(&mut self, d: &SecParam) -> Result<(), OperationError> { - self.digest = shake(&mut self.msg, d); - Ok(()) + fn compute_sha3_hash(&mut self, d: SecParam) { + self.digest = shake(&mut self.msg, d) } /// # Tagged Hash @@ -32,7 +31,7 @@ impl Hashable for Message { /// * `s: &mut str`: domain seperation string /// * `d: u64`: requested security strength in bits. Supported /// bitstrengths are 224, 256, 384, or 512. - fn compute_tagged_hash(&mut self, pw: &[u8], s: &str, d: &SecParam) { + fn compute_tagged_hash(&mut self, pw: &[u8], s: &str, d: SecParam) { self.digest = kmac_xof(pw, &self.msg, d.bit_length(), s, d); } } diff --git a/src/sha3/shake_functions.rs b/src/sha3/shake_functions.rs index 1f3437c..18b8f7f 100644 --- a/src/sha3/shake_functions.rs +++ b/src/sha3/shake_functions.rs @@ -10,10 +10,10 @@ use crate::{ aux_functions::nist_800_185::{byte_pad, encode_string, right_encode}, sponge::{sponge_absorb, sponge_squeeze}, }, - OperationError, SecParam, + SecParam, }; -use super::constants::{BitLength, Capacity, OutputLength, Rate, RATE_IN_BYTES}; +use super::constants::{BitLength, Capacity, Rate, RATE_IN_BYTES}; /// # SHA3-Keccak /// ref NIST FIPS 202. @@ -22,14 +22,14 @@ use super::constants::{BitLength, Capacity, OutputLength, Rate, RATE_IN_BYTES}; /// * `d: usize`: requested output length and security strength /// ## Returns: /// * `return -> Vec`: SHA3-d message digest -pub(crate) fn shake(n: &mut Vec, d: &dyn BitLength) -> Result, OperationError> { +pub(crate) fn shake(n: &mut Vec, d: impl BitLength) -> Vec { let bytes_to_pad = RATE_IN_BYTES - n.len() % RATE_IN_BYTES; match bytes_to_pad { 1 => n.extend_from_slice(&[0x86]), // delim suffix _ => n.extend_from_slice(&[0x06]), // delim suffix } let c = Capacity::from_bit_length(d.bit_length()); - Ok(sponge_squeeze(&mut sponge_absorb(n, &c), d, Rate::from(&c))) + sponge_squeeze(&mut sponge_absorb(n, c), d.bit_length(), Rate::from(&d)) } /// # Customizable SHAKE @@ -47,15 +47,7 @@ pub(crate) fn shake(n: &mut Vec, d: &dyn BitLength) -> Result, Opera /// length output and there is no possible way to know this value in advance. /// The only constraint on `l` from NIST is that it is a value less than /// the absurdly large 2^{2040}. -pub(crate) fn cshake( - x: &[u8], - l: u64, - n: &str, - s: &str, - d: &SecParam, -) -> Result, OperationError> { - d.validate()?; - +pub(crate) fn cshake(x: &[u8], l: usize, n: &str, s: &str, d: SecParam) -> Vec { let mut encoded_n = encode_string(n.as_bytes()); encoded_n.extend_from_slice(&encode_string(s.as_bytes())); @@ -65,17 +57,11 @@ pub(crate) fn cshake( out.extend_from_slice(x); out.push(0x04); - let length = OutputLength::try_from(l)?; - if n.is_empty() && s.is_empty() { - shake(&mut out, &length)?; + shake(&mut out, d); } - Ok(sponge_squeeze( - &mut sponge_absorb(&mut out, d), - &length, - Rate::from(d), - )) + sponge_squeeze(&mut sponge_absorb(&mut out, d), l, Rate::from(&d)) } /// # Keyed Message Authtentication @@ -91,13 +77,7 @@ pub(crate) fn cshake( /// /// ## Returns: /// * `return -> Vec`: kmac_xof of `x` under `k` -pub fn kmac_xof( - k: &[u8], - x: &[u8], - l: u64, - s: &str, - d: &SecParam, -) -> Result, OperationError> { +pub fn kmac_xof(k: &[u8], x: &[u8], l: usize, s: &str, d: SecParam) -> Vec { let mut encode_k = encode_string(k); let bytepad_w = d.bytepad_value(); let mut bp = byte_pad(&mut encode_k, bytepad_w); @@ -112,7 +92,7 @@ pub fn kmac_xof( /// TESTS #[cfg(test)] mod shake_tests { - use crate::{sha3::hashable::Hashable, Message, SecParam}; + use crate::{sha3::hashable::SpongeHashable, Message, SecParam}; #[test] fn test_shake_224() { @@ -121,24 +101,17 @@ mod shake_tests { 0x6b, 0x4e, 0x03, 0x42, 0x36, 0x67, 0xdb, 0xb7, 0x3b, 0x6e, 0x15, 0x45, 0x4f, 0x0e, 0xb1, 0xab, 0xd4, 0x59, 0x7f, 0x9a, 0x1b, 0x07, 0x8e, 0x3f, 0x5b, 0x5a, 0x6b, 0xc7, ]; - assert!(data.compute_sha3_hash(&SecParam::D224).is_ok()); - assert!(data - .digest - .as_ref() - .map(|digest| *digest == expected.to_vec()) - .unwrap_or(false)); + + data.compute_sha3_hash(SecParam::D224); + assert!(data.digest == expected.to_vec()); let mut data = Message::new("test".as_bytes().to_vec()); let expected: [u8; 28] = [ 0x37, 0x97, 0xbf, 0x0a, 0xfb, 0xbf, 0xca, 0x4a, 0x7b, 0xbb, 0xa7, 0x60, 0x2a, 0x2b, 0x55, 0x27, 0x46, 0x87, 0x65, 0x17, 0xa7, 0xf9, 0xb7, 0xce, 0x2d, 0xb0, 0xae, 0x7b, ]; - assert!(data.compute_sha3_hash(&SecParam::D224).is_ok()); - assert!(data - .digest - .as_ref() - .map(|digest| *digest == expected.to_vec()) - .unwrap_or(false)); + data.compute_sha3_hash(SecParam::D224); + assert!(data.digest == expected.to_vec()); } #[test] @@ -149,12 +122,8 @@ mod shake_tests { 0xd6, 0x62, 0xf5, 0x80, 0xff, 0x4d, 0xe4, 0x3b, 0x49, 0xfa, 0x82, 0xd8, 0x0a, 0x4b, 0x80, 0xf8, 0x43, 0x4a, ]; - assert!(data.compute_sha3_hash(&SecParam::D256).is_ok()); - assert!(data - .digest - .as_ref() - .map(|digest| *digest == expected.to_vec()) - .unwrap_or(false)); + data.compute_sha3_hash(SecParam::D256); + assert!(data.digest == expected.to_vec()); let mut data = Message::new("test".as_bytes().to_vec()); let expected: [u8; 32] = [ @@ -162,12 +131,8 @@ mod shake_tests { 0x00, 0xe3, 0x46, 0xe2, 0x76, 0xae, 0x66, 0x4e, 0x45, 0xee, 0x80, 0x74, 0x55, 0x74, 0xe2, 0xf5, 0xab, 0x80, ]; - assert!(data.compute_sha3_hash(&SecParam::D256).is_ok()); - assert!(data - .digest - .as_ref() - .map(|digest| *digest == expected.to_vec()) - .unwrap_or(false)); + data.compute_sha3_hash(SecParam::D256); + assert!(data.digest == expected.to_vec()); } #[test] @@ -179,12 +144,8 @@ mod shake_tests { 0xee, 0x98, 0x3a, 0x2a, 0xc3, 0x71, 0x38, 0x31, 0x26, 0x4a, 0xdb, 0x47, 0xfb, 0x6b, 0xd1, 0xe0, 0x58, 0xd5, 0xf0, 0x04, ]; - assert!(data.compute_sha3_hash(&SecParam::D384).is_ok()); - assert!(data - .digest - .as_ref() - .map(|digest| *digest == expected.to_vec()) - .unwrap_or(false)); + data.compute_sha3_hash(SecParam::D384); + assert!(data.digest == expected.to_vec()); let mut data = Message::new("test".as_bytes().to_vec()); let expected: [u8; 48] = [ @@ -193,12 +154,8 @@ mod shake_tests { 0xf0, 0xf1, 0xb4, 0x1e, 0xec, 0xb9, 0xdb, 0x3f, 0xf2, 0x19, 0x00, 0x7c, 0x4e, 0x09, 0x72, 0x60, 0xd5, 0x86, 0x21, 0xbd, ]; - assert!(data.compute_sha3_hash(&SecParam::D384).is_ok()); - assert!(data - .digest - .as_ref() - .map(|digest| *digest == expected.to_vec()) - .unwrap_or(false)); + data.compute_sha3_hash(SecParam::D384); + assert!(data.digest == expected.to_vec()); } #[test] @@ -211,12 +168,8 @@ mod shake_tests { 0xf2, 0xe9, 0xb3, 0xca, 0x9f, 0x48, 0x4f, 0x52, 0x1d, 0x0c, 0xe4, 0x64, 0x34, 0x5c, 0xc1, 0xae, 0xc9, 0x67, 0x79, 0x14, 0x9c, 0x14, ]; - assert!(data.compute_sha3_hash(&SecParam::D512).is_ok()); - assert!(data - .digest - .as_ref() - .map(|digest| *digest == expected.to_vec()) - .unwrap_or(false)); + data.compute_sha3_hash(SecParam::D512); + assert!(data.digest == expected.to_vec()); } #[test] @@ -229,13 +182,9 @@ mod shake_tests { 0xa4, 0xa3, 0x81, 0x72, 0xbf, 0x11, 0x42, 0xa6, 0xa9, 0xc1, 0x93, 0x0e, 0x50, 0xdf, 0x03, 0x90, 0x43, 0x12, ]; - data.compute_tagged_hash(&pw, &s, &SecParam::D256); + data.compute_tagged_hash(&pw, &s, SecParam::D256); - assert!(data - .digest - .as_ref() - .map(|digest| *digest == expected.to_vec()) - .unwrap_or(false)); + assert!(data.digest == expected.to_vec()); } #[test] @@ -249,13 +198,9 @@ mod shake_tests { 0x63, 0xf4, 0xca, 0x0b, 0x65, 0x83, 0x6f, 0x52, 0x61, 0xee, 0x64, 0x64, 0x4c, 0xe5, 0xa8, 0x84, 0x56, 0xd3, 0xd3, 0x0e, 0xfb, 0xed, ]; - data.compute_tagged_hash(&pw, "", &SecParam::D512); + data.compute_tagged_hash(&pw, "", SecParam::D512); - assert!(data - .digest - .as_ref() - .map(|digest| *digest == expected.to_vec()) - .unwrap_or(false)); + assert!(data.digest == expected.to_vec()); } } @@ -272,7 +217,7 @@ mod cshake_tests { let n = ""; let s = "Email Signature"; - let res = cshake(&data, 256, n, s, &SecParam::D256).unwrap(); + let res = cshake(&data, 256, n, s, SecParam::D256); let expected: [u8; 32] = [ 0xc5, 0x22, 0x1d, 0x50, 0xe4, 0xf8, 0x22, 0xd9, 0x6a, 0x2e, 0x88, 0x81, 0xa9, 0x61, 0x42, 0x0f, 0x29, 0x4b, 0x7b, 0x24, 0xfe, 0x3d, 0x20, 0x94, 0xba, 0xed, 0x2c, 0x65, @@ -286,7 +231,7 @@ mod cshake_tests { let data = NIST_DATA_SPONGE_INIT; let n = ""; let s = "Email Signature"; - let res = cshake(&data, 512, n, s, &SecParam::D512).unwrap(); + let res = cshake(&data, 512, n, s, SecParam::D512); let expected: [u8; 64] = [ 0x07, 0xdc, 0x27, 0xb1, 0x1e, 0x51, 0xfb, 0xac, 0x75, 0xbc, 0x7b, 0x3c, 0x1d, 0x98, 0x3e, 0x8b, 0x4b, 0x85, 0xfb, 0x1d, 0xef, 0xaf, 0x21, 0x89, 0x12, 0xac, 0x86, 0x43, @@ -314,7 +259,7 @@ mod kmac_tests { let s_str = "My Tagged Application"; let key_bytes = key_str; let data = hex::decode("00010203").unwrap(); - let res = kmac_xof(key_bytes.as_ref(), &data, 64, s_str, &SecParam::D512).unwrap(); + let res = kmac_xof(key_bytes.as_ref(), &data, 64, s_str, SecParam::D512); let expected = "1755133f1534752a"; assert_eq!(hex::encode(res), expected) } @@ -330,7 +275,7 @@ mod kmac_tests { let key_bytes = key_str; let data = NIST_DATA_SPONGE_INIT; - let res = kmac_xof(key_bytes.as_ref(), &data, 512, s_str, &SecParam::D512).unwrap(); + let res = kmac_xof(key_bytes.as_ref(), &data, 512, s_str, SecParam::D512); let expected: [u8; 64] = [ 0xd5, 0xbe, 0x73, 0x1c, 0x95, 0x4e, 0xd7, 0x73, 0x28, 0x46, 0xbb, 0x59, 0xdb, 0xe3, 0xa8, 0xe3, 0x0f, 0x83, 0xe7, 0x7a, 0x4b, 0xff, 0x44, 0x59, 0xf2, 0xf1, 0xc2, 0xb4, diff --git a/src/sha3/sponge.rs b/src/sha3/sponge.rs index dc38a95..1403196 100644 --- a/src/sha3/sponge.rs +++ b/src/sha3/sponge.rs @@ -8,13 +8,13 @@ use super::constants::{BitLength, Rate}; // * m: message to be absorbed // * capacity: security parameter which determines rate = bit_width - capacity // * return: a state consisting of 25 words of 64 bits each. -pub(crate) fn sponge_absorb(m: &mut Vec, capacity: &S) -> [u64; 25] { +pub(crate) fn sponge_absorb(m: &mut Vec, capacity: C) -> [u64; 25] { let c = capacity.bit_length(); - let r: u64 = (1600 - c) / 8; - if (m.len() % r as usize) != 0 { - pad_ten_one(m, r as usize); + let r = (1600 - c) / 8; + if (m.len() % r) != 0 { + pad_ten_one(m, r); } - bytes_to_state(m, r as usize) + bytes_to_state(m, r) } // Finalizes a state. @@ -23,18 +23,14 @@ pub(crate) fn sponge_absorb(m: &mut Vec, capacity: &S // * bit_length: requested output length in bits // * rate: security parameter // * return: digest of permuted states of length `bit_length`. -pub(crate) fn sponge_squeeze( - s: &mut [u64; 25], - bit_length: &S, - rate: Rate, -) -> Vec { +pub(crate) fn sponge_squeeze(s: &mut [u64; 25], bit_length: usize, rate: Rate) -> Vec { let mut out: Vec = Vec::new(); //FIPS 202 Algorithm 8 Step 8 - let block_size: usize = (rate.value() / 64) as usize; - while out.len() * 8 < bit_length.bit_length() as usize { + let block_size: usize = rate.value() / 64; + while out.len() * 8 < bit_length { out.append(&mut state_to_byte_array(&s[0..block_size])); keccakf_1600(s); //FIPS 202 Algorithm 8 Step 10 } - out.truncate((bit_length.bit_length() / 8) as usize); + out.truncate(bit_length / 8); out } diff --git a/tests/aes_tests.rs b/tests/aes_tests.rs index 7af64a7..c548bf6 100644 --- a/tests/aes_tests.rs +++ b/tests/aes_tests.rs @@ -12,9 +12,7 @@ mod aes_modes_tests { let mut input = Message::new(get_random_bytes(5242880)); let _ = input.aes_encrypt_cbc(&key); // Encrypt the input - let _ = input.aes_decrypt_cbc(&key); // Decrypt the input - - assert!(input.op_result.is_ok()); // Verify operation success + assert!(input.aes_decrypt_cbc(&key).is_ok()); // Verify operation success } #[test] @@ -25,8 +23,7 @@ mod aes_modes_tests { let mut input = Message::new(get_random_bytes(5242880)); let _ = input.aes_encrypt_cbc(&key); // Encrypt the input - let _ = input.aes_decrypt_cbc(&key); // Decrypt the input - assert!(input.op_result.is_ok()); // Verify operation success + assert!(input.aes_decrypt_cbc(&key).is_ok()); // Verify operation success } #[test] @@ -37,8 +34,7 @@ mod aes_modes_tests { let mut input = Message::new(get_random_bytes(5242880)); let _ = input.aes_encrypt_cbc(&key); // Encrypt the input - let _ = input.aes_decrypt_cbc(&key); // Decrypt the input - assert!(input.op_result.is_ok()); // Verify operation success + assert!(input.aes_decrypt_cbc(&key).is_ok()); // Verify operation success } #[test] @@ -49,9 +45,7 @@ mod aes_modes_tests { let mut input = Message::new(get_random_bytes(5242880)); let _ = input.aes_encrypt_ctr(&key); // Encrypt the input - let _ = input.aes_decrypt_ctr(&key); // Decrypt the input - - assert!(input.op_result.is_ok()); // Verify operation success + assert!(input.aes_decrypt_ctr(&key).is_ok()); // Verify operation success } #[test] @@ -62,9 +56,7 @@ mod aes_modes_tests { let mut input = Message::new(get_random_bytes(5242880)); let _ = input.aes_encrypt_ctr(&key); // Encrypt the input - let _ = input.aes_decrypt_ctr(&key); // Decrypt the input - - assert!(input.op_result.is_ok()); // Verify operation success + assert!(input.aes_decrypt_ctr(&key).is_ok()); // Verify operation success } #[test] @@ -75,9 +67,7 @@ mod aes_modes_tests { let mut input = Message::new(get_random_bytes(5242880)); let _ = input.aes_encrypt_ctr(&key); // Encrypt the input - let _ = input.aes_decrypt_ctr(&key); // Decrypt the input - - assert!(input.op_result.is_ok()); // Verify operation success + assert!(input.aes_decrypt_ctr(&key).is_ok()); // Verify operation success } } diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index 0bdedff..45514e0 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -1,216 +1,254 @@ -#[cfg(test)] -pub mod ops_tests { - use std::time::Instant; - use tempfile::tempdir; - +#[test] +pub fn test_kem_enc_512() { use capycrypt::{ - ecc::{encryptable::KeyEncryptable, keypair::KeyPair, signable::Signable}, kem::{encryptable::KEMEncryptable, keypair::kem_keygen}, - sha3::{aux_functions::byte_utils::get_random_bytes, encryptable::SpongeEncryptable}, + sha3::aux_functions::byte_utils::get_random_bytes, Message, SecParam, }; - #[test] - pub fn test_sym_enc_512() { - let pw = get_random_bytes(64); - let mut msg = Message::new(get_random_bytes(5242880)); + // Get 5mb random data + let mut msg = Message::new(get_random_bytes(5242880)); - assert!(msg.sha3_encrypt(&pw, &SecParam::D512).is_ok()); - assert!(msg.sha3_decrypt(&pw).is_ok()); + // Create a new ML-KEM public/private keypair + let (kem_pub_key, kem_priv_key) = kem_keygen(); + // Encrypt the message + assert!(msg.kem_encrypt(&kem_pub_key, SecParam::D256).is_ok()); + // Decrypt and verify + assert!(msg.kem_decrypt(&kem_priv_key).is_ok()); +} - assert!(msg.op_result.is_ok()); - } - #[test] - pub fn test_sym_enc_256() { - let pw = get_random_bytes(64); - let mut msg = Message::new(get_random_bytes(5242880)); +#[test] +fn test_key_gen_enc_dec_256() { + use capycrypt::{ + ecc::{encryptable::KeyEncryptable, keypair::KeyPair}, + sha3::aux_functions::byte_utils::get_random_bytes, + Message, SecParam, + }; - assert!(msg.sha3_encrypt(&pw, &SecParam::D256).is_ok()); - assert!(msg.sha3_decrypt(&pw).is_ok()); + // Get 5mb random data + let mut msg = Message::new(get_random_bytes(5242880)); + + // Create a new elliptic-curve public/private keypair + let key_pair = KeyPair::new( + &get_random_bytes(64), // random password for key + "test key".to_string(), // label + SecParam::D256, // bit-security for key + ); + // Encrypt the message + assert!(msg.key_encrypt(&key_pair.pub_key, SecParam::D256).is_ok()); + // Decrypt and verify + assert!(msg.key_decrypt(&key_pair.priv_key).is_ok()); +} - assert!(msg.op_result.is_ok()); - } +#[test] +fn test_key_gen_enc_dec_512() { + use capycrypt::{ + ecc::{encryptable::KeyEncryptable, keypair::KeyPair}, + sha3::aux_functions::byte_utils::get_random_bytes, + Message, SecParam, + }; - #[test] - pub fn test_kem_enc_256() { - let mut msg = Message::new(get_random_bytes(5242880)); + let mut msg = Message::new(get_random_bytes(5242880)); + let key_pair = KeyPair::new( + &get_random_bytes(32), + "test key".to_string(), + SecParam::D512, + ); - let (kem_pub_key, kem_priv_key) = kem_keygen(); + assert!(msg.key_encrypt(&key_pair.pub_key, SecParam::D512).is_ok()); + assert!(msg.key_decrypt(&key_pair.priv_key).is_ok()); +} - assert!(msg.kem_encrypt(&kem_pub_key, &SecParam::D256).is_ok()); - assert!(msg.kem_decrypt(&kem_priv_key).is_ok()); - assert!(msg.op_result.is_ok()); - } +#[test] +pub fn test_signature_256() { + use capycrypt::{ + ecc::{keypair::KeyPair, signable::Signable}, + sha3::aux_functions::byte_utils::get_random_bytes, + Message, SecParam, + }; + // Get random 5mb + let mut msg = Message::new(get_random_bytes(5242880)); + // Create a new elliptic-curve public/private keypair + let key_pair = KeyPair::new( + &get_random_bytes(64), // random password for key + "test key".to_string(), // label + SecParam::D256, // bit-security for key + ); + // Sign with 256 bits of security + assert!(msg.sign(&key_pair, SecParam::D256).is_ok()); + // Verify signature + assert!(msg.verify(&key_pair.pub_key).is_ok()); +} - #[test] - pub fn test_kem_enc_512() { - let mut msg = Message::new(get_random_bytes(5242880)); +#[test] +fn test_hashable() { + use capycrypt::{sha3::hashable::SpongeHashable, Message, SecParam}; + // Hash the empty string + let mut data = Message::new(vec![]); + // Obtained from echo -n "" | openssl dgst -sha3-256 + let expected = "a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a"; + // Compute a SHA3 digest with 128 bits of security + data.compute_sha3_hash(SecParam::D256); + assert!(hex::encode(data.digest) == expected); +} - let (kem_pub_key, kem_priv_key) = kem_keygen(); +#[test] +fn test_symmetric_encryptable() { + use capycrypt::{ + aes::encryptable::AesEncryptable, + sha3::{aux_functions::byte_utils::get_random_bytes, encryptable::SpongeEncryptable}, + Message, SecParam, + }; + // Get a random password + let pw = get_random_bytes(16); + // Get 5mb random data + let mut msg = Message::new(get_random_bytes(5242880)); + // Encrypt the data + assert!(msg.aes_encrypt_ctr(&pw).is_ok()); + // Decrypt the data + assert!(msg.aes_decrypt_ctr(&pw).is_ok()); + // Encrypt the data + assert!(msg.sha3_encrypt(&pw, SecParam::D512).is_ok()); + // Decrypt and verify + assert!(msg.sha3_decrypt(&pw).is_ok()); +} - assert!(msg.kem_encrypt(&kem_pub_key, &SecParam::D512).is_ok()); - assert!(msg.kem_decrypt(&kem_priv_key).is_ok()); - assert!(msg.op_result.is_ok()); - } +#[test] +pub fn test_signature_512() { + use capycrypt::{ + ecc::{keypair::KeyPair, signable::Signable}, + sha3::aux_functions::byte_utils::get_random_bytes, + Message, SecParam, + }; - #[test] - fn test_key_gen_enc_dec_256() { - let mut msg = Message::new(get_random_bytes(5242880)); - let key_pair = KeyPair::new( - &get_random_bytes(64), - "test key".to_string(), - &SecParam::D256, - ) - .unwrap(); + let mut msg = Message::new(get_random_bytes(5242880)); + let pw = get_random_bytes(64); + let key_pair = KeyPair::new(&pw, "test key".to_string(), SecParam::D512); - assert!(msg.key_encrypt(&key_pair.pub_key, &SecParam::D256).is_ok()); - assert!(msg.key_decrypt(&key_pair.priv_key).is_ok()); + assert!(msg.sign(&key_pair, SecParam::D512).is_ok()); + assert!(msg.verify(&key_pair.pub_key).is_ok()); +} - assert!(msg.op_result.is_ok()); - } +// This test shouldnt have a huge variation between key sizes due to the fixed-time +// nature of the lookup table being used for scalar decomposition in the +// variable_base multiplication algorithm. +// ## OBSERVATION: +// key size larger than message has timing variation on larger values of i +#[test] +fn test_sig_timing_side_channel() { + use capycrypt::{ + ecc::{keypair::KeyPair, signable::Signable}, + sha3::aux_functions::byte_utils::get_random_bytes, + Message, SecParam, + }; + use std::time::Instant; - #[test] - fn test_key_gen_enc_dec_512() { + for i in 0..10 { let mut msg = Message::new(get_random_bytes(5242880)); - let key_pair = KeyPair::new( - &get_random_bytes(32), - "test key".to_string(), - &SecParam::D512, - ) - .unwrap(); + let pw = get_random_bytes(1 << i); + let key_pair = KeyPair::new(&pw, "test key".to_string(), SecParam::D512); - assert!(msg.key_encrypt(&key_pair.pub_key, &SecParam::D512).is_ok()); - assert!(msg.key_decrypt(&key_pair.priv_key).is_ok()); - - assert!(msg.op_result.is_ok()); + let now = Instant::now(); + let _ = msg.sign(&key_pair, SecParam::D512); + println!("{} needed {} microseconds", i, now.elapsed().as_micros()); + let _ = msg.verify(&key_pair.pub_key); } - #[test] - pub fn test_signature_256() { - let mut msg = Message::new(get_random_bytes(5242880)); - let pw = get_random_bytes(64); - let key_pair = KeyPair::new(&pw, "test key".to_string(), &SecParam::D256).unwrap(); +} - assert!(msg.sign(&key_pair, &SecParam::D256).is_ok()); - assert!(msg.verify(&key_pair.pub_key).is_ok()); +#[test] +fn test_reading_writing_keypair() { + use capycrypt::{ + ecc::keypair::KeyPair, sha3::aux_functions::byte_utils::get_random_bytes, SecParam, + }; - assert!(msg.op_result.is_ok()); - } + use tempfile::tempdir; - #[test] - pub fn test_signature_512() { - let mut msg = Message::new(get_random_bytes(5242880)); - let pw = get_random_bytes(64); - let key_pair = KeyPair::new(&pw, "test key".to_string(), &SecParam::D512).unwrap(); + let key_pair = KeyPair::new( + &get_random_bytes(32), + "test key".to_string(), + SecParam::D512, + ); - assert!(msg.sign(&key_pair, &SecParam::D512).is_ok()); - assert!(msg.verify(&key_pair.pub_key).is_ok()); + let temp_dir = tempdir().expect("Failed to create temporary directory"); + let temp_file_path = temp_dir.path().join("read_write_keypair.json"); - assert!(msg.op_result.is_ok()); - } + let _ = key_pair.write_to_file(temp_file_path.to_str().unwrap()); + let read_key_pair = KeyPair::read_from_file(temp_file_path.to_str().unwrap()) + .expect("Failed to read key pair from file"); - // This test shouldnt have a huge variation between key sizes due to the fixed-time - // nature of the lookup table being used for scalar decomposition in the - // variable_base multiplication algorithm. - // ## OBSERVATION: - // key size larger than message has timing variation on larger values of i - #[test] - fn test_sig_timing_side_channel() { - for i in 0..10 { - let mut msg = Message::new(get_random_bytes(5242880)); - let pw = get_random_bytes(1 << i); - let key_pair = KeyPair::new(&pw, "test key".to_string(), &SecParam::D512).unwrap(); - - let now = Instant::now(); - let _ = msg.sign(&key_pair, &SecParam::D512); - println!("{} needed {} microseconds", i, now.elapsed().as_micros()); - let _ = msg.verify(&key_pair.pub_key); - assert!(msg.op_result.is_ok()); - } - } + assert_eq!(key_pair, read_key_pair); +} - #[test] - fn test_reading_writing_keypair() { - let key_pair = KeyPair::new( - &get_random_bytes(32), - "test key".to_string(), - &SecParam::D512, - ) - .expect("Failed to create key pair"); - - let temp_dir = tempdir().expect("Failed to create temporary directory"); - let temp_file_path = temp_dir.path().join("read_write_keypair.json"); - - let _ = key_pair.write_to_file(temp_file_path.to_str().unwrap()); - let read_key_pair = KeyPair::read_from_file(temp_file_path.to_str().unwrap()) - .expect("Failed to read key pair from file"); - - assert_eq!(key_pair, read_key_pair); - } +#[test] +pub fn test_signature_512_read_keypair_from_file() { + use capycrypt::{ + ecc::{keypair::KeyPair, signable::Signable}, + sha3::aux_functions::byte_utils::get_random_bytes, + Message, SecParam, + }; - #[test] - pub fn test_signature_512_read_keypair_from_file() { - let mut msg = Message::new(get_random_bytes(5242880)); - let pw = get_random_bytes(64); + use tempfile::tempdir; - let key_pair = KeyPair::new(&pw, "test key".to_string(), &SecParam::D512) - .expect("Failed to create key pair"); + let mut msg = Message::new(get_random_bytes(5242880)); + let pw = get_random_bytes(64); - let temp_dir = tempdir().expect("Failed to create temporary directory"); - let temp_file_path: std::path::PathBuf = temp_dir.path().join("read_write_keypair.json"); + let key_pair = KeyPair::new(&pw, "test key".to_string(), SecParam::D512); - let _ = key_pair.write_to_file(temp_file_path.to_str().unwrap()); - let read_key_pair = KeyPair::read_from_file(temp_file_path.to_str().unwrap()) - .expect("Failed to read key pair from file"); + let temp_dir = tempdir().expect("Failed to create temporary directory"); + let temp_file_path: std::path::PathBuf = temp_dir.path().join("read_write_keypair.json"); - assert!(msg.sign(&read_key_pair, &SecParam::D512).is_ok()); - assert!(msg.verify(&read_key_pair.pub_key).is_ok()); - assert!(msg.op_result.is_ok()); - } + let _ = key_pair.write_to_file(temp_file_path.to_str().unwrap()); + let read_key_pair = KeyPair::read_from_file(temp_file_path.to_str().unwrap()) + .expect("Failed to read key pair from file"); - #[test] - pub fn test_signature_512_read_message_from_file() { - let temp_dir = tempdir().expect("Failed to create temporary directory"); - let temp_file_path: std::path::PathBuf = temp_dir.path().join("temp_message.json"); - Message::new(get_random_bytes(5242880)) - .write_to_file(temp_file_path.to_str().unwrap()) - .unwrap(); + assert!(msg.sign(&read_key_pair, SecParam::D512).is_ok()); + assert!(msg.verify(&read_key_pair.pub_key).is_ok()); +} + +#[test] +pub fn test_signature_512_read_message_from_file() { + use capycrypt::{ + ecc::{keypair::KeyPair, signable::Signable}, + sha3::aux_functions::byte_utils::get_random_bytes, + Message, SecParam, + }; - let mut initial_msg = Message::read_from_file(temp_file_path.to_str().unwrap()).unwrap(); + use tempfile::tempdir; - let pw = get_random_bytes(64); - let key_pair = KeyPair::new(&pw, "test key".to_string(), &SecParam::D512).unwrap(); + let temp_dir = tempdir().expect("Failed to create temporary directory"); + let temp_file_path: std::path::PathBuf = temp_dir.path().join("temp_message.json"); + Message::new(get_random_bytes(5242880)) + .write_to_file(temp_file_path.to_str().unwrap()) + .unwrap(); - assert!(initial_msg.sign(&key_pair, &SecParam::D512).is_ok()); + let mut initial_msg = Message::read_from_file(temp_file_path.to_str().unwrap()).unwrap(); - initial_msg - .write_to_file(temp_file_path.to_str().unwrap()) - .unwrap(); + let pw = get_random_bytes(64); + let key_pair = KeyPair::new(&pw, "test key".to_string(), SecParam::D512); - let mut signed_msg = Message::read_from_file(temp_file_path.to_str().unwrap()).unwrap(); + assert!(initial_msg.sign(&key_pair, SecParam::D512).is_ok()); - assert!(signed_msg.verify(&key_pair.pub_key).is_ok()); + initial_msg + .write_to_file(temp_file_path.to_str().unwrap()) + .unwrap(); - assert!(signed_msg.op_result.is_ok()); - } + let mut signed_msg = Message::read_from_file(temp_file_path.to_str().unwrap()).unwrap(); + + assert!(signed_msg.verify(&key_pair.pub_key).is_ok()); } #[cfg(test)] mod decryption_test { - use capycrypt::ecc::encryptable::KeyEncryptable; - use capycrypt::ecc::keypair::KeyPair; - use capycrypt::sha3::aux_functions::byte_utils::get_random_bytes; - use capycrypt::sha3::encryptable::SpongeEncryptable; - use capycrypt::Message; - use capycrypt::SecParam::D512; - - /// Testing a security parameters whether the failed decryption preserves - /// the original encrypted text. If an encrypted text is decrypted with a wrong password, - /// then the original encrypted message should remain the same. - /// - /// Note: Message were cloned for the test purposes, but in a production setting, - /// clone() will not be used, as the operation is done in memory. - /// Although a single security parameter is tested, - /// it should work on the remaining security parameters. + use capycrypt::{ + ecc::{encryptable::KeyEncryptable, keypair::KeyPair}, + sha3::{aux_functions::byte_utils::get_random_bytes, encryptable::SpongeEncryptable}, + Message, + SecParam::D512, + }; + + // Testing a security parameters whether the failed decryption preserves + // the original encrypted text. If an encrypted text is decrypted with a wrong password, + // then the original encrypted message should remain the same. #[test] fn test_sha3_decrypt_handling_bad_input() { let pw1 = get_random_bytes(64); @@ -218,33 +256,30 @@ mod decryption_test { // D512 let mut new_msg = Message::new(get_random_bytes(523)); - let _ = new_msg.sha3_encrypt(&pw1, &D512); + let _ = new_msg.sha3_encrypt(&pw1, D512); let msg2 = new_msg.msg.clone(); - let _ = new_msg.sha3_decrypt(&pw2); + let res = new_msg.sha3_decrypt(&pw2); + assert!(res.is_err()); assert_eq!(msg2, new_msg.msg); } - /// Testing a security parameters whether the failed decryption preserves - /// the original encrypted text. If an encrypted text is decrypted with a wrong password, - /// then the original encrypted message should remain the same. - /// - /// Note: Message were cloned for the test purposes, but in a production setting, - /// clone() will not be used, as the operation is done in memory. - /// Although a single security parameter is tested, - /// it should work on the remaining security parameters. + // Testing a security parameters whether the failed decryption preserves + // the original encrypted text. If an encrypted text is decrypted with a wrong password, + // then the original encrypted message should remain the same. #[test] fn test_key_decrypt_handling_bad_input() { let mut new_msg = Message::new(get_random_bytes(125)); // D512 - let key_pair1 = KeyPair::new(&get_random_bytes(32), "test key".to_string(), &D512).unwrap(); - let key_pair2 = KeyPair::new(&get_random_bytes(32), "test key".to_string(), &D512).unwrap(); + let key_pair1 = KeyPair::new(&get_random_bytes(32), "test key".to_string(), D512); + let key_pair2 = KeyPair::new(&get_random_bytes(32), "test key".to_string(), D512); - let _ = new_msg.key_encrypt(&key_pair1.pub_key, &D512); + let _ = new_msg.key_encrypt(&key_pair1.pub_key, D512); let new_msg2 = new_msg.msg.clone(); - let _ = new_msg.key_decrypt(&key_pair2.priv_key); + let res = new_msg.key_decrypt(&key_pair2.priv_key); + assert!(res.is_err()); assert_eq!(*new_msg.msg, *new_msg2, "Message after reverting a failed decryption does not match the original encrypted message"); } }