sntrup-sys
A standalone, single-algorithm Rust bindings to Streamlined NTRU Prime,
a post-quantum key encapsulation mechanism (KEM). The C reference implementation
is vendored from SUPERCOP and
compiled with the cc crate — no system package,
no submodule, no network access at build time. Portable ISO C11, no CPU-specific
intrinsics (yet), so it builds anywhere a C compiler does.
Usage
let = keypair;
let = encapsulate;
let ss2 = decapsulate;
assert_eq!;
Every parameter set exposes the same three functions plus
PUBLIC_KEY_BYTES/SECRET_KEY_BYTES/CIPHERTEXT_BYTES/SHARED_SECRET_BYTES
constants (see src/lib.rs).
Parameter sets
All six are gated behind their own Cargo feature (all on by default), so
you can trim the build to just what you need with
default-features = false, features = ["sntrup761"]. Sizes in bytes:
| Parameter set | NIST category | Public key | Secret key | Ciphertext | Shared secret |
|---|---|---|---|---|---|
sntrup653 |
1 | 994 | 1518 | 897 | 32 |
sntrup761 |
2 | 1158 | 1763 | 1039 | 32 |
sntrup857 |
3 | 1322 | 1999 | 1184 | 32 |
sntrup953 |
4 | 1505 | 2254 | 1349 | 32 |
sntrup1013 |
4 | 1623 | 2417 | 1455 | 32 |
sntrup1277 |
5 | 2067 | 3059 | 1847 | 32 |
sntrup761 is the one used in OpenSSH's sntrup761x25519-sha512 hybrid
key exchange. Sizes are taken directly from each parameter set's api.h
and were confirmed by this crate's own build validation. NIST categories:
sntrup761 = Category 2 is confirmed directly by
liboqs;
the rest follow the same monotonic mapping commonly cited across the
NTRU Prime ecosystem — if the exact category matters for a compliance
requirement, verify against the
official submission.
Only the ref (portable ISO C11) implementation is included; SUPERCOP's
only alternative is x86-64-only hand-written assembly with avx2 feature,
which is on the to-do list for this crate.
Benchmarking
Uses criterion to benchmark
keypair/encapsulate/decapsulate for every enabled parameter set, with
proper warm-up, iteration-count tuning, outlier rejection, and a confidence
interval, unlike a bare timer loop. Numbers are still environment-sensitive
(CPU, and the platform's random-number-source call overhead), so run it on
hardware you actually care about rather than trusting a number quoted here.
For a third-party reference point, SUPERCOP publishes its own cycle counts
for the ref implementation across many CPUs: https://bench.cr.yp.to/results-kem.html.
Randomness
The vendored C code calls one external C function, randombytes(unsigned char *, unsigned long long), for all key-generation and encapsulation
randomness. src/lib.rs implements it via the getrandom crate (the OS
CSPRNG) — the one piece of "dependency" the vendored C itself doesn't supply,
by SUPERCOP's own convention.
Platform notes
build.rs targets both compiler families that matter on Windows, not just
one:
- GCC/Clang (
*-gnu, and any Unix target): uses-includeto force-include each directory'snamespace.hahead of everything else, and-Dfor theCRYPTO_NAMESPACE-style defines. - MSVC's
cl.exe(*-pc-windows-msvc— the Rust team's recommended Windows target, not the-gnu/MinGW one):cl.exedoesn't understand-includeat all — its equivalent is/FI, and it needs/Dinstead of-D.build.rsdetects this fromTARGETand switches spelling.
Getting the flags right wasn't quite the whole story, though: cl.exe
(real MSVC, not clang-cl) also doesn't understand the GCC/Clang
__attribute__((...)) extension used in the vendored cryptoint headers
under vendor/common/. Since those files are untouched autogen output
(see vendor/NOTICE.md) and not something to hand-edit,
vendor/common/namespace.h neutralizes __attribute__ to a no-op macro
specifically when _MSC_VER is defined and neither __GNUC__ nor
__clang__ is — i.e. only for real cl.exe, not clang-cl, which already
understands the extension natively. The same headers also contain inline
GCC-style __asm__ blocks, but every one is already guarded behind
#if defined(__GNUC__) with a portable C fallback, and cl.exe never
defines __GNUC__, so those need no shim at all.
All of that said: this has only been compiled and tested on clang on
macOS (arm64) so far. The MSVC path above is a from-first-principles fix
for a real, checked incompatibility (verified by reading cl.exe's actual
documented flag set and grepping every vendored file for GCC/Clang-only
constructs), not something run through an actual Windows/MSVC toolchain —
there isn't one available in this environment. Treat it as "should work,
please verify" rather than "verified," and file an issue with the actual
compiler error if it doesn't.
Internals
vendor/ is a deduplicated extraction from SUPERCOP: the generic helper
layer (SHA-512, sort networks, crypto_declassify, the cryptoint
headers) compiles once and is shared by every parameter set, while the
Streamlined NTRU Prime algorithm itself is stored once but compiled once
per parameter set (the object code genuinely differs — parameter sizes are
baked in at compile time). See vendor/NOTICE.md for
exactly what was changed from the original SUPERCOP sources, why the split
is drawn where it is, and how the extraction was validated — worth reading
before modifying anything under vendor/.
License
This crate's own code (build.rs, src/lib.rs, benches/) is dual-licensed
under MIT (LICENSE-MIT) or Apache-2.0
(LICENSE-APACHE), at your option. The vendored
Streamlined NTRU Prime sources under vendor/algo/ are not covered by
that — they're Daniel J. Bernstein and coauthors' code, and the only usage
grant on record from them is purpose-scoped to NIST's public review
process, not a general open-source release. See LICENSE.md
for the full split and vendor/NOTICE.md for the
source-by-source breakdown — read both before relying on this past
experimentation.