Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ on:
"frodo-kem/v**",
"ml-kem/v**",
"module-lattice/v**",
"sntrup-kem/v**",
"x-wing/v**"
]

Expand Down
76 changes: 76 additions & 0 deletions .github/workflows/sntrup-kem.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
name: sntrup-kem

on:
pull_request:
paths:
- ".github/workflows/sntrup-kem.yml"
- "sntrup-kem/**"
- "Cargo.*"
push:
branches: master

defaults:
run:
working-directory: sntrup-kem

env:
RUSTFLAGS: "-Dwarnings"
CARGO_INCREMENTAL: 0

# Cancels CI jobs when new commits are pushed to a PR branch
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true

jobs:
set-msrv:
uses: RustCrypto/actions/.github/workflows/set-msrv.yml@master
with:
msrv: 1.85.0

minimal-versions:
if: false
uses: RustCrypto/actions/.github/workflows/minimal-versions.yml@master
with:
working-directory: ${{ github.workflow }}

test:
needs: set-msrv
runs-on: ubuntu-latest
strategy:
matrix:
rust:
- ${{needs.set-msrv.outputs.msrv}}
- stable
steps:
- uses: actions/checkout@v6.0.2
- uses: RustCrypto/actions/cargo-cache@master
- uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ matrix.rust }}
- run: cargo build --benches
- run: cargo build --benches --all-features
- run: cargo test --no-default-features
- run: cargo test
- run: cargo test --all-features
- run: cargo test --features serde,force-scalar

cross:
needs: set-msrv
strategy:
matrix:
include:
# Big-endian target exercises the portable scalar path and encoding byte order.
- target: powerpc-unknown-linux-gnu
rust: ${{needs.set-msrv.outputs.msrv}}
- target: powerpc-unknown-linux-gnu
rust: stable
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6.0.2
- uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ matrix.rust }}
targets: ${{ matrix.target }}
- uses: RustCrypto/actions/cross-install@master
- run: cross test --release --target ${{ matrix.target }} --all-features
39 changes: 39 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ members = [
"ml-kem",
"hqc-kem",
"module-lattice",
"sntrup-kem",
"x-wing"
]

Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ commonly used in transport encryption protocols (e.g. [TLS]) and hybrid cryptosy
|----------------------|---------------------------------------------------------------------------------------------|------------------------------------------------------------------------------|--------------------|
| [`dhkem`](./dhkem) | [![crates.io](https://img.shields.io/crates/v/dhkem.svg?logo=rust)](https://crates.io/crates/dhkem) | [![Documentation](https://docs.rs/dhkem/badge.svg)](https://docs.rs/dhkem) | Diffie-Hellman KEM |
| [`frodo‑kem`](./frodo-kem) | [![crates.io](https://img.shields.io/crates/v/frodo-kem.svg?logo=rust)](https://crates.io/crates/frodo-kem) | [![Documentation](https://docs.rs/frodo-kem/badge.svg)](https://docs.rs/frodo-kem) | Frodo KEM |
| [`hqc‑kem`](./hqc-kem) | [![crates.io](https://img.shields.io/crates/v/hqc-kem.svg?logo=rust)](https://crates.io/crates/hqc-kem) | [![Documentation](https://docs.rs/hqc-kem/badge.svg)](https://docs.rs/hqc-kem) | HQC Code-based KEM |
| [`ml‑kem`](./ml-kem) | [![crates.io](https://img.shields.io/crates/v/ml-kem.svg?logo=rust)](https://crates.io/crates/ml-kem) | [![Documentation](https://docs.rs/ml-kem/badge.svg)](https://docs.rs/ml-kem) | Module Lattice KEM |
| [`sntrup‑kem`](./sntrup-kem) | [![crates.io](https://img.shields.io/crates/v/sntrup-kem.svg?logo=rust)](https://crates.io/crates/sntrup-kem) | [![Documentation](https://docs.rs/sntrup-kem/badge.svg)](https://docs.rs/sntrup-kem) | Streamlined NTRU Prime KEM |
| [`x‑wing`](./x-wing) | [![crates.io](https://img.shields.io/crates/v/x-wing.svg?logo=rust)](https://crates.io/crates/x-wing) | [![Documentation](https://docs.rs/x-wing/badge.svg)](https://docs.rs/x-wing) | Hybrid PQ KEM |

## License
Expand Down
46 changes: 43 additions & 3 deletions hqc-kem/src/kem_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,16 +153,55 @@ mod tests {
use super::*;
use kem_traits::common::{Generate, KeyExport, KeyInit};
use kem_traits::{Decapsulate, Encapsulate, Kem};
use shake::{ExtendableOutput, Shake256, Shake256Reader, Update, XofReader};

struct TestRng {
reader: Shake256Reader,
}

impl TestRng {
fn new(label: &[u8]) -> Self {
let mut hasher = Shake256::default();
hasher.update(label);
Self {
reader: hasher.finalize_xof(),
}
}
}

impl rand::TryRng for TestRng {
type Error = core::convert::Infallible;

fn try_next_u32(&mut self) -> Result<u32, Self::Error> {
let mut buf = [0u8; 4];
self.try_fill_bytes(&mut buf)?;
Ok(u32::from_le_bytes(buf))
}

fn try_next_u64(&mut self) -> Result<u64, Self::Error> {
let mut buf = [0u8; 8];
self.try_fill_bytes(&mut buf)?;
Ok(u64::from_le_bytes(buf))
}

fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Self::Error> {
self.reader.read(dest);
Ok(())
}
}

impl rand::TryCryptoRng for TestRng {}

macro_rules! kem_roundtrip_test {
($name:ident, $params:ty) => {
#[test]
fn $name() {
// Generate via kem trait
let mut rng = rand::rng();
let mut rng = TestRng::new(concat!(stringify!($name), "-keygen").as_bytes());
let (dk, ek) = <$params>::generate_keypair_from_rng(&mut rng);

// Encapsulate
let mut rng = TestRng::new(concat!(stringify!($name), "-encaps").as_bytes());
let (ct, ss1) = ek.encapsulate_with_rng(&mut rng);

// Decapsulate (use UFCS to call trait method, not inherent)
Expand All @@ -185,7 +224,8 @@ mod tests {
#[test]
fn $name() {
// Generate DK, export seed, re-import, verify deterministic
let dk = DecapsulationKey::<$params>::generate_from_rng(&mut rand::rng());
let mut rng = TestRng::new(concat!(stringify!($name), "-keygen").as_bytes());
let dk = DecapsulationKey::<$params>::generate_from_rng(&mut rng);
let seed = dk.to_bytes();
let dk2 = DecapsulationKey::<$params>::new(&seed);

Expand All @@ -195,7 +235,7 @@ mod tests {
assert_eq!(ek1, ek2);

// Both should produce the same shared secret
let mut rng = rand::rng();
let mut rng = TestRng::new(concat!(stringify!($name), "-encaps").as_bytes());
let (ct, ss1) = ek1.encapsulate_with_rng(&mut rng);
let ss2 = <_ as Decapsulate>::decapsulate(&dk2, &ct);
assert_eq!(ss1, ss2);
Expand Down
9 changes: 6 additions & 3 deletions hqc-kem/tests/kat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,26 +136,29 @@ fn test_hqc256_kat() {

#[test]
fn test_hqc128_roundtrip() {
let mut rng = rand::rng();
let mut rng = KatRng::new(b"hqc128-roundtrip-keygen");
let (ek, dk) = hqc128::generate_key(&mut rng);
let mut rng = KatRng::new(b"hqc128-roundtrip-encaps");
let (ct, ss1) = ek.encapsulate(&mut rng);
let ss2 = dk.decapsulate(&ct);
assert_eq!(ss1, ss2, "HQC-128 roundtrip failed");
}

#[test]
fn test_hqc192_roundtrip() {
let mut rng = rand::rng();
let mut rng = KatRng::new(b"hqc192-roundtrip-keygen");
let (ek, dk) = hqc192::generate_key(&mut rng);
let mut rng = KatRng::new(b"hqc192-roundtrip-encaps");
let (ct, ss1) = ek.encapsulate(&mut rng);
let ss2 = dk.decapsulate(&ct);
assert_eq!(ss1, ss2, "HQC-192 roundtrip failed");
}

#[test]
fn test_hqc256_roundtrip() {
let mut rng = rand::rng();
let mut rng = KatRng::new(b"hqc256-roundtrip-keygen");
let (ek, dk) = hqc256::generate_key(&mut rng);
let mut rng = KatRng::new(b"hqc256-roundtrip-encaps");
let (ct, ss1) = ek.encapsulate(&mut rng);
let ss2 = dk.decapsulate(&ct);
assert_eq!(ss1, ss2, "HQC-256 roundtrip failed");
Expand Down
1 change: 1 addition & 0 deletions sntrup-kem/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
target/
68 changes: 68 additions & 0 deletions sntrup-kem/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
[package]
name = "sntrup-kem"
version = "0.1.0"
authors = ["Michael Lodder <redmike7@gmail.com>"]
license = "MIT OR Apache-2.0"
keywords = ["sntrup", "kem", "post-quantum", "cryptography", "NTRU"]
description = "Pure Rust implementation of the Streamlined NTRU Prime KEM for all parameter sizes"
homepage = "https://github.com/RustCrypto/KEMs/tree/master/sntrup-kem"
repository = "https://github.com/RustCrypto/KEMs"
categories = ["algorithms", "cryptography"]
readme = "README.md"
edition = "2024"

[features]
default = ["kgen", "ecap", "dcap"]
kgen = []
ecap = []
dcap = []
alloc = []
force-scalar = []
std = []
serde = ["dep:serdect", "dep:serde"]
js = ["getrandom/wasm_js"]

[dependencies]
hex = "0.4"
rand = "0.10.0"
rand_chacha = "0.10.0"
subtle = "2"
getrandom = { version = "0.4", optional = true }
serde = { version = "1", optional = true, default-features = false }
serdect = { version = "0.4", optional = true }
# sha2 0.11 dropped the `asm` feature; hardware SHA acceleration is now selected
# automatically per-target, so a single unconditional dependency suffices.
sha2 = "0.11"
thiserror = "2.0"
zeroize = { version = "1", features = ["derive"] }

[dev-dependencies]
criterion = "0.7"
serde_json = "1"

[[bench]]
name = "mod"
harness = false

[lints.rust]
missing_docs = "deny"
missing_debug_implementations = "deny"
trivial_casts = "deny"
trivial_numeric_casts = "deny"
unstable_features = "deny"
unused_import_braces = "deny"
unused_parens = "deny"
unused_lifetimes = "deny"
unused_qualifications = "deny"
unused_extern_crates = "deny"

[lints.clippy]
unwrap_used = "deny"
cast_precision_loss = "warn"
cast_possible_truncation = "warn"
cast_possible_wrap = "warn"
cast_sign_loss = "warn"
checked_conversions = "warn"
mod_module_files = "warn"
panic = "warn"
panic_in_result_fn = "warn"
Loading