Compare commits
No commits in common. "df1b2129c1fc3e05173d218fe74efda14a973377" and "da4e7718907b094641fa6fea750135c5a967f79e" have entirely different histories.
df1b2129c1
...
da4e771890
20 changed files with 2054 additions and 705 deletions
2
.github/actions-rs/grcov.yml
vendored
Normal file
2
.github/actions-rs/grcov.yml
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
ignore:
|
||||||
|
- "../*"
|
||||||
11
.github/dependabot.yml
vendored
Normal file
11
.github/dependabot.yml
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
# To get started with Dependabot version updates, you'll need to specify which
|
||||||
|
# package ecosystems to update and where the package manifests are located.
|
||||||
|
# Please see the documentation for all configuration options:
|
||||||
|
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
||||||
|
|
||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: "cargo" # See documentation for possible values
|
||||||
|
directory: "/" # Location of package manifests
|
||||||
|
schedule:
|
||||||
|
interval: "daily"
|
||||||
15
.github/workflows/rust-sec.yml
vendored
Normal file
15
.github/workflows/rust-sec.yml
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
name: Security audit
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- "*"
|
||||||
|
schedule:
|
||||||
|
- cron: '0 0 * * *'
|
||||||
|
jobs:
|
||||||
|
security_audit:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v1
|
||||||
|
- uses: actions-rs/audit-check@v1
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
60
.github/workflows/rust.yml
vendored
Normal file
60
.github/workflows/rust.yml
vendored
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
name: Rust
|
||||||
|
|
||||||
|
on:
|
||||||
|
push
|
||||||
|
|
||||||
|
env:
|
||||||
|
CARGO_TERM_COLOR: always
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-and-test-native:
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
platform: [ubuntu-latest, macos-latest, windows-latest]
|
||||||
|
|
||||||
|
runs-on: ${{ matrix.platform }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
# `cargo check` command here will use installed `nightly`
|
||||||
|
# as it is set as an "override" for current directory
|
||||||
|
|
||||||
|
- name: Run cargo check
|
||||||
|
run: cargo check
|
||||||
|
|
||||||
|
- name: Run cargo test
|
||||||
|
run: cargo test
|
||||||
|
|
||||||
|
- name: Run cargo build
|
||||||
|
run: cargo build
|
||||||
|
|
||||||
|
lint:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Actions-rs
|
||||||
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: nightly
|
||||||
|
override: true
|
||||||
|
|
||||||
|
- name: Run Test
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: test
|
||||||
|
args: --all-features --no-fail-fast
|
||||||
|
env:
|
||||||
|
CARGO_INCREMENTAL: '0'
|
||||||
|
RUSTFLAGS: '-Zprofile -Ccodegen-units=1 -Cinline-threshold=0 -Clink-dead-code -Coverflow-checks=off -Cpanic=abort -Zpanic_abort_tests'
|
||||||
|
RUSTDOCFLAGS: '-Zprofile -Ccodegen-units=1 -Cinline-threshold=0 -Clink-dead-code -Coverflow-checks=off -Cpanic=abort -Zpanic_abort_tests'
|
||||||
|
|
||||||
|
- id: coverage
|
||||||
|
uses: actions-rs/grcov@v0.1
|
||||||
|
|
||||||
|
- name: Coveralls upload
|
||||||
|
uses: coverallsapp/github-action@master
|
||||||
|
with:
|
||||||
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
path-to-lcov: ${{ steps.coverage.outputs.report }}
|
||||||
16
.gitignore
vendored
16
.gitignore
vendored
|
|
@ -1,14 +1,2 @@
|
||||||
# Generated by Cargo
|
/target
|
||||||
# will have compiled files and executables
|
/.idea
|
||||||
debug/
|
|
||||||
target/
|
|
||||||
|
|
||||||
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
|
|
||||||
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
|
|
||||||
Cargo.lock
|
|
||||||
|
|
||||||
# These are backup files generated by rustfmt
|
|
||||||
**/*.rs.bk
|
|
||||||
|
|
||||||
# MSVC Windows builds of rustc generate these, which store debugging information
|
|
||||||
*.pdb
|
|
||||||
|
|
|
||||||
1109
Cargo.lock
generated
Normal file
1109
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
57
Cargo.toml
57
Cargo.toml
|
|
@ -1,39 +1,37 @@
|
||||||
[package]
|
[package]
|
||||||
name = "double-ratchet-rs"
|
name = "double-ratchet-2"
|
||||||
version = "0.4.6"
|
authors = ["Hannes Furmans"]
|
||||||
authors = ["satvrn", "Hannes Furmans", "Pascal Engélibert"]
|
description = "Implementation of Double Ratchet as specified by Signal."
|
||||||
edition = "2021"
|
homepage = "https://github.com/Dione-Software/double-ratchet-2"
|
||||||
rust-version = "1.60"
|
repository = "https://github.com/Dione-Software/double-ratchet-2"
|
||||||
description = "A pure Rust implementation of the Double Ratchet algorithm as described by Signal."
|
|
||||||
documentation = "https://docs.rs/double-ratchet-rs"
|
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
homepage = "https://git.txmn.tk/tuxmain/double-ratchet-rs"
|
keywords = ["double-ratchet", "crypto", "cryptography", "signal"]
|
||||||
repository = "https://git.txmn.tk/tuxmain/double-ratchet-rs"
|
version = "0.3.7"
|
||||||
|
edition = "2018"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
keywords = ["double-ratchet", "signal"]
|
|
||||||
categories = ["algorithms", "cryptography", "no-std"]
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[badges]
|
||||||
|
maintenance = { status = "actively-developed" }
|
||||||
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
x25519-dalek = {version = "2", default-features = false, features = ["serde", "static_secrets", "zeroize"]}
|
p256 = {version = "0.10", features = ["ecdh", "arithmetic", "pem", "jwk"]}
|
||||||
rand_core = "0.6"
|
rand_core = {version = "0.6"}
|
||||||
|
getrandom = {version = "0.2.3"}
|
||||||
hkdf = "0.12"
|
hkdf = "0.12"
|
||||||
hmac = "0.12"
|
hmac = "0.12"
|
||||||
aes-gcm-siv = "0.11"
|
aes-gcm-siv = {version = "0.10.3"}
|
||||||
sha2 = {version = "0.10", default-features = false}
|
sha2 = {version = "0.10"}
|
||||||
serde = {version = "1.0", default-features = false, features = ["derive"]}
|
serde = {version = "1", default-features = false, features = ["derive"]}
|
||||||
postcard = {version = "1.0", default-features = false, features = ["alloc"]}
|
serde_bytes = "0.11"
|
||||||
hashbrown = {version = "0.14", features = ["serde"], optional = true}
|
bincode = "1"
|
||||||
zeroize = {version = "1.6", default-features = false, features = ["zeroize_derive"]}
|
hashbrown = {version = "0.13", features = ["serde"]}
|
||||||
|
zeroize = {version = "1.3", features = ["zeroize_derive"]}
|
||||||
|
|
||||||
[target.'cfg(all(target_family = "wasm", target_vendor = "unknown"))'.dependencies]
|
[dev-dependencies]
|
||||||
getrandom = { version = "0.2", features = ["js"] }
|
criterion = "0.4.0"
|
||||||
|
|
||||||
[target.'cfg(not(target_family = "wasm"))'.dev-dependencies]
|
|
||||||
criterion = "0.4"
|
|
||||||
|
|
||||||
[target.'cfg(target_family = "wasm")'.dev-dependencies]
|
|
||||||
criterion = { version = "0.4", default-features = false, features = ["plotters", "cargo_bench_support"] }
|
|
||||||
wasm-bindgen-test = "0.2"
|
|
||||||
|
|
||||||
[[bench]]
|
[[bench]]
|
||||||
name = "double_ratchet_bench"
|
name = "double_ratchet_bench"
|
||||||
|
|
@ -43,5 +41,4 @@ harness = false
|
||||||
lto = true
|
lto = true
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["hashbrown"]
|
wasm = ["getrandom/js"]
|
||||||
std = ["sha2/std", "serde/std", "postcard/use-std", "zeroize/std"]
|
|
||||||
|
|
|
||||||
2
LICENSE
2
LICENSE
|
|
@ -1,8 +1,6 @@
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2023 satvrn
|
|
||||||
Copyright (c) 2021 Hannes Furmans
|
Copyright (c) 2021 Hannes Furmans
|
||||||
Copyright (c) 2024 Pascal Engélibert
|
|
||||||
|
|
||||||
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
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
||||||
161
README.md
161
README.md
|
|
@ -1,87 +1,71 @@
|
||||||
# double-ratchet-rs
|
[](https://crates.io/crates/double-ratchet-2)
|
||||||
|
[](https://github.com/Dione-Software/double-ratchet-2/blob/main/LICENSE)
|
||||||
|
[](https://coveralls.io/github/Dione-Software/double-ratchet-2?branch=main)
|
||||||
|
[](https://github.com/Dione-Software/double-ratchet-2/actions/workflows/rust.yml)
|
||||||
|

|
||||||
|
|
||||||
A pure Rust implementation of the Double Ratchet algorithm as described by [Signal][1].
|
# double-ratchet-2
|
||||||
|
|
||||||
This implementation follows the cryptographic recommendations provided by [Signal][2].
|
Implementation of the double ratchet system/encryption as specified by [Signal][1].
|
||||||
The AEAD algorithm uses a constant Nonce. This might be changed in the future.
|
|
||||||
|
|
||||||
Temporary fork of [double-ratchet-rs](https://github.com/notsatvrn/double-ratchet-rs), which is published on crates.io. Use this one instead because my fork is published for a proof of concept only, not meant to stay forever.
|
**WARNING! This implementation uses P-256 NOT Curve25519 as specified by Signal!**
|
||||||
|
|
||||||
## Examples
|
The implementation follows the cryptographic recommendations provided by [Signal][2].
|
||||||
|
The AEAD Algorithm uses a constant Nonce. This might be changed in the future.
|
||||||
|
|
||||||
### Standard Usage
|
## Example Usage:
|
||||||
|
|
||||||
Alice encrypts a message which is then decrypted by Bob.
|
|
||||||
|
|
||||||
|
### Standard:
|
||||||
```rust
|
```rust
|
||||||
use double_ratchet_rs::Ratchet;
|
use double_ratchet_2::ratchet::Ratchet;
|
||||||
|
|
||||||
let sk = [1; 32]; // Shared key created by a symmetric key agreement protocol
|
let sk = [1; 32]; // Initial Key created by a symmetric key agreement protocol
|
||||||
|
let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk); // Creating Bobs Ratchet (returns Bobs PublicKey)
|
||||||
let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk); // Creating Bob's Ratchet (returns Bob's PublicKey)
|
let mut alice_ratchet = Ratchet::init_alice(sk, public_key); // Creating Alice Ratchet with Bobs PublicKey
|
||||||
let mut alice_ratchet = Ratchet::init_alice(sk, public_key); // Creating Alice's Ratchet with Bob's PublicKey
|
let data = b"Hello World".to_vec(); // Data to be encrypted
|
||||||
|
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)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Recovering a Lost Message
|
### With 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]; // Shared key created by a symmetric key agreement protocol
|
let sk = [1; 32]; // Initial Key created by a symmetric key agreement protocol
|
||||||
|
let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk); // Creating Bobs Ratchet (returns Bobs PublicKey)
|
||||||
|
let mut alice_ratchet = Ratchet::init_alice(sk, public_key); // Creating Alice Ratchet with Bobs PublicKey
|
||||||
|
let data = b"Hello World".to_vec(); // Data to be encrypted
|
||||||
|
let ad = b"Associated Data"; // Associated Data
|
||||||
|
|
||||||
let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk); // Creating Bob's Ratchet (returns Bob's PublicKey)
|
let (header1, encrypted1, nonce1) = alice_ratchet.ratchet_encrypt(&data, ad); // Lost message
|
||||||
let mut alice_ratchet = Ratchet::init_alice(sk, public_key); // Creating Alice's Ratchet with Bob's PublicKey
|
let (header2, encrypted2, nonce2) = alice_ratchet.ratchet_encrypt(&data, ad); // Successful message
|
||||||
|
|
||||||
let data = b"Hello World".to_vec(); // Data to be encrypted
|
let decrypted2 = bob_ratchet.ratchet_decrypt(&header2, &encrypted2, &nonce2, ad); // Decrypting second message first
|
||||||
let ad = b"Associated Data"; // Associated data
|
let decrypted1 = bob_ratchet.ratchet_decrypt(&header1, &encrypted1, &nonce1, ad); // Decrypting latter message
|
||||||
|
|
||||||
let (header1, encrypted1, nonce1) = alice_ratchet.encrypt(&data, ad); // Lost message
|
let comp = decrypted1 == data && decrypted2 == data;
|
||||||
let (header2, encrypted2, nonce2) = alice_ratchet.encrypt(&data, ad); // Successful message
|
assert!(comp);
|
||||||
|
|
||||||
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 Decrypting First Message
|
### Encryption before recieving inital message
|
||||||
|
|
||||||
Bob encrypts a message before decrypting one from Alice.
|
|
||||||
This will result in a panic.
|
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use double_ratchet_rs::Ratchet;
|
use double_ratchet_2::ratchet::Ratchet;
|
||||||
|
|
||||||
let sk = [1; 32];
|
let sk = [1; 32];
|
||||||
|
|
||||||
let (mut bob_ratchet, _) = Ratchet::init_bob(sk);
|
|
||||||
|
|
||||||
let data = b"Hello World".to_vec();
|
|
||||||
let ad = b"Associated Data";
|
let ad = b"Associated Data";
|
||||||
|
let (mut bob_ratchet, _) = Ratchet::init_bob(sk);
|
||||||
|
let data = b"Hello World".to_vec();
|
||||||
|
|
||||||
let (_, _, _) = bob_ratchet.encrypt(&data, ad);
|
let (_, _, _) = bob_ratchet.ratchet_encrypt(&data, ad);
|
||||||
```
|
```
|
||||||
|
|
||||||
### Encryption After Decrypting First Message
|
### Encryption after recieving initial 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_rs::Ratchet;
|
use double_ratchet_2::ratchet::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);
|
||||||
|
|
@ -90,87 +74,64 @@ 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.encrypt(&data, ad);
|
let (header1, encrypted1, nonce1) = alice_ratchet.ratchet_encrypt(&data, ad);
|
||||||
let _decrypted1 = bob_ratchet.decrypt(&header1, &encrypted1, &nonce1, ad);
|
let _decrypted1 = bob_ratchet.ratchet_decrypt(&header1, &encrypted1, &nonce1, ad);
|
||||||
|
|
||||||
let (header2, encrypted2, nonce2) = bob_ratchet.encrypt(&data, ad);
|
let (header2, encrypted2, nonce2) = bob_ratchet.ratchet_encrypt(&data, ad);
|
||||||
let decrypted2 = alice_ratchet.decrypt(&header2, &encrypted2, &nonce2, ad);
|
let decrypted2 = alice_ratchet.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);
|
||||||
```
|
```
|
||||||
|
|
||||||
### Encrypted Headers
|
## Example Ratchet with encrypted headers
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use double_ratchet_rs::RatchetEncHeader;
|
use double_ratchet_2::ratchet::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.encrypt(&data, ad);
|
let (header, encrypted, nonce) = alice_ratchet.ratchet_encrypt(&data, ad);
|
||||||
let decrypted = bob_ratchet.decrypt(&header, &encrypted, &nonce, ad);
|
let decrypted = bob_ratchet.ratchet_decrypt(&header, &encrypted, &nonce, ad);
|
||||||
|
|
||||||
assert_eq!(data, decrypted)
|
assert_eq!(data, decrypted)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Exporting / Importing Ratchet w/ Encrypted Headers
|
## Export / Import Ratchet with encrypted headers
|
||||||
|
This ratchet implements import and export functionality. This works over a bincode backend and
|
||||||
This can be used for storing and using ratchets in a file.
|
maybe useful for saving Ratchets to and loading from 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).unwrap();
|
let im_ratchet = RatchetEncHeader::import(&ex_ratchet);
|
||||||
|
|
||||||
assert_eq!(im_ratchet, bob_ratchet)
|
assert_eq!(im_ratchet, bob_ratchet)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- `hashbrown`: Use `hashbrown` for `HashMap`. Enabled by default for `no_std` support.
|
Currently the crate only supports one feature: ring. If feature is enabled the crate switches
|
||||||
- `std`: Use `std` instead of `alloc`. Can be used with `hashbrown`, but it isn't required.
|
to ring-compat and uses ring as backend for Sha512 Hashing. May result in slightly better performance.
|
||||||
|
|
||||||
## **M**inimum **S**upported **R**ust **V**ersion (MSRV)
|
|
||||||
|
|
||||||
The current MSRV is 1.60.0 without `hashbrown` and 1.64.0 with `hashbrown`.
|
TODO:
|
||||||
|
- [x] Standard Double Ratchet
|
||||||
## License
|
- [x] [Double Ratchet with encrypted headers][3]
|
||||||
|
|
||||||
This project is licensed under the [MIT license](https://git.txmn.tk/tuxmain/double-ratchet-rs/blob/main/LICENSE).
|
|
||||||
|
|
||||||
[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
Normal file
13
README.tpl
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
[](https://crates.io/crates/double-ratchet-2)
|
||||||
|
[](https://github.com/Dione-Software/double-ratchet-2/blob/main/LICENSE)
|
||||||
|
[](https://coveralls.io/github/Dione-Software/double-ratchet-2?branch=main)
|
||||||
|
[](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 criterion::{criterion_group, criterion_main, Criterion};
|
use double_ratchet_2::ratchet::{Ratchet, RatchetEncHeader};
|
||||||
use double_ratchet_rs::{Ratchet, RatchetEncHeader};
|
use criterion::{Criterion, criterion_main, criterion_group};
|
||||||
|
|
||||||
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.encrypt(&data, b"");
|
let (header, encrypted, nonce) = alice_ratchet.ratchet_encrypt(&data, b"");
|
||||||
let _decrypted = bob_ratchet.decrypt(&header, &encrypted, &nonce, b"");
|
let _decrypted = bob_ratchet.ratchet_decrypt(&header, &encrypted, &nonce, b"");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn criterion_benchmark_1(c: &mut Criterion) {
|
fn criterion_benchmark_1(c: &mut Criterion) {
|
||||||
|
|
@ -19,99 +19,93 @@ 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.encrypt(&data, b"");
|
let (header1, encrypted1, nonce1) = alice_ratchet.ratchet_encrypt(&data, b"");
|
||||||
let (header2, encrypted2, nonce2) = alice_ratchet.encrypt(&data, b"");
|
let (header2, encrypted2, nonce2) = alice_ratchet.ratchet_encrypt(&data, b"");
|
||||||
let _decrypted2 = bob_ratchet.decrypt(&header2, &encrypted2, &nonce2, b"");
|
let _decrypted2 = bob_ratchet.ratchet_decrypt(&header2, &encrypted2, &nonce2, b"");
|
||||||
let _decrypted1 = bob_ratchet.decrypt(&header1, &encrypted1, &nonce1, b"");
|
let _decrypted1 = bob_ratchet.ratchet_decrypt(&header1, &encrypted1, &nonce1, b"");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn criterion_benchmark_2(c: &mut Criterion) {
|
fn criterion_benchmark_2(c: &mut Criterion) {
|
||||||
c.bench_function("Ratchet Enc Skip", |b| b.iter(|| ratchet_enc_skip()));
|
c.bench_function("Ratchet Enc Skip", |b| b.iter(|| ratchet_enc_skip()));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ratchet_encrypt_decrypt_four() {
|
fn ratchet_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.encrypt(&data, b"");
|
let (header1, encrypted1, nonce1) = alice_ratchet.ratchet_encrypt(&data, b"");
|
||||||
let _decrypted1 = bob_ratchet.decrypt(&header1, &encrypted1, &nonce1, b"").unwrap();
|
let _decrypted1 = bob_ratchet.ratchet_decrypt(&header1, &encrypted1, &nonce1, b"");
|
||||||
let (header2, encrypted2, nonce2) = bob_ratchet.encrypt(&data, b"");
|
let (header2, encrypted2, nonce2) = bob_ratchet.ratchet_encrypt(&data, b"");
|
||||||
let _decrypted2 = alice_ratchet.decrypt(&header2, &encrypted2, &nonce2, b"").unwrap();
|
let _decrypted2 = alice_ratchet.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| {
|
c.bench_function("Ratchet Dec Four", |b| b.iter(|| ratchet_encryt_decrypt_four()));
|
||||||
b.iter(|| ratchet_encrypt_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, shared_hka, shared_nhkb);
|
let (mut bob_ratchet, public_key) = RatchetEncHeader::init_bob(sk,
|
||||||
let mut alice_ratchet = RatchetEncHeader::init_alice(sk, public_key, shared_hka, shared_nhkb);
|
shared_hka,
|
||||||
|
shared_nhkb);
|
||||||
|
let mut alice_ratchet = RatchetEncHeader::init_alice(sk,
|
||||||
|
public_key,
|
||||||
|
shared_hka,
|
||||||
|
shared_nhkb);
|
||||||
let data = include_bytes!("../src/header.rs").to_vec();
|
let data = include_bytes!("../src/header.rs").to_vec();
|
||||||
let (header, encrypted, nonce) = alice_ratchet.encrypt(&data, b"");
|
let (header, encrypted, nonce) = alice_ratchet.ratchet_encrypt(&data, b"");
|
||||||
let decrypted = bob_ratchet.decrypt(&header, &encrypted, &nonce, b"").unwrap();
|
let decrypted = bob_ratchet.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| {
|
c.bench_function("Encrypted Header Ratchet Enc Single", |b| b.iter(|| ratchet_ench_enc_single()));
|
||||||
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, shared_hka, shared_nhkb);
|
let (mut bob_ratchet, public_key) = RatchetEncHeader::init_bob(sk,
|
||||||
let mut alice_ratchet = RatchetEncHeader::init_alice(sk, public_key, shared_hka, shared_nhkb);
|
shared_hka,
|
||||||
|
shared_nhkb);
|
||||||
|
let mut alice_ratchet = RatchetEncHeader::init_alice(sk,
|
||||||
|
public_key,
|
||||||
|
shared_hka,
|
||||||
|
shared_nhkb);
|
||||||
let data = include_bytes!("../src/header.rs").to_vec();
|
let data = include_bytes!("../src/header.rs").to_vec();
|
||||||
let (header1, encrypted1, nonce1) = alice_ratchet.encrypt(&data, b"");
|
let (header1, encrypted1, nonce1) = alice_ratchet.ratchet_encrypt(&data, b"");
|
||||||
let (header2, encrypted2, nonce2) = alice_ratchet.encrypt(&data, b"");
|
let (header2, encrypted2, nonce2) = alice_ratchet.ratchet_encrypt(&data, b"");
|
||||||
let _decrypted2 = bob_ratchet.decrypt(&header2, &encrypted2, &nonce2, b"");
|
let _decrypted2 = bob_ratchet.ratchet_decrypt(&header2, &encrypted2, &nonce2, b"");
|
||||||
let _decrypted1 = bob_ratchet.decrypt(&header1, &encrypted1, &nonce1, b"");
|
let _decrypted1 = bob_ratchet.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| {
|
c.bench_function("Encrypted Header Ratchet Enc Skip", |b| b.iter(|| ratchet_ench_enc_skip()));
|
||||||
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, 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 = include_bytes!("../src/dh.rs").to_vec();
|
let data = include_bytes!("../src/dh.rs").to_vec();
|
||||||
let (header1, encrypted1, nonce1) = alice_ratchet.encrypt(&data, b"");
|
let (header1, encrypted1, nonce1) = alice_ratchet.ratchet_encrypt(&data, b"");
|
||||||
let _decrypted1 = bob_ratchet.decrypt(&header1, &encrypted1, &nonce1, b"").unwrap();
|
let _decrypted1 = bob_ratchet.ratchet_decrypt(&header1, &encrypted1, &nonce1, b"");
|
||||||
let (header2, encrypted2, nonce2) = bob_ratchet.encrypt(&data, b"");
|
let (header2, encrypted2, nonce2) = bob_ratchet.ratchet_encrypt(&data, b"");
|
||||||
let _decrypted2 = alice_ratchet.decrypt(&header2, &encrypted2, &nonce2, b"").unwrap();
|
let _decrypted2 = alice_ratchet.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| {
|
c.bench_function("Encrypted Header Ratchet Dec Four", |b| b.iter(|| ratchet_ench_decrypt_four()));
|
||||||
b.iter(|| ratchet_ench_decrypt_four())
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
criterion_group!(
|
criterion_group!(without_enc_headerd, criterion_benchmark_1, criterion_benchmark_2, criterion_benchmark_3);
|
||||||
without_enc_headerd,
|
criterion_group!(with_enc_headerd, criterion_benchmark_4, criterion_benchmark_5, criterion_benchmark_6);
|
||||||
criterion_benchmark_1,
|
criterion_main!(without_enc_headerd, with_enc_headerd);
|
||||||
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
Normal file
204
deny.toml
Normal file
|
|
@ -0,0 +1,204 @@
|
||||||
|
# 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 = [""]
|
||||||
65
src/aead.rs
65
src/aead.rs
|
|
@ -1,62 +1,45 @@
|
||||||
use aes_gcm_siv::aead::AeadInPlace;
|
use aes_gcm_siv::{Key, Aes256GcmSiv, Nonce};
|
||||||
use aes_gcm_siv::{Aes256GcmSiv, KeyInit, Nonce};
|
use aes_gcm_siv::aead::{NewAead, AeadInPlace};
|
||||||
|
use alloc::vec::Vec;
|
||||||
use rand_core::{OsRng, RngCore};
|
use rand_core::{OsRng, RngCore};
|
||||||
|
|
||||||
#[cfg(not(feature = "std"))]
|
pub fn encrypt(mk: &[u8; 32], plaintext: &[u8], associated_data: &[u8]) -> (Vec<u8>, [u8; 12]) {
|
||||||
use alloc::vec::Vec;
|
let key = Key::from_slice(mk);
|
||||||
|
let cipher = Aes256GcmSiv::new(key);
|
||||||
pub fn encrypt(mk: &[u8; 32], data: &[u8], associated_data: &[u8]) -> (Vec<u8>, [u8; 12]) {
|
let mut nonce_data = [0_u8; 12];
|
||||||
let cipher = Aes256GcmSiv::new_from_slice(mk).expect("Encryption failure {}");
|
OsRng::fill_bytes(&mut OsRng, &mut nonce_data);
|
||||||
|
|
||||||
let mut nonce_data = [0u8; 12];
|
|
||||||
OsRng.fill_bytes(&mut nonce_data);
|
|
||||||
let nonce = Nonce::from_slice(&nonce_data);
|
let nonce = Nonce::from_slice(&nonce_data);
|
||||||
|
|
||||||
let mut buffer = Vec::new();
|
let mut buffer = Vec::new();
|
||||||
buffer.extend_from_slice(data);
|
buffer.extend_from_slice(plaintext);
|
||||||
|
|
||||||
cipher
|
|
||||||
.encrypt_in_place(nonce, associated_data, &mut buffer)
|
|
||||||
.expect("Encryption failure {}");
|
|
||||||
|
|
||||||
|
cipher.encrypt_in_place(nonce, associated_data, &mut buffer)
|
||||||
|
.expect("Encryption failed");
|
||||||
(buffer, nonce_data)
|
(buffer, nonce_data)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
pub fn decrypt(mk: &[u8; 32], ciphertext: &[u8], associated_data: &[u8], nonce: &[u8; 12]) -> Vec<u8> {
|
||||||
pub struct InvalidAd;
|
let key = Key::from_slice(mk);
|
||||||
|
let cipher = Aes256GcmSiv::new(key);
|
||||||
pub fn decrypt(
|
|
||||||
mk: &[u8; 32],
|
|
||||||
enc_data: &[u8],
|
|
||||||
associated_data: &[u8],
|
|
||||||
nonce: &[u8; 12],
|
|
||||||
) -> Result<Vec<u8>, InvalidAd> {
|
|
||||||
let cipher = Aes256GcmSiv::new_from_slice(mk).expect("unreachable");
|
|
||||||
|
|
||||||
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(enc_data);
|
buffer.extend_from_slice(ciphertext);
|
||||||
|
cipher.decrypt_in_place(nonce, associated_data, &mut buffer).expect("Decryption failure {}");
|
||||||
cipher
|
buffer
|
||||||
.decrypt_in_place(nonce, associated_data, &mut buffer)
|
|
||||||
.map_err(|_| InvalidAd)?;
|
|
||||||
|
|
||||||
Ok(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");
|
let test_data = include_bytes!("aead.rs").to_vec();
|
||||||
let associated_data = include_bytes!("lib.rs");
|
let associated_data = include_bytes!("lib.rs").to_vec();
|
||||||
let mk = gen_mk();
|
let mk = gen_mk();
|
||||||
let (enc_data, nonce) = encrypt(&mk, test_data, associated_data);
|
let (ciphertext, nonce) = encrypt(&mk, &test_data, &associated_data);
|
||||||
let data = decrypt(&mk, &enc_data, associated_data, &nonce).unwrap();
|
let plaintext = decrypt(&mk, &ciphertext, &associated_data, &nonce);
|
||||||
assert_eq!(test_data, data.as_slice())
|
assert_eq!(test_data, plaintext)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
58
src/dh.rs
58
src/dh.rs
|
|
@ -1,31 +1,43 @@
|
||||||
use core::fmt::{Debug, Formatter, Result};
|
|
||||||
use rand_core::OsRng;
|
use rand_core::OsRng;
|
||||||
use serde::{Deserialize, Serialize};
|
use core::fmt::{Debug, Formatter};
|
||||||
use x25519_dalek::{PublicKey, SharedSecret, StaticSecret};
|
use core::fmt;
|
||||||
|
use p256::PublicKey as PublicKey;
|
||||||
|
use p256::ecdh::SharedSecret;
|
||||||
|
use p256::SecretKey;
|
||||||
|
use alloc::vec::Vec;
|
||||||
|
use alloc::string::ToString;
|
||||||
|
use p256::elliptic_curve::ecdh::diffie_hellman;
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, Clone)]
|
#[derive(Clone)]
|
||||||
pub struct DhKeyPair {
|
pub struct DhKeyPair {
|
||||||
pub private_key: StaticSecret,
|
pub private_key: SecretKey,
|
||||||
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_bytes() != other.private_key.to_bytes() {
|
if self.private_key.to_be_bytes() != other.private_key.to_be_bytes() {
|
||||||
return false;
|
return false
|
||||||
}
|
}
|
||||||
if self.public_key != other.public_key {
|
if self.ex_public_key_bytes() != other.ex_public_key_bytes() {
|
||||||
return false;
|
return false
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for DhKeyPair {
|
impl Debug for DhKeyPair {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
f.debug_struct("DhKeyPair")
|
f.debug_struct("DhKeyPair")
|
||||||
.field("private_key", self.private_key.as_bytes())
|
.field("private_key", &self.private_key.to_be_bytes())
|
||||||
.field("public_key", self.public_key.as_bytes())
|
.field("public_key", &self.ex_public_key_bytes())
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -38,8 +50,8 @@ impl Default for DhKeyPair {
|
||||||
|
|
||||||
impl DhKeyPair {
|
impl DhKeyPair {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let secret = StaticSecret::random_from_rng(OsRng);
|
let secret = SecretKey::random(&mut OsRng);
|
||||||
let public = PublicKey::from(&secret);
|
let public = secret.public_key();
|
||||||
DhKeyPair {
|
DhKeyPair {
|
||||||
private_key: secret,
|
private_key: secret,
|
||||||
public_key: public,
|
public_key: public,
|
||||||
|
|
@ -47,7 +59,7 @@ impl DhKeyPair {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn key_agreement(&self, public_key: &PublicKey) -> SharedSecret {
|
pub fn key_agreement(&self, public_key: &PublicKey) -> SharedSecret {
|
||||||
self.private_key.diffie_hellman(public_key)
|
diffie_hellman(self.private_key.to_nonzero_scalar(), public_key.as_affine())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -58,9 +70,15 @@ pub fn gen_shared_secret() -> SharedSecret {
|
||||||
alice_pair.key_agreement(&bob_pair.public_key)
|
alice_pair.key_agreement(&bob_pair.public_key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub fn gen_key_pair() -> DhKeyPair {
|
||||||
|
DhKeyPair::new()
|
||||||
|
}
|
||||||
|
|
||||||
#[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() {
|
||||||
|
|
@ -78,6 +96,14 @@ 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();
|
||||||
|
|
@ -98,4 +124,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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
157
src/header.rs
157
src/header.rs
|
|
@ -1,61 +1,107 @@
|
||||||
//! Message header.
|
use p256::PublicKey;
|
||||||
|
|
||||||
use crate::aead::encrypt;
|
|
||||||
use crate::dh::DhKeyPair;
|
use crate::dh::DhKeyPair;
|
||||||
use aes_gcm_siv::aead::AeadInPlace;
|
use alloc::vec::Vec;
|
||||||
use aes_gcm_siv::{Aes256GcmSiv, KeyInit, Nonce};
|
use serde::{Serialize, Deserialize};
|
||||||
use serde::{Deserialize, Serialize};
|
use crate::aead::encrypt;
|
||||||
use x25519_dalek::PublicKey;
|
use aes_gcm_siv::{Key, Nonce, Aes256GcmSiv};
|
||||||
|
use aes_gcm_siv::aead::{NewAead, AeadInPlace};
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
use crate::dh::gen_key_pair;
|
||||||
|
use alloc::string::{ToString, String};
|
||||||
|
use core::str::FromStr;
|
||||||
use zeroize::Zeroize;
|
use zeroize::Zeroize;
|
||||||
|
|
||||||
#[cfg(not(feature = "std"))]
|
#[derive(Debug, Clone)]
|
||||||
use alloc::vec::Vec;
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Zeroize, Clone, PartialEq, Eq)]
|
|
||||||
#[zeroize(drop)]
|
|
||||||
pub struct Header {
|
pub struct Header {
|
||||||
pub ad: Vec<u8>,
|
|
||||||
pub public_key: PublicKey,
|
pub public_key: PublicKey,
|
||||||
pub pn: usize,
|
pub pn: usize, // Previous Chain Length
|
||||||
pub n: usize,
|
pub n: usize, // Message Number
|
||||||
}
|
}
|
||||||
|
|
||||||
// A message header.
|
#[derive(Serialize, Deserialize, Debug, Zeroize)]
|
||||||
|
#[zeroize(drop)]
|
||||||
|
struct ExHeader {
|
||||||
|
#[serde(with = "serde_bytes")]
|
||||||
|
ad: Vec<u8>,
|
||||||
|
public_key: Vec<u8>,
|
||||||
|
pn: usize,
|
||||||
|
n: usize
|
||||||
|
}
|
||||||
|
|
||||||
|
// Message Header
|
||||||
impl Header {
|
impl Header {
|
||||||
/// Create a new message header.
|
// #[doc(hidden)]
|
||||||
/// 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 mut header = self.clone();
|
let ex_header = ExHeader {
|
||||||
header.ad = ad.to_vec();
|
ad: ad.to_vec(),
|
||||||
postcard::to_allocvec(&header).expect("Failed to serialize Header")
|
public_key: self.public_key.to_string().as_bytes().to_vec(),
|
||||||
|
pn: self.pn,
|
||||||
|
n: self.n
|
||||||
|
};
|
||||||
|
bincode::serialize(&ex_header).expect("Failed to serialize Header")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn encrypt(&self, hk: &[u8; 32], ad: &[u8]) -> EncryptedHeader {
|
pub fn encrypt(&self, hk: &[u8; 32], ad: &[u8]) -> (Vec<u8>, [u8; 12]) {
|
||||||
let header_data = self.concat(ad);
|
let header_data = self.concat(ad);
|
||||||
let enc_header = encrypt(hk, &header_data, b"");
|
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 {
|
||||||
postcard::from_bytes(&d).unwrap()
|
let ex_header: ExHeader = bincode::deserialize(&d).unwrap();
|
||||||
|
let public_key_string = String::from_utf8(ex_header.public_key.clone()).unwrap();
|
||||||
|
Header {
|
||||||
|
public_key: PublicKey::from_str(&public_key_string).unwrap(),
|
||||||
|
pn: ex_header.pn,
|
||||||
|
n: ex_header.n,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&[u8]> for Header {
|
impl From<&[u8]> for Header {
|
||||||
fn from(d: &[u8]) -> Self {
|
fn from(d: &[u8]) -> Self {
|
||||||
postcard::from_bytes(d).unwrap()
|
let ex_header: ExHeader = bincode::deserialize(d).unwrap();
|
||||||
|
let public_key_string = String::from_utf8(ex_header.public_key.clone()).unwrap();
|
||||||
|
Header {
|
||||||
|
public_key: PublicKey::from_str(&public_key_string).unwrap(),
|
||||||
|
pn: ex_header.pn,
|
||||||
|
n: ex_header.n,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -65,46 +111,31 @@ impl From<Header> for Vec<u8> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct EncryptedHeader(Vec<u8>, [u8; 12]);
|
impl PartialEq for Header {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
impl EncryptedHeader {
|
if self.public_key == other.public_key
|
||||||
pub fn decrypt(&self, hk: &Option<[u8; 32]>) -> Option<Header> {
|
&& self.pn == other.pn
|
||||||
let key_d = match hk {
|
&& self.n == other.n {
|
||||||
None => return None,
|
return true
|
||||||
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))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub fn gen_header() -> Header {
|
pub fn gen_header() -> Header {
|
||||||
let dh_pair = DhKeyPair::new();
|
let dh_pair = gen_key_pair();
|
||||||
let pn = 10;
|
let pn = 10;
|
||||||
let n = 50;
|
let n = 50;
|
||||||
Header::new(&dh_pair, pn, n)
|
Header::new(&dh_pair, pn, n)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use x25519_dalek::PublicKey;
|
use crate::header::{gen_header, Header, ExHeader};
|
||||||
|
|
||||||
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() {
|
||||||
|
|
@ -122,7 +153,7 @@ mod tests {
|
||||||
let header_data = header.concat(b"");
|
let header_data = header.concat(b"");
|
||||||
let data = include_bytes!("aead.rs");
|
let data = include_bytes!("aead.rs");
|
||||||
let (encrypted, nonce) = encrypt(&mk, data, &header_data);
|
let (encrypted, nonce) = encrypt(&mk, data, &header_data);
|
||||||
let decrypted = decrypt(&mk, &encrypted, &header_data, &nonce).unwrap();
|
let decrypted = decrypt(&mk, &encrypted, &header_data, &nonce);
|
||||||
assert_eq!(decrypted, data.to_vec())
|
assert_eq!(decrypted, data.to_vec())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -141,11 +172,11 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn gen_ex_header() {
|
fn gen_ex_header() {
|
||||||
let ex_header = Header {
|
let ex_header = ExHeader {
|
||||||
ad: alloc::vec![0],
|
ad: alloc::vec![0],
|
||||||
public_key: PublicKey::from([1; 32]),
|
public_key: alloc::vec![1],
|
||||||
pn: 0,
|
pn: 0,
|
||||||
n: 0,
|
n: 0
|
||||||
};
|
};
|
||||||
let _string = alloc::format!("{:?}", ex_header);
|
let _string = alloc::format!("{:?}", ex_header);
|
||||||
}
|
}
|
||||||
|
|
@ -153,8 +184,8 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn dec_header() {
|
fn dec_header() {
|
||||||
let header = gen_header();
|
let header = gen_header();
|
||||||
let encrypted = header.encrypt(&[0; 32], &[0]);
|
let (encrypted, nonce) = header.encrypt(&[0; 32], &[0]);
|
||||||
let decrypted = encrypted.decrypt(&Some([1u8; 32]));
|
let decrypted = Header::decrypt(&Some([1_u8; 32]), &encrypted, &nonce);
|
||||||
assert_eq!(None, decrypted)
|
assert_eq!(None, decrypted)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,21 +1,23 @@
|
||||||
use core::convert::TryInto;
|
|
||||||
use hmac::{Hmac, Mac};
|
use hmac::{Hmac, Mac};
|
||||||
|
|
||||||
use sha2::Sha512;
|
use sha2::Sha512;
|
||||||
|
|
||||||
|
use core::convert::TryInto;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use crate::kdf_root::gen_ck;
|
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).expect("Invalid Key Length");
|
let mac = HmacSha512::new_from_slice(ck)
|
||||||
|
.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"),
|
||||||
a.try_into().expect("Incorrect Length"),
|
b.try_into()
|
||||||
b.try_into().expect("Incorrect Length"),
|
.expect("Incorrect Length"))
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
@ -27,8 +29,8 @@ pub fn gen_mk() -> [u8; 32] {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::kdf_chain::kdf_ck;
|
|
||||||
use crate::kdf_root::gen_ck;
|
use crate::kdf_root::gen_ck;
|
||||||
|
use crate::kdf_chain::kdf_ck;
|
||||||
#[test]
|
#[test]
|
||||||
fn kdf_chain_ratchet() {
|
fn kdf_chain_ratchet() {
|
||||||
let ck = gen_ck();
|
let ck = gen_ck();
|
||||||
|
|
@ -36,4 +38,4 @@ mod tests {
|
||||||
let (_, mk2) = kdf_ck(&ck);
|
let (_, mk2) = kdf_ck(&ck);
|
||||||
assert_ne!(mk1, mk2)
|
assert_ne!(mk1, mk2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,10 +1,14 @@
|
||||||
use core::convert::TryInto;
|
|
||||||
use hkdf::Hkdf;
|
use hkdf::Hkdf;
|
||||||
|
|
||||||
|
|
||||||
use sha2::Sha512;
|
use sha2::Sha512;
|
||||||
|
|
||||||
|
use core::convert::TryInto;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use crate::dh::gen_shared_secret;
|
use crate::dh::gen_shared_secret;
|
||||||
use x25519_dalek::SharedSecret;
|
use p256::ecdh::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());
|
||||||
|
|
@ -12,10 +16,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()
|
||||||
a.try_into().expect("Incorrect length"),
|
.expect("Incorrect length"),
|
||||||
b.try_into().expect("Incorrect length"),
|
b.try_into()
|
||||||
)
|
.expect("Incorrect length"))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn kdf_rk_he(rk: &[u8; 32], dh_out: &SharedSecret) -> ([u8; 32], [u8; 32], [u8; 32]) {
|
pub fn kdf_rk_he(rk: &[u8; 32], dh_out: &SharedSecret) -> ([u8; 32], [u8; 32], [u8; 32]) {
|
||||||
|
|
@ -28,7 +32,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")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -53,4 +57,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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
186
src/lib.rs
186
src/lib.rs
|
|
@ -1,85 +1,64 @@
|
||||||
//! A pure Rust implementation of the Double Ratchet algorithm as described by [Signal][1].
|
//! Implementation of the double ratchet system/encryption as specified by [Signal][1].
|
||||||
//!
|
//!
|
||||||
//! This implementation follows the cryptographic recommendations provided by [Signal][2].
|
//! **WARNING! This implementation uses P-256 NOT Curve25519 as specified by Signal!**
|
||||||
//! The AEAD algorithm uses a constant Nonce. This might be changed in the future.
|
|
||||||
//!
|
//!
|
||||||
//! Fork of [double-ratchet-2](https://github.com/Dione-Software/double-ratchet-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.
|
||||||
//!
|
//!
|
||||||
//! ## Examples
|
//! # Example Usage:
|
||||||
//!
|
|
||||||
//! ### Standard Usage
|
|
||||||
//!
|
|
||||||
//! Alice encrypts a message which is then decrypted by Bob.
|
|
||||||
//!
|
//!
|
||||||
|
//! ## Standard:
|
||||||
//! ```
|
//! ```
|
||||||
//! use double_ratchet_rs::Ratchet;
|
//! use double_ratchet_2::ratchet::Ratchet;
|
||||||
//!
|
//!
|
||||||
//! let sk = [1; 32]; // Shared key created by a symmetric key agreement protocol
|
//! let sk = [1; 32]; // Initial Key created by a symmetric key agreement protocol
|
||||||
//!
|
//! let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk); // Creating Bobs Ratchet (returns Bobs PublicKey)
|
||||||
//! let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk); // Creating Bob's Ratchet (returns Bob's PublicKey)
|
//! let mut alice_ratchet = Ratchet::init_alice(sk, public_key); // Creating Alice Ratchet with Bobs PublicKey
|
||||||
//! let mut alice_ratchet = Ratchet::init_alice(sk, public_key); // Creating Alice's Ratchet with Bob's PublicKey
|
//! let data = b"Hello World".to_vec(); // Data to be encrypted
|
||||||
//!
|
//! 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)
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! ### Recovering a Lost Message
|
//! ## With lost message:
|
||||||
//!
|
|
||||||
//! Alice encrypts 2 messages for Bob.
|
|
||||||
//! The latest message must be decrypted first.
|
|
||||||
//!
|
|
||||||
//! ```
|
//! ```
|
||||||
//! use double_ratchet_rs::Ratchet;
|
//! # use double_ratchet_2::ratchet::Ratchet;
|
||||||
//!
|
//!
|
||||||
//! let sk = [1; 32]; // Shared key created by a symmetric key agreement protocol
|
//! let sk = [1; 32]; // Initial Key created by a symmetric key agreement protocol
|
||||||
|
//! let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk); // Creating Bobs Ratchet (returns Bobs PublicKey)
|
||||||
|
//! let mut alice_ratchet = Ratchet::init_alice(sk, public_key); // Creating Alice Ratchet with Bobs PublicKey
|
||||||
|
//! let data = b"Hello World".to_vec(); // Data to be encrypted
|
||||||
|
//! let ad = b"Associated Data"; // Associated Data
|
||||||
//!
|
//!
|
||||||
//! let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk); // Creating Bob's Ratchet (returns Bob's PublicKey)
|
//! let (header1, encrypted1, nonce1) = alice_ratchet.ratchet_encrypt(&data, ad); // Lost message
|
||||||
//! let mut alice_ratchet = Ratchet::init_alice(sk, public_key); // Creating Alice's Ratchet with Bob's PublicKey
|
//! let (header2, encrypted2, nonce2) = alice_ratchet.ratchet_encrypt(&data, ad); // Successful message
|
||||||
//!
|
//!
|
||||||
//! let data = b"Hello World".to_vec(); // Data to be encrypted
|
//! let decrypted2 = bob_ratchet.ratchet_decrypt(&header2, &encrypted2, &nonce2, ad); // Decrypting second message first
|
||||||
//! let ad = b"Associated Data"; // Associated data
|
//! let decrypted1 = bob_ratchet.ratchet_decrypt(&header1, &encrypted1, &nonce1, ad); // Decrypting latter message
|
||||||
//!
|
//!
|
||||||
//! let (header1, encrypted1, nonce1) = alice_ratchet.encrypt(&data, ad); // Lost message
|
//! let comp = decrypted1 == data && decrypted2 == data;
|
||||||
//! let (header2, encrypted2, nonce2) = alice_ratchet.encrypt(&data, ad); // Successful message
|
//! assert!(comp);
|
||||||
//!
|
|
||||||
//! 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 Decrypting First Message
|
//! ## Encryption before recieving inital message
|
||||||
//!
|
|
||||||
//! Bob encrypts a message before decrypting one from Alice.
|
|
||||||
//! This will result in a panic.
|
|
||||||
//!
|
//!
|
||||||
//! ```should_panic
|
//! ```should_panic
|
||||||
//! use double_ratchet_rs::Ratchet;
|
//! use double_ratchet_2::ratchet::Ratchet;
|
||||||
//!
|
|
||||||
//! let sk = [1; 32];
|
//! let sk = [1; 32];
|
||||||
//!
|
|
||||||
//! let (mut bob_ratchet, _) = Ratchet::init_bob(sk);
|
|
||||||
//!
|
|
||||||
//! let data = b"Hello World".to_vec();
|
|
||||||
//! let ad = b"Associated Data";
|
//! let ad = b"Associated Data";
|
||||||
|
//! let (mut bob_ratchet, _) = Ratchet::init_bob(sk);
|
||||||
|
//! let data = b"Hello World".to_vec();
|
||||||
//!
|
//!
|
||||||
//! let (_, _, _) = bob_ratchet.encrypt(&data, ad);
|
//! let (_, _, _) = bob_ratchet.ratchet_encrypt(&data, ad);
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! ### Encryption After Decrypting First Message
|
//! ## Encryption after recieving initial 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_rs::Ratchet;
|
//! use double_ratchet_2::ratchet::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);
|
||||||
|
|
@ -88,109 +67,90 @@
|
||||||
//! 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.encrypt(&data, ad);
|
//! let (header1, encrypted1, nonce1) = alice_ratchet.ratchet_encrypt(&data, ad);
|
||||||
//! let _decrypted1 = bob_ratchet.decrypt(&header1, &encrypted1, &nonce1, ad);
|
//! let _decrypted1 = bob_ratchet.ratchet_decrypt(&header1, &encrypted1, &nonce1, ad);
|
||||||
//!
|
//!
|
||||||
//! let (header2, encrypted2, nonce2) = bob_ratchet.encrypt(&data, ad);
|
//! let (header2, encrypted2, nonce2) = bob_ratchet.ratchet_encrypt(&data, ad);
|
||||||
//! let decrypted2 = alice_ratchet.decrypt(&header2, &encrypted2, &nonce2, ad);
|
//! let decrypted2 = alice_ratchet.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_rs::{Header, Ratchet};
|
//! # use double_ratchet_2::ratchet::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 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 = b"hello World".to_vec();
|
||||||
//!
|
//! # let ad = b"Associated Data";
|
||||||
//! let data = b"hello World".to_vec();
|
//! # let (header, _, _) = alice_ratchet.ratchet_encrypt(&data, ad);
|
||||||
//! 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);
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! ### Encrypted Headers
|
//! # Example Ratchet with encrypted headers
|
||||||
//!
|
//!
|
||||||
//! ```
|
//! ```
|
||||||
//! use double_ratchet_rs::RatchetEncHeader;
|
//! use double_ratchet_2::ratchet::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.encrypt(&data, ad);
|
//! let (header, encrypted, nonce) = alice_ratchet.ratchet_encrypt(&data, ad);
|
||||||
//! let decrypted = bob_ratchet.decrypt(&header, &encrypted, &nonce, ad);
|
//! let decrypted = bob_ratchet.ratchet_decrypt(&header, &encrypted, &nonce, ad);
|
||||||
//!
|
|
||||||
//! assert_eq!(data, decrypted)
|
//! assert_eq!(data, decrypted)
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! ### Exporting / Importing Ratchet w/ Encrypted Headers
|
//! # Export / Import Ratchet with encrypted headers
|
||||||
//!
|
//! This ratchet implements import and export functionality. This works over a bincode backend and
|
||||||
//! This can be used for storing and using ratchets in a file.
|
//! maybe useful for saving Ratchets to and loading from a file.
|
||||||
//!
|
|
||||||
//! ```
|
//! ```
|
||||||
//! use double_ratchet_rs::RatchetEncHeader;
|
//! # use double_ratchet_2::ratchet::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 (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
|
//! # Features
|
||||||
//!
|
//!
|
||||||
//! - `hashbrown`: Use `hashbrown` for `HashMap`. Enabled by default for `no_std` support.
|
//! Currently the crate only supports one feature: ring. If feature is enabled the crate switches
|
||||||
//! - `std`: Use `std` instead of `alloc`. Can be used with `hashbrown`, but it isn't required.
|
//! to ring-compat and uses ring as backend for Sha512 Hashing. May result in slightly better performance.
|
||||||
//!
|
//!
|
||||||
//! ## **M**inimum **S**upported **R**ust **V**ersion (MSRV)
|
|
||||||
//!
|
//!
|
||||||
//! The current MSRV is 1.60.0 without `hashbrown` and 1.64.0 with `hashbrown`.
|
//! TODO:
|
||||||
//!
|
//! - [x] Standard Double Ratchet
|
||||||
//! ## License
|
//! - [x] [Double Ratchet with encrypted headers][3]
|
||||||
//!
|
|
||||||
//! This project is licensed under the [MIT license](https://github.com/notsatvrn/double-ratchet-rs/blob/main/LICENSE).
|
|
||||||
//!
|
//!
|
||||||
//! [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
|
||||||
|
|
||||||
#![cfg_attr(not(feature = "std"), no_std)]
|
#![no_std]
|
||||||
#![allow(stable_features)]
|
#![allow(stable_features)]
|
||||||
|
|
||||||
#[cfg(not(feature = "std"))]
|
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
pub use p256::PublicKey;
|
||||||
extern crate std as alloc;
|
|
||||||
|
|
||||||
pub use x25519_dalek::PublicKey;
|
|
||||||
|
|
||||||
mod aead;
|
mod aead;
|
||||||
mod dh;
|
mod dh;
|
||||||
mod header;
|
|
||||||
mod kdf_chain;
|
|
||||||
mod kdf_root;
|
mod kdf_root;
|
||||||
mod ratchet;
|
mod kdf_chain;
|
||||||
|
|
||||||
|
pub mod ratchet;
|
||||||
|
|
||||||
|
/// Message Header
|
||||||
|
pub mod header;
|
||||||
|
|
||||||
pub use dh::*;
|
|
||||||
pub use header::*;
|
|
||||||
pub use ratchet::*;
|
|
||||||
|
|
|
||||||
372
src/ratchet.rs
372
src/ratchet.rs
|
|
@ -1,25 +1,24 @@
|
||||||
//! Ratchet providing encryption and decryption.
|
//! Encryption with encrypted Headers
|
||||||
|
//!
|
||||||
|
|
||||||
use crate::aead::{decrypt, encrypt, InvalidAd};
|
|
||||||
use crate::dh::DhKeyPair;
|
use crate::dh::DhKeyPair;
|
||||||
use crate::header::{EncryptedHeader, Header};
|
use p256::{PublicKey, SecretKey};
|
||||||
use crate::kdf_chain::kdf_ck;
|
|
||||||
use crate::kdf_root::{kdf_rk, kdf_rk_he};
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use x25519_dalek::PublicKey;
|
|
||||||
use zeroize::Zeroize;
|
|
||||||
|
|
||||||
#[cfg(not(feature = "std"))]
|
|
||||||
use alloc::vec::Vec;
|
|
||||||
#[cfg(any(not(feature = "std"), feature = "hashbrown"))]
|
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
#[cfg(all(feature = "std", not(feature = "hashbrown")))]
|
use crate::kdf_root::{kdf_rk, kdf_rk_he};
|
||||||
use std::collections::HashMap;
|
use crate::header::Header;
|
||||||
|
use alloc::vec::Vec;
|
||||||
|
use crate::kdf_chain::kdf_ck;
|
||||||
|
use crate::aead::{encrypt, decrypt};
|
||||||
|
use alloc::string::{ToString, String};
|
||||||
|
use zeroize::Zeroize;
|
||||||
|
use rand_core::OsRng;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
const MAX_SKIP: usize = 100;
|
const MAX_SKIP: usize = 100;
|
||||||
|
|
||||||
/// A standard ratchet.
|
type HeaderNonceCipherNonce = ((Vec<u8>, [u8; 12]), Vec<u8>, [u8; 12]);
|
||||||
#[derive(Deserialize, Serialize, PartialEq, Debug)]
|
|
||||||
|
/// Object Representing Ratchet
|
||||||
pub struct Ratchet {
|
pub struct Ratchet {
|
||||||
dhs: DhKeyPair,
|
dhs: DhKeyPair,
|
||||||
dhr: Option<PublicKey>,
|
dhr: Option<PublicKey>,
|
||||||
|
|
@ -29,14 +28,18 @@ pub struct Ratchet {
|
||||||
ns: usize,
|
ns: usize,
|
||||||
nr: usize,
|
nr: usize,
|
||||||
pn: usize,
|
pn: usize,
|
||||||
mkskipped: HashMap<([u8; 32], usize), [u8; 32]>,
|
mkskipped: HashMap<(Vec<u8>, usize), [u8; 32]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Zeroize for Ratchet {
|
impl Drop for Ratchet {
|
||||||
fn zeroize(&mut self) {
|
fn drop(&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.cks.zeroize();
|
|
||||||
self.ckr.zeroize();
|
self.ckr.zeroize();
|
||||||
|
self.cks.zeroize();
|
||||||
self.ns.zeroize();
|
self.ns.zeroize();
|
||||||
self.nr.zeroize();
|
self.nr.zeroize();
|
||||||
self.pn.zeroize();
|
self.pn.zeroize();
|
||||||
|
|
@ -44,19 +47,12 @@ impl Zeroize for Ratchet {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Ratchet {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
self.zeroize();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Ratchet {
|
impl Ratchet {
|
||||||
/// Initialize a [Ratchet] with a remote [PublicKey]. Initialized second.
|
/// Init Ratchet with other [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, &dhs.key_agreement(&bob_dh_public_key));
|
let (rk, cks) = kdf_rk(&sk,
|
||||||
|
&dhs.key_agreement(&bob_dh_public_key));
|
||||||
Ratchet {
|
Ratchet {
|
||||||
dhs,
|
dhs,
|
||||||
dhr: Some(bob_dh_public_key),
|
dhr: Some(bob_dh_public_key),
|
||||||
|
|
@ -70,9 +66,7 @@ impl Ratchet {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initialize a [Ratchet] without a remote [PublicKey]. Initialized first.
|
/// Init Ratchet without other [PublicKey]. Initialized first. Returns [Ratchet] and [PublicKey].
|
||||||
/// 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;
|
||||||
|
|
@ -90,42 +84,22 @@ impl Ratchet {
|
||||||
(ratchet, public_key)
|
(ratchet, public_key)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Encrypt bytes with a [Ratchet].
|
/// Encrypt Plaintext with [Ratchet]. Returns Message [Header] and ciphertext.
|
||||||
/// Requires bytes and associated bytes.
|
pub fn ratchet_encrypt(&mut self, plaintext: &[u8], ad: &[u8]) -> (Header, Vec<u8>, [u8; 12]) {
|
||||||
/// 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, data, &header.concat(associated_data));
|
let (encrypted_data, nonce) = encrypt(&mk, plaintext, &header.concat(ad));
|
||||||
(header, encrypted_data, nonce)
|
(header, encrypted_data, nonce)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_skipped_message_keys(
|
fn try_skipped_message_keys(&mut self, header: &Header, ciphertext: &[u8], nonce: &[u8; 12], ad: &[u8]) -> Option<Vec<u8>> {
|
||||||
&mut self,
|
if self.mkskipped.contains_key(&(header.ex_public_key_bytes(), header.n)) {
|
||||||
header: &Header,
|
let mk = *self.mkskipped.get(&(header.ex_public_key_bytes(), header.n))
|
||||||
enc_data: &[u8],
|
|
||||||
nonce: &[u8; 12],
|
|
||||||
associated_data: &[u8],
|
|
||||||
) -> Option<Result<Vec<u8>, InvalidAd>> {
|
|
||||||
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
|
self.mkskipped.remove(&(header.ex_public_key_bytes(), header.n)).unwrap();
|
||||||
.remove(&(header.public_key.to_bytes(), header.n))
|
Some(decrypt(&mk, ciphertext, &header.concat(ad), nonce))
|
||||||
.unwrap();
|
|
||||||
Some(decrypt(
|
|
||||||
&mk,
|
|
||||||
enc_data,
|
|
||||||
&header.concat(associated_data),
|
|
||||||
nonce,
|
|
||||||
))
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
@ -141,32 +115,23 @@ 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
|
self.mkskipped.insert((self.dhr.unwrap().to_string().as_bytes().to_vec(), self.nr), mk);
|
||||||
.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 encrypted bytes with a [Ratchet].
|
/// Decrypt ciphertext with ratchet. Requires Header. Returns plaintext.
|
||||||
/// Requires a [Header], encrypted bytes, a nonce, and associated bytes.
|
pub fn ratchet_decrypt(&mut self, header: &Header, ciphertext: &[u8], nonce: &[u8; 12], ad: &[u8]) -> Vec<u8> {
|
||||||
/// Returns decrypted bytes.
|
let plaintext = self.try_skipped_message_keys(header, ciphertext, nonce, ad);
|
||||||
pub fn decrypt(
|
match plaintext {
|
||||||
&mut self,
|
|
||||||
header: &Header,
|
|
||||||
enc_data: &[u8],
|
|
||||||
nonce: &[u8; 12],
|
|
||||||
associated_data: &[u8],
|
|
||||||
) -> Result<Vec<u8>, InvalidAd> {
|
|
||||||
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 {
|
||||||
if self.ckr.is_some() {
|
if self.ckr != None {
|
||||||
self.skip_message_keys(header.pn).unwrap();
|
self.skip_message_keys(header.pn).unwrap();
|
||||||
}
|
}
|
||||||
self.dhratchet(header);
|
self.dhratchet(header);
|
||||||
|
|
@ -175,7 +140,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, enc_data, &header.concat(associated_data), nonce)
|
decrypt(&mk, ciphertext, &header.concat(ad), nonce)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -185,31 +150,19 @@ 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, &self.dhs.key_agreement(&self.dhr.unwrap()));
|
let (rk, ckr) = kdf_rk(&self.rk,
|
||||||
|
&self.dhs.key_agreement(&self.dhr.unwrap()));
|
||||||
self.rk = rk;
|
self.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, &self.dhs.key_agreement(&self.dhr.unwrap()));
|
let (rk, cks) = kdf_rk(&self.rk,
|
||||||
|
&self.dhs.key_agreement(&self.dhr.unwrap()));
|
||||||
self.rk = rk;
|
self.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()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A [Ratchet], but with header encryption.
|
#[derive(PartialEq, Debug)]
|
||||||
#[derive(Deserialize, Serialize, PartialEq, Debug)]
|
|
||||||
pub struct RatchetEncHeader {
|
pub struct RatchetEncHeader {
|
||||||
dhs: DhKeyPair,
|
dhs: DhKeyPair,
|
||||||
dhr: Option<PublicKey>,
|
dhr: Option<PublicKey>,
|
||||||
|
|
@ -223,7 +176,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 {
|
||||||
|
|
@ -239,6 +192,7 @@ impl Zeroize for RatchetEncHeader {
|
||||||
self.nhks.zeroize();
|
self.nhks.zeroize();
|
||||||
self.nhkr.zeroize();
|
self.nhkr.zeroize();
|
||||||
self.mkskipped.clear();
|
self.mkskipped.clear();
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -248,16 +202,90 @@ 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 {
|
||||||
/// Initialize a [RatchetEncHeader] with a remote [PublicKey]. Initialized second.
|
pub fn init_alice(sk: [u8; 32],
|
||||||
/// Requires a shared key, a [PublicKey], a shared HKA, and a shared NHKB.
|
bob_dh_public_key: PublicKey,
|
||||||
/// Returns a [RatchetEncHeader].
|
shared_hka: [u8; 32],
|
||||||
pub fn init_alice(
|
shared_nhkb: [u8; 32]) -> Self {
|
||||||
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 {
|
||||||
|
|
@ -277,14 +305,7 @@ impl RatchetEncHeader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initialize a [RatchetEncHeader] without a remote [PublicKey]. Initialized first.
|
pub fn init_bob(sk: [u8; 32], shared_hka: [u8; 32], shared_nhkb: [u8; 32]) -> (Self, PublicKey) {
|
||||||
/// 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 {
|
||||||
|
|
@ -305,74 +326,50 @@ impl RatchetEncHeader {
|
||||||
(ratchet, public_key)
|
(ratchet, public_key)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Encrypt bytes with a [RatchetEncHeader].
|
pub fn ratchet_encrypt(&mut self, plaintext: &[u8], ad: &[u8]) -> HeaderNonceCipherNonce {
|
||||||
/// 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(), associated_data);
|
let enc_header = header.encrypt(&self.hks.unwrap(), ad);
|
||||||
self.ns += 1;
|
self.ns += 1;
|
||||||
let encrypted = encrypt(&mk, data, &header.concat(associated_data));
|
let encrypted = encrypt(&mk, plaintext, &header.concat(ad));
|
||||||
(enc_header, encrypted.0, encrypted.1)
|
(enc_header, encrypted.0, encrypted.1)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_skipped_message_keys(
|
fn try_skipped_message_keys(&mut self, enc_header: &(Vec<u8>, [u8; 12]),
|
||||||
&mut self,
|
ciphertext: &[u8], nonce: &[u8; 12], ad: &[u8]) -> (Option<Vec<u8>>, Option<Header>) {
|
||||||
enc_header: &EncryptedHeader,
|
|
||||||
enc_data: &[u8],
|
|
||||||
nonce: &[u8; 12],
|
|
||||||
associated_data: &[u8],
|
|
||||||
) -> (Option<Result<Vec<u8>, InvalidAd>>, Option<Header>) {
|
|
||||||
let ret_data = self.mkskipped.clone().into_iter().find(|e| {
|
let ret_data = self.mkskipped.clone().into_iter().find(|e| {
|
||||||
let header = enc_header.decrypt(&e.0 .0);
|
let header = Header::decrypt(&e.0.0, &enc_header.0, &enc_header.1);
|
||||||
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 = enc_header.decrypt(&data.0 .0);
|
let header = Header::decrypt(&data.0.0, &enc_header.0, &enc_header.1);
|
||||||
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,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Decrypt an [EncryptedHeader] with a [RatchetEncHeader].
|
fn decrypt_header(&mut self, enc_header: &(Vec<u8>, [u8; 12])) -> Result<(Header, bool), &str> {
|
||||||
/// Requires an [EncryptedHeader].
|
let header = Header::decrypt(&self.hkr, &enc_header.0, &enc_header.1);
|
||||||
/// Returns a decrypted [Header] and boolean, if decryption was successful.
|
if let Some(h) = header { return Ok((h, false)) };
|
||||||
fn decrypt_header(&mut self, enc_header: &EncryptedHeader) -> Result<(Header, bool), &str> {
|
let header = Header::decrypt(&self.nhkr, &enc_header.0, &enc_header.1);
|
||||||
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 {
|
||||||
|
|
@ -392,31 +389,22 @@ 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, &self.dhs.key_agreement(&self.dhr.unwrap()));
|
let (rk, ckr, nhkr) = kdf_rk_he(&self.rk,
|
||||||
|
&self.dhs.key_agreement(&self.dhr.unwrap()));
|
||||||
self.rk = rk;
|
self.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, &self.dhs.key_agreement(&self.dhr.unwrap()));
|
let (rk, cks, nhks) = kdf_rk_he(&self.rk,
|
||||||
|
&self.dhs.key_agreement(&self.dhr.unwrap()));
|
||||||
self.rk = rk;
|
self.rk = rk;
|
||||||
self.cks = Some(cks);
|
self.cks = Some(cks);
|
||||||
self.nhks = Some(nhks);
|
self.nhks = Some(nhks);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Decrypt encrypted bytes with a [RatchetEncHeader].
|
pub fn ratchet_decrypt(&mut self, enc_header: &(Vec<u8>, [u8; 12]), ciphertext: &[u8], nonce: &[u8; 12], ad: &[u8]) -> Vec<u8> {
|
||||||
/// Requires an [EncryptedHeader], encrypted bytes, a nonce, and associated bytes.
|
let (plaintext, _) = self.try_skipped_message_keys(enc_header, ciphertext, nonce, ad);
|
||||||
/// Returns decrypted bytes.
|
if let Some(d) = plaintext { return d };
|
||||||
pub fn decrypt(
|
|
||||||
&mut self,
|
|
||||||
enc_header: &EncryptedHeader,
|
|
||||||
enc_data: &[u8],
|
|
||||||
nonce: &[u8; 12],
|
|
||||||
associated_data: &[u8],
|
|
||||||
) -> Result<Vec<u8>, InvalidAd> {
|
|
||||||
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();
|
||||||
|
|
@ -426,24 +414,12 @@ 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, enc_data, &header.concat(associated_data), nonce)
|
decrypt(&mk, ciphertext, &header.concat(ad), nonce)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Decrypt encrypted bytes and an [EncryptedHeader] with a [RatchetEncHeader].
|
pub fn ratchet_decrypt_w_header(&mut self, enc_header: &(Vec<u8>, [u8; 12]), ciphertext: &[u8], nonce: &[u8; 12], ad: &[u8]) -> (Vec<u8>, Header) {
|
||||||
/// Requires an [EncryptedHeader], encrypted bytes, a nonce, and associated bytes.
|
let (plaintext, header) = self.try_skipped_message_keys(enc_header, ciphertext, nonce, ad);
|
||||||
/// Returns decrypted bytes and a [Header].
|
if let Some(d) = plaintext { return (d, header.unwrap()) };
|
||||||
pub fn decrypt_with_header(
|
|
||||||
&mut self,
|
|
||||||
enc_header: &EncryptedHeader,
|
|
||||||
enc_data: &[u8],
|
|
||||||
nonce: &[u8; 12],
|
|
||||||
associated_data: &[u8],
|
|
||||||
) -> (Result<Vec<u8>, InvalidAd>, 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();
|
||||||
|
|
@ -453,22 +429,18 @@ 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 a [RatchetEncHeader].
|
/// Export the ratchet to Binary data
|
||||||
/// Returns bytes.
|
|
||||||
pub fn export(&self) -> Vec<u8> {
|
pub fn export(&self) -> Vec<u8> {
|
||||||
postcard::to_allocvec(&self).unwrap()
|
let ex: ExRatchetEncHeader = self.into();
|
||||||
|
bincode::serialize(&ex).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Import a previously exported [RatchetEncHeader].
|
/// Import the ratchet from Binary data. Panics when binary data is invalid.
|
||||||
/// Requires bytes.
|
pub fn import(inp: &[u8]) -> Option<Self> {
|
||||||
/// Returns a [RatchetEncHeader], or nothing if invalid data is provided.
|
let ex: ExRatchetEncHeader = bincode::deserialize(inp).ok()?;
|
||||||
pub fn import(data: &[u8]) -> Option<Self> {
|
Some(RatchetEncHeader::from(&ex))
|
||||||
postcard::from_bytes(data).ok()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
131
tests/mod.rs
131
tests/mod.rs
|
|
@ -1,150 +1,167 @@
|
||||||
use double_ratchet_rs::{Ratchet, RatchetEncHeader};
|
use double_ratchet_2::ratchet::{Ratchet, RatchetEncHeader};
|
||||||
|
|
||||||
#[cfg(not(feature = "std"))]
|
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
extern crate std as alloc;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn init() {
|
fn ratchet_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 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.encrypt(&data, b"");
|
let (header, encrypted, nonce) = alice_ratchet.ratchet_encrypt(&data, b"");
|
||||||
let decrypted = bob_ratchet.decrypt(&header, &encrypted, &nonce, b"").unwrap();
|
let decrypted = bob_ratchet.ratchet_decrypt(&header, &encrypted, &nonce, b"");
|
||||||
assert_eq!(data, decrypted)
|
assert_eq!(data, decrypted)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn enc_skip() {
|
fn ratchet_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.encrypt(&data, b"");
|
let (header1, encrypted1, nonce1) = alice_ratchet.ratchet_encrypt(&data, b"");
|
||||||
let (header2, encrypted2, nonce2) = alice_ratchet.encrypt(&data, b"");
|
let (header2, encrypted2, nonce2) = alice_ratchet.ratchet_encrypt(&data, b"");
|
||||||
let (header3, encrypted3, nonce3) = alice_ratchet.encrypt(&data, b"");
|
let (header3, encrypted3, nonce3) = alice_ratchet.ratchet_encrypt(&data, b"");
|
||||||
let decrypted3 = bob_ratchet.decrypt(&header3, &encrypted3, &nonce3, b"").unwrap();
|
let decrypted3 = bob_ratchet.ratchet_decrypt(&header3, &encrypted3, &nonce3, b"");
|
||||||
let decrypted2 = bob_ratchet.decrypt(&header2, &encrypted2, &nonce2, b"").unwrap();
|
let decrypted2 = bob_ratchet.ratchet_decrypt(&header2, &encrypted2, &nonce2, b"");
|
||||||
let decrypted1 = bob_ratchet.decrypt(&header1, &encrypted1, &nonce1, b"").unwrap();
|
let decrypted1 = bob_ratchet.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 panic_bob() {
|
fn ratchet_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.encrypt(&data, b"");
|
let (_, _, _) = bob_ratchet.ratchet_encrypt(&data, b"");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn encryt_decrypt_four() {
|
fn ratchet_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.encrypt(&data, b"");
|
let (header1, encrypted1, nonce1) = alice_ratchet.ratchet_encrypt(&data, b"");
|
||||||
let decrypted1 = bob_ratchet.decrypt(&header1, &encrypted1, &nonce1, b"").unwrap();
|
let decrypted1 = bob_ratchet.ratchet_decrypt(&header1, &encrypted1, &nonce1, b"");
|
||||||
let (header2, encrypted2, nonce2) = bob_ratchet.encrypt(&data, b"");
|
let (header2, encrypted2, nonce2) = bob_ratchet.ratchet_encrypt(&data, b"");
|
||||||
let decrypted2 = alice_ratchet.decrypt(&header2, &encrypted2, &nonce2, b"").unwrap();
|
let decrypted2 = alice_ratchet.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 ench_init() {
|
fn ratchet_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, shared_hka, shared_nhkb);
|
let (_bob_ratchet, public_key) = RatchetEncHeader::init_bob(sk,
|
||||||
let _alice_ratchet = RatchetEncHeader::init_alice(sk, public_key, shared_hka, shared_nhkb);
|
shared_hka,
|
||||||
|
shared_nhkb);
|
||||||
|
let _alice_ratchet = RatchetEncHeader::init_alice(sk, public_key,
|
||||||
|
shared_hka, shared_nhkb);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn 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, shared_hka, shared_nhkb);
|
let (mut bob_ratchet, public_key) = RatchetEncHeader::init_bob(sk,
|
||||||
let mut alice_ratchet = RatchetEncHeader::init_alice(sk, public_key, shared_hka, shared_nhkb);
|
shared_hka,
|
||||||
|
shared_nhkb);
|
||||||
|
let mut alice_ratchet = RatchetEncHeader::init_alice(sk,
|
||||||
|
public_key,
|
||||||
|
shared_hka,
|
||||||
|
shared_nhkb);
|
||||||
let data = include_bytes!("../src/header.rs").to_vec();
|
let data = include_bytes!("../src/header.rs").to_vec();
|
||||||
let (header, encrypted, nonce) = alice_ratchet.encrypt(&data, b"");
|
let (header, encrypted, nonce) = alice_ratchet.ratchet_encrypt(&data, b"");
|
||||||
let decrypted = bob_ratchet.decrypt(&header, &encrypted, &nonce, b"").unwrap();
|
let decrypted = bob_ratchet.ratchet_decrypt(&header, &encrypted, &nonce, b"");
|
||||||
assert_eq!(data, decrypted)
|
assert_eq!(data, decrypted)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn 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, shared_hka, shared_nhkb);
|
let (mut bob_ratchet, public_key) = RatchetEncHeader::init_bob(sk,
|
||||||
let mut alice_ratchet = RatchetEncHeader::init_alice(sk, public_key, shared_hka, shared_nhkb);
|
shared_hka,
|
||||||
|
shared_nhkb);
|
||||||
|
let mut alice_ratchet = RatchetEncHeader::init_alice(sk,
|
||||||
|
public_key,
|
||||||
|
shared_hka,
|
||||||
|
shared_nhkb);
|
||||||
let data = include_bytes!("../src/header.rs").to_vec();
|
let data = include_bytes!("../src/header.rs").to_vec();
|
||||||
let (header1, encrypted1, nonce1) = alice_ratchet.encrypt(&data, b"");
|
let (header1, encrypted1, nonce1) = alice_ratchet.ratchet_encrypt(&data, b"");
|
||||||
let (header2, encrypted2, nonce2) = alice_ratchet.encrypt(&data, b"");
|
let (header2, encrypted2, nonce2) = alice_ratchet.ratchet_encrypt(&data, b"");
|
||||||
let (header3, encrypted3, nonce3) = alice_ratchet.encrypt(&data, b"");
|
let (header3, encrypted3, nonce3) = alice_ratchet.ratchet_encrypt(&data, b"");
|
||||||
let decrypted3 = bob_ratchet.decrypt(&header3, &encrypted3, &nonce3, b"").unwrap();
|
let decrypted3 = bob_ratchet.ratchet_decrypt(&header3, &encrypted3, &nonce3, b"");
|
||||||
let decrypted2 = bob_ratchet.decrypt(&header2, &encrypted2, &nonce2, b"").unwrap();
|
let decrypted2 = bob_ratchet.ratchet_decrypt(&header2, &encrypted2, &nonce2, b"");
|
||||||
let decrypted1 = bob_ratchet.decrypt(&header1, &encrypted1, &nonce1, b"").unwrap();
|
let decrypted1 = bob_ratchet.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 ench_panic_bob() {
|
fn ratchet_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, shared_hka, shared_nhkb);
|
let (mut bob_ratchet, _) = RatchetEncHeader::init_bob(sk,
|
||||||
|
shared_hka,
|
||||||
|
shared_nhkb);
|
||||||
let data = include_bytes!("../src/header.rs").to_vec();
|
let data = include_bytes!("../src/header.rs").to_vec();
|
||||||
let (_, _, _) = bob_ratchet.encrypt(&data, b"");
|
let (_, _, _) = bob_ratchet.ratchet_encrypt(&data, b"");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn 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, 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 = include_bytes!("../src/dh.rs").to_vec();
|
let data = include_bytes!("../src/dh.rs").to_vec();
|
||||||
let (header1, encrypted1, nonce1) = alice_ratchet.encrypt(&data, b"");
|
let (header1, encrypted1, nonce1) = alice_ratchet.ratchet_encrypt(&data, b"");
|
||||||
let decrypted1 = bob_ratchet.decrypt(&header1, &encrypted1, &nonce1, b"").unwrap();
|
let decrypted1 = bob_ratchet.ratchet_decrypt(&header1, &encrypted1, &nonce1, b"");
|
||||||
let (header2, encrypted2, nonce2) = bob_ratchet.encrypt(&data, b"");
|
let (header2, encrypted2, nonce2) = bob_ratchet.ratchet_encrypt(&data, b"");
|
||||||
let decrypted2 = alice_ratchet.decrypt(&header2, &encrypted2, &nonce2, b"").unwrap();
|
let decrypted2 = alice_ratchet.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 ench_enc_skip_panic() {
|
fn ratchet_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, shared_hka, shared_nhkb);
|
let (mut bob_ratchet, public_key) = RatchetEncHeader::init_bob(sk,
|
||||||
let mut alice_ratchet = RatchetEncHeader::init_alice(sk, public_key, shared_hka, shared_nhkb);
|
shared_hka,
|
||||||
|
shared_nhkb);
|
||||||
|
let mut alice_ratchet = RatchetEncHeader::init_alice(sk,
|
||||||
|
public_key,
|
||||||
|
shared_hka,
|
||||||
|
shared_nhkb);
|
||||||
let data = include_bytes!("../src/header.rs").to_vec();
|
let 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.encrypt(&data, b"");
|
let (header, encrypted, nonce) = alice_ratchet.ratchet_encrypt(&data, b"");
|
||||||
headers.push(header);
|
headers.push(header);
|
||||||
encrypteds.push(encrypted);
|
encrypteds.push(encrypted);
|
||||||
nonces.push(nonce);
|
nonces.push(nonce);
|
||||||
|
|
@ -156,7 +173,7 @@ fn 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.decrypt(header, encrypted, nonce, b"").unwrap();
|
let decrypted = bob_ratchet.ratchet_decrypt(header, encrypted, nonce, b"");
|
||||||
decrypteds.push(decrypted);
|
decrypteds.push(decrypted);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -166,7 +183,9 @@ 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, shared_hka, shared_nhkb);
|
let (bob_ratchet, public_key) = RatchetEncHeader::init_bob(sk,
|
||||||
|
shared_hka,
|
||||||
|
shared_nhkb);
|
||||||
let alice_ratchet = RatchetEncHeader::init_alice(sk, public_key, shared_hka, shared_nhkb);
|
let 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…
Add table
Add a link
Reference in a new issue