add std
support, wording changes, code improvements
This commit is contained in:
parent
45916848ad
commit
2b559b79f9
7 changed files with 65 additions and 36 deletions
10
Cargo.toml
10
Cargo.toml
|
@ -1,12 +1,12 @@
|
||||||
[package]
|
[package]
|
||||||
name = "double-ratchet-rs"
|
name = "double-ratchet-rs"
|
||||||
authors = ["satvrn", "Hannes Furmans"]
|
authors = ["satvrn", "Hannes Furmans"]
|
||||||
description = "A pure Rust implementation of the Double Ratchet Algorithm as specified by Signal."
|
description = "A pure Rust implementation of the Double Ratchet algorithm as described by Signal."
|
||||||
homepage = "https://github.com/notsatvrn/double-ratchet-rs"
|
homepage = "https://github.com/notsatvrn/double-ratchet-rs"
|
||||||
repository = "https://github.com/notsatvrn/double-ratchet-rs"
|
repository = "https://github.com/notsatvrn/double-ratchet-rs"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
keywords = ["double-ratchet", "crypto", "cryptography", "signal"]
|
keywords = ["double-ratchet", "crypto", "cryptography", "signal"]
|
||||||
version = "0.4.5"
|
version = "0.4.6"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ aes-gcm-siv = "0.11"
|
||||||
sha2 = {version = "0.10", default-features = false}
|
sha2 = {version = "0.10", default-features = false}
|
||||||
serde = {version = "1.0", default-features = false, features = ["derive"]}
|
serde = {version = "1.0", default-features = false, features = ["derive"]}
|
||||||
postcard = {version = "1.0", default-features = false, features = ["alloc"]}
|
postcard = {version = "1.0", default-features = false, features = ["alloc"]}
|
||||||
hashbrown = {version = "0.14", features = ["serde"]}
|
hashbrown = {version = "0.14", features = ["serde"], optional = true}
|
||||||
zeroize = {version = "1.6", default-features = false, features = ["zeroize_derive"]}
|
zeroize = {version = "1.6", default-features = false, features = ["zeroize_derive"]}
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
@ -31,3 +31,7 @@ harness = false
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
lto = true
|
lto = true
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["hashbrown"]
|
||||||
|
std = ["sha2/std", "serde/std", "postcard/use-std", "zeroize/std"]
|
||||||
|
|
12
README.md
12
README.md
|
@ -5,10 +5,10 @@
|
||||||
|
|
||||||
# double-ratchet-rs
|
# double-ratchet-rs
|
||||||
|
|
||||||
A pure Rust implementation of the Double Ratchet Algorithm as specified by [Signal][1].
|
A pure Rust implementation of the Double Ratchet algorithm as described by [Signal][1].
|
||||||
|
|
||||||
This implementation follows the cryptographic recommendations provided by [Signal][2].
|
This implementation follows the cryptographic recommendations provided by [Signal][2].
|
||||||
The AEAD Algorithm uses a constant Nonce. This might be changed in the future.
|
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).
|
Fork of [double-ratchet-2](https://github.com/Dione-Software/double-ratchet-2).
|
||||||
|
|
||||||
|
@ -163,9 +163,14 @@ let im_ratchet = RatchetEncHeader::import(&ex_ratchet).unwrap();
|
||||||
assert_eq!(im_ratchet, bob_ratchet)
|
assert_eq!(im_ratchet, bob_ratchet)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- `hashbrown`: Use `hashbrown` for `HashMap`. Enabled by default for `no_std` support.
|
||||||
|
- `std`: Use `std` instead of `alloc`. Can be used with `hashbrown`, but it isn't required.
|
||||||
|
|
||||||
## **M**inimum **S**upported **R**ust **V**ersion (MSRV)
|
## **M**inimum **S**upported **R**ust **V**ersion (MSRV)
|
||||||
|
|
||||||
The current MSRV is 1.61.0.
|
The current MSRV is 1.60.0 without `hashbrown` and 1.64.0 with `hashbrown`.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
@ -174,4 +179,3 @@ This project is licensed under the [MIT license](https://github.com/notsatvrn/do
|
||||||
[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
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,15 @@
|
||||||
use aes_gcm_siv::aead::AeadInPlace;
|
use aes_gcm_siv::aead::AeadInPlace;
|
||||||
use aes_gcm_siv::{Aes256GcmSiv, KeyInit, Nonce};
|
use aes_gcm_siv::{Aes256GcmSiv, KeyInit, Nonce};
|
||||||
use alloc::vec::Vec;
|
|
||||||
use rand_core::{OsRng, RngCore};
|
use rand_core::{OsRng, RngCore};
|
||||||
|
|
||||||
|
#[cfg(not(feature = "std"))]
|
||||||
|
use alloc::vec::Vec;
|
||||||
|
|
||||||
pub fn encrypt(mk: &[u8; 32], data: &[u8], associated_data: &[u8]) -> (Vec<u8>, [u8; 12]) {
|
pub fn encrypt(mk: &[u8; 32], data: &[u8], associated_data: &[u8]) -> (Vec<u8>, [u8; 12]) {
|
||||||
let cipher = Aes256GcmSiv::new_from_slice(mk).expect("Encryption failure {}");
|
let cipher = Aes256GcmSiv::new_from_slice(mk).expect("Encryption failure {}");
|
||||||
|
|
||||||
let mut nonce_data = [0u8; 12];
|
let mut nonce_data = [0u8; 12];
|
||||||
OsRng::fill_bytes(&mut OsRng, &mut nonce_data);
|
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();
|
||||||
|
|
|
@ -24,7 +24,7 @@ impl PartialEq for DhKeyPair {
|
||||||
impl Debug for DhKeyPair {
|
impl Debug for DhKeyPair {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
|
||||||
f.debug_struct("DhKeyPair")
|
f.debug_struct("DhKeyPair")
|
||||||
.field("private_key", &self.private_key.to_bytes())
|
.field("private_key", self.private_key.as_bytes())
|
||||||
.field("public_key", self.public_key.as_bytes())
|
.field("public_key", self.public_key.as_bytes())
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,7 @@ impl Default for DhKeyPair {
|
||||||
|
|
||||||
impl DhKeyPair {
|
impl DhKeyPair {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let secret = StaticSecret::random_from_rng(&mut OsRng);
|
let secret = StaticSecret::random_from_rng(OsRng);
|
||||||
let public = PublicKey::from(&secret);
|
let public = PublicKey::from(&secret);
|
||||||
DhKeyPair {
|
DhKeyPair {
|
||||||
private_key: secret,
|
private_key: secret,
|
||||||
|
@ -58,11 +58,6 @@ 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;
|
||||||
|
|
|
@ -4,14 +4,13 @@ use crate::aead::encrypt;
|
||||||
use crate::dh::DhKeyPair;
|
use crate::dh::DhKeyPair;
|
||||||
use aes_gcm_siv::aead::AeadInPlace;
|
use aes_gcm_siv::aead::AeadInPlace;
|
||||||
use aes_gcm_siv::{Aes256GcmSiv, KeyInit, Nonce};
|
use aes_gcm_siv::{Aes256GcmSiv, KeyInit, Nonce};
|
||||||
use alloc::vec::Vec;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use x25519_dalek::PublicKey;
|
use x25519_dalek::PublicKey;
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
use crate::dh::gen_key_pair;
|
|
||||||
use zeroize::Zeroize;
|
use zeroize::Zeroize;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "std"))]
|
||||||
|
use alloc::vec::Vec;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Zeroize, Clone, PartialEq, Eq)]
|
#[derive(Serialize, Deserialize, Debug, Zeroize, Clone, PartialEq, Eq)]
|
||||||
#[zeroize(drop)]
|
#[zeroize(drop)]
|
||||||
pub struct Header {
|
pub struct Header {
|
||||||
|
@ -93,7 +92,7 @@ impl EncryptedHeader {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub fn gen_header() -> Header {
|
pub fn gen_header() -> Header {
|
||||||
let dh_pair = gen_key_pair();
|
let dh_pair = DhKeyPair::new();
|
||||||
let pn = 10;
|
let pn = 10;
|
||||||
let n = 50;
|
let n = 50;
|
||||||
Header::new(&dh_pair, pn, n)
|
Header::new(&dh_pair, pn, n)
|
||||||
|
|
22
src/lib.rs
22
src/lib.rs
|
@ -1,7 +1,7 @@
|
||||||
//! A pure Rust implementation of the Double Ratchet Algorithm as specified by [Signal][1].
|
//! A pure Rust implementation of the Double Ratchet algorithm as described by [Signal][1].
|
||||||
//!
|
//!
|
||||||
//! This implementation follows the cryptographic recommendations provided by [Signal][2].
|
//! This implementation follows the cryptographic recommendations provided by [Signal][2].
|
||||||
//! The AEAD Algorithm uses a constant Nonce. This might be changed in the future.
|
//! 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).
|
//! Fork of [double-ratchet-2](https://github.com/Dione-Software/double-ratchet-2).
|
||||||
//!
|
//!
|
||||||
|
@ -156,9 +156,14 @@
|
||||||
//! assert_eq!(im_ratchet, bob_ratchet)
|
//! assert_eq!(im_ratchet, bob_ratchet)
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
|
//! ## Features
|
||||||
|
//!
|
||||||
|
//! - `hashbrown`: Use `hashbrown` for `HashMap`. Enabled by default for `no_std` support.
|
||||||
|
//! - `std`: Use `std` instead of `alloc`. Can be used with `hashbrown`, but it isn't required.
|
||||||
|
//!
|
||||||
//! ## **M**inimum **S**upported **R**ust **V**ersion (MSRV)
|
//! ## **M**inimum **S**upported **R**ust **V**ersion (MSRV)
|
||||||
//!
|
//!
|
||||||
//! The current MSRV is 1.61.0.
|
//! The current MSRV is 1.60.0 without `hashbrown` and 1.64.0 with `hashbrown`.
|
||||||
//!
|
//!
|
||||||
//! ## License
|
//! ## License
|
||||||
//!
|
//!
|
||||||
|
@ -168,19 +173,24 @@
|
||||||
//! [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
|
||||||
|
|
||||||
#![no_std]
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
#![allow(stable_features)]
|
#![allow(stable_features)]
|
||||||
|
|
||||||
|
#[cfg(not(feature = "std"))]
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
extern crate std as alloc;
|
||||||
|
|
||||||
pub use x25519_dalek::PublicKey;
|
pub use x25519_dalek::PublicKey;
|
||||||
|
|
||||||
mod aead;
|
mod aead;
|
||||||
mod dh;
|
mod dh;
|
||||||
|
mod header;
|
||||||
mod kdf_chain;
|
mod kdf_chain;
|
||||||
mod kdf_root;
|
mod kdf_root;
|
||||||
mod ratchet;
|
mod ratchet;
|
||||||
mod header;
|
|
||||||
|
|
||||||
pub use ratchet::*;
|
pub use dh::*;
|
||||||
pub use header::*;
|
pub use header::*;
|
||||||
|
pub use ratchet::*;
|
||||||
|
|
|
@ -2,15 +2,20 @@
|
||||||
|
|
||||||
use crate::aead::{decrypt, encrypt};
|
use crate::aead::{decrypt, encrypt};
|
||||||
use crate::dh::DhKeyPair;
|
use crate::dh::DhKeyPair;
|
||||||
use crate::header::{Header, EncryptedHeader};
|
use crate::header::{EncryptedHeader, Header};
|
||||||
use crate::kdf_chain::kdf_ck;
|
use crate::kdf_chain::kdf_ck;
|
||||||
use crate::kdf_root::{kdf_rk, kdf_rk_he};
|
use crate::kdf_root::{kdf_rk, kdf_rk_he};
|
||||||
use alloc::vec::Vec;
|
|
||||||
use hashbrown::HashMap;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use x25519_dalek::PublicKey;
|
use x25519_dalek::PublicKey;
|
||||||
use zeroize::Zeroize;
|
use zeroize::Zeroize;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "std"))]
|
||||||
|
use alloc::vec::Vec;
|
||||||
|
#[cfg(any(not(feature = "std"), feature = "hashbrown"))]
|
||||||
|
use hashbrown::HashMap;
|
||||||
|
#[cfg(all(feature = "std", not(feature = "hashbrown")))]
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
const MAX_SKIP: usize = 100;
|
const MAX_SKIP: usize = 100;
|
||||||
|
|
||||||
/// A standard ratchet.
|
/// A standard ratchet.
|
||||||
|
@ -115,7 +120,12 @@ impl Ratchet {
|
||||||
self.mkskipped
|
self.mkskipped
|
||||||
.remove(&(header.public_key.to_bytes(), header.n))
|
.remove(&(header.public_key.to_bytes(), header.n))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
Some(decrypt(&mk, enc_data, &header.concat(associated_data), nonce))
|
Some(decrypt(
|
||||||
|
&mk,
|
||||||
|
enc_data,
|
||||||
|
&header.concat(associated_data),
|
||||||
|
nonce,
|
||||||
|
))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -156,7 +166,7 @@ impl Ratchet {
|
||||||
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 != None {
|
if self.ckr.is_some() {
|
||||||
self.skip_message_keys(header.pn).unwrap();
|
self.skip_message_keys(header.pn).unwrap();
|
||||||
}
|
}
|
||||||
self.dhratchet(header);
|
self.dhratchet(header);
|
||||||
|
@ -298,7 +308,11 @@ impl RatchetEncHeader {
|
||||||
/// Encrypt bytes with a [RatchetEncHeader].
|
/// Encrypt bytes with a [RatchetEncHeader].
|
||||||
/// Requires bytes and associated bytes.
|
/// Requires bytes and associated bytes.
|
||||||
/// Returns an [EncryptedHeader], encrypted bytes, and a nonce.
|
/// Returns an [EncryptedHeader], encrypted bytes, and a nonce.
|
||||||
pub fn encrypt(&mut self, data: &[u8], associated_data: &[u8]) -> (EncryptedHeader, Vec<u8>, [u8; 12]) {
|
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);
|
||||||
|
@ -316,7 +330,7 @@ impl RatchetEncHeader {
|
||||||
associated_data: &[u8],
|
associated_data: &[u8],
|
||||||
) -> (Option<Vec<u8>>, Option<Header>) {
|
) -> (Option<Vec<u8>>, Option<Header>) {
|
||||||
let ret_data = self.mkskipped.clone().into_iter().find(|e| {
|
let ret_data = self.mkskipped.clone().into_iter().find(|e| {
|
||||||
let header = enc_header.decrypt(&e.0.0);
|
let header = enc_header.decrypt(&e.0 .0);
|
||||||
match header {
|
match header {
|
||||||
None => false,
|
None => false,
|
||||||
Some(h) => h.n == e.0 .1,
|
Some(h) => h.n == e.0 .1,
|
||||||
|
@ -325,7 +339,7 @@ impl RatchetEncHeader {
|
||||||
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 = enc_header.decrypt(&data.0 .0);
|
||||||
let mk = data.1;
|
let mk = data.1;
|
||||||
self.mkskipped.remove(&(data.0 .0, data.0 .1));
|
self.mkskipped.remove(&(data.0 .0, data.0 .1));
|
||||||
(
|
(
|
||||||
|
@ -425,7 +439,8 @@ impl RatchetEncHeader {
|
||||||
nonce: &[u8; 12],
|
nonce: &[u8; 12],
|
||||||
associated_data: &[u8],
|
associated_data: &[u8],
|
||||||
) -> (Vec<u8>, Header) {
|
) -> (Vec<u8>, Header) {
|
||||||
let (data, header) = self.try_skipped_message_keys(enc_header, enc_data, nonce, associated_data);
|
let (data, header) =
|
||||||
|
self.try_skipped_message_keys(enc_header, enc_data, nonce, associated_data);
|
||||||
if let Some(d) = data {
|
if let Some(d) = data {
|
||||||
return (d, header.unwrap());
|
return (d, header.unwrap());
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue