enigma-double-ratchet

Crates.ioenigma-double-ratchet
lib.rsenigma-double-ratchet
version0.1.0
created_at2025-12-15 11:37:50.987186+00
updated_at2025-12-15 11:37:50.987186+00
descriptionStandalone Signal-style double ratchet focused on deterministic, reusable async messaging primitives
homepagehttps://github.com/Gladius33/enigma-ratchet
repositoryhttps://github.com/Gladius33/enigma-ratchet
max_upload_size
id1985878
size54,992
Sébastien TLX (Gladius33)

documentation

https://docs.rs/enigma-ratchet

README

enigma-ratchet

Rust implementation of a standalone Signal-style Double Ratchet optimized for reuse in asynchronous systems. The crate focuses on determinism, explicit error handling, strict serialization, and easy integration into higher level protocols.

Features

  • X25519 Diffie-Hellman ratchet with HKDF-SHA256 based root and chain KDFs
  • XChaCha20-Poly1305 authenticated encryption with caller-supplied associated data
  • Stable binary message header format with canonical encoding tests
  • Bounded skipped-key storage for out-of-order delivery and replay defense
  • Zeroization of all secret material on drop
  • Comprehensive unit tests covering protocol state, KDF behavior, framing, negative flows, and limit enforcement

Quickstart

use enigma_ratchet::{RatchetKeyPair, RatchetState};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let shared_secret = [42u8; 32];
    let bob_keys = RatchetKeyPair::generate();
    let bob_pub = bob_keys.public();

    let mut alice = RatchetState::new_alice(shared_secret, bob_pub, b"handshake")?;
    let mut bob = RatchetState::new_bob(shared_secret, bob_keys, b"handshake")?;

    let packet = alice.encrypt(b"hello", b"ad")?;
    let plaintext = bob.decrypt(&packet, b"ad")?;
    assert_eq!(plaintext, b"hello");

    let response = bob.encrypt(b"hi", b"ad")?;
    let echoed = alice.decrypt(&response, b"ad")?;
    assert_eq!(echoed, b"hi");
    Ok(())
}

new_bob does not know the remote key at creation time. Its set_remote_dh helper allows the responder to preload a known DH public key before the first ciphertext if the surrounding handshake provides it.

Serialization and Framing

Each ciphertext is framed as len(header) || header || nonce || ciphertext+tag where the header length is encoded in a two-byte big-endian integer and header length is always forty bytes. Headers contain the sender Diffie-Hellman public key plus message counters and are included in the AEAD associated data along with the application-provided context hash. The deterministic encoding is exercised in both positive and negative tests.

Testing

Run the full suite with:

cargo test

The tests cover KDF rules, header encoding, packet framing, sequential and out-of-order delivery, skip-limit enforcement, replay detection, corrupted packets, and mismatched initialization secrets. All critical responsibilities have dedicated assertions. Refer to the documents in docs/ for deeper protocol, API, and security notes.

Commit count: 0

cargo fmt