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"
|
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]
|
||||||
|
|
12
README.md
12
README.md
|
@ -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
|
||||||
|
|
14
src/lib.rs
14
src/lib.rs
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
19
tests/mod.rs
19
tests/mod.rs
|
@ -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);
|
||||||
|
}
|
Loading…
Reference in a new issue