@@ -27,6 +27,11 @@ use crate::alloc::borrow::ToOwned;
27
27
#[ cfg( not( feature = "std" ) ) ]
28
28
use alloc:: vec:: Vec ;
29
29
30
+ #[ cfg( feature = "std" ) ]
31
+ use chacha20poly1305:: aead:: Aead ;
32
+ #[ cfg( feature = "std" ) ]
33
+ use chacha20poly1305:: Error ;
34
+
30
35
pub const HEADER_SIZE : usize = 56 ;
31
36
pub const KEY_SIZE : usize = 32 ;
32
37
@@ -274,6 +279,93 @@ impl Header {
274
279
}
275
280
}
276
281
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
+
277
369
#[ cfg( test) ]
278
370
mod tests {
279
371
mod detached {
0 commit comments