Added Ratchet Import and Export

This commit is contained in:
Hannes 2021-08-23 14:05:05 +02:00
parent de3e308d09
commit 0ada2f3372
5 changed files with 141 additions and 5 deletions

View file

@ -6,7 +6,7 @@ homepage = "https://github.com/Dione-Software/double-ratchet-2"
repository = "https://github.com/Dione-Software/double-ratchet-2" repository = "https://github.com/Dione-Software/double-ratchet-2"
readme = "README.md" readme = "README.md"
keywords = ["double-ratchet", "crypto", "cryptography", "signal"] keywords = ["double-ratchet", "crypto", "cryptography", "signal"]
version = "0.3.3" version = "0.3.4"
edition = "2018" edition = "2018"
license = "MIT" license = "MIT"
@ -17,7 +17,7 @@ maintenance = { status = "actively-developed" }
[dependencies] [dependencies]
p256 = {version = "0.9", features = ["zeroize", "ecdh", "arithmetic", "pem"]} p256 = {version = "0.9", features = ["zeroize", "ecdh", "arithmetic", "pem", "jwk"]}
rand_core = {version = "0.6", features = ["getrandom"]} rand_core = {version = "0.6", features = ["getrandom"]}
hkdf = "0.11.0" hkdf = "0.11.0"
hmac = "0.11.0" hmac = "0.11.0"
@ -27,7 +27,7 @@ sha2 = {version = "0.9.5", optional = true}
serde = {version = "1.0.125", default-features = false, features = ["derive"]} serde = {version = "1.0.125", default-features = false, features = ["derive"]}
serde_bytes = "0.11.5" serde_bytes = "0.11.5"
bincode = "1.3.3" bincode = "1.3.3"
hashbrown = "0.11.2" hashbrown = {version = "0.11.2", features = ["serde"]}
zeroize = {version = "1.3.0", features = ["zeroize_derive"]} zeroize = {version = "1.3.0", features = ["zeroize_derive"]}
[dev-dependencies] [dev-dependencies]

View file

@ -106,6 +106,16 @@ let decrypted = bob_ratchet.ratchet_decrypt(&header, &encrypted, &nonce, ad);
assert_eq!(data, decrypted) assert_eq!(data, decrypted)
``` ```
## Export / Import Ratchet with encrypted headers
This ratchet implements import and export functionality. This works over a bincode backend and
maybe useful for saving Ratchets to and loading from a file.
```rust
let (bob_ratchet, public_key) = RatchetEncHeader::init_bob(sk, shared_hka, shared_nhkb);
let ex_ratchet = bob_ratchet.export();
let im_ratchet = RatchetEncHeader::import(&ex_ratchet);
assert_eq!(im_ratchet, bob_ratchet)
```
## Features ## Features
Currently the crate only supports one feature: ring. If feature is enabled the crate switches Currently the crate only supports one feature: ring. If feature is enabled the crate switches
@ -120,6 +130,6 @@ TODO:
[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.3.1 Current version: 0.3.4
License: MIT License: MIT

View file

@ -107,6 +107,20 @@
//! assert_eq!(data, decrypted) //! assert_eq!(data, decrypted)
//! ``` //! ```
//! //!
//! # Export / Import Ratchet with encrypted headers
//! This ratchet implements import and export functionality. This works over a bincode backend and
//! maybe useful for saving Ratchets to and loading from a file.
//! ```
//! # use double_ratchet_2::ratchet::RatchetEncHeader;
//! # let sk = [0; 32];
//! # let shared_hka = [1; 32];
//! # let shared_nhkb = [2; 32];
//! let (bob_ratchet, public_key) = RatchetEncHeader::init_bob(sk, shared_hka, shared_nhkb);
//! let ex_ratchet = bob_ratchet.export();
//! let im_ratchet = RatchetEncHeader::import(&ex_ratchet);
//! assert_eq!(im_ratchet, bob_ratchet)
//! ```
//!
//! # Features //! # Features
//! //!
//! Currently the crate only supports one feature: ring. If feature is enabled the crate switches //! Currently the crate only supports one feature: ring. If feature is enabled the crate switches

View file

@ -9,9 +9,10 @@ use crate::header::Header;
use alloc::vec::Vec; use alloc::vec::Vec;
use crate::kdf_chain::kdf_ck; use crate::kdf_chain::kdf_ck;
use crate::aead::{encrypt, decrypt}; use crate::aead::{encrypt, decrypt};
use alloc::string::ToString; use alloc::string::{ToString, String};
use zeroize::Zeroize; use zeroize::Zeroize;
use rand_core::OsRng; use rand_core::OsRng;
use serde::{Deserialize, Serialize};
const MAX_SKIP: usize = 100; const MAX_SKIP: usize = 100;
@ -161,6 +162,7 @@ impl Ratchet {
} }
} }
#[derive(PartialEq, Debug)]
pub struct RatchetEncHeader { pub struct RatchetEncHeader {
dhs: DhKeyPair, dhs: DhKeyPair,
dhr: Option<PublicKey>, dhr: Option<PublicKey>,
@ -201,6 +203,85 @@ 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, public_dhs);
let dhr = reh.dhr.map(|e| e.to_jwk_string());
let rk = reh.rk;
let cks = reh.cks;
let ckr = reh.ckr;
let ns = reh.ns;
let nr = reh.nr;
let pn = reh.pn;
let hks = reh.hks;
let hkr = reh.hkr;
let nhks = reh.nhks;
let nhkr = reh.nhkr;
let mkskipped = reh.mkskipped.clone();
Self {
dhs,
dhr,
rk,
cks,
ckr,
ns,
nr,
pn,
hks,
hkr,
nhks,
nhkr,
mkskipped
}
}
}
impl From<&ExRatchetEncHeader> for RatchetEncHeader {
fn from(ex_reh: &ExRatchetEncHeader) -> Self {
let private_dhs = SecretKey::from_jwk_str(&ex_reh.dhs.0).unwrap();
let public_dhs = PublicKey::from_jwk_str(&ex_reh.dhs.1).unwrap();
let dhs = DhKeyPair {
private_key: private_dhs,
public_key: public_dhs
};
let dhr = ex_reh.dhr.as_ref().map(|e| PublicKey::from_jwk_str(&e).unwrap());
Self {
dhs,
dhr,
rk: ex_reh.rk,
cks: ex_reh.cks,
ckr: ex_reh.ckr,
ns: ex_reh.ns,
nr: ex_reh.nr,
pn: ex_reh.pn,
hks: ex_reh.hks,
hkr: ex_reh.hkr,
nhks: ex_reh.nhks,
nhkr: ex_reh.nhkr,
mkskipped: ex_reh.mkskipped.clone()
}
}
}
impl RatchetEncHeader { impl RatchetEncHeader {
pub fn init_alice(sk: [u8; 32], pub fn init_alice(sk: [u8; 32],
bob_dh_public_key: PublicKey, bob_dh_public_key: PublicKey,
@ -351,4 +432,16 @@ impl RatchetEncHeader {
self.nr += 1; self.nr += 1;
(decrypt(&mk, ciphertext, &header.concat(ad), nonce), header) (decrypt(&mk, ciphertext, &header.concat(ad), nonce), header)
} }
/// Export the ratchet to Binary data
pub fn export(&self) -> Vec<u8> {
let ex: ExRatchetEncHeader = self.into();
bincode::serialize(&ex).unwrap()
}
/// Import the ratchet from Binary data. Panics when binary data is invalid.
pub fn import(inp: &[u8]) -> Self {
let ex: ExRatchetEncHeader = bincode::deserialize(inp).unwrap();
RatchetEncHeader::from(&ex)
}
} }

View file

@ -173,3 +173,22 @@ fn ratchet_ench_enc_skip_panic() {
decrypteds.push(decrypted); decrypteds.push(decrypted);
} }
} }
#[test]
fn import_export() {
let sk = [1; 32];
let shared_hka = [2; 32];
let shared_nhkb = [3; 32];
let (bob_ratchet, public_key) = RatchetEncHeader::init_bob(sk,
shared_hka,
shared_nhkb);
let alice_ratchet = RatchetEncHeader::init_alice(sk, public_key, shared_hka, shared_nhkb);
let ex_bob_ratchet = bob_ratchet.export();
let in_bob_ratchet = RatchetEncHeader::import(&ex_bob_ratchet);
assert_eq!(in_bob_ratchet, bob_ratchet);
let ex_alice_ratchet = alice_ratchet.export();
let in_alice_ratchet = RatchetEncHeader::import(&ex_alice_ratchet);
assert_eq!(in_alice_ratchet, alice_ratchet);
}