Added Ratchet Import and Export
This commit is contained in:
parent
de3e308d09
commit
0ada2f3372
5 changed files with 141 additions and 5 deletions
|
@ -6,7 +6,7 @@ homepage = "https://github.com/Dione-Software/double-ratchet-2"
|
|||
repository = "https://github.com/Dione-Software/double-ratchet-2"
|
||||
readme = "README.md"
|
||||
keywords = ["double-ratchet", "crypto", "cryptography", "signal"]
|
||||
version = "0.3.3"
|
||||
version = "0.3.4"
|
||||
edition = "2018"
|
||||
license = "MIT"
|
||||
|
||||
|
@ -17,7 +17,7 @@ maintenance = { status = "actively-developed" }
|
|||
|
||||
|
||||
[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"]}
|
||||
hkdf = "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_bytes = "0.11.5"
|
||||
bincode = "1.3.3"
|
||||
hashbrown = "0.11.2"
|
||||
hashbrown = {version = "0.11.2", features = ["serde"]}
|
||||
zeroize = {version = "1.3.0", features = ["zeroize_derive"]}
|
||||
|
||||
[dev-dependencies]
|
||||
|
|
12
README.md
12
README.md
|
@ -106,6 +106,16 @@ let decrypted = bob_ratchet.ratchet_decrypt(&header, &encrypted, &nonce, ad);
|
|||
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
|
||||
|
||||
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
|
||||
[3]: https://signal.org/docs/specifications/doubleratchet/#double-ratchet-with-header-encryption
|
||||
|
||||
Current version: 0.3.1
|
||||
Current version: 0.3.4
|
||||
|
||||
License: MIT
|
||||
|
|
14
src/lib.rs
14
src/lib.rs
|
@ -107,6 +107,20 @@
|
|||
//! 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
|
||||
//!
|
||||
//! Currently the crate only supports one feature: ring. If feature is enabled the crate switches
|
||||
|
|
|
@ -9,9 +9,10 @@ use crate::header::Header;
|
|||
use alloc::vec::Vec;
|
||||
use crate::kdf_chain::kdf_ck;
|
||||
use crate::aead::{encrypt, decrypt};
|
||||
use alloc::string::ToString;
|
||||
use alloc::string::{ToString, String};
|
||||
use zeroize::Zeroize;
|
||||
use rand_core::OsRng;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
const MAX_SKIP: usize = 100;
|
||||
|
||||
|
@ -161,6 +162,7 @@ impl Ratchet {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub struct RatchetEncHeader {
|
||||
dhs: DhKeyPair,
|
||||
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 {
|
||||
pub fn init_alice(sk: [u8; 32],
|
||||
bob_dh_public_key: PublicKey,
|
||||
|
@ -351,4 +432,16 @@ impl RatchetEncHeader {
|
|||
self.nr += 1;
|
||||
(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)
|
||||
}
|
||||
}
|
19
tests/mod.rs
19
tests/mod.rs
|
@ -173,3 +173,22 @@ fn ratchet_ench_enc_skip_panic() {
|
|||
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);
|
||||
}
|
Loading…
Reference in a new issue