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]
|
[package]
|
||||||
name = "double-ratchet-2"
|
name = "double-ratchet-rs"
|
||||||
authors = ["Hannes Furmans"]
|
authors = ["Magnetite Contributors", "Hannes Furmans"]
|
||||||
description = "Implementation of Double Ratchet as specified by Signal."
|
description = "A pure Rust implementation of the Double Ratchet Algorithm as specified by Signal."
|
||||||
homepage = "https://github.com/Dione-Software/double-ratchet-2"
|
homepage = "https://github.com/magnetite-dev/double-ratchet-rs"
|
||||||
repository = "https://github.com/Dione-Software/double-ratchet-2"
|
repository = "https://github.com/magnetite-dev/double-ratchet-rs"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
keywords = ["double-ratchet", "crypto", "cryptography", "signal"]
|
keywords = ["double-ratchet", "crypto", "cryptography", "signal"]
|
||||||
version = "0.3.7"
|
version = "0.4.3"
|
||||||
edition = "2018"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[badges]
|
|
||||||
maintenance = { status = "actively-developed" }
|
|
||||||
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
p256 = {version = "0.10", features = ["ecdh", "arithmetic", "pem", "jwk"]}
|
x25519-dalek = {version = "2.0.0-pre.1", default-features = false, features = ["serde", "u64_backend"]}
|
||||||
rand_core = {version = "0.6"}
|
rand_core = "0.6"
|
||||||
getrandom = {version = "0.2.3"}
|
|
||||||
hkdf = "0.12"
|
hkdf = "0.12"
|
||||||
hmac = "0.12"
|
hmac = "0.12"
|
||||||
aes-gcm-siv = {version = "0.10.3"}
|
aes-gcm-siv = "0.11"
|
||||||
sha2 = {version = "0.10"}
|
sha2 = {version = "0.10", default-features = false}
|
||||||
serde = {version = "1", default-features = false, features = ["derive"]}
|
serde = {version = "1", default-features = false, features = ["derive"]}
|
||||||
serde_bytes = "0.11"
|
postcard = {version = "1", default-features = false, features = ["alloc"]}
|
||||||
bincode = "1"
|
|
||||||
hashbrown = {version = "0.13", features = ["serde"]}
|
hashbrown = {version = "0.13", features = ["serde"]}
|
||||||
zeroize = {version = "1.3", features = ["zeroize_derive"]}
|
zeroize = {version = "1.5", default-features = false, features = ["zeroize_derive"]}
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
criterion = "0.4.0"
|
criterion = "0.4"
|
||||||
|
|
||||||
[[bench]]
|
[[bench]]
|
||||||
name = "double_ratchet_bench"
|
name = "double_ratchet_bench"
|
||||||
|
@ -39,6 +31,3 @@ harness = false
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
lto = true
|
lto = true
|
||||||
|
|
||||||
[features]
|
|
||||||
wasm = ["getrandom/js"]
|
|
||||||
|
|
1
LICENSE
1
LICENSE
|
@ -1,5 +1,6 @@
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2022 Magnetite Contributors
|
||||||
Copyright (c) 2021 Hannes Furmans
|
Copyright (c) 2021 Hannes Furmans
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
|
164
README.md
164
README.md
|
@ -1,71 +1,92 @@
|
||||||
[![Crate](https://img.shields.io/crates/v/double-ratchet-2)](https://crates.io/crates/double-ratchet-2)
|
[![Crate](https://img.shields.io/crates/v/double-ratchet-rs)](https://crates.io/crates/double-ratchet-rs)
|
||||||
[![License](https://img.shields.io/github/license/Dione-Software/double-ratchet-2)](https://github.com/Dione-Software/double-ratchet-2/blob/main/LICENSE)
|
[![License](https://img.shields.io/github/license/magnetite-dev/double-ratchet-rs)](https://github.com/magnetite-dev/double-ratchet-rs/blob/main/LICENSE)
|
||||||
[![Coverage Status](https://coveralls.io/repos/github/Dione-Software/double-ratchet-2/badge.svg?branch=main)](https://coveralls.io/github/Dione-Software/double-ratchet-2?branch=main)
|
[![Coverage Status](https://coveralls.io/repos/github/magnetite-dev/double-ratchet-rs/badge.svg?branch=main)](https://coveralls.io/github/magnetite-dev/double-ratchet-rs?branch=main)
|
||||||
[![Workflow Status](https://github.com/Dione-Software/double-ratchet-2/actions/workflows/rust.yml/badge.svg)](https://github.com/Dione-Software/double-ratchet-2/actions/workflows/rust.yml)
|
[![Workflow Status](https://github.com/magnetite-dev/double-ratchet-rs/actions/workflows/rust.yml/badge.svg)](https://github.com/magnetite-dev/double-ratchet-rs/actions/workflows/rust.yml)
|
||||||
![Maintenance](https://img.shields.io/badge/maintenance-activly--developed-brightgreen.svg)
|
|
||||||
|
|
||||||
# double-ratchet-2
|
# double-ratchet-rs
|
||||||
|
|
||||||
Implementation of the double ratchet system/encryption as specified by [Signal][1].
|
A pure Rust implementation of the Double Ratchet Algorithm as specified by [Signal][1].
|
||||||
|
|
||||||
**WARNING! This implementation uses P-256 NOT Curve25519 as specified by Signal!**
|
This implementation follows the cryptographic recommendations provided by [Signal][2].
|
||||||
|
|
||||||
The implementation follows the cryptographic recommendations provided by [Signal][2].
|
|
||||||
The AEAD Algorithm uses a constant Nonce. This might be changed in the future.
|
The AEAD Algorithm uses a constant Nonce. This might be changed in the future.
|
||||||
|
|
||||||
## Example Usage:
|
Fork of [double-ratchet-2](https://github.com/Dione-Software/double-ratchet-2).
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
### Standard Usage
|
||||||
|
|
||||||
|
Alice encrypts a message which is then decrypted by Bob.
|
||||||
|
|
||||||
### Standard:
|
|
||||||
```rust
|
```rust
|
||||||
use double_ratchet_2::ratchet::Ratchet;
|
use double_ratchet_rs::Ratchet;
|
||||||
|
|
||||||
let sk = [1; 32]; // Initial Key created by a symmetric key agreement protocol
|
let sk = [1; 32]; // Shared key created by a symmetric key agreement protocol
|
||||||
let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk); // Creating Bobs Ratchet (returns Bobs PublicKey)
|
|
||||||
let mut alice_ratchet = Ratchet::init_alice(sk, public_key); // Creating Alice Ratchet with Bobs PublicKey
|
let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk); // Creating Bob's Ratchet (returns Bob's PublicKey)
|
||||||
let data = b"Hello World".to_vec(); // Data to be encrypted
|
let mut alice_ratchet = Ratchet::init_alice(sk, public_key); // Creating Alice's Ratchet with Bob's PublicKey
|
||||||
let ad = b"Associated Data"; // Associated Data
|
|
||||||
|
let data = b"Hello World".to_vec(); // Data to be encrypted
|
||||||
|
let ad = b"Associated Data"; // Associated data
|
||||||
|
|
||||||
|
let (header, encrypted, nonce) = alice_ratchet.encrypt(&data, ad); // Encrypting message with Alice's Ratchet (Alice always needs to send the first message)
|
||||||
|
let decrypted = bob_ratchet.decrypt(&header, &encrypted, &nonce, ad); // Decrypt message with Bob's Ratchet
|
||||||
|
|
||||||
let (header, encrypted, nonce) = alice_ratchet.ratchet_encrypt(&data, ad); // Encrypting message with Alice Ratchet (Alice always needs to send the first message)
|
|
||||||
let decrypted = bob_ratchet.ratchet_decrypt(&header, &encrypted, &nonce, ad); // Decrypt message with Bobs Ratchet
|
|
||||||
assert_eq!(data, decrypted)
|
assert_eq!(data, decrypted)
|
||||||
```
|
```
|
||||||
|
|
||||||
### With lost message:
|
### Recovering a Lost Message
|
||||||
|
|
||||||
|
Alice encrypts 2 messages for Bob.
|
||||||
|
The latest message must be decrypted first.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
|
use double_ratchet_rs::Ratchet;
|
||||||
|
|
||||||
let sk = [1; 32]; // Initial Key created by a symmetric key agreement protocol
|
let sk = [1; 32]; // Shared key created by a symmetric key agreement protocol
|
||||||
let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk); // Creating Bobs Ratchet (returns Bobs PublicKey)
|
|
||||||
let mut alice_ratchet = Ratchet::init_alice(sk, public_key); // Creating Alice Ratchet with Bobs PublicKey
|
|
||||||
let data = b"Hello World".to_vec(); // Data to be encrypted
|
|
||||||
let ad = b"Associated Data"; // Associated Data
|
|
||||||
|
|
||||||
let (header1, encrypted1, nonce1) = alice_ratchet.ratchet_encrypt(&data, ad); // Lost message
|
let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk); // Creating Bob's Ratchet (returns Bob's PublicKey)
|
||||||
let (header2, encrypted2, nonce2) = alice_ratchet.ratchet_encrypt(&data, ad); // Successful message
|
let mut alice_ratchet = Ratchet::init_alice(sk, public_key); // Creating Alice's Ratchet with Bob's PublicKey
|
||||||
|
|
||||||
let decrypted2 = bob_ratchet.ratchet_decrypt(&header2, &encrypted2, &nonce2, ad); // Decrypting second message first
|
let data = b"Hello World".to_vec(); // Data to be encrypted
|
||||||
let decrypted1 = bob_ratchet.ratchet_decrypt(&header1, &encrypted1, &nonce1, ad); // Decrypting latter message
|
let ad = b"Associated Data"; // Associated data
|
||||||
|
|
||||||
let comp = decrypted1 == data && decrypted2 == data;
|
let (header1, encrypted1, nonce1) = alice_ratchet.encrypt(&data, ad); // Lost message
|
||||||
assert!(comp);
|
let (header2, encrypted2, nonce2) = alice_ratchet.encrypt(&data, ad); // Successful message
|
||||||
|
|
||||||
|
let decrypted2 = bob_ratchet.decrypt(&header2, &encrypted2, &nonce2, ad); // Decrypting second message first
|
||||||
|
let decrypted1 = bob_ratchet.decrypt(&header1, &encrypted1, &nonce1, ad); // Decrypting latter message
|
||||||
|
|
||||||
|
assert_eq!(data, decrypted1);
|
||||||
|
assert_eq!(data, decrypted2);
|
||||||
```
|
```
|
||||||
|
|
||||||
### Encryption before recieving inital message
|
### Encryption Before Decrypting First Message
|
||||||
|
|
||||||
|
Bob encrypts a message before decrypting one from Alice.
|
||||||
|
This will result in a panic.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use double_ratchet_2::ratchet::Ratchet;
|
use double_ratchet_rs::Ratchet;
|
||||||
|
|
||||||
let sk = [1; 32];
|
let sk = [1; 32];
|
||||||
let ad = b"Associated Data";
|
|
||||||
let (mut bob_ratchet, _) = Ratchet::init_bob(sk);
|
|
||||||
let data = b"Hello World".to_vec();
|
|
||||||
|
|
||||||
let (_, _, _) = bob_ratchet.ratchet_encrypt(&data, ad);
|
let (mut bob_ratchet, _) = Ratchet::init_bob(sk);
|
||||||
|
|
||||||
|
let data = b"Hello World".to_vec();
|
||||||
|
let ad = b"Associated Data";
|
||||||
|
|
||||||
|
let (_, _, _) = bob_ratchet.encrypt(&data, ad);
|
||||||
```
|
```
|
||||||
|
|
||||||
### Encryption after recieving initial message
|
### Encryption After Decrypting First Message
|
||||||
However bob can (of course) also encrypt messages. This is possible, after decrypting the first message from alice.
|
|
||||||
|
Bob *can* also encrypt messages.
|
||||||
|
This is only possible after decrypting one from Alice first though.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use double_ratchet_2::ratchet::Ratchet;
|
use double_ratchet_rs::Ratchet;
|
||||||
|
|
||||||
let sk = [1; 32];
|
let sk = [1; 32];
|
||||||
|
|
||||||
let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk);
|
let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk);
|
||||||
|
@ -74,64 +95,83 @@ let mut alice_ratchet = Ratchet::init_alice(sk, public_key);
|
||||||
let data = b"Hello World".to_vec();
|
let data = b"Hello World".to_vec();
|
||||||
let ad = b"Associated Data";
|
let ad = b"Associated Data";
|
||||||
|
|
||||||
let (header1, encrypted1, nonce1) = alice_ratchet.ratchet_encrypt(&data, ad);
|
let (header1, encrypted1, nonce1) = alice_ratchet.encrypt(&data, ad);
|
||||||
let _decrypted1 = bob_ratchet.ratchet_decrypt(&header1, &encrypted1, &nonce1, ad);
|
let _decrypted1 = bob_ratchet.decrypt(&header1, &encrypted1, &nonce1, ad);
|
||||||
|
|
||||||
let (header2, encrypted2, nonce2) = bob_ratchet.ratchet_encrypt(&data, ad);
|
let (header2, encrypted2, nonce2) = bob_ratchet.encrypt(&data, ad);
|
||||||
let decrypted2 = alice_ratchet.ratchet_decrypt(&header2, &encrypted2, &nonce2, ad);
|
let decrypted2 = alice_ratchet.decrypt(&header2, &encrypted2, &nonce2, ad);
|
||||||
|
|
||||||
assert_eq!(data, decrypted2);
|
assert_eq!(data, decrypted2);
|
||||||
```
|
```
|
||||||
|
|
||||||
### Constructing and Deconstructing Headers
|
### Constructing and Deconstructing Headers
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
|
use double_ratchet_rs::{Header, Ratchet};
|
||||||
|
|
||||||
|
let sk = [1; 32];
|
||||||
|
|
||||||
|
let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk);
|
||||||
|
let mut alice_ratchet = Ratchet::init_alice(sk, public_key);
|
||||||
|
|
||||||
|
let data = b"hello World".to_vec();
|
||||||
|
let ad = b"Associated Data";
|
||||||
|
|
||||||
|
let (header, _, _) = alice_ratchet.encrypt(&data, ad);
|
||||||
let header_bytes: Vec<u8> = header.clone().into();
|
let header_bytes: Vec<u8> = header.clone().into();
|
||||||
let header_const = Header::from(header_bytes);
|
let header_const = Header::from(header_bytes);
|
||||||
|
|
||||||
assert_eq!(header, header_const);
|
assert_eq!(header, header_const);
|
||||||
```
|
```
|
||||||
|
|
||||||
## Example Ratchet with encrypted headers
|
### Encrypted Headers
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use double_ratchet_2::ratchet::RatchetEncHeader;
|
use double_ratchet_rs::RatchetEncHeader;
|
||||||
|
|
||||||
let sk = [0; 32];
|
let sk = [0; 32];
|
||||||
let shared_hka = [1; 32];
|
let shared_hka = [1; 32];
|
||||||
let shared_nhkb = [2; 32];
|
let shared_nhkb = [2; 32];
|
||||||
|
|
||||||
let (mut bob_ratchet, public_key) = RatchetEncHeader::init_bob(sk, shared_hka, shared_nhkb);
|
let (mut bob_ratchet, public_key) = RatchetEncHeader::init_bob(sk, shared_hka, shared_nhkb);
|
||||||
let mut alice_ratchet = RatchetEncHeader::init_alice(sk, public_key, shared_hka, shared_nhkb);
|
let mut alice_ratchet = RatchetEncHeader::init_alice(sk, public_key, shared_hka, shared_nhkb);
|
||||||
|
|
||||||
let data = b"Hello World".to_vec();
|
let data = b"Hello World".to_vec();
|
||||||
let ad = b"Associated Data";
|
let ad = b"Associated Data";
|
||||||
|
|
||||||
let (header, encrypted, nonce) = alice_ratchet.ratchet_encrypt(&data, ad);
|
let (header, encrypted, nonce) = alice_ratchet.encrypt(&data, ad);
|
||||||
let decrypted = bob_ratchet.ratchet_decrypt(&header, &encrypted, &nonce, ad);
|
let decrypted = bob_ratchet.decrypt(&header, &encrypted, &nonce, ad);
|
||||||
|
|
||||||
assert_eq!(data, decrypted)
|
assert_eq!(data, decrypted)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Export / Import Ratchet with encrypted headers
|
### Exporting / Importing Ratchet w/ Encrypted Headers
|
||||||
This ratchet implements import and export functionality. This works over a bincode backend and
|
|
||||||
maybe useful for saving Ratchets to and loading from a file.
|
This can be used for storing and using ratchets in a file.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
|
use double_ratchet_rs::RatchetEncHeader;
|
||||||
|
|
||||||
|
let sk = [0; 32];
|
||||||
|
let shared_hka = [1; 32];
|
||||||
|
let shared_nhkb = [2; 32];
|
||||||
|
|
||||||
let (bob_ratchet, public_key) = RatchetEncHeader::init_bob(sk, shared_hka, shared_nhkb);
|
let (bob_ratchet, public_key) = RatchetEncHeader::init_bob(sk, shared_hka, shared_nhkb);
|
||||||
let ex_ratchet = bob_ratchet.export();
|
let ex_ratchet = bob_ratchet.export();
|
||||||
let im_ratchet = RatchetEncHeader::import(&ex_ratchet);
|
let im_ratchet = RatchetEncHeader::import(&ex_ratchet).unwrap();
|
||||||
|
|
||||||
assert_eq!(im_ratchet, bob_ratchet)
|
assert_eq!(im_ratchet, bob_ratchet)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Features
|
## **M**inimum **S**upported **R**ust **V**ersion (MSRV)
|
||||||
|
|
||||||
Currently the crate only supports one feature: ring. If feature is enabled the crate switches
|
The current MSRV is 1.61.0.
|
||||||
to ring-compat and uses ring as backend for Sha512 Hashing. May result in slightly better performance.
|
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
TODO:
|
This project is licensed under the [MIT license](https://github.com/magnetite-dev/double-ratchet-rs/blob/main/LICENSE).
|
||||||
- [x] Standard Double Ratchet
|
|
||||||
- [x] [Double Ratchet with encrypted headers][3]
|
|
||||||
|
|
||||||
[1]: https://signal.org/docs/specifications/doubleratchet/
|
[1]: https://signal.org/docs/specifications/doubleratchet/
|
||||||
[2]: https://signal.org/docs/specifications/doubleratchet/#recommended-cryptographic-algorithms
|
[2]: https://signal.org/docs/specifications/doubleratchet/#recommended-cryptographic-algorithms
|
||||||
[3]: https://signal.org/docs/specifications/doubleratchet/#double-ratchet-with-header-encryption
|
[3]: https://signal.org/docs/specifications/doubleratchet/#double-ratchet-with-header-encryption
|
||||||
|
|
||||||
Current version: 0.4.0
|
|
||||||
|
|
||||||
License: MIT
|
|
||||||
|
|
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_group, criterion_main, Criterion};
|
||||||
use criterion::{Criterion, criterion_main, criterion_group};
|
use double_ratchet_rs::{Ratchet, RatchetEncHeader};
|
||||||
|
|
||||||
fn ratchet_enc_single() {
|
fn ratchet_enc_single() {
|
||||||
let sk = [1; 32];
|
let sk = [1; 32];
|
||||||
let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk);
|
let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk);
|
||||||
let mut alice_ratchet = Ratchet::init_alice(sk, public_key);
|
let mut alice_ratchet = Ratchet::init_alice(sk, public_key);
|
||||||
let data = include_bytes!("../src/header.rs").to_vec();
|
let data = include_bytes!("../src/header.rs").to_vec();
|
||||||
let (header, encrypted, nonce) = alice_ratchet.ratchet_encrypt(&data, b"");
|
let (header, encrypted, nonce) = alice_ratchet.encrypt(&data, b"");
|
||||||
let _decrypted = bob_ratchet.ratchet_decrypt(&header, &encrypted, &nonce, b"");
|
let _decrypted = bob_ratchet.decrypt(&header, &encrypted, &nonce, b"");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn criterion_benchmark_1(c: &mut Criterion) {
|
fn criterion_benchmark_1(c: &mut Criterion) {
|
||||||
|
@ -19,10 +19,10 @@ fn ratchet_enc_skip() {
|
||||||
let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk);
|
let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk);
|
||||||
let mut alice_ratchet = Ratchet::init_alice(sk, public_key);
|
let mut alice_ratchet = Ratchet::init_alice(sk, public_key);
|
||||||
let data = include_bytes!("../src/header.rs").to_vec();
|
let data = include_bytes!("../src/header.rs").to_vec();
|
||||||
let (header1, encrypted1, nonce1) = alice_ratchet.ratchet_encrypt(&data, b"");
|
let (header1, encrypted1, nonce1) = alice_ratchet.encrypt(&data, b"");
|
||||||
let (header2, encrypted2, nonce2) = alice_ratchet.ratchet_encrypt(&data, b"");
|
let (header2, encrypted2, nonce2) = alice_ratchet.encrypt(&data, b"");
|
||||||
let _decrypted2 = bob_ratchet.ratchet_decrypt(&header2, &encrypted2, &nonce2, b"");
|
let _decrypted2 = bob_ratchet.decrypt(&header2, &encrypted2, &nonce2, b"");
|
||||||
let _decrypted1 = bob_ratchet.ratchet_decrypt(&header1, &encrypted1, &nonce1, b"");
|
let _decrypted1 = bob_ratchet.decrypt(&header1, &encrypted1, &nonce1, b"");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn criterion_benchmark_2(c: &mut Criterion) {
|
fn criterion_benchmark_2(c: &mut Criterion) {
|
||||||
|
@ -34,78 +34,84 @@ fn ratchet_encryt_decrypt_four() {
|
||||||
let data = include_bytes!("../src/dh.rs").to_vec();
|
let data = include_bytes!("../src/dh.rs").to_vec();
|
||||||
let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk);
|
let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk);
|
||||||
let mut alice_ratchet = Ratchet::init_alice(sk, public_key);
|
let mut alice_ratchet = Ratchet::init_alice(sk, public_key);
|
||||||
let (header1, encrypted1, nonce1) = alice_ratchet.ratchet_encrypt(&data, b"");
|
let (header1, encrypted1, nonce1) = alice_ratchet.encrypt(&data, b"");
|
||||||
let _decrypted1 = bob_ratchet.ratchet_decrypt(&header1, &encrypted1, &nonce1, b"");
|
let _decrypted1 = bob_ratchet.decrypt(&header1, &encrypted1, &nonce1, b"");
|
||||||
let (header2, encrypted2, nonce2) = bob_ratchet.ratchet_encrypt(&data, b"");
|
let (header2, encrypted2, nonce2) = bob_ratchet.encrypt(&data, b"");
|
||||||
let _decrypted2 = alice_ratchet.ratchet_decrypt(&header2, &encrypted2, &nonce2, b"");
|
let _decrypted2 = alice_ratchet.decrypt(&header2, &encrypted2, &nonce2, b"");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn criterion_benchmark_3(c: &mut Criterion) {
|
fn criterion_benchmark_3(c: &mut Criterion) {
|
||||||
c.bench_function("Ratchet Dec Four", |b| b.iter(|| ratchet_encryt_decrypt_four()));
|
c.bench_function("Ratchet Dec Four", |b| {
|
||||||
|
b.iter(|| ratchet_encryt_decrypt_four())
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ratchet_ench_enc_single() {
|
fn ratchet_ench_enc_single() {
|
||||||
let sk = [1; 32];
|
let sk = [1; 32];
|
||||||
let shared_hka = [2; 32];
|
let shared_hka = [2; 32];
|
||||||
let shared_nhkb = [3; 32];
|
let shared_nhkb = [3; 32];
|
||||||
let (mut bob_ratchet, public_key) = RatchetEncHeader::init_bob(sk,
|
let (mut bob_ratchet, public_key) = RatchetEncHeader::init_bob(sk, shared_hka, shared_nhkb);
|
||||||
shared_hka,
|
let mut alice_ratchet = RatchetEncHeader::init_alice(sk, public_key, shared_hka, shared_nhkb);
|
||||||
shared_nhkb);
|
|
||||||
let mut alice_ratchet = RatchetEncHeader::init_alice(sk,
|
|
||||||
public_key,
|
|
||||||
shared_hka,
|
|
||||||
shared_nhkb);
|
|
||||||
let data = include_bytes!("../src/header.rs").to_vec();
|
let data = include_bytes!("../src/header.rs").to_vec();
|
||||||
let (header, encrypted, nonce) = alice_ratchet.ratchet_encrypt(&data, b"");
|
let (header, encrypted, nonce) = alice_ratchet.encrypt(&data, b"");
|
||||||
let decrypted = bob_ratchet.ratchet_decrypt(&header, &encrypted, &nonce, b"");
|
let decrypted = bob_ratchet.decrypt(&header, &encrypted, &nonce, b"");
|
||||||
assert_eq!(data, decrypted)
|
assert_eq!(data, decrypted)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn criterion_benchmark_4(c: &mut Criterion) {
|
fn criterion_benchmark_4(c: &mut Criterion) {
|
||||||
c.bench_function("Encrypted Header Ratchet Enc Single", |b| b.iter(|| ratchet_ench_enc_single()));
|
c.bench_function("Encrypted Header Ratchet Enc Single", |b| {
|
||||||
|
b.iter(|| ratchet_ench_enc_single())
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ratchet_ench_enc_skip() {
|
fn ratchet_ench_enc_skip() {
|
||||||
let sk = [1; 32];
|
let sk = [1; 32];
|
||||||
let shared_hka = [2; 32];
|
let shared_hka = [2; 32];
|
||||||
let shared_nhkb = [3; 32];
|
let shared_nhkb = [3; 32];
|
||||||
let (mut bob_ratchet, public_key) = RatchetEncHeader::init_bob(sk,
|
let (mut bob_ratchet, public_key) = RatchetEncHeader::init_bob(sk, shared_hka, shared_nhkb);
|
||||||
shared_hka,
|
let mut alice_ratchet = RatchetEncHeader::init_alice(sk, public_key, shared_hka, shared_nhkb);
|
||||||
shared_nhkb);
|
|
||||||
let mut alice_ratchet = RatchetEncHeader::init_alice(sk,
|
|
||||||
public_key,
|
|
||||||
shared_hka,
|
|
||||||
shared_nhkb);
|
|
||||||
let data = include_bytes!("../src/header.rs").to_vec();
|
let data = include_bytes!("../src/header.rs").to_vec();
|
||||||
let (header1, encrypted1, nonce1) = alice_ratchet.ratchet_encrypt(&data, b"");
|
let (header1, encrypted1, nonce1) = alice_ratchet.encrypt(&data, b"");
|
||||||
let (header2, encrypted2, nonce2) = alice_ratchet.ratchet_encrypt(&data, b"");
|
let (header2, encrypted2, nonce2) = alice_ratchet.encrypt(&data, b"");
|
||||||
let _decrypted2 = bob_ratchet.ratchet_decrypt(&header2, &encrypted2, &nonce2, b"");
|
let _decrypted2 = bob_ratchet.decrypt(&header2, &encrypted2, &nonce2, b"");
|
||||||
let _decrypted1 = bob_ratchet.ratchet_decrypt(&header1, &encrypted1, &nonce1, b"");
|
let _decrypted1 = bob_ratchet.decrypt(&header1, &encrypted1, &nonce1, b"");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn criterion_benchmark_5(c: &mut Criterion) {
|
fn criterion_benchmark_5(c: &mut Criterion) {
|
||||||
c.bench_function("Encrypted Header Ratchet Enc Skip", |b| b.iter(|| ratchet_ench_enc_skip()));
|
c.bench_function("Encrypted Header Ratchet Enc Skip", |b| {
|
||||||
|
b.iter(|| ratchet_ench_enc_skip())
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ratchet_ench_decrypt_four() {
|
fn ratchet_ench_decrypt_four() {
|
||||||
let sk = [1; 32];
|
let sk = [1; 32];
|
||||||
let shared_hka = [2; 32];
|
let shared_hka = [2; 32];
|
||||||
let shared_nhkb = [3; 32];
|
let shared_nhkb = [3; 32];
|
||||||
let (mut bob_ratchet, public_key) = RatchetEncHeader::init_bob(sk,
|
let (mut bob_ratchet, public_key) = RatchetEncHeader::init_bob(sk, shared_hka, shared_nhkb);
|
||||||
shared_hka,
|
|
||||||
shared_nhkb);
|
|
||||||
let mut alice_ratchet = RatchetEncHeader::init_alice(sk, public_key, shared_hka, shared_nhkb);
|
let mut alice_ratchet = RatchetEncHeader::init_alice(sk, public_key, shared_hka, shared_nhkb);
|
||||||
let data = include_bytes!("../src/dh.rs").to_vec();
|
let data = include_bytes!("../src/dh.rs").to_vec();
|
||||||
let (header1, encrypted1, nonce1) = alice_ratchet.ratchet_encrypt(&data, b"");
|
let (header1, encrypted1, nonce1) = alice_ratchet.encrypt(&data, b"");
|
||||||
let _decrypted1 = bob_ratchet.ratchet_decrypt(&header1, &encrypted1, &nonce1, b"");
|
let _decrypted1 = bob_ratchet.decrypt(&header1, &encrypted1, &nonce1, b"");
|
||||||
let (header2, encrypted2, nonce2) = bob_ratchet.ratchet_encrypt(&data, b"");
|
let (header2, encrypted2, nonce2) = bob_ratchet.encrypt(&data, b"");
|
||||||
let _decrypted2 = alice_ratchet.ratchet_decrypt(&header2, &encrypted2, &nonce2, b"");
|
let _decrypted2 = alice_ratchet.decrypt(&header2, &encrypted2, &nonce2, b"");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn criterion_benchmark_6(c: &mut Criterion) {
|
fn criterion_benchmark_6(c: &mut Criterion) {
|
||||||
c.bench_function("Encrypted Header Ratchet Dec Four", |b| b.iter(|| ratchet_ench_decrypt_four()));
|
c.bench_function("Encrypted Header Ratchet Dec Four", |b| {
|
||||||
|
b.iter(|| ratchet_ench_decrypt_four())
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
criterion_group!(without_enc_headerd, criterion_benchmark_1, criterion_benchmark_2, criterion_benchmark_3);
|
criterion_group!(
|
||||||
criterion_group!(with_enc_headerd, criterion_benchmark_4, criterion_benchmark_5, criterion_benchmark_6);
|
without_enc_headerd,
|
||||||
criterion_main!(without_enc_headerd, with_enc_headerd);
|
criterion_benchmark_1,
|
||||||
|
criterion_benchmark_2,
|
||||||
|
criterion_benchmark_3
|
||||||
|
);
|
||||||
|
criterion_group!(
|
||||||
|
with_enc_headerd,
|
||||||
|
criterion_benchmark_4,
|
||||||
|
criterion_benchmark_5,
|
||||||
|
criterion_benchmark_6
|
||||||
|
);
|
||||||
|
criterion_main!(without_enc_headerd, with_enc_headerd);
|
||||||
|
|
204
deny.toml
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 = [""]
|
|
56
src/aead.rs
56
src/aead.rs
|
@ -1,45 +1,57 @@
|
||||||
use aes_gcm_siv::{Key, Aes256GcmSiv, Nonce};
|
use aes_gcm_siv::aead::AeadInPlace;
|
||||||
use aes_gcm_siv::aead::{NewAead, AeadInPlace};
|
use aes_gcm_siv::{Aes256GcmSiv, KeyInit, Nonce};
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
use rand_core::{OsRng, RngCore};
|
use rand_core::{OsRng, RngCore};
|
||||||
|
|
||||||
pub fn encrypt(mk: &[u8; 32], plaintext: &[u8], associated_data: &[u8]) -> (Vec<u8>, [u8; 12]) {
|
pub fn encrypt(mk: &[u8; 32], data: &[u8], associated_data: &[u8]) -> (Vec<u8>, [u8; 12]) {
|
||||||
let key = Key::from_slice(mk);
|
let cipher = Aes256GcmSiv::new_from_slice(mk).expect("Encryption failure {}");
|
||||||
let cipher = Aes256GcmSiv::new(key);
|
|
||||||
let mut nonce_data = [0_u8; 12];
|
let mut nonce_data = [0u8; 12];
|
||||||
OsRng::fill_bytes(&mut OsRng, &mut nonce_data);
|
OsRng::fill_bytes(&mut OsRng, &mut nonce_data);
|
||||||
let nonce = Nonce::from_slice(&nonce_data);
|
let nonce = Nonce::from_slice(&nonce_data);
|
||||||
let mut buffer = Vec::new();
|
|
||||||
buffer.extend_from_slice(plaintext);
|
|
||||||
|
|
||||||
cipher.encrypt_in_place(nonce, associated_data, &mut buffer)
|
let mut buffer = Vec::new();
|
||||||
.expect("Encryption failed");
|
buffer.extend_from_slice(data);
|
||||||
|
|
||||||
|
cipher
|
||||||
|
.encrypt_in_place(nonce, associated_data, &mut buffer)
|
||||||
|
.expect("Encryption failure {}");
|
||||||
|
|
||||||
(buffer, nonce_data)
|
(buffer, nonce_data)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn decrypt(mk: &[u8; 32], ciphertext: &[u8], associated_data: &[u8], nonce: &[u8; 12]) -> Vec<u8> {
|
pub fn decrypt(
|
||||||
let key = Key::from_slice(mk);
|
mk: &[u8; 32],
|
||||||
let cipher = Aes256GcmSiv::new(key);
|
enc_data: &[u8],
|
||||||
|
associated_data: &[u8],
|
||||||
|
nonce: &[u8; 12],
|
||||||
|
) -> Vec<u8> {
|
||||||
|
let cipher = Aes256GcmSiv::new_from_slice(mk).expect("Decryption failure {}");
|
||||||
|
|
||||||
let nonce = Nonce::from_slice(nonce);
|
let nonce = Nonce::from_slice(nonce);
|
||||||
|
|
||||||
let mut buffer = Vec::new();
|
let mut buffer = Vec::new();
|
||||||
buffer.extend_from_slice(ciphertext);
|
buffer.extend_from_slice(enc_data);
|
||||||
cipher.decrypt_in_place(nonce, associated_data, &mut buffer).expect("Decryption failure {}");
|
|
||||||
|
cipher
|
||||||
|
.decrypt_in_place(nonce, associated_data, &mut buffer)
|
||||||
|
.expect("Decryption failure {}");
|
||||||
|
|
||||||
buffer
|
buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use crate::aead::{decrypt, encrypt};
|
||||||
use crate::kdf_chain::gen_mk;
|
use crate::kdf_chain::gen_mk;
|
||||||
use crate::aead::{encrypt, decrypt};
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn enc_a_dec() {
|
fn enc_a_dec() {
|
||||||
let test_data = include_bytes!("aead.rs").to_vec();
|
let test_data = include_bytes!("aead.rs");
|
||||||
let associated_data = include_bytes!("lib.rs").to_vec();
|
let associated_data = include_bytes!("lib.rs");
|
||||||
let mk = gen_mk();
|
let mk = gen_mk();
|
||||||
let (ciphertext, nonce) = encrypt(&mk, &test_data, &associated_data);
|
let (enc_data, nonce) = encrypt(&mk, test_data, associated_data);
|
||||||
let plaintext = decrypt(&mk, &ciphertext, &associated_data, &nonce);
|
let data = decrypt(&mk, &enc_data, associated_data, &nonce);
|
||||||
assert_eq!(test_data, plaintext)
|
assert_eq!(test_data, data.as_slice())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
53
src/dh.rs
53
src/dh.rs
|
@ -1,43 +1,31 @@
|
||||||
|
use core::fmt::{Debug, Formatter, Result};
|
||||||
use rand_core::OsRng;
|
use rand_core::OsRng;
|
||||||
use core::fmt::{Debug, Formatter};
|
use serde::{Deserialize, Serialize};
|
||||||
use core::fmt;
|
use x25519_dalek::{PublicKey, SharedSecret, StaticSecret};
|
||||||
use p256::PublicKey as PublicKey;
|
|
||||||
use p256::ecdh::SharedSecret;
|
|
||||||
use p256::SecretKey;
|
|
||||||
use alloc::vec::Vec;
|
|
||||||
use alloc::string::ToString;
|
|
||||||
use p256::elliptic_curve::ecdh::diffie_hellman;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Deserialize, Serialize, Clone)]
|
||||||
pub struct DhKeyPair {
|
pub struct DhKeyPair {
|
||||||
pub private_key: SecretKey,
|
pub private_key: StaticSecret,
|
||||||
pub public_key: PublicKey,
|
pub public_key: PublicKey,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl DhKeyPair {
|
|
||||||
fn ex_public_key_bytes(&self) -> Vec<u8> {
|
|
||||||
self.public_key.to_string().as_bytes().to_vec()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq for DhKeyPair {
|
impl PartialEq for DhKeyPair {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
if self.private_key.to_be_bytes() != other.private_key.to_be_bytes() {
|
if self.private_key.to_bytes() != other.private_key.to_bytes() {
|
||||||
return false
|
return false;
|
||||||
}
|
}
|
||||||
if self.ex_public_key_bytes() != other.ex_public_key_bytes() {
|
if self.public_key != other.public_key {
|
||||||
return false
|
return false;
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for DhKeyPair {
|
impl Debug for DhKeyPair {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
|
||||||
f.debug_struct("DhKeyPair")
|
f.debug_struct("DhKeyPair")
|
||||||
.field("private_key", &self.private_key.to_be_bytes())
|
.field("private_key", &self.private_key.to_bytes())
|
||||||
.field("public_key", &self.ex_public_key_bytes())
|
.field("public_key", self.public_key.as_bytes())
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,8 +38,8 @@ impl Default for DhKeyPair {
|
||||||
|
|
||||||
impl DhKeyPair {
|
impl DhKeyPair {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let secret = SecretKey::random(&mut OsRng);
|
let secret = StaticSecret::new(&mut OsRng);
|
||||||
let public = secret.public_key();
|
let public = PublicKey::from(&secret);
|
||||||
DhKeyPair {
|
DhKeyPair {
|
||||||
private_key: secret,
|
private_key: secret,
|
||||||
public_key: public,
|
public_key: public,
|
||||||
|
@ -59,7 +47,7 @@ impl DhKeyPair {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn key_agreement(&self, public_key: &PublicKey) -> SharedSecret {
|
pub fn key_agreement(&self, public_key: &PublicKey) -> SharedSecret {
|
||||||
diffie_hellman(self.private_key.to_nonzero_scalar(), public_key.as_affine())
|
self.private_key.diffie_hellman(public_key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,7 +66,6 @@ pub fn gen_key_pair() -> DhKeyPair {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::dh::DhKeyPair;
|
use crate::dh::DhKeyPair;
|
||||||
use alloc::string::ToString;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn key_generation() {
|
fn key_generation() {
|
||||||
|
@ -96,14 +83,6 @@ mod tests {
|
||||||
assert_eq!(alice_shared_secret.as_bytes(), bob_shared_secret.as_bytes())
|
assert_eq!(alice_shared_secret.as_bytes(), bob_shared_secret.as_bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn ex_public_key() {
|
|
||||||
let key_pair = DhKeyPair::new();
|
|
||||||
let public_key_bytes = key_pair.ex_public_key_bytes();
|
|
||||||
let extracted_pk = key_pair.public_key.to_string().as_bytes().to_vec();
|
|
||||||
assert_eq!(extracted_pk, public_key_bytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn nq_key_pair() {
|
fn nq_key_pair() {
|
||||||
let key_pair1 = DhKeyPair::new();
|
let key_pair1 = DhKeyPair::new();
|
||||||
|
@ -124,4 +103,4 @@ mod tests {
|
||||||
let key_pair = DhKeyPair::default();
|
let key_pair = DhKeyPair::default();
|
||||||
let _str = alloc::format!("{:?}", key_pair);
|
let _str = alloc::format!("{:?}", key_pair);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
148
src/header.rs
148
src/header.rs
|
@ -1,107 +1,62 @@
|
||||||
use p256::PublicKey;
|
//! Message header.
|
||||||
use crate::dh::DhKeyPair;
|
|
||||||
use alloc::vec::Vec;
|
|
||||||
use serde::{Serialize, Deserialize};
|
|
||||||
use crate::aead::encrypt;
|
use crate::aead::encrypt;
|
||||||
use aes_gcm_siv::{Key, Nonce, Aes256GcmSiv};
|
use crate::dh::DhKeyPair;
|
||||||
use aes_gcm_siv::aead::{NewAead, AeadInPlace};
|
use aes_gcm_siv::aead::AeadInPlace;
|
||||||
|
use aes_gcm_siv::{Aes256GcmSiv, KeyInit, Nonce};
|
||||||
|
use alloc::vec::Vec;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use x25519_dalek::PublicKey;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use crate::dh::gen_key_pair;
|
use crate::dh::gen_key_pair;
|
||||||
use alloc::string::{ToString, String};
|
|
||||||
use core::str::FromStr;
|
|
||||||
use zeroize::Zeroize;
|
use zeroize::Zeroize;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Zeroize, Clone, PartialEq, Eq)]
|
||||||
pub struct Header {
|
|
||||||
pub public_key: PublicKey,
|
|
||||||
pub pn: usize, // Previous Chain Length
|
|
||||||
pub n: usize, // Message Number
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Zeroize)]
|
|
||||||
#[zeroize(drop)]
|
#[zeroize(drop)]
|
||||||
struct ExHeader {
|
pub struct Header {
|
||||||
#[serde(with = "serde_bytes")]
|
|
||||||
ad: Vec<u8>,
|
ad: Vec<u8>,
|
||||||
public_key: Vec<u8>,
|
pub public_key: PublicKey,
|
||||||
pn: usize,
|
pub pn: usize,
|
||||||
n: usize
|
pub n: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Message Header
|
// A message header.
|
||||||
impl Header {
|
impl Header {
|
||||||
// #[doc(hidden)]
|
/// Create a new message header.
|
||||||
|
/// Requires a [DhKeyPair], previous chain length, and message number.
|
||||||
|
/// Returns a [Header].
|
||||||
pub fn new(dh_pair: &DhKeyPair, pn: usize, n: usize) -> Self {
|
pub fn new(dh_pair: &DhKeyPair, pn: usize, n: usize) -> Self {
|
||||||
Header {
|
Header {
|
||||||
|
ad: Vec::new(),
|
||||||
public_key: dh_pair.public_key,
|
public_key: dh_pair.public_key,
|
||||||
pn,
|
pn,
|
||||||
n,
|
n,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// #[doc(hidden)]
|
|
||||||
pub fn concat(&self, ad: &[u8]) -> Vec<u8> {
|
pub fn concat(&self, ad: &[u8]) -> Vec<u8> {
|
||||||
let ex_header = ExHeader {
|
let mut header = self.clone();
|
||||||
ad: ad.to_vec(),
|
header.ad = ad.to_vec();
|
||||||
public_key: self.public_key.to_string().as_bytes().to_vec(),
|
postcard::to_allocvec(&header).expect("Failed to serialize Header")
|
||||||
pn: self.pn,
|
|
||||||
n: self.n
|
|
||||||
};
|
|
||||||
bincode::serialize(&ex_header).expect("Failed to serialize Header")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn encrypt(&self, hk: &[u8; 32], ad: &[u8]) -> (Vec<u8>, [u8; 12]) {
|
pub fn encrypt(&self, hk: &[u8; 32], ad: &[u8]) -> EncryptedHeader {
|
||||||
let header_data = self.concat(ad);
|
let header_data = self.concat(ad);
|
||||||
encrypt(hk, &header_data, b"")
|
let enc_header = encrypt(hk, &header_data, b"");
|
||||||
}
|
EncryptedHeader(enc_header.0, enc_header.1)
|
||||||
|
|
||||||
pub fn decrypt(hk: &Option<[u8; 32]>, ciphertext: &[u8], nonce: &[u8; 12]) -> Option<Self> {
|
|
||||||
let key_d = match hk {
|
|
||||||
None => {
|
|
||||||
return None
|
|
||||||
},
|
|
||||||
Some(d) => d
|
|
||||||
};
|
|
||||||
let key = Key::from_slice(key_d);
|
|
||||||
let cipher = Aes256GcmSiv::new(key);
|
|
||||||
|
|
||||||
let nonce = Nonce::from_slice(nonce);
|
|
||||||
let mut buffer = Vec::new();
|
|
||||||
buffer.extend_from_slice(ciphertext);
|
|
||||||
match cipher.decrypt_in_place(nonce, b"", &mut buffer) {
|
|
||||||
Ok(_) => {}
|
|
||||||
Err(_) => {
|
|
||||||
return None
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Some(Header::from(buffer))
|
|
||||||
}
|
|
||||||
pub fn ex_public_key_bytes(&self) -> Vec<u8> {
|
|
||||||
self.public_key.to_string().as_bytes().to_vec()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Vec<u8>> for Header {
|
impl From<Vec<u8>> for Header {
|
||||||
fn from(d: Vec<u8>) -> Self {
|
fn from(d: Vec<u8>) -> Self {
|
||||||
let ex_header: ExHeader = bincode::deserialize(&d).unwrap();
|
postcard::from_bytes(&d).unwrap()
|
||||||
let public_key_string = String::from_utf8(ex_header.public_key.clone()).unwrap();
|
|
||||||
Header {
|
|
||||||
public_key: PublicKey::from_str(&public_key_string).unwrap(),
|
|
||||||
pn: ex_header.pn,
|
|
||||||
n: ex_header.n,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&[u8]> for Header {
|
impl From<&[u8]> for Header {
|
||||||
fn from(d: &[u8]) -> Self {
|
fn from(d: &[u8]) -> Self {
|
||||||
let ex_header: ExHeader = bincode::deserialize(d).unwrap();
|
postcard::from_bytes(d).unwrap()
|
||||||
let public_key_string = String::from_utf8(ex_header.public_key.clone()).unwrap();
|
|
||||||
Header {
|
|
||||||
public_key: PublicKey::from_str(&public_key_string).unwrap(),
|
|
||||||
pn: ex_header.pn,
|
|
||||||
n: ex_header.n,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,14 +66,28 @@ impl From<Header> for Vec<u8> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for Header {
|
pub struct EncryptedHeader(Vec<u8>, [u8; 12]);
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
if self.public_key == other.public_key
|
impl EncryptedHeader {
|
||||||
&& self.pn == other.pn
|
pub fn decrypt(&self, hk: &Option<[u8; 32]>) -> Option<Header> {
|
||||||
&& self.n == other.n {
|
let key_d = match hk {
|
||||||
return true
|
None => return None,
|
||||||
}
|
Some(d) => d,
|
||||||
false
|
};
|
||||||
|
|
||||||
|
let cipher = match Aes256GcmSiv::new_from_slice(key_d) {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(_) => return None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let nonce = Nonce::from_slice(&self.1);
|
||||||
|
let mut buffer = Vec::new();
|
||||||
|
buffer.extend_from_slice(&self.0);
|
||||||
|
match cipher.decrypt_in_place(nonce, b"", &mut buffer) {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(_) => return None,
|
||||||
|
};
|
||||||
|
Some(Header::from(buffer))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,12 +99,13 @@ pub fn gen_header() -> Header {
|
||||||
Header::new(&dh_pair, pn, n)
|
Header::new(&dh_pair, pn, n)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::header::{gen_header, Header, ExHeader};
|
use x25519_dalek::PublicKey;
|
||||||
|
|
||||||
|
use crate::aead::{decrypt, encrypt};
|
||||||
|
use crate::header::{gen_header, Header};
|
||||||
use crate::kdf_chain::gen_mk;
|
use crate::kdf_chain::gen_mk;
|
||||||
use crate::aead::{encrypt, decrypt};
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn ser_des() {
|
fn ser_des() {
|
||||||
|
@ -172,11 +142,11 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn gen_ex_header() {
|
fn gen_ex_header() {
|
||||||
let ex_header = ExHeader {
|
let ex_header = Header {
|
||||||
ad: alloc::vec![0],
|
ad: alloc::vec![0],
|
||||||
public_key: alloc::vec![1],
|
public_key: PublicKey::from([1; 32]),
|
||||||
pn: 0,
|
pn: 0,
|
||||||
n: 0
|
n: 0,
|
||||||
};
|
};
|
||||||
let _string = alloc::format!("{:?}", ex_header);
|
let _string = alloc::format!("{:?}", ex_header);
|
||||||
}
|
}
|
||||||
|
@ -184,8 +154,8 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn dec_header() {
|
fn dec_header() {
|
||||||
let header = gen_header();
|
let header = gen_header();
|
||||||
let (encrypted, nonce) = header.encrypt(&[0; 32], &[0]);
|
let encrypted = header.encrypt(&[0; 32], &[0]);
|
||||||
let decrypted = Header::decrypt(&Some([1_u8; 32]), &encrypted, &nonce);
|
let decrypted = encrypted.decrypt(&Some([1u8; 32]));
|
||||||
assert_eq!(None, decrypted)
|
assert_eq!(None, decrypted)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
use hmac::{Hmac, Mac};
|
|
||||||
|
|
||||||
use sha2::Sha512;
|
|
||||||
|
|
||||||
use core::convert::TryInto;
|
use core::convert::TryInto;
|
||||||
|
use hmac::{Hmac, Mac};
|
||||||
|
use sha2::Sha512;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use crate::kdf_root::gen_ck;
|
use crate::kdf_root::gen_ck;
|
||||||
|
@ -10,14 +8,14 @@ use crate::kdf_root::gen_ck;
|
||||||
type HmacSha512 = Hmac<Sha512>;
|
type HmacSha512 = Hmac<Sha512>;
|
||||||
|
|
||||||
pub fn kdf_ck(ck: &[u8; 32]) -> ([u8; 32], [u8; 32]) {
|
pub fn kdf_ck(ck: &[u8; 32]) -> ([u8; 32], [u8; 32]) {
|
||||||
let mac = HmacSha512::new_from_slice(ck)
|
let mac = HmacSha512::new_from_slice(ck).expect("Invalid Key Length");
|
||||||
.expect("Invalid Key Length");
|
|
||||||
let result = mac.finalize().into_bytes();
|
let result = mac.finalize().into_bytes();
|
||||||
let (a, b) = result.split_at(32);
|
let (a, b) = result.split_at(32);
|
||||||
(a.try_into()
|
|
||||||
.expect("Incorrect Length"),
|
(
|
||||||
b.try_into()
|
a.try_into().expect("Incorrect Length"),
|
||||||
.expect("Incorrect Length"))
|
b.try_into().expect("Incorrect Length"),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -29,8 +27,8 @@ pub fn gen_mk() -> [u8; 32] {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::kdf_root::gen_ck;
|
|
||||||
use crate::kdf_chain::kdf_ck;
|
use crate::kdf_chain::kdf_ck;
|
||||||
|
use crate::kdf_root::gen_ck;
|
||||||
#[test]
|
#[test]
|
||||||
fn kdf_chain_ratchet() {
|
fn kdf_chain_ratchet() {
|
||||||
let ck = gen_ck();
|
let ck = gen_ck();
|
||||||
|
@ -38,4 +36,4 @@ mod tests {
|
||||||
let (_, mk2) = kdf_ck(&ck);
|
let (_, mk2) = kdf_ck(&ck);
|
||||||
assert_ne!(mk1, mk2)
|
assert_ne!(mk1, mk2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,10 @@
|
||||||
|
|
||||||
use hkdf::Hkdf;
|
|
||||||
|
|
||||||
|
|
||||||
use sha2::Sha512;
|
|
||||||
|
|
||||||
use core::convert::TryInto;
|
use core::convert::TryInto;
|
||||||
|
use hkdf::Hkdf;
|
||||||
|
use sha2::Sha512;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use crate::dh::gen_shared_secret;
|
use crate::dh::gen_shared_secret;
|
||||||
use p256::ecdh::SharedSecret;
|
use x25519_dalek::SharedSecret;
|
||||||
|
|
||||||
pub fn kdf_rk(rk: &[u8; 32], dh_out: &SharedSecret) -> ([u8; 32], [u8; 32]) {
|
pub fn kdf_rk(rk: &[u8; 32], dh_out: &SharedSecret) -> ([u8; 32], [u8; 32]) {
|
||||||
let h = Hkdf::<Sha512>::new(Some(rk), dh_out.as_bytes());
|
let h = Hkdf::<Sha512>::new(Some(rk), dh_out.as_bytes());
|
||||||
|
@ -16,10 +12,10 @@ pub fn kdf_rk(rk: &[u8; 32], dh_out: &SharedSecret) -> ([u8; 32], [u8; 32]) {
|
||||||
let info = b"Root Key Info";
|
let info = b"Root Key Info";
|
||||||
h.expand(info, &mut okm).unwrap();
|
h.expand(info, &mut okm).unwrap();
|
||||||
let (a, b) = okm.split_at(32);
|
let (a, b) = okm.split_at(32);
|
||||||
(a.try_into()
|
(
|
||||||
.expect("Incorrect length"),
|
a.try_into().expect("Incorrect length"),
|
||||||
b.try_into()
|
b.try_into().expect("Incorrect length"),
|
||||||
.expect("Incorrect length"))
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn kdf_rk_he(rk: &[u8; 32], dh_out: &SharedSecret) -> ([u8; 32], [u8; 32], [u8; 32]) {
|
pub fn kdf_rk_he(rk: &[u8; 32], dh_out: &SharedSecret) -> ([u8; 32], [u8; 32], [u8; 32]) {
|
||||||
|
@ -32,7 +28,7 @@ pub fn kdf_rk_he(rk: &[u8; 32], dh_out: &SharedSecret) -> ([u8; 32], [u8; 32], [
|
||||||
(
|
(
|
||||||
rk.try_into().expect("Wrong length"),
|
rk.try_into().expect("Wrong length"),
|
||||||
ck.try_into().expect("Wrong length"),
|
ck.try_into().expect("Wrong length"),
|
||||||
nhk.try_into().expect("Wrong length")
|
nhk.try_into().expect("Wrong length"),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,4 +53,4 @@ mod tests {
|
||||||
let (rk2, _) = kdf_rk(&rk1, &shared_secret);
|
let (rk2, _) = kdf_rk(&rk1, &shared_secret);
|
||||||
assert_ne!(rk1, rk2)
|
assert_ne!(rk1, rk2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
172
src/lib.rs
172
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!**
|
//! This implementation follows the cryptographic recommendations provided by [Signal][2].
|
||||||
//!
|
|
||||||
//! The implementation follows the cryptographic recommendations provided by [Signal][2].
|
|
||||||
//! The AEAD Algorithm uses a constant Nonce. This might be changed in the future.
|
//! The AEAD Algorithm uses a constant Nonce. This might be changed in the future.
|
||||||
//!
|
//!
|
||||||
//! # Example Usage:
|
//! Fork of [double-ratchet-2](https://github.com/Dione-Software/double-ratchet-2).
|
||||||
|
//!
|
||||||
|
//! ## Examples
|
||||||
|
//!
|
||||||
|
//! ### Standard Usage
|
||||||
|
//!
|
||||||
|
//! Alice encrypts a message which is then decrypted by Bob.
|
||||||
//!
|
//!
|
||||||
//! ## Standard:
|
|
||||||
//! ```
|
//! ```
|
||||||
//! use double_ratchet_2::ratchet::Ratchet;
|
//! use double_ratchet_rs::Ratchet;
|
||||||
//!
|
//!
|
||||||
//! let sk = [1; 32]; // Initial Key created by a symmetric key agreement protocol
|
//! let sk = [1; 32]; // Shared key created by a symmetric key agreement protocol
|
||||||
//! let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk); // Creating Bobs Ratchet (returns Bobs PublicKey)
|
//!
|
||||||
//! let mut alice_ratchet = Ratchet::init_alice(sk, public_key); // Creating Alice Ratchet with Bobs PublicKey
|
//! let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk); // Creating Bob's Ratchet (returns Bob's PublicKey)
|
||||||
//! let data = b"Hello World".to_vec(); // Data to be encrypted
|
//! let mut alice_ratchet = Ratchet::init_alice(sk, public_key); // Creating Alice's Ratchet with Bob's PublicKey
|
||||||
//! let ad = b"Associated Data"; // Associated Data
|
//!
|
||||||
|
//! let data = b"Hello World".to_vec(); // Data to be encrypted
|
||||||
|
//! let ad = b"Associated Data"; // Associated data
|
||||||
|
//!
|
||||||
|
//! let (header, encrypted, nonce) = alice_ratchet.encrypt(&data, ad); // Encrypting message with Alice's Ratchet (Alice always needs to send the first message)
|
||||||
|
//! let decrypted = bob_ratchet.decrypt(&header, &encrypted, &nonce, ad); // Decrypt message with Bob's Ratchet
|
||||||
//!
|
//!
|
||||||
//! let (header, encrypted, nonce) = alice_ratchet.ratchet_encrypt(&data, ad); // Encrypting message with Alice Ratchet (Alice always needs to send the first message)
|
|
||||||
//! let decrypted = bob_ratchet.ratchet_decrypt(&header, &encrypted, &nonce, ad); // Decrypt message with Bobs Ratchet
|
|
||||||
//! assert_eq!(data, decrypted)
|
//! assert_eq!(data, decrypted)
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! ## With lost message:
|
//! ### Recovering a Lost Message
|
||||||
|
//!
|
||||||
|
//! Alice encrypts 2 messages for Bob.
|
||||||
|
//! The latest message must be decrypted first.
|
||||||
|
//!
|
||||||
//! ```
|
//! ```
|
||||||
//! # use double_ratchet_2::ratchet::Ratchet;
|
//! use double_ratchet_rs::Ratchet;
|
||||||
//!
|
//!
|
||||||
//! let sk = [1; 32]; // Initial Key created by a symmetric key agreement protocol
|
//! let sk = [1; 32]; // Shared key created by a symmetric key agreement protocol
|
||||||
//! let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk); // Creating Bobs Ratchet (returns Bobs PublicKey)
|
|
||||||
//! let mut alice_ratchet = Ratchet::init_alice(sk, public_key); // Creating Alice Ratchet with Bobs PublicKey
|
|
||||||
//! let data = b"Hello World".to_vec(); // Data to be encrypted
|
|
||||||
//! let ad = b"Associated Data"; // Associated Data
|
|
||||||
//!
|
//!
|
||||||
//! let (header1, encrypted1, nonce1) = alice_ratchet.ratchet_encrypt(&data, ad); // Lost message
|
//! let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk); // Creating Bob's Ratchet (returns Bob's PublicKey)
|
||||||
//! let (header2, encrypted2, nonce2) = alice_ratchet.ratchet_encrypt(&data, ad); // Successful message
|
//! let mut alice_ratchet = Ratchet::init_alice(sk, public_key); // Creating Alice's Ratchet with Bob's PublicKey
|
||||||
//!
|
//!
|
||||||
//! let decrypted2 = bob_ratchet.ratchet_decrypt(&header2, &encrypted2, &nonce2, ad); // Decrypting second message first
|
//! let data = b"Hello World".to_vec(); // Data to be encrypted
|
||||||
//! let decrypted1 = bob_ratchet.ratchet_decrypt(&header1, &encrypted1, &nonce1, ad); // Decrypting latter message
|
//! let ad = b"Associated Data"; // Associated data
|
||||||
//!
|
//!
|
||||||
//! let comp = decrypted1 == data && decrypted2 == data;
|
//! let (header1, encrypted1, nonce1) = alice_ratchet.encrypt(&data, ad); // Lost message
|
||||||
//! assert!(comp);
|
//! let (header2, encrypted2, nonce2) = alice_ratchet.encrypt(&data, ad); // Successful message
|
||||||
|
//!
|
||||||
|
//! let decrypted2 = bob_ratchet.decrypt(&header2, &encrypted2, &nonce2, ad); // Decrypting second message first
|
||||||
|
//! let decrypted1 = bob_ratchet.decrypt(&header1, &encrypted1, &nonce1, ad); // Decrypting latter message
|
||||||
|
//!
|
||||||
|
//! assert_eq!(data, decrypted1);
|
||||||
|
//! assert_eq!(data, decrypted2);
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! ## Encryption before recieving inital message
|
//! ### Encryption Before Decrypting First Message
|
||||||
|
//!
|
||||||
|
//! Bob encrypts a message before decrypting one from Alice.
|
||||||
|
//! This will result in a panic.
|
||||||
//!
|
//!
|
||||||
//! ```should_panic
|
//! ```should_panic
|
||||||
//! use double_ratchet_2::ratchet::Ratchet;
|
//! use double_ratchet_rs::Ratchet;
|
||||||
|
//!
|
||||||
//! let sk = [1; 32];
|
//! let sk = [1; 32];
|
||||||
//! let ad = b"Associated Data";
|
//!
|
||||||
//! let (mut bob_ratchet, _) = Ratchet::init_bob(sk);
|
//! let (mut bob_ratchet, _) = Ratchet::init_bob(sk);
|
||||||
|
//!
|
||||||
//! let data = b"Hello World".to_vec();
|
//! let data = b"Hello World".to_vec();
|
||||||
|
//! let ad = b"Associated Data";
|
||||||
//!
|
//!
|
||||||
//! let (_, _, _) = bob_ratchet.ratchet_encrypt(&data, ad);
|
//! let (_, _, _) = bob_ratchet.encrypt(&data, ad);
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! ## Encryption after recieving initial message
|
//! ### Encryption After Decrypting First Message
|
||||||
//! However bob can (of course) also encrypt messages. This is possible, after decrypting the first message from alice.
|
//!
|
||||||
|
//! Bob *can* also encrypt messages.
|
||||||
|
//! This is only possible after decrypting one from Alice first though.
|
||||||
//!
|
//!
|
||||||
//! ```
|
//! ```
|
||||||
//! use double_ratchet_2::ratchet::Ratchet;
|
//! use double_ratchet_rs::Ratchet;
|
||||||
|
//!
|
||||||
//! let sk = [1; 32];
|
//! let sk = [1; 32];
|
||||||
//!
|
//!
|
||||||
//! let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk);
|
//! let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk);
|
||||||
|
@ -67,71 +88,81 @@
|
||||||
//! let data = b"Hello World".to_vec();
|
//! let data = b"Hello World".to_vec();
|
||||||
//! let ad = b"Associated Data";
|
//! let ad = b"Associated Data";
|
||||||
//!
|
//!
|
||||||
//! let (header1, encrypted1, nonce1) = alice_ratchet.ratchet_encrypt(&data, ad);
|
//! let (header1, encrypted1, nonce1) = alice_ratchet.encrypt(&data, ad);
|
||||||
//! let _decrypted1 = bob_ratchet.ratchet_decrypt(&header1, &encrypted1, &nonce1, ad);
|
//! let _decrypted1 = bob_ratchet.decrypt(&header1, &encrypted1, &nonce1, ad);
|
||||||
//!
|
//!
|
||||||
//! let (header2, encrypted2, nonce2) = bob_ratchet.ratchet_encrypt(&data, ad);
|
//! let (header2, encrypted2, nonce2) = bob_ratchet.encrypt(&data, ad);
|
||||||
//! let decrypted2 = alice_ratchet.ratchet_decrypt(&header2, &encrypted2, &nonce2, ad);
|
//! let decrypted2 = alice_ratchet.decrypt(&header2, &encrypted2, &nonce2, ad);
|
||||||
//!
|
//!
|
||||||
//! assert_eq!(data, decrypted2);
|
//! assert_eq!(data, decrypted2);
|
||||||
//! ```
|
//! ```
|
||||||
//! ## Constructing and Deconstructing Headers
|
//!
|
||||||
|
//! ### Constructing and Deconstructing Headers
|
||||||
//!
|
//!
|
||||||
//! ```
|
//! ```
|
||||||
//! # use double_ratchet_2::ratchet::Ratchet;
|
//! use double_ratchet_rs::{Header, Ratchet};
|
||||||
//! # use double_ratchet_2::header::Header;
|
//!
|
||||||
//! # let sk = [1; 32];
|
//! let sk = [1; 32];
|
||||||
//! # let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk);
|
//!
|
||||||
//! # let mut alice_ratchet = Ratchet::init_alice(sk, public_key);
|
//! let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk);
|
||||||
//! # let data = b"hello World".to_vec();
|
//! let mut alice_ratchet = Ratchet::init_alice(sk, public_key);
|
||||||
//! # let ad = b"Associated Data";
|
//!
|
||||||
//! # let (header, _, _) = alice_ratchet.ratchet_encrypt(&data, ad);
|
//! let data = b"hello World".to_vec();
|
||||||
|
//! let ad = b"Associated Data";
|
||||||
|
//!
|
||||||
|
//! let (header, _, _) = alice_ratchet.encrypt(&data, ad);
|
||||||
//! let header_bytes: Vec<u8> = header.clone().into();
|
//! let header_bytes: Vec<u8> = header.clone().into();
|
||||||
//! let header_const = Header::from(header_bytes);
|
//! let header_const = Header::from(header_bytes);
|
||||||
|
//!
|
||||||
//! assert_eq!(header, header_const);
|
//! assert_eq!(header, header_const);
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! # Example Ratchet with encrypted headers
|
//! ### Encrypted Headers
|
||||||
//!
|
//!
|
||||||
//! ```
|
//! ```
|
||||||
//! use double_ratchet_2::ratchet::RatchetEncHeader;
|
//! use double_ratchet_rs::RatchetEncHeader;
|
||||||
|
//!
|
||||||
//! let sk = [0; 32];
|
//! let sk = [0; 32];
|
||||||
//! let shared_hka = [1; 32];
|
//! let shared_hka = [1; 32];
|
||||||
//! let shared_nhkb = [2; 32];
|
//! let shared_nhkb = [2; 32];
|
||||||
//!
|
//!
|
||||||
//! let (mut bob_ratchet, public_key) = RatchetEncHeader::init_bob(sk, shared_hka, shared_nhkb);
|
//! let (mut bob_ratchet, public_key) = RatchetEncHeader::init_bob(sk, shared_hka, shared_nhkb);
|
||||||
//! let mut alice_ratchet = RatchetEncHeader::init_alice(sk, public_key, shared_hka, shared_nhkb);
|
//! let mut alice_ratchet = RatchetEncHeader::init_alice(sk, public_key, shared_hka, shared_nhkb);
|
||||||
|
//!
|
||||||
//! let data = b"Hello World".to_vec();
|
//! let data = b"Hello World".to_vec();
|
||||||
//! let ad = b"Associated Data";
|
//! let ad = b"Associated Data";
|
||||||
//!
|
//!
|
||||||
//! let (header, encrypted, nonce) = alice_ratchet.ratchet_encrypt(&data, ad);
|
//! let (header, encrypted, nonce) = alice_ratchet.encrypt(&data, ad);
|
||||||
//! let decrypted = bob_ratchet.ratchet_decrypt(&header, &encrypted, &nonce, ad);
|
//! let decrypted = bob_ratchet.decrypt(&header, &encrypted, &nonce, ad);
|
||||||
|
//!
|
||||||
//! assert_eq!(data, decrypted)
|
//! assert_eq!(data, decrypted)
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! # Export / Import Ratchet with encrypted headers
|
//! ### Exporting / Importing Ratchet w/ Encrypted Headers
|
||||||
//! This ratchet implements import and export functionality. This works over a bincode backend and
|
//!
|
||||||
//! maybe useful for saving Ratchets to and loading from a file.
|
//! This can be used for storing and using ratchets in a file.
|
||||||
|
//!
|
||||||
//! ```
|
//! ```
|
||||||
//! # use double_ratchet_2::ratchet::RatchetEncHeader;
|
//! use double_ratchet_rs::RatchetEncHeader;
|
||||||
//! # let sk = [0; 32];
|
//!
|
||||||
//! # let shared_hka = [1; 32];
|
//! let sk = [0; 32];
|
||||||
//! # let shared_nhkb = [2; 32];
|
//! let shared_hka = [1; 32];
|
||||||
|
//! let shared_nhkb = [2; 32];
|
||||||
|
//!
|
||||||
//! let (bob_ratchet, public_key) = RatchetEncHeader::init_bob(sk, shared_hka, shared_nhkb);
|
//! let (bob_ratchet, public_key) = RatchetEncHeader::init_bob(sk, shared_hka, shared_nhkb);
|
||||||
//! let ex_ratchet = bob_ratchet.export();
|
//! let ex_ratchet = bob_ratchet.export();
|
||||||
//! let im_ratchet = RatchetEncHeader::import(&ex_ratchet).unwrap();
|
//! let im_ratchet = RatchetEncHeader::import(&ex_ratchet).unwrap();
|
||||||
|
//!
|
||||||
//! assert_eq!(im_ratchet, bob_ratchet)
|
//! assert_eq!(im_ratchet, bob_ratchet)
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! # Features
|
//! ## **M**inimum **S**upported **R**ust **V**ersion (MSRV)
|
||||||
//!
|
//!
|
||||||
//! Currently the crate only supports one feature: ring. If feature is enabled the crate switches
|
//! The current MSRV is 1.61.0.
|
||||||
//! to ring-compat and uses ring as backend for Sha512 Hashing. May result in slightly better performance.
|
|
||||||
//!
|
//!
|
||||||
|
//! ## License
|
||||||
//!
|
//!
|
||||||
//! TODO:
|
//! This project is licensed under the [MIT license](https://github.com/magnetite-dev/double-ratchet-rs/blob/main/LICENSE).
|
||||||
//! - [x] Standard Double Ratchet
|
|
||||||
//! - [x] [Double Ratchet with encrypted headers][3]
|
|
||||||
//!
|
//!
|
||||||
//! [1]: https://signal.org/docs/specifications/doubleratchet/
|
//! [1]: https://signal.org/docs/specifications/doubleratchet/
|
||||||
//! [2]: https://signal.org/docs/specifications/doubleratchet/#recommended-cryptographic-algorithms
|
//! [2]: https://signal.org/docs/specifications/doubleratchet/#recommended-cryptographic-algorithms
|
||||||
|
@ -142,15 +173,14 @@
|
||||||
|
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
pub use p256::PublicKey;
|
pub use x25519_dalek::PublicKey;
|
||||||
|
|
||||||
mod aead;
|
mod aead;
|
||||||
mod dh;
|
mod dh;
|
||||||
mod kdf_root;
|
|
||||||
mod kdf_chain;
|
mod kdf_chain;
|
||||||
|
mod kdf_root;
|
||||||
|
mod ratchet;
|
||||||
|
mod header;
|
||||||
|
|
||||||
pub mod ratchet;
|
pub use ratchet::*;
|
||||||
|
pub use header::*;
|
||||||
/// Message Header
|
|
||||||
pub mod header;
|
|
||||||
|
|
||||||
|
|
353
src/ratchet.rs
353
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 crate::dh::DhKeyPair;
|
||||||
use p256::{PublicKey, SecretKey};
|
use crate::header::{Header, EncryptedHeader};
|
||||||
use hashbrown::HashMap;
|
|
||||||
use crate::kdf_root::{kdf_rk, kdf_rk_he};
|
|
||||||
use crate::header::Header;
|
|
||||||
use alloc::vec::Vec;
|
|
||||||
use crate::kdf_chain::kdf_ck;
|
use crate::kdf_chain::kdf_ck;
|
||||||
use crate::aead::{encrypt, decrypt};
|
use crate::kdf_root::{kdf_rk, kdf_rk_he};
|
||||||
use alloc::string::{ToString, String};
|
use alloc::vec::Vec;
|
||||||
use zeroize::Zeroize;
|
use hashbrown::HashMap;
|
||||||
use rand_core::OsRng;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use x25519_dalek::PublicKey;
|
||||||
|
use zeroize::Zeroize;
|
||||||
|
|
||||||
const MAX_SKIP: usize = 100;
|
const MAX_SKIP: usize = 100;
|
||||||
|
|
||||||
type HeaderNonceCipherNonce = ((Vec<u8>, [u8; 12]), Vec<u8>, [u8; 12]);
|
/// A standard ratchet.
|
||||||
|
#[derive(Deserialize, Serialize, PartialEq, Debug)]
|
||||||
/// Object Representing Ratchet
|
|
||||||
pub struct Ratchet {
|
pub struct Ratchet {
|
||||||
dhs: DhKeyPair,
|
dhs: DhKeyPair,
|
||||||
dhr: Option<PublicKey>,
|
dhr: Option<PublicKey>,
|
||||||
|
@ -28,18 +24,14 @@ pub struct Ratchet {
|
||||||
ns: usize,
|
ns: usize,
|
||||||
nr: usize,
|
nr: usize,
|
||||||
pn: usize,
|
pn: usize,
|
||||||
mkskipped: HashMap<(Vec<u8>, usize), [u8; 32]>,
|
mkskipped: HashMap<([u8; 32], usize), [u8; 32]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Ratchet {
|
impl Zeroize for Ratchet {
|
||||||
fn drop(&mut self) {
|
fn zeroize(&mut self) {
|
||||||
if let Some(mut _d) = self.dhr {
|
|
||||||
let sk = SecretKey::random(&mut OsRng);
|
|
||||||
_d = sk.public_key()
|
|
||||||
}
|
|
||||||
self.rk.zeroize();
|
self.rk.zeroize();
|
||||||
self.ckr.zeroize();
|
|
||||||
self.cks.zeroize();
|
self.cks.zeroize();
|
||||||
|
self.ckr.zeroize();
|
||||||
self.ns.zeroize();
|
self.ns.zeroize();
|
||||||
self.nr.zeroize();
|
self.nr.zeroize();
|
||||||
self.pn.zeroize();
|
self.pn.zeroize();
|
||||||
|
@ -47,12 +39,19 @@ impl Drop for Ratchet {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Drop for Ratchet {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.zeroize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Ratchet {
|
impl Ratchet {
|
||||||
/// Init Ratchet with other [PublicKey]. Initialized second.
|
/// Initialize a [Ratchet] with a remote [PublicKey]. Initialized second.
|
||||||
|
/// Requires a shared key and a [PublicKey].
|
||||||
|
/// Returns a [Ratchet].
|
||||||
pub fn init_alice(sk: [u8; 32], bob_dh_public_key: PublicKey) -> Self {
|
pub fn init_alice(sk: [u8; 32], bob_dh_public_key: PublicKey) -> Self {
|
||||||
let dhs = DhKeyPair::new();
|
let dhs = DhKeyPair::new();
|
||||||
let (rk, cks) = kdf_rk(&sk,
|
let (rk, cks) = kdf_rk(&sk, &dhs.key_agreement(&bob_dh_public_key));
|
||||||
&dhs.key_agreement(&bob_dh_public_key));
|
|
||||||
Ratchet {
|
Ratchet {
|
||||||
dhs,
|
dhs,
|
||||||
dhr: Some(bob_dh_public_key),
|
dhr: Some(bob_dh_public_key),
|
||||||
|
@ -66,7 +65,9 @@ impl Ratchet {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Init Ratchet without other [PublicKey]. Initialized first. Returns [Ratchet] and [PublicKey].
|
/// Initialize a [Ratchet] without a remote [PublicKey]. Initialized first.
|
||||||
|
/// Requires a shared key.
|
||||||
|
/// Returns a [Ratchet] and a [PublicKey].
|
||||||
pub fn init_bob(sk: [u8; 32]) -> (Self, PublicKey) {
|
pub fn init_bob(sk: [u8; 32]) -> (Self, PublicKey) {
|
||||||
let dhs = DhKeyPair::new();
|
let dhs = DhKeyPair::new();
|
||||||
let public_key = dhs.public_key;
|
let public_key = dhs.public_key;
|
||||||
|
@ -84,22 +85,37 @@ impl Ratchet {
|
||||||
(ratchet, public_key)
|
(ratchet, public_key)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Encrypt Plaintext with [Ratchet]. Returns Message [Header] and ciphertext.
|
/// Encrypt bytes with a [Ratchet].
|
||||||
pub fn ratchet_encrypt(&mut self, plaintext: &[u8], ad: &[u8]) -> (Header, Vec<u8>, [u8; 12]) {
|
/// Requires bytes and associated bytes.
|
||||||
|
/// Returns a [Header], encrypted bytes, and a nonce.
|
||||||
|
pub fn encrypt(&mut self, data: &[u8], associated_data: &[u8]) -> (Header, Vec<u8>, [u8; 12]) {
|
||||||
let (cks, mk) = kdf_ck(&self.cks.unwrap());
|
let (cks, mk) = kdf_ck(&self.cks.unwrap());
|
||||||
self.cks = Some(cks);
|
self.cks = Some(cks);
|
||||||
let header = Header::new(&self.dhs, self.pn, self.ns);
|
let header = Header::new(&self.dhs, self.pn, self.ns);
|
||||||
self.ns += 1;
|
self.ns += 1;
|
||||||
let (encrypted_data, nonce) = encrypt(&mk, plaintext, &header.concat(ad));
|
let (encrypted_data, nonce) = encrypt(&mk, data, &header.concat(associated_data));
|
||||||
(header, encrypted_data, nonce)
|
(header, encrypted_data, nonce)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_skipped_message_keys(&mut self, header: &Header, ciphertext: &[u8], nonce: &[u8; 12], ad: &[u8]) -> Option<Vec<u8>> {
|
fn try_skipped_message_keys(
|
||||||
if self.mkskipped.contains_key(&(header.ex_public_key_bytes(), header.n)) {
|
&mut self,
|
||||||
let mk = *self.mkskipped.get(&(header.ex_public_key_bytes(), header.n))
|
header: &Header,
|
||||||
|
enc_data: &[u8],
|
||||||
|
nonce: &[u8; 12],
|
||||||
|
associated_data: &[u8],
|
||||||
|
) -> Option<Vec<u8>> {
|
||||||
|
if self
|
||||||
|
.mkskipped
|
||||||
|
.contains_key(&(header.public_key.to_bytes(), header.n))
|
||||||
|
{
|
||||||
|
let mk = *self
|
||||||
|
.mkskipped
|
||||||
|
.get(&(header.public_key.to_bytes(), header.n))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
self.mkskipped.remove(&(header.ex_public_key_bytes(), header.n)).unwrap();
|
self.mkskipped
|
||||||
Some(decrypt(&mk, ciphertext, &header.concat(ad), nonce))
|
.remove(&(header.public_key.to_bytes(), header.n))
|
||||||
|
.unwrap();
|
||||||
|
Some(decrypt(&mk, enc_data, &header.concat(associated_data), nonce))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -115,19 +131,28 @@ impl Ratchet {
|
||||||
let (ckr, mk) = kdf_ck(&d);
|
let (ckr, mk) = kdf_ck(&d);
|
||||||
self.ckr = Some(ckr);
|
self.ckr = Some(ckr);
|
||||||
d = ckr;
|
d = ckr;
|
||||||
self.mkskipped.insert((self.dhr.unwrap().to_string().as_bytes().to_vec(), self.nr), mk);
|
self.mkskipped
|
||||||
|
.insert((self.dhr.unwrap().to_bytes(), self.nr), mk);
|
||||||
self.nr += 1
|
self.nr += 1
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
}
|
||||||
None => { Err("No Ckr set") }
|
None => Err("No Ckr set"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Decrypt ciphertext with ratchet. Requires Header. Returns plaintext.
|
/// Decrypt encrypted bytes with a [Ratchet].
|
||||||
pub fn ratchet_decrypt(&mut self, header: &Header, ciphertext: &[u8], nonce: &[u8; 12], ad: &[u8]) -> Vec<u8> {
|
/// Requires a [Header], encrypted bytes, a nonce, and associated bytes.
|
||||||
let plaintext = self.try_skipped_message_keys(header, ciphertext, nonce, ad);
|
/// Returns decrypted bytes.
|
||||||
match plaintext {
|
pub fn decrypt(
|
||||||
|
&mut self,
|
||||||
|
header: &Header,
|
||||||
|
enc_data: &[u8],
|
||||||
|
nonce: &[u8; 12],
|
||||||
|
associated_data: &[u8],
|
||||||
|
) -> Vec<u8> {
|
||||||
|
let data = self.try_skipped_message_keys(header, enc_data, nonce, associated_data);
|
||||||
|
match data {
|
||||||
Some(d) => d,
|
Some(d) => d,
|
||||||
None => {
|
None => {
|
||||||
if Some(header.public_key) != self.dhr {
|
if Some(header.public_key) != self.dhr {
|
||||||
|
@ -140,7 +165,7 @@ impl Ratchet {
|
||||||
let (ckr, mk) = kdf_ck(&self.ckr.unwrap());
|
let (ckr, mk) = kdf_ck(&self.ckr.unwrap());
|
||||||
self.ckr = Some(ckr);
|
self.ckr = Some(ckr);
|
||||||
self.nr += 1;
|
self.nr += 1;
|
||||||
decrypt(&mk, ciphertext, &header.concat(ad), nonce)
|
decrypt(&mk, enc_data, &header.concat(associated_data), nonce)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -150,19 +175,31 @@ impl Ratchet {
|
||||||
self.ns = 0;
|
self.ns = 0;
|
||||||
self.nr = 0;
|
self.nr = 0;
|
||||||
self.dhr = Some(header.public_key);
|
self.dhr = Some(header.public_key);
|
||||||
let (rk, ckr) = kdf_rk(&self.rk,
|
let (rk, ckr) = kdf_rk(&self.rk, &self.dhs.key_agreement(&self.dhr.unwrap()));
|
||||||
&self.dhs.key_agreement(&self.dhr.unwrap()));
|
|
||||||
self.rk = rk;
|
self.rk = rk;
|
||||||
self.ckr = Some(ckr);
|
self.ckr = Some(ckr);
|
||||||
self.dhs = DhKeyPair::new();
|
self.dhs = DhKeyPair::new();
|
||||||
let (rk, cks) = kdf_rk(&self.rk,
|
let (rk, cks) = kdf_rk(&self.rk, &self.dhs.key_agreement(&self.dhr.unwrap()));
|
||||||
&self.dhs.key_agreement(&self.dhr.unwrap()));
|
|
||||||
self.rk = rk;
|
self.rk = rk;
|
||||||
self.cks = Some(cks);
|
self.cks = Some(cks);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Export a [Ratchet].
|
||||||
|
/// Returns bytes.
|
||||||
|
pub fn export(&self) -> Vec<u8> {
|
||||||
|
postcard::to_allocvec(&self).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Import a previously exported [Ratchet].
|
||||||
|
/// Requires bytes.
|
||||||
|
/// Returns a [Ratchet], or nothing if invalid data is provided.
|
||||||
|
pub fn import(data: &[u8]) -> Option<Self> {
|
||||||
|
postcard::from_bytes(data).ok()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Debug)]
|
/// A [Ratchet], but with header encryption.
|
||||||
|
#[derive(Deserialize, Serialize, PartialEq, Debug)]
|
||||||
pub struct RatchetEncHeader {
|
pub struct RatchetEncHeader {
|
||||||
dhs: DhKeyPair,
|
dhs: DhKeyPair,
|
||||||
dhr: Option<PublicKey>,
|
dhr: Option<PublicKey>,
|
||||||
|
@ -176,7 +213,7 @@ pub struct RatchetEncHeader {
|
||||||
hkr: Option<[u8; 32]>,
|
hkr: Option<[u8; 32]>,
|
||||||
nhks: Option<[u8; 32]>,
|
nhks: Option<[u8; 32]>,
|
||||||
nhkr: Option<[u8; 32]>,
|
nhkr: Option<[u8; 32]>,
|
||||||
mkskipped: HashMap<(Option<[u8; 32]>, usize), [u8; 32]>
|
mkskipped: HashMap<(Option<[u8; 32]>, usize), [u8; 32]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Zeroize for RatchetEncHeader {
|
impl Zeroize for RatchetEncHeader {
|
||||||
|
@ -192,7 +229,6 @@ impl Zeroize for RatchetEncHeader {
|
||||||
self.nhks.zeroize();
|
self.nhks.zeroize();
|
||||||
self.nhkr.zeroize();
|
self.nhkr.zeroize();
|
||||||
self.mkskipped.clear();
|
self.mkskipped.clear();
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -202,90 +238,16 @@ impl Drop for RatchetEncHeader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
struct ExRatchetEncHeader {
|
|
||||||
dhs: (String, String),
|
|
||||||
dhr: Option<String>,
|
|
||||||
rk: [u8; 32],
|
|
||||||
cks: Option<[u8; 32]>,
|
|
||||||
ckr: Option<[u8; 32]>,
|
|
||||||
ns: usize,
|
|
||||||
nr: usize,
|
|
||||||
pn: usize,
|
|
||||||
hks: Option<[u8; 32]>,
|
|
||||||
hkr: Option<[u8; 32]>,
|
|
||||||
nhks: Option<[u8; 32]>,
|
|
||||||
nhkr: Option<[u8; 32]>,
|
|
||||||
mkskipped: HashMap<(Option<[u8; 32]>, usize), [u8; 32]>
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&RatchetEncHeader> for ExRatchetEncHeader {
|
|
||||||
fn from(reh: &RatchetEncHeader) -> Self {
|
|
||||||
let private_dhs = reh.dhs.private_key.to_jwk_string();
|
|
||||||
let public_dhs = reh.dhs.public_key.to_jwk_string();
|
|
||||||
let dhs = (private_dhs.to_string(), public_dhs);
|
|
||||||
let dhr = reh.dhr.map(|e| e.to_jwk_string());
|
|
||||||
let rk = reh.rk;
|
|
||||||
let cks = reh.cks;
|
|
||||||
let ckr = reh.ckr;
|
|
||||||
let ns = reh.ns;
|
|
||||||
let nr = reh.nr;
|
|
||||||
let pn = reh.pn;
|
|
||||||
let hks = reh.hks;
|
|
||||||
let hkr = reh.hkr;
|
|
||||||
let nhks = reh.nhks;
|
|
||||||
let nhkr = reh.nhkr;
|
|
||||||
let mkskipped = reh.mkskipped.clone();
|
|
||||||
Self {
|
|
||||||
dhs,
|
|
||||||
dhr,
|
|
||||||
rk,
|
|
||||||
cks,
|
|
||||||
ckr,
|
|
||||||
ns,
|
|
||||||
nr,
|
|
||||||
pn,
|
|
||||||
hks,
|
|
||||||
hkr,
|
|
||||||
nhks,
|
|
||||||
nhkr,
|
|
||||||
mkskipped
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&ExRatchetEncHeader> for RatchetEncHeader {
|
|
||||||
fn from(ex_reh: &ExRatchetEncHeader) -> Self {
|
|
||||||
let private_dhs = SecretKey::from_jwk_str(&ex_reh.dhs.0).unwrap();
|
|
||||||
let public_dhs = PublicKey::from_jwk_str(&ex_reh.dhs.1).unwrap();
|
|
||||||
let dhs = DhKeyPair {
|
|
||||||
private_key: private_dhs,
|
|
||||||
public_key: public_dhs
|
|
||||||
};
|
|
||||||
let dhr = ex_reh.dhr.as_ref().map(|e| PublicKey::from_jwk_str(e).unwrap());
|
|
||||||
Self {
|
|
||||||
dhs,
|
|
||||||
dhr,
|
|
||||||
rk: ex_reh.rk,
|
|
||||||
cks: ex_reh.cks,
|
|
||||||
ckr: ex_reh.ckr,
|
|
||||||
ns: ex_reh.ns,
|
|
||||||
nr: ex_reh.nr,
|
|
||||||
pn: ex_reh.pn,
|
|
||||||
hks: ex_reh.hks,
|
|
||||||
hkr: ex_reh.hkr,
|
|
||||||
nhks: ex_reh.nhks,
|
|
||||||
nhkr: ex_reh.nhkr,
|
|
||||||
mkskipped: ex_reh.mkskipped.clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RatchetEncHeader {
|
impl RatchetEncHeader {
|
||||||
pub fn init_alice(sk: [u8; 32],
|
/// Initialize a [RatchetEncHeader] with a remote [PublicKey]. Initialized second.
|
||||||
bob_dh_public_key: PublicKey,
|
/// Requires a shared key, a [PublicKey], a shared HKA, and a shared NHKB.
|
||||||
shared_hka: [u8; 32],
|
/// Returns a [RatchetEncHeader].
|
||||||
shared_nhkb: [u8; 32]) -> Self {
|
pub fn init_alice(
|
||||||
|
sk: [u8; 32],
|
||||||
|
bob_dh_public_key: PublicKey,
|
||||||
|
shared_hka: [u8; 32],
|
||||||
|
shared_nhkb: [u8; 32],
|
||||||
|
) -> Self {
|
||||||
let dhs = DhKeyPair::new();
|
let dhs = DhKeyPair::new();
|
||||||
let (rk, cks, nhks) = kdf_rk_he(&sk, &dhs.key_agreement(&bob_dh_public_key));
|
let (rk, cks, nhks) = kdf_rk_he(&sk, &dhs.key_agreement(&bob_dh_public_key));
|
||||||
RatchetEncHeader {
|
RatchetEncHeader {
|
||||||
|
@ -305,7 +267,14 @@ impl RatchetEncHeader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init_bob(sk: [u8; 32], shared_hka: [u8; 32], shared_nhkb: [u8; 32]) -> (Self, PublicKey) {
|
/// Initialize a [RatchetEncHeader] without a remote [PublicKey]. Initialized first.
|
||||||
|
/// Requires a shared key, a shared HKA, and a shared NHKB.
|
||||||
|
/// Returns a [RatchetEncHeader] and a [PublicKey].
|
||||||
|
pub fn init_bob(
|
||||||
|
sk: [u8; 32],
|
||||||
|
shared_hka: [u8; 32],
|
||||||
|
shared_nhkb: [u8; 32],
|
||||||
|
) -> (Self, PublicKey) {
|
||||||
let dhs = DhKeyPair::new();
|
let dhs = DhKeyPair::new();
|
||||||
let public_key = dhs.public_key;
|
let public_key = dhs.public_key;
|
||||||
let ratchet = Self {
|
let ratchet = Self {
|
||||||
|
@ -326,50 +295,70 @@ impl RatchetEncHeader {
|
||||||
(ratchet, public_key)
|
(ratchet, public_key)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ratchet_encrypt(&mut self, plaintext: &[u8], ad: &[u8]) -> HeaderNonceCipherNonce {
|
/// Encrypt bytes with a [RatchetEncHeader].
|
||||||
|
/// Requires bytes and associated bytes.
|
||||||
|
/// Returns an [EncryptedHeader], encrypted bytes, and a nonce.
|
||||||
|
pub fn encrypt(&mut self, data: &[u8], associated_data: &[u8]) -> (EncryptedHeader, Vec<u8>, [u8; 12]) {
|
||||||
let (cks, mk) = kdf_ck(&self.cks.unwrap());
|
let (cks, mk) = kdf_ck(&self.cks.unwrap());
|
||||||
self.cks = Some(cks);
|
self.cks = Some(cks);
|
||||||
let header = Header::new(&self.dhs, self.pn, self.ns);
|
let header = Header::new(&self.dhs, self.pn, self.ns);
|
||||||
let enc_header = header.encrypt(&self.hks.unwrap(), ad);
|
let enc_header = header.encrypt(&self.hks.unwrap(), associated_data);
|
||||||
self.ns += 1;
|
self.ns += 1;
|
||||||
let encrypted = encrypt(&mk, plaintext, &header.concat(ad));
|
let encrypted = encrypt(&mk, data, &header.concat(associated_data));
|
||||||
(enc_header, encrypted.0, encrypted.1)
|
(enc_header, encrypted.0, encrypted.1)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_skipped_message_keys(&mut self, enc_header: &(Vec<u8>, [u8; 12]),
|
fn try_skipped_message_keys(
|
||||||
ciphertext: &[u8], nonce: &[u8; 12], ad: &[u8]) -> (Option<Vec<u8>>, Option<Header>) {
|
&mut self,
|
||||||
|
enc_header: &EncryptedHeader,
|
||||||
|
enc_data: &[u8],
|
||||||
|
nonce: &[u8; 12],
|
||||||
|
associated_data: &[u8],
|
||||||
|
) -> (Option<Vec<u8>>, Option<Header>) {
|
||||||
let ret_data = self.mkskipped.clone().into_iter().find(|e| {
|
let ret_data = self.mkskipped.clone().into_iter().find(|e| {
|
||||||
let header = Header::decrypt(&e.0.0, &enc_header.0, &enc_header.1);
|
let header = enc_header.decrypt(&e.0.0);
|
||||||
match header {
|
match header {
|
||||||
None => false,
|
None => false,
|
||||||
Some(h) => h.n == e.0.1
|
Some(h) => h.n == e.0 .1,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
match ret_data {
|
match ret_data {
|
||||||
None => { (None, None) },
|
None => (None, None),
|
||||||
Some(data) => {
|
Some(data) => {
|
||||||
let header = Header::decrypt(&data.0.0, &enc_header.0, &enc_header.1);
|
let header = enc_header.decrypt(&data.0.0);
|
||||||
let mk = data.1;
|
let mk = data.1;
|
||||||
self.mkskipped.remove(&(data.0.0, data.0.1));
|
self.mkskipped.remove(&(data.0 .0, data.0 .1));
|
||||||
(Some(decrypt(&mk, ciphertext, &header.clone().unwrap().concat(ad), nonce)), header)
|
(
|
||||||
|
Some(decrypt(
|
||||||
|
&mk,
|
||||||
|
enc_data,
|
||||||
|
&header.clone().unwrap().concat(associated_data),
|
||||||
|
nonce,
|
||||||
|
)),
|
||||||
|
header,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decrypt_header(&mut self, enc_header: &(Vec<u8>, [u8; 12])) -> Result<(Header, bool), &str> {
|
/// Decrypt an [EncryptedHeader] with a [RatchetEncHeader].
|
||||||
let header = Header::decrypt(&self.hkr, &enc_header.0, &enc_header.1);
|
/// Requires an [EncryptedHeader].
|
||||||
if let Some(h) = header { return Ok((h, false)) };
|
/// Returns a decrypted [Header] and boolean, if decryption was successful.
|
||||||
let header = Header::decrypt(&self.nhkr, &enc_header.0, &enc_header.1);
|
fn decrypt_header(&mut self, enc_header: &EncryptedHeader) -> Result<(Header, bool), &str> {
|
||||||
|
let header = enc_header.decrypt(&self.hkr);
|
||||||
|
if let Some(h) = header {
|
||||||
|
return Ok((h, false));
|
||||||
|
};
|
||||||
|
let header = enc_header.decrypt(&self.nhkr);
|
||||||
match header {
|
match header {
|
||||||
Some(h) => Ok((h, true)),
|
Some(h) => Ok((h, true)),
|
||||||
None => Err("Header is unencryptable!")
|
None => Err("Header is unencryptable!"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn skip_message_keys(&mut self, until: usize) -> Result<(), &str> {
|
fn skip_message_keys(&mut self, until: usize) -> Result<(), &str> {
|
||||||
if self.nr + MAX_SKIP < until {
|
if self.nr + MAX_SKIP < until {
|
||||||
return Err("Skipping went wrong")
|
return Err("Skipping went wrong");
|
||||||
}
|
}
|
||||||
if let Some(d) = &mut self.ckr {
|
if let Some(d) = &mut self.ckr {
|
||||||
while self.nr < until {
|
while self.nr < until {
|
||||||
|
@ -389,22 +378,31 @@ impl RatchetEncHeader {
|
||||||
self.hks = self.nhks;
|
self.hks = self.nhks;
|
||||||
self.hkr = self.nhkr;
|
self.hkr = self.nhkr;
|
||||||
self.dhr = Some(header.public_key);
|
self.dhr = Some(header.public_key);
|
||||||
let (rk, ckr, nhkr) = kdf_rk_he(&self.rk,
|
let (rk, ckr, nhkr) = kdf_rk_he(&self.rk, &self.dhs.key_agreement(&self.dhr.unwrap()));
|
||||||
&self.dhs.key_agreement(&self.dhr.unwrap()));
|
|
||||||
self.rk = rk;
|
self.rk = rk;
|
||||||
self.ckr = Some(ckr);
|
self.ckr = Some(ckr);
|
||||||
self.nhkr = Some(nhkr);
|
self.nhkr = Some(nhkr);
|
||||||
self.dhs = DhKeyPair::new();
|
self.dhs = DhKeyPair::new();
|
||||||
let (rk, cks, nhks) = kdf_rk_he(&self.rk,
|
let (rk, cks, nhks) = kdf_rk_he(&self.rk, &self.dhs.key_agreement(&self.dhr.unwrap()));
|
||||||
&self.dhs.key_agreement(&self.dhr.unwrap()));
|
|
||||||
self.rk = rk;
|
self.rk = rk;
|
||||||
self.cks = Some(cks);
|
self.cks = Some(cks);
|
||||||
self.nhks = Some(nhks);
|
self.nhks = Some(nhks);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ratchet_decrypt(&mut self, enc_header: &(Vec<u8>, [u8; 12]), ciphertext: &[u8], nonce: &[u8; 12], ad: &[u8]) -> Vec<u8> {
|
/// Decrypt encrypted bytes with a [RatchetEncHeader].
|
||||||
let (plaintext, _) = self.try_skipped_message_keys(enc_header, ciphertext, nonce, ad);
|
/// Requires an [EncryptedHeader], encrypted bytes, a nonce, and associated bytes.
|
||||||
if let Some(d) = plaintext { return d };
|
/// Returns decrypted bytes.
|
||||||
|
pub fn decrypt(
|
||||||
|
&mut self,
|
||||||
|
enc_header: &EncryptedHeader,
|
||||||
|
enc_data: &[u8],
|
||||||
|
nonce: &[u8; 12],
|
||||||
|
associated_data: &[u8],
|
||||||
|
) -> Vec<u8> {
|
||||||
|
let (data, _) = self.try_skipped_message_keys(enc_header, enc_data, nonce, associated_data);
|
||||||
|
if let Some(d) = data {
|
||||||
|
return d;
|
||||||
|
};
|
||||||
let (header, dh_ratchet) = self.decrypt_header(enc_header).unwrap();
|
let (header, dh_ratchet) = self.decrypt_header(enc_header).unwrap();
|
||||||
if dh_ratchet {
|
if dh_ratchet {
|
||||||
self.skip_message_keys(header.pn).unwrap();
|
self.skip_message_keys(header.pn).unwrap();
|
||||||
|
@ -414,12 +412,23 @@ impl RatchetEncHeader {
|
||||||
let (ckr, mk) = kdf_ck(&self.ckr.unwrap());
|
let (ckr, mk) = kdf_ck(&self.ckr.unwrap());
|
||||||
self.ckr = Some(ckr);
|
self.ckr = Some(ckr);
|
||||||
self.nr += 1;
|
self.nr += 1;
|
||||||
decrypt(&mk, ciphertext, &header.concat(ad), nonce)
|
decrypt(&mk, enc_data, &header.concat(associated_data), nonce)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ratchet_decrypt_w_header(&mut self, enc_header: &(Vec<u8>, [u8; 12]), ciphertext: &[u8], nonce: &[u8; 12], ad: &[u8]) -> (Vec<u8>, Header) {
|
/// Decrypt encrypted bytes and an [EncryptedHeader] with a [RatchetEncHeader].
|
||||||
let (plaintext, header) = self.try_skipped_message_keys(enc_header, ciphertext, nonce, ad);
|
/// Requires an [EncryptedHeader], encrypted bytes, a nonce, and associated bytes.
|
||||||
if let Some(d) = plaintext { return (d, header.unwrap()) };
|
/// Returns decrypted bytes and a [Header].
|
||||||
|
pub fn decrypt_with_header(
|
||||||
|
&mut self,
|
||||||
|
enc_header: &EncryptedHeader,
|
||||||
|
enc_data: &[u8],
|
||||||
|
nonce: &[u8; 12],
|
||||||
|
associated_data: &[u8],
|
||||||
|
) -> (Vec<u8>, Header) {
|
||||||
|
let (data, header) = self.try_skipped_message_keys(enc_header, enc_data, nonce, associated_data);
|
||||||
|
if let Some(d) = data {
|
||||||
|
return (d, header.unwrap());
|
||||||
|
};
|
||||||
let (header, dh_ratchet) = self.decrypt_header(enc_header).unwrap();
|
let (header, dh_ratchet) = self.decrypt_header(enc_header).unwrap();
|
||||||
if dh_ratchet {
|
if dh_ratchet {
|
||||||
self.skip_message_keys(header.pn).unwrap();
|
self.skip_message_keys(header.pn).unwrap();
|
||||||
|
@ -429,18 +438,22 @@ impl RatchetEncHeader {
|
||||||
let (ckr, mk) = kdf_ck(&self.ckr.unwrap());
|
let (ckr, mk) = kdf_ck(&self.ckr.unwrap());
|
||||||
self.ckr = Some(ckr);
|
self.ckr = Some(ckr);
|
||||||
self.nr += 1;
|
self.nr += 1;
|
||||||
(decrypt(&mk, ciphertext, &header.concat(ad), nonce), header)
|
(
|
||||||
|
decrypt(&mk, enc_data, &header.concat(associated_data), nonce),
|
||||||
|
header,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Export the ratchet to Binary data
|
/// Export a [RatchetEncHeader].
|
||||||
|
/// Returns bytes.
|
||||||
pub fn export(&self) -> Vec<u8> {
|
pub fn export(&self) -> Vec<u8> {
|
||||||
let ex: ExRatchetEncHeader = self.into();
|
postcard::to_allocvec(&self).unwrap()
|
||||||
bincode::serialize(&ex).unwrap()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Import the ratchet from Binary data. Panics when binary data is invalid.
|
/// Import a previously exported [RatchetEncHeader].
|
||||||
pub fn import(inp: &[u8]) -> Option<Self> {
|
/// Requires bytes.
|
||||||
let ex: ExRatchetEncHeader = bincode::deserialize(inp).ok()?;
|
/// Returns a [RatchetEncHeader], or nothing if invalid data is provided.
|
||||||
Some(RatchetEncHeader::from(&ex))
|
pub fn import(data: &[u8]) -> Option<Self> {
|
||||||
|
postcard::from_bytes(data).ok()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
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;
|
extern crate alloc;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn ratchet_init() {
|
fn init() {
|
||||||
let sk = [1; 32];
|
let sk = [1; 32];
|
||||||
let (_bob_ratchet, public_key) = Ratchet::init_bob(sk);
|
let (_bob_ratchet, public_key) = Ratchet::init_bob(sk);
|
||||||
let _alice_ratchet = Ratchet::init_alice(sk, public_key);
|
let _alice_ratchet = Ratchet::init_alice(sk, public_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn ratchet_enc_single() {
|
fn enc_single() {
|
||||||
let sk = [1; 32];
|
let sk = [1; 32];
|
||||||
let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk);
|
let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk);
|
||||||
let mut alice_ratchet = Ratchet::init_alice(sk, public_key);
|
let mut alice_ratchet = Ratchet::init_alice(sk, public_key);
|
||||||
let data = include_bytes!("../src/header.rs").to_vec();
|
let data = include_bytes!("../src/header.rs").to_vec();
|
||||||
let (header, encrypted, nonce) = alice_ratchet.ratchet_encrypt(&data, b"");
|
let (header, encrypted, nonce) = alice_ratchet.encrypt(&data, b"");
|
||||||
let decrypted = bob_ratchet.ratchet_decrypt(&header, &encrypted, &nonce, b"");
|
let decrypted = bob_ratchet.decrypt(&header, &encrypted, &nonce, b"");
|
||||||
assert_eq!(data, decrypted)
|
assert_eq!(data, decrypted)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn ratchet_enc_skip() {
|
fn enc_skip() {
|
||||||
let sk = [1; 32];
|
let sk = [1; 32];
|
||||||
let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk);
|
let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk);
|
||||||
let mut alice_ratchet = Ratchet::init_alice(sk, public_key);
|
let mut alice_ratchet = Ratchet::init_alice(sk, public_key);
|
||||||
let data = include_bytes!("../src/header.rs").to_vec();
|
let data = include_bytes!("../src/header.rs").to_vec();
|
||||||
let (header1, encrypted1, nonce1) = alice_ratchet.ratchet_encrypt(&data, b"");
|
let (header1, encrypted1, nonce1) = alice_ratchet.encrypt(&data, b"");
|
||||||
let (header2, encrypted2, nonce2) = alice_ratchet.ratchet_encrypt(&data, b"");
|
let (header2, encrypted2, nonce2) = alice_ratchet.encrypt(&data, b"");
|
||||||
let (header3, encrypted3, nonce3) = alice_ratchet.ratchet_encrypt(&data, b"");
|
let (header3, encrypted3, nonce3) = alice_ratchet.encrypt(&data, b"");
|
||||||
let decrypted3 = bob_ratchet.ratchet_decrypt(&header3, &encrypted3, &nonce3, b"");
|
let decrypted3 = bob_ratchet.decrypt(&header3, &encrypted3, &nonce3, b"");
|
||||||
let decrypted2 = bob_ratchet.ratchet_decrypt(&header2, &encrypted2, &nonce2, b"");
|
let decrypted2 = bob_ratchet.decrypt(&header2, &encrypted2, &nonce2, b"");
|
||||||
let decrypted1 = bob_ratchet.ratchet_decrypt(&header1, &encrypted1, &nonce1, b"");
|
let decrypted1 = bob_ratchet.decrypt(&header1, &encrypted1, &nonce1, b"");
|
||||||
let comp_res = decrypted1 == data && decrypted2 == data && decrypted3 == data;
|
let comp_res = decrypted1 == data && decrypted2 == data && decrypted3 == data;
|
||||||
assert!(comp_res)
|
assert!(comp_res)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic]
|
#[should_panic]
|
||||||
fn ratchet_panic_bob() {
|
fn panic_bob() {
|
||||||
let sk = [1; 32];
|
let sk = [1; 32];
|
||||||
let (mut bob_ratchet, _) = Ratchet::init_bob(sk);
|
let (mut bob_ratchet, _) = Ratchet::init_bob(sk);
|
||||||
let data = include_bytes!("../src/header.rs").to_vec();
|
let data = include_bytes!("../src/header.rs").to_vec();
|
||||||
let (_, _, _) = bob_ratchet.ratchet_encrypt(&data, b"");
|
let (_, _, _) = bob_ratchet.encrypt(&data, b"");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn ratchet_encryt_decrypt_four() {
|
fn encryt_decrypt_four() {
|
||||||
let sk = [1; 32];
|
let sk = [1; 32];
|
||||||
let data = include_bytes!("../src/dh.rs").to_vec();
|
let data = include_bytes!("../src/dh.rs").to_vec();
|
||||||
let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk);
|
let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk);
|
||||||
let mut alice_ratchet = Ratchet::init_alice(sk, public_key);
|
let mut alice_ratchet = Ratchet::init_alice(sk, public_key);
|
||||||
let (header1, encrypted1, nonce1) = alice_ratchet.ratchet_encrypt(&data, b"");
|
let (header1, encrypted1, nonce1) = alice_ratchet.encrypt(&data, b"");
|
||||||
let decrypted1 = bob_ratchet.ratchet_decrypt(&header1, &encrypted1, &nonce1, b"");
|
let decrypted1 = bob_ratchet.decrypt(&header1, &encrypted1, &nonce1, b"");
|
||||||
let (header2, encrypted2, nonce2) = bob_ratchet.ratchet_encrypt(&data, b"");
|
let (header2, encrypted2, nonce2) = bob_ratchet.encrypt(&data, b"");
|
||||||
let decrypted2 = alice_ratchet.ratchet_decrypt(&header2, &encrypted2, &nonce2, b"");
|
let decrypted2 = alice_ratchet.decrypt(&header2, &encrypted2, &nonce2, b"");
|
||||||
let comp_res = decrypted1 == data && decrypted2 == data;
|
let comp_res = decrypted1 == data && decrypted2 == data;
|
||||||
assert!(comp_res)
|
assert!(comp_res)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn ratchet_ench_init() {
|
fn ench_init() {
|
||||||
let sk = [1; 32];
|
let sk = [1; 32];
|
||||||
let shared_hka = [2; 32];
|
let shared_hka = [2; 32];
|
||||||
let shared_nhkb = [3; 32];
|
let shared_nhkb = [3; 32];
|
||||||
let (_bob_ratchet, public_key) = RatchetEncHeader::init_bob(sk,
|
let (_bob_ratchet, public_key) = RatchetEncHeader::init_bob(sk, shared_hka, shared_nhkb);
|
||||||
shared_hka,
|
let _alice_ratchet = RatchetEncHeader::init_alice(sk, public_key, shared_hka, shared_nhkb);
|
||||||
shared_nhkb);
|
|
||||||
let _alice_ratchet = RatchetEncHeader::init_alice(sk, public_key,
|
|
||||||
shared_hka, shared_nhkb);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn ratchet_ench_enc_single() {
|
fn ench_enc_single() {
|
||||||
let sk = [1; 32];
|
let sk = [1; 32];
|
||||||
let shared_hka = [2; 32];
|
let shared_hka = [2; 32];
|
||||||
let shared_nhkb = [3; 32];
|
let shared_nhkb = [3; 32];
|
||||||
let (mut bob_ratchet, public_key) = RatchetEncHeader::init_bob(sk,
|
let (mut bob_ratchet, public_key) = RatchetEncHeader::init_bob(sk, shared_hka, shared_nhkb);
|
||||||
shared_hka,
|
let mut alice_ratchet = RatchetEncHeader::init_alice(sk, public_key, shared_hka, shared_nhkb);
|
||||||
shared_nhkb);
|
|
||||||
let mut alice_ratchet = RatchetEncHeader::init_alice(sk,
|
|
||||||
public_key,
|
|
||||||
shared_hka,
|
|
||||||
shared_nhkb);
|
|
||||||
let data = include_bytes!("../src/header.rs").to_vec();
|
let data = include_bytes!("../src/header.rs").to_vec();
|
||||||
let (header, encrypted, nonce) = alice_ratchet.ratchet_encrypt(&data, b"");
|
let (header, encrypted, nonce) = alice_ratchet.encrypt(&data, b"");
|
||||||
let decrypted = bob_ratchet.ratchet_decrypt(&header, &encrypted, &nonce, b"");
|
let decrypted = bob_ratchet.decrypt(&header, &encrypted, &nonce, b"");
|
||||||
assert_eq!(data, decrypted)
|
assert_eq!(data, decrypted)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn ratchet_ench_enc_skip() {
|
fn ench_enc_skip() {
|
||||||
let sk = [1; 32];
|
let sk = [1; 32];
|
||||||
let shared_hka = [2; 32];
|
let shared_hka = [2; 32];
|
||||||
let shared_nhkb = [3; 32];
|
let shared_nhkb = [3; 32];
|
||||||
let (mut bob_ratchet, public_key) = RatchetEncHeader::init_bob(sk,
|
let (mut bob_ratchet, public_key) = RatchetEncHeader::init_bob(sk, shared_hka, shared_nhkb);
|
||||||
shared_hka,
|
let mut alice_ratchet = RatchetEncHeader::init_alice(sk, public_key, shared_hka, shared_nhkb);
|
||||||
shared_nhkb);
|
|
||||||
let mut alice_ratchet = RatchetEncHeader::init_alice(sk,
|
|
||||||
public_key,
|
|
||||||
shared_hka,
|
|
||||||
shared_nhkb);
|
|
||||||
let data = include_bytes!("../src/header.rs").to_vec();
|
let data = include_bytes!("../src/header.rs").to_vec();
|
||||||
let (header1, encrypted1, nonce1) = alice_ratchet.ratchet_encrypt(&data, b"");
|
let (header1, encrypted1, nonce1) = alice_ratchet.encrypt(&data, b"");
|
||||||
let (header2, encrypted2, nonce2) = alice_ratchet.ratchet_encrypt(&data, b"");
|
let (header2, encrypted2, nonce2) = alice_ratchet.encrypt(&data, b"");
|
||||||
let (header3, encrypted3, nonce3) = alice_ratchet.ratchet_encrypt(&data, b"");
|
let (header3, encrypted3, nonce3) = alice_ratchet.encrypt(&data, b"");
|
||||||
let decrypted3 = bob_ratchet.ratchet_decrypt(&header3, &encrypted3, &nonce3, b"");
|
let decrypted3 = bob_ratchet.decrypt(&header3, &encrypted3, &nonce3, b"");
|
||||||
let decrypted2 = bob_ratchet.ratchet_decrypt(&header2, &encrypted2, &nonce2, b"");
|
let decrypted2 = bob_ratchet.decrypt(&header2, &encrypted2, &nonce2, b"");
|
||||||
let decrypted1 = bob_ratchet.ratchet_decrypt(&header1, &encrypted1, &nonce1, b"");
|
let decrypted1 = bob_ratchet.decrypt(&header1, &encrypted1, &nonce1, b"");
|
||||||
let comp_res = decrypted1 == data && decrypted2 == data && decrypted3 == data;
|
let comp_res = decrypted1 == data && decrypted2 == data && decrypted3 == data;
|
||||||
assert!(comp_res)
|
assert!(comp_res)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic]
|
#[should_panic]
|
||||||
fn ratchet_ench_panic_bob() {
|
fn ench_panic_bob() {
|
||||||
let sk = [1; 32];
|
let sk = [1; 32];
|
||||||
let shared_hka = [2; 32];
|
let shared_hka = [2; 32];
|
||||||
let shared_nhkb = [3; 32];
|
let shared_nhkb = [3; 32];
|
||||||
let (mut bob_ratchet, _) = RatchetEncHeader::init_bob(sk,
|
let (mut bob_ratchet, _) = RatchetEncHeader::init_bob(sk, shared_hka, shared_nhkb);
|
||||||
shared_hka,
|
|
||||||
shared_nhkb);
|
|
||||||
let data = include_bytes!("../src/header.rs").to_vec();
|
let data = include_bytes!("../src/header.rs").to_vec();
|
||||||
let (_, _, _) = bob_ratchet.ratchet_encrypt(&data, b"");
|
let (_, _, _) = bob_ratchet.encrypt(&data, b"");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn ratchet_ench_decrypt_four() {
|
fn ench_decrypt_four() {
|
||||||
let sk = [1; 32];
|
let sk = [1; 32];
|
||||||
let shared_hka = [2; 32];
|
let shared_hka = [2; 32];
|
||||||
let shared_nhkb = [3; 32];
|
let shared_nhkb = [3; 32];
|
||||||
let (mut bob_ratchet, public_key) = RatchetEncHeader::init_bob(sk,
|
let (mut bob_ratchet, public_key) = RatchetEncHeader::init_bob(sk, shared_hka, shared_nhkb);
|
||||||
shared_hka,
|
|
||||||
shared_nhkb);
|
|
||||||
let mut alice_ratchet = RatchetEncHeader::init_alice(sk, public_key, shared_hka, shared_nhkb);
|
let mut alice_ratchet = RatchetEncHeader::init_alice(sk, public_key, shared_hka, shared_nhkb);
|
||||||
let data = include_bytes!("../src/dh.rs").to_vec();
|
let data = include_bytes!("../src/dh.rs").to_vec();
|
||||||
let (header1, encrypted1, nonce1) = alice_ratchet.ratchet_encrypt(&data, b"");
|
let (header1, encrypted1, nonce1) = alice_ratchet.encrypt(&data, b"");
|
||||||
let decrypted1 = bob_ratchet.ratchet_decrypt(&header1, &encrypted1, &nonce1, b"");
|
let decrypted1 = bob_ratchet.decrypt(&header1, &encrypted1, &nonce1, b"");
|
||||||
let (header2, encrypted2, nonce2) = bob_ratchet.ratchet_encrypt(&data, b"");
|
let (header2, encrypted2, nonce2) = bob_ratchet.encrypt(&data, b"");
|
||||||
let decrypted2 = alice_ratchet.ratchet_decrypt(&header2, &encrypted2, &nonce2, b"");
|
let decrypted2 = alice_ratchet.decrypt(&header2, &encrypted2, &nonce2, b"");
|
||||||
let comp_res = decrypted1 == data && decrypted2 == data;
|
let comp_res = decrypted1 == data && decrypted2 == data;
|
||||||
assert!(comp_res)
|
assert!(comp_res)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic]
|
#[should_panic]
|
||||||
fn ratchet_ench_enc_skip_panic() {
|
fn ench_enc_skip_panic() {
|
||||||
let sk = [1; 32];
|
let sk = [1; 32];
|
||||||
let shared_hka = [2; 32];
|
let shared_hka = [2; 32];
|
||||||
let shared_nhkb = [3; 32];
|
let shared_nhkb = [3; 32];
|
||||||
let (mut bob_ratchet, public_key) = RatchetEncHeader::init_bob(sk,
|
let (mut bob_ratchet, public_key) = RatchetEncHeader::init_bob(sk, shared_hka, shared_nhkb);
|
||||||
shared_hka,
|
let mut alice_ratchet = RatchetEncHeader::init_alice(sk, public_key, shared_hka, shared_nhkb);
|
||||||
shared_nhkb);
|
|
||||||
let mut alice_ratchet = RatchetEncHeader::init_alice(sk,
|
|
||||||
public_key,
|
|
||||||
shared_hka,
|
|
||||||
shared_nhkb);
|
|
||||||
let data = include_bytes!("../src/header.rs").to_vec();
|
let data = include_bytes!("../src/header.rs").to_vec();
|
||||||
let mut headers = alloc::vec![];
|
let mut headers = alloc::vec![];
|
||||||
let mut encrypteds = alloc::vec![];
|
let mut encrypteds = alloc::vec![];
|
||||||
let mut nonces = alloc::vec![];
|
let mut nonces = alloc::vec![];
|
||||||
let mut decrypteds = alloc::vec![];
|
let mut decrypteds = alloc::vec![];
|
||||||
for _ in 0..200 {
|
for _ in 0..200 {
|
||||||
let (header, encrypted, nonce) = alice_ratchet.ratchet_encrypt(&data, b"");
|
let (header, encrypted, nonce) = alice_ratchet.encrypt(&data, b"");
|
||||||
headers.push(header);
|
headers.push(header);
|
||||||
encrypteds.push(encrypted);
|
encrypteds.push(encrypted);
|
||||||
nonces.push(nonce);
|
nonces.push(nonce);
|
||||||
|
@ -173,7 +151,7 @@ fn ratchet_ench_enc_skip_panic() {
|
||||||
let header = headers.get(idx).unwrap();
|
let header = headers.get(idx).unwrap();
|
||||||
let encrypted = encrypteds.get(idx).unwrap();
|
let encrypted = encrypteds.get(idx).unwrap();
|
||||||
let nonce = nonces.get(idx).unwrap();
|
let nonce = nonces.get(idx).unwrap();
|
||||||
let decrypted = bob_ratchet.ratchet_decrypt(header, encrypted, nonce, b"");
|
let decrypted = bob_ratchet.decrypt(header, encrypted, nonce, b"");
|
||||||
decrypteds.push(decrypted);
|
decrypteds.push(decrypted);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -183,9 +161,7 @@ fn import_export() {
|
||||||
let sk = [1; 32];
|
let sk = [1; 32];
|
||||||
let shared_hka = [2; 32];
|
let shared_hka = [2; 32];
|
||||||
let shared_nhkb = [3; 32];
|
let shared_nhkb = [3; 32];
|
||||||
let (bob_ratchet, public_key) = RatchetEncHeader::init_bob(sk,
|
let (bob_ratchet, public_key) = RatchetEncHeader::init_bob(sk, shared_hka, shared_nhkb);
|
||||||
shared_hka,
|
|
||||||
shared_nhkb);
|
|
||||||
let alice_ratchet = RatchetEncHeader::init_alice(sk, public_key, shared_hka, shared_nhkb);
|
let alice_ratchet = RatchetEncHeader::init_alice(sk, public_key, shared_hka, shared_nhkb);
|
||||||
|
|
||||||
let ex_bob_ratchet = bob_ratchet.export();
|
let ex_bob_ratchet = bob_ratchet.export();
|
||||||
|
|
Loading…
Reference in a new issue