Skip to content

Commit

Permalink
Preparation for v1.0.0
Browse files Browse the repository at this point in the history
* pqc_kyber removed as KEM option as it is still vulnerable to KyberSlash
* Lots of documentation improvements
* Test coverage improvements
* Added initial support for heap allocations via `alloc` feature flag
  • Loading branch information
jmlepisto committed Nov 15, 2024
1 parent df0a38d commit 04fd84a
Show file tree
Hide file tree
Showing 33 changed files with 720 additions and 430 deletions.
15 changes: 4 additions & 11 deletions Cargo.lock

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

10 changes: 4 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "clatter"
version = "1.0.0-rc.2"
version = "1.0.0"
edition = "2021"
license = "MIT"
description = "no_std compatible implementation of Noise protocol framework with Post-Quantum extensions"
Expand All @@ -9,6 +9,7 @@ repository = "https://github.com/jmlepisto/clatter"

keywords = [
"noise",
"noise-protocol",
"crypto",
"protocol",
"post-quantum",
Expand Down Expand Up @@ -40,6 +41,7 @@ default = [

alloc = []
std = [
"alloc",
"sha2/std",
"blake2/std",
"aes-gcm/std",
Expand All @@ -54,9 +56,6 @@ use-aes-gcm = ["aes-gcm"]
use-chacha20poly1305 = ["chacha20poly1305"]
use-pqclean-kyber = ["pqcrypto-kyber", "pqcrypto-traits"]
use-rust-crypto-kyber = ["ml-kem"]
use-argyle-kyber512 = ["pqc_kyber/kyber512"]
use-argyle-kyber768 = ["pqc_kyber"]
use-argyle-kyber1024 = ["pqc_kyber/kyber1024"]
use-25519 = ["x25519-dalek"]

# docs.rs-specific configuration
Expand All @@ -66,7 +65,7 @@ rustdoc-args = ["--cfg", "docsrs"]

[dependencies]
# Mandatory
arrayvec = { version = "0.7.6", default-features = false }
arrayvec = { version = "0.7.6", default-features = false, features = ["zeroize"] }
rand_core = "0.6.4"
zeroize = { version = "1.8.1", default-features = false, features = ["zeroize_derive"] }

Expand All @@ -75,7 +74,6 @@ aes-gcm = { version = "0.10.3", default-features = false, features = ["aes"], op
blake2 = { version = "0.10.6", default-features = false, optional = true }
chacha20poly1305 = { version = "0.10.1", default-features = false, features = ["rand_core"], optional = true }
ml-kem = { version = "0.2.1", default-features = false, features = ["zeroize"], optional = true }
pqc_kyber = { version = "0.7.1", default-features = false, optional = true }
sha2 = { version = "0.10.8", default-features = false, optional = true }
thiserror-no-std = { version = "2.0.2", default-features = false }
x25519-dalek = { version = "2.0.1", default-features = false, features = ["static_secrets", "zeroize"], optional = true }
Expand Down
24 changes: 11 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,15 +127,11 @@ of all the configurable features supported by Clatter:
| `use-blake2` | Enable BLAKE2 hashing | yes | |
| `use-rust-crypto-kyber` | Enable Kyber KEMs by [RustCrypto][RustCrypto] | yes | |
| `use-pqclean-kyber` | Enable Kyber KEMs by [PQClean][PQClean] | yes | |
| `use-argyle-kyber512` | Eable Kyber512 KEM by [Argyle-Software][Argyle] | no | Only one Argyle KEM can be enabled at a time |
| `use-argyle-kyber768` | Eable Kyber768 KEM by [Argyle-Software][Argyle] | no | |
| `use-argyle-kyber1024` | Eable Kyber1024 KEM by [Argyle-Software][Argyle] | no | |
| `std` | Enable standard library support | no | Currently only affects dependencies |
| `alloc` | Enable allocator support | no | Reserved for future use |
| `std` | Enable standard library support | no | Enables `std` for supported dependencies |
| `alloc` | Enable allocator support | no | |

[RustCrypto]: https://github.com/RustCrypto/KEMs
[PQClean]: https://github.com/rustpq/pqcrypto
[Argyle]: https://github.com/Argyle-Software/kyber

## PQ? NQ? Why should I care?

Expand Down Expand Up @@ -177,11 +173,13 @@ possible to configure a separate KEM for ephemeral use.
* PQNoise presents *SEEC*, a method for improving RNG security in bad randomness settings. Clatter
does not currently implement *SEEC*.

## Roadmap before first stable release
## Verification

* ~~Add support for PSKs as defined by the Noise spec~~
* ~~Add support for all crypto algorithms listed in Noise spec~~, no compatible X448 implementation exists
* ~~Add support for all fundamental Noise patterns (one-way patterns missing)~~
* ~~More KEMs with ability to configure the desired vendor~~
* ~~Proper testing and fuzzing~~
* ~~Better documentation~~
Caltter is verified by:

* Unit tests
* [Integration tests](tests/)
* [Fuzzing](fuzz/)
* [Cacophony](https://github.com/haskell-cryptography/cacophony) and [Snow](https://github.com/mcginty/snow) test vectors
* Supported pre-made handshake patterns verified
* Test harness in [vectors/](vectors/)
2 changes: 1 addition & 1 deletion examples/basic_dual_layer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ fn main() {
let _ = bob.read_message(&buf_alice[..n], &mut buf_bob).unwrap();

// Second handshake message from responder to initiator
// <-- e, qq
// <-- e, ee
let n = bob.write_message(&[], &mut buf_bob).unwrap();
let _ = alice.read_message(&buf_bob[..n], &mut buf_alice).unwrap();
// --------------------------
Expand Down
2 changes: 1 addition & 1 deletion examples/basic_nq.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ fn main() {
let mut rng_alice = rand::thread_rng();
let mut rng_bob = rand::thread_rng();

// Generate static keys
// Generate keys
let alice_s = X25519::genkey(&mut rng_alice).unwrap();
let bob_s = X25519::genkey(&mut rng_bob).unwrap();

Expand Down
119 changes: 119 additions & 0 deletions examples/custom_crypto.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
use core::str;

use arrayvec::ArrayVec;
use clatter::bytearray::{ByteArray, SensitiveByteArray};
use clatter::crypto::cipher::ChaChaPoly;
use clatter::crypto::kem::pqclean_kyber::Kyber768;
// We can mix and match KEMs from different vendors
use clatter::crypto::kem::rust_crypto_kyber::Kyber512;
use clatter::handshakepattern::noise_pqnn;
use clatter::traits::Handshaker;
use clatter::PqHandshake;

/// **Obvious warning**
///
/// This is not a real hash function. This is a silly example
/// which will NEVER WORK in real life.
///
/// With **careful** analysis you may implement support for existing well-established
/// cryptographic primitives but as a rule-of-thumb you should never implement
/// your own custom cryptography. Even if you use well known primitives you must have
/// the competence to evaluate the risks of integrating such cryptography to Noise protocol
/// which is not endorsed by the original author.
#[derive(Default)]
struct MySillyHash(u128);

// This has to be implemented for all cryptographic components.
// The component name will appear in the Noise pattern name.
impl clatter::traits::CryptoComponent for MySillyHash {
fn name() -> &'static str {
"SillyHash"
}
}

impl clatter::traits::Hash for MySillyHash {
// Most of the crypto traits by clatter have type parameters, which
// indicate some static sizing qualities of the algorithms. These types
// usually have to implement `clatter::bytearray::ByteArray` but implementations
// for the most common static array sizes are built-in.
type Block = [u8; 32];
// ..But you can also use any size with the help of ArrayVec (stack allocated).
// Here we are also using `SensitiveByteArray`, a wrapper provided
// by Clatter, which is zeroized on drop.
type Output = SensitiveByteArray<ArrayVec<u8, 32>>;

// If you have the `alloc` crate feature enabled, you could
// also use `clatter::bytearray::HeapArray` for a heap-allocated
// alternative.

fn input(&mut self, data: &[u8]) {
for d in data {
self.0 ^= *d as u128;
}
}

fn result(self) -> Self::Output {
let mut out = [0u8; 32];
out[..16].copy_from_slice(&self.0.to_be_bytes());
Self::Output::from_slice(&out)
}
}

fn main() {
let mut rng_alice = rand::thread_rng();
let mut rng_bob = rand::thread_rng();
let mut alice = PqHandshake::<Kyber512, Kyber768, ChaChaPoly, MySillyHash, _>::new(
noise_pqnn(),
&[],
true,
None,
None,
None,
None,
&mut rng_alice,
)
.unwrap();

let mut bob = PqHandshake::<Kyber512, Kyber768, ChaChaPoly, MySillyHash, _>::new(
noise_pqnn(),
&[],
false,
None,
None,
None,
None,
&mut rng_bob,
)
.unwrap();

// Handshake message buffers
let mut buf_alice = [0u8; 4096];
let mut buf_bob = [0u8; 4096];

// First handshake message from initiator to responder
// e -->
let n = alice.write_message(&[], &mut buf_alice).unwrap();
let _ = bob.read_message(&buf_alice[..n], &mut buf_bob).unwrap();

// Second handshake message from responder to initiator
// <-- ekem
let n = bob.write_message(&[], &mut buf_bob).unwrap();
let _ = alice.read_message(&buf_bob[..n], &mut buf_alice).unwrap();

// Handshake should be done
assert!(alice.is_finished() && bob.is_finished());

// Finish handshakes and move to transport mode
let mut alice = alice.finalize().unwrap();
let mut bob = bob.finalize().unwrap();

// Send a message from Alice to Bob
let msg = b"Hello from initiator";
let n = alice.send(msg, &mut buf_alice).unwrap();
let n = bob.receive(&buf_alice[..n], &mut buf_bob).unwrap();

println!(
"Bob received from Alice: {}",
str::from_utf8(&buf_bob[..n]).unwrap()
);
}
2 changes: 1 addition & 1 deletion examples/psk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ fn main() {
let mut rng_alice = rand::thread_rng();
let mut rng_bob = rand::thread_rng();

// Generate static keys
// Generate keys
let alice_s = X25519::genkey(&mut rng_alice).unwrap();
let bob_s = X25519::genkey(&mut rng_bob).unwrap();

Expand Down
17 changes: 17 additions & 0 deletions fuzz-all.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/bin/bash
set -e

if [ -z "$1" ]
then
echo "Please supply target fuzzing time in seconds as an argument"
fi

T="$1"
FUZZ="cargo +nightly fuzz"

targets=$($FUZZ list)

$FUZZ build --release
for t in $targets; do
$FUZZ run --release $t -- -max_total_time=$T
done
44 changes: 31 additions & 13 deletions fuzz/fuzz_targets/nq_handshake_payload.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,31 @@
#![no_main]

use clatter::bytearray::ByteArray;
use clatter::constants::MAX_MESSAGE_LEN;
use clatter::crypto::cipher::ChaChaPoly;
use clatter::crypto::cipher::{AesGcm, ChaChaPoly};
use clatter::crypto::dh::X25519;
use clatter::crypto::hash::Sha256;
use clatter::crypto::hash::{Blake2b, Blake2s, Sha256, Sha512};
use clatter::handshakepattern::*;
use clatter::traits::Dh;
use clatter::traits::{Cipher, Dh, Hash};
use clatter::{Handshaker, NqHandshake};
use libfuzzer_sys::fuzz_target;

fuzz_target!(|data: &[u8]| {
verify_with::<X25519, AesGcm, Sha256>(data);
verify_with::<X25519, AesGcm, Sha512>(data);
verify_with::<X25519, AesGcm, Blake2b>(data);
verify_with::<X25519, AesGcm, Blake2s>(data);
verify_with::<X25519, ChaChaPoly, Sha256>(data);
verify_with::<X25519, ChaChaPoly, Sha512>(data);
verify_with::<X25519, ChaChaPoly, Blake2b>(data);
verify_with::<X25519, ChaChaPoly, Blake2s>(data);
});

fn verify_with<DH: Dh, C: Cipher, H: Hash>(data: &[u8]) {
let handshakes = [
noise_n(),
noise_k(),
noise_x(),
noise_ik(),
noise_in(),
noise_ix(),
Expand All @@ -23,6 +38,9 @@ fuzz_target!(|data: &[u8]| {
noise_xk(),
noise_xn(),
noise_xx(),
noise_n_psk0(),
noise_k_psk0(),
noise_x_psk1(),
noise_ik_psk1(),
noise_ik_psk2(),
noise_in_psk1(),
Expand All @@ -45,21 +63,21 @@ fuzz_target!(|data: &[u8]| {

const PSK: &[u8] = b"Trapped inside this Octavarium!!";

let mut alice_rng = rand::thread_rng();
let mut bob_rng = rand::thread_rng();
for pattern in handshakes {
let mut alice_rng = rand::thread_rng();
let mut bob_rng = rand::thread_rng();

let mut alice_buf = [0u8; MAX_MESSAGE_LEN];
let mut alice_buf = [0u8; MAX_MESSAGE_LEN];

let alice_key = X25519::genkey(&mut alice_rng).unwrap();
let bob_key = X25519::genkey(&mut bob_rng).unwrap();
let bob_pub = bob_key.public.clone();
let alice_key = DH::genkey(&mut alice_rng).unwrap();
let bob_key = DH::genkey(&mut bob_rng).unwrap();
let bob_pub = bob_key.public.clone();

for pattern in handshakes {
let mut alice = NqHandshake::<X25519, ChaChaPoly, Sha256, _>::new(
let mut alice = NqHandshake::<DH, C, H, _>::new(
pattern.clone(),
&[],
true,
Some(alice_key.clone()),
Some(alice_key),
None,
Some(bob_pub),
None,
Expand All @@ -72,4 +90,4 @@ fuzz_target!(|data: &[u8]| {
// Alice writes fuzzed payload
let _ = alice.write_message(data, &mut alice_buf);
}
});
}
Loading

0 comments on commit 04fd84a

Please sign in to comment.