Compare commits

..

No commits in common. "df1b2129c1fc3e05173d218fe74efda14a973377" and "da4e7718907b094641fa6fea750135c5a967f79e" have entirely different histories.

20 changed files with 2054 additions and 705 deletions

2
.github/actions-rs/grcov.yml vendored Normal file
View file

@ -0,0 +1,2 @@
ignore:
- "../*"

11
.github/dependabot.yml vendored Normal file
View 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
View 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
View 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
View file

@ -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

File diff suppressed because it is too large Load diff

View file

@ -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"]

View file

@ -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

157
README.md
View file

@ -1,87 +1,71 @@
# double-ratchet-rs [![Crate](https://img.shields.io/crates/v/double-ratchet-2)](https://crates.io/crates/double-ratchet-2)
[![License](https://img.shields.io/github/license/Dione-Software/double-ratchet-2)](https://github.com/Dione-Software/double-ratchet-2/blob/main/LICENSE)
[![Coverage Status](https://coveralls.io/repos/github/Dione-Software/double-ratchet-2/badge.svg?branch=main)](https://coveralls.io/github/Dione-Software/double-ratchet-2?branch=main)
[![Workflow Status](https://github.com/Dione-Software/double-ratchet-2/actions/workflows/rust.yml/badge.svg)](https://github.com/Dione-Software/double-ratchet-2/actions/workflows/rust.yml)
![Maintenance](https://img.shields.io/badge/maintenance-activly--developed-brightgreen.svg)
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 (mut bob_ratchet, public_key) = Ratchet::init_bob(sk); // Creating Bob's Ratchet (returns Bob's PublicKey)
let mut alice_ratchet = Ratchet::init_alice(sk, public_key); // Creating Alice's Ratchet with Bob's PublicKey
let sk = [1; 32]; // Initial Key created by a symmetric key agreement protocol
let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk); // Creating Bobs Ratchet (returns Bobs PublicKey)
let mut alice_ratchet = Ratchet::init_alice(sk, public_key); // Creating Alice Ratchet with Bobs PublicKey
let data = b"Hello World".to_vec(); // Data to be encrypted let data = b"Hello World".to_vec(); // Data to be encrypted
let ad = b"Associated Data"; // Associated data let ad = b"Associated Data"; // Associated Data
let (header, encrypted, nonce) = alice_ratchet.encrypt(&data, ad); // Encrypting message with Alice's Ratchet (Alice always needs to send the first message)
let decrypted = bob_ratchet.decrypt(&header, &encrypted, &nonce, ad); // Decrypt message with Bob's Ratchet
let (header, encrypted, nonce) = alice_ratchet.ratchet_encrypt(&data, ad); // Encrypting message with Alice Ratchet (Alice always needs to send the first message)
let decrypted = bob_ratchet.ratchet_decrypt(&header, &encrypted, &nonce, ad); // Decrypt message with Bobs Ratchet
assert_eq!(data, decrypted) 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 (mut bob_ratchet, public_key) = Ratchet::init_bob(sk); // Creating Bob's Ratchet (returns Bob's PublicKey)
let mut alice_ratchet = Ratchet::init_alice(sk, public_key); // Creating Alice's Ratchet with Bob's PublicKey
let sk = [1; 32]; // Initial Key created by a symmetric key agreement protocol
let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk); // Creating Bobs Ratchet (returns Bobs PublicKey)
let mut alice_ratchet = Ratchet::init_alice(sk, public_key); // Creating Alice Ratchet with Bobs PublicKey
let data = b"Hello World".to_vec(); // Data to be encrypted let data = b"Hello World".to_vec(); // Data to be encrypted
let ad = b"Associated Data"; // Associated data let ad = b"Associated Data"; // Associated Data
let (header1, encrypted1, nonce1) = alice_ratchet.encrypt(&data, ad); // Lost message let (header1, encrypted1, nonce1) = alice_ratchet.ratchet_encrypt(&data, ad); // Lost message
let (header2, encrypted2, nonce2) = alice_ratchet.encrypt(&data, ad); // Successful message let (header2, encrypted2, nonce2) = alice_ratchet.ratchet_encrypt(&data, ad); // Successful message
let decrypted2 = bob_ratchet.decrypt(&header2, &encrypted2, &nonce2, ad); // Decrypting second message first let decrypted2 = bob_ratchet.ratchet_decrypt(&header2, &encrypted2, &nonce2, ad); // Decrypting second message first
let decrypted1 = bob_ratchet.decrypt(&header1, &encrypted1, &nonce1, ad); // Decrypting latter message let decrypted1 = bob_ratchet.ratchet_decrypt(&header1, &encrypted1, &nonce1, ad); // Decrypting latter message
assert_eq!(data, decrypted1); let comp = decrypted1 == data && decrypted2 == data;
assert_eq!(data, decrypted2); assert!(comp);
``` ```
### 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
View file

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

View file

@ -1,13 +1,13 @@
use 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_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); criterion_main!(without_enc_headerd, with_enc_headerd);

204
deny.toml Normal file
View 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 = [""]

View file

@ -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)
} }
} }

View file

@ -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();

View file

@ -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)
} }
} }

View file

@ -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();

View file

@ -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")
) )
} }

View file

@ -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 (mut bob_ratchet, public_key) = Ratchet::init_bob(sk); // Creating Bob's Ratchet (returns Bob's PublicKey)
//! let mut alice_ratchet = Ratchet::init_alice(sk, public_key); // Creating Alice's Ratchet with Bob's PublicKey
//! //!
//! let sk = [1; 32]; // Initial Key created by a symmetric key agreement protocol
//! let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk); // Creating Bobs Ratchet (returns Bobs PublicKey)
//! let mut alice_ratchet = Ratchet::init_alice(sk, public_key); // Creating Alice Ratchet with Bobs PublicKey
//! let data = b"Hello World".to_vec(); // Data to be encrypted //! let data = b"Hello World".to_vec(); // Data to be encrypted
//! let ad = b"Associated Data"; // Associated data //! let ad = b"Associated Data"; // Associated Data
//!
//! let (header, encrypted, nonce) = alice_ratchet.encrypt(&data, ad); // Encrypting message with Alice's Ratchet (Alice always needs to send the first message)
//! let decrypted = bob_ratchet.decrypt(&header, &encrypted, &nonce, ad); // Decrypt message with Bob's Ratchet
//! //!
//! let (header, encrypted, nonce) = alice_ratchet.ratchet_encrypt(&data, ad); // Encrypting message with Alice Ratchet (Alice always needs to send the first message)
//! let decrypted = bob_ratchet.ratchet_decrypt(&header, &encrypted, &nonce, ad); // Decrypt message with Bobs Ratchet
//! assert_eq!(data, decrypted) //! 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 (mut bob_ratchet, public_key) = Ratchet::init_bob(sk); // Creating Bob's Ratchet (returns Bob's PublicKey)
//! let mut alice_ratchet = Ratchet::init_alice(sk, public_key); // Creating Alice's Ratchet with Bob's PublicKey
//! //!
//! let sk = [1; 32]; // Initial Key created by a symmetric key agreement protocol
//! let (mut bob_ratchet, public_key) = Ratchet::init_bob(sk); // Creating Bobs Ratchet (returns Bobs PublicKey)
//! let mut alice_ratchet = Ratchet::init_alice(sk, public_key); // Creating Alice Ratchet with Bobs PublicKey
//! let data = b"Hello World".to_vec(); // Data to be encrypted //! let data = b"Hello World".to_vec(); // Data to be encrypted
//! let ad = b"Associated Data"; // Associated data //! let ad = b"Associated Data"; // Associated Data
//! //!
//! let (header1, encrypted1, nonce1) = alice_ratchet.encrypt(&data, ad); // Lost message //! let (header1, encrypted1, nonce1) = alice_ratchet.ratchet_encrypt(&data, ad); // Lost message
//! let (header2, encrypted2, nonce2) = alice_ratchet.encrypt(&data, ad); // Successful message //! let (header2, encrypted2, nonce2) = alice_ratchet.ratchet_encrypt(&data, ad); // Successful message
//! //!
//! let decrypted2 = bob_ratchet.decrypt(&header2, &encrypted2, &nonce2, ad); // Decrypting second message first //! let decrypted2 = bob_ratchet.ratchet_decrypt(&header2, &encrypted2, &nonce2, ad); // Decrypting second message first
//! let decrypted1 = bob_ratchet.decrypt(&header1, &encrypted1, &nonce1, ad); // Decrypting latter message //! let decrypted1 = bob_ratchet.ratchet_decrypt(&header1, &encrypted1, &nonce1, ad); // Decrypting latter message
//! //!
//! assert_eq!(data, decrypted1); //! let comp = decrypted1 == data && decrypted2 == data;
//! assert_eq!(data, decrypted2); //! assert!(comp);
//! ``` //! ```
//! //!
//! ### 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::*;

View file

@ -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]. #[derive(PartialEq, Debug)]
/// Requires bytes.
/// Returns a [Ratchet], or nothing if invalid data is provided.
pub fn import(data: &[u8]) -> Option<Self> {
postcard::from_bytes(data).ok()
}
}
/// A [Ratchet], but with header encryption.
#[derive(Deserialize, Serialize, PartialEq, Debug)]
pub struct RatchetEncHeader { 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.
/// Returns a [RatchetEncHeader].
pub fn init_alice(
sk: [u8; 32],
bob_dh_public_key: PublicKey, bob_dh_public_key: PublicKey,
shared_hka: [u8; 32], shared_hka: [u8; 32],
shared_nhkb: [u8; 32], shared_nhkb: [u8; 32]) -> Self {
) -> 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()
} }
} }

View file

@ -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();