diff --git a/frodo-kem/Cargo.toml b/frodo-kem/Cargo.toml index c2d37e0..83d67cb 100644 --- a/frodo-kem/Cargo.toml +++ b/frodo-kem/Cargo.toml @@ -11,7 +11,7 @@ license = "Apache-2.0 OR MIT" name = "frodo-kem" readme = "README.md" repository = "https://github.com/RustCrypto/KEMs/frodo-kem" -version = "0.3.0" +version = "0.4.0" [features] default = [ @@ -62,14 +62,20 @@ serde = { version = "1.0", features = ["derive"], optional = true } serdect = "0.3.0-rc.0" subtle = "2.6" thiserror = "2.0" -zeroize = "1" [target.'cfg(target_arch = "aarch64")'.dependencies] sha3 = { version = "0.10", features = ["asm"] } +zeroize = { version = "1", features = ["aarch64"] } + +[target.'cfg(any(target_arch = "x86_64", target_arch = "x86"))'.dependencies] +zeroize = { version = "1", features = ["simd"] } [target.'cfg(not(target_arch = "aarch64"))'.dependencies] sha3 = { version = "0.10" } +[target.'cfg(not(any(target_arch = "x86_64", target_arch = "x86", target_arch = "aarch64")))'.dependencies] +zeroize = "1" + [dev-dependencies] aes = "0.8" bincode = "1.3" @@ -95,21 +101,14 @@ features = [ ] rustdoc-args = ["--cfg", "docsrs"] -[lints.rust] -missing_docs = "warn" -missing_debug_implementations = "warn" -missing_copy_implementations = "warn" -trivial_casts = "warn" -trivial_numeric_casts = "warn" -unused = "warn" - -[lints.clippy] -mod_module_files = "warn" -unwrap_used = "deny" - [[bench]] name = "safe-oqs" harness = false -path = "bench/safe_oqs.rs" +path = "benches/safe_oqs.rs" required-features = ["efrodo"] +[[bench]] +name = "frodo" +harness = false +path = "benches/frodo.rs" +required-features = ["frodo"] \ No newline at end of file diff --git a/frodo-kem/benches/frodo.rs b/frodo-kem/benches/frodo.rs new file mode 100644 index 0000000..b538700 --- /dev/null +++ b/frodo-kem/benches/frodo.rs @@ -0,0 +1,175 @@ +use criterion::{ + criterion_group, criterion_main, measurement::Measurement, BenchmarkGroup, Criterion, +}; +use frodo_kem::*; +use rand_core::SeedableRng; + +fn bench_keygen<'a, M: Measurement>(group: &mut BenchmarkGroup<'a, M>) { + let mut rng = rand_chacha::ChaCha8Rng::from_entropy(); + group.bench_function("KeyGen 640Aes", |b| { + b.iter(|| { + let (_pk, _sk) = Algorithm::FrodoKem640Aes.generate_keypair(&mut rng); + }); + }); + + group.bench_function("KeyGen 976Aes", |b| { + b.iter(|| { + let (_pk, _sk) = Algorithm::FrodoKem976Aes.generate_keypair(&mut rng); + }); + }); + + group.bench_function("KeyGen 1344Aes", |b| { + b.iter(|| { + let (_pk, _sk) = Algorithm::FrodoKem1344Aes.generate_keypair(&mut rng); + }); + }); + + group.bench_function("KeyGen 640Shake", |b| { + b.iter(|| { + let (_pk, _sk) = Algorithm::FrodoKem640Shake.generate_keypair(&mut rng); + }); + }); + + group.bench_function("KeyGen 976Shake", |b| { + b.iter(|| { + let (_pk, _sk) = Algorithm::FrodoKem976Shake.generate_keypair(&mut rng); + }); + }); + + group.bench_function("KeyGen 1344Shake", |b| { + b.iter(|| { + let (_pk, _sk) = Algorithm::FrodoKem1344Shake.generate_keypair(&mut rng); + }); + }); +} + +fn bench_encapsulate<'a, M: Measurement>(group: &mut BenchmarkGroup<'a, M>) { + let mut rng = rand_chacha::ChaCha8Rng::from_entropy(); + let (pk, _sk) = Algorithm::FrodoKem640Aes.generate_keypair(&mut rng); + group.bench_function("Encapsulate 640Aes", |b| { + b.iter(|| { + let (_ct, _ss) = Algorithm::FrodoKem640Aes + .encapsulate_with_rng(&pk, &mut rng) + .unwrap(); + }); + }); + + let (pk, _sk) = Algorithm::FrodoKem976Aes.generate_keypair(&mut rng); + group.bench_function("Encapsulate 976Aes", |b| { + b.iter(|| { + let (_ct, _ss) = Algorithm::FrodoKem976Aes + .encapsulate_with_rng(&pk, &mut rng) + .unwrap(); + }); + }); + + let (pk, _sk) = Algorithm::FrodoKem1344Aes.generate_keypair(&mut rng); + group.bench_function("Encapsulate 1344Aes", |b| { + b.iter(|| { + let (_ct, _ss) = Algorithm::FrodoKem1344Aes + .encapsulate_with_rng(&pk, &mut rng) + .unwrap(); + }); + }); + + let (pk, _sk) = Algorithm::FrodoKem640Shake.generate_keypair(&mut rng); + group.bench_function("Encapsulate 640Shake", |b| { + b.iter(|| { + let (_ct, _ss) = Algorithm::FrodoKem640Shake + .encapsulate_with_rng(&pk, &mut rng) + .unwrap(); + }); + }); + + let (pk, _sk) = Algorithm::FrodoKem976Shake.generate_keypair(&mut rng); + group.bench_function("Encapsulate 976Shake", |b| { + b.iter(|| { + let (_ct, _ss) = Algorithm::FrodoKem976Shake + .encapsulate_with_rng(&pk, &mut rng) + .unwrap(); + }); + }); + + let (pk, _sk) = Algorithm::FrodoKem1344Shake.generate_keypair(&mut rng); + group.bench_function("Encapsulate 1344Shake", |b| { + b.iter(|| { + let (_ct, _ss) = Algorithm::FrodoKem1344Shake + .encapsulate_with_rng(&pk, &mut rng) + .unwrap(); + }); + }); +} + +fn bench_decapsulate<'a, M: Measurement>(group: &mut BenchmarkGroup<'a, M>) { + let mut rng = rand_chacha::ChaCha8Rng::from_entropy(); + let (pk, sk) = Algorithm::FrodoKem640Aes.generate_keypair(&mut rng); + let (ct, _ss) = Algorithm::FrodoKem640Aes + .encapsulate_with_rng(&pk, &mut rng) + .unwrap(); + group.bench_function("Decapsulate 640Aes", |b| { + b.iter(|| { + let (_ss, _mu) = Algorithm::FrodoKem640Aes.decapsulate(&sk, &ct).unwrap(); + }); + }); + + let (pk, sk) = Algorithm::FrodoKem976Aes.generate_keypair(&mut rng); + let (ct, _ss) = Algorithm::FrodoKem976Aes + .encapsulate_with_rng(&pk, &mut rng) + .unwrap(); + group.bench_function("Decapsulate 976Aes", |b| { + b.iter(|| { + let (_ss, _mu) = Algorithm::FrodoKem976Aes.decapsulate(&sk, &ct).unwrap(); + }); + }); + + let (pk, sk) = Algorithm::FrodoKem1344Aes.generate_keypair(&mut rng); + let (ct, _ss) = Algorithm::FrodoKem1344Aes + .encapsulate_with_rng(&pk, &mut rng) + .unwrap(); + group.bench_function("Decapsulate 1344Aes", |b| { + b.iter(|| { + let (_ss, _mu) = Algorithm::FrodoKem1344Aes.decapsulate(&sk, &ct).unwrap(); + }); + }); + + let (pk, sk) = Algorithm::FrodoKem640Shake.generate_keypair(&mut rng); + let (ct, _ss) = Algorithm::FrodoKem640Shake + .encapsulate_with_rng(&pk, &mut rng) + .unwrap(); + group.bench_function("Decapsulate 640Shake", |b| { + b.iter(|| { + let (_ss, _mu) = Algorithm::FrodoKem640Shake.decapsulate(&sk, &ct).unwrap(); + }); + }); + + let (pk, sk) = Algorithm::FrodoKem976Shake.generate_keypair(&mut rng); + let (ct, _ss) = Algorithm::FrodoKem976Shake + .encapsulate_with_rng(&pk, &mut rng) + .unwrap(); + group.bench_function("Decapsulate 976Shake", |b| { + b.iter(|| { + let (_ss, _mu) = Algorithm::FrodoKem976Shake.decapsulate(&sk, &ct).unwrap(); + }); + }); + + let (pk, sk) = Algorithm::FrodoKem1344Shake.generate_keypair(&mut rng); + let (ct, _ss) = Algorithm::FrodoKem1344Shake + .encapsulate_with_rng(&pk, &mut rng) + .unwrap(); + group.bench_function("Decapsulate 1344Shake", |b| { + b.iter(|| { + let (_ss, _mu) = Algorithm::FrodoKem1344Shake.decapsulate(&sk, &ct).unwrap(); + }); + }); +} + +fn bench_against_liboqs(c: &mut Criterion) { + let mut group = c.benchmark_group("FrodoKEM"); + bench_keygen(&mut group); + bench_encapsulate(&mut group); + bench_decapsulate(&mut group); + group.finish(); +} + +criterion_group!(benches, bench_against_liboqs); +criterion_main!(benches); diff --git a/frodo-kem/bench/safe_oqs.rs b/frodo-kem/benches/safe_oqs.rs similarity index 73% rename from frodo-kem/bench/safe_oqs.rs rename to frodo-kem/benches/safe_oqs.rs index 93015b0..4ebfa72 100644 --- a/frodo-kem/bench/safe_oqs.rs +++ b/frodo-kem/benches/safe_oqs.rs @@ -1,14 +1,15 @@ +//! Benchmarking FrodoKEM against liboqs use criterion::{ criterion_group, criterion_main, measurement::Measurement, BenchmarkGroup, Criterion, }; -use frodo_kem_rs::*; +use frodo_kem::*; use rand_core::SeedableRng; fn bench_keygen<'a, M: Measurement>(group: &mut BenchmarkGroup<'a, M>) { let mut rng = rand_chacha::ChaCha8Rng::from_entropy(); group.bench_function("KeyGen 640Aes", |b| { b.iter(|| { - let (_pk, _sk) = Algorithm::FrodoKem640Aes.generate_keypair(&mut rng); + let (_pk, _sk) = Algorithm::EphemeralFrodoKem640Aes.generate_keypair(&mut rng); }); }); let kem = safe_oqs::kem::Kem::new(safe_oqs::kem::Algorithm::FrodoKem640Aes).unwrap(); @@ -20,7 +21,7 @@ fn bench_keygen<'a, M: Measurement>(group: &mut BenchmarkGroup<'a, M>) { group.bench_function("KeyGen 976Aes", |b| { b.iter(|| { - let (_pk, _sk) = Algorithm::FrodoKem976Aes.generate_keypair(&mut rng); + let (_pk, _sk) = Algorithm::EphemeralFrodoKem976Aes.generate_keypair(&mut rng); }); }); let kem = safe_oqs::kem::Kem::new(safe_oqs::kem::Algorithm::FrodoKem976Aes).unwrap(); @@ -32,7 +33,7 @@ fn bench_keygen<'a, M: Measurement>(group: &mut BenchmarkGroup<'a, M>) { group.bench_function("KeyGen 1344Aes", |b| { b.iter(|| { - let (_pk, _sk) = Algorithm::FrodoKem1344Aes.generate_keypair(&mut rng); + let (_pk, _sk) = Algorithm::EphemeralFrodoKem1344Aes.generate_keypair(&mut rng); }); }); let kem = safe_oqs::kem::Kem::new(safe_oqs::kem::Algorithm::FrodoKem1344Aes).unwrap(); @@ -44,7 +45,7 @@ fn bench_keygen<'a, M: Measurement>(group: &mut BenchmarkGroup<'a, M>) { group.bench_function("KeyGen 640Shake", |b| { b.iter(|| { - let (_pk, _sk) = Algorithm::FrodoKem640Shake.generate_keypair(&mut rng); + let (_pk, _sk) = Algorithm::EphemeralFrodoKem640Shake.generate_keypair(&mut rng); }); }); let kem = safe_oqs::kem::Kem::new(safe_oqs::kem::Algorithm::FrodoKem640Shake).unwrap(); @@ -56,7 +57,7 @@ fn bench_keygen<'a, M: Measurement>(group: &mut BenchmarkGroup<'a, M>) { group.bench_function("KeyGen 976Shake", |b| { b.iter(|| { - let (_pk, _sk) = Algorithm::FrodoKem976Shake.generate_keypair(&mut rng); + let (_pk, _sk) = Algorithm::EphemeralFrodoKem976Shake.generate_keypair(&mut rng); }); }); let kem = safe_oqs::kem::Kem::new(safe_oqs::kem::Algorithm::FrodoKem976Shake).unwrap(); @@ -68,7 +69,7 @@ fn bench_keygen<'a, M: Measurement>(group: &mut BenchmarkGroup<'a, M>) { group.bench_function("KeyGen 1344Shake", |b| { b.iter(|| { - let (_pk, _sk) = Algorithm::FrodoKem1344Shake.generate_keypair(&mut rng); + let (_pk, _sk) = Algorithm::EphemeralFrodoKem1344Shake.generate_keypair(&mut rng); }); }); let kem = safe_oqs::kem::Kem::new(safe_oqs::kem::Algorithm::FrodoKem1344Shake).unwrap(); @@ -81,10 +82,10 @@ fn bench_keygen<'a, M: Measurement>(group: &mut BenchmarkGroup<'a, M>) { fn bench_encapsulate<'a, M: Measurement>(group: &mut BenchmarkGroup<'a, M>) { let mut rng = rand_chacha::ChaCha8Rng::from_entropy(); - let (pk, _sk) = Algorithm::FrodoKem640Aes.generate_keypair(&mut rng); + let (pk, _sk) = Algorithm::EphemeralFrodoKem640Aes.generate_keypair(&mut rng); group.bench_function("Encapsulate 640Aes", |b| { b.iter(|| { - let (_ct, _ss) = Algorithm::FrodoKem640Aes + let (_ct, _ss) = Algorithm::EphemeralFrodoKem640Aes .encapsulate_with_rng(&pk, &mut rng) .unwrap(); }); @@ -97,10 +98,10 @@ fn bench_encapsulate<'a, M: Measurement>(group: &mut BenchmarkGroup<'a, M>) { }); }); - let (pk, _sk) = Algorithm::FrodoKem976Aes.generate_keypair(&mut rng); + let (pk, _sk) = Algorithm::EphemeralFrodoKem976Aes.generate_keypair(&mut rng); group.bench_function("Encapsulate 976Aes", |b| { b.iter(|| { - let (_ct, _ss) = Algorithm::FrodoKem976Aes + let (_ct, _ss) = Algorithm::EphemeralFrodoKem976Aes .encapsulate_with_rng(&pk, &mut rng) .unwrap(); }); @@ -113,10 +114,10 @@ fn bench_encapsulate<'a, M: Measurement>(group: &mut BenchmarkGroup<'a, M>) { }); }); - let (pk, _sk) = Algorithm::FrodoKem1344Aes.generate_keypair(&mut rng); + let (pk, _sk) = Algorithm::EphemeralFrodoKem1344Aes.generate_keypair(&mut rng); group.bench_function("Encapsulate 1344Aes", |b| { b.iter(|| { - let (_ct, _ss) = Algorithm::FrodoKem1344Aes + let (_ct, _ss) = Algorithm::EphemeralFrodoKem1344Aes .encapsulate_with_rng(&pk, &mut rng) .unwrap(); }); @@ -129,10 +130,10 @@ fn bench_encapsulate<'a, M: Measurement>(group: &mut BenchmarkGroup<'a, M>) { }); }); - let (pk, _sk) = Algorithm::FrodoKem640Shake.generate_keypair(&mut rng); + let (pk, _sk) = Algorithm::EphemeralFrodoKem640Shake.generate_keypair(&mut rng); group.bench_function("Encapsulate 640Shake", |b| { b.iter(|| { - let (_ct, _ss) = Algorithm::FrodoKem640Shake + let (_ct, _ss) = Algorithm::EphemeralFrodoKem640Shake .encapsulate_with_rng(&pk, &mut rng) .unwrap(); }); @@ -145,10 +146,10 @@ fn bench_encapsulate<'a, M: Measurement>(group: &mut BenchmarkGroup<'a, M>) { }); }); - let (pk, _sk) = Algorithm::FrodoKem976Shake.generate_keypair(&mut rng); + let (pk, _sk) = Algorithm::EphemeralFrodoKem976Shake.generate_keypair(&mut rng); group.bench_function("Encapsulate 976Shake", |b| { b.iter(|| { - let (_ct, _ss) = Algorithm::FrodoKem976Shake + let (_ct, _ss) = Algorithm::EphemeralFrodoKem976Shake .encapsulate_with_rng(&pk, &mut rng) .unwrap(); }); @@ -161,10 +162,10 @@ fn bench_encapsulate<'a, M: Measurement>(group: &mut BenchmarkGroup<'a, M>) { }); }); - let (pk, _sk) = Algorithm::FrodoKem1344Shake.generate_keypair(&mut rng); + let (pk, _sk) = Algorithm::EphemeralFrodoKem1344Shake.generate_keypair(&mut rng); group.bench_function("Encapsulate 1344Shake", |b| { b.iter(|| { - let (_ct, _ss) = Algorithm::FrodoKem1344Shake + let (_ct, _ss) = Algorithm::EphemeralFrodoKem1344Shake .encapsulate_with_rng(&pk, &mut rng) .unwrap(); }); @@ -180,13 +181,15 @@ fn bench_encapsulate<'a, M: Measurement>(group: &mut BenchmarkGroup<'a, M>) { fn bench_decapsulate<'a, M: Measurement>(group: &mut BenchmarkGroup<'a, M>) { let mut rng = rand_chacha::ChaCha8Rng::from_entropy(); - let (pk, sk) = Algorithm::FrodoKem640Aes.generate_keypair(&mut rng); - let (ct, _ss) = Algorithm::FrodoKem640Aes + let (pk, sk) = Algorithm::EphemeralFrodoKem640Aes.generate_keypair(&mut rng); + let (ct, _ss) = Algorithm::EphemeralFrodoKem640Aes .encapsulate_with_rng(&pk, &mut rng) .unwrap(); group.bench_function("Decapsulate 640Aes", |b| { b.iter(|| { - let (_ss, _mu) = Algorithm::FrodoKem640Aes.decapsulate(&sk, &ct).unwrap(); + let (_ss, _mu) = Algorithm::EphemeralFrodoKem640Aes + .decapsulate(&sk, &ct) + .unwrap(); }); }); let kem = safe_oqs::kem::Kem::new(safe_oqs::kem::Algorithm::FrodoKem640Aes).unwrap(); @@ -198,13 +201,15 @@ fn bench_decapsulate<'a, M: Measurement>(group: &mut BenchmarkGroup<'a, M>) { }); }); - let (pk, sk) = Algorithm::FrodoKem976Aes.generate_keypair(&mut rng); - let (ct, _ss) = Algorithm::FrodoKem976Aes + let (pk, sk) = Algorithm::EphemeralFrodoKem976Aes.generate_keypair(&mut rng); + let (ct, _ss) = Algorithm::EphemeralFrodoKem976Aes .encapsulate_with_rng(&pk, &mut rng) .unwrap(); group.bench_function("Decapsulate 976Aes", |b| { b.iter(|| { - let (_ss, _mu) = Algorithm::FrodoKem976Aes.decapsulate(&sk, &ct).unwrap(); + let (_ss, _mu) = Algorithm::EphemeralFrodoKem976Aes + .decapsulate(&sk, &ct) + .unwrap(); }); }); let kem = safe_oqs::kem::Kem::new(safe_oqs::kem::Algorithm::FrodoKem976Aes).unwrap(); @@ -216,13 +221,15 @@ fn bench_decapsulate<'a, M: Measurement>(group: &mut BenchmarkGroup<'a, M>) { }); }); - let (pk, sk) = Algorithm::FrodoKem1344Aes.generate_keypair(&mut rng); - let (ct, _ss) = Algorithm::FrodoKem1344Aes + let (pk, sk) = Algorithm::EphemeralFrodoKem1344Aes.generate_keypair(&mut rng); + let (ct, _ss) = Algorithm::EphemeralFrodoKem1344Aes .encapsulate_with_rng(&pk, &mut rng) .unwrap(); group.bench_function("Decapsulate 1344Aes", |b| { b.iter(|| { - let (_ss, _mu) = Algorithm::FrodoKem1344Aes.decapsulate(&sk, &ct).unwrap(); + let (_ss, _mu) = Algorithm::EphemeralFrodoKem1344Aes + .decapsulate(&sk, &ct) + .unwrap(); }); }); let kem = safe_oqs::kem::Kem::new(safe_oqs::kem::Algorithm::FrodoKem1344Aes).unwrap(); @@ -234,13 +241,15 @@ fn bench_decapsulate<'a, M: Measurement>(group: &mut BenchmarkGroup<'a, M>) { }); }); - let (pk, sk) = Algorithm::FrodoKem640Shake.generate_keypair(&mut rng); - let (ct, _ss) = Algorithm::FrodoKem640Shake + let (pk, sk) = Algorithm::EphemeralFrodoKem640Shake.generate_keypair(&mut rng); + let (ct, _ss) = Algorithm::EphemeralFrodoKem640Shake .encapsulate_with_rng(&pk, &mut rng) .unwrap(); group.bench_function("Decapsulate 640Shake", |b| { b.iter(|| { - let (_ss, _mu) = Algorithm::FrodoKem640Shake.decapsulate(&sk, &ct).unwrap(); + let (_ss, _mu) = Algorithm::EphemeralFrodoKem640Shake + .decapsulate(&sk, &ct) + .unwrap(); }); }); let kem = safe_oqs::kem::Kem::new(safe_oqs::kem::Algorithm::FrodoKem640Shake).unwrap(); @@ -252,13 +261,15 @@ fn bench_decapsulate<'a, M: Measurement>(group: &mut BenchmarkGroup<'a, M>) { }); }); - let (pk, sk) = Algorithm::FrodoKem976Shake.generate_keypair(&mut rng); - let (ct, _ss) = Algorithm::FrodoKem976Shake + let (pk, sk) = Algorithm::EphemeralFrodoKem976Shake.generate_keypair(&mut rng); + let (ct, _ss) = Algorithm::EphemeralFrodoKem976Shake .encapsulate_with_rng(&pk, &mut rng) .unwrap(); group.bench_function("Decapsulate 976Shake", |b| { b.iter(|| { - let (_ss, _mu) = Algorithm::FrodoKem976Shake.decapsulate(&sk, &ct).unwrap(); + let (_ss, _mu) = Algorithm::EphemeralFrodoKem976Shake + .decapsulate(&sk, &ct) + .unwrap(); }); }); let kem = safe_oqs::kem::Kem::new(safe_oqs::kem::Algorithm::FrodoKem976Shake).unwrap(); @@ -270,13 +281,15 @@ fn bench_decapsulate<'a, M: Measurement>(group: &mut BenchmarkGroup<'a, M>) { }); }); - let (pk, sk) = Algorithm::FrodoKem1344Shake.generate_keypair(&mut rng); - let (ct, _ss) = Algorithm::FrodoKem1344Shake + let (pk, sk) = Algorithm::EphemeralFrodoKem1344Shake.generate_keypair(&mut rng); + let (ct, _ss) = Algorithm::EphemeralFrodoKem1344Shake .encapsulate_with_rng(&pk, &mut rng) .unwrap(); group.bench_function("Decapsulate 1344Shake", |b| { b.iter(|| { - let (_ss, _mu) = Algorithm::FrodoKem1344Shake.decapsulate(&sk, &ct).unwrap(); + let (_ss, _mu) = Algorithm::EphemeralFrodoKem1344Shake + .decapsulate(&sk, &ct) + .unwrap(); }); }); let kem = safe_oqs::kem::Kem::new(safe_oqs::kem::Algorithm::FrodoKem1344Shake).unwrap(); @@ -290,7 +303,7 @@ fn bench_decapsulate<'a, M: Measurement>(group: &mut BenchmarkGroup<'a, M>) { } fn bench_against_liboqs(c: &mut Criterion) { - let mut group = c.benchmark_group("FrodoKEM"); + let mut group = c.benchmark_group("eFrodoKEM"); bench_keygen(&mut group); bench_encapsulate(&mut group); bench_decapsulate(&mut group); diff --git a/frodo-kem/rustfmt.toml b/frodo-kem/rustfmt.toml index 4420db5..619d124 100644 --- a/frodo-kem/rustfmt.toml +++ b/frodo-kem/rustfmt.toml @@ -1,2 +1,5 @@ newline_style = "Unix" use_field_init_shorthand = true +reorder_imports = true +reorder_modules = true +reorder_impl_items = true diff --git a/frodo-kem/src/hazmat/models.rs b/frodo-kem/src/hazmat/models.rs index e91600b..3e7671a 100644 --- a/frodo-kem/src/hazmat/models.rs +++ b/frodo-kem/src/hazmat/models.rs @@ -499,14 +499,15 @@ pub struct FrodoKem(pub PhantomData<(P, E, S) ))] impl Params for FrodoKem { type Shake = P::Shake; - const N: usize = P::N; - const LOG_Q: usize = P::LOG_Q; - const EXTRACTED_BITS: usize = P::EXTRACTED_BITS; + + const BYTES_SALT: usize = P::BYTES_SALT; + const BYTES_SEED_SE: usize = P::BYTES_SEED_SE; const CDF_TABLE: &'static [u16] = P::CDF_TABLE; const CLAIMED_NIST_LEVEL: usize = P::CLAIMED_NIST_LEVEL; + const EXTRACTED_BITS: usize = P::EXTRACTED_BITS; + const LOG_Q: usize = P::LOG_Q; + const N: usize = P::N; const SHARED_SECRET_LENGTH: usize = P::SHARED_SECRET_LENGTH; - const BYTES_SEED_SE: usize = P::BYTES_SEED_SE; - const BYTES_SALT: usize = P::BYTES_SALT; } #[cfg(any( @@ -519,6 +520,7 @@ impl Params for FrodoKem { ))] impl Expanded for FrodoKem { const METHOD: &'static str = E::METHOD; + fn expand_a(&self, seed_a: &[u8], a: &mut [u16]) { E::expand_a(&E::default(), seed_a, a) } @@ -573,14 +575,15 @@ pub struct EphemeralFrodoKem(pub PhantomData< ))] impl Params for EphemeralFrodoKem { type Shake = P::Shake; - const N: usize = P::N; - const LOG_Q: usize = P::LOG_Q; - const EXTRACTED_BITS: usize = P::EXTRACTED_BITS; + + const BYTES_SALT: usize = P::BYTES_SALT; + const BYTES_SEED_SE: usize = P::BYTES_SEED_SE; const CDF_TABLE: &'static [u16] = P::CDF_TABLE; const CLAIMED_NIST_LEVEL: usize = P::CLAIMED_NIST_LEVEL; + const EXTRACTED_BITS: usize = P::EXTRACTED_BITS; + const LOG_Q: usize = P::LOG_Q; + const N: usize = P::N; const SHARED_SECRET_LENGTH: usize = P::SHARED_SECRET_LENGTH; - const BYTES_SEED_SE: usize = P::BYTES_SEED_SE; - const BYTES_SALT: usize = P::BYTES_SALT; } #[cfg(any( @@ -593,6 +596,7 @@ impl Params for EphemeralFrodoKem { ))] impl Expanded for EphemeralFrodoKem { const METHOD: &'static str = E::METHOD; + fn expand_a(&self, seed_a: &[u8], a: &mut [u16]) { E::expand_a(&E::default(), seed_a, a) } @@ -639,13 +643,13 @@ struct InnerFrodo640; feature = "frodo640shake", ))] impl InnerFrodo640 { - const N: usize = 640; - const LOG_Q: usize = 15; - const EXTRACTED_BITS: usize = 2; const CDF_TABLE: &'static [u16] = &[ 4643, 13363, 20579, 25843, 29227, 31145, 32103, 32525, 32689, 32745, 32762, 32766, 32767, ]; const CLAIMED_NIST_LEVEL: usize = 1; + const EXTRACTED_BITS: usize = 2; + const LOG_Q: usize = 15; + const N: usize = 640; const SHARED_SECRET_LENGTH: usize = 16; } @@ -658,11 +662,12 @@ pub struct Frodo640; #[cfg(any(feature = "frodo640aes", feature = "frodo640shake",))] impl Params for Frodo640 { type Shake = sha3::Shake128; - const N: usize = InnerFrodo640::N; - const LOG_Q: usize = InnerFrodo640::LOG_Q; - const EXTRACTED_BITS: usize = InnerFrodo640::EXTRACTED_BITS; + const CDF_TABLE: &'static [u16] = InnerFrodo640::CDF_TABLE; const CLAIMED_NIST_LEVEL: usize = InnerFrodo640::CLAIMED_NIST_LEVEL; + const EXTRACTED_BITS: usize = InnerFrodo640::EXTRACTED_BITS; + const LOG_Q: usize = InnerFrodo640::LOG_Q; + const N: usize = InnerFrodo640::N; const SHARED_SECRET_LENGTH: usize = InnerFrodo640::SHARED_SECRET_LENGTH; } @@ -675,14 +680,15 @@ pub struct EphemeralFrodo640; #[cfg(any(feature = "efrodo640aes", feature = "efrodo640shake",))] impl Params for EphemeralFrodo640 { type Shake = sha3::Shake128; - const N: usize = InnerFrodo640::N; - const LOG_Q: usize = InnerFrodo640::LOG_Q; - const EXTRACTED_BITS: usize = InnerFrodo640::EXTRACTED_BITS; + + const BYTES_SALT: usize = 0; + const BYTES_SEED_SE: usize = Self::SHARED_SECRET_LENGTH; const CDF_TABLE: &'static [u16] = InnerFrodo640::CDF_TABLE; const CLAIMED_NIST_LEVEL: usize = InnerFrodo640::CLAIMED_NIST_LEVEL; + const EXTRACTED_BITS: usize = InnerFrodo640::EXTRACTED_BITS; + const LOG_Q: usize = InnerFrodo640::LOG_Q; + const N: usize = InnerFrodo640::N; const SHARED_SECRET_LENGTH: usize = InnerFrodo640::SHARED_SECRET_LENGTH; - const BYTES_SEED_SE: usize = Self::SHARED_SECRET_LENGTH; - const BYTES_SALT: usize = 0; } #[cfg(any( @@ -700,13 +706,13 @@ struct InnerFrodo976; feature = "frodo976shake", ))] impl InnerFrodo976 { - const N: usize = 976; - const LOG_Q: usize = 16; - const EXTRACTED_BITS: usize = 3; const CDF_TABLE: &'static [u16] = &[ 5638, 15915, 23689, 28571, 31116, 32217, 32613, 32731, 32760, 32766, 32767, ]; const CLAIMED_NIST_LEVEL: usize = 3; + const EXTRACTED_BITS: usize = 3; + const LOG_Q: usize = 16; + const N: usize = 976; const SHARED_SECRET_LENGTH: usize = 24; } @@ -719,11 +725,12 @@ pub struct Frodo976; #[cfg(any(feature = "frodo976aes", feature = "frodo976shake",))] impl Params for Frodo976 { type Shake = sha3::Shake256; - const N: usize = InnerFrodo976::N; - const LOG_Q: usize = InnerFrodo976::LOG_Q; - const EXTRACTED_BITS: usize = InnerFrodo976::EXTRACTED_BITS; + const CDF_TABLE: &'static [u16] = InnerFrodo976::CDF_TABLE; const CLAIMED_NIST_LEVEL: usize = InnerFrodo976::CLAIMED_NIST_LEVEL; + const EXTRACTED_BITS: usize = InnerFrodo976::EXTRACTED_BITS; + const LOG_Q: usize = InnerFrodo976::LOG_Q; + const N: usize = InnerFrodo976::N; const SHARED_SECRET_LENGTH: usize = InnerFrodo976::SHARED_SECRET_LENGTH; } @@ -736,14 +743,15 @@ pub struct EphemeralFrodo976; #[cfg(any(feature = "efrodo976aes", feature = "efrodo976shake",))] impl Params for EphemeralFrodo976 { type Shake = sha3::Shake256; - const N: usize = InnerFrodo976::N; - const LOG_Q: usize = InnerFrodo976::LOG_Q; - const EXTRACTED_BITS: usize = InnerFrodo976::EXTRACTED_BITS; + + const BYTES_SALT: usize = 0; + const BYTES_SEED_SE: usize = InnerFrodo976::SHARED_SECRET_LENGTH; const CDF_TABLE: &'static [u16] = InnerFrodo976::CDF_TABLE; const CLAIMED_NIST_LEVEL: usize = InnerFrodo976::CLAIMED_NIST_LEVEL; + const EXTRACTED_BITS: usize = InnerFrodo976::EXTRACTED_BITS; + const LOG_Q: usize = InnerFrodo976::LOG_Q; + const N: usize = InnerFrodo976::N; const SHARED_SECRET_LENGTH: usize = InnerFrodo976::SHARED_SECRET_LENGTH; - const BYTES_SEED_SE: usize = InnerFrodo976::SHARED_SECRET_LENGTH; - const BYTES_SALT: usize = 0; } #[cfg(any( @@ -761,11 +769,11 @@ struct InnerFrodo1344; feature = "frodo1344shake", ))] impl InnerFrodo1344 { - const N: usize = 1344; - const LOG_Q: usize = 16; - const EXTRACTED_BITS: usize = 4; const CDF_TABLE: &'static [u16] = &[9142, 23462, 30338, 32361, 32725, 32765, 32767]; const CLAIMED_NIST_LEVEL: usize = 5; + const EXTRACTED_BITS: usize = 4; + const LOG_Q: usize = 16; + const N: usize = 1344; const SHARED_SECRET_LENGTH: usize = 32; } @@ -778,11 +786,12 @@ pub struct Frodo1344; #[cfg(any(feature = "frodo1344aes", feature = "frodo1344shake",))] impl Params for Frodo1344 { type Shake = sha3::Shake256; - const N: usize = InnerFrodo1344::N; - const LOG_Q: usize = InnerFrodo1344::LOG_Q; - const EXTRACTED_BITS: usize = InnerFrodo1344::EXTRACTED_BITS; + const CDF_TABLE: &'static [u16] = InnerFrodo1344::CDF_TABLE; const CLAIMED_NIST_LEVEL: usize = InnerFrodo1344::CLAIMED_NIST_LEVEL; + const EXTRACTED_BITS: usize = InnerFrodo1344::EXTRACTED_BITS; + const LOG_Q: usize = InnerFrodo1344::LOG_Q; + const N: usize = InnerFrodo1344::N; const SHARED_SECRET_LENGTH: usize = InnerFrodo1344::SHARED_SECRET_LENGTH; } @@ -795,14 +804,15 @@ pub struct EphemeralFrodo1344; #[cfg(any(feature = "efrodo1344aes", feature = "efrodo1344shake",))] impl Params for EphemeralFrodo1344 { type Shake = sha3::Shake256; - const N: usize = InnerFrodo1344::N; - const LOG_Q: usize = InnerFrodo1344::LOG_Q; - const EXTRACTED_BITS: usize = InnerFrodo1344::EXTRACTED_BITS; + + const BYTES_SALT: usize = 0; + const BYTES_SEED_SE: usize = Self::SHARED_SECRET_LENGTH; const CDF_TABLE: &'static [u16] = InnerFrodo1344::CDF_TABLE; const CLAIMED_NIST_LEVEL: usize = InnerFrodo1344::CLAIMED_NIST_LEVEL; + const EXTRACTED_BITS: usize = InnerFrodo1344::EXTRACTED_BITS; + const LOG_Q: usize = InnerFrodo1344::LOG_Q; + const N: usize = InnerFrodo1344::N; const SHARED_SECRET_LENGTH: usize = InnerFrodo1344::SHARED_SECRET_LENGTH; - const BYTES_SEED_SE: usize = Self::SHARED_SECRET_LENGTH; - const BYTES_SALT: usize = 0; } #[cfg(any( @@ -864,8 +874,11 @@ impl Expanded for FrodoAes

{ // } // } - let mut blocks = Vec::with_capacity(P::N_X_N / P::STRIPE_STEP); - + // Treat `a` as blocks then overwrite in place to avoid allocation + let blocks = unsafe { + std::slice::from_raw_parts_mut(a.as_mut_ptr() as *mut Block, P::N_X_N / P::STRIPE_STEP) + }; + let mut pos = 0; for i in 0..P::N { let ii = i as u16; let mut block = Block::default(); @@ -873,19 +886,16 @@ impl Expanded for FrodoAes

{ for j in (0..P::N).step_by(P::STRIPE_STEP) { let jj = j as u16; block[2..4].copy_from_slice(&jj.to_le_bytes()); - blocks.push(block); + blocks[pos] = block; + pos += 1; } } - enc.encrypt_blocks(&mut blocks); - let mut block_iter = blocks.into_iter(); - for i in 0..P::N { - let row = i * P::N; - for j in (0..P::N).step_by(P::STRIPE_STEP) { - let block = block_iter.next().expect("another block"); - for k in 0..P::STRIPE_STEP { - a[row + j + k] = - u16::from_le_bytes([block[2 * k], block[2 * k + 1]]) & P::Q_MASK; - } + + enc.encrypt_blocks(blocks); + #[cfg(target_endian = "big")] + { + for i in a.iter_mut() { + *i = i.to_be(); } } } @@ -910,15 +920,18 @@ impl Expanded for FrodoAes

{ debug_assert_eq!(seed_a.len(), P::BYTES_SEED_A); debug_assert_eq!(seed_a.len(), 16); - let mut in_blocks = Vec::with_capacity(P::N_X_N * 16 / P::STRIPE_STEP); + let in_blocks = + unsafe { std::slice::from_raw_parts_mut(a.as_mut_ptr() as *mut u8, P::N_X_N * 2) }; let mut in_block = [0u8; 16]; + let mut pos = 0; for i in 0..P::N { let ii = i as u16; in_block[..2].copy_from_slice(&ii.to_le_bytes()); for j in (0..P::N).step_by(P::STRIPE_STEP) { let jj = j as u16; in_block[2..4].copy_from_slice(&jj.to_le_bytes()); - in_blocks.extend_from_slice(&in_block); + in_blocks[pos..pos + 16].copy_from_slice(&in_block); + pos += 16; } } unsafe { @@ -949,16 +962,10 @@ impl Expanded for FrodoAes

{ panic!("EVP_EncryptInit_ex failed"); } } - let mut pos = 0; - for i in 0..P::N { - let row = i * P::N; - for j in (0..P::N).step_by(P::STRIPE_STEP) { - let block = &in_blocks[pos..pos + 16]; - for k in 0..P::STRIPE_STEP { - a[row + j + k] = - u16::from_le_bytes([block[2 * k], block[2 * k + 1]]) & P::Q_MASK; - } - pos += 16; + #[cfg(target_endian = "big")] + { + for i in a.iter_mut() { + *i = i.to_be(); } } } @@ -992,6 +999,7 @@ pub struct FrodoShake(pub PhantomData

); ))] impl Expanded for FrodoShake

{ const METHOD: &'static str = "SHAKE"; + fn expand_a(&self, seed_a: &[u8], a: &mut [u16]) { use sha3::{ digest::{ExtendableOutputReset, Update}, @@ -1001,7 +1009,6 @@ impl Expanded for FrodoShake

{ debug_assert_eq!(a.len(), P::N_X_N); debug_assert_eq!(seed_a.len(), P::BYTES_SEED_A); - let mut a_row = vec![0u8; P::TWO_N]; let mut seed_separated = vec![0u8; P::TWO_PLUS_BYTES_SEED_A]; let mut shake = Shake128::default(); @@ -1012,10 +1019,16 @@ impl Expanded for FrodoShake

{ seed_separated[0..2].copy_from_slice(&(i as u16).to_le_bytes()); shake.update(&seed_separated); - shake.finalize_xof_reset_into(&mut a_row); - - for j in 0..P::N { - a[ii + j] = u16::from_le_bytes([a_row[j * 2], a_row[j * 2 + 1]]); + let a_temp = &mut a[ii..ii + P::N]; + let bytes = unsafe { + std::slice::from_raw_parts_mut(a_temp.as_mut_ptr() as *mut u8, a_temp.len() * 2) + }; + shake.finalize_xof_reset_into(bytes); + #[cfg(target_endian = "big")] + { + for i in a_temp { + *i = i.to_be(); + } } } } @@ -1034,11 +1047,12 @@ impl Expanded for FrodoShake

{ ))] impl Expanded for FrodoShake

{ const METHOD: &'static str = "SHAKE"; + fn expand_a(&self, seed_a: &[u8], a: &mut [u16]) { debug_assert_eq!(a.len(), P::N_X_N); debug_assert_eq!(seed_a.len(), P::BYTES_SEED_A); - let mut a_row = vec![0u8; P::TWO_N]; + // let mut a_row = vec![0u8; P::TWO_N]; let mut seed_separated = vec![0u8; P::TWO_PLUS_BYTES_SEED_A]; seed_separated[2..].copy_from_slice(seed_a); @@ -1068,13 +1082,20 @@ impl Expanded for FrodoShake

{ { panic!("EVP_DigestUpdate failed"); } - if openssl_sys::EVP_DigestFinalXOF(shake, a_row.as_mut_ptr(), a_row.len()) != 1 { + if openssl_sys::EVP_DigestFinalXOF( + shake, + a[ii..ii + P::N].as_mut_ptr() as *mut u8, + P::TWO_N, + ) != 1 + { panic!("EVP_DigestFinalXOF failed"); } } - - for j in 0..P::N { - a[ii + j] = u16::from_le_bytes([a_row[j * 2], a_row[j * 2 + 1]]); + } + #[cfg(target_endian = "big")] + { + for i in a.iter_mut() { + *i = i.to_be(); } } } diff --git a/frodo-kem/src/hazmat/traits.rs b/frodo-kem/src/hazmat/traits.rs index c8079af..860eef6 100644 --- a/frodo-kem/src/hazmat/traits.rs +++ b/frodo-kem/src/hazmat/traits.rs @@ -3,7 +3,7 @@ use crate::hazmat::{ SharedSecret, }; use rand_core::CryptoRngCore; -use sha3::digest::{ExtendableOutput, ExtendableOutputReset, Update, XofReader}; +use sha3::digest::{ExtendableOutput, ExtendableOutputReset, Update}; use subtle::{Choice, ConditionallySelectable}; use zeroize::Zeroize; @@ -132,15 +132,20 @@ pub trait Kem: Params + Expanded + Sample { shake.update(&[0x5F]); shake.update(randomness_seed_se); - let mut shake_reader = shake.finalize_xof_reset(); - let mut u16_buffer = [0u8; 2]; - // 1st half is matrix S // 2nd half is matrix E let mut bytes_se = vec![0u16; Self::TWO_N_X_N_BAR]; - for b in bytes_se.iter_mut() { - shake_reader.read(&mut u16_buffer); - *b = u16::from_le_bytes(u16_buffer); + { + let bytes_se = unsafe { + std::slice::from_raw_parts_mut(bytes_se.as_mut_ptr() as *mut u8, bytes_se.len() * 2) + }; + shake.finalize_xof_reset_into(bytes_se); + } + #[cfg(target_endian = "big")] + { + for b in bytes_se.iter_mut() { + *b = b.to_be(); + } } self.sample(&mut bytes_se); @@ -173,7 +178,6 @@ pub trait Kem: Params + Expanded + Sample { bytes_se.zeroize(); randomness.zeroize(); - u16_buffer.zeroize(); (pk, sk) } @@ -226,11 +230,16 @@ pub trait Kem: Params + Expanded + Sample { let mut sp = vec![0u16; (2 * Self::N + Self::N_BAR) * Self::N_BAR]; shake.update(&[0x96]); shake.update(&g2_out[..Self::BYTES_SEED_SE]); - let mut shake_reader = shake.finalize_xof_reset(); - let mut u16_buffer = [0u8; 2]; - for b in sp.iter_mut() { - shake_reader.read(&mut u16_buffer); - *b = u16::from_le_bytes(u16_buffer); + { + let bytes_sp = + unsafe { std::slice::from_raw_parts_mut(sp.as_mut_ptr() as *mut u8, sp.len() * 2) }; + shake.finalize_xof_reset_into(bytes_sp); + } + #[cfg(target_endian = "big")] + { + for b in sp.iter_mut() { + *b = b.to_be(); + } } self.sample(&mut sp); @@ -339,11 +348,16 @@ pub trait Kem: Params + Expanded + Sample { let mut sp = vec![0u16; (2 * Self::N + Self::N_BAR) * Self::N_BAR]; shake.update(&[0x96]); shake.update(&g2_out[..Self::BYTES_SEED_SE]); - let mut shake_reader = shake.finalize_xof_reset(); - let mut u16_buffer = [0u8; 2]; - for b in sp.iter_mut() { - shake_reader.read(&mut u16_buffer); - *b = u16::from_le_bytes(u16_buffer); + { + let bytes_sp = + unsafe { std::slice::from_raw_parts_mut(sp.as_mut_ptr() as *mut u8, sp.len() * 2) }; + shake.finalize_xof_reset_into(bytes_sp); + } + #[cfg(target_endian = "big")] + { + for b in sp.iter_mut() { + *b = b.to_be(); + } } self.sample(&mut sp); @@ -444,14 +458,53 @@ pub trait Kem: Params + Expanded + Sample { debug_assert_eq!(e.len(), Self::N_X_N_BAR); debug_assert_eq!(out.len(), Self::N_BAR_X_N); - for i in 0..Self::N { + // Reference implementation + // for i in 0..Self::N { + // for k in 0..Self::N_BAR { + // let mut sum = e[k * Self::N + i]; + // let k_n = k * Self::N; + // for j in 0..Self::N { + // sum = sum.wrapping_add(a[j * Self::N + i].wrapping_mul(s[k_n + j])); + // } + // out[k_n + i] = out[k_n + i].wrapping_add(sum); + // } + // } + + // Unroll to process 8 columns at a time + for i in (0..Self::N).step_by(8) { for k in 0..Self::N_BAR { - let mut sum = e[k * Self::N + i]; let k_n = k * Self::N; + let mut sum = [ + e[k_n + i], + e[k_n + i + 1], + e[k_n + i + 2], + e[k_n + i + 3], + e[k_n + i + 4], + e[k_n + i + 5], + e[k_n + i + 6], + e[k_n + i + 7], + ]; + for j in 0..Self::N { - sum = sum.wrapping_add(a[j * Self::N + i].wrapping_mul(s[k_n + j])); + let sp = s[k_n + j]; + sum[0] = sum[0].wrapping_add(a[j * Self::N + i].wrapping_mul(sp)); + sum[1] = sum[1].wrapping_add(a[j * Self::N + i + 1].wrapping_mul(sp)); + sum[2] = sum[2].wrapping_add(a[j * Self::N + i + 2].wrapping_mul(sp)); + sum[3] = sum[3].wrapping_add(a[j * Self::N + i + 3].wrapping_mul(sp)); + sum[4] = sum[4].wrapping_add(a[j * Self::N + i + 4].wrapping_mul(sp)); + sum[5] = sum[5].wrapping_add(a[j * Self::N + i + 5].wrapping_mul(sp)); + sum[6] = sum[6].wrapping_add(a[j * Self::N + i + 6].wrapping_mul(sp)); + sum[7] = sum[7].wrapping_add(a[j * Self::N + i + 7].wrapping_mul(sp)); } - out[k_n + i] = out[k_n + i].wrapping_add(sum); + + out[k_n + i] = out[k_n + i].wrapping_add(sum[0]); + out[k_n + i + 1] = out[k_n + i + 1].wrapping_add(sum[1]); + out[k_n + i + 2] = out[k_n + i + 2].wrapping_add(sum[2]); + out[k_n + i + 3] = out[k_n + i + 3].wrapping_add(sum[3]); + out[k_n + i + 4] = out[k_n + i + 4].wrapping_add(sum[4]); + out[k_n + i + 5] = out[k_n + i + 5].wrapping_add(sum[5]); + out[k_n + i + 6] = out[k_n + i + 6].wrapping_add(sum[6]); + out[k_n + i + 7] = out[k_n + i + 7].wrapping_add(sum[7]); } } } @@ -465,16 +518,55 @@ pub trait Kem: Params + Expanded + Sample { debug_assert_eq!(e.len(), Self::N_BAR_X_N_BAR); debug_assert_eq!(out.len(), Self::N_BAR_X_N_BAR); + // Reference implementation + // for k in 0..Self::N_BAR { + // let k_n = k * Self::N; + // let k_bar = k * Self::N_BAR; + // for i in 0..Self::N_BAR { + // let mut sum = e[k_bar + i]; + // for j in 0..Self::N { + // sum = sum.wrapping_add(s[k_n + j].wrapping_mul(b[j * Self::N_BAR + i])); + // } + // out[k_bar + i] = sum & Self::Q_MASK; + // } + // } + + // Unroll to process 8 columns at a time for k in 0..Self::N_BAR { let k_n = k * Self::N; let k_bar = k * Self::N_BAR; - for i in 0..Self::N_BAR { - let mut sum = e[k_bar + i]; - for j in 0..Self::N { - sum = sum.wrapping_add(s[k_n + j].wrapping_mul(b[j * Self::N_BAR + i])); - } - out[k_bar + i] = sum & Self::Q_MASK; + + let mut sum = [ + e[k_bar], + e[k_bar + 1], + e[k_bar + 2], + e[k_bar + 3], + e[k_bar + 4], + e[k_bar + 5], + e[k_bar + 6], + e[k_bar + 7], + ]; + + for j in 0..Self::N { + let sp = s[k_n + j]; + sum[0] = sum[0].wrapping_add(sp.wrapping_mul(b[j * Self::N_BAR])); + sum[1] = sum[1].wrapping_add(sp.wrapping_mul(b[j * Self::N_BAR + 1])); + sum[2] = sum[2].wrapping_add(sp.wrapping_mul(b[j * Self::N_BAR + 2])); + sum[3] = sum[3].wrapping_add(sp.wrapping_mul(b[j * Self::N_BAR + 3])); + sum[4] = sum[4].wrapping_add(sp.wrapping_mul(b[j * Self::N_BAR + 4])); + sum[5] = sum[5].wrapping_add(sp.wrapping_mul(b[j * Self::N_BAR + 5])); + sum[6] = sum[6].wrapping_add(sp.wrapping_mul(b[j * Self::N_BAR + 6])); + sum[7] = sum[7].wrapping_add(sp.wrapping_mul(b[j * Self::N_BAR + 7])); } + + out[k_bar] = sum[0] & Self::Q_MASK; + out[k_bar + 1] = sum[1] & Self::Q_MASK; + out[k_bar + 2] = sum[2] & Self::Q_MASK; + out[k_bar + 3] = sum[3] & Self::Q_MASK; + out[k_bar + 4] = sum[4] & Self::Q_MASK; + out[k_bar + 5] = sum[5] & Self::Q_MASK; + out[k_bar + 6] = sum[6] & Self::Q_MASK; + out[k_bar + 7] = sum[7] & Self::Q_MASK; } } diff --git a/frodo-kem/src/lib.rs b/frodo-kem/src/lib.rs index 7430411..e8397d3 100644 --- a/frodo-kem/src/lib.rs +++ b/frodo-kem/src/lib.rs @@ -5,7 +5,7 @@ //! and `decapsulate` it on the other side. //! //! ``` -//! use frodo_kem_rs::Algorithm; +//! use frodo_kem::Algorithm; //! use rand_core::OsRng; //! //! let alg = Algorithm::FrodoKem640Shake; @@ -26,14 +26,15 @@ //! For more information see [ISO Standard Annex](https://frodokem.org/files/FrodoKEM-annex-20230418.pdf). //! //! ``` -//! use frodo_kem_rs::Algorithm; +//! use frodo_kem::Algorithm; //! use rand_core::{RngCore, OsRng}; //! //! let alg = Algorithm::FrodoKem1344Shake; +//! let params = alg.params(); //! let (ek, dk) = alg.generate_keypair(OsRng); //! // Key is known, generate -//! let aes_256_key = vec![3u8; alg.message_length()]; -//! let mut salt = vec![0u8; alg.salt_length()]; +//! let aes_256_key = vec![3u8; params.message_length]; +//! let mut salt = vec![0u8; params.salt_length]; //! OsRng.fill_bytes(&mut salt); //! let (ct, enc_ss) = alg.encapsulate(&ek, &aes_256_key, &salt).unwrap(); //! let (dec_ss, dec_msg) = alg.decapsulate(&dk, &ct).unwrap(); @@ -58,6 +59,16 @@ //! the necessary traits and models for creating a custom implementation. //! Be warned, this is not recommended unless you are sure of what you are doing. #![cfg_attr(docsrs, feature(doc_auto_cfg))] +#![warn( + missing_docs, + missing_debug_implementations, + missing_copy_implementations, + trivial_casts, + trivial_numeric_casts, + unused, + clippy::mod_module_files +)] +#![deny(clippy::unwrap_used)] #[cfg(not(any( feature = "efrodo640aes", @@ -635,8 +646,8 @@ impl std::str::FromStr for Algorithm { }); (*ALGORITHMS) .get(s) - .ok_or(Error::UnsupportedAlgorithm) .copied() + .ok_or(Error::UnsupportedAlgorithm) } } @@ -843,318 +854,57 @@ impl Algorithm { ] } - /// Get the claimed NIST level - pub const fn claimed_nist_level(&self) -> usize { + /// Get the parameters for this algorithm + pub const fn params(&self) -> AlgorithmParams { match self { #[cfg(feature = "frodo640aes")] - Self::FrodoKem640Aes => self.inner_claimed_nist_level::(), + Self::FrodoKem640Aes => self.inner_params::(), #[cfg(feature = "frodo976aes")] - Self::FrodoKem976Aes => self.inner_claimed_nist_level::(), + Self::FrodoKem976Aes => self.inner_params::(), #[cfg(feature = "frodo1344aes")] - Self::FrodoKem1344Aes => self.inner_claimed_nist_level::(), + Self::FrodoKem1344Aes => self.inner_params::(), #[cfg(feature = "frodo640shake")] - Self::FrodoKem640Shake => self.inner_claimed_nist_level::(), + Self::FrodoKem640Shake => self.inner_params::(), #[cfg(feature = "frodo976shake")] - Self::FrodoKem976Shake => self.inner_claimed_nist_level::(), + Self::FrodoKem976Shake => self.inner_params::(), #[cfg(feature = "frodo1344shake")] - Self::FrodoKem1344Shake => self.inner_claimed_nist_level::(), + Self::FrodoKem1344Shake => self.inner_params::(), #[cfg(feature = "efrodo640aes")] - Self::EphemeralFrodoKem640Aes => { - self.inner_claimed_nist_level::() - } + Self::EphemeralFrodoKem640Aes => self.inner_params::(), #[cfg(feature = "efrodo976aes")] - Self::EphemeralFrodoKem976Aes => { - self.inner_claimed_nist_level::() - } + Self::EphemeralFrodoKem976Aes => self.inner_params::(), #[cfg(feature = "efrodo1344aes")] - Self::EphemeralFrodoKem1344Aes => { - self.inner_claimed_nist_level::() - } + Self::EphemeralFrodoKem1344Aes => self.inner_params::(), #[cfg(feature = "efrodo640shake")] - Self::EphemeralFrodoKem640Shake => { - self.inner_claimed_nist_level::() - } + Self::EphemeralFrodoKem640Shake => self.inner_params::(), #[cfg(feature = "efrodo976shake")] - Self::EphemeralFrodoKem976Shake => { - self.inner_claimed_nist_level::() - } + Self::EphemeralFrodoKem976Shake => self.inner_params::(), #[cfg(feature = "efrodo1344shake")] - Self::EphemeralFrodoKem1344Shake => { - self.inner_claimed_nist_level::() - } + Self::EphemeralFrodoKem1344Shake => self.inner_params::(), } } - const fn inner_claimed_nist_level(&self) -> usize { - B::CLAIMED_NIST_LEVEL - } - - /// Get the length of the message - pub const fn message_length(&self) -> usize { - match self { - #[cfg(feature = "frodo640aes")] - Self::FrodoKem640Aes => self.inner_message_length::(), - #[cfg(feature = "frodo976aes")] - Self::FrodoKem976Aes => self.inner_message_length::(), - #[cfg(feature = "frodo1344aes")] - Self::FrodoKem1344Aes => self.inner_message_length::(), - #[cfg(feature = "frodo640shake")] - Self::FrodoKem640Shake => self.inner_message_length::(), - #[cfg(feature = "frodo976shake")] - Self::FrodoKem976Shake => self.inner_message_length::(), - #[cfg(feature = "frodo1344shake")] - Self::FrodoKem1344Shake => self.inner_message_length::(), - #[cfg(feature = "efrodo640aes")] - Self::EphemeralFrodoKem640Aes => self.inner_message_length::(), - #[cfg(feature = "efrodo976aes")] - Self::EphemeralFrodoKem976Aes => self.inner_message_length::(), - #[cfg(feature = "efrodo1344aes")] - Self::EphemeralFrodoKem1344Aes => { - self.inner_message_length::() - } - #[cfg(feature = "efrodo640shake")] - Self::EphemeralFrodoKem640Shake => { - self.inner_message_length::() - } - #[cfg(feature = "efrodo976shake")] - Self::EphemeralFrodoKem976Shake => { - self.inner_message_length::() - } - #[cfg(feature = "efrodo1344shake")] - Self::EphemeralFrodoKem1344Shake => { - self.inner_message_length::() - } + const fn inner_params(&self) -> AlgorithmParams { + AlgorithmParams { + n: B::N, + n_bar: B::N_BAR, + log_q: B::LOG_Q, + q: B::Q, + extracted_bits: B::EXTRACTED_BITS, + stripe_step: B::STRIPE_STEP, + bytes_seed_a: B::BYTES_SEED_A, + bytes_pk_hash: B::BYTES_PK_HASH, + cdf_table: B::CDF_TABLE, + claimed_nist_level: B::CLAIMED_NIST_LEVEL, + shared_secret_length: B::SHARED_SECRET_LENGTH, + message_length: B::BYTES_MU, + salt_length: B::BYTES_SALT, + encryption_key_length: B::PUBLIC_KEY_LENGTH, + decryption_key_length: B::SECRET_KEY_LENGTH, + ciphertext_length: B::CIPHERTEXT_LENGTH, } } - const fn inner_message_length(&self) -> usize { - P::BYTES_MU - } - - /// Get the length of the salt - pub const fn salt_length(&self) -> usize { - match self { - #[cfg(feature = "frodo640aes")] - Self::FrodoKem640Aes => self.inner_salt_length::(), - #[cfg(feature = "frodo976aes")] - Self::FrodoKem976Aes => self.inner_salt_length::(), - #[cfg(feature = "frodo1344aes")] - Self::FrodoKem1344Aes => self.inner_salt_length::(), - #[cfg(feature = "frodo640shake")] - Self::FrodoKem640Shake => self.inner_salt_length::(), - #[cfg(feature = "frodo976shake")] - Self::FrodoKem976Shake => self.inner_salt_length::(), - #[cfg(feature = "frodo1344shake")] - Self::FrodoKem1344Shake => self.inner_salt_length::(), - #[cfg(feature = "efrodo640aes")] - Self::EphemeralFrodoKem640Aes => self.inner_salt_length::(), - #[cfg(feature = "efrodo976aes")] - Self::EphemeralFrodoKem976Aes => self.inner_salt_length::(), - #[cfg(feature = "efrodo1344aes")] - Self::EphemeralFrodoKem1344Aes => self.inner_salt_length::(), - #[cfg(feature = "efrodo640shake")] - Self::EphemeralFrodoKem640Shake => { - self.inner_salt_length::() - } - #[cfg(feature = "efrodo976shake")] - Self::EphemeralFrodoKem976Shake => { - self.inner_salt_length::() - } - #[cfg(feature = "efrodo1344shake")] - Self::EphemeralFrodoKem1344Shake => { - self.inner_salt_length::() - } - } - } - - const fn inner_salt_length(&self) -> usize { - B::BYTES_SALT - } - - /// Get the length of the public key - pub const fn encryption_key_length(&self) -> usize { - match self { - #[cfg(feature = "frodo640aes")] - Self::FrodoKem640Aes => self.inner_public_key_length::(), - #[cfg(feature = "frodo976aes")] - Self::FrodoKem976Aes => self.inner_public_key_length::(), - #[cfg(feature = "frodo1344aes")] - Self::FrodoKem1344Aes => self.inner_public_key_length::(), - #[cfg(feature = "frodo640shake")] - Self::FrodoKem640Shake => self.inner_public_key_length::(), - #[cfg(feature = "frodo976shake")] - Self::FrodoKem976Shake => self.inner_public_key_length::(), - #[cfg(feature = "frodo1344shake")] - Self::FrodoKem1344Shake => self.inner_public_key_length::(), - #[cfg(feature = "efrodo640aes")] - Self::EphemeralFrodoKem640Aes => { - self.inner_public_key_length::() - } - #[cfg(feature = "efrodo976aes")] - Self::EphemeralFrodoKem976Aes => { - self.inner_public_key_length::() - } - #[cfg(feature = "efrodo1344aes")] - Self::EphemeralFrodoKem1344Aes => { - self.inner_public_key_length::() - } - #[cfg(feature = "efrodo640shake")] - Self::EphemeralFrodoKem640Shake => { - self.inner_public_key_length::() - } - #[cfg(feature = "efrodo976shake")] - Self::EphemeralFrodoKem976Shake => { - self.inner_public_key_length::() - } - #[cfg(feature = "efrodo1344shake")] - Self::EphemeralFrodoKem1344Shake => { - self.inner_public_key_length::() - } - } - } - - const fn inner_public_key_length(&self) -> usize { - B::PUBLIC_KEY_LENGTH - } - - /// Get the length of the secret key - pub const fn decryption_key_length(&self) -> usize { - match self { - #[cfg(feature = "frodo640aes")] - Self::FrodoKem640Aes => self.inner_decryption_key_length::(), - #[cfg(feature = "frodo976aes")] - Self::FrodoKem976Aes => self.inner_decryption_key_length::(), - #[cfg(feature = "frodo1344aes")] - Self::FrodoKem1344Aes => self.inner_decryption_key_length::(), - #[cfg(feature = "frodo640shake")] - Self::FrodoKem640Shake => self.inner_decryption_key_length::(), - #[cfg(feature = "frodo976shake")] - Self::FrodoKem976Shake => self.inner_decryption_key_length::(), - #[cfg(feature = "frodo1344shake")] - Self::FrodoKem1344Shake => self.inner_decryption_key_length::(), - #[cfg(feature = "efrodo640aes")] - Self::EphemeralFrodoKem640Aes => { - self.inner_decryption_key_length::() - } - #[cfg(feature = "efrodo976aes")] - Self::EphemeralFrodoKem976Aes => { - self.inner_decryption_key_length::() - } - #[cfg(feature = "efrodo1344aes")] - Self::EphemeralFrodoKem1344Aes => { - self.inner_decryption_key_length::() - } - #[cfg(feature = "efrodo640shake")] - Self::EphemeralFrodoKem640Shake => { - self.inner_decryption_key_length::() - } - #[cfg(feature = "efrodo976shake")] - Self::EphemeralFrodoKem976Shake => { - self.inner_decryption_key_length::() - } - #[cfg(feature = "efrodo1344shake")] - Self::EphemeralFrodoKem1344Shake => { - self.inner_decryption_key_length::() - } - } - } - - const fn inner_decryption_key_length(&self) -> usize { - B::SECRET_KEY_LENGTH - } - - /// Get the length of the shared secret - pub const fn shared_secret_length(&self) -> usize { - match self { - #[cfg(feature = "frodo640aes")] - Self::FrodoKem640Aes => self.inner_shared_secret_length::(), - #[cfg(feature = "frodo976aes")] - Self::FrodoKem976Aes => self.inner_shared_secret_length::(), - #[cfg(feature = "frodo1344aes")] - Self::FrodoKem1344Aes => self.inner_shared_secret_length::(), - #[cfg(feature = "frodo640shake")] - Self::FrodoKem640Shake => self.inner_shared_secret_length::(), - #[cfg(feature = "frodo976shake")] - Self::FrodoKem976Shake => self.inner_shared_secret_length::(), - #[cfg(feature = "frodo1344shake")] - Self::FrodoKem1344Shake => self.inner_shared_secret_length::(), - #[cfg(feature = "efrodo640aes")] - Self::EphemeralFrodoKem640Aes => { - self.inner_shared_secret_length::() - } - #[cfg(feature = "efrodo976aes")] - Self::EphemeralFrodoKem976Aes => { - self.inner_shared_secret_length::() - } - #[cfg(feature = "efrodo1344aes")] - Self::EphemeralFrodoKem1344Aes => { - self.inner_shared_secret_length::() - } - #[cfg(feature = "efrodo640shake")] - Self::EphemeralFrodoKem640Shake => { - self.inner_shared_secret_length::() - } - #[cfg(feature = "efrodo976shake")] - Self::EphemeralFrodoKem976Shake => { - self.inner_shared_secret_length::() - } - #[cfg(feature = "efrodo1344shake")] - Self::EphemeralFrodoKem1344Shake => { - self.inner_shared_secret_length::() - } - } - } - - const fn inner_shared_secret_length(&self) -> usize { - B::SHARED_SECRET_LENGTH - } - - /// Get the length of the ciphertext - pub const fn ciphertext_length(&self) -> usize { - match self { - #[cfg(feature = "frodo640aes")] - Self::FrodoKem640Aes => self.inner_ciphertext_length::(), - #[cfg(feature = "frodo976aes")] - Self::FrodoKem976Aes => self.inner_ciphertext_length::(), - #[cfg(feature = "frodo1344aes")] - Self::FrodoKem1344Aes => self.inner_ciphertext_length::(), - #[cfg(feature = "frodo640shake")] - Self::FrodoKem640Shake => self.inner_ciphertext_length::(), - #[cfg(feature = "frodo976shake")] - Self::FrodoKem976Shake => self.inner_ciphertext_length::(), - #[cfg(feature = "frodo1344shake")] - Self::FrodoKem1344Shake => self.inner_ciphertext_length::(), - #[cfg(feature = "efrodo640aes")] - Self::EphemeralFrodoKem640Aes => { - self.inner_ciphertext_length::() - } - #[cfg(feature = "efrodo976aes")] - Self::EphemeralFrodoKem976Aes => { - self.inner_ciphertext_length::() - } - #[cfg(feature = "efrodo1344aes")] - Self::EphemeralFrodoKem1344Aes => { - self.inner_ciphertext_length::() - } - #[cfg(feature = "efrodo640shake")] - Self::EphemeralFrodoKem640Shake => { - self.inner_ciphertext_length::() - } - #[cfg(feature = "efrodo976shake")] - Self::EphemeralFrodoKem976Shake => { - self.inner_ciphertext_length::() - } - #[cfg(feature = "efrodo1344shake")] - Self::EphemeralFrodoKem1344Shake => { - self.inner_ciphertext_length::() - } - } - } - - const fn inner_ciphertext_length(&self) -> usize { - B::CIPHERTEXT_LENGTH - } - /// Get the [`EncryptionKey`] from a [`DecryptionKey`] pub fn encryption_key_from_decryption_key(&self, secret_key: &DecryptionKey) -> EncryptionKey { match self { @@ -1821,6 +1571,44 @@ impl Algorithm { } } +/// The algorithm underlying parameters +#[derive(Debug, Clone, Copy)] +pub struct AlgorithmParams { + /// Number of elements in the ring + pub n: usize, + /// Number of rows in the matrix + pub n_bar: usize, + /// The log of the modulus + pub log_q: usize, + /// The modulus + pub q: usize, + /// The number of bits to extract when packing/unpacking + /// encoding/decoding + pub extracted_bits: usize, + /// The number of steps for striping + pub stripe_step: usize, + /// The number of bytes in the seed for generating the matrix A + pub bytes_seed_a: usize, + /// The number of bytes in the public key hash + pub bytes_pk_hash: usize, + /// The CDF sampling table + pub cdf_table: &'static [u16], + /// The claimed NIST level + pub claimed_nist_level: usize, + /// The byte length of the shared secret + pub shared_secret_length: usize, + /// The byte length of an encrypted message + pub message_length: usize, + /// The byte length of the salt + pub salt_length: usize, + /// The byte length of the encryption key + pub encryption_key_length: usize, + /// The byte length of the decryption key + pub decryption_key_length: usize, + /// The byte length of the ciphertext + pub ciphertext_length: usize, +} + fn ct_eq_bytes(lhs: &[u8], rhs: &[u8]) -> Choice { if lhs.len() != rhs.len() { return 0u8.into(); @@ -1865,7 +1653,7 @@ mod tests { let their_pk = opt_pk.unwrap(); let their_sk = opt_sk.unwrap(); - let mut mu = vec![0u8; alg.message_length()]; + let mut mu = vec![0u8; alg.params().message_length]; rng.fill_bytes(&mut mu); let (our_ct, our_ess) = alg.encapsulate(&our_pk, &mu, &[]).unwrap(); let (our_dss, mu_prime) = alg.decapsulate(&our_sk, &our_ct).unwrap(); @@ -1895,9 +1683,9 @@ mod tests { let mut rng = rand_chacha::ChaCha8Rng::from_seed([1u8; 32]); let (our_pk, our_sk) = alg.generate_keypair(&mut rng); - let mut mu = vec![0u8; alg.message_length()]; + let mut mu = vec![0u8; alg.params().message_length]; rng.fill_bytes(&mut mu); - let mut salt = vec![0u8; alg.salt_length()]; + let mut salt = vec![0u8; alg.params().salt_length]; rng.fill_bytes(&mut salt); let (our_ct, our_ess) = alg.encapsulate(&our_pk, &mu, &salt).unwrap(); let (our_dss, mu_prime) = alg.decapsulate(&our_sk, &our_ct).unwrap(); diff --git a/frodo-kem/tests/frodo.rs b/frodo-kem/tests/frodo.rs index b649c8b..c09b5e7 100644 --- a/frodo-kem/tests/frodo.rs +++ b/frodo-kem/tests/frodo.rs @@ -1,3 +1,4 @@ +//! Tests for the FrodoKEM and FrodoKEM-640 schemes use rstest::*; use std::path::PathBuf; diff --git a/frodo-kem/tests/rng.rs b/frodo-kem/tests/rng.rs index 2d3ee1c..a432610 100644 --- a/frodo-kem/tests/rng.rs +++ b/frodo-kem/tests/rng.rs @@ -1,3 +1,5 @@ +//! Random number generator for testing +//! AES-CTR DRBG use aes::{ cipher::{BlockEncrypt, KeyInit}, Aes256Enc, Block, @@ -5,8 +7,10 @@ use aes::{ use hybrid_array::{typenum::U48, Array}; use rand_core::{CryptoRng, Error, RngCore, SeedableRng}; +/// Seed type for the AES-CTR DRBG pub type RngSeed = Array; +/// AES-CTR DRBG #[derive(Debug, Default, Copy, Clone)] pub struct AesCtrDrbg { reseed_counter: usize, @@ -71,6 +75,7 @@ impl RngCore for AesCtrDrbg { impl CryptoRng for AesCtrDrbg {} impl AesCtrDrbg { + /// Reseed the DRBG with a new seed pub fn reseed(&mut self, seed: &RngSeed) { self.counter.iter_mut().for_each(|c| *c = 0); self.key.iter_mut().for_each(|k| *k = 0); diff --git a/frodo-kem/tests/rsp_reader.rs b/frodo-kem/tests/rsp_reader.rs index c4ae6d7..7b4f818 100644 --- a/frodo-kem/tests/rsp_reader.rs +++ b/frodo-kem/tests/rsp_reader.rs @@ -1,4 +1,5 @@ -use frodo_kem_rs::*; +//! Reader for Frodo KAT files and test vectors +use frodo_kem::*; use hybrid_array::{typenum::U48, Array}; use std::path::Path; use std::{ @@ -21,12 +22,15 @@ const CT_PREFIX: usize = 5; /// "ss = ".len() const SS_PREFIX: usize = 5; +/// Reader for Frodo KAT files +#[derive(Debug)] pub struct RspReader { lines: Lines>, scheme: Algorithm, } impl RspReader { + /// Create a new RspReader from a file pub fn new>(file: P) -> Self { let path = file.as_ref(); assert!( @@ -88,13 +92,21 @@ impl Iterator for RspReader { } } +/// Test vector data #[derive(Debug, Clone, Default)] pub struct RspData { + /// Algorithm used in the test vector pub scheme: Algorithm, + /// Test vector number pub count: usize, + /// RNG seed pub seed: Array, + /// Public key pub pk: EncryptionKey, + /// Secret key pub sk: DecryptionKey, + /// Ciphertext pub ct: Ciphertext, + /// Shared secret pub ss: SharedSecret, }