Skip to content

Latest commit

 

History

33 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

nivalis

A no_std implementation of the Noise Protocol Framework revision 34 in Rust.

Why this exists

snow is the "official" Noise implementation in Rust that is used by most people. Although the implementation is great, the pattern/ciphersuite is passed in a string and most of the validation is done at runtime.

nivalis has the goal to check the validity of the pattern, role, and ciphersuite at compile time. It will also block the compilation if you didn't provided the correct keys for a given pattern using const assertion in the build() method. The goal of that is to make it almost impossible to missuse and potentially panic at runtime. It also allows you to use your IDE's auto-complete to see which patterns/ciphersuites are available.

This isn't a claim that nivalis is more correct than snow cryptographically, it is just a new API which aims to catch setup mistake earlier. The builder uses a typestate pattern where LS, RS, LE, RE, and PSK are bool const generics tracking, at compile time, which pieces have been supplied so far, and P: PatternRequirements<R> ties those flags to what the chosen pattern and role actually need.

Also, with snow, a PSK is supplied with an explicit slot index (e.g. .psk(0, key)), which means it's possible to build something like IKpsk2 while actually passing the key at index 0. nivalis doesn't expose an index at all, .psk(key) takes a single key, and the pattern itself determines where that key gets mixed into the handshake. There's exactly one PSK slot to fill, so there's no wrong slot to fill it at. The drawback of this is that it doesn't allow you to use patterns that use multiple PSKs such as XXpsk0+psk3.

Available Patterns

The patterns supported by this implementation are all the one-way, interactive (fundamental), and the PSK variation presented on Noise Explorer.

The full list includes:

  • N, K and X
  • NN, NK, NX, XN, XK, XX, KN, KK, KX, IN, IK, IX
  • Npsk0, Kpsk0, Xpsk1
  • NNpsk0, NNpsk2, NKpsk0, NKpsk2, NXpsk2, XNpsk3, XKpsk3, XXpsk3, KNpsk0, KNpsk2, KKpsk0, KKpsk2, KXpsk2, INpsk1, INpsk2, IKpsk1, IKpsk2, IXpsk2

Fallback patterns are not supported yet.

Available Ciphersuites

All cipher, dh and hash methods defined by the Noise protocol's documentation can be used.

For Cipher, it includes:

  • AES-GCM
  • ChaCha20-Poly1305

For DH:

  • X25519
  • X448

For Hash:

  • Blake2b
  • Blake2s
  • SHA256
  • SHA512

no_std

This crate has been implemented as no_std-only.

Because nivalis does not assume an OS or runtime environment, it doesn't provide a default source of randomness. Applications must provide an RNG implementation appropriate for their target.

Test vectors

nivalis is tested against, and passes, the full set of published test vectors from both cacophony and snow.

Example

Building an initiator and a responder for the IK pattern:

use nivalis::{
   builder::NewBuilder,
   crypto::{cipher::chacha20::ChaChaPoly, dh::{DH, DHKeypair, x25519::X25519dh}, hash::blake2s::Blake2s},
   patterns::{IK, roles::{Initiator, Responder}},
};
use rand::rng;

let mut rng = rng();
let init_static = X25519dh::generate_keypair(&mut rng);
let resp_static = X25519dh::generate_keypair(&mut rng);

let initiator = NewBuilder::<IK, Initiator, X25519dh, ChaChaPoly, Blake2s>::new()
        .local_static_key(init_static.private().to_owned())
        .remote_static_key(resp_static.public())
        .build()
        .expect("failed to build initiator handshake state");

let responder = NewBuilder::<IK, Responder, X25519dh, ChaChaPoly, Blake2s>::new()
        .local_static_key(resp_static.private().to_owned())
        .build()
        .expect("failed to build responder handshake state");

If you tried to .build() the initiator above without calling .remote_static_key(...) first, the program would refuse to compile.

See examples/ for a full handshake-to-transport walkthrough, including sending and decrypting an encrypted message once the handshake completes.

Benchmarks

This crate's performance is comparable to snow. The benchmarks below (that were adapted from snow's) compare equivalent operations between the two libraries using the same patterns and ciphersuites :

Benchmark snow nivalis
XX Builder 23.829 µs 28.678 µs
NN Builder 238.97 ns 147.35 ns
XX Handshake 323.08 µs 316.36 µs
NN Handshake 127.89 µs 133.59 µs
Transport throughput 1.2163 GiB/s 1.2284 GiB/s

These numbers come from a single run on a personal computer, they are only documented to give you an idea of the performances between the two crates.

To test it yourself on your computer, run:

 ╰─λ cargo bench

Status

nivalis has not been independently security-reviewed or audited. It passes the standard Noise test vectors, which gives confidence in protocol-level correctness, but that is not a substitute for cryptographic review. Do not use this in a security-critical context without an audit. Use at your own risk, and please report anything that looks wrong.

Contributing

This is very much a young project, and there's a lot of room to help. Issues and PRs are welcome, if you're considering a larger change, opening an issue first to discuss the approach is appreciated.

About

A compile-time verified no_std implementation of the Noise Protocol Framework in Rust.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages