reinstate repo with source from crates.io
This commit is contained in:
parent
93727cedf1
commit
d8c9e9b36c
15 changed files with 632 additions and 1948 deletions
1109
Cargo.lock
generated
1109
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
39
Cargo.toml
39
Cargo.toml
|
@ -1,37 +1,29 @@
|
|||
[package]
|
||||
name = "double-ratchet-2"
|
||||
authors = ["Hannes Furmans"]
|
||||
description = "Implementation of Double Ratchet as specified by Signal."
|
||||
homepage = "https://github.com/Dione-Software/double-ratchet-2"
|
||||
repository = "https://github.com/Dione-Software/double-ratchet-2"
|
||||
name = "double-ratchet-rs"
|
||||
authors = ["Magnetite Contributors", "Hannes Furmans"]
|
||||
description = "A pure Rust implementation of the Double Ratchet Algorithm as specified by Signal."
|
||||
homepage = "https://github.com/magnetite-dev/double-ratchet-rs"
|
||||
repository = "https://github.com/magnetite-dev/double-ratchet-rs"
|
||||
readme = "README.md"
|
||||
keywords = ["double-ratchet", "crypto", "cryptography", "signal"]
|
||||
version = "0.3.7"
|
||||
edition = "2018"
|
||||
version = "0.4.3"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[badges]
|
||||
maintenance = { status = "actively-developed" }
|
||||
|
||||
|
||||
[dependencies]
|
||||
p256 = {version = "0.10", features = ["ecdh", "arithmetic", "pem", "jwk"]}
|
||||
rand_core = {version = "0.6"}
|
||||
getrandom = {version = "0.2.3"}
|
||||
x25519-dalek = {version = "2.0.0-pre.1", default-features = false, features = ["serde", "u64_backend"]}
|
||||
rand_core = "0.6"
|
||||
hkdf = "0.12"
|
||||
hmac = "0.12"
|
||||
aes-gcm-siv = {version = "0.10.3"}
|
||||
sha2 = {version = "0.10"}
|
||||
aes-gcm-siv = "0.11"
|
||||
sha2 = {version = "0.10", default-features = false}
|
||||
serde = {version = "1", default-features = false, features = ["derive"]}
|
||||
serde_bytes = "0.11"
|
||||
bincode = "1"
|
||||
postcard = {version = "1", default-features = false, features = ["alloc"]}
|
||||
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]
|
||||
criterion = "0.4.0"
|
||||
criterion = "0.4"
|
||||
|
||||
[[bench]]
|
||||
name = "double_ratchet_bench"
|
||||
|
@ -39,6 +31,3 @@ harness = false
|
|||
|
||||
[profile.release]
|
||||
lto = true
|
||||
|
||||
[features]
|
||||
wasm = ["getrandom/js"]
|
||||
|
|
1
LICENSE
1
LICENSE
|
@ -1,5 +1,6 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2022 Magnetite Contributors
|
||||
Copyright (c) 2021 Hannes Furmans
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
|
|
162
README.md
162
README.md
|
@ -1,71 +1,92 @@
|
|||
[![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)
|
||||
![Maintenance](https://img.shields.io/badge/maintenance-activly--developed-brightgreen.svg)
|
||||
[![Crate](https://img.shields.io/crates/v/double-ratchet-rs)](https://crates.io/crates/double-ratchet-rs)
|
||||
[![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/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/magnetite-dev/double-ratchet-rs/actions/workflows/rust.yml/badge.svg)](https://github.com/magnetite-dev/double-ratchet-rs/actions/workflows/rust.yml)
|
||||
|
||||
# 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!**
|
||||
|
||||
The implementation follows the cryptographic recommendations provided by [Signal][2].
|
||||
This implementation follows the cryptographic recommendations provided by [Signal][2].
|
||||
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
|
||||
use double_ratchet_2::ratchet::Ratchet;
|
||||
use double_ratchet_rs::Ratchet;
|
||||
|
||||
let sk = [1; 32]; // Shared key created by a symmetric key agreement protocol
|
||||
|
||||
let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk); // Creating Bob's Ratchet (returns Bob's PublicKey)
|
||||
let mut alice_ratchet = Ratchet::init_alice(sk, public_key); // Creating Alice's Ratchet with Bob's PublicKey
|
||||
|
||||
let sk = [1; 32]; // Initial 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 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)
|
||||
```
|
||||
|
||||
### With lost message:
|
||||
```rust
|
||||
### Recovering a Lost Message
|
||||
|
||||
Alice encrypts 2 messages for Bob.
|
||||
The latest message must be decrypted first.
|
||||
|
||||
```rust
|
||||
use double_ratchet_rs::Ratchet;
|
||||
|
||||
let sk = [1; 32]; // Shared key created by a symmetric key agreement protocol
|
||||
|
||||
let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk); // Creating Bob's Ratchet (returns Bob's PublicKey)
|
||||
let mut alice_ratchet = Ratchet::init_alice(sk, public_key); // Creating Alice's Ratchet with Bob's PublicKey
|
||||
|
||||
let sk = [1; 32]; // Initial 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 ad = b"Associated Data"; // Associated data
|
||||
|
||||
let (header1, encrypted1, nonce1) = alice_ratchet.ratchet_encrypt(&data, ad); // Lost message
|
||||
let (header2, encrypted2, nonce2) = alice_ratchet.ratchet_encrypt(&data, ad); // Successful message
|
||||
let (header1, encrypted1, nonce1) = alice_ratchet.encrypt(&data, ad); // Lost message
|
||||
let (header2, encrypted2, nonce2) = alice_ratchet.encrypt(&data, ad); // Successful message
|
||||
|
||||
let decrypted2 = bob_ratchet.ratchet_decrypt(&header2, &encrypted2, &nonce2, ad); // Decrypting second message first
|
||||
let decrypted1 = bob_ratchet.ratchet_decrypt(&header1, &encrypted1, &nonce1, ad); // Decrypting latter 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
|
||||
|
||||
let comp = decrypted1 == data && decrypted2 == data;
|
||||
assert!(comp);
|
||||
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
|
||||
use double_ratchet_2::ratchet::Ratchet;
|
||||
use double_ratchet_rs::Ratchet;
|
||||
|
||||
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
|
||||
However bob can (of course) also encrypt messages. This is possible, after decrypting the first message from alice.
|
||||
### Encryption After Decrypting First Message
|
||||
|
||||
Bob *can* also encrypt messages.
|
||||
This is only possible after decrypting one from Alice first though.
|
||||
|
||||
```rust
|
||||
use double_ratchet_2::ratchet::Ratchet;
|
||||
use double_ratchet_rs::Ratchet;
|
||||
|
||||
let sk = [1; 32];
|
||||
|
||||
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 ad = b"Associated Data";
|
||||
|
||||
let (header1, encrypted1, nonce1) = alice_ratchet.ratchet_encrypt(&data, ad);
|
||||
let _decrypted1 = bob_ratchet.ratchet_decrypt(&header1, &encrypted1, &nonce1, ad);
|
||||
let (header1, encrypted1, nonce1) = alice_ratchet.encrypt(&data, ad);
|
||||
let _decrypted1 = bob_ratchet.decrypt(&header1, &encrypted1, &nonce1, ad);
|
||||
|
||||
let (header2, encrypted2, nonce2) = bob_ratchet.ratchet_encrypt(&data, ad);
|
||||
let decrypted2 = alice_ratchet.ratchet_decrypt(&header2, &encrypted2, &nonce2, ad);
|
||||
let (header2, encrypted2, nonce2) = bob_ratchet.encrypt(&data, ad);
|
||||
let decrypted2 = alice_ratchet.decrypt(&header2, &encrypted2, &nonce2, ad);
|
||||
|
||||
assert_eq!(data, decrypted2);
|
||||
```
|
||||
|
||||
### Constructing and Deconstructing Headers
|
||||
|
||||
```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_const = Header::from(header_bytes);
|
||||
|
||||
assert_eq!(header, header_const);
|
||||
```
|
||||
|
||||
## Example Ratchet with encrypted headers
|
||||
### Encrypted Headers
|
||||
|
||||
```rust
|
||||
use double_ratchet_2::ratchet::RatchetEncHeader;
|
||||
use double_ratchet_rs::RatchetEncHeader;
|
||||
|
||||
let sk = [0; 32];
|
||||
let shared_hka = [1; 32];
|
||||
let shared_nhkb = [2; 32];
|
||||
|
||||
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 data = b"Hello World".to_vec();
|
||||
let ad = b"Associated Data";
|
||||
|
||||
let (header, encrypted, nonce) = alice_ratchet.ratchet_encrypt(&data, ad);
|
||||
let decrypted = bob_ratchet.ratchet_decrypt(&header, &encrypted, &nonce, ad);
|
||||
let (header, encrypted, nonce) = alice_ratchet.encrypt(&data, ad);
|
||||
let decrypted = bob_ratchet.decrypt(&header, &encrypted, &nonce, ad);
|
||||
|
||||
assert_eq!(data, decrypted)
|
||||
```
|
||||
|
||||
## Export / Import Ratchet with 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.
|
||||
### Exporting / Importing Ratchet w/ Encrypted Headers
|
||||
|
||||
This can be used for storing and using ratchets in a file.
|
||||
|
||||
```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 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)
|
||||
```
|
||||
|
||||
## 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
|
||||
to ring-compat and uses ring as backend for Sha512 Hashing. May result in slightly better performance.
|
||||
The current MSRV is 1.61.0.
|
||||
|
||||
## License
|
||||
|
||||
TODO:
|
||||
- [x] Standard Double Ratchet
|
||||
- [x] [Double Ratchet with encrypted headers][3]
|
||||
This project is licensed under the [MIT license](https://github.com/magnetite-dev/double-ratchet-rs/blob/main/LICENSE).
|
||||
|
||||
[1]: https://signal.org/docs/specifications/doubleratchet/
|
||||
[2]: https://signal.org/docs/specifications/doubleratchet/#recommended-cryptographic-algorithms
|
||||
[3]: https://signal.org/docs/specifications/doubleratchet/#double-ratchet-with-header-encryption
|
||||
|
||||
Current version: 0.4.0
|
||||
|
||||
License: MIT
|
||||
|
|
13
README.tpl
13
README.tpl
|
@ -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}}
|
|
@ -1,13 +1,13 @@
|
|||
use double_ratchet_2::ratchet::{Ratchet, RatchetEncHeader};
|
||||
use criterion::{Criterion, criterion_main, criterion_group};
|
||||
use criterion::{criterion_group, criterion_main, Criterion};
|
||||
use double_ratchet_rs::{Ratchet, RatchetEncHeader};
|
||||
|
||||
fn ratchet_enc_single() {
|
||||
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 = include_bytes!("../src/header.rs").to_vec();
|
||||
let (header, encrypted, nonce) = alice_ratchet.ratchet_encrypt(&data, b"");
|
||||
let _decrypted = bob_ratchet.ratchet_decrypt(&header, &encrypted, &nonce, b"");
|
||||
let (header, encrypted, nonce) = alice_ratchet.encrypt(&data, b"");
|
||||
let _decrypted = bob_ratchet.decrypt(&header, &encrypted, &nonce, b"");
|
||||
}
|
||||
|
||||
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 alice_ratchet = Ratchet::init_alice(sk, public_key);
|
||||
let data = include_bytes!("../src/header.rs").to_vec();
|
||||
let (header1, encrypted1, nonce1) = alice_ratchet.ratchet_encrypt(&data, b"");
|
||||
let (header2, encrypted2, nonce2) = alice_ratchet.ratchet_encrypt(&data, b"");
|
||||
let _decrypted2 = bob_ratchet.ratchet_decrypt(&header2, &encrypted2, &nonce2, b"");
|
||||
let _decrypted1 = bob_ratchet.ratchet_decrypt(&header1, &encrypted1, &nonce1, b"");
|
||||
let (header1, encrypted1, nonce1) = alice_ratchet.encrypt(&data, b"");
|
||||
let (header2, encrypted2, nonce2) = alice_ratchet.encrypt(&data, b"");
|
||||
let _decrypted2 = bob_ratchet.decrypt(&header2, &encrypted2, &nonce2, b"");
|
||||
let _decrypted1 = bob_ratchet.decrypt(&header1, &encrypted1, &nonce1, b"");
|
||||
}
|
||||
|
||||
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 (mut bob_ratchet, public_key) = Ratchet::init_bob(sk);
|
||||
let mut alice_ratchet = Ratchet::init_alice(sk, public_key);
|
||||
let (header1, encrypted1, nonce1) = alice_ratchet.ratchet_encrypt(&data, b"");
|
||||
let _decrypted1 = bob_ratchet.ratchet_decrypt(&header1, &encrypted1, &nonce1, b"");
|
||||
let (header2, encrypted2, nonce2) = bob_ratchet.ratchet_encrypt(&data, b"");
|
||||
let _decrypted2 = alice_ratchet.ratchet_decrypt(&header2, &encrypted2, &nonce2, b"");
|
||||
let (header1, encrypted1, nonce1) = alice_ratchet.encrypt(&data, b"");
|
||||
let _decrypted1 = bob_ratchet.decrypt(&header1, &encrypted1, &nonce1, b"");
|
||||
let (header2, encrypted2, nonce2) = bob_ratchet.encrypt(&data, b"");
|
||||
let _decrypted2 = alice_ratchet.decrypt(&header2, &encrypted2, &nonce2, b"");
|
||||
}
|
||||
|
||||
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() {
|
||||
let sk = [1; 32];
|
||||
let shared_hka = [2; 32];
|
||||
let shared_nhkb = [3; 32];
|
||||
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 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 data = include_bytes!("../src/header.rs").to_vec();
|
||||
let (header, encrypted, nonce) = alice_ratchet.ratchet_encrypt(&data, b"");
|
||||
let decrypted = bob_ratchet.ratchet_decrypt(&header, &encrypted, &nonce, b"");
|
||||
let (header, encrypted, nonce) = alice_ratchet.encrypt(&data, b"");
|
||||
let decrypted = bob_ratchet.decrypt(&header, &encrypted, &nonce, b"");
|
||||
assert_eq!(data, decrypted)
|
||||
}
|
||||
|
||||
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() {
|
||||
let sk = [1; 32];
|
||||
let shared_hka = [2; 32];
|
||||
let shared_nhkb = [3; 32];
|
||||
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 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 data = include_bytes!("../src/header.rs").to_vec();
|
||||
let (header1, encrypted1, nonce1) = alice_ratchet.ratchet_encrypt(&data, b"");
|
||||
let (header2, encrypted2, nonce2) = alice_ratchet.ratchet_encrypt(&data, b"");
|
||||
let _decrypted2 = bob_ratchet.ratchet_decrypt(&header2, &encrypted2, &nonce2, b"");
|
||||
let _decrypted1 = bob_ratchet.ratchet_decrypt(&header1, &encrypted1, &nonce1, b"");
|
||||
let (header1, encrypted1, nonce1) = alice_ratchet.encrypt(&data, b"");
|
||||
let (header2, encrypted2, nonce2) = alice_ratchet.encrypt(&data, b"");
|
||||
let _decrypted2 = bob_ratchet.decrypt(&header2, &encrypted2, &nonce2, b"");
|
||||
let _decrypted1 = bob_ratchet.decrypt(&header1, &encrypted1, &nonce1, b"");
|
||||
}
|
||||
|
||||
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() {
|
||||
let sk = [1; 32];
|
||||
let shared_hka = [2; 32];
|
||||
let shared_nhkb = [3; 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 data = include_bytes!("../src/dh.rs").to_vec();
|
||||
let (header1, encrypted1, nonce1) = alice_ratchet.ratchet_encrypt(&data, b"");
|
||||
let _decrypted1 = bob_ratchet.ratchet_decrypt(&header1, &encrypted1, &nonce1, b"");
|
||||
let (header2, encrypted2, nonce2) = bob_ratchet.ratchet_encrypt(&data, b"");
|
||||
let _decrypted2 = alice_ratchet.ratchet_decrypt(&header2, &encrypted2, &nonce2, b"");
|
||||
let (header1, encrypted1, nonce1) = alice_ratchet.encrypt(&data, b"");
|
||||
let _decrypted1 = bob_ratchet.decrypt(&header1, &encrypted1, &nonce1, b"");
|
||||
let (header2, encrypted2, nonce2) = bob_ratchet.encrypt(&data, b"");
|
||||
let _decrypted2 = alice_ratchet.decrypt(&header2, &encrypted2, &nonce2, b"");
|
||||
}
|
||||
|
||||
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!(with_enc_headerd, criterion_benchmark_4, criterion_benchmark_5, criterion_benchmark_6);
|
||||
criterion_group!(
|
||||
without_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
204
deny.toml
|
@ -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 = [""]
|
54
src/aead.rs
54
src/aead.rs
|
@ -1,45 +1,57 @@
|
|||
use aes_gcm_siv::{Key, Aes256GcmSiv, Nonce};
|
||||
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 rand_core::{OsRng, RngCore};
|
||||
|
||||
pub fn encrypt(mk: &[u8; 32], plaintext: &[u8], associated_data: &[u8]) -> (Vec<u8>, [u8; 12]) {
|
||||
let key = Key::from_slice(mk);
|
||||
let cipher = Aes256GcmSiv::new(key);
|
||||
let mut nonce_data = [0_u8; 12];
|
||||
pub fn encrypt(mk: &[u8; 32], data: &[u8], associated_data: &[u8]) -> (Vec<u8>, [u8; 12]) {
|
||||
let cipher = Aes256GcmSiv::new_from_slice(mk).expect("Encryption failure {}");
|
||||
|
||||
let mut nonce_data = [0u8; 12];
|
||||
OsRng::fill_bytes(&mut OsRng, &mut 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)
|
||||
.expect("Encryption failed");
|
||||
let mut buffer = Vec::new();
|
||||
buffer.extend_from_slice(data);
|
||||
|
||||
cipher
|
||||
.encrypt_in_place(nonce, associated_data, &mut buffer)
|
||||
.expect("Encryption failure {}");
|
||||
|
||||
(buffer, nonce_data)
|
||||
}
|
||||
|
||||
pub fn decrypt(mk: &[u8; 32], ciphertext: &[u8], associated_data: &[u8], nonce: &[u8; 12]) -> Vec<u8> {
|
||||
let key = Key::from_slice(mk);
|
||||
let cipher = Aes256GcmSiv::new(key);
|
||||
pub fn decrypt(
|
||||
mk: &[u8; 32],
|
||||
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 mut buffer = Vec::new();
|
||||
buffer.extend_from_slice(ciphertext);
|
||||
cipher.decrypt_in_place(nonce, associated_data, &mut buffer).expect("Decryption failure {}");
|
||||
buffer.extend_from_slice(enc_data);
|
||||
|
||||
cipher
|
||||
.decrypt_in_place(nonce, associated_data, &mut buffer)
|
||||
.expect("Decryption failure {}");
|
||||
|
||||
buffer
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::aead::{decrypt, encrypt};
|
||||
use crate::kdf_chain::gen_mk;
|
||||
use crate::aead::{encrypt, decrypt};
|
||||
|
||||
#[test]
|
||||
fn enc_a_dec() {
|
||||
let test_data = include_bytes!("aead.rs").to_vec();
|
||||
let associated_data = include_bytes!("lib.rs").to_vec();
|
||||
let test_data = include_bytes!("aead.rs");
|
||||
let associated_data = include_bytes!("lib.rs");
|
||||
let mk = gen_mk();
|
||||
let (ciphertext, nonce) = encrypt(&mk, &test_data, &associated_data);
|
||||
let plaintext = decrypt(&mk, &ciphertext, &associated_data, &nonce);
|
||||
assert_eq!(test_data, plaintext)
|
||||
let (enc_data, nonce) = encrypt(&mk, test_data, associated_data);
|
||||
let data = decrypt(&mk, &enc_data, associated_data, &nonce);
|
||||
assert_eq!(test_data, data.as_slice())
|
||||
}
|
||||
}
|
51
src/dh.rs
51
src/dh.rs
|
@ -1,43 +1,31 @@
|
|||
use core::fmt::{Debug, Formatter, Result};
|
||||
use rand_core::OsRng;
|
||||
use core::fmt::{Debug, Formatter};
|
||||
use core::fmt;
|
||||
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;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use x25519_dalek::{PublicKey, SharedSecret, StaticSecret};
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Deserialize, Serialize, Clone)]
|
||||
pub struct DhKeyPair {
|
||||
pub private_key: SecretKey,
|
||||
pub private_key: StaticSecret,
|
||||
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 {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
if self.private_key.to_be_bytes() != other.private_key.to_be_bytes() {
|
||||
return false
|
||||
if self.private_key.to_bytes() != other.private_key.to_bytes() {
|
||||
return false;
|
||||
}
|
||||
if self.ex_public_key_bytes() != other.ex_public_key_bytes() {
|
||||
return false
|
||||
if self.public_key != other.public_key {
|
||||
return false;
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for DhKeyPair {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
|
||||
f.debug_struct("DhKeyPair")
|
||||
.field("private_key", &self.private_key.to_be_bytes())
|
||||
.field("public_key", &self.ex_public_key_bytes())
|
||||
.field("private_key", &self.private_key.to_bytes())
|
||||
.field("public_key", self.public_key.as_bytes())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
@ -50,8 +38,8 @@ impl Default for DhKeyPair {
|
|||
|
||||
impl DhKeyPair {
|
||||
pub fn new() -> Self {
|
||||
let secret = SecretKey::random(&mut OsRng);
|
||||
let public = secret.public_key();
|
||||
let secret = StaticSecret::new(&mut OsRng);
|
||||
let public = PublicKey::from(&secret);
|
||||
DhKeyPair {
|
||||
private_key: secret,
|
||||
public_key: public,
|
||||
|
@ -59,7 +47,7 @@ impl DhKeyPair {
|
|||
}
|
||||
|
||||
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)]
|
||||
mod tests {
|
||||
use crate::dh::DhKeyPair;
|
||||
use alloc::string::ToString;
|
||||
|
||||
#[test]
|
||||
fn key_generation() {
|
||||
|
@ -96,14 +83,6 @@ mod tests {
|
|||
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]
|
||||
fn nq_key_pair() {
|
||||
let key_pair1 = DhKeyPair::new();
|
||||
|
|
146
src/header.rs
146
src/header.rs
|
@ -1,107 +1,62 @@
|
|||
use p256::PublicKey;
|
||||
use crate::dh::DhKeyPair;
|
||||
use alloc::vec::Vec;
|
||||
use serde::{Serialize, Deserialize};
|
||||
//! Message header.
|
||||
|
||||
use crate::aead::encrypt;
|
||||
use aes_gcm_siv::{Key, Nonce, Aes256GcmSiv};
|
||||
use aes_gcm_siv::aead::{NewAead, AeadInPlace};
|
||||
use crate::dh::DhKeyPair;
|
||||
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)]
|
||||
use crate::dh::gen_key_pair;
|
||||
use alloc::string::{ToString, String};
|
||||
use core::str::FromStr;
|
||||
use zeroize::Zeroize;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Header {
|
||||
pub public_key: PublicKey,
|
||||
pub pn: usize, // Previous Chain Length
|
||||
pub n: usize, // Message Number
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Zeroize)]
|
||||
#[derive(Serialize, Deserialize, Debug, Zeroize, Clone, PartialEq, Eq)]
|
||||
#[zeroize(drop)]
|
||||
struct ExHeader {
|
||||
#[serde(with = "serde_bytes")]
|
||||
pub struct Header {
|
||||
ad: Vec<u8>,
|
||||
public_key: Vec<u8>,
|
||||
pn: usize,
|
||||
n: usize
|
||||
pub public_key: PublicKey,
|
||||
pub pn: usize,
|
||||
pub n: usize,
|
||||
}
|
||||
|
||||
// Message Header
|
||||
// A message 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 {
|
||||
Header {
|
||||
ad: Vec::new(),
|
||||
public_key: dh_pair.public_key,
|
||||
pn,
|
||||
n,
|
||||
}
|
||||
}
|
||||
// #[doc(hidden)]
|
||||
|
||||
pub fn concat(&self, ad: &[u8]) -> Vec<u8> {
|
||||
let ex_header = ExHeader {
|
||||
ad: ad.to_vec(),
|
||||
public_key: self.public_key.to_string().as_bytes().to_vec(),
|
||||
pn: self.pn,
|
||||
n: self.n
|
||||
};
|
||||
bincode::serialize(&ex_header).expect("Failed to serialize Header")
|
||||
let mut header = self.clone();
|
||||
header.ad = ad.to_vec();
|
||||
postcard::to_allocvec(&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);
|
||||
encrypt(hk, &header_data, b"")
|
||||
}
|
||||
|
||||
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()
|
||||
let enc_header = encrypt(hk, &header_data, b"");
|
||||
EncryptedHeader(enc_header.0, enc_header.1)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<u8>> for Header {
|
||||
fn from(d: Vec<u8>) -> Self {
|
||||
let ex_header: ExHeader = bincode::deserialize(&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,
|
||||
}
|
||||
postcard::from_bytes(&d).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&[u8]> for Header {
|
||||
fn from(d: &[u8]) -> Self {
|
||||
let ex_header: ExHeader = bincode::deserialize(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,
|
||||
}
|
||||
postcard::from_bytes(d).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -111,14 +66,28 @@ impl From<Header> for Vec<u8> {
|
|||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Header {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
if self.public_key == other.public_key
|
||||
&& self.pn == other.pn
|
||||
&& self.n == other.n {
|
||||
return true
|
||||
}
|
||||
false
|
||||
pub struct EncryptedHeader(Vec<u8>, [u8; 12]);
|
||||
|
||||
impl EncryptedHeader {
|
||||
pub fn decrypt(&self, hk: &Option<[u8; 32]>) -> Option<Header> {
|
||||
let key_d = match hk {
|
||||
None => return None,
|
||||
Some(d) => d,
|
||||
};
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
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::aead::{encrypt, decrypt};
|
||||
|
||||
#[test]
|
||||
fn ser_des() {
|
||||
|
@ -172,11 +142,11 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn gen_ex_header() {
|
||||
let ex_header = ExHeader {
|
||||
let ex_header = Header {
|
||||
ad: alloc::vec![0],
|
||||
public_key: alloc::vec![1],
|
||||
public_key: PublicKey::from([1; 32]),
|
||||
pn: 0,
|
||||
n: 0
|
||||
n: 0,
|
||||
};
|
||||
let _string = alloc::format!("{:?}", ex_header);
|
||||
}
|
||||
|
@ -184,8 +154,8 @@ mod tests {
|
|||
#[test]
|
||||
fn dec_header() {
|
||||
let header = gen_header();
|
||||
let (encrypted, nonce) = header.encrypt(&[0; 32], &[0]);
|
||||
let decrypted = Header::decrypt(&Some([1_u8; 32]), &encrypted, &nonce);
|
||||
let encrypted = header.encrypt(&[0; 32], &[0]);
|
||||
let decrypted = encrypted.decrypt(&Some([1u8; 32]));
|
||||
assert_eq!(None, decrypted)
|
||||
}
|
||||
}
|
|
@ -1,8 +1,6 @@
|
|||
use hmac::{Hmac, Mac};
|
||||
|
||||
use sha2::Sha512;
|
||||
|
||||
use core::convert::TryInto;
|
||||
use hmac::{Hmac, Mac};
|
||||
use sha2::Sha512;
|
||||
|
||||
#[cfg(test)]
|
||||
use crate::kdf_root::gen_ck;
|
||||
|
@ -10,14 +8,14 @@ use crate::kdf_root::gen_ck;
|
|||
type HmacSha512 = Hmac<Sha512>;
|
||||
|
||||
pub fn kdf_ck(ck: &[u8; 32]) -> ([u8; 32], [u8; 32]) {
|
||||
let mac = HmacSha512::new_from_slice(ck)
|
||||
.expect("Invalid Key Length");
|
||||
let mac = HmacSha512::new_from_slice(ck).expect("Invalid Key Length");
|
||||
let result = mac.finalize().into_bytes();
|
||||
let (a, b) = result.split_at(32);
|
||||
(a.try_into()
|
||||
.expect("Incorrect Length"),
|
||||
b.try_into()
|
||||
.expect("Incorrect Length"))
|
||||
|
||||
(
|
||||
a.try_into().expect("Incorrect Length"),
|
||||
b.try_into().expect("Incorrect Length"),
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -29,8 +27,8 @@ pub fn gen_mk() -> [u8; 32] {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::kdf_root::gen_ck;
|
||||
use crate::kdf_chain::kdf_ck;
|
||||
use crate::kdf_root::gen_ck;
|
||||
#[test]
|
||||
fn kdf_chain_ratchet() {
|
||||
let ck = gen_ck();
|
||||
|
|
|
@ -1,14 +1,10 @@
|
|||
|
||||
use hkdf::Hkdf;
|
||||
|
||||
|
||||
use sha2::Sha512;
|
||||
|
||||
use core::convert::TryInto;
|
||||
use hkdf::Hkdf;
|
||||
use sha2::Sha512;
|
||||
|
||||
#[cfg(test)]
|
||||
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]) {
|
||||
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";
|
||||
h.expand(info, &mut okm).unwrap();
|
||||
let (a, b) = okm.split_at(32);
|
||||
(a.try_into()
|
||||
.expect("Incorrect length"),
|
||||
b.try_into()
|
||||
.expect("Incorrect length"))
|
||||
(
|
||||
a.try_into().expect("Incorrect length"),
|
||||
b.try_into().expect("Incorrect length"),
|
||||
)
|
||||
}
|
||||
|
||||
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"),
|
||||
ck.try_into().expect("Wrong length"),
|
||||
nhk.try_into().expect("Wrong length")
|
||||
nhk.try_into().expect("Wrong length"),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
170
src/lib.rs
170
src/lib.rs
|
@ -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!**
|
||||
//!
|
||||
//! The implementation follows the cryptographic recommendations provided by [Signal][2].
|
||||
//! This implementation follows the cryptographic recommendations provided by [Signal][2].
|
||||
//! 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]; // Shared key created by a symmetric key agreement protocol
|
||||
//!
|
||||
//! let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk); // Creating Bob's Ratchet (returns Bob's PublicKey)
|
||||
//! let mut alice_ratchet = Ratchet::init_alice(sk, public_key); // Creating Alice's Ratchet with Bob's PublicKey
|
||||
//!
|
||||
//! let sk = [1; 32]; // Initial 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 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)
|
||||
//! ```
|
||||
//!
|
||||
//! ## With lost message:
|
||||
//! ```
|
||||
//! # use double_ratchet_2::ratchet::Ratchet;
|
||||
//! ### Recovering a Lost Message
|
||||
//!
|
||||
//! Alice encrypts 2 messages for Bob.
|
||||
//! The latest message must be decrypted first.
|
||||
//!
|
||||
//! ```
|
||||
//! use double_ratchet_rs::Ratchet;
|
||||
//!
|
||||
//! let sk = [1; 32]; // Shared key created by a symmetric key agreement protocol
|
||||
//!
|
||||
//! let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk); // Creating Bob's Ratchet (returns Bob's PublicKey)
|
||||
//! let mut alice_ratchet = Ratchet::init_alice(sk, public_key); // Creating Alice's Ratchet with Bob's PublicKey
|
||||
//!
|
||||
//! let sk = [1; 32]; // Initial 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 ad = b"Associated Data"; // Associated data
|
||||
//!
|
||||
//! let (header1, encrypted1, nonce1) = alice_ratchet.ratchet_encrypt(&data, ad); // Lost message
|
||||
//! let (header2, encrypted2, nonce2) = alice_ratchet.ratchet_encrypt(&data, ad); // Successful message
|
||||
//! let (header1, encrypted1, nonce1) = alice_ratchet.encrypt(&data, ad); // Lost message
|
||||
//! let (header2, encrypted2, nonce2) = alice_ratchet.encrypt(&data, ad); // Successful message
|
||||
//!
|
||||
//! let decrypted2 = bob_ratchet.ratchet_decrypt(&header2, &encrypted2, &nonce2, ad); // Decrypting second message first
|
||||
//! let decrypted1 = bob_ratchet.ratchet_decrypt(&header1, &encrypted1, &nonce1, ad); // Decrypting latter 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
|
||||
//!
|
||||
//! let comp = decrypted1 == data && decrypted2 == data;
|
||||
//! assert!(comp);
|
||||
//! 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
|
||||
//! use double_ratchet_2::ratchet::Ratchet;
|
||||
//! use double_ratchet_rs::Ratchet;
|
||||
//!
|
||||
//! 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 ad = b"Associated Data";
|
||||
//!
|
||||
//! let (_, _, _) = bob_ratchet.ratchet_encrypt(&data, ad);
|
||||
//! let (_, _, _) = bob_ratchet.encrypt(&data, ad);
|
||||
//! ```
|
||||
//!
|
||||
//! ## Encryption after recieving initial message
|
||||
//! However bob can (of course) also encrypt messages. This is possible, after decrypting the first message from alice.
|
||||
//! ### Encryption After Decrypting First Message
|
||||
//!
|
||||
//! 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 (mut bob_ratchet, public_key) = Ratchet::init_bob(sk);
|
||||
|
@ -67,71 +88,81 @@
|
|||
//! let data = b"Hello World".to_vec();
|
||||
//! let ad = b"Associated Data";
|
||||
//!
|
||||
//! let (header1, encrypted1, nonce1) = alice_ratchet.ratchet_encrypt(&data, ad);
|
||||
//! let _decrypted1 = bob_ratchet.ratchet_decrypt(&header1, &encrypted1, &nonce1, ad);
|
||||
//! let (header1, encrypted1, nonce1) = alice_ratchet.encrypt(&data, ad);
|
||||
//! let _decrypted1 = bob_ratchet.decrypt(&header1, &encrypted1, &nonce1, ad);
|
||||
//!
|
||||
//! let (header2, encrypted2, nonce2) = bob_ratchet.ratchet_encrypt(&data, ad);
|
||||
//! let decrypted2 = alice_ratchet.ratchet_decrypt(&header2, &encrypted2, &nonce2, ad);
|
||||
//! let (header2, encrypted2, nonce2) = bob_ratchet.encrypt(&data, ad);
|
||||
//! let decrypted2 = alice_ratchet.decrypt(&header2, &encrypted2, &nonce2, ad);
|
||||
//!
|
||||
//! assert_eq!(data, decrypted2);
|
||||
//! ```
|
||||
//! ## Constructing and Deconstructing Headers
|
||||
//!
|
||||
//! ### Constructing and Deconstructing Headers
|
||||
//!
|
||||
//! ```
|
||||
//! # use double_ratchet_2::ratchet::Ratchet;
|
||||
//! # use double_ratchet_2::header::Header;
|
||||
//! # 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.ratchet_encrypt(&data, ad);
|
||||
//! 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_const = Header::from(header_bytes);
|
||||
//!
|
||||
//! 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 shared_hka = [1; 32];
|
||||
//! let shared_nhkb = [2; 32];
|
||||
//!
|
||||
//! 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 data = b"Hello World".to_vec();
|
||||
//! let ad = b"Associated Data";
|
||||
//!
|
||||
//! let (header, encrypted, nonce) = alice_ratchet.ratchet_encrypt(&data, ad);
|
||||
//! let decrypted = bob_ratchet.ratchet_decrypt(&header, &encrypted, &nonce, ad);
|
||||
//! let (header, encrypted, nonce) = alice_ratchet.encrypt(&data, ad);
|
||||
//! let decrypted = bob_ratchet.decrypt(&header, &encrypted, &nonce, ad);
|
||||
//!
|
||||
//! assert_eq!(data, decrypted)
|
||||
//! ```
|
||||
//!
|
||||
//! # Export / Import Ratchet with 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.
|
||||
//! ### Exporting / Importing Ratchet w/ Encrypted Headers
|
||||
//!
|
||||
//! This can be used for storing and using ratchets in a file.
|
||||
//!
|
||||
//! ```
|
||||
//! # use double_ratchet_2::ratchet::RatchetEncHeader;
|
||||
//! # let sk = [0; 32];
|
||||
//! # let shared_hka = [1; 32];
|
||||
//! # let shared_nhkb = [2; 32];
|
||||
//! 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 ex_ratchet = bob_ratchet.export();
|
||||
//! let im_ratchet = RatchetEncHeader::import(&ex_ratchet).unwrap();
|
||||
//!
|
||||
//! 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
|
||||
//! to ring-compat and uses ring as backend for Sha512 Hashing. May result in slightly better performance.
|
||||
//! The current MSRV is 1.61.0.
|
||||
//!
|
||||
//! ## License
|
||||
//!
|
||||
//! TODO:
|
||||
//! - [x] Standard Double Ratchet
|
||||
//! - [x] [Double Ratchet with encrypted headers][3]
|
||||
//! This project is licensed under the [MIT license](https://github.com/magnetite-dev/double-ratchet-rs/blob/main/LICENSE).
|
||||
//!
|
||||
//! [1]: https://signal.org/docs/specifications/doubleratchet/
|
||||
//! [2]: https://signal.org/docs/specifications/doubleratchet/#recommended-cryptographic-algorithms
|
||||
|
@ -142,15 +173,14 @@
|
|||
|
||||
extern crate alloc;
|
||||
|
||||
pub use p256::PublicKey;
|
||||
pub use x25519_dalek::PublicKey;
|
||||
|
||||
mod aead;
|
||||
mod dh;
|
||||
mod kdf_root;
|
||||
mod kdf_chain;
|
||||
mod kdf_root;
|
||||
mod ratchet;
|
||||
mod header;
|
||||
|
||||
pub mod ratchet;
|
||||
|
||||
/// Message Header
|
||||
pub mod header;
|
||||
|
||||
pub use ratchet::*;
|
||||
pub use header::*;
|
||||
|
|
347
src/ratchet.rs
347
src/ratchet.rs
|
@ -1,24 +1,20 @@
|
|||
//! Encryption with encrypted Headers
|
||||
//!
|
||||
//! Ratchet providing encryption and decryption.
|
||||
|
||||
use crate::aead::{decrypt, encrypt};
|
||||
use crate::dh::DhKeyPair;
|
||||
use p256::{PublicKey, SecretKey};
|
||||
use hashbrown::HashMap;
|
||||
use crate::kdf_root::{kdf_rk, kdf_rk_he};
|
||||
use crate::header::Header;
|
||||
use alloc::vec::Vec;
|
||||
use crate::header::{Header, EncryptedHeader};
|
||||
use crate::kdf_chain::kdf_ck;
|
||||
use crate::aead::{encrypt, decrypt};
|
||||
use alloc::string::{ToString, String};
|
||||
use zeroize::Zeroize;
|
||||
use rand_core::OsRng;
|
||||
use crate::kdf_root::{kdf_rk, kdf_rk_he};
|
||||
use alloc::vec::Vec;
|
||||
use hashbrown::HashMap;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use x25519_dalek::PublicKey;
|
||||
use zeroize::Zeroize;
|
||||
|
||||
const MAX_SKIP: usize = 100;
|
||||
|
||||
type HeaderNonceCipherNonce = ((Vec<u8>, [u8; 12]), Vec<u8>, [u8; 12]);
|
||||
|
||||
/// Object Representing Ratchet
|
||||
/// A standard ratchet.
|
||||
#[derive(Deserialize, Serialize, PartialEq, Debug)]
|
||||
pub struct Ratchet {
|
||||
dhs: DhKeyPair,
|
||||
dhr: Option<PublicKey>,
|
||||
|
@ -28,18 +24,14 @@ pub struct Ratchet {
|
|||
ns: usize,
|
||||
nr: usize,
|
||||
pn: usize,
|
||||
mkskipped: HashMap<(Vec<u8>, usize), [u8; 32]>,
|
||||
mkskipped: HashMap<([u8; 32], usize), [u8; 32]>,
|
||||
}
|
||||
|
||||
impl Drop for Ratchet {
|
||||
fn drop(&mut self) {
|
||||
if let Some(mut _d) = self.dhr {
|
||||
let sk = SecretKey::random(&mut OsRng);
|
||||
_d = sk.public_key()
|
||||
}
|
||||
impl Zeroize for Ratchet {
|
||||
fn zeroize(&mut self) {
|
||||
self.rk.zeroize();
|
||||
self.ckr.zeroize();
|
||||
self.cks.zeroize();
|
||||
self.ckr.zeroize();
|
||||
self.ns.zeroize();
|
||||
self.nr.zeroize();
|
||||
self.pn.zeroize();
|
||||
|
@ -47,12 +39,19 @@ impl Drop for Ratchet {
|
|||
}
|
||||
}
|
||||
|
||||
impl Drop for Ratchet {
|
||||
fn drop(&mut self) {
|
||||
self.zeroize();
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
let dhs = DhKeyPair::new();
|
||||
let (rk, cks) = kdf_rk(&sk,
|
||||
&dhs.key_agreement(&bob_dh_public_key));
|
||||
let (rk, cks) = kdf_rk(&sk, &dhs.key_agreement(&bob_dh_public_key));
|
||||
Ratchet {
|
||||
dhs,
|
||||
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) {
|
||||
let dhs = DhKeyPair::new();
|
||||
let public_key = dhs.public_key;
|
||||
|
@ -84,22 +85,37 @@ impl Ratchet {
|
|||
(ratchet, public_key)
|
||||
}
|
||||
|
||||
/// Encrypt Plaintext with [Ratchet]. Returns Message [Header] and ciphertext.
|
||||
pub fn ratchet_encrypt(&mut self, plaintext: &[u8], ad: &[u8]) -> (Header, Vec<u8>, [u8; 12]) {
|
||||
/// Encrypt bytes with a [Ratchet].
|
||||
/// 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());
|
||||
self.cks = Some(cks);
|
||||
let header = Header::new(&self.dhs, self.pn, self.ns);
|
||||
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)
|
||||
}
|
||||
|
||||
fn try_skipped_message_keys(&mut self, header: &Header, ciphertext: &[u8], nonce: &[u8; 12], ad: &[u8]) -> Option<Vec<u8>> {
|
||||
if self.mkskipped.contains_key(&(header.ex_public_key_bytes(), header.n)) {
|
||||
let mk = *self.mkskipped.get(&(header.ex_public_key_bytes(), header.n))
|
||||
fn try_skipped_message_keys(
|
||||
&mut self,
|
||||
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();
|
||||
self.mkskipped.remove(&(header.ex_public_key_bytes(), header.n)).unwrap();
|
||||
Some(decrypt(&mk, ciphertext, &header.concat(ad), nonce))
|
||||
self.mkskipped
|
||||
.remove(&(header.public_key.to_bytes(), header.n))
|
||||
.unwrap();
|
||||
Some(decrypt(&mk, enc_data, &header.concat(associated_data), nonce))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -115,19 +131,28 @@ impl Ratchet {
|
|||
let (ckr, mk) = kdf_ck(&d);
|
||||
self.ckr = Some(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
|
||||
}
|
||||
Ok(())
|
||||
},
|
||||
None => { Err("No Ckr set") }
|
||||
}
|
||||
None => Err("No Ckr set"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Decrypt ciphertext with ratchet. Requires Header. Returns plaintext.
|
||||
pub fn ratchet_decrypt(&mut self, header: &Header, ciphertext: &[u8], nonce: &[u8; 12], ad: &[u8]) -> Vec<u8> {
|
||||
let plaintext = self.try_skipped_message_keys(header, ciphertext, nonce, ad);
|
||||
match plaintext {
|
||||
/// Decrypt encrypted bytes with a [Ratchet].
|
||||
/// Requires a [Header], encrypted bytes, a nonce, and associated bytes.
|
||||
/// Returns decrypted bytes.
|
||||
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,
|
||||
None => {
|
||||
if Some(header.public_key) != self.dhr {
|
||||
|
@ -140,7 +165,7 @@ impl Ratchet {
|
|||
let (ckr, mk) = kdf_ck(&self.ckr.unwrap());
|
||||
self.ckr = Some(ckr);
|
||||
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.nr = 0;
|
||||
self.dhr = Some(header.public_key);
|
||||
let (rk, ckr) = kdf_rk(&self.rk,
|
||||
&self.dhs.key_agreement(&self.dhr.unwrap()));
|
||||
let (rk, ckr) = kdf_rk(&self.rk, &self.dhs.key_agreement(&self.dhr.unwrap()));
|
||||
self.rk = rk;
|
||||
self.ckr = Some(ckr);
|
||||
self.dhs = DhKeyPair::new();
|
||||
let (rk, cks) = kdf_rk(&self.rk,
|
||||
&self.dhs.key_agreement(&self.dhr.unwrap()));
|
||||
let (rk, cks) = kdf_rk(&self.rk, &self.dhs.key_agreement(&self.dhr.unwrap()));
|
||||
self.rk = rk;
|
||||
self.cks = Some(cks);
|
||||
}
|
||||
|
||||
/// Export a [Ratchet].
|
||||
/// Returns bytes.
|
||||
pub fn export(&self) -> Vec<u8> {
|
||||
postcard::to_allocvec(&self).unwrap()
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
/// 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()
|
||||
}
|
||||
}
|
||||
|
||||
/// A [Ratchet], but with header encryption.
|
||||
#[derive(Deserialize, Serialize, PartialEq, Debug)]
|
||||
pub struct RatchetEncHeader {
|
||||
dhs: DhKeyPair,
|
||||
dhr: Option<PublicKey>,
|
||||
|
@ -176,7 +213,7 @@ pub struct RatchetEncHeader {
|
|||
hkr: Option<[u8; 32]>,
|
||||
nhks: 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 {
|
||||
|
@ -192,7 +229,6 @@ impl Zeroize for RatchetEncHeader {
|
|||
self.nhks.zeroize();
|
||||
self.nhkr.zeroize();
|
||||
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 {
|
||||
pub fn init_alice(sk: [u8; 32],
|
||||
/// Initialize a [RatchetEncHeader] with a remote [PublicKey]. Initialized second.
|
||||
/// Requires a shared key, a [PublicKey], a shared HKA, and a shared NHKB.
|
||||
/// Returns a [RatchetEncHeader].
|
||||
pub fn init_alice(
|
||||
sk: [u8; 32],
|
||||
bob_dh_public_key: PublicKey,
|
||||
shared_hka: [u8; 32],
|
||||
shared_nhkb: [u8; 32]) -> Self {
|
||||
shared_nhkb: [u8; 32],
|
||||
) -> Self {
|
||||
let dhs = DhKeyPair::new();
|
||||
let (rk, cks, nhks) = kdf_rk_he(&sk, &dhs.key_agreement(&bob_dh_public_key));
|
||||
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 public_key = dhs.public_key;
|
||||
let ratchet = Self {
|
||||
|
@ -326,50 +295,70 @@ impl RatchetEncHeader {
|
|||
(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());
|
||||
self.cks = Some(cks);
|
||||
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;
|
||||
let encrypted = encrypt(&mk, plaintext, &header.concat(ad));
|
||||
let encrypted = encrypt(&mk, data, &header.concat(associated_data));
|
||||
(enc_header, encrypted.0, encrypted.1)
|
||||
}
|
||||
|
||||
fn try_skipped_message_keys(&mut self, enc_header: &(Vec<u8>, [u8; 12]),
|
||||
ciphertext: &[u8], nonce: &[u8; 12], ad: &[u8]) -> (Option<Vec<u8>>, Option<Header>) {
|
||||
|
||||
fn try_skipped_message_keys(
|
||||
&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 header = Header::decrypt(&e.0.0, &enc_header.0, &enc_header.1);
|
||||
let header = enc_header.decrypt(&e.0.0);
|
||||
match header {
|
||||
None => false,
|
||||
Some(h) => h.n == e.0.1
|
||||
Some(h) => h.n == e.0 .1,
|
||||
}
|
||||
});
|
||||
match ret_data {
|
||||
None => { (None, None) },
|
||||
None => (None, None),
|
||||
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;
|
||||
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> {
|
||||
let header = Header::decrypt(&self.hkr, &enc_header.0, &enc_header.1);
|
||||
if let Some(h) = header { return Ok((h, false)) };
|
||||
let header = Header::decrypt(&self.nhkr, &enc_header.0, &enc_header.1);
|
||||
/// Decrypt an [EncryptedHeader] with a [RatchetEncHeader].
|
||||
/// Requires an [EncryptedHeader].
|
||||
/// Returns a decrypted [Header] and boolean, if decryption was successful.
|
||||
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 {
|
||||
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> {
|
||||
if self.nr + MAX_SKIP < until {
|
||||
return Err("Skipping went wrong")
|
||||
return Err("Skipping went wrong");
|
||||
}
|
||||
if let Some(d) = &mut self.ckr {
|
||||
while self.nr < until {
|
||||
|
@ -389,22 +378,31 @@ impl RatchetEncHeader {
|
|||
self.hks = self.nhks;
|
||||
self.hkr = self.nhkr;
|
||||
self.dhr = Some(header.public_key);
|
||||
let (rk, ckr, nhkr) = kdf_rk_he(&self.rk,
|
||||
&self.dhs.key_agreement(&self.dhr.unwrap()));
|
||||
let (rk, ckr, nhkr) = kdf_rk_he(&self.rk, &self.dhs.key_agreement(&self.dhr.unwrap()));
|
||||
self.rk = rk;
|
||||
self.ckr = Some(ckr);
|
||||
self.nhkr = Some(nhkr);
|
||||
self.dhs = DhKeyPair::new();
|
||||
let (rk, cks, nhks) = kdf_rk_he(&self.rk,
|
||||
&self.dhs.key_agreement(&self.dhr.unwrap()));
|
||||
let (rk, cks, nhks) = kdf_rk_he(&self.rk, &self.dhs.key_agreement(&self.dhr.unwrap()));
|
||||
self.rk = rk;
|
||||
self.cks = Some(cks);
|
||||
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> {
|
||||
let (plaintext, _) = self.try_skipped_message_keys(enc_header, ciphertext, nonce, ad);
|
||||
if let Some(d) = plaintext { return d };
|
||||
/// Decrypt encrypted bytes with a [RatchetEncHeader].
|
||||
/// Requires an [EncryptedHeader], encrypted bytes, a nonce, and associated bytes.
|
||||
/// 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();
|
||||
if dh_ratchet {
|
||||
self.skip_message_keys(header.pn).unwrap();
|
||||
|
@ -414,12 +412,23 @@ impl RatchetEncHeader {
|
|||
let (ckr, mk) = kdf_ck(&self.ckr.unwrap());
|
||||
self.ckr = Some(ckr);
|
||||
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) {
|
||||
let (plaintext, header) = self.try_skipped_message_keys(enc_header, ciphertext, nonce, ad);
|
||||
if let Some(d) = plaintext { return (d, header.unwrap()) };
|
||||
/// Decrypt encrypted bytes and an [EncryptedHeader] with a [RatchetEncHeader].
|
||||
/// Requires an [EncryptedHeader], encrypted bytes, a nonce, and associated bytes.
|
||||
/// 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();
|
||||
if dh_ratchet {
|
||||
self.skip_message_keys(header.pn).unwrap();
|
||||
|
@ -429,18 +438,22 @@ impl RatchetEncHeader {
|
|||
let (ckr, mk) = kdf_ck(&self.ckr.unwrap());
|
||||
self.ckr = Some(ckr);
|
||||
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> {
|
||||
let ex: ExRatchetEncHeader = self.into();
|
||||
bincode::serialize(&ex).unwrap()
|
||||
postcard::to_allocvec(&self).unwrap()
|
||||
}
|
||||
|
||||
/// Import the ratchet from Binary data. Panics when binary data is invalid.
|
||||
pub fn import(inp: &[u8]) -> Option<Self> {
|
||||
let ex: ExRatchetEncHeader = bincode::deserialize(inp).ok()?;
|
||||
Some(RatchetEncHeader::from(&ex))
|
||||
/// Import a previously exported [RatchetEncHeader].
|
||||
/// Requires bytes.
|
||||
/// Returns a [RatchetEncHeader], or nothing if invalid data is provided.
|
||||
pub fn import(data: &[u8]) -> Option<Self> {
|
||||
postcard::from_bytes(data).ok()
|
||||
}
|
||||
}
|
||||
|
|
126
tests/mod.rs
126
tests/mod.rs
|
@ -1,167 +1,145 @@
|
|||
use double_ratchet_2::ratchet::{Ratchet, RatchetEncHeader};
|
||||
use double_ratchet_rs::{Ratchet, RatchetEncHeader};
|
||||
extern crate alloc;
|
||||
|
||||
#[test]
|
||||
fn ratchet_init() {
|
||||
fn init() {
|
||||
let sk = [1; 32];
|
||||
let (_bob_ratchet, public_key) = Ratchet::init_bob(sk);
|
||||
let _alice_ratchet = Ratchet::init_alice(sk, public_key);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ratchet_enc_single() {
|
||||
fn enc_single() {
|
||||
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 = include_bytes!("../src/header.rs").to_vec();
|
||||
let (header, encrypted, nonce) = alice_ratchet.ratchet_encrypt(&data, b"");
|
||||
let decrypted = bob_ratchet.ratchet_decrypt(&header, &encrypted, &nonce, b"");
|
||||
let (header, encrypted, nonce) = alice_ratchet.encrypt(&data, b"");
|
||||
let decrypted = bob_ratchet.decrypt(&header, &encrypted, &nonce, b"");
|
||||
assert_eq!(data, decrypted)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ratchet_enc_skip() {
|
||||
fn enc_skip() {
|
||||
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 = include_bytes!("../src/header.rs").to_vec();
|
||||
let (header1, encrypted1, nonce1) = alice_ratchet.ratchet_encrypt(&data, b"");
|
||||
let (header2, encrypted2, nonce2) = alice_ratchet.ratchet_encrypt(&data, b"");
|
||||
let (header3, encrypted3, nonce3) = alice_ratchet.ratchet_encrypt(&data, b"");
|
||||
let decrypted3 = bob_ratchet.ratchet_decrypt(&header3, &encrypted3, &nonce3, b"");
|
||||
let decrypted2 = bob_ratchet.ratchet_decrypt(&header2, &encrypted2, &nonce2, b"");
|
||||
let decrypted1 = bob_ratchet.ratchet_decrypt(&header1, &encrypted1, &nonce1, b"");
|
||||
let (header1, encrypted1, nonce1) = alice_ratchet.encrypt(&data, b"");
|
||||
let (header2, encrypted2, nonce2) = alice_ratchet.encrypt(&data, b"");
|
||||
let (header3, encrypted3, nonce3) = alice_ratchet.encrypt(&data, b"");
|
||||
let decrypted3 = bob_ratchet.decrypt(&header3, &encrypted3, &nonce3, b"");
|
||||
let decrypted2 = bob_ratchet.decrypt(&header2, &encrypted2, &nonce2, b"");
|
||||
let decrypted1 = bob_ratchet.decrypt(&header1, &encrypted1, &nonce1, b"");
|
||||
let comp_res = decrypted1 == data && decrypted2 == data && decrypted3 == data;
|
||||
assert!(comp_res)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn ratchet_panic_bob() {
|
||||
fn panic_bob() {
|
||||
let sk = [1; 32];
|
||||
let (mut bob_ratchet, _) = Ratchet::init_bob(sk);
|
||||
let data = include_bytes!("../src/header.rs").to_vec();
|
||||
let (_, _, _) = bob_ratchet.ratchet_encrypt(&data, b"");
|
||||
let (_, _, _) = bob_ratchet.encrypt(&data, b"");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ratchet_encryt_decrypt_four() {
|
||||
fn encryt_decrypt_four() {
|
||||
let sk = [1; 32];
|
||||
let data = include_bytes!("../src/dh.rs").to_vec();
|
||||
let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk);
|
||||
let mut alice_ratchet = Ratchet::init_alice(sk, public_key);
|
||||
let (header1, encrypted1, nonce1) = alice_ratchet.ratchet_encrypt(&data, b"");
|
||||
let decrypted1 = bob_ratchet.ratchet_decrypt(&header1, &encrypted1, &nonce1, b"");
|
||||
let (header2, encrypted2, nonce2) = bob_ratchet.ratchet_encrypt(&data, b"");
|
||||
let decrypted2 = alice_ratchet.ratchet_decrypt(&header2, &encrypted2, &nonce2, b"");
|
||||
let (header1, encrypted1, nonce1) = alice_ratchet.encrypt(&data, b"");
|
||||
let decrypted1 = bob_ratchet.decrypt(&header1, &encrypted1, &nonce1, b"");
|
||||
let (header2, encrypted2, nonce2) = bob_ratchet.encrypt(&data, b"");
|
||||
let decrypted2 = alice_ratchet.decrypt(&header2, &encrypted2, &nonce2, b"");
|
||||
let comp_res = decrypted1 == data && decrypted2 == data;
|
||||
assert!(comp_res)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ratchet_ench_init() {
|
||||
fn ench_init() {
|
||||
let sk = [1; 32];
|
||||
let shared_hka = [2; 32];
|
||||
let shared_nhkb = [3; 32];
|
||||
let (_bob_ratchet, public_key) = RatchetEncHeader::init_bob(sk,
|
||||
shared_hka,
|
||||
shared_nhkb);
|
||||
let _alice_ratchet = RatchetEncHeader::init_alice(sk, public_key,
|
||||
shared_hka, shared_nhkb);
|
||||
let (_bob_ratchet, public_key) = RatchetEncHeader::init_bob(sk, shared_hka, shared_nhkb);
|
||||
let _alice_ratchet = RatchetEncHeader::init_alice(sk, public_key, shared_hka, shared_nhkb);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ratchet_ench_enc_single() {
|
||||
fn ench_enc_single() {
|
||||
let sk = [1; 32];
|
||||
let shared_hka = [2; 32];
|
||||
let shared_nhkb = [3; 32];
|
||||
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 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 data = include_bytes!("../src/header.rs").to_vec();
|
||||
let (header, encrypted, nonce) = alice_ratchet.ratchet_encrypt(&data, b"");
|
||||
let decrypted = bob_ratchet.ratchet_decrypt(&header, &encrypted, &nonce, b"");
|
||||
let (header, encrypted, nonce) = alice_ratchet.encrypt(&data, b"");
|
||||
let decrypted = bob_ratchet.decrypt(&header, &encrypted, &nonce, b"");
|
||||
assert_eq!(data, decrypted)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ratchet_ench_enc_skip() {
|
||||
fn ench_enc_skip() {
|
||||
let sk = [1; 32];
|
||||
let shared_hka = [2; 32];
|
||||
let shared_nhkb = [3; 32];
|
||||
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 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 data = include_bytes!("../src/header.rs").to_vec();
|
||||
let (header1, encrypted1, nonce1) = alice_ratchet.ratchet_encrypt(&data, b"");
|
||||
let (header2, encrypted2, nonce2) = alice_ratchet.ratchet_encrypt(&data, b"");
|
||||
let (header3, encrypted3, nonce3) = alice_ratchet.ratchet_encrypt(&data, b"");
|
||||
let decrypted3 = bob_ratchet.ratchet_decrypt(&header3, &encrypted3, &nonce3, b"");
|
||||
let decrypted2 = bob_ratchet.ratchet_decrypt(&header2, &encrypted2, &nonce2, b"");
|
||||
let decrypted1 = bob_ratchet.ratchet_decrypt(&header1, &encrypted1, &nonce1, b"");
|
||||
let (header1, encrypted1, nonce1) = alice_ratchet.encrypt(&data, b"");
|
||||
let (header2, encrypted2, nonce2) = alice_ratchet.encrypt(&data, b"");
|
||||
let (header3, encrypted3, nonce3) = alice_ratchet.encrypt(&data, b"");
|
||||
let decrypted3 = bob_ratchet.decrypt(&header3, &encrypted3, &nonce3, b"");
|
||||
let decrypted2 = bob_ratchet.decrypt(&header2, &encrypted2, &nonce2, b"");
|
||||
let decrypted1 = bob_ratchet.decrypt(&header1, &encrypted1, &nonce1, b"");
|
||||
let comp_res = decrypted1 == data && decrypted2 == data && decrypted3 == data;
|
||||
assert!(comp_res)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn ratchet_ench_panic_bob() {
|
||||
fn ench_panic_bob() {
|
||||
let sk = [1; 32];
|
||||
let shared_hka = [2; 32];
|
||||
let shared_nhkb = [3; 32];
|
||||
let (mut bob_ratchet, _) = RatchetEncHeader::init_bob(sk,
|
||||
shared_hka,
|
||||
shared_nhkb);
|
||||
let (mut bob_ratchet, _) = RatchetEncHeader::init_bob(sk, shared_hka, shared_nhkb);
|
||||
let data = include_bytes!("../src/header.rs").to_vec();
|
||||
let (_, _, _) = bob_ratchet.ratchet_encrypt(&data, b"");
|
||||
let (_, _, _) = bob_ratchet.encrypt(&data, b"");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ratchet_ench_decrypt_four() {
|
||||
fn ench_decrypt_four() {
|
||||
let sk = [1; 32];
|
||||
let shared_hka = [2; 32];
|
||||
let shared_nhkb = [3; 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 data = include_bytes!("../src/dh.rs").to_vec();
|
||||
let (header1, encrypted1, nonce1) = alice_ratchet.ratchet_encrypt(&data, b"");
|
||||
let decrypted1 = bob_ratchet.ratchet_decrypt(&header1, &encrypted1, &nonce1, b"");
|
||||
let (header2, encrypted2, nonce2) = bob_ratchet.ratchet_encrypt(&data, b"");
|
||||
let decrypted2 = alice_ratchet.ratchet_decrypt(&header2, &encrypted2, &nonce2, b"");
|
||||
let (header1, encrypted1, nonce1) = alice_ratchet.encrypt(&data, b"");
|
||||
let decrypted1 = bob_ratchet.decrypt(&header1, &encrypted1, &nonce1, b"");
|
||||
let (header2, encrypted2, nonce2) = bob_ratchet.encrypt(&data, b"");
|
||||
let decrypted2 = alice_ratchet.decrypt(&header2, &encrypted2, &nonce2, b"");
|
||||
let comp_res = decrypted1 == data && decrypted2 == data;
|
||||
assert!(comp_res)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn ratchet_ench_enc_skip_panic() {
|
||||
fn ench_enc_skip_panic() {
|
||||
let sk = [1; 32];
|
||||
let shared_hka = [2; 32];
|
||||
let shared_nhkb = [3; 32];
|
||||
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 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 data = include_bytes!("../src/header.rs").to_vec();
|
||||
let mut headers = alloc::vec![];
|
||||
let mut encrypteds = alloc::vec![];
|
||||
let mut nonces = alloc::vec![];
|
||||
let mut decrypteds = alloc::vec![];
|
||||
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);
|
||||
encrypteds.push(encrypted);
|
||||
nonces.push(nonce);
|
||||
|
@ -173,7 +151,7 @@ fn ratchet_ench_enc_skip_panic() {
|
|||
let header = headers.get(idx).unwrap();
|
||||
let encrypted = encrypteds.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);
|
||||
}
|
||||
}
|
||||
|
@ -183,9 +161,7 @@ fn import_export() {
|
|||
let sk = [1; 32];
|
||||
let shared_hka = [2; 32];
|
||||
let shared_nhkb = [3; 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 alice_ratchet = RatchetEncHeader::init_alice(sk, public_key, shared_hka, shared_nhkb);
|
||||
|
||||
let ex_bob_ratchet = bob_ratchet.export();
|
||||
|
|
Loading…
Reference in a new issue