1use crate::core::*;
2use crypto_secretbox as secretbox;
3use crypto_secretbox::{
4 aead::{generic_array::GenericArray, Aead, AeadCore},
5 KeyInit, XSalsa20Poly1305,
6};
7use hkdf::Hkdf;
8use serde_derive::{Deserialize, Serialize};
9use sha2::{digest::FixedOutput, Digest, Sha256};
10use spake2::{Ed25519Group, Identity, Password, Spake2};
11
12pub trait KeyPurpose: std::fmt::Debug {}
16
17#[derive(Debug)]
19pub struct WormholeKey;
20impl KeyPurpose for WormholeKey {}
21
22#[derive(Debug)]
24#[deprecated(
25 since = "0.7.0",
26 note = "This will be a private type in the future. Open an issue if you require access to protocol intrinsics in the future"
27)]
28pub struct GenericKey;
29impl KeyPurpose for GenericKey {}
30
31#[derive(Debug, Clone, derive_more::Display, derive_more::Deref)]
37#[display("{:?}", _0)]
38#[deref(forward)]
39pub struct Key<P: KeyPurpose>(
40 #[deref]
41 #[deprecated(
42 since = "0.7.0",
43 note = "Use the AsRef<Key> implementation to get access to the secretbox key"
44 )]
45 pub Box<secretbox::Key>,
46 #[deref(ignore)] std::marker::PhantomData<P>,
47);
48
49impl Key<WormholeKey> {
50 #[cfg(feature = "transit")]
59 #[deprecated(
60 since = "0.7.0",
61 note = "This will be a private method in the future. Open an issue if you require access to protocol intrinsics in the future"
62 )]
63 pub fn derive_transit_key(&self, appid: &AppID) -> Key<crate::transit::TransitKey> {
64 let transit_purpose = format!("{}/transit-key", appid);
65 let derived_key = self.derive_subkey_from_purpose(&transit_purpose);
66 tracing::trace!(
67 "Input key: {}, Transit key: {}, Transit purpose: '{}'",
68 self.to_hex(),
69 derived_key.to_hex(),
70 &transit_purpose
71 );
72 derived_key
73 }
74}
75
76impl<P: KeyPurpose> Key<P> {
77 pub fn new(key: Box<secretbox::Key>) -> Self {
79 Self(key, std::marker::PhantomData)
80 }
81
82 pub fn to_hex(&self) -> String {
84 hex::encode(**self)
85 }
86
87 pub fn derive_subkey_from_purpose<NewP: KeyPurpose>(&self, purpose: &str) -> Key<NewP> {
91 Key(
92 Box::new(derive_key(self, purpose.as_bytes())),
93 std::marker::PhantomData,
94 )
95 }
96}
97
98#[derive(Serialize, Deserialize, Debug)]
99struct PhaseMessage {
100 #[serde(with = "hex::serde")]
101 pake_v1: Vec<u8>,
102}
103
104pub fn make_pake(password: &str, appid: &AppID) -> (Spake2<Ed25519Group>, Vec<u8>) {
109 let (pake_state, msg1) = Spake2::<Ed25519Group>::start_symmetric(
110 &Password::new(password.as_bytes()),
111 &Identity::new(appid.0.as_bytes()),
112 );
113 let pake_msg = PhaseMessage { pake_v1: msg1 };
114 let pake_msg_ser = serde_json::to_vec(&pake_msg).unwrap();
115 (pake_state, pake_msg_ser)
116}
117
118#[derive(Clone, Debug, Default, Serialize, Deserialize)]
119pub struct VersionsMessage {
120 #[serde(default)]
121 pub abilities: Vec<String>,
122 #[serde(default)]
123 pub app_versions: serde_json::Value,
124 }
126
127impl VersionsMessage {
128 pub fn new() -> Self {
129 Default::default()
130 }
131
132 pub fn set_app_versions(&mut self, versions: serde_json::Value) {
133 self.app_versions = versions;
134 }
135
136 }
140
141pub fn build_version_msg(
142 side: &MySide,
143 key: &secretbox::Key,
144 versions: &VersionsMessage,
145) -> (Phase, Vec<u8>) {
146 let phase = Phase::VERSION;
147 let data_key = derive_phase_key(side, key, &phase);
148 let plaintext = serde_json::to_vec(versions).unwrap();
149 let (_nonce, encrypted) = encrypt_data(&data_key, &plaintext);
150 (phase, encrypted)
151}
152
153pub fn extract_pake_msg(body: &[u8]) -> Result<Vec<u8>, WormholeError> {
154 serde_json::from_slice(body)
155 .map(|res: PhaseMessage| res.pake_v1)
156 .map_err(WormholeError::ProtocolJson)
157}
158
159fn encrypt_data_with_nonce(
160 key: &secretbox::Key,
161 plaintext: &[u8],
162 nonce: &secretbox::Nonce,
163) -> Vec<u8> {
164 let cipher = XSalsa20Poly1305::new(GenericArray::from_slice(key));
165 let mut ciphertext = cipher.encrypt(nonce, plaintext).unwrap();
166 let mut nonce_and_ciphertext = vec![];
167 nonce_and_ciphertext.extend_from_slice(nonce);
168 nonce_and_ciphertext.append(&mut ciphertext);
169 nonce_and_ciphertext
170}
171
172pub fn encrypt_data(key: &secretbox::Key, plaintext: &[u8]) -> (secretbox::Nonce, Vec<u8>) {
173 let nonce = secretbox::SecretBox::<secretbox::XSalsa20Poly1305>::generate_nonce(
174 &mut rand::thread_rng(),
175 );
176 let nonce_and_ciphertext = encrypt_data_with_nonce(key, plaintext, &nonce);
177 (nonce, nonce_and_ciphertext)
178}
179
180pub fn decrypt_data(key: &secretbox::Key, encrypted: &[u8]) -> Option<Vec<u8>> {
182 use secretbox::aead::generic_array::typenum::marker_traits::Unsigned;
183 let nonce_size = <XSalsa20Poly1305 as AeadCore>::NonceSize::to_usize();
184 let (nonce, ciphertext) = encrypted.split_at(nonce_size);
185 assert_eq!(nonce.len(), nonce_size);
186 let cipher = XSalsa20Poly1305::new(GenericArray::from_slice(key));
187 cipher
188 .decrypt(GenericArray::from_slice(nonce), ciphertext)
189 .ok()
190}
191
192fn sha256_digest(input: &[u8]) -> Vec<u8> {
193 let mut hasher = Sha256::default();
194 hasher.update(input);
195 hasher.finalize_fixed().to_vec()
196}
197
198pub fn derive_key(key: &secretbox::Key, purpose: &[u8]) -> secretbox::Key {
199 let hk = Hkdf::<Sha256>::new(None, key);
200 let mut key = secretbox::Key::default();
201 hk.expand(purpose, &mut key).unwrap();
202 key
203}
204
205pub fn derive_phase_key(side: &EitherSide, key: &secretbox::Key, phase: &Phase) -> secretbox::Key {
206 let side_digest: Vec<u8> = sha256_digest(side.0.as_bytes());
207 let phase_digest: Vec<u8> = sha256_digest(phase.0.as_bytes());
208 let mut purpose_vec: Vec<u8> = b"wormhole:phase:".to_vec();
209 purpose_vec.extend(side_digest);
210 purpose_vec.extend(phase_digest);
211
212 derive_key(key, &purpose_vec)
213}
214
215pub fn derive_verifier(key: &crypto_secretbox::Key) -> crypto_secretbox::Key {
216 derive_key(key, b"wormhole:verifier")
217}
218
219#[cfg(test)]
220mod test {
221 use super::*;
222 use crate::core::EitherSide;
223
224 #[test]
225 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
226 fn test_extract_pake_msg() {
227 let s1 = "7b2270616b655f7631223a22353337363331646366643064336164386130346234663531643935336131343563386538626663373830646461393834373934656634666136656536306339663665227d";
234 let pake_msg = super::extract_pake_msg(&hex::decode(s1).unwrap());
235 assert_eq!(
236 pake_msg.ok(),
237 Some(
238 hex::decode("537631dcfd0d3ad8a04b4f51d953a145c8e8bfc780dda984794ef4fa6ee60c9f6e")
239 .unwrap()
240 )
241 );
242 }
243
244 #[test]
245 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
246 fn test_derive_key() {
247 let main = secretbox::Key::from_exact_iter(
248 hex::decode("588ba9eef353778b074413a0140205d90d7479e36e0dd4ee35bb729d26131ef1")
249 .unwrap(),
250 )
251 .unwrap();
252 let dk1 = derive_key(&main, b"purpose1");
253 assert_eq!(
254 hex::encode(dk1),
255 "835b5df80ce9ca46908e8524fb308649122cfbcefbeaa7e65061c6ef08ee1b2a"
256 );
257
258 }
264
265 #[test]
266 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
267 fn test_derive_phase_key() {
268 let main = secretbox::Key::from_exact_iter(
269 hex::decode("588ba9eef353778b074413a0140205d90d7479e36e0dd4ee35bb729d26131ef1")
270 .unwrap(),
271 )
272 .unwrap();
273 let dk11 = derive_phase_key(&EitherSide::from("side1"), &main, &Phase("phase1".into()));
274 assert_eq!(
275 hex::encode(&*dk11),
276 "3af6a61d1a111225cc8968c6ca6265efe892065c3ab46de79dda21306b062990"
277 );
278 let dk12 = derive_phase_key(&EitherSide::from("side1"), &main, &Phase("phase2".into()));
279 assert_eq!(
280 hex::encode(&*dk12),
281 "88a1dd12182d989ff498022a9656d1e2806f17328d8bf5d8d0c9753e4381a752"
282 );
283 let dk21 = derive_phase_key(&EitherSide::from("side2"), &main, &Phase("phase1".into()));
284 assert_eq!(
285 hex::encode(&*dk21),
286 "a306627b436ec23bdae3af8fa90c9ac927780d86be1831003e7f617c518ea689"
287 );
288 let dk22 = derive_phase_key(&EitherSide::from("side2"), &main, &Phase("phase2".into()));
289 assert_eq!(
290 hex::encode(&*dk22),
291 "bf99e3e16420f2dad33f9b1ccb0be1462b253d639dacdb50ed9496fa528d8758"
292 );
293 }
294
295 #[test]
296 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
297 fn test_derive_phase_key2() {
298 }
322
323 #[test]
324 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
325 fn test_encrypt_data() {
326 let k = secretbox::Key::from_exact_iter(
327 hex::decode("ddc543ef8e4629a603d39dd0307a51bb1e7adb9cb259f6b085c91d0842a18679")
328 .unwrap(),
329 )
330 .unwrap();
331 let plaintext = hex::decode("edc089a518219ec1cee184e89d2d37af").unwrap();
332 assert_eq!(plaintext.len(), 16);
333 let nonce = secretbox::Nonce::from_exact_iter(
334 hex::decode("2d5e43eb465aa42e750f991e425bee485f06abad7e04af80").unwrap(),
335 )
336 .unwrap();
337 assert_eq!(nonce.len(), 24);
338 let msg = encrypt_data_with_nonce(&k, &plaintext, &nonce);
339 assert_eq!(hex::encode(msg), "2d5e43eb465aa42e750f991e425bee485f06abad7e04af80fe318e39d0e4ce932d2b54b300c56d2cda55ee5f0488d63eb1d5f76f7919a49a");
340 }
341
342 #[test]
343 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
344 fn test_decrypt_data() {
345 let k = secretbox::Key::from_exact_iter(
346 hex::decode("ddc543ef8e4629a603d39dd0307a51bb1e7adb9cb259f6b085c91d0842a18679")
347 .unwrap(),
348 )
349 .unwrap();
350 let encrypted = hex::decode("2d5e43eb465aa42e750f991e425bee485f06abad7e04af80fe318e39d0e4ce932d2b54b300c56d2cda55ee5f0488d63eb1d5f76f7919a49a").unwrap();
351 match decrypt_data(&k, &encrypted) {
352 Some(plaintext) => {
353 assert_eq!(hex::encode(plaintext), "edc089a518219ec1cee184e89d2d37af");
354 },
355 None => {
356 panic!("failed to decrypt");
357 },
358 };
359 }
360
361 }