13 releases
Uses new Rust 2024
new 0.3.0 | Apr 27, 2025 |
---|---|
0.2.2 | Apr 26, 2025 |
0.1.5 | Apr 25, 2025 |
#315 in Cryptography
1,176 downloads per month
Used in 2 crates
68KB
1.5K
SLoC
A double-ratchet implementation following Signal.
Also with X3DH and XEdDSA implementation.
E2EE chat app (TUI)
An E2EE chat server-client TUI app base on this crate is available here.
Compared to others
- There's no global party state, instead, it is each ratchet having its own state.
- It's really double-ratchet (2 kinds of ratchets), DhRootRatchet and MessageRatchet (AKA ChainRatchet).
- Header encryption support.
- Provide chat parties implementation.
- Provide gRPC transport implementation.
- Provide X3DH shared key initialization implementation.
- Provide XEdDSA implementation.
Example
Ratchet only example:
use ratchetx2::SharedKeys;
use ratchetx2::rand::SystemRandom;
use ratchetx2::agreement::{EphemeralPrivateKey, X25519};
let shared_keys = SharedKeys {
secret_key: [0; 32],
header_key_alice: [1; 32],
header_key_bob: [2; 32],
};
let mut bob = shared_keys.bob(EphemeralPrivateKey::generate(&X25519, &SystemRandom::new()).unwrap());
let mut alice = shared_keys.alice(&bob.public_key());
// Alice sends first
bob.step_dh_root(&alice.public_key());
assert_eq!(alice, bob); // Alice and Bob have the "same" state
assert_eq!(alice.step_msgs(), bob.step_msgr()); // returning the same message key
assert_eq!(alice.step_msgs(), bob.step_msgr());
// Bob sends
bob.step_dh_root(&alice.public_key());
alice.step_dh_root(&bob.public_key());
assert_eq!(alice, bob);
assert_eq!(bob.step_msgs(), alice.step_msgr());
assert_eq!(bob.step_msgs(), alice.step_msgr());
// Alice sends
alice.step_dh_root(&bob.public_key());
bob.step_dh_root(&alice.public_key());
assert_eq!(alice, bob);
assert_eq!(alice.step_msgs(), bob.step_msgr());
assert_eq!(alice.step_msgs(), bob.step_msgr());
E2EE chat app example:
use ratchetx2::{transport::ChannelTransport, Party, SharedKeys};
use ratchetx2::rand::SystemRandom;
use ratchetx2::agreement::{EphemeralPrivateKey, X25519};
# #[tokio::main]
# async fn main() {
let shared_keys = SharedKeys {
secret_key: [0; 32],
header_key_alice: [1; 32],
header_key_bob: [2; 32],
};
let bob_ratchetx2 = shared_keys.bob(EphemeralPrivateKey::generate(&X25519, &SystemRandom::new()).unwrap());
let alice_ratchetx2 = shared_keys.alice(&bob_ratchetx2.public_key());
let (a, b) = ChannelTransport::new();
let mut alice = Party::new(alice_ratchetx2, a, "AliceBob");
let mut bob = Party::new(bob_ratchetx2, b, "AliceBob");
alice.push("hello world").await.unwrap();
assert_eq!(bob.fetch().await.unwrap().remove(0).unwrap(), b"hello world");
alice.push("hello Bob").await.unwrap();
assert_eq!(bob.fetch().await.unwrap().remove(0).unwrap(), b"hello Bob");
bob.push("hello Alice").await.unwrap();
assert_eq!(alice.fetch().await.unwrap().remove(0).unwrap(), b"hello Alice");
# }
XEdDSA example:
use ratchetx2::xeddsa::XEdDSAPrivateKey;
use ratchetx2::rand::SystemRandom;
let xeddsa = XEdDSAPrivateKey::generate(&SystemRandom::new());
let signature = xeddsa.sign("hello world");
let public_key = xeddsa.compute_public_key();
public_key.verify("hello world", &signature).unwrap();
assert!(public_key.verify("goodbye world", &signature).is_err());
let alice = XEdDSAPrivateKey::generate(&SystemRandom::new());
let bob = XEdDSAPrivateKey::generate(&SystemRandom::new());
assert_eq!(
alice.agree_ephemeral(bob.compute_public_key().as_ref()).unwrap(),
bob.agree_ephemeral(alice.compute_public_key().as_ref()).unwrap()
);
X3DH initialize example:
use ratchetx2::server::RpcServer;
use ratchetx2::x3dh::X3DHClient;
# #[tokio::main]
# async fn main() {
tokio::spawn(async {
RpcServer::run("127.0.0.1:3002", None).await.unwrap();
});
// wait server start
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
const SERVER_ADDR: &str = "http://127.0.0.1:3002";
let mut alice_x3dh = X3DHClient::connect(SERVER_ADDR, None).await.unwrap();
let mut bob_x3dh = X3DHClient::connect(SERVER_ADDR, None).await.unwrap();
bob_x3dh.publish_keys().await.unwrap();
let mut alice = alice_x3dh
.push_initial_message(&bob_x3dh.public_identity_key(), SERVER_ADDR)
.await
.unwrap();
let mut bob = bob_x3dh
.handle_initial_message(&alice_x3dh.public_identity_key(), SERVER_ADDR)
.await
.unwrap();
alice.push("hello world").await.unwrap();
assert_eq!(
bob.fetch().await.unwrap().remove(0).unwrap(),
b"hello world"
);
alice.push("hello Bob").await.unwrap();
assert_eq!(
bob.fetch().await.unwrap().remove(0).unwrap(),
b"hello Bob"
);
bob.push("hello Alice").await.unwrap();
assert_eq!(
alice.fetch().await.unwrap().remove(0).unwrap(),
b"hello Alice"
);
# }
Dependencies
~14–25MB
~450K SLoC