Skip to content

Commit faf310a

Browse files
authored
Merge pull request #80 from iron-fish/fix/hughy/decrypt-legacy
adds decrypt_legacy to decrypt data from previous version
2 parents bf23fbf + d86107d commit faf310a

File tree

1 file changed

+92
-0
lines changed

1 file changed

+92
-0
lines changed

src/multienc.rs

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ use crate::alloc::borrow::ToOwned;
2727
#[cfg(not(feature = "std"))]
2828
use alloc::vec::Vec;
2929

30+
#[cfg(feature = "std")]
31+
use chacha20poly1305::aead::Aead;
32+
#[cfg(feature = "std")]
33+
use chacha20poly1305::Error;
34+
3035
pub const HEADER_SIZE: usize = 56;
3136
pub const KEY_SIZE: usize = 32;
3237

@@ -274,6 +279,93 @@ impl Header {
274279
}
275280
}
276281

282+
#[cfg(feature = "std")]
283+
pub type EncryptedKey = [u8; KEY_SIZE];
284+
285+
#[cfg(feature = "std")]
286+
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
287+
pub struct MultiRecipientBlob<Keys, Cipher>
288+
where
289+
Keys: AsRef<[EncryptedKey]>,
290+
Cipher: AsRef<[u8]>,
291+
{
292+
pub agreement_key: PublicKey,
293+
pub encrypted_keys: Keys,
294+
pub ciphertext: Cipher,
295+
}
296+
297+
#[cfg(feature = "std")]
298+
impl MultiRecipientBlob<Vec<EncryptedKey>, Vec<u8>> {
299+
pub fn deserialize_from<R: io::Read>(mut reader: R) -> io::Result<Self> {
300+
let mut agreement_key = [0u8; 32];
301+
reader.read_exact(&mut agreement_key)?;
302+
let agreement_key = PublicKey::from(agreement_key);
303+
304+
let mut encrypted_keys_len = [0u8; 4];
305+
reader.read_exact(&mut encrypted_keys_len)?;
306+
let encrypted_keys_len = u32::from_le_bytes(encrypted_keys_len) as usize;
307+
308+
let mut encrypted_keys = Vec::with_capacity(encrypted_keys_len);
309+
for _ in 0..encrypted_keys_len {
310+
let mut key = EncryptedKey::default();
311+
reader.read_exact(&mut key)?;
312+
encrypted_keys.push(key);
313+
}
314+
315+
let mut ciphertext_len = [0u8; 4];
316+
reader.read_exact(&mut ciphertext_len)?;
317+
let ciphertext_len = u32::from_le_bytes(ciphertext_len) as usize;
318+
319+
let mut ciphertext = vec![0u8; ciphertext_len];
320+
reader.read_exact(&mut ciphertext)?;
321+
322+
Ok(Self {
323+
agreement_key,
324+
encrypted_keys,
325+
ciphertext,
326+
})
327+
}
328+
}
329+
330+
/// Decrypt data produced by a previous version of [`encrypt`] that produced a
331+
/// [`MultiRecipientBlob`]
332+
#[cfg(feature = "std")]
333+
pub fn decrypt_legacy<Keys, Cipher>(
334+
secret: &Secret,
335+
blob: &MultiRecipientBlob<Keys, Cipher>,
336+
) -> Result<Vec<u8>, Error>
337+
where
338+
Keys: AsRef<[EncryptedKey]>,
339+
Cipher: AsRef<[u8]>,
340+
{
341+
let nonce = Nonce::default();
342+
let shared_secret = secret
343+
.decryption_key()
344+
.diffie_hellman(&blob.agreement_key)
345+
.to_bytes();
346+
347+
// Try to decrypt each encryption key one by one, and use them to attempt to decrypt the data,
348+
// until the data is recovered.
349+
blob.encrypted_keys
350+
.as_ref()
351+
.iter()
352+
.filter_map(|encrypted_key| {
353+
// Decrypt the encryption key with X25519 + ChaCha20. This will always succeed, even if
354+
// the encryption key was not for this participant (in which case, it will result in
355+
// random bytes).
356+
let mut encryption_key = *encrypted_key;
357+
let mut cipher = ChaCha20::new((&shared_secret).into(), &nonce);
358+
cipher.apply_keystream(&mut encryption_key);
359+
360+
// Decrypt the data with ChaCha20Poly1305. This will fail if the encryption key was not
361+
// for this participant (or if the encryption key was tampered).
362+
let cipher = ChaCha20Poly1305::new((&encryption_key).into());
363+
cipher.decrypt(&nonce, blob.ciphertext.as_ref()).ok()
364+
})
365+
.next()
366+
.ok_or(Error)
367+
}
368+
277369
#[cfg(test)]
278370
mod tests {
279371
mod detached {

0 commit comments

Comments
 (0)