reinstate repo with source from crates.io

This commit is contained in:
satvrn 2023-07-18 23:27:53 +00:00
parent 93727cedf1
commit d8c9e9b36c
15 changed files with 632 additions and 1948 deletions

1109
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,37 +1,29 @@
[package] [package]
name = "double-ratchet-2" name = "double-ratchet-rs"
authors = ["Hannes Furmans"] authors = ["Magnetite Contributors", "Hannes Furmans"]
description = "Implementation of Double Ratchet as specified by Signal." description = "A pure Rust implementation of the Double Ratchet Algorithm as specified by Signal."
homepage = "https://github.com/Dione-Software/double-ratchet-2" homepage = "https://github.com/magnetite-dev/double-ratchet-rs"
repository = "https://github.com/Dione-Software/double-ratchet-2" repository = "https://github.com/magnetite-dev/double-ratchet-rs"
readme = "README.md" readme = "README.md"
keywords = ["double-ratchet", "crypto", "cryptography", "signal"] keywords = ["double-ratchet", "crypto", "cryptography", "signal"]
version = "0.3.7" version = "0.4.3"
edition = "2018" edition = "2021"
license = "MIT" license = "MIT"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[badges]
maintenance = { status = "actively-developed" }
[dependencies] [dependencies]
p256 = {version = "0.10", features = ["ecdh", "arithmetic", "pem", "jwk"]} x25519-dalek = {version = "2.0.0-pre.1", default-features = false, features = ["serde", "u64_backend"]}
rand_core = {version = "0.6"} rand_core = "0.6"
getrandom = {version = "0.2.3"}
hkdf = "0.12" hkdf = "0.12"
hmac = "0.12" hmac = "0.12"
aes-gcm-siv = {version = "0.10.3"} aes-gcm-siv = "0.11"
sha2 = {version = "0.10"} sha2 = {version = "0.10", default-features = false}
serde = {version = "1", default-features = false, features = ["derive"]} serde = {version = "1", default-features = false, features = ["derive"]}
serde_bytes = "0.11" postcard = {version = "1", default-features = false, features = ["alloc"]}
bincode = "1"
hashbrown = {version = "0.13", features = ["serde"]} hashbrown = {version = "0.13", features = ["serde"]}
zeroize = {version = "1.3", features = ["zeroize_derive"]} zeroize = {version = "1.5", default-features = false, features = ["zeroize_derive"]}
[dev-dependencies] [dev-dependencies]
criterion = "0.4.0" criterion = "0.4"
[[bench]] [[bench]]
name = "double_ratchet_bench" name = "double_ratchet_bench"
@ -39,6 +31,3 @@ harness = false
[profile.release] [profile.release]
lto = true lto = true
[features]
wasm = ["getrandom/js"]

View file

@ -1,5 +1,6 @@
MIT License MIT License
Copyright (c) 2022 Magnetite Contributors
Copyright (c) 2021 Hannes Furmans Copyright (c) 2021 Hannes Furmans
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy

164
README.md
View file

@ -1,71 +1,92 @@
[![Crate](https://img.shields.io/crates/v/double-ratchet-2)](https://crates.io/crates/double-ratchet-2) [![Crate](https://img.shields.io/crates/v/double-ratchet-rs)](https://crates.io/crates/double-ratchet-rs)
[![License](https://img.shields.io/github/license/Dione-Software/double-ratchet-2)](https://github.com/Dione-Software/double-ratchet-2/blob/main/LICENSE) [![License](https://img.shields.io/github/license/magnetite-dev/double-ratchet-rs)](https://github.com/magnetite-dev/double-ratchet-rs/blob/main/LICENSE)
[![Coverage Status](https://coveralls.io/repos/github/Dione-Software/double-ratchet-2/badge.svg?branch=main)](https://coveralls.io/github/Dione-Software/double-ratchet-2?branch=main) [![Coverage Status](https://coveralls.io/repos/github/magnetite-dev/double-ratchet-rs/badge.svg?branch=main)](https://coveralls.io/github/magnetite-dev/double-ratchet-rs?branch=main)
[![Workflow Status](https://github.com/Dione-Software/double-ratchet-2/actions/workflows/rust.yml/badge.svg)](https://github.com/Dione-Software/double-ratchet-2/actions/workflows/rust.yml) [![Workflow Status](https://github.com/magnetite-dev/double-ratchet-rs/actions/workflows/rust.yml/badge.svg)](https://github.com/magnetite-dev/double-ratchet-rs/actions/workflows/rust.yml)
![Maintenance](https://img.shields.io/badge/maintenance-activly--developed-brightgreen.svg)
# double-ratchet-2 # double-ratchet-rs
Implementation of the double ratchet system/encryption as specified by [Signal][1]. A pure Rust implementation of the Double Ratchet Algorithm as specified by [Signal][1].
**WARNING! This implementation uses P-256 NOT Curve25519 as specified by Signal!** This implementation follows the cryptographic recommendations provided by [Signal][2].
The implementation follows the cryptographic recommendations provided by [Signal][2].
The AEAD Algorithm uses a constant Nonce. This might be changed in the future. The AEAD Algorithm uses a constant Nonce. This might be changed in the future.
## Example Usage: Fork of [double-ratchet-2](https://github.com/Dione-Software/double-ratchet-2).
## Examples
### Standard Usage
Alice encrypts a message which is then decrypted by Bob.
### Standard:
```rust ```rust
use double_ratchet_2::ratchet::Ratchet; use double_ratchet_rs::Ratchet;
let sk = [1; 32]; // Initial Key created by a symmetric key agreement protocol let sk = [1; 32]; // Shared key created by a symmetric key agreement protocol
let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk); // Creating Bobs Ratchet (returns Bobs PublicKey)
let mut alice_ratchet = Ratchet::init_alice(sk, public_key); // Creating Alice Ratchet with Bobs PublicKey let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk); // Creating Bob's Ratchet (returns Bob's PublicKey)
let data = b"Hello World".to_vec(); // Data to be encrypted let mut alice_ratchet = Ratchet::init_alice(sk, public_key); // Creating Alice's Ratchet with Bob's PublicKey
let ad = b"Associated Data"; // Associated Data
let data = b"Hello World".to_vec(); // Data to be encrypted
let ad = b"Associated Data"; // Associated data
let (header, encrypted, nonce) = alice_ratchet.encrypt(&data, ad); // Encrypting message with Alice's Ratchet (Alice always needs to send the first message)
let decrypted = bob_ratchet.decrypt(&header, &encrypted, &nonce, ad); // Decrypt message with Bob's Ratchet
let (header, encrypted, nonce) = alice_ratchet.ratchet_encrypt(&data, ad); // Encrypting message with Alice Ratchet (Alice always needs to send the first message)
let decrypted = bob_ratchet.ratchet_decrypt(&header, &encrypted, &nonce, ad); // Decrypt message with Bobs Ratchet
assert_eq!(data, decrypted) assert_eq!(data, decrypted)
``` ```
### With lost message: ### Recovering a Lost Message
Alice encrypts 2 messages for Bob.
The latest message must be decrypted first.
```rust ```rust
use double_ratchet_rs::Ratchet;
let sk = [1; 32]; // Initial Key created by a symmetric key agreement protocol let sk = [1; 32]; // Shared key created by a symmetric key agreement protocol
let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk); // Creating Bobs Ratchet (returns Bobs PublicKey)
let mut alice_ratchet = Ratchet::init_alice(sk, public_key); // Creating Alice Ratchet with Bobs PublicKey
let data = b"Hello World".to_vec(); // Data to be encrypted
let ad = b"Associated Data"; // Associated Data
let (header1, encrypted1, nonce1) = alice_ratchet.ratchet_encrypt(&data, ad); // Lost message let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk); // Creating Bob's Ratchet (returns Bob's PublicKey)
let (header2, encrypted2, nonce2) = alice_ratchet.ratchet_encrypt(&data, ad); // Successful message let mut alice_ratchet = Ratchet::init_alice(sk, public_key); // Creating Alice's Ratchet with Bob's PublicKey
let decrypted2 = bob_ratchet.ratchet_decrypt(&header2, &encrypted2, &nonce2, ad); // Decrypting second message first let data = b"Hello World".to_vec(); // Data to be encrypted
let decrypted1 = bob_ratchet.ratchet_decrypt(&header1, &encrypted1, &nonce1, ad); // Decrypting latter message let ad = b"Associated Data"; // Associated data
let comp = decrypted1 == data && decrypted2 == data; let (header1, encrypted1, nonce1) = alice_ratchet.encrypt(&data, ad); // Lost message
assert!(comp); let (header2, encrypted2, nonce2) = alice_ratchet.encrypt(&data, ad); // Successful message
let decrypted2 = bob_ratchet.decrypt(&header2, &encrypted2, &nonce2, ad); // Decrypting second message first
let decrypted1 = bob_ratchet.decrypt(&header1, &encrypted1, &nonce1, ad); // Decrypting latter message
assert_eq!(data, decrypted1);
assert_eq!(data, decrypted2);
``` ```
### Encryption before recieving inital message ### Encryption Before Decrypting First Message
Bob encrypts a message before decrypting one from Alice.
This will result in a panic.
```rust ```rust
use double_ratchet_2::ratchet::Ratchet; use double_ratchet_rs::Ratchet;
let sk = [1; 32]; let sk = [1; 32];
let ad = b"Associated Data";
let (mut bob_ratchet, _) = Ratchet::init_bob(sk);
let data = b"Hello World".to_vec();
let (_, _, _) = bob_ratchet.ratchet_encrypt(&data, ad); let (mut bob_ratchet, _) = Ratchet::init_bob(sk);
let data = b"Hello World".to_vec();
let ad = b"Associated Data";
let (_, _, _) = bob_ratchet.encrypt(&data, ad);
``` ```
### Encryption after recieving initial message ### Encryption After Decrypting First Message
However bob can (of course) also encrypt messages. This is possible, after decrypting the first message from alice.
Bob *can* also encrypt messages.
This is only possible after decrypting one from Alice first though.
```rust ```rust
use double_ratchet_2::ratchet::Ratchet; use double_ratchet_rs::Ratchet;
let sk = [1; 32]; let sk = [1; 32];
let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk); let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk);
@ -74,64 +95,83 @@ let mut alice_ratchet = Ratchet::init_alice(sk, public_key);
let data = b"Hello World".to_vec(); let data = b"Hello World".to_vec();
let ad = b"Associated Data"; let ad = b"Associated Data";
let (header1, encrypted1, nonce1) = alice_ratchet.ratchet_encrypt(&data, ad); let (header1, encrypted1, nonce1) = alice_ratchet.encrypt(&data, ad);
let _decrypted1 = bob_ratchet.ratchet_decrypt(&header1, &encrypted1, &nonce1, ad); let _decrypted1 = bob_ratchet.decrypt(&header1, &encrypted1, &nonce1, ad);
let (header2, encrypted2, nonce2) = bob_ratchet.ratchet_encrypt(&data, ad); let (header2, encrypted2, nonce2) = bob_ratchet.encrypt(&data, ad);
let decrypted2 = alice_ratchet.ratchet_decrypt(&header2, &encrypted2, &nonce2, ad); let decrypted2 = alice_ratchet.decrypt(&header2, &encrypted2, &nonce2, ad);
assert_eq!(data, decrypted2); assert_eq!(data, decrypted2);
``` ```
### Constructing and Deconstructing Headers ### Constructing and Deconstructing Headers
```rust ```rust
use double_ratchet_rs::{Header, Ratchet};
let sk = [1; 32];
let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk);
let mut alice_ratchet = Ratchet::init_alice(sk, public_key);
let data = b"hello World".to_vec();
let ad = b"Associated Data";
let (header, _, _) = alice_ratchet.encrypt(&data, ad);
let header_bytes: Vec<u8> = header.clone().into(); let header_bytes: Vec<u8> = header.clone().into();
let header_const = Header::from(header_bytes); let header_const = Header::from(header_bytes);
assert_eq!(header, header_const); assert_eq!(header, header_const);
``` ```
## Example Ratchet with encrypted headers ### Encrypted Headers
```rust ```rust
use double_ratchet_2::ratchet::RatchetEncHeader; use double_ratchet_rs::RatchetEncHeader;
let sk = [0; 32]; let sk = [0; 32];
let shared_hka = [1; 32]; let shared_hka = [1; 32];
let shared_nhkb = [2; 32]; let shared_nhkb = [2; 32];
let (mut bob_ratchet, public_key) = RatchetEncHeader::init_bob(sk, shared_hka, shared_nhkb); let (mut bob_ratchet, public_key) = RatchetEncHeader::init_bob(sk, shared_hka, shared_nhkb);
let mut alice_ratchet = RatchetEncHeader::init_alice(sk, public_key, shared_hka, shared_nhkb); let mut alice_ratchet = RatchetEncHeader::init_alice(sk, public_key, shared_hka, shared_nhkb);
let data = b"Hello World".to_vec(); let data = b"Hello World".to_vec();
let ad = b"Associated Data"; let ad = b"Associated Data";
let (header, encrypted, nonce) = alice_ratchet.ratchet_encrypt(&data, ad); let (header, encrypted, nonce) = alice_ratchet.encrypt(&data, ad);
let decrypted = bob_ratchet.ratchet_decrypt(&header, &encrypted, &nonce, ad); let decrypted = bob_ratchet.decrypt(&header, &encrypted, &nonce, ad);
assert_eq!(data, decrypted) assert_eq!(data, decrypted)
``` ```
## Export / Import Ratchet with encrypted headers ### Exporting / Importing Ratchet w/ Encrypted Headers
This ratchet implements import and export functionality. This works over a bincode backend and
maybe useful for saving Ratchets to and loading from a file. This can be used for storing and using ratchets in a file.
```rust ```rust
use double_ratchet_rs::RatchetEncHeader;
let sk = [0; 32];
let shared_hka = [1; 32];
let shared_nhkb = [2; 32];
let (bob_ratchet, public_key) = RatchetEncHeader::init_bob(sk, shared_hka, shared_nhkb); let (bob_ratchet, public_key) = RatchetEncHeader::init_bob(sk, shared_hka, shared_nhkb);
let ex_ratchet = bob_ratchet.export(); let ex_ratchet = bob_ratchet.export();
let im_ratchet = RatchetEncHeader::import(&ex_ratchet); let im_ratchet = RatchetEncHeader::import(&ex_ratchet).unwrap();
assert_eq!(im_ratchet, bob_ratchet) assert_eq!(im_ratchet, bob_ratchet)
``` ```
## Features ## **M**inimum **S**upported **R**ust **V**ersion (MSRV)
Currently the crate only supports one feature: ring. If feature is enabled the crate switches The current MSRV is 1.61.0.
to ring-compat and uses ring as backend for Sha512 Hashing. May result in slightly better performance.
## License
TODO: This project is licensed under the [MIT license](https://github.com/magnetite-dev/double-ratchet-rs/blob/main/LICENSE).
- [x] Standard Double Ratchet
- [x] [Double Ratchet with encrypted headers][3]
[1]: https://signal.org/docs/specifications/doubleratchet/ [1]: https://signal.org/docs/specifications/doubleratchet/
[2]: https://signal.org/docs/specifications/doubleratchet/#recommended-cryptographic-algorithms [2]: https://signal.org/docs/specifications/doubleratchet/#recommended-cryptographic-algorithms
[3]: https://signal.org/docs/specifications/doubleratchet/#double-ratchet-with-header-encryption [3]: https://signal.org/docs/specifications/doubleratchet/#double-ratchet-with-header-encryption
Current version: 0.4.0
License: MIT

View file

@ -1,13 +0,0 @@
[![Crate](https://img.shields.io/crates/v/double-ratchet-2)](https://crates.io/crates/double-ratchet-2)
[![License](https://img.shields.io/github/license/Dione-Software/double-ratchet-2)](https://github.com/Dione-Software/double-ratchet-2/blob/main/LICENSE)
[![Coverage Status](https://coveralls.io/repos/github/Dione-Software/double-ratchet-2/badge.svg?branch=main)](https://coveralls.io/github/Dione-Software/double-ratchet-2?branch=main)
[![Workflow Status](https://github.com/Dione-Software/double-ratchet-2/actions/workflows/rust.yml/badge.svg)](https://github.com/Dione-Software/double-ratchet-2/actions/workflows/rust.yml)
{{badges}}
# {{crate}}
{{readme}}
Current version: {{version}}
License: {{license}}

View file

@ -1,13 +1,13 @@
use double_ratchet_2::ratchet::{Ratchet, RatchetEncHeader}; use criterion::{criterion_group, criterion_main, Criterion};
use criterion::{Criterion, criterion_main, criterion_group}; use double_ratchet_rs::{Ratchet, RatchetEncHeader};
fn ratchet_enc_single() { fn ratchet_enc_single() {
let sk = [1; 32]; let sk = [1; 32];
let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk); let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk);
let mut alice_ratchet = Ratchet::init_alice(sk, public_key); let mut alice_ratchet = Ratchet::init_alice(sk, public_key);
let data = include_bytes!("../src/header.rs").to_vec(); let data = include_bytes!("../src/header.rs").to_vec();
let (header, encrypted, nonce) = alice_ratchet.ratchet_encrypt(&data, b""); let (header, encrypted, nonce) = alice_ratchet.encrypt(&data, b"");
let _decrypted = bob_ratchet.ratchet_decrypt(&header, &encrypted, &nonce, b""); let _decrypted = bob_ratchet.decrypt(&header, &encrypted, &nonce, b"");
} }
fn criterion_benchmark_1(c: &mut Criterion) { fn criterion_benchmark_1(c: &mut Criterion) {
@ -19,10 +19,10 @@ fn ratchet_enc_skip() {
let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk); let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk);
let mut alice_ratchet = Ratchet::init_alice(sk, public_key); let mut alice_ratchet = Ratchet::init_alice(sk, public_key);
let data = include_bytes!("../src/header.rs").to_vec(); let data = include_bytes!("../src/header.rs").to_vec();
let (header1, encrypted1, nonce1) = alice_ratchet.ratchet_encrypt(&data, b""); let (header1, encrypted1, nonce1) = alice_ratchet.encrypt(&data, b"");
let (header2, encrypted2, nonce2) = alice_ratchet.ratchet_encrypt(&data, b""); let (header2, encrypted2, nonce2) = alice_ratchet.encrypt(&data, b"");
let _decrypted2 = bob_ratchet.ratchet_decrypt(&header2, &encrypted2, &nonce2, b""); let _decrypted2 = bob_ratchet.decrypt(&header2, &encrypted2, &nonce2, b"");
let _decrypted1 = bob_ratchet.ratchet_decrypt(&header1, &encrypted1, &nonce1, b""); let _decrypted1 = bob_ratchet.decrypt(&header1, &encrypted1, &nonce1, b"");
} }
fn criterion_benchmark_2(c: &mut Criterion) { fn criterion_benchmark_2(c: &mut Criterion) {
@ -34,78 +34,84 @@ fn ratchet_encryt_decrypt_four() {
let data = include_bytes!("../src/dh.rs").to_vec(); let data = include_bytes!("../src/dh.rs").to_vec();
let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk); let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk);
let mut alice_ratchet = Ratchet::init_alice(sk, public_key); let mut alice_ratchet = Ratchet::init_alice(sk, public_key);
let (header1, encrypted1, nonce1) = alice_ratchet.ratchet_encrypt(&data, b""); let (header1, encrypted1, nonce1) = alice_ratchet.encrypt(&data, b"");
let _decrypted1 = bob_ratchet.ratchet_decrypt(&header1, &encrypted1, &nonce1, b""); let _decrypted1 = bob_ratchet.decrypt(&header1, &encrypted1, &nonce1, b"");
let (header2, encrypted2, nonce2) = bob_ratchet.ratchet_encrypt(&data, b""); let (header2, encrypted2, nonce2) = bob_ratchet.encrypt(&data, b"");
let _decrypted2 = alice_ratchet.ratchet_decrypt(&header2, &encrypted2, &nonce2, b""); let _decrypted2 = alice_ratchet.decrypt(&header2, &encrypted2, &nonce2, b"");
} }
fn criterion_benchmark_3(c: &mut Criterion) { fn criterion_benchmark_3(c: &mut Criterion) {
c.bench_function("Ratchet Dec Four", |b| b.iter(|| ratchet_encryt_decrypt_four())); c.bench_function("Ratchet Dec Four", |b| {
b.iter(|| ratchet_encryt_decrypt_four())
});
} }
fn ratchet_ench_enc_single() { fn ratchet_ench_enc_single() {
let sk = [1; 32]; let sk = [1; 32];
let shared_hka = [2; 32]; let shared_hka = [2; 32];
let shared_nhkb = [3; 32]; let shared_nhkb = [3; 32];
let (mut bob_ratchet, public_key) = RatchetEncHeader::init_bob(sk, let (mut bob_ratchet, public_key) = RatchetEncHeader::init_bob(sk, shared_hka, shared_nhkb);
shared_hka, let mut alice_ratchet = RatchetEncHeader::init_alice(sk, public_key, shared_hka, shared_nhkb);
shared_nhkb);
let mut alice_ratchet = RatchetEncHeader::init_alice(sk,
public_key,
shared_hka,
shared_nhkb);
let data = include_bytes!("../src/header.rs").to_vec(); let data = include_bytes!("../src/header.rs").to_vec();
let (header, encrypted, nonce) = alice_ratchet.ratchet_encrypt(&data, b""); let (header, encrypted, nonce) = alice_ratchet.encrypt(&data, b"");
let decrypted = bob_ratchet.ratchet_decrypt(&header, &encrypted, &nonce, b""); let decrypted = bob_ratchet.decrypt(&header, &encrypted, &nonce, b"");
assert_eq!(data, decrypted) assert_eq!(data, decrypted)
} }
fn criterion_benchmark_4(c: &mut Criterion) { fn criterion_benchmark_4(c: &mut Criterion) {
c.bench_function("Encrypted Header Ratchet Enc Single", |b| b.iter(|| ratchet_ench_enc_single())); c.bench_function("Encrypted Header Ratchet Enc Single", |b| {
b.iter(|| ratchet_ench_enc_single())
});
} }
fn ratchet_ench_enc_skip() { fn ratchet_ench_enc_skip() {
let sk = [1; 32]; let sk = [1; 32];
let shared_hka = [2; 32]; let shared_hka = [2; 32];
let shared_nhkb = [3; 32]; let shared_nhkb = [3; 32];
let (mut bob_ratchet, public_key) = RatchetEncHeader::init_bob(sk, let (mut bob_ratchet, public_key) = RatchetEncHeader::init_bob(sk, shared_hka, shared_nhkb);
shared_hka, let mut alice_ratchet = RatchetEncHeader::init_alice(sk, public_key, shared_hka, shared_nhkb);
shared_nhkb);
let mut alice_ratchet = RatchetEncHeader::init_alice(sk,
public_key,
shared_hka,
shared_nhkb);
let data = include_bytes!("../src/header.rs").to_vec(); let data = include_bytes!("../src/header.rs").to_vec();
let (header1, encrypted1, nonce1) = alice_ratchet.ratchet_encrypt(&data, b""); let (header1, encrypted1, nonce1) = alice_ratchet.encrypt(&data, b"");
let (header2, encrypted2, nonce2) = alice_ratchet.ratchet_encrypt(&data, b""); let (header2, encrypted2, nonce2) = alice_ratchet.encrypt(&data, b"");
let _decrypted2 = bob_ratchet.ratchet_decrypt(&header2, &encrypted2, &nonce2, b""); let _decrypted2 = bob_ratchet.decrypt(&header2, &encrypted2, &nonce2, b"");
let _decrypted1 = bob_ratchet.ratchet_decrypt(&header1, &encrypted1, &nonce1, b""); let _decrypted1 = bob_ratchet.decrypt(&header1, &encrypted1, &nonce1, b"");
} }
fn criterion_benchmark_5(c: &mut Criterion) { fn criterion_benchmark_5(c: &mut Criterion) {
c.bench_function("Encrypted Header Ratchet Enc Skip", |b| b.iter(|| ratchet_ench_enc_skip())); c.bench_function("Encrypted Header Ratchet Enc Skip", |b| {
b.iter(|| ratchet_ench_enc_skip())
});
} }
fn ratchet_ench_decrypt_four() { fn ratchet_ench_decrypt_four() {
let sk = [1; 32]; let sk = [1; 32];
let shared_hka = [2; 32]; let shared_hka = [2; 32];
let shared_nhkb = [3; 32]; let shared_nhkb = [3; 32];
let (mut bob_ratchet, public_key) = RatchetEncHeader::init_bob(sk, let (mut bob_ratchet, public_key) = RatchetEncHeader::init_bob(sk, shared_hka, shared_nhkb);
shared_hka,
shared_nhkb);
let mut alice_ratchet = RatchetEncHeader::init_alice(sk, public_key, shared_hka, shared_nhkb); let mut alice_ratchet = RatchetEncHeader::init_alice(sk, public_key, shared_hka, shared_nhkb);
let data = include_bytes!("../src/dh.rs").to_vec(); let data = include_bytes!("../src/dh.rs").to_vec();
let (header1, encrypted1, nonce1) = alice_ratchet.ratchet_encrypt(&data, b""); let (header1, encrypted1, nonce1) = alice_ratchet.encrypt(&data, b"");
let _decrypted1 = bob_ratchet.ratchet_decrypt(&header1, &encrypted1, &nonce1, b""); let _decrypted1 = bob_ratchet.decrypt(&header1, &encrypted1, &nonce1, b"");
let (header2, encrypted2, nonce2) = bob_ratchet.ratchet_encrypt(&data, b""); let (header2, encrypted2, nonce2) = bob_ratchet.encrypt(&data, b"");
let _decrypted2 = alice_ratchet.ratchet_decrypt(&header2, &encrypted2, &nonce2, b""); let _decrypted2 = alice_ratchet.decrypt(&header2, &encrypted2, &nonce2, b"");
} }
fn criterion_benchmark_6(c: &mut Criterion) { fn criterion_benchmark_6(c: &mut Criterion) {
c.bench_function("Encrypted Header Ratchet Dec Four", |b| b.iter(|| ratchet_ench_decrypt_four())); c.bench_function("Encrypted Header Ratchet Dec Four", |b| {
b.iter(|| ratchet_ench_decrypt_four())
});
} }
criterion_group!(without_enc_headerd, criterion_benchmark_1, criterion_benchmark_2, criterion_benchmark_3); criterion_group!(
criterion_group!(with_enc_headerd, criterion_benchmark_4, criterion_benchmark_5, criterion_benchmark_6); without_enc_headerd,
criterion_main!(without_enc_headerd, with_enc_headerd); criterion_benchmark_1,
criterion_benchmark_2,
criterion_benchmark_3
);
criterion_group!(
with_enc_headerd,
criterion_benchmark_4,
criterion_benchmark_5,
criterion_benchmark_6
);
criterion_main!(without_enc_headerd, with_enc_headerd);

204
deny.toml
View file

@ -1,204 +0,0 @@
# This template contains all of the possible sections and their default values
# Note that all fields that take a lint level have these possible values:
# * deny - An error will be produced and the check will fail
# * warn - A warning will be produced, but the check will not fail
# * allow - No warning or error will be produced, though in some cases a note
# will be
# The values provided in this template are the default values that will be used
# when any section or field is not specified in your own configuration
# If 1 or more target triples (and optionally, target_features) are specified,
# only the specified targets will be checked when running `cargo deny check`.
# This means, if a particular package is only ever used as a target specific
# dependency, such as, for example, the `nix` crate only being used via the
# `target_family = "unix"` configuration, that only having windows targets in
# this list would mean the nix crate, as well as any of its exclusive
# dependencies not shared by any other crates, would be ignored, as the target
# list here is effectively saying which targets you are building for.
targets = [
# The triple can be any string, but only the target triples built in to
# rustc (as of 1.40) can be checked against actual config expressions
#{ triple = "x86_64-unknown-linux-musl" },
# You can also specify which target_features you promise are enabled for a
# particular target. target_features are currently not validated against
# the actual valid features supported by the target architecture.
#{ triple = "wasm32-unknown-unknown", features = ["atomics"] },
]
# This section is considered when running `cargo deny check advisories`
# More documentation for the advisories section can be found here:
# https://embarkstudios.github.io/cargo-deny/checks/advisories/cfg.html
[advisories]
# The path where the advisory database is cloned/fetched into
db-path = "~/.cargo/advisory-db"
# The url(s) of the advisory databases to use
db-urls = ["https://github.com/rustsec/advisory-db"]
# The lint level for security vulnerabilities
vulnerability = "deny"
# The lint level for unmaintained crates
unmaintained = "warn"
# The lint level for crates that have been yanked from their source registry
yanked = "warn"
# The lint level for crates with security notices. Note that as of
# 2019-12-17 there are no security notice advisories in
# https://github.com/rustsec/advisory-db
notice = "warn"
# A list of advisory IDs to ignore. Note that ignored advisories will still
# output a note when they are encountered.
ignore = [
#"RUSTSEC-0000-0000",
]
# Threshold for security vulnerabilities, any vulnerability with a CVSS score
# lower than the range specified will be ignored. Note that ignored advisories
# will still output a note when they are encountered.
# * None - CVSS Score 0.0
# * Low - CVSS Score 0.1 - 3.9
# * Medium - CVSS Score 4.0 - 6.9
# * High - CVSS Score 7.0 - 8.9
# * Critical - CVSS Score 9.0 - 10.0
#severity-threshold =
# This section is considered when running `cargo deny check licenses`
# More documentation for the licenses section can be found here:
# https://embarkstudios.github.io/cargo-deny/checks/licenses/cfg.html
[licenses]
# The lint level for crates which do not have a detectable license
unlicensed = "deny"
# List of explictly allowed licenses
# See https://spdx.org/licenses/ for list of possible licenses
# [possible values: any SPDX 3.11 short identifier (+ optional exception)].
allow = [
"MIT",
"Apache-2.0",
"BSD-3-Clause",
#"Apache-2.0 WITH LLVM-exception",
]
# List of explictly disallowed licenses
# See https://spdx.org/licenses/ for list of possible licenses
# [possible values: any SPDX 3.11 short identifier (+ optional exception)].
deny = [
#"Nokia",
]
# Lint level for licenses considered copyleft
copyleft = "warn"
# Blanket approval or denial for OSI-approved or FSF Free/Libre licenses
# * both - The license will be approved if it is both OSI-approved *AND* FSF
# * either - The license will be approved if it is either OSI-approved *OR* FSF
# * osi-only - The license will be approved if is OSI-approved *AND NOT* FSF
# * fsf-only - The license will be approved if is FSF *AND NOT* OSI-approved
# * neither - This predicate is ignored and the default lint level is used
allow-osi-fsf-free = "neither"
# Lint level used when no other predicates are matched
# 1. License isn't in the allow or deny lists
# 2. License isn't copyleft
# 3. License isn't OSI/FSF, or allow-osi-fsf-free = "neither"
default = "deny"
# The confidence threshold for detecting a license from license text.
# The higher the value, the more closely the license text must be to the
# canonical license text of a valid SPDX license file.
# [possible values: any between 0.0 and 1.0].
confidence-threshold = 0.8
# Allow 1 or more licenses on a per-crate basis, so that particular licenses
# aren't accepted for every possible crate as with the normal allow list
exceptions = [
# Each entry is the crate and version constraint, and its specific allow
# list
#{ allow = ["Zlib"], name = "adler32", version = "*" },
]
# Some crates don't have (easily) machine readable licensing information,
# adding a clarification entry for it allows you to manually specify the
# licensing information
#[[licenses.clarify]]
# The name of the crate the clarification applies to
#name = "ring"
# The optional version constraint for the crate
#version = "*"
# The SPDX expression for the license requirements of the crate
#expression = "MIT AND ISC AND OpenSSL"
# One or more files in the crate's source used as the "source of truth" for
# the license expression. If the contents match, the clarification will be used
# when running the license check, otherwise the clarification will be ignored
# and the crate will be checked normally, which may produce warnings or errors
# depending on the rest of your configuration
#license-files = [
# Each entry is a crate relative path, and the (opaque) hash of its contents
#{ path = "LICENSE", hash = 0xbd0eed23 }
#]
[licenses.private]
# If true, ignores workspace crates that aren't published, or are only
# published to private registries
ignore = false
# One or more private registries that you might publish crates to, if a crate
# is only published to private registries, and ignore is true, the crate will
# not have its license(s) checked
registries = [
#"https://sekretz.com/registry
]
# This section is considered when running `cargo deny check bans`.
# More documentation about the 'bans' section can be found here:
# https://embarkstudios.github.io/cargo-deny/checks/bans/cfg.html
[bans]
# Lint level for when multiple versions of the same crate are detected
multiple-versions = "warn"
# Lint level for when a crate version requirement is `*`
wildcards = "allow"
# The graph highlighting used when creating dotgraphs for crates
# with multiple versions
# * lowest-version - The path to the lowest versioned duplicate is highlighted
# * simplest-path - The path to the version with the fewest edges is highlighted
# * all - Both lowest-version and simplest-path are used
highlight = "all"
# List of crates that are allowed. Use with care!
allow = [
#{ name = "ansi_term", version = "=0.11.0" },
]
# List of crates to deny
deny = [
# Each entry the name of a crate and a version range. If version is
# not specified, all versions will be matched.
#{ name = "ansi_term", version = "=0.11.0" },
#
# Wrapper crates can optionally be specified to allow the crate when it
# is a direct dependency of the otherwise banned crate
#{ name = "ansi_term", version = "=0.11.0", wrappers = [] },
]
# Certain crates/versions that will be skipped when doing duplicate detection.
skip = [
#{ name = "ansi_term", version = "=0.11.0" },
]
# Similarly to `skip` allows you to skip certain crates during duplicate
# detection. Unlike skip, it also includes the entire tree of transitive
# dependencies starting at the specified crate, up to a certain depth, which is
# by default infinite
skip-tree = [
#{ name = "ansi_term", version = "=0.11.0", depth = 20 },
]
# This section is considered when running `cargo deny check sources`.
# More documentation about the 'sources' section can be found here:
# https://embarkstudios.github.io/cargo-deny/checks/sources/cfg.html
[sources]
# Lint level for what to happen when a crate from a crate registry that is not
# in the allow list is encountered
unknown-registry = "warn"
# Lint level for what to happen when a crate from a git repository that is not
# in the allow list is encountered
unknown-git = "warn"
# List of URLs for allowed crate registries. Defaults to the crates.io index
# if not specified. If it is specified but empty, no registries are allowed.
allow-registry = ["https://github.com/rust-lang/crates.io-index"]
# List of URLs for allowed Git repositories
allow-git = []
[sources.allow-org]
# 1 or more github.com organizations to allow git sources for
# github = [""]
# 1 or more gitlab.com organizations to allow git sources for
# gitlab = [""]
# 1 or more bitbucket.org organizations to allow git sources for
# bitbucket = [""]

View file

@ -1,45 +1,57 @@
use aes_gcm_siv::{Key, Aes256GcmSiv, Nonce}; use aes_gcm_siv::aead::AeadInPlace;
use aes_gcm_siv::aead::{NewAead, AeadInPlace}; use aes_gcm_siv::{Aes256GcmSiv, KeyInit, Nonce};
use alloc::vec::Vec; use alloc::vec::Vec;
use rand_core::{OsRng, RngCore}; use rand_core::{OsRng, RngCore};
pub fn encrypt(mk: &[u8; 32], plaintext: &[u8], associated_data: &[u8]) -> (Vec<u8>, [u8; 12]) { pub fn encrypt(mk: &[u8; 32], data: &[u8], associated_data: &[u8]) -> (Vec<u8>, [u8; 12]) {
let key = Key::from_slice(mk); let cipher = Aes256GcmSiv::new_from_slice(mk).expect("Encryption failure {}");
let cipher = Aes256GcmSiv::new(key);
let mut nonce_data = [0_u8; 12]; let mut nonce_data = [0u8; 12];
OsRng::fill_bytes(&mut OsRng, &mut nonce_data); OsRng::fill_bytes(&mut OsRng, &mut nonce_data);
let nonce = Nonce::from_slice(&nonce_data); let nonce = Nonce::from_slice(&nonce_data);
let mut buffer = Vec::new();
buffer.extend_from_slice(plaintext);
cipher.encrypt_in_place(nonce, associated_data, &mut buffer) let mut buffer = Vec::new();
.expect("Encryption failed"); buffer.extend_from_slice(data);
cipher
.encrypt_in_place(nonce, associated_data, &mut buffer)
.expect("Encryption failure {}");
(buffer, nonce_data) (buffer, nonce_data)
} }
pub fn decrypt(mk: &[u8; 32], ciphertext: &[u8], associated_data: &[u8], nonce: &[u8; 12]) -> Vec<u8> { pub fn decrypt(
let key = Key::from_slice(mk); mk: &[u8; 32],
let cipher = Aes256GcmSiv::new(key); enc_data: &[u8],
associated_data: &[u8],
nonce: &[u8; 12],
) -> Vec<u8> {
let cipher = Aes256GcmSiv::new_from_slice(mk).expect("Decryption failure {}");
let nonce = Nonce::from_slice(nonce); let nonce = Nonce::from_slice(nonce);
let mut buffer = Vec::new(); let mut buffer = Vec::new();
buffer.extend_from_slice(ciphertext); buffer.extend_from_slice(enc_data);
cipher.decrypt_in_place(nonce, associated_data, &mut buffer).expect("Decryption failure {}");
cipher
.decrypt_in_place(nonce, associated_data, &mut buffer)
.expect("Decryption failure {}");
buffer buffer
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::aead::{decrypt, encrypt};
use crate::kdf_chain::gen_mk; use crate::kdf_chain::gen_mk;
use crate::aead::{encrypt, decrypt};
#[test] #[test]
fn enc_a_dec() { fn enc_a_dec() {
let test_data = include_bytes!("aead.rs").to_vec(); let test_data = include_bytes!("aead.rs");
let associated_data = include_bytes!("lib.rs").to_vec(); let associated_data = include_bytes!("lib.rs");
let mk = gen_mk(); let mk = gen_mk();
let (ciphertext, nonce) = encrypt(&mk, &test_data, &associated_data); let (enc_data, nonce) = encrypt(&mk, test_data, associated_data);
let plaintext = decrypt(&mk, &ciphertext, &associated_data, &nonce); let data = decrypt(&mk, &enc_data, associated_data, &nonce);
assert_eq!(test_data, plaintext) assert_eq!(test_data, data.as_slice())
} }
} }

View file

@ -1,43 +1,31 @@
use core::fmt::{Debug, Formatter, Result};
use rand_core::OsRng; use rand_core::OsRng;
use core::fmt::{Debug, Formatter}; use serde::{Deserialize, Serialize};
use core::fmt; use x25519_dalek::{PublicKey, SharedSecret, StaticSecret};
use p256::PublicKey as PublicKey;
use p256::ecdh::SharedSecret;
use p256::SecretKey;
use alloc::vec::Vec;
use alloc::string::ToString;
use p256::elliptic_curve::ecdh::diffie_hellman;
#[derive(Clone)] #[derive(Deserialize, Serialize, Clone)]
pub struct DhKeyPair { pub struct DhKeyPair {
pub private_key: SecretKey, pub private_key: StaticSecret,
pub public_key: PublicKey, pub public_key: PublicKey,
} }
impl DhKeyPair {
fn ex_public_key_bytes(&self) -> Vec<u8> {
self.public_key.to_string().as_bytes().to_vec()
}
}
impl PartialEq for DhKeyPair { impl PartialEq for DhKeyPair {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
if self.private_key.to_be_bytes() != other.private_key.to_be_bytes() { if self.private_key.to_bytes() != other.private_key.to_bytes() {
return false return false;
} }
if self.ex_public_key_bytes() != other.ex_public_key_bytes() { if self.public_key != other.public_key {
return false return false;
} }
true true
} }
} }
impl Debug for DhKeyPair { impl Debug for DhKeyPair {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> Result {
f.debug_struct("DhKeyPair") f.debug_struct("DhKeyPair")
.field("private_key", &self.private_key.to_be_bytes()) .field("private_key", &self.private_key.to_bytes())
.field("public_key", &self.ex_public_key_bytes()) .field("public_key", self.public_key.as_bytes())
.finish() .finish()
} }
} }
@ -50,8 +38,8 @@ impl Default for DhKeyPair {
impl DhKeyPair { impl DhKeyPair {
pub fn new() -> Self { pub fn new() -> Self {
let secret = SecretKey::random(&mut OsRng); let secret = StaticSecret::new(&mut OsRng);
let public = secret.public_key(); let public = PublicKey::from(&secret);
DhKeyPair { DhKeyPair {
private_key: secret, private_key: secret,
public_key: public, public_key: public,
@ -59,7 +47,7 @@ impl DhKeyPair {
} }
pub fn key_agreement(&self, public_key: &PublicKey) -> SharedSecret { pub fn key_agreement(&self, public_key: &PublicKey) -> SharedSecret {
diffie_hellman(self.private_key.to_nonzero_scalar(), public_key.as_affine()) self.private_key.diffie_hellman(public_key)
} }
} }
@ -78,7 +66,6 @@ pub fn gen_key_pair() -> DhKeyPair {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::dh::DhKeyPair; use crate::dh::DhKeyPair;
use alloc::string::ToString;
#[test] #[test]
fn key_generation() { fn key_generation() {
@ -96,14 +83,6 @@ mod tests {
assert_eq!(alice_shared_secret.as_bytes(), bob_shared_secret.as_bytes()) assert_eq!(alice_shared_secret.as_bytes(), bob_shared_secret.as_bytes())
} }
#[test]
fn ex_public_key() {
let key_pair = DhKeyPair::new();
let public_key_bytes = key_pair.ex_public_key_bytes();
let extracted_pk = key_pair.public_key.to_string().as_bytes().to_vec();
assert_eq!(extracted_pk, public_key_bytes)
}
#[test] #[test]
fn nq_key_pair() { fn nq_key_pair() {
let key_pair1 = DhKeyPair::new(); let key_pair1 = DhKeyPair::new();
@ -124,4 +103,4 @@ mod tests {
let key_pair = DhKeyPair::default(); let key_pair = DhKeyPair::default();
let _str = alloc::format!("{:?}", key_pair); let _str = alloc::format!("{:?}", key_pair);
} }
} }

View file

@ -1,107 +1,62 @@
use p256::PublicKey; //! Message header.
use crate::dh::DhKeyPair;
use alloc::vec::Vec;
use serde::{Serialize, Deserialize};
use crate::aead::encrypt; use crate::aead::encrypt;
use aes_gcm_siv::{Key, Nonce, Aes256GcmSiv}; use crate::dh::DhKeyPair;
use aes_gcm_siv::aead::{NewAead, AeadInPlace}; use aes_gcm_siv::aead::AeadInPlace;
use aes_gcm_siv::{Aes256GcmSiv, KeyInit, Nonce};
use alloc::vec::Vec;
use serde::{Deserialize, Serialize};
use x25519_dalek::PublicKey;
#[cfg(test)] #[cfg(test)]
use crate::dh::gen_key_pair; use crate::dh::gen_key_pair;
use alloc::string::{ToString, String};
use core::str::FromStr;
use zeroize::Zeroize; use zeroize::Zeroize;
#[derive(Debug, Clone)] #[derive(Serialize, Deserialize, Debug, Zeroize, Clone, PartialEq, Eq)]
pub struct Header {
pub public_key: PublicKey,
pub pn: usize, // Previous Chain Length
pub n: usize, // Message Number
}
#[derive(Serialize, Deserialize, Debug, Zeroize)]
#[zeroize(drop)] #[zeroize(drop)]
struct ExHeader { pub struct Header {
#[serde(with = "serde_bytes")]
ad: Vec<u8>, ad: Vec<u8>,
public_key: Vec<u8>, pub public_key: PublicKey,
pn: usize, pub pn: usize,
n: usize pub n: usize,
} }
// Message Header // A message header.
impl Header { impl Header {
// #[doc(hidden)] /// Create a new message header.
/// Requires a [DhKeyPair], previous chain length, and message number.
/// Returns a [Header].
pub fn new(dh_pair: &DhKeyPair, pn: usize, n: usize) -> Self { pub fn new(dh_pair: &DhKeyPair, pn: usize, n: usize) -> Self {
Header { Header {
ad: Vec::new(),
public_key: dh_pair.public_key, public_key: dh_pair.public_key,
pn, pn,
n, n,
} }
} }
// #[doc(hidden)]
pub fn concat(&self, ad: &[u8]) -> Vec<u8> { pub fn concat(&self, ad: &[u8]) -> Vec<u8> {
let ex_header = ExHeader { let mut header = self.clone();
ad: ad.to_vec(), header.ad = ad.to_vec();
public_key: self.public_key.to_string().as_bytes().to_vec(), postcard::to_allocvec(&header).expect("Failed to serialize Header")
pn: self.pn,
n: self.n
};
bincode::serialize(&ex_header).expect("Failed to serialize Header")
} }
pub fn encrypt(&self, hk: &[u8; 32], ad: &[u8]) -> (Vec<u8>, [u8; 12]) { pub fn encrypt(&self, hk: &[u8; 32], ad: &[u8]) -> EncryptedHeader {
let header_data = self.concat(ad); let header_data = self.concat(ad);
encrypt(hk, &header_data, b"") let enc_header = encrypt(hk, &header_data, b"");
} EncryptedHeader(enc_header.0, enc_header.1)
pub fn decrypt(hk: &Option<[u8; 32]>, ciphertext: &[u8], nonce: &[u8; 12]) -> Option<Self> {
let key_d = match hk {
None => {
return None
},
Some(d) => d
};
let key = Key::from_slice(key_d);
let cipher = Aes256GcmSiv::new(key);
let nonce = Nonce::from_slice(nonce);
let mut buffer = Vec::new();
buffer.extend_from_slice(ciphertext);
match cipher.decrypt_in_place(nonce, b"", &mut buffer) {
Ok(_) => {}
Err(_) => {
return None
}
};
Some(Header::from(buffer))
}
pub fn ex_public_key_bytes(&self) -> Vec<u8> {
self.public_key.to_string().as_bytes().to_vec()
} }
} }
impl From<Vec<u8>> for Header { impl From<Vec<u8>> for Header {
fn from(d: Vec<u8>) -> Self { fn from(d: Vec<u8>) -> Self {
let ex_header: ExHeader = bincode::deserialize(&d).unwrap(); postcard::from_bytes(&d).unwrap()
let public_key_string = String::from_utf8(ex_header.public_key.clone()).unwrap();
Header {
public_key: PublicKey::from_str(&public_key_string).unwrap(),
pn: ex_header.pn,
n: ex_header.n,
}
} }
} }
impl From<&[u8]> for Header { impl From<&[u8]> for Header {
fn from(d: &[u8]) -> Self { fn from(d: &[u8]) -> Self {
let ex_header: ExHeader = bincode::deserialize(d).unwrap(); postcard::from_bytes(d).unwrap()
let public_key_string = String::from_utf8(ex_header.public_key.clone()).unwrap();
Header {
public_key: PublicKey::from_str(&public_key_string).unwrap(),
pn: ex_header.pn,
n: ex_header.n,
}
} }
} }
@ -111,14 +66,28 @@ impl From<Header> for Vec<u8> {
} }
} }
impl PartialEq for Header { pub struct EncryptedHeader(Vec<u8>, [u8; 12]);
fn eq(&self, other: &Self) -> bool {
if self.public_key == other.public_key impl EncryptedHeader {
&& self.pn == other.pn pub fn decrypt(&self, hk: &Option<[u8; 32]>) -> Option<Header> {
&& self.n == other.n { let key_d = match hk {
return true None => return None,
} Some(d) => d,
false };
let cipher = match Aes256GcmSiv::new_from_slice(key_d) {
Ok(v) => v,
Err(_) => return None,
};
let nonce = Nonce::from_slice(&self.1);
let mut buffer = Vec::new();
buffer.extend_from_slice(&self.0);
match cipher.decrypt_in_place(nonce, b"", &mut buffer) {
Ok(_) => {}
Err(_) => return None,
};
Some(Header::from(buffer))
} }
} }
@ -130,12 +99,13 @@ pub fn gen_header() -> Header {
Header::new(&dh_pair, pn, n) Header::new(&dh_pair, pn, n)
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::header::{gen_header, Header, ExHeader}; use x25519_dalek::PublicKey;
use crate::aead::{decrypt, encrypt};
use crate::header::{gen_header, Header};
use crate::kdf_chain::gen_mk; use crate::kdf_chain::gen_mk;
use crate::aead::{encrypt, decrypt};
#[test] #[test]
fn ser_des() { fn ser_des() {
@ -172,11 +142,11 @@ mod tests {
#[test] #[test]
fn gen_ex_header() { fn gen_ex_header() {
let ex_header = ExHeader { let ex_header = Header {
ad: alloc::vec![0], ad: alloc::vec![0],
public_key: alloc::vec![1], public_key: PublicKey::from([1; 32]),
pn: 0, pn: 0,
n: 0 n: 0,
}; };
let _string = alloc::format!("{:?}", ex_header); let _string = alloc::format!("{:?}", ex_header);
} }
@ -184,8 +154,8 @@ mod tests {
#[test] #[test]
fn dec_header() { fn dec_header() {
let header = gen_header(); let header = gen_header();
let (encrypted, nonce) = header.encrypt(&[0; 32], &[0]); let encrypted = header.encrypt(&[0; 32], &[0]);
let decrypted = Header::decrypt(&Some([1_u8; 32]), &encrypted, &nonce); let decrypted = encrypted.decrypt(&Some([1u8; 32]));
assert_eq!(None, decrypted) assert_eq!(None, decrypted)
} }
} }

View file

@ -1,8 +1,6 @@
use hmac::{Hmac, Mac};
use sha2::Sha512;
use core::convert::TryInto; use core::convert::TryInto;
use hmac::{Hmac, Mac};
use sha2::Sha512;
#[cfg(test)] #[cfg(test)]
use crate::kdf_root::gen_ck; use crate::kdf_root::gen_ck;
@ -10,14 +8,14 @@ use crate::kdf_root::gen_ck;
type HmacSha512 = Hmac<Sha512>; type HmacSha512 = Hmac<Sha512>;
pub fn kdf_ck(ck: &[u8; 32]) -> ([u8; 32], [u8; 32]) { pub fn kdf_ck(ck: &[u8; 32]) -> ([u8; 32], [u8; 32]) {
let mac = HmacSha512::new_from_slice(ck) let mac = HmacSha512::new_from_slice(ck).expect("Invalid Key Length");
.expect("Invalid Key Length");
let result = mac.finalize().into_bytes(); let result = mac.finalize().into_bytes();
let (a, b) = result.split_at(32); let (a, b) = result.split_at(32);
(a.try_into()
.expect("Incorrect Length"), (
b.try_into() a.try_into().expect("Incorrect Length"),
.expect("Incorrect Length")) b.try_into().expect("Incorrect Length"),
)
} }
#[cfg(test)] #[cfg(test)]
@ -29,8 +27,8 @@ pub fn gen_mk() -> [u8; 32] {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::kdf_root::gen_ck;
use crate::kdf_chain::kdf_ck; use crate::kdf_chain::kdf_ck;
use crate::kdf_root::gen_ck;
#[test] #[test]
fn kdf_chain_ratchet() { fn kdf_chain_ratchet() {
let ck = gen_ck(); let ck = gen_ck();
@ -38,4 +36,4 @@ mod tests {
let (_, mk2) = kdf_ck(&ck); let (_, mk2) = kdf_ck(&ck);
assert_ne!(mk1, mk2) assert_ne!(mk1, mk2)
} }
} }

View file

@ -1,14 +1,10 @@
use hkdf::Hkdf;
use sha2::Sha512;
use core::convert::TryInto; use core::convert::TryInto;
use hkdf::Hkdf;
use sha2::Sha512;
#[cfg(test)] #[cfg(test)]
use crate::dh::gen_shared_secret; use crate::dh::gen_shared_secret;
use p256::ecdh::SharedSecret; use x25519_dalek::SharedSecret;
pub fn kdf_rk(rk: &[u8; 32], dh_out: &SharedSecret) -> ([u8; 32], [u8; 32]) { pub fn kdf_rk(rk: &[u8; 32], dh_out: &SharedSecret) -> ([u8; 32], [u8; 32]) {
let h = Hkdf::<Sha512>::new(Some(rk), dh_out.as_bytes()); let h = Hkdf::<Sha512>::new(Some(rk), dh_out.as_bytes());
@ -16,10 +12,10 @@ pub fn kdf_rk(rk: &[u8; 32], dh_out: &SharedSecret) -> ([u8; 32], [u8; 32]) {
let info = b"Root Key Info"; let info = b"Root Key Info";
h.expand(info, &mut okm).unwrap(); h.expand(info, &mut okm).unwrap();
let (a, b) = okm.split_at(32); let (a, b) = okm.split_at(32);
(a.try_into() (
.expect("Incorrect length"), a.try_into().expect("Incorrect length"),
b.try_into() b.try_into().expect("Incorrect length"),
.expect("Incorrect length")) )
} }
pub fn kdf_rk_he(rk: &[u8; 32], dh_out: &SharedSecret) -> ([u8; 32], [u8; 32], [u8; 32]) { pub fn kdf_rk_he(rk: &[u8; 32], dh_out: &SharedSecret) -> ([u8; 32], [u8; 32], [u8; 32]) {
@ -32,7 +28,7 @@ pub fn kdf_rk_he(rk: &[u8; 32], dh_out: &SharedSecret) -> ([u8; 32], [u8; 32], [
( (
rk.try_into().expect("Wrong length"), rk.try_into().expect("Wrong length"),
ck.try_into().expect("Wrong length"), ck.try_into().expect("Wrong length"),
nhk.try_into().expect("Wrong length") nhk.try_into().expect("Wrong length"),
) )
} }
@ -57,4 +53,4 @@ mod tests {
let (rk2, _) = kdf_rk(&rk1, &shared_secret); let (rk2, _) = kdf_rk(&rk1, &shared_secret);
assert_ne!(rk1, rk2) assert_ne!(rk1, rk2)
} }
} }

View file

@ -1,64 +1,85 @@
//! Implementation of the double ratchet system/encryption as specified by [Signal][1]. //! A pure Rust implementation of the Double Ratchet Algorithm as specified by [Signal][1].
//! //!
//! **WARNING! This implementation uses P-256 NOT Curve25519 as specified by Signal!** //! This implementation follows the cryptographic recommendations provided by [Signal][2].
//!
//! The implementation follows the cryptographic recommendations provided by [Signal][2].
//! The AEAD Algorithm uses a constant Nonce. This might be changed in the future. //! The AEAD Algorithm uses a constant Nonce. This might be changed in the future.
//! //!
//! # Example Usage: //! Fork of [double-ratchet-2](https://github.com/Dione-Software/double-ratchet-2).
//!
//! ## Examples
//!
//! ### Standard Usage
//!
//! Alice encrypts a message which is then decrypted by Bob.
//! //!
//! ## Standard:
//! ``` //! ```
//! use double_ratchet_2::ratchet::Ratchet; //! use double_ratchet_rs::Ratchet;
//! //!
//! let sk = [1; 32]; // Initial Key created by a symmetric key agreement protocol //! let sk = [1; 32]; // Shared key created by a symmetric key agreement protocol
//! let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk); // Creating Bobs Ratchet (returns Bobs PublicKey) //!
//! let mut alice_ratchet = Ratchet::init_alice(sk, public_key); // Creating Alice Ratchet with Bobs PublicKey //! let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk); // Creating Bob's Ratchet (returns Bob's PublicKey)
//! let data = b"Hello World".to_vec(); // Data to be encrypted //! let mut alice_ratchet = Ratchet::init_alice(sk, public_key); // Creating Alice's Ratchet with Bob's PublicKey
//! let ad = b"Associated Data"; // Associated Data //!
//! let data = b"Hello World".to_vec(); // Data to be encrypted
//! let ad = b"Associated Data"; // Associated data
//!
//! let (header, encrypted, nonce) = alice_ratchet.encrypt(&data, ad); // Encrypting message with Alice's Ratchet (Alice always needs to send the first message)
//! let decrypted = bob_ratchet.decrypt(&header, &encrypted, &nonce, ad); // Decrypt message with Bob's Ratchet
//! //!
//! let (header, encrypted, nonce) = alice_ratchet.ratchet_encrypt(&data, ad); // Encrypting message with Alice Ratchet (Alice always needs to send the first message)
//! let decrypted = bob_ratchet.ratchet_decrypt(&header, &encrypted, &nonce, ad); // Decrypt message with Bobs Ratchet
//! assert_eq!(data, decrypted) //! assert_eq!(data, decrypted)
//! ``` //! ```
//! //!
//! ## With lost message: //! ### Recovering a Lost Message
//!
//! Alice encrypts 2 messages for Bob.
//! The latest message must be decrypted first.
//!
//! ``` //! ```
//! # use double_ratchet_2::ratchet::Ratchet; //! use double_ratchet_rs::Ratchet;
//! //!
//! let sk = [1; 32]; // Initial Key created by a symmetric key agreement protocol //! let sk = [1; 32]; // Shared key created by a symmetric key agreement protocol
//! let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk); // Creating Bobs Ratchet (returns Bobs PublicKey)
//! let mut alice_ratchet = Ratchet::init_alice(sk, public_key); // Creating Alice Ratchet with Bobs PublicKey
//! let data = b"Hello World".to_vec(); // Data to be encrypted
//! let ad = b"Associated Data"; // Associated Data
//! //!
//! let (header1, encrypted1, nonce1) = alice_ratchet.ratchet_encrypt(&data, ad); // Lost message //! let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk); // Creating Bob's Ratchet (returns Bob's PublicKey)
//! let (header2, encrypted2, nonce2) = alice_ratchet.ratchet_encrypt(&data, ad); // Successful message //! let mut alice_ratchet = Ratchet::init_alice(sk, public_key); // Creating Alice's Ratchet with Bob's PublicKey
//! //!
//! let decrypted2 = bob_ratchet.ratchet_decrypt(&header2, &encrypted2, &nonce2, ad); // Decrypting second message first //! let data = b"Hello World".to_vec(); // Data to be encrypted
//! let decrypted1 = bob_ratchet.ratchet_decrypt(&header1, &encrypted1, &nonce1, ad); // Decrypting latter message //! let ad = b"Associated Data"; // Associated data
//! //!
//! let comp = decrypted1 == data && decrypted2 == data; //! let (header1, encrypted1, nonce1) = alice_ratchet.encrypt(&data, ad); // Lost message
//! assert!(comp); //! let (header2, encrypted2, nonce2) = alice_ratchet.encrypt(&data, ad); // Successful message
//!
//! let decrypted2 = bob_ratchet.decrypt(&header2, &encrypted2, &nonce2, ad); // Decrypting second message first
//! let decrypted1 = bob_ratchet.decrypt(&header1, &encrypted1, &nonce1, ad); // Decrypting latter message
//!
//! assert_eq!(data, decrypted1);
//! assert_eq!(data, decrypted2);
//! ``` //! ```
//! //!
//! ## Encryption before recieving inital message //! ### Encryption Before Decrypting First Message
//!
//! Bob encrypts a message before decrypting one from Alice.
//! This will result in a panic.
//! //!
//! ```should_panic //! ```should_panic
//! use double_ratchet_2::ratchet::Ratchet; //! use double_ratchet_rs::Ratchet;
//!
//! let sk = [1; 32]; //! let sk = [1; 32];
//! let ad = b"Associated Data"; //!
//! let (mut bob_ratchet, _) = Ratchet::init_bob(sk); //! let (mut bob_ratchet, _) = Ratchet::init_bob(sk);
//!
//! let data = b"Hello World".to_vec(); //! let data = b"Hello World".to_vec();
//! let ad = b"Associated Data";
//! //!
//! let (_, _, _) = bob_ratchet.ratchet_encrypt(&data, ad); //! let (_, _, _) = bob_ratchet.encrypt(&data, ad);
//! ``` //! ```
//! //!
//! ## Encryption after recieving initial message //! ### Encryption After Decrypting First Message
//! However bob can (of course) also encrypt messages. This is possible, after decrypting the first message from alice. //!
//! Bob *can* also encrypt messages.
//! This is only possible after decrypting one from Alice first though.
//! //!
//! ``` //! ```
//! use double_ratchet_2::ratchet::Ratchet; //! use double_ratchet_rs::Ratchet;
//!
//! let sk = [1; 32]; //! let sk = [1; 32];
//! //!
//! let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk); //! let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk);
@ -67,71 +88,81 @@
//! let data = b"Hello World".to_vec(); //! let data = b"Hello World".to_vec();
//! let ad = b"Associated Data"; //! let ad = b"Associated Data";
//! //!
//! let (header1, encrypted1, nonce1) = alice_ratchet.ratchet_encrypt(&data, ad); //! let (header1, encrypted1, nonce1) = alice_ratchet.encrypt(&data, ad);
//! let _decrypted1 = bob_ratchet.ratchet_decrypt(&header1, &encrypted1, &nonce1, ad); //! let _decrypted1 = bob_ratchet.decrypt(&header1, &encrypted1, &nonce1, ad);
//! //!
//! let (header2, encrypted2, nonce2) = bob_ratchet.ratchet_encrypt(&data, ad); //! let (header2, encrypted2, nonce2) = bob_ratchet.encrypt(&data, ad);
//! let decrypted2 = alice_ratchet.ratchet_decrypt(&header2, &encrypted2, &nonce2, ad); //! let decrypted2 = alice_ratchet.decrypt(&header2, &encrypted2, &nonce2, ad);
//! //!
//! assert_eq!(data, decrypted2); //! assert_eq!(data, decrypted2);
//! ``` //! ```
//! ## Constructing and Deconstructing Headers //!
//! ### Constructing and Deconstructing Headers
//! //!
//! ``` //! ```
//! # use double_ratchet_2::ratchet::Ratchet; //! use double_ratchet_rs::{Header, Ratchet};
//! # use double_ratchet_2::header::Header; //!
//! # let sk = [1; 32]; //! let sk = [1; 32];
//! # let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk); //!
//! # let mut alice_ratchet = Ratchet::init_alice(sk, public_key); //! let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk);
//! # let data = b"hello World".to_vec(); //! let mut alice_ratchet = Ratchet::init_alice(sk, public_key);
//! # let ad = b"Associated Data"; //!
//! # let (header, _, _) = alice_ratchet.ratchet_encrypt(&data, ad); //! let data = b"hello World".to_vec();
//! let ad = b"Associated Data";
//!
//! let (header, _, _) = alice_ratchet.encrypt(&data, ad);
//! let header_bytes: Vec<u8> = header.clone().into(); //! let header_bytes: Vec<u8> = header.clone().into();
//! let header_const = Header::from(header_bytes); //! let header_const = Header::from(header_bytes);
//!
//! assert_eq!(header, header_const); //! assert_eq!(header, header_const);
//! ``` //! ```
//! //!
//! # Example Ratchet with encrypted headers //! ### Encrypted Headers
//! //!
//! ``` //! ```
//! use double_ratchet_2::ratchet::RatchetEncHeader; //! use double_ratchet_rs::RatchetEncHeader;
//!
//! let sk = [0; 32]; //! let sk = [0; 32];
//! let shared_hka = [1; 32]; //! let shared_hka = [1; 32];
//! let shared_nhkb = [2; 32]; //! let shared_nhkb = [2; 32];
//! //!
//! let (mut bob_ratchet, public_key) = RatchetEncHeader::init_bob(sk, shared_hka, shared_nhkb); //! let (mut bob_ratchet, public_key) = RatchetEncHeader::init_bob(sk, shared_hka, shared_nhkb);
//! let mut alice_ratchet = RatchetEncHeader::init_alice(sk, public_key, shared_hka, shared_nhkb); //! let mut alice_ratchet = RatchetEncHeader::init_alice(sk, public_key, shared_hka, shared_nhkb);
//!
//! let data = b"Hello World".to_vec(); //! let data = b"Hello World".to_vec();
//! let ad = b"Associated Data"; //! let ad = b"Associated Data";
//! //!
//! let (header, encrypted, nonce) = alice_ratchet.ratchet_encrypt(&data, ad); //! let (header, encrypted, nonce) = alice_ratchet.encrypt(&data, ad);
//! let decrypted = bob_ratchet.ratchet_decrypt(&header, &encrypted, &nonce, ad); //! let decrypted = bob_ratchet.decrypt(&header, &encrypted, &nonce, ad);
//!
//! assert_eq!(data, decrypted) //! assert_eq!(data, decrypted)
//! ``` //! ```
//! //!
//! # Export / Import Ratchet with encrypted headers //! ### Exporting / Importing Ratchet w/ Encrypted Headers
//! This ratchet implements import and export functionality. This works over a bincode backend and //!
//! maybe useful for saving Ratchets to and loading from a file. //! This can be used for storing and using ratchets in a file.
//!
//! ``` //! ```
//! # use double_ratchet_2::ratchet::RatchetEncHeader; //! use double_ratchet_rs::RatchetEncHeader;
//! # let sk = [0; 32]; //!
//! # let shared_hka = [1; 32]; //! let sk = [0; 32];
//! # let shared_nhkb = [2; 32]; //! let shared_hka = [1; 32];
//! let shared_nhkb = [2; 32];
//!
//! let (bob_ratchet, public_key) = RatchetEncHeader::init_bob(sk, shared_hka, shared_nhkb); //! let (bob_ratchet, public_key) = RatchetEncHeader::init_bob(sk, shared_hka, shared_nhkb);
//! let ex_ratchet = bob_ratchet.export(); //! let ex_ratchet = bob_ratchet.export();
//! let im_ratchet = RatchetEncHeader::import(&ex_ratchet).unwrap(); //! let im_ratchet = RatchetEncHeader::import(&ex_ratchet).unwrap();
//!
//! assert_eq!(im_ratchet, bob_ratchet) //! assert_eq!(im_ratchet, bob_ratchet)
//! ``` //! ```
//! //!
//! # Features //! ## **M**inimum **S**upported **R**ust **V**ersion (MSRV)
//! //!
//! Currently the crate only supports one feature: ring. If feature is enabled the crate switches //! The current MSRV is 1.61.0.
//! to ring-compat and uses ring as backend for Sha512 Hashing. May result in slightly better performance.
//! //!
//! ## License
//! //!
//! TODO: //! This project is licensed under the [MIT license](https://github.com/magnetite-dev/double-ratchet-rs/blob/main/LICENSE).
//! - [x] Standard Double Ratchet
//! - [x] [Double Ratchet with encrypted headers][3]
//! //!
//! [1]: https://signal.org/docs/specifications/doubleratchet/ //! [1]: https://signal.org/docs/specifications/doubleratchet/
//! [2]: https://signal.org/docs/specifications/doubleratchet/#recommended-cryptographic-algorithms //! [2]: https://signal.org/docs/specifications/doubleratchet/#recommended-cryptographic-algorithms
@ -142,15 +173,14 @@
extern crate alloc; extern crate alloc;
pub use p256::PublicKey; pub use x25519_dalek::PublicKey;
mod aead; mod aead;
mod dh; mod dh;
mod kdf_root;
mod kdf_chain; mod kdf_chain;
mod kdf_root;
mod ratchet;
mod header;
pub mod ratchet; pub use ratchet::*;
pub use header::*;
/// Message Header
pub mod header;

View file

@ -1,24 +1,20 @@
//! Encryption with encrypted Headers //! Ratchet providing encryption and decryption.
//!
use crate::aead::{decrypt, encrypt};
use crate::dh::DhKeyPair; use crate::dh::DhKeyPair;
use p256::{PublicKey, SecretKey}; use crate::header::{Header, EncryptedHeader};
use hashbrown::HashMap;
use crate::kdf_root::{kdf_rk, kdf_rk_he};
use crate::header::Header;
use alloc::vec::Vec;
use crate::kdf_chain::kdf_ck; use crate::kdf_chain::kdf_ck;
use crate::aead::{encrypt, decrypt}; use crate::kdf_root::{kdf_rk, kdf_rk_he};
use alloc::string::{ToString, String}; use alloc::vec::Vec;
use zeroize::Zeroize; use hashbrown::HashMap;
use rand_core::OsRng;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use x25519_dalek::PublicKey;
use zeroize::Zeroize;
const MAX_SKIP: usize = 100; const MAX_SKIP: usize = 100;
type HeaderNonceCipherNonce = ((Vec<u8>, [u8; 12]), Vec<u8>, [u8; 12]); /// A standard ratchet.
#[derive(Deserialize, Serialize, PartialEq, Debug)]
/// Object Representing Ratchet
pub struct Ratchet { pub struct Ratchet {
dhs: DhKeyPair, dhs: DhKeyPair,
dhr: Option<PublicKey>, dhr: Option<PublicKey>,
@ -28,18 +24,14 @@ pub struct Ratchet {
ns: usize, ns: usize,
nr: usize, nr: usize,
pn: usize, pn: usize,
mkskipped: HashMap<(Vec<u8>, usize), [u8; 32]>, mkskipped: HashMap<([u8; 32], usize), [u8; 32]>,
} }
impl Drop for Ratchet { impl Zeroize for Ratchet {
fn drop(&mut self) { fn zeroize(&mut self) {
if let Some(mut _d) = self.dhr {
let sk = SecretKey::random(&mut OsRng);
_d = sk.public_key()
}
self.rk.zeroize(); self.rk.zeroize();
self.ckr.zeroize();
self.cks.zeroize(); self.cks.zeroize();
self.ckr.zeroize();
self.ns.zeroize(); self.ns.zeroize();
self.nr.zeroize(); self.nr.zeroize();
self.pn.zeroize(); self.pn.zeroize();
@ -47,12 +39,19 @@ impl Drop for Ratchet {
} }
} }
impl Drop for Ratchet {
fn drop(&mut self) {
self.zeroize();
}
}
impl Ratchet { impl Ratchet {
/// Init Ratchet with other [PublicKey]. Initialized second. /// Initialize a [Ratchet] with a remote [PublicKey]. Initialized second.
/// Requires a shared key and a [PublicKey].
/// Returns a [Ratchet].
pub fn init_alice(sk: [u8; 32], bob_dh_public_key: PublicKey) -> Self { pub fn init_alice(sk: [u8; 32], bob_dh_public_key: PublicKey) -> Self {
let dhs = DhKeyPair::new(); let dhs = DhKeyPair::new();
let (rk, cks) = kdf_rk(&sk, let (rk, cks) = kdf_rk(&sk, &dhs.key_agreement(&bob_dh_public_key));
&dhs.key_agreement(&bob_dh_public_key));
Ratchet { Ratchet {
dhs, dhs,
dhr: Some(bob_dh_public_key), dhr: Some(bob_dh_public_key),
@ -66,7 +65,9 @@ impl Ratchet {
} }
} }
/// Init Ratchet without other [PublicKey]. Initialized first. Returns [Ratchet] and [PublicKey]. /// Initialize a [Ratchet] without a remote [PublicKey]. Initialized first.
/// Requires a shared key.
/// Returns a [Ratchet] and a [PublicKey].
pub fn init_bob(sk: [u8; 32]) -> (Self, PublicKey) { pub fn init_bob(sk: [u8; 32]) -> (Self, PublicKey) {
let dhs = DhKeyPair::new(); let dhs = DhKeyPair::new();
let public_key = dhs.public_key; let public_key = dhs.public_key;
@ -84,22 +85,37 @@ impl Ratchet {
(ratchet, public_key) (ratchet, public_key)
} }
/// Encrypt Plaintext with [Ratchet]. Returns Message [Header] and ciphertext. /// Encrypt bytes with a [Ratchet].
pub fn ratchet_encrypt(&mut self, plaintext: &[u8], ad: &[u8]) -> (Header, Vec<u8>, [u8; 12]) { /// Requires bytes and associated bytes.
/// Returns a [Header], encrypted bytes, and a nonce.
pub fn encrypt(&mut self, data: &[u8], associated_data: &[u8]) -> (Header, Vec<u8>, [u8; 12]) {
let (cks, mk) = kdf_ck(&self.cks.unwrap()); let (cks, mk) = kdf_ck(&self.cks.unwrap());
self.cks = Some(cks); self.cks = Some(cks);
let header = Header::new(&self.dhs, self.pn, self.ns); let header = Header::new(&self.dhs, self.pn, self.ns);
self.ns += 1; self.ns += 1;
let (encrypted_data, nonce) = encrypt(&mk, plaintext, &header.concat(ad)); let (encrypted_data, nonce) = encrypt(&mk, data, &header.concat(associated_data));
(header, encrypted_data, nonce) (header, encrypted_data, nonce)
} }
fn try_skipped_message_keys(&mut self, header: &Header, ciphertext: &[u8], nonce: &[u8; 12], ad: &[u8]) -> Option<Vec<u8>> { fn try_skipped_message_keys(
if self.mkskipped.contains_key(&(header.ex_public_key_bytes(), header.n)) { &mut self,
let mk = *self.mkskipped.get(&(header.ex_public_key_bytes(), header.n)) header: &Header,
enc_data: &[u8],
nonce: &[u8; 12],
associated_data: &[u8],
) -> Option<Vec<u8>> {
if self
.mkskipped
.contains_key(&(header.public_key.to_bytes(), header.n))
{
let mk = *self
.mkskipped
.get(&(header.public_key.to_bytes(), header.n))
.unwrap(); .unwrap();
self.mkskipped.remove(&(header.ex_public_key_bytes(), header.n)).unwrap(); self.mkskipped
Some(decrypt(&mk, ciphertext, &header.concat(ad), nonce)) .remove(&(header.public_key.to_bytes(), header.n))
.unwrap();
Some(decrypt(&mk, enc_data, &header.concat(associated_data), nonce))
} else { } else {
None None
} }
@ -115,19 +131,28 @@ impl Ratchet {
let (ckr, mk) = kdf_ck(&d); let (ckr, mk) = kdf_ck(&d);
self.ckr = Some(ckr); self.ckr = Some(ckr);
d = ckr; d = ckr;
self.mkskipped.insert((self.dhr.unwrap().to_string().as_bytes().to_vec(), self.nr), mk); self.mkskipped
.insert((self.dhr.unwrap().to_bytes(), self.nr), mk);
self.nr += 1 self.nr += 1
} }
Ok(()) Ok(())
}, }
None => { Err("No Ckr set") } None => Err("No Ckr set"),
} }
} }
/// Decrypt ciphertext with ratchet. Requires Header. Returns plaintext. /// Decrypt encrypted bytes with a [Ratchet].
pub fn ratchet_decrypt(&mut self, header: &Header, ciphertext: &[u8], nonce: &[u8; 12], ad: &[u8]) -> Vec<u8> { /// Requires a [Header], encrypted bytes, a nonce, and associated bytes.
let plaintext = self.try_skipped_message_keys(header, ciphertext, nonce, ad); /// Returns decrypted bytes.
match plaintext { pub fn decrypt(
&mut self,
header: &Header,
enc_data: &[u8],
nonce: &[u8; 12],
associated_data: &[u8],
) -> Vec<u8> {
let data = self.try_skipped_message_keys(header, enc_data, nonce, associated_data);
match data {
Some(d) => d, Some(d) => d,
None => { None => {
if Some(header.public_key) != self.dhr { if Some(header.public_key) != self.dhr {
@ -140,7 +165,7 @@ impl Ratchet {
let (ckr, mk) = kdf_ck(&self.ckr.unwrap()); let (ckr, mk) = kdf_ck(&self.ckr.unwrap());
self.ckr = Some(ckr); self.ckr = Some(ckr);
self.nr += 1; self.nr += 1;
decrypt(&mk, ciphertext, &header.concat(ad), nonce) decrypt(&mk, enc_data, &header.concat(associated_data), nonce)
} }
} }
} }
@ -150,19 +175,31 @@ impl Ratchet {
self.ns = 0; self.ns = 0;
self.nr = 0; self.nr = 0;
self.dhr = Some(header.public_key); self.dhr = Some(header.public_key);
let (rk, ckr) = kdf_rk(&self.rk, let (rk, ckr) = kdf_rk(&self.rk, &self.dhs.key_agreement(&self.dhr.unwrap()));
&self.dhs.key_agreement(&self.dhr.unwrap()));
self.rk = rk; self.rk = rk;
self.ckr = Some(ckr); self.ckr = Some(ckr);
self.dhs = DhKeyPair::new(); self.dhs = DhKeyPair::new();
let (rk, cks) = kdf_rk(&self.rk, let (rk, cks) = kdf_rk(&self.rk, &self.dhs.key_agreement(&self.dhr.unwrap()));
&self.dhs.key_agreement(&self.dhr.unwrap()));
self.rk = rk; self.rk = rk;
self.cks = Some(cks); self.cks = Some(cks);
} }
/// Export a [Ratchet].
/// Returns bytes.
pub fn export(&self) -> Vec<u8> {
postcard::to_allocvec(&self).unwrap()
}
/// Import a previously exported [Ratchet].
/// Requires bytes.
/// Returns a [Ratchet], or nothing if invalid data is provided.
pub fn import(data: &[u8]) -> Option<Self> {
postcard::from_bytes(data).ok()
}
} }
#[derive(PartialEq, Debug)] /// A [Ratchet], but with header encryption.
#[derive(Deserialize, Serialize, PartialEq, Debug)]
pub struct RatchetEncHeader { pub struct RatchetEncHeader {
dhs: DhKeyPair, dhs: DhKeyPair,
dhr: Option<PublicKey>, dhr: Option<PublicKey>,
@ -176,7 +213,7 @@ pub struct RatchetEncHeader {
hkr: Option<[u8; 32]>, hkr: Option<[u8; 32]>,
nhks: Option<[u8; 32]>, nhks: Option<[u8; 32]>,
nhkr: Option<[u8; 32]>, nhkr: Option<[u8; 32]>,
mkskipped: HashMap<(Option<[u8; 32]>, usize), [u8; 32]> mkskipped: HashMap<(Option<[u8; 32]>, usize), [u8; 32]>,
} }
impl Zeroize for RatchetEncHeader { impl Zeroize for RatchetEncHeader {
@ -192,7 +229,6 @@ impl Zeroize for RatchetEncHeader {
self.nhks.zeroize(); self.nhks.zeroize();
self.nhkr.zeroize(); self.nhkr.zeroize();
self.mkskipped.clear(); self.mkskipped.clear();
} }
} }
@ -202,90 +238,16 @@ impl Drop for RatchetEncHeader {
} }
} }
#[derive(Serialize, Deserialize)]
struct ExRatchetEncHeader {
dhs: (String, String),
dhr: Option<String>,
rk: [u8; 32],
cks: Option<[u8; 32]>,
ckr: Option<[u8; 32]>,
ns: usize,
nr: usize,
pn: usize,
hks: Option<[u8; 32]>,
hkr: Option<[u8; 32]>,
nhks: Option<[u8; 32]>,
nhkr: Option<[u8; 32]>,
mkskipped: HashMap<(Option<[u8; 32]>, usize), [u8; 32]>
}
impl From<&RatchetEncHeader> for ExRatchetEncHeader {
fn from(reh: &RatchetEncHeader) -> Self {
let private_dhs = reh.dhs.private_key.to_jwk_string();
let public_dhs = reh.dhs.public_key.to_jwk_string();
let dhs = (private_dhs.to_string(), public_dhs);
let dhr = reh.dhr.map(|e| e.to_jwk_string());
let rk = reh.rk;
let cks = reh.cks;
let ckr = reh.ckr;
let ns = reh.ns;
let nr = reh.nr;
let pn = reh.pn;
let hks = reh.hks;
let hkr = reh.hkr;
let nhks = reh.nhks;
let nhkr = reh.nhkr;
let mkskipped = reh.mkskipped.clone();
Self {
dhs,
dhr,
rk,
cks,
ckr,
ns,
nr,
pn,
hks,
hkr,
nhks,
nhkr,
mkskipped
}
}
}
impl From<&ExRatchetEncHeader> for RatchetEncHeader {
fn from(ex_reh: &ExRatchetEncHeader) -> Self {
let private_dhs = SecretKey::from_jwk_str(&ex_reh.dhs.0).unwrap();
let public_dhs = PublicKey::from_jwk_str(&ex_reh.dhs.1).unwrap();
let dhs = DhKeyPair {
private_key: private_dhs,
public_key: public_dhs
};
let dhr = ex_reh.dhr.as_ref().map(|e| PublicKey::from_jwk_str(e).unwrap());
Self {
dhs,
dhr,
rk: ex_reh.rk,
cks: ex_reh.cks,
ckr: ex_reh.ckr,
ns: ex_reh.ns,
nr: ex_reh.nr,
pn: ex_reh.pn,
hks: ex_reh.hks,
hkr: ex_reh.hkr,
nhks: ex_reh.nhks,
nhkr: ex_reh.nhkr,
mkskipped: ex_reh.mkskipped.clone()
}
}
}
impl RatchetEncHeader { impl RatchetEncHeader {
pub fn init_alice(sk: [u8; 32], /// Initialize a [RatchetEncHeader] with a remote [PublicKey]. Initialized second.
bob_dh_public_key: PublicKey, /// Requires a shared key, a [PublicKey], a shared HKA, and a shared NHKB.
shared_hka: [u8; 32], /// Returns a [RatchetEncHeader].
shared_nhkb: [u8; 32]) -> Self { pub fn init_alice(
sk: [u8; 32],
bob_dh_public_key: PublicKey,
shared_hka: [u8; 32],
shared_nhkb: [u8; 32],
) -> Self {
let dhs = DhKeyPair::new(); let dhs = DhKeyPair::new();
let (rk, cks, nhks) = kdf_rk_he(&sk, &dhs.key_agreement(&bob_dh_public_key)); let (rk, cks, nhks) = kdf_rk_he(&sk, &dhs.key_agreement(&bob_dh_public_key));
RatchetEncHeader { RatchetEncHeader {
@ -305,7 +267,14 @@ impl RatchetEncHeader {
} }
} }
pub fn init_bob(sk: [u8; 32], shared_hka: [u8; 32], shared_nhkb: [u8; 32]) -> (Self, PublicKey) { /// Initialize a [RatchetEncHeader] without a remote [PublicKey]. Initialized first.
/// Requires a shared key, a shared HKA, and a shared NHKB.
/// Returns a [RatchetEncHeader] and a [PublicKey].
pub fn init_bob(
sk: [u8; 32],
shared_hka: [u8; 32],
shared_nhkb: [u8; 32],
) -> (Self, PublicKey) {
let dhs = DhKeyPair::new(); let dhs = DhKeyPair::new();
let public_key = dhs.public_key; let public_key = dhs.public_key;
let ratchet = Self { let ratchet = Self {
@ -326,50 +295,70 @@ impl RatchetEncHeader {
(ratchet, public_key) (ratchet, public_key)
} }
pub fn ratchet_encrypt(&mut self, plaintext: &[u8], ad: &[u8]) -> HeaderNonceCipherNonce { /// Encrypt bytes with a [RatchetEncHeader].
/// Requires bytes and associated bytes.
/// Returns an [EncryptedHeader], encrypted bytes, and a nonce.
pub fn encrypt(&mut self, data: &[u8], associated_data: &[u8]) -> (EncryptedHeader, Vec<u8>, [u8; 12]) {
let (cks, mk) = kdf_ck(&self.cks.unwrap()); let (cks, mk) = kdf_ck(&self.cks.unwrap());
self.cks = Some(cks); self.cks = Some(cks);
let header = Header::new(&self.dhs, self.pn, self.ns); let header = Header::new(&self.dhs, self.pn, self.ns);
let enc_header = header.encrypt(&self.hks.unwrap(), ad); let enc_header = header.encrypt(&self.hks.unwrap(), associated_data);
self.ns += 1; self.ns += 1;
let encrypted = encrypt(&mk, plaintext, &header.concat(ad)); let encrypted = encrypt(&mk, data, &header.concat(associated_data));
(enc_header, encrypted.0, encrypted.1) (enc_header, encrypted.0, encrypted.1)
} }
fn try_skipped_message_keys(&mut self, enc_header: &(Vec<u8>, [u8; 12]), fn try_skipped_message_keys(
ciphertext: &[u8], nonce: &[u8; 12], ad: &[u8]) -> (Option<Vec<u8>>, Option<Header>) { &mut self,
enc_header: &EncryptedHeader,
enc_data: &[u8],
nonce: &[u8; 12],
associated_data: &[u8],
) -> (Option<Vec<u8>>, Option<Header>) {
let ret_data = self.mkskipped.clone().into_iter().find(|e| { let ret_data = self.mkskipped.clone().into_iter().find(|e| {
let header = Header::decrypt(&e.0.0, &enc_header.0, &enc_header.1); let header = enc_header.decrypt(&e.0.0);
match header { match header {
None => false, None => false,
Some(h) => h.n == e.0.1 Some(h) => h.n == e.0 .1,
} }
}); });
match ret_data { match ret_data {
None => { (None, None) }, None => (None, None),
Some(data) => { Some(data) => {
let header = Header::decrypt(&data.0.0, &enc_header.0, &enc_header.1); let header = enc_header.decrypt(&data.0.0);
let mk = data.1; let mk = data.1;
self.mkskipped.remove(&(data.0.0, data.0.1)); self.mkskipped.remove(&(data.0 .0, data.0 .1));
(Some(decrypt(&mk, ciphertext, &header.clone().unwrap().concat(ad), nonce)), header) (
Some(decrypt(
&mk,
enc_data,
&header.clone().unwrap().concat(associated_data),
nonce,
)),
header,
)
} }
} }
} }
fn decrypt_header(&mut self, enc_header: &(Vec<u8>, [u8; 12])) -> Result<(Header, bool), &str> { /// Decrypt an [EncryptedHeader] with a [RatchetEncHeader].
let header = Header::decrypt(&self.hkr, &enc_header.0, &enc_header.1); /// Requires an [EncryptedHeader].
if let Some(h) = header { return Ok((h, false)) }; /// Returns a decrypted [Header] and boolean, if decryption was successful.
let header = Header::decrypt(&self.nhkr, &enc_header.0, &enc_header.1); fn decrypt_header(&mut self, enc_header: &EncryptedHeader) -> Result<(Header, bool), &str> {
let header = enc_header.decrypt(&self.hkr);
if let Some(h) = header {
return Ok((h, false));
};
let header = enc_header.decrypt(&self.nhkr);
match header { match header {
Some(h) => Ok((h, true)), Some(h) => Ok((h, true)),
None => Err("Header is unencryptable!") None => Err("Header is unencryptable!"),
} }
} }
fn skip_message_keys(&mut self, until: usize) -> Result<(), &str> { fn skip_message_keys(&mut self, until: usize) -> Result<(), &str> {
if self.nr + MAX_SKIP < until { if self.nr + MAX_SKIP < until {
return Err("Skipping went wrong") return Err("Skipping went wrong");
} }
if let Some(d) = &mut self.ckr { if let Some(d) = &mut self.ckr {
while self.nr < until { while self.nr < until {
@ -389,22 +378,31 @@ impl RatchetEncHeader {
self.hks = self.nhks; self.hks = self.nhks;
self.hkr = self.nhkr; self.hkr = self.nhkr;
self.dhr = Some(header.public_key); self.dhr = Some(header.public_key);
let (rk, ckr, nhkr) = kdf_rk_he(&self.rk, let (rk, ckr, nhkr) = kdf_rk_he(&self.rk, &self.dhs.key_agreement(&self.dhr.unwrap()));
&self.dhs.key_agreement(&self.dhr.unwrap()));
self.rk = rk; self.rk = rk;
self.ckr = Some(ckr); self.ckr = Some(ckr);
self.nhkr = Some(nhkr); self.nhkr = Some(nhkr);
self.dhs = DhKeyPair::new(); self.dhs = DhKeyPair::new();
let (rk, cks, nhks) = kdf_rk_he(&self.rk, let (rk, cks, nhks) = kdf_rk_he(&self.rk, &self.dhs.key_agreement(&self.dhr.unwrap()));
&self.dhs.key_agreement(&self.dhr.unwrap()));
self.rk = rk; self.rk = rk;
self.cks = Some(cks); self.cks = Some(cks);
self.nhks = Some(nhks); self.nhks = Some(nhks);
} }
pub fn ratchet_decrypt(&mut self, enc_header: &(Vec<u8>, [u8; 12]), ciphertext: &[u8], nonce: &[u8; 12], ad: &[u8]) -> Vec<u8> { /// Decrypt encrypted bytes with a [RatchetEncHeader].
let (plaintext, _) = self.try_skipped_message_keys(enc_header, ciphertext, nonce, ad); /// Requires an [EncryptedHeader], encrypted bytes, a nonce, and associated bytes.
if let Some(d) = plaintext { return d }; /// Returns decrypted bytes.
pub fn decrypt(
&mut self,
enc_header: &EncryptedHeader,
enc_data: &[u8],
nonce: &[u8; 12],
associated_data: &[u8],
) -> Vec<u8> {
let (data, _) = self.try_skipped_message_keys(enc_header, enc_data, nonce, associated_data);
if let Some(d) = data {
return d;
};
let (header, dh_ratchet) = self.decrypt_header(enc_header).unwrap(); let (header, dh_ratchet) = self.decrypt_header(enc_header).unwrap();
if dh_ratchet { if dh_ratchet {
self.skip_message_keys(header.pn).unwrap(); self.skip_message_keys(header.pn).unwrap();
@ -414,12 +412,23 @@ impl RatchetEncHeader {
let (ckr, mk) = kdf_ck(&self.ckr.unwrap()); let (ckr, mk) = kdf_ck(&self.ckr.unwrap());
self.ckr = Some(ckr); self.ckr = Some(ckr);
self.nr += 1; self.nr += 1;
decrypt(&mk, ciphertext, &header.concat(ad), nonce) decrypt(&mk, enc_data, &header.concat(associated_data), nonce)
} }
pub fn ratchet_decrypt_w_header(&mut self, enc_header: &(Vec<u8>, [u8; 12]), ciphertext: &[u8], nonce: &[u8; 12], ad: &[u8]) -> (Vec<u8>, Header) { /// Decrypt encrypted bytes and an [EncryptedHeader] with a [RatchetEncHeader].
let (plaintext, header) = self.try_skipped_message_keys(enc_header, ciphertext, nonce, ad); /// Requires an [EncryptedHeader], encrypted bytes, a nonce, and associated bytes.
if let Some(d) = plaintext { return (d, header.unwrap()) }; /// Returns decrypted bytes and a [Header].
pub fn decrypt_with_header(
&mut self,
enc_header: &EncryptedHeader,
enc_data: &[u8],
nonce: &[u8; 12],
associated_data: &[u8],
) -> (Vec<u8>, Header) {
let (data, header) = self.try_skipped_message_keys(enc_header, enc_data, nonce, associated_data);
if let Some(d) = data {
return (d, header.unwrap());
};
let (header, dh_ratchet) = self.decrypt_header(enc_header).unwrap(); let (header, dh_ratchet) = self.decrypt_header(enc_header).unwrap();
if dh_ratchet { if dh_ratchet {
self.skip_message_keys(header.pn).unwrap(); self.skip_message_keys(header.pn).unwrap();
@ -429,18 +438,22 @@ impl RatchetEncHeader {
let (ckr, mk) = kdf_ck(&self.ckr.unwrap()); let (ckr, mk) = kdf_ck(&self.ckr.unwrap());
self.ckr = Some(ckr); self.ckr = Some(ckr);
self.nr += 1; self.nr += 1;
(decrypt(&mk, ciphertext, &header.concat(ad), nonce), header) (
decrypt(&mk, enc_data, &header.concat(associated_data), nonce),
header,
)
} }
/// Export the ratchet to Binary data /// Export a [RatchetEncHeader].
/// Returns bytes.
pub fn export(&self) -> Vec<u8> { pub fn export(&self) -> Vec<u8> {
let ex: ExRatchetEncHeader = self.into(); postcard::to_allocvec(&self).unwrap()
bincode::serialize(&ex).unwrap()
} }
/// Import the ratchet from Binary data. Panics when binary data is invalid. /// Import a previously exported [RatchetEncHeader].
pub fn import(inp: &[u8]) -> Option<Self> { /// Requires bytes.
let ex: ExRatchetEncHeader = bincode::deserialize(inp).ok()?; /// Returns a [RatchetEncHeader], or nothing if invalid data is provided.
Some(RatchetEncHeader::from(&ex)) pub fn import(data: &[u8]) -> Option<Self> {
postcard::from_bytes(data).ok()
} }
} }

View file

@ -1,167 +1,145 @@
use double_ratchet_2::ratchet::{Ratchet, RatchetEncHeader}; use double_ratchet_rs::{Ratchet, RatchetEncHeader};
extern crate alloc; extern crate alloc;
#[test] #[test]
fn ratchet_init() { fn init() {
let sk = [1; 32]; let sk = [1; 32];
let (_bob_ratchet, public_key) = Ratchet::init_bob(sk); let (_bob_ratchet, public_key) = Ratchet::init_bob(sk);
let _alice_ratchet = Ratchet::init_alice(sk, public_key); let _alice_ratchet = Ratchet::init_alice(sk, public_key);
} }
#[test] #[test]
fn ratchet_enc_single() { fn enc_single() {
let sk = [1; 32]; let sk = [1; 32];
let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk); let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk);
let mut alice_ratchet = Ratchet::init_alice(sk, public_key); let mut alice_ratchet = Ratchet::init_alice(sk, public_key);
let data = include_bytes!("../src/header.rs").to_vec(); let data = include_bytes!("../src/header.rs").to_vec();
let (header, encrypted, nonce) = alice_ratchet.ratchet_encrypt(&data, b""); let (header, encrypted, nonce) = alice_ratchet.encrypt(&data, b"");
let decrypted = bob_ratchet.ratchet_decrypt(&header, &encrypted, &nonce, b""); let decrypted = bob_ratchet.decrypt(&header, &encrypted, &nonce, b"");
assert_eq!(data, decrypted) assert_eq!(data, decrypted)
} }
#[test] #[test]
fn ratchet_enc_skip() { fn enc_skip() {
let sk = [1; 32]; let sk = [1; 32];
let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk); let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk);
let mut alice_ratchet = Ratchet::init_alice(sk, public_key); let mut alice_ratchet = Ratchet::init_alice(sk, public_key);
let data = include_bytes!("../src/header.rs").to_vec(); let data = include_bytes!("../src/header.rs").to_vec();
let (header1, encrypted1, nonce1) = alice_ratchet.ratchet_encrypt(&data, b""); let (header1, encrypted1, nonce1) = alice_ratchet.encrypt(&data, b"");
let (header2, encrypted2, nonce2) = alice_ratchet.ratchet_encrypt(&data, b""); let (header2, encrypted2, nonce2) = alice_ratchet.encrypt(&data, b"");
let (header3, encrypted3, nonce3) = alice_ratchet.ratchet_encrypt(&data, b""); let (header3, encrypted3, nonce3) = alice_ratchet.encrypt(&data, b"");
let decrypted3 = bob_ratchet.ratchet_decrypt(&header3, &encrypted3, &nonce3, b""); let decrypted3 = bob_ratchet.decrypt(&header3, &encrypted3, &nonce3, b"");
let decrypted2 = bob_ratchet.ratchet_decrypt(&header2, &encrypted2, &nonce2, b""); let decrypted2 = bob_ratchet.decrypt(&header2, &encrypted2, &nonce2, b"");
let decrypted1 = bob_ratchet.ratchet_decrypt(&header1, &encrypted1, &nonce1, b""); let decrypted1 = bob_ratchet.decrypt(&header1, &encrypted1, &nonce1, b"");
let comp_res = decrypted1 == data && decrypted2 == data && decrypted3 == data; let comp_res = decrypted1 == data && decrypted2 == data && decrypted3 == data;
assert!(comp_res) assert!(comp_res)
} }
#[test] #[test]
#[should_panic] #[should_panic]
fn ratchet_panic_bob() { fn panic_bob() {
let sk = [1; 32]; let sk = [1; 32];
let (mut bob_ratchet, _) = Ratchet::init_bob(sk); let (mut bob_ratchet, _) = Ratchet::init_bob(sk);
let data = include_bytes!("../src/header.rs").to_vec(); let data = include_bytes!("../src/header.rs").to_vec();
let (_, _, _) = bob_ratchet.ratchet_encrypt(&data, b""); let (_, _, _) = bob_ratchet.encrypt(&data, b"");
} }
#[test] #[test]
fn ratchet_encryt_decrypt_four() { fn encryt_decrypt_four() {
let sk = [1; 32]; let sk = [1; 32];
let data = include_bytes!("../src/dh.rs").to_vec(); let data = include_bytes!("../src/dh.rs").to_vec();
let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk); let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk);
let mut alice_ratchet = Ratchet::init_alice(sk, public_key); let mut alice_ratchet = Ratchet::init_alice(sk, public_key);
let (header1, encrypted1, nonce1) = alice_ratchet.ratchet_encrypt(&data, b""); let (header1, encrypted1, nonce1) = alice_ratchet.encrypt(&data, b"");
let decrypted1 = bob_ratchet.ratchet_decrypt(&header1, &encrypted1, &nonce1, b""); let decrypted1 = bob_ratchet.decrypt(&header1, &encrypted1, &nonce1, b"");
let (header2, encrypted2, nonce2) = bob_ratchet.ratchet_encrypt(&data, b""); let (header2, encrypted2, nonce2) = bob_ratchet.encrypt(&data, b"");
let decrypted2 = alice_ratchet.ratchet_decrypt(&header2, &encrypted2, &nonce2, b""); let decrypted2 = alice_ratchet.decrypt(&header2, &encrypted2, &nonce2, b"");
let comp_res = decrypted1 == data && decrypted2 == data; let comp_res = decrypted1 == data && decrypted2 == data;
assert!(comp_res) assert!(comp_res)
} }
#[test] #[test]
fn ratchet_ench_init() { fn ench_init() {
let sk = [1; 32]; let sk = [1; 32];
let shared_hka = [2; 32]; let shared_hka = [2; 32];
let shared_nhkb = [3; 32]; let shared_nhkb = [3; 32];
let (_bob_ratchet, public_key) = RatchetEncHeader::init_bob(sk, let (_bob_ratchet, public_key) = RatchetEncHeader::init_bob(sk, shared_hka, shared_nhkb);
shared_hka, let _alice_ratchet = RatchetEncHeader::init_alice(sk, public_key, shared_hka, shared_nhkb);
shared_nhkb);
let _alice_ratchet = RatchetEncHeader::init_alice(sk, public_key,
shared_hka, shared_nhkb);
} }
#[test] #[test]
fn ratchet_ench_enc_single() { fn ench_enc_single() {
let sk = [1; 32]; let sk = [1; 32];
let shared_hka = [2; 32]; let shared_hka = [2; 32];
let shared_nhkb = [3; 32]; let shared_nhkb = [3; 32];
let (mut bob_ratchet, public_key) = RatchetEncHeader::init_bob(sk, let (mut bob_ratchet, public_key) = RatchetEncHeader::init_bob(sk, shared_hka, shared_nhkb);
shared_hka, let mut alice_ratchet = RatchetEncHeader::init_alice(sk, public_key, shared_hka, shared_nhkb);
shared_nhkb);
let mut alice_ratchet = RatchetEncHeader::init_alice(sk,
public_key,
shared_hka,
shared_nhkb);
let data = include_bytes!("../src/header.rs").to_vec(); let data = include_bytes!("../src/header.rs").to_vec();
let (header, encrypted, nonce) = alice_ratchet.ratchet_encrypt(&data, b""); let (header, encrypted, nonce) = alice_ratchet.encrypt(&data, b"");
let decrypted = bob_ratchet.ratchet_decrypt(&header, &encrypted, &nonce, b""); let decrypted = bob_ratchet.decrypt(&header, &encrypted, &nonce, b"");
assert_eq!(data, decrypted) assert_eq!(data, decrypted)
} }
#[test] #[test]
fn ratchet_ench_enc_skip() { fn ench_enc_skip() {
let sk = [1; 32]; let sk = [1; 32];
let shared_hka = [2; 32]; let shared_hka = [2; 32];
let shared_nhkb = [3; 32]; let shared_nhkb = [3; 32];
let (mut bob_ratchet, public_key) = RatchetEncHeader::init_bob(sk, let (mut bob_ratchet, public_key) = RatchetEncHeader::init_bob(sk, shared_hka, shared_nhkb);
shared_hka, let mut alice_ratchet = RatchetEncHeader::init_alice(sk, public_key, shared_hka, shared_nhkb);
shared_nhkb);
let mut alice_ratchet = RatchetEncHeader::init_alice(sk,
public_key,
shared_hka,
shared_nhkb);
let data = include_bytes!("../src/header.rs").to_vec(); let data = include_bytes!("../src/header.rs").to_vec();
let (header1, encrypted1, nonce1) = alice_ratchet.ratchet_encrypt(&data, b""); let (header1, encrypted1, nonce1) = alice_ratchet.encrypt(&data, b"");
let (header2, encrypted2, nonce2) = alice_ratchet.ratchet_encrypt(&data, b""); let (header2, encrypted2, nonce2) = alice_ratchet.encrypt(&data, b"");
let (header3, encrypted3, nonce3) = alice_ratchet.ratchet_encrypt(&data, b""); let (header3, encrypted3, nonce3) = alice_ratchet.encrypt(&data, b"");
let decrypted3 = bob_ratchet.ratchet_decrypt(&header3, &encrypted3, &nonce3, b""); let decrypted3 = bob_ratchet.decrypt(&header3, &encrypted3, &nonce3, b"");
let decrypted2 = bob_ratchet.ratchet_decrypt(&header2, &encrypted2, &nonce2, b""); let decrypted2 = bob_ratchet.decrypt(&header2, &encrypted2, &nonce2, b"");
let decrypted1 = bob_ratchet.ratchet_decrypt(&header1, &encrypted1, &nonce1, b""); let decrypted1 = bob_ratchet.decrypt(&header1, &encrypted1, &nonce1, b"");
let comp_res = decrypted1 == data && decrypted2 == data && decrypted3 == data; let comp_res = decrypted1 == data && decrypted2 == data && decrypted3 == data;
assert!(comp_res) assert!(comp_res)
} }
#[test] #[test]
#[should_panic] #[should_panic]
fn ratchet_ench_panic_bob() { fn ench_panic_bob() {
let sk = [1; 32]; let sk = [1; 32];
let shared_hka = [2; 32]; let shared_hka = [2; 32];
let shared_nhkb = [3; 32]; let shared_nhkb = [3; 32];
let (mut bob_ratchet, _) = RatchetEncHeader::init_bob(sk, let (mut bob_ratchet, _) = RatchetEncHeader::init_bob(sk, shared_hka, shared_nhkb);
shared_hka,
shared_nhkb);
let data = include_bytes!("../src/header.rs").to_vec(); let data = include_bytes!("../src/header.rs").to_vec();
let (_, _, _) = bob_ratchet.ratchet_encrypt(&data, b""); let (_, _, _) = bob_ratchet.encrypt(&data, b"");
} }
#[test] #[test]
fn ratchet_ench_decrypt_four() { fn ench_decrypt_four() {
let sk = [1; 32]; let sk = [1; 32];
let shared_hka = [2; 32]; let shared_hka = [2; 32];
let shared_nhkb = [3; 32]; let shared_nhkb = [3; 32];
let (mut bob_ratchet, public_key) = RatchetEncHeader::init_bob(sk, let (mut bob_ratchet, public_key) = RatchetEncHeader::init_bob(sk, shared_hka, shared_nhkb);
shared_hka,
shared_nhkb);
let mut alice_ratchet = RatchetEncHeader::init_alice(sk, public_key, shared_hka, shared_nhkb); let mut alice_ratchet = RatchetEncHeader::init_alice(sk, public_key, shared_hka, shared_nhkb);
let data = include_bytes!("../src/dh.rs").to_vec(); let data = include_bytes!("../src/dh.rs").to_vec();
let (header1, encrypted1, nonce1) = alice_ratchet.ratchet_encrypt(&data, b""); let (header1, encrypted1, nonce1) = alice_ratchet.encrypt(&data, b"");
let decrypted1 = bob_ratchet.ratchet_decrypt(&header1, &encrypted1, &nonce1, b""); let decrypted1 = bob_ratchet.decrypt(&header1, &encrypted1, &nonce1, b"");
let (header2, encrypted2, nonce2) = bob_ratchet.ratchet_encrypt(&data, b""); let (header2, encrypted2, nonce2) = bob_ratchet.encrypt(&data, b"");
let decrypted2 = alice_ratchet.ratchet_decrypt(&header2, &encrypted2, &nonce2, b""); let decrypted2 = alice_ratchet.decrypt(&header2, &encrypted2, &nonce2, b"");
let comp_res = decrypted1 == data && decrypted2 == data; let comp_res = decrypted1 == data && decrypted2 == data;
assert!(comp_res) assert!(comp_res)
} }
#[test] #[test]
#[should_panic] #[should_panic]
fn ratchet_ench_enc_skip_panic() { fn ench_enc_skip_panic() {
let sk = [1; 32]; let sk = [1; 32];
let shared_hka = [2; 32]; let shared_hka = [2; 32];
let shared_nhkb = [3; 32]; let shared_nhkb = [3; 32];
let (mut bob_ratchet, public_key) = RatchetEncHeader::init_bob(sk, let (mut bob_ratchet, public_key) = RatchetEncHeader::init_bob(sk, shared_hka, shared_nhkb);
shared_hka, let mut alice_ratchet = RatchetEncHeader::init_alice(sk, public_key, shared_hka, shared_nhkb);
shared_nhkb);
let mut alice_ratchet = RatchetEncHeader::init_alice(sk,
public_key,
shared_hka,
shared_nhkb);
let data = include_bytes!("../src/header.rs").to_vec(); let data = include_bytes!("../src/header.rs").to_vec();
let mut headers = alloc::vec![]; let mut headers = alloc::vec![];
let mut encrypteds = alloc::vec![]; let mut encrypteds = alloc::vec![];
let mut nonces = alloc::vec![]; let mut nonces = alloc::vec![];
let mut decrypteds = alloc::vec![]; let mut decrypteds = alloc::vec![];
for _ in 0..200 { for _ in 0..200 {
let (header, encrypted, nonce) = alice_ratchet.ratchet_encrypt(&data, b""); let (header, encrypted, nonce) = alice_ratchet.encrypt(&data, b"");
headers.push(header); headers.push(header);
encrypteds.push(encrypted); encrypteds.push(encrypted);
nonces.push(nonce); nonces.push(nonce);
@ -173,7 +151,7 @@ fn ratchet_ench_enc_skip_panic() {
let header = headers.get(idx).unwrap(); let header = headers.get(idx).unwrap();
let encrypted = encrypteds.get(idx).unwrap(); let encrypted = encrypteds.get(idx).unwrap();
let nonce = nonces.get(idx).unwrap(); let nonce = nonces.get(idx).unwrap();
let decrypted = bob_ratchet.ratchet_decrypt(header, encrypted, nonce, b""); let decrypted = bob_ratchet.decrypt(header, encrypted, nonce, b"");
decrypteds.push(decrypted); decrypteds.push(decrypted);
} }
} }
@ -183,9 +161,7 @@ fn import_export() {
let sk = [1; 32]; let sk = [1; 32];
let shared_hka = [2; 32]; let shared_hka = [2; 32];
let shared_nhkb = [3; 32]; let shared_nhkb = [3; 32];
let (bob_ratchet, public_key) = RatchetEncHeader::init_bob(sk, let (bob_ratchet, public_key) = RatchetEncHeader::init_bob(sk, shared_hka, shared_nhkb);
shared_hka,
shared_nhkb);
let alice_ratchet = RatchetEncHeader::init_alice(sk, public_key, shared_hka, shared_nhkb); let alice_ratchet = RatchetEncHeader::init_alice(sk, public_key, shared_hka, shared_nhkb);
let ex_bob_ratchet = bob_ratchet.export(); let ex_bob_ratchet = bob_ratchet.export();