Skip to content

Commit d86107d

Browse files
author
Hugh Cunningham
committed
adds decrypt_legacy to decrypt data from previous version
we recently made changes to the 'multienc' module that changed the structure of encrypted data since it's possible that users have exported multisig accounts encrypted using the old legacy encryption methods we need to support decryption of that data adds MultiRecipientBlob back into the multienc module behind the std feature and defines 'decrypt_legacy' for use decrypting these blobs
1 parent bf23fbf commit d86107d

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)