Pure-Rust X25519 (Curve25519) elliptic curve Diffie-Hellman implementation for no_std and bare-metal targets.
The standard curve25519-dalek and x25519-dalek crates (from RustCrypto) fail to compile on certain bare-metal targets like x86_64-unknown-none with LLVM errors:
LLVM ERROR: Do not know how to split the result of this operator!
This happens because these crates use SIMD intrinsics that aren't available on all targets. This pure-Rust implementation avoids SIMD entirely, making it portable to any Rust target.
- 100% Pure Rust: No assembly, no SIMD, no platform-specific code
no_stdcompatible: Works in bare-metal and embedded environments- Constant-time: Side-channel resistant Montgomery ladder
- RFC 7748 compliant: Passes official test vectors
- Simple API: Drop-in replacement for other X25519 implementations
- 128-bit security level
- Constant-time operations (resistant to timing attacks)
- 32-byte keys (compact and efficient)
- ~100k operations/sec on modern CPUs
Add to your Cargo.toml:
[dependencies]
x25519-nostd = "0.1"use x25519_nostd::{public_key, diffie_hellman};
// Alice generates a keypair
let alice_secret = [0x42u8; 32]; // In practice, use secure random
let alice_public = public_key(&alice_secret);
// Bob generates a keypair
let bob_secret = [0x07u8; 32];
let bob_public = public_key(&bob_secret);
// Both compute the same shared secret
let alice_shared = diffie_hellman(&alice_secret, &bob_public);
let bob_shared = diffie_hellman(&bob_secret, &alice_public);
assert_eq!(alice_shared, bob_shared);use x25519_nostd::{public_key, diffie_hellman};
// Generate static keypair
let static_secret = [/* secure random 32 bytes */];
let static_public = public_key(&static_secret);
// Compute shared secret with peer
let peer_public = [/* peer's public key */];
let shared_secret = diffie_hellman(&static_secret, &peer_public);
// Use shared_secret with a KDF (like BLAKE2s or HKDF)X25519 performs scalar multiplication on Curve25519:
- Curve equation:
y² = x³ + 486662x² + x - Prime field:
p = 2^255 - 19 - Base point:
u = 9 - Scalar clamping:
s[0] &= 248, s[31] &= 127, s[31] |= 64
Uses the Montgomery ladder for constant-time scalar multiplication.
- Field elements represented as 5 limbs of 51 bits each (255 bits total)
- Montgomery ladder for constant-time point multiplication
- Field arithmetic modulo
2^255 - 19 - Inversion using Fermat's little theorem:
a^(p-2) ≡ a^(-1) (mod p) - No heap allocations
On modern x86_64 CPUs:
- ~100k key exchanges per second
- ~10 µs per scalar multiplication
(Performance varies by target; bare-metal may differ)
Tested on:
x86_64-unknown-none(bare-metal)x86_64-unknown-linux-gnu(std)- Other targets should work but are untested
- Never reuse secret keys: Generate new ephemeral keys for each session
- Use a KDF: The raw shared secret should be passed through HKDF or similar
- Validate public keys: Check for small-subgroup attacks in your protocol
- Use secure randomness: Never use weak RNGs for key generation
Run the test suite:
cargo testIncludes RFC 7748 test vectors.
Licensed under Apache License 2.0.
- RFC 7748: Elliptic Curves for Security
- Curve25519: new Diffie-Hellman speed records
- WireGuard: Next Generation Kernel Network Tunnel
Contributions welcome! This crate was extracted from a bare-metal OS project to help the Rust embedded and bare-metal community overcome LLVM SIMD limitations.