use crate::core::*;
use crypto_secretbox as secretbox;
use crypto_secretbox::{
aead::{generic_array::GenericArray, Aead, AeadCore},
KeyInit, XSalsa20Poly1305,
};
use hkdf::Hkdf;
use serde_derive::{Deserialize, Serialize};
use sha2::{digest::FixedOutput, Digest, Sha256};
use spake2::{Ed25519Group, Identity, Password, Spake2};
pub trait KeyPurpose: std::fmt::Debug {}
#[derive(Debug)]
pub struct WormholeKey;
impl KeyPurpose for WormholeKey {}
#[derive(Debug)]
pub struct GenericKey;
impl KeyPurpose for GenericKey {}
#[derive(Debug, Clone, derive_more::Display, derive_more::Deref)]
#[display(fmt = "{:?}", _0)]
#[deref(forward)]
pub struct Key<P: KeyPurpose>(
#[deref] pub Box<secretbox::Key>,
#[deref(ignore)] std::marker::PhantomData<P>,
);
impl Key<WormholeKey> {
#[cfg(feature = "transit")]
pub fn derive_transit_key(&self, appid: &AppID) -> Key<crate::transit::TransitKey> {
let transit_purpose = format!("{}/transit-key", &*appid);
let derived_key = self.derive_subkey_from_purpose(&transit_purpose);
trace!(
"Input key: {}, Transit key: {}, Transit purpose: '{}'",
self.to_hex(),
derived_key.to_hex(),
&transit_purpose
);
derived_key
}
}
impl<P: KeyPurpose> Key<P> {
pub fn new(key: Box<secretbox::Key>) -> Self {
Self(key, std::marker::PhantomData)
}
pub fn to_hex(&self) -> String {
hex::encode(&**self)
}
pub fn derive_subkey_from_purpose<NewP: KeyPurpose>(&self, purpose: &str) -> Key<NewP> {
Key(
Box::new(derive_key(&*self, purpose.as_bytes())),
std::marker::PhantomData,
)
}
}
#[derive(Serialize, Deserialize, Debug)]
struct PhaseMessage {
#[serde(with = "hex::serde")]
pake_v1: Vec<u8>,
}
pub fn make_pake(password: &str, appid: &AppID) -> (Spake2<Ed25519Group>, Vec<u8>) {
let (pake_state, msg1) = Spake2::<Ed25519Group>::start_symmetric(
&Password::new(password.as_bytes()),
&Identity::new(appid.0.as_bytes()),
);
let pake_msg = PhaseMessage { pake_v1: msg1 };
let pake_msg_ser = serde_json::to_vec(&pake_msg).unwrap();
(pake_state, pake_msg_ser)
}
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct VersionsMessage {
#[serde(default)]
pub abilities: Vec<String>,
#[serde(default)]
pub app_versions: serde_json::Value,
}
impl VersionsMessage {
pub fn new() -> Self {
Default::default()
}
pub fn set_app_versions(&mut self, versions: serde_json::Value) {
self.app_versions = versions;
}
}
pub fn build_version_msg(
side: &MySide,
key: &secretbox::Key,
versions: &VersionsMessage,
) -> (Phase, Vec<u8>) {
let phase = Phase::VERSION;
let data_key = derive_phase_key(side, key, &phase);
let plaintext = serde_json::to_vec(versions).unwrap();
let (_nonce, encrypted) = encrypt_data(&data_key, &plaintext);
(phase, encrypted)
}
pub fn extract_pake_msg(body: &[u8]) -> Result<Vec<u8>, WormholeError> {
serde_json::from_slice(body)
.map(|res: PhaseMessage| res.pake_v1)
.map_err(WormholeError::ProtocolJson)
}
fn encrypt_data_with_nonce(
key: &secretbox::Key,
plaintext: &[u8],
nonce: &secretbox::Nonce,
) -> Vec<u8> {
let cipher = XSalsa20Poly1305::new(GenericArray::from_slice(key));
let mut ciphertext = cipher.encrypt(nonce, plaintext).unwrap();
let mut nonce_and_ciphertext = vec![];
nonce_and_ciphertext.extend_from_slice(nonce);
nonce_and_ciphertext.append(&mut ciphertext);
nonce_and_ciphertext
}
pub fn encrypt_data(key: &secretbox::Key, plaintext: &[u8]) -> (secretbox::Nonce, Vec<u8>) {
let nonce = secretbox::SecretBox::<secretbox::XSalsa20Poly1305>::generate_nonce(
&mut rand::thread_rng(),
);
let nonce_and_ciphertext = encrypt_data_with_nonce(key, plaintext, &nonce);
(nonce, nonce_and_ciphertext)
}
pub fn decrypt_data(key: &secretbox::Key, encrypted: &[u8]) -> Option<Vec<u8>> {
use secretbox::aead::generic_array::typenum::marker_traits::Unsigned;
let nonce_size = <XSalsa20Poly1305 as AeadCore>::NonceSize::to_usize();
let (nonce, ciphertext) = encrypted.split_at(nonce_size);
assert_eq!(nonce.len(), nonce_size);
let cipher = XSalsa20Poly1305::new(GenericArray::from_slice(key));
cipher
.decrypt(GenericArray::from_slice(nonce), ciphertext)
.ok()
}
fn sha256_digest(input: &[u8]) -> Vec<u8> {
let mut hasher = Sha256::default();
hasher.update(input);
hasher.finalize_fixed().to_vec()
}
pub fn derive_key(key: &secretbox::Key, purpose: &[u8]) -> secretbox::Key {
let hk = Hkdf::<Sha256>::new(None, key);
let mut key = secretbox::Key::default();
hk.expand(purpose, &mut key).unwrap();
key
}
pub fn derive_phase_key(side: &EitherSide, key: &secretbox::Key, phase: &Phase) -> secretbox::Key {
let side_digest: Vec<u8> = sha256_digest(side.0.as_bytes());
let phase_digest: Vec<u8> = sha256_digest(phase.0.as_bytes());
let mut purpose_vec: Vec<u8> = b"wormhole:phase:".to_vec();
purpose_vec.extend(side_digest);
purpose_vec.extend(phase_digest);
derive_key(key, &purpose_vec)
}
pub fn derive_verifier(key: &secretbox::Key) -> secretbox::Key {
derive_key(key, b"wormhole:verifier")
}
#[cfg(test)]
mod test {
use super::*;
use crate::core::EitherSide;
#[test]
fn test_extract_pake_msg() {
let s1 = "7b2270616b655f7631223a22353337363331646366643064336164386130346234663531643935336131343563386538626663373830646461393834373934656634666136656536306339663665227d";
let pake_msg = super::extract_pake_msg(&hex::decode(s1).unwrap());
assert_eq!(
pake_msg.ok(),
Some(
hex::decode("537631dcfd0d3ad8a04b4f51d953a145c8e8bfc780dda984794ef4fa6ee60c9f6e")
.unwrap()
)
);
}
#[test]
fn test_derive_key() {
let main = secretbox::Key::from_exact_iter(
hex::decode("588ba9eef353778b074413a0140205d90d7479e36e0dd4ee35bb729d26131ef1")
.unwrap(),
)
.unwrap();
let dk1 = derive_key(&main, b"purpose1");
assert_eq!(
hex::encode(dk1),
"835b5df80ce9ca46908e8524fb308649122cfbcefbeaa7e65061c6ef08ee1b2a"
);
}
#[test]
fn test_derive_phase_key() {
let main = secretbox::Key::from_exact_iter(
hex::decode("588ba9eef353778b074413a0140205d90d7479e36e0dd4ee35bb729d26131ef1")
.unwrap(),
)
.unwrap();
let dk11 = derive_phase_key(&EitherSide::from("side1"), &main, &Phase("phase1".into()));
assert_eq!(
hex::encode(&*dk11),
"3af6a61d1a111225cc8968c6ca6265efe892065c3ab46de79dda21306b062990"
);
let dk12 = derive_phase_key(&EitherSide::from("side1"), &main, &Phase("phase2".into()));
assert_eq!(
hex::encode(&*dk12),
"88a1dd12182d989ff498022a9656d1e2806f17328d8bf5d8d0c9753e4381a752"
);
let dk21 = derive_phase_key(&EitherSide::from("side2"), &main, &Phase("phase1".into()));
assert_eq!(
hex::encode(&*dk21),
"a306627b436ec23bdae3af8fa90c9ac927780d86be1831003e7f617c518ea689"
);
let dk22 = derive_phase_key(&EitherSide::from("side2"), &main, &Phase("phase2".into()));
assert_eq!(
hex::encode(&*dk22),
"bf99e3e16420f2dad33f9b1ccb0be1462b253d639dacdb50ed9496fa528d8758"
);
}
#[test]
fn test_derive_phase_key2() {
}
#[test]
fn test_encrypt_data() {
let k = secretbox::Key::from_exact_iter(
hex::decode("ddc543ef8e4629a603d39dd0307a51bb1e7adb9cb259f6b085c91d0842a18679")
.unwrap(),
)
.unwrap();
let plaintext = hex::decode("edc089a518219ec1cee184e89d2d37af").unwrap();
assert_eq!(plaintext.len(), 16);
let nonce = secretbox::Nonce::from_exact_iter(
hex::decode("2d5e43eb465aa42e750f991e425bee485f06abad7e04af80").unwrap(),
)
.unwrap();
assert_eq!(nonce.len(), 24);
let msg = encrypt_data_with_nonce(&k, &plaintext, &nonce);
assert_eq!(hex::encode(msg), "2d5e43eb465aa42e750f991e425bee485f06abad7e04af80fe318e39d0e4ce932d2b54b300c56d2cda55ee5f0488d63eb1d5f76f7919a49a");
}
#[test]
fn test_decrypt_data() {
let k = secretbox::Key::from_exact_iter(
hex::decode("ddc543ef8e4629a603d39dd0307a51bb1e7adb9cb259f6b085c91d0842a18679")
.unwrap(),
)
.unwrap();
let encrypted = hex::decode("2d5e43eb465aa42e750f991e425bee485f06abad7e04af80fe318e39d0e4ce932d2b54b300c56d2cda55ee5f0488d63eb1d5f76f7919a49a").unwrap();
match decrypt_data(&k, &encrypted) {
Some(plaintext) => {
assert_eq!(hex::encode(plaintext), "edc089a518219ec1cee184e89d2d37af");
},
None => {
panic!("failed to decrypt");
},
};
}
}