pgp/crypto/
sym.rs

1use aes::{Aes128, Aes192, Aes256};
2use blowfish::Blowfish;
3use camellia::{Camellia128, Camellia192, Camellia256};
4use cast5::Cast5;
5use cfb_mode::{
6    cipher::{AsyncStreamCipher, KeyIvInit},
7    BufDecryptor, BufEncryptor, Decryptor, Encryptor,
8};
9use cipher::{BlockCipher, BlockDecrypt, BlockEncryptMut};
10use des::TdesEde3;
11use idea::Idea;
12use log::debug;
13use num_enum::{FromPrimitive, IntoPrimitive};
14use rand::{CryptoRng, Rng};
15use twofish::Twofish;
16use zeroize::Zeroizing;
17
18use crate::{
19    composed::RawSessionKey,
20    errors::{bail, ensure, unimplemented_err, Error, Result},
21};
22
23mod decryptor;
24mod encryptor;
25
26pub use self::{decryptor::StreamDecryptor, encryptor::StreamEncryptor};
27
28fn decrypt<MODE>(key: &[u8], iv: &[u8], prefix: &mut [u8], data: &mut [u8]) -> Result<()>
29where
30    MODE: BlockDecrypt + BlockEncryptMut + BlockCipher,
31    BufDecryptor<MODE>: KeyIvInit,
32{
33    let mut mode = BufDecryptor::<MODE>::new_from_slices(key, iv)?;
34
35    // We do not do use "quick check" here.
36    // See the "Security Considerations" section
37    // in <https://www.rfc-editor.org/rfc/rfc9580.html#name-risks-of-a-quick-check-orac>
38    // and the paper <https://eprint.iacr.org/2005/033>
39    // for details.
40
41    mode.decrypt(prefix);
42
43    mode.decrypt(data);
44    Ok(())
45}
46
47/// Legacy format using custom resync
48fn decrypt_resync<MODE>(key: &[u8], iv: &[u8], prefix: &mut [u8], data: &mut [u8]) -> Result<()>
49where
50    MODE: BlockDecrypt + BlockEncryptMut + BlockCipher,
51    BufDecryptor<MODE>: KeyIvInit,
52{
53    let mut mode = BufDecryptor::<MODE>::new_from_slices(key, iv)?;
54
55    // We do not do use "quick check" here.
56    // See the "Security Considerations" section
57    // in <https://www.rfc-editor.org/rfc/rfc9580.html#name-risks-of-a-quick-check-orac>
58    // and the paper <https://eprint.iacr.org/2005/033>
59    // for details.
60
61    let encrypted_prefix = prefix[2..].to_vec();
62    mode.decrypt(prefix);
63    mode = BufDecryptor::<MODE>::new_from_slices(key, &encrypted_prefix)?;
64
65    mode.decrypt(data);
66    Ok(())
67}
68
69fn encrypt<MODE>(key: &[u8], iv: &[u8], prefix: &mut [u8], data: &mut [u8]) -> Result<()>
70where
71    MODE: BlockDecrypt + BlockEncryptMut + BlockCipher,
72    BufEncryptor<MODE>: KeyIvInit,
73{
74    let mut mode = BufEncryptor::<MODE>::new_from_slices(key, iv)?;
75    mode.encrypt(prefix);
76
77    mode.encrypt(data);
78
79    Ok(())
80}
81
82/// Legacy format using OpengPGP CFB Mode
83///
84/// <https://datatracker.ietf.org/doc/html/rfc4880.html#section-13.9>
85fn encrypt_resync<MODE>(key: &[u8], iv: &[u8], prefix: &mut [u8], data: &mut [u8]) -> Result<()>
86where
87    MODE: BlockDecrypt + BlockEncryptMut + BlockCipher,
88    BufEncryptor<MODE>: KeyIvInit,
89{
90    let mut mode = BufEncryptor::<MODE>::new_from_slices(key, iv)?;
91    mode.encrypt(prefix);
92
93    // resync
94    mode = BufEncryptor::<MODE>::new_from_slices(key, &prefix[2..])?;
95    mode.encrypt(data);
96
97    Ok(())
98}
99
100/// Available symmetric key algorithms.
101/// Ref: <https://www.rfc-editor.org/rfc/rfc9580.html#name-symmetric-key-algorithms>
102#[derive(Debug, PartialEq, Eq, Copy, Clone, FromPrimitive, IntoPrimitive)]
103#[cfg_attr(test, derive(proptest_derive::Arbitrary))]
104#[repr(u8)]
105#[non_exhaustive]
106pub enum SymmetricKeyAlgorithm {
107    /// Plaintext or unencrypted data
108    #[cfg_attr(test, proptest(skip))]
109    Plaintext = 0,
110    /// IDEA
111    IDEA = 1,
112    /// Triple-DES
113    TripleDES = 2,
114    /// CAST5
115    CAST5 = 3,
116    /// Blowfish
117    Blowfish = 4,
118    // 5 & 6 are reserved for DES/SK
119    /// AES with 128-bit key
120    AES128 = 7,
121    /// AES with 192-bit key
122    AES192 = 8,
123    /// AES with 256-bit key
124    AES256 = 9,
125    /// Twofish with 256-bit key
126    Twofish = 10,
127    /// [Camellia](https://tools.ietf.org/html/rfc5581#section-3) with 128-bit key
128    Camellia128 = 11,
129    /// [Camellia](https://tools.ietf.org/html/rfc5581#section-3) with 192-bit key
130    Camellia192 = 12,
131    /// [Camellia](https://tools.ietf.org/html/rfc5581#section-3) with 256-bit key
132    Camellia256 = 13,
133    Private10 = 110,
134
135    #[num_enum(catch_all)]
136    Other(#[cfg_attr(test, proptest(strategy = "111u8.."))] u8),
137}
138
139impl Default for SymmetricKeyAlgorithm {
140    fn default() -> Self {
141        Self::AES128
142    }
143}
144
145impl zeroize::DefaultIsZeroes for SymmetricKeyAlgorithm {}
146
147impl SymmetricKeyAlgorithm {
148    /// The size of a single block in bytes.
149    /// Based on <https://github.com/gpg/libgcrypt/blob/master/cipher>
150    pub fn block_size(self) -> usize {
151        match self {
152            SymmetricKeyAlgorithm::Plaintext => 0,
153            SymmetricKeyAlgorithm::IDEA => 8,
154            SymmetricKeyAlgorithm::TripleDES => 8,
155            SymmetricKeyAlgorithm::CAST5 => 8,
156            SymmetricKeyAlgorithm::Blowfish => 8,
157            SymmetricKeyAlgorithm::AES128 => 16,
158            SymmetricKeyAlgorithm::AES192 => 16,
159            SymmetricKeyAlgorithm::AES256 => 16,
160            SymmetricKeyAlgorithm::Twofish => 16,
161            SymmetricKeyAlgorithm::Camellia128 => 16,
162            SymmetricKeyAlgorithm::Camellia192 => 16,
163            SymmetricKeyAlgorithm::Camellia256 => 16,
164            SymmetricKeyAlgorithm::Private10 | SymmetricKeyAlgorithm::Other(_) => 0,
165        }
166    }
167
168    #[cfg(test)]
169    pub(crate) fn cfb_prefix_size(&self) -> usize {
170        self.block_size() + 2
171    }
172
173    /// The size of a single block in bytes.
174    /// Based on <https://github.com/gpg/libgcrypt/blob/master/cipher>
175    pub const fn key_size(self) -> usize {
176        match self {
177            SymmetricKeyAlgorithm::Plaintext => 0,
178            SymmetricKeyAlgorithm::IDEA => 16,
179            SymmetricKeyAlgorithm::TripleDES => 24,
180            SymmetricKeyAlgorithm::CAST5 => 16,
181            // TODO: Validate this is the right key size.
182            SymmetricKeyAlgorithm::Blowfish => 16, //56,
183            SymmetricKeyAlgorithm::AES128 => 16,
184            SymmetricKeyAlgorithm::AES192 => 24,
185            SymmetricKeyAlgorithm::AES256 => 32,
186            SymmetricKeyAlgorithm::Twofish => 32,
187            SymmetricKeyAlgorithm::Camellia128 => 16,
188            SymmetricKeyAlgorithm::Camellia192 => 24,
189            SymmetricKeyAlgorithm::Camellia256 => 32,
190
191            SymmetricKeyAlgorithm::Private10 | SymmetricKeyAlgorithm::Other(_) => 0,
192        }
193    }
194
195    /// Decrypt the data using CFB mode, without padding. Overwrites the input.
196    /// Uses an IV of all zeroes, as specified in the openpgp cfb mode. Does
197    /// resynchronization.
198    pub fn decrypt(self, key: &[u8], prefix: &mut [u8], ciphertext: &mut [u8]) -> Result<()> {
199        debug!("unprotected decrypt");
200        let iv_vec = vec![0u8; self.block_size()];
201        self.decrypt_with_iv_resync(key, &iv_vec, prefix, ciphertext)?;
202        Ok(())
203    }
204
205    /// Decrypt the data using CFB mode, without padding. Overwrites the input.
206    /// Uses an IV of all zeroes, as specified in the openpgp cfb mode.
207    /// Does not do resynchronization.
208    ///
209    /// The result will be in `ciphertext`.
210    pub fn decrypt_protected(
211        self,
212        key: &[u8],
213        prefix: &mut [u8],
214        ciphertext: &mut Vec<u8>,
215    ) -> Result<()> {
216        debug!("protected decrypt");
217
218        let iv_vec = vec![0u8; self.block_size()];
219        self.decrypt_with_iv(key, &iv_vec, prefix, ciphertext)?;
220
221        // MDC is 1 byte packet tag, 1 byte length prefix and 20 bytes SHA1 hash.
222        const MDC_LEN: usize = 22;
223        let mdc = ciphertext.split_off(ciphertext.len() - MDC_LEN);
224
225        // We use regular sha1 for MDC, not sha1_checked. Collisions are not currently a concern with MDC.
226        let sha1 = calculate_sha1_unchecked([prefix, &ciphertext[..], &mdc[0..2]]);
227        if mdc[0] != 0xD3 || // Invalid MDC tag
228           mdc[1] != 0x14 || // Invalid MDC length
229            mdc[2..] != sha1[..]
230        {
231            return Err(Error::MdcError);
232        }
233
234        Ok(())
235    }
236
237    /// Decrypt the data using CFB mode, without padding. Overwrites the input.
238    ///
239    /// OpenPGP CFB mode uses an initialization vector (IV) of all zeros, and
240    /// prefixes the plaintext with BS+2 octets of random data, such that
241    /// octets BS+1 and BS+2 match octets BS-1 and BS.  It does a CFB
242    /// resynchronization after encrypting those BS+2 octets.
243    ///
244    /// Thus, for an algorithm that has a block size of 8 octets (64 bits),
245    /// the IV is 10 octets long and octets 7 and 8 of the IV are the same as
246    /// octets 9 and 10.  For an algorithm with a block size of 16 octets
247    /// (128 bits), the IV is 18 octets long, and octets 17 and 18 replicate
248    /// octets 15 and 16.  Those extra two octets are an easy check for a
249    /// correct key.
250    pub fn decrypt_with_iv(
251        self,
252        key: &[u8],
253        iv_vec: &[u8],
254        encrypted_prefix: &mut [u8],
255        encrypted_data: &mut [u8],
256    ) -> Result<()> {
257        let bs = self.block_size();
258        let ciphertext_len = encrypted_prefix.len() + encrypted_data.len();
259        ensure!(bs + 2 < ciphertext_len, "invalid ciphertext");
260
261        match self {
262            SymmetricKeyAlgorithm::Plaintext => {
263                bail!("'Plaintext' is not a legal cipher for encrypted data")
264            }
265            SymmetricKeyAlgorithm::IDEA => {
266                decrypt::<Idea>(key, iv_vec, encrypted_prefix, encrypted_data)?;
267            }
268            SymmetricKeyAlgorithm::TripleDES => {
269                decrypt::<TdesEde3>(key, iv_vec, encrypted_prefix, encrypted_data)?;
270            }
271            SymmetricKeyAlgorithm::CAST5 => {
272                decrypt::<Cast5>(key, iv_vec, encrypted_prefix, encrypted_data)?;
273            }
274            SymmetricKeyAlgorithm::Blowfish => {
275                decrypt::<Blowfish>(key, iv_vec, encrypted_prefix, encrypted_data)?;
276            }
277            SymmetricKeyAlgorithm::AES128 => {
278                decrypt::<Aes128>(key, iv_vec, encrypted_prefix, encrypted_data)?;
279            }
280            SymmetricKeyAlgorithm::AES192 => {
281                decrypt::<Aes192>(key, iv_vec, encrypted_prefix, encrypted_data)?;
282            }
283            SymmetricKeyAlgorithm::AES256 => {
284                decrypt::<Aes256>(key, iv_vec, encrypted_prefix, encrypted_data)?;
285            }
286            SymmetricKeyAlgorithm::Twofish => {
287                decrypt::<Twofish>(key, iv_vec, encrypted_prefix, encrypted_data)?;
288            }
289            SymmetricKeyAlgorithm::Camellia128 => {
290                decrypt::<Camellia128>(key, iv_vec, encrypted_prefix, encrypted_data)?;
291            }
292            SymmetricKeyAlgorithm::Camellia192 => {
293                decrypt::<Camellia192>(key, iv_vec, encrypted_prefix, encrypted_data)?;
294            }
295            SymmetricKeyAlgorithm::Camellia256 => {
296                decrypt::<Camellia256>(key, iv_vec, encrypted_prefix, encrypted_data)?
297            }
298            SymmetricKeyAlgorithm::Private10 | SymmetricKeyAlgorithm::Other(_) => {
299                unimplemented_err!("SymmetricKeyAlgorithm {} is unsupported", u8::from(self))
300            }
301        }
302
303        Ok(())
304    }
305
306    /// Applies the legacy resyncing
307    pub fn decrypt_with_iv_resync(
308        self,
309        key: &[u8],
310        iv_vec: &[u8],
311        encrypted_prefix: &mut [u8],
312        encrypted_data: &mut [u8],
313    ) -> Result<()> {
314        let bs = self.block_size();
315        let ciphertext_len = encrypted_prefix.len() + encrypted_data.len();
316        ensure!(bs + 2 < ciphertext_len, "invalid ciphertext");
317
318        match self {
319            SymmetricKeyAlgorithm::Plaintext => {
320                bail!("'Plaintext' is not a legal cipher for encrypted data")
321            }
322            SymmetricKeyAlgorithm::IDEA => {
323                decrypt_resync::<Idea>(key, iv_vec, encrypted_prefix, encrypted_data)?;
324            }
325            SymmetricKeyAlgorithm::TripleDES => {
326                decrypt_resync::<TdesEde3>(key, iv_vec, encrypted_prefix, encrypted_data)?;
327            }
328            SymmetricKeyAlgorithm::CAST5 => {
329                decrypt_resync::<Cast5>(key, iv_vec, encrypted_prefix, encrypted_data)?;
330            }
331            SymmetricKeyAlgorithm::Blowfish => {
332                decrypt_resync::<Blowfish>(key, iv_vec, encrypted_prefix, encrypted_data)?;
333            }
334            SymmetricKeyAlgorithm::AES128 => {
335                decrypt_resync::<Aes128>(key, iv_vec, encrypted_prefix, encrypted_data)?;
336            }
337            SymmetricKeyAlgorithm::AES192 => {
338                decrypt_resync::<Aes192>(key, iv_vec, encrypted_prefix, encrypted_data)?;
339            }
340            SymmetricKeyAlgorithm::AES256 => {
341                decrypt_resync::<Aes256>(key, iv_vec, encrypted_prefix, encrypted_data)?;
342            }
343            SymmetricKeyAlgorithm::Twofish => {
344                decrypt_resync::<Twofish>(key, iv_vec, encrypted_prefix, encrypted_data)?;
345            }
346            SymmetricKeyAlgorithm::Camellia128 => {
347                decrypt_resync::<Camellia128>(key, iv_vec, encrypted_prefix, encrypted_data)?;
348            }
349            SymmetricKeyAlgorithm::Camellia192 => {
350                decrypt_resync::<Camellia192>(key, iv_vec, encrypted_prefix, encrypted_data)?;
351            }
352            SymmetricKeyAlgorithm::Camellia256 => {
353                decrypt_resync::<Camellia256>(key, iv_vec, encrypted_prefix, encrypted_data)?
354            }
355            SymmetricKeyAlgorithm::Private10 | SymmetricKeyAlgorithm::Other(_) => {
356                unimplemented_err!("SymmetricKeyAlgorithm {} is unsupported", u8::from(self))
357            }
358        }
359
360        Ok(())
361    }
362
363    /// Decrypt the data using CFB mode, without padding. Overwrites the input.
364    /// This is regular CFB, not OpenPgP CFB.
365    pub fn decrypt_with_iv_regular(
366        self,
367        key: &[u8],
368        iv_vec: &[u8],
369        ciphertext: &mut [u8],
370    ) -> Result<()> {
371        match self {
372            SymmetricKeyAlgorithm::Plaintext => {
373                bail!("'Plaintext' is not a legal cipher for encrypted data")
374            }
375            SymmetricKeyAlgorithm::IDEA => {
376                Decryptor::<Idea>::new_from_slices(key, iv_vec)?.decrypt(ciphertext);
377            }
378            SymmetricKeyAlgorithm::TripleDES => {
379                Decryptor::<TdesEde3>::new_from_slices(key, iv_vec)?.decrypt(ciphertext);
380            }
381            SymmetricKeyAlgorithm::CAST5 => {
382                Decryptor::<Cast5>::new_from_slices(key, iv_vec)?.decrypt(ciphertext);
383            }
384            SymmetricKeyAlgorithm::Blowfish => {
385                Decryptor::<Blowfish>::new_from_slices(key, iv_vec)?.decrypt(ciphertext);
386            }
387            SymmetricKeyAlgorithm::AES128 => {
388                Decryptor::<Aes128>::new_from_slices(key, iv_vec)?.decrypt(ciphertext);
389            }
390            SymmetricKeyAlgorithm::AES192 => {
391                Decryptor::<Aes192>::new_from_slices(key, iv_vec)?.decrypt(ciphertext);
392            }
393            SymmetricKeyAlgorithm::AES256 => {
394                Decryptor::<Aes256>::new_from_slices(key, iv_vec)?.decrypt(ciphertext);
395            }
396            SymmetricKeyAlgorithm::Twofish => {
397                Decryptor::<Twofish>::new_from_slices(key, iv_vec)?.decrypt(ciphertext);
398            }
399            SymmetricKeyAlgorithm::Camellia128 => {
400                Decryptor::<Camellia128>::new_from_slices(key, iv_vec)?.decrypt(ciphertext);
401            }
402            SymmetricKeyAlgorithm::Camellia192 => {
403                Decryptor::<Camellia192>::new_from_slices(key, iv_vec)?.decrypt(ciphertext);
404            }
405            SymmetricKeyAlgorithm::Camellia256 => {
406                Decryptor::<Camellia256>::new_from_slices(key, iv_vec)?.decrypt(ciphertext);
407            }
408            SymmetricKeyAlgorithm::Private10 | SymmetricKeyAlgorithm::Other(_) => {
409                unimplemented_err!("SymmetricKeyAlgorithm {} is unsupported", u8::from(self))
410            }
411        }
412
413        Ok(())
414    }
415
416    /// Encrypt the data using CFB mode, without padding. Overwrites the input.
417    /// Uses an IV of all zeroes, as specified in the openpgp cfb mode.
418    pub fn encrypt<R: CryptoRng + Rng>(
419        self,
420        mut rng: R,
421        key: &[u8],
422        plaintext: &[u8],
423    ) -> Result<Vec<u8>> {
424        debug!("encrypt unprotected");
425
426        let iv_vec = vec![0u8; self.block_size()];
427
428        let bs = self.block_size();
429
430        let prefix_len = bs + 2;
431        let plaintext_len = plaintext.len();
432
433        let mut ciphertext = vec![0u8; prefix_len + plaintext_len];
434        // prefix
435        rng.fill_bytes(&mut ciphertext[..bs]);
436
437        // add quick check
438        ciphertext[bs] = ciphertext[bs - 2];
439        ciphertext[bs + 1] = ciphertext[bs - 1];
440
441        // plaintext
442        ciphertext[prefix_len..].copy_from_slice(plaintext);
443
444        self.encrypt_with_iv_resync(key, &iv_vec, &mut ciphertext)?;
445
446        Ok(ciphertext)
447    }
448
449    pub fn encrypt_protected<R: CryptoRng + Rng>(
450        self,
451        mut rng: R,
452        key: &[u8],
453        plaintext: &[u8],
454    ) -> Result<Vec<u8>> {
455        // We use regular sha1 for MDC, not sha1_checked. Collisions are not currently a concern with MDC.
456        use sha1::{Digest, Sha1};
457
458        debug!("protected encrypt");
459
460        // MDC is 1 byte packet tag, 1 byte length prefix and 20 bytes SHA1 hash.
461        let mdc_len = 22;
462
463        let bs = self.block_size();
464
465        let prefix_len = bs + 2;
466        let plaintext_len = plaintext.len();
467
468        let mut ciphertext = vec![0u8; prefix_len + plaintext_len + mdc_len];
469
470        // prefix
471        rng.fill_bytes(&mut ciphertext[..bs]);
472
473        // add quick check
474        ciphertext[bs] = ciphertext[bs - 2];
475        ciphertext[bs + 1] = ciphertext[bs - 1];
476
477        // plaintext
478        ciphertext[prefix_len..(prefix_len + plaintext_len)].copy_from_slice(plaintext);
479        // mdc header
480        ciphertext[prefix_len + plaintext_len] = 0xD3;
481        ciphertext[prefix_len + plaintext_len + 1] = 0x14;
482        // mdc body
483        let checksum = &Sha1::digest(&ciphertext[..(prefix_len + plaintext_len + 2)])[..20];
484        ciphertext[(prefix_len + plaintext_len + 2)..].copy_from_slice(checksum);
485
486        // IV is all zeroes
487        let iv_vec = vec![0u8; self.block_size()];
488
489        self.encrypt_with_iv(key, &iv_vec, &mut ciphertext)?;
490
491        Ok(ciphertext)
492    }
493
494    pub fn encrypted_protected_len(&self, plaintext_len: usize) -> usize {
495        self.encrypted_protected_overhead() + plaintext_len
496    }
497
498    pub fn encrypted_protected_overhead(&self) -> usize {
499        // See https://www.rfc-editor.org/rfc/rfc9580.html#name-version-1-symmetrically-enc
500
501        // One "block size" of random
502        self.block_size() +
503                // 2 bytes "quick check"
504                2 +
505                // MDC (1 byte tag + 1 byte digest size + SHA1 digest)
506                22
507    }
508
509    pub fn stream_encryptor<R, I>(
510        self,
511        rng: R,
512        key: &[u8],
513        plaintext: I,
514    ) -> Result<StreamEncryptor<I>>
515    where
516        R: Rng + CryptoRng,
517        I: std::io::Read,
518    {
519        StreamEncryptor::new(rng, self, key, plaintext)
520    }
521
522    /// Protected decryption stream
523    pub fn stream_decryptor_protected<R>(
524        self,
525        key: &[u8],
526        ciphertext: R,
527    ) -> Result<StreamDecryptor<R>>
528    where
529        R: std::io::BufRead,
530    {
531        StreamDecryptor::new(self, true, key, ciphertext)
532    }
533
534    /// Unprotected decryption stream
535    pub fn stream_decryptor_unprotected<R>(
536        self,
537        key: &[u8],
538        ciphertext: R,
539    ) -> Result<StreamDecryptor<R>>
540    where
541        R: std::io::BufRead,
542    {
543        StreamDecryptor::new(self, false, key, ciphertext)
544    }
545
546    /// Encrypt the data using CFB mode, without padding. Overwrites the input.
547    ///
548    /// OpenPGP CFB mode uses an initialization vector (IV) of all zeros, and
549    /// prefixes the plaintext with BS+2 octets of random data, such that
550    /// octets BS+1 and BS+2 match octets BS-1 and BS. It does a CFB
551    /// resynchronization after encrypting those BS+2 octets.
552    pub fn encrypt_with_iv(self, key: &[u8], iv_vec: &[u8], ciphertext: &mut [u8]) -> Result<()> {
553        let bs = self.block_size();
554
555        let (prefix, data) = ciphertext.split_at_mut(bs + 2);
556
557        {
558            match self {
559                SymmetricKeyAlgorithm::Plaintext => {
560                    bail!("'Plaintext' is not a legal cipher for encrypted data")
561                }
562                SymmetricKeyAlgorithm::IDEA => {
563                    encrypt::<Idea>(key, iv_vec, prefix, data)?;
564                }
565                SymmetricKeyAlgorithm::TripleDES => {
566                    encrypt::<TdesEde3>(key, iv_vec, prefix, data)?;
567                }
568                SymmetricKeyAlgorithm::CAST5 => {
569                    encrypt::<Cast5>(key, iv_vec, prefix, data)?;
570                }
571                SymmetricKeyAlgorithm::Blowfish => {
572                    encrypt::<Blowfish>(key, iv_vec, prefix, data)?;
573                }
574                SymmetricKeyAlgorithm::AES128 => {
575                    encrypt::<Aes128>(key, iv_vec, prefix, data)?;
576                }
577                SymmetricKeyAlgorithm::AES192 => {
578                    encrypt::<Aes192>(key, iv_vec, prefix, data)?;
579                }
580                SymmetricKeyAlgorithm::AES256 => encrypt::<Aes256>(key, iv_vec, prefix, data)?,
581                SymmetricKeyAlgorithm::Twofish => {
582                    encrypt::<Twofish>(key, iv_vec, prefix, data)?;
583                }
584                SymmetricKeyAlgorithm::Camellia128 => {
585                    encrypt::<Camellia128>(key, iv_vec, prefix, data)?;
586                }
587                SymmetricKeyAlgorithm::Camellia192 => {
588                    encrypt::<Camellia192>(key, iv_vec, prefix, data)?;
589                }
590                SymmetricKeyAlgorithm::Camellia256 => {
591                    encrypt::<Camellia256>(key, iv_vec, prefix, data)?;
592                }
593                SymmetricKeyAlgorithm::Private10 | SymmetricKeyAlgorithm::Other(_) => {
594                    bail!("SymmetricKeyAlgorithm {} is unsupported", u8::from(self))
595                }
596            }
597        }
598
599        Ok(())
600    }
601
602    /// Uses legacy resycing
603    pub fn encrypt_with_iv_resync(
604        self,
605        key: &[u8],
606        iv_vec: &[u8],
607        ciphertext: &mut [u8],
608    ) -> Result<()> {
609        let bs = self.block_size();
610
611        let (prefix, data) = ciphertext.split_at_mut(bs + 2);
612
613        {
614            match self {
615                SymmetricKeyAlgorithm::Plaintext => {
616                    bail!("'Plaintext' is not a legal cipher for encrypted data")
617                }
618                SymmetricKeyAlgorithm::IDEA => {
619                    encrypt_resync::<Idea>(key, iv_vec, prefix, data)?;
620                }
621                SymmetricKeyAlgorithm::TripleDES => {
622                    encrypt_resync::<TdesEde3>(key, iv_vec, prefix, data)?;
623                }
624                SymmetricKeyAlgorithm::CAST5 => {
625                    encrypt_resync::<Cast5>(key, iv_vec, prefix, data)?;
626                }
627                SymmetricKeyAlgorithm::Blowfish => {
628                    encrypt_resync::<Blowfish>(key, iv_vec, prefix, data)?;
629                }
630                SymmetricKeyAlgorithm::AES128 => {
631                    encrypt_resync::<Aes128>(key, iv_vec, prefix, data)?;
632                }
633                SymmetricKeyAlgorithm::AES192 => {
634                    encrypt_resync::<Aes192>(key, iv_vec, prefix, data)?;
635                }
636                SymmetricKeyAlgorithm::AES256 => {
637                    encrypt_resync::<Aes256>(key, iv_vec, prefix, data)?
638                }
639                SymmetricKeyAlgorithm::Twofish => {
640                    encrypt_resync::<Twofish>(key, iv_vec, prefix, data)?;
641                }
642                SymmetricKeyAlgorithm::Camellia128 => {
643                    encrypt_resync::<Camellia128>(key, iv_vec, prefix, data)?;
644                }
645                SymmetricKeyAlgorithm::Camellia192 => {
646                    encrypt_resync::<Camellia192>(key, iv_vec, prefix, data)?;
647                }
648                SymmetricKeyAlgorithm::Camellia256 => {
649                    encrypt_resync::<Camellia256>(key, iv_vec, prefix, data)?;
650                }
651                SymmetricKeyAlgorithm::Private10 | SymmetricKeyAlgorithm::Other(_) => {
652                    bail!("SymmetricKeyAlgorithm {} is unsupported", u8::from(self))
653                }
654            }
655        }
656
657        Ok(())
658    }
659
660    /// Encrypt the data using CFB mode, without padding. Overwrites the input.
661    pub fn encrypt_with_iv_regular(
662        self,
663        key: &[u8],
664        iv_vec: &[u8],
665        plaintext: &mut [u8],
666    ) -> Result<()> {
667        match self {
668            SymmetricKeyAlgorithm::Plaintext => {
669                bail!("'Plaintext' is not a legal cipher for encrypted data")
670            }
671            SymmetricKeyAlgorithm::IDEA => {
672                Encryptor::<Idea>::new_from_slices(key, iv_vec)?.encrypt(plaintext);
673            }
674            SymmetricKeyAlgorithm::TripleDES => {
675                Encryptor::<TdesEde3>::new_from_slices(key, iv_vec)?.encrypt(plaintext);
676            }
677            SymmetricKeyAlgorithm::CAST5 => {
678                Encryptor::<Cast5>::new_from_slices(key, iv_vec)?.encrypt(plaintext);
679            }
680            SymmetricKeyAlgorithm::Blowfish => {
681                Encryptor::<Blowfish>::new_from_slices(key, iv_vec)?.encrypt(plaintext);
682            }
683            SymmetricKeyAlgorithm::AES128 => {
684                Encryptor::<Aes128>::new_from_slices(key, iv_vec)?.encrypt(plaintext);
685            }
686            SymmetricKeyAlgorithm::AES192 => {
687                Encryptor::<Aes192>::new_from_slices(key, iv_vec)?.encrypt(plaintext);
688            }
689            SymmetricKeyAlgorithm::AES256 => {
690                Encryptor::<Aes256>::new_from_slices(key, iv_vec)?.encrypt(plaintext);
691            }
692            SymmetricKeyAlgorithm::Twofish => {
693                Encryptor::<Twofish>::new_from_slices(key, iv_vec)?.encrypt(plaintext);
694            }
695            SymmetricKeyAlgorithm::Camellia128 => {
696                Encryptor::<Camellia128>::new_from_slices(key, iv_vec)?.encrypt(plaintext);
697            }
698            SymmetricKeyAlgorithm::Camellia192 => {
699                Encryptor::<Camellia192>::new_from_slices(key, iv_vec)?.encrypt(plaintext);
700            }
701            SymmetricKeyAlgorithm::Camellia256 => {
702                Encryptor::<Camellia256>::new_from_slices(key, iv_vec)?.encrypt(plaintext);
703            }
704            SymmetricKeyAlgorithm::Private10 | SymmetricKeyAlgorithm::Other(_) => {
705                unimplemented_err!("SymmetricKeyAlgorithm {} is unsupported", u8::from(self))
706            }
707        }
708        Ok(())
709    }
710
711    /// Generate a new session key.
712    pub fn new_session_key<R: Rng + CryptoRng>(self, mut rng: R) -> RawSessionKey {
713        let mut session_key = Zeroizing::new(vec![0u8; self.key_size()]);
714        rng.fill_bytes(&mut session_key);
715        session_key.into()
716    }
717}
718
719#[inline]
720fn calculate_sha1_unchecked<I, T>(data: I) -> [u8; 20]
721where
722    T: AsRef<[u8]>,
723    I: IntoIterator<Item = T>,
724{
725    use sha1::{Digest, Sha1};
726
727    let mut digest = Sha1::new();
728    for chunk in data {
729        digest.update(chunk.as_ref());
730    }
731    digest.finalize().into()
732}
733
734#[cfg(test)]
735mod tests {
736    use std::{io::Read, time::Instant};
737
738    use log::info;
739    use rand::{Rng, SeedableRng};
740    use rand_chacha::ChaCha8Rng;
741
742    use super::*;
743
744    macro_rules! roundtrip_unprotected {
745        ($name:ident, $alg:path) => {
746            #[test]
747            fn $name() {
748                pretty_env_logger::try_init().ok();
749
750                let mut data_rng = ChaCha8Rng::seed_from_u64(0);
751
752                const MAX_SIZE: usize = 2048;
753
754                // Unprotected
755                for i in 1..MAX_SIZE {
756                    info!("Size {}", i);
757                    let mut data = vec![0u8; i];
758                    data_rng.fill(&mut data[..]);
759                    let mut key = vec![0u8; $alg.key_size()];
760                    data_rng.fill(&mut key[..]);
761
762                    info!("unprotected encrypt");
763                    let mut rng = ChaCha8Rng::seed_from_u64(8);
764                    let ciphertext = $alg.encrypt(&mut rng, &key, &data).unwrap();
765                    assert_ne!(data, ciphertext);
766
767                    {
768                        info!("unprotected decrypt");
769                        let mut ciphertext = ciphertext.clone();
770                        let mut plaintext = ciphertext.split_off($alg.cfb_prefix_size());
771                        let mut prefix = ciphertext;
772                        $alg.decrypt(&key, &mut prefix, &mut plaintext).unwrap();
773                        assert_eq!(
774                            hex::encode(&data),
775                            hex::encode(&plaintext),
776                            "unprotected decrypt"
777                        );
778                    }
779
780                    {
781                        info!("unprotected decrypt streaming");
782                        dbg!(ciphertext.len(), $alg.cfb_prefix_size());
783                        let mut input = std::io::Cursor::new(&ciphertext);
784                        let mut decryptor =
785                            $alg.stream_decryptor_unprotected(&key, &mut input).unwrap();
786                        let mut plaintext = Vec::new();
787                        decryptor.read_to_end(&mut plaintext).unwrap();
788                        assert_eq!(
789                            hex::encode(&data),
790                            hex::encode(&plaintext),
791                            "stream decrypt failed"
792                        );
793                    }
794                }
795            }
796        };
797    }
798
799    macro_rules! roundtrip_protected {
800        ($name:ident, $alg:path) => {
801            #[test]
802            fn $name() {
803                pretty_env_logger::try_init().ok();
804
805                let mut data_rng = ChaCha8Rng::seed_from_u64(0);
806
807                const MAX_SIZE: usize = 2048;
808
809                // Protected
810                for i in 1..MAX_SIZE {
811                    info!("Size {}", i);
812                    let mut data = vec![0u8; i];
813                    data_rng.fill(&mut data[..]);
814                    let mut key = vec![0u8; $alg.key_size()];
815                    data_rng.fill(&mut key[..]);
816
817                    info!("encrypt");
818                    let mut rng = ChaCha8Rng::seed_from_u64(8);
819                    let ciphertext = $alg.encrypt_protected(&mut rng, &key, &data).unwrap();
820                    assert_ne!(data, ciphertext, "failed to encrypt");
821
822                    {
823                        info!("encrypt streaming");
824                        let mut input = std::io::Cursor::new(&data);
825                        let len = $alg.encrypted_protected_len(data.len());
826                        assert_eq!(len, ciphertext.len(), "failed to encrypt");
827                        let mut output = Vec::new();
828                        let mut rng = ChaCha8Rng::seed_from_u64(8);
829                        let mut encryptor =
830                            $alg.stream_encryptor(&mut rng, &key, &mut input).unwrap();
831                        encryptor.read_to_end(&mut output).unwrap();
832
833                        assert_eq!(output.len(), len, "output length mismatch");
834                        assert_eq!(ciphertext, output, "output mismatch");
835                    }
836
837                    {
838                        info!("decrypt");
839                        let mut ciphertext = ciphertext.clone();
840                        let mut plaintext = ciphertext.split_off($alg.cfb_prefix_size());
841                        let mut prefix = ciphertext;
842                        $alg.decrypt_protected(&key, &mut prefix, &mut plaintext)
843                            .unwrap();
844                        assert_eq!(data, plaintext, "decrypt failed");
845                    }
846                    {
847                        info!("decrypt streaming");
848                        dbg!(ciphertext.len(), $alg.cfb_prefix_size());
849                        let mut input = std::io::Cursor::new(&ciphertext);
850                        let mut decryptor =
851                            $alg.stream_decryptor_protected(&key, &mut input).unwrap();
852                        let mut plaintext = Vec::new();
853                        decryptor.read_to_end(&mut plaintext).unwrap();
854                        assert_eq!(
855                            hex::encode(&data),
856                            hex::encode(&plaintext),
857                            "stream decrypt failed"
858                        );
859                    }
860                }
861            }
862        };
863    }
864
865    roundtrip_protected!(roundtrip_protected_aes128, SymmetricKeyAlgorithm::AES128);
866    roundtrip_protected!(roundtrip_protected_aes192, SymmetricKeyAlgorithm::AES192);
867    roundtrip_protected!(roundtrip_protected_aes256, SymmetricKeyAlgorithm::AES256);
868    roundtrip_protected!(
869        roundtrip_protected_tripledes,
870        SymmetricKeyAlgorithm::TripleDES
871    );
872    roundtrip_protected!(
873        roundtrip_protected_blowfish,
874        SymmetricKeyAlgorithm::Blowfish
875    );
876    roundtrip_protected!(roundtrip_protected_twofish, SymmetricKeyAlgorithm::Twofish);
877    roundtrip_protected!(roundtrip_protected_cast5, SymmetricKeyAlgorithm::CAST5);
878    roundtrip_protected!(roundtrip_protected_idea, SymmetricKeyAlgorithm::IDEA);
879    roundtrip_protected!(
880        roundtrip_protected_camellia128,
881        SymmetricKeyAlgorithm::Camellia128
882    );
883    roundtrip_protected!(
884        roundtrip_protected_camellia192,
885        SymmetricKeyAlgorithm::Camellia192
886    );
887    roundtrip_protected!(
888        roundtrip_protected_camellia256,
889        SymmetricKeyAlgorithm::Camellia256
890    );
891
892    roundtrip_unprotected!(roundtrip_unprotected_aes128, SymmetricKeyAlgorithm::AES128);
893    roundtrip_unprotected!(roundtrip_unprotected_aes192, SymmetricKeyAlgorithm::AES192);
894    roundtrip_unprotected!(roundtrip_unprotected_aes256, SymmetricKeyAlgorithm::AES256);
895    roundtrip_unprotected!(
896        roundtrip_unprotected_tripledes,
897        SymmetricKeyAlgorithm::TripleDES
898    );
899    roundtrip_unprotected!(
900        roundtrip_unprotected_blowfish,
901        SymmetricKeyAlgorithm::Blowfish
902    );
903    roundtrip_unprotected!(
904        roundtrip_unprotected_twofish,
905        SymmetricKeyAlgorithm::Twofish
906    );
907    roundtrip_unprotected!(roundtrip_unprotected_cast5, SymmetricKeyAlgorithm::CAST5);
908    roundtrip_unprotected!(roundtrip_unprotected_idea, SymmetricKeyAlgorithm::IDEA);
909    roundtrip_unprotected!(
910        roundtrip_unprotected_camellia128,
911        SymmetricKeyAlgorithm::Camellia128
912    );
913    roundtrip_unprotected!(
914        roundtrip_unprotected_camellia192,
915        SymmetricKeyAlgorithm::Camellia192
916    );
917    roundtrip_unprotected!(
918        roundtrip_unprotected_camellia256,
919        SymmetricKeyAlgorithm::Camellia256
920    );
921
922    #[test]
923    pub fn decrypt_without_enough_ciphertext() {
924        let key: [u8; 0] = [];
925        let mut prefix: [u8; 0] = [];
926        let mut cipher_text: [u8; 0] = [];
927        assert!(SymmetricKeyAlgorithm::AES128
928            .decrypt(&key, &mut prefix, &mut cipher_text)
929            .is_err());
930    }
931
932    use rand::RngCore;
933
934    #[ignore]
935    #[test]
936    fn bench_aes_256_protected() {
937        const SIZE: usize = 1024 * 1024 * 64;
938        let mut rng = ChaCha8Rng::seed_from_u64(0);
939        let mut data = vec![0u8; SIZE];
940        rng.fill_bytes(&mut data);
941
942        let mut key = vec![0u8; SymmetricKeyAlgorithm::AES256.key_size()];
943        rng.fill_bytes(&mut key);
944
945        let now = Instant::now();
946        let mut encryptor = SymmetricKeyAlgorithm::AES256
947            .stream_encryptor(&mut rng, &key, &data[..])
948            .unwrap();
949
950        let mut output = Vec::with_capacity(SIZE);
951        encryptor.read_to_end(&mut output).unwrap();
952
953        let elapsed = now.elapsed();
954        let elapsed_milli = elapsed.as_millis();
955        let mb_per_s = ((SIZE as f64) / 1000f64 / 1000f64 / elapsed_milli as f64) * 1000f64;
956        println!("Encryption: {elapsed_milli} ms, MByte/s: {mb_per_s:.2?}");
957
958        let now = Instant::now();
959
960        let mut decryptor = SymmetricKeyAlgorithm::AES256
961            .stream_decryptor_protected(&key, &output[..])
962            .unwrap();
963        let mut res = Vec::with_capacity(SIZE);
964        decryptor.read_to_end(&mut res).unwrap();
965        let elapsed = now.elapsed();
966
967        assert_eq!(res, data);
968
969        let elapsed_milli = elapsed.as_millis();
970        let mb_per_s = (SIZE as f64 / 1000f64 / 1000f64 / elapsed_milli as f64) * 1000f64;
971        println!("Decryption: {elapsed_milli} ms, MByte/s: {mb_per_s:.2?}");
972    }
973
974    #[ignore]
975    #[test]
976    fn bench_aes_256_unprotected() {
977        const SIZE: usize = 1024 * 1024 * 256;
978        let mut rng = ChaCha8Rng::seed_from_u64(0);
979        let mut data = vec![0u8; SIZE];
980        rng.fill_bytes(&mut data);
981
982        let mut key = vec![0u8; SymmetricKeyAlgorithm::AES256.key_size()];
983        rng.fill_bytes(&mut key);
984
985        let now = Instant::now();
986        let output = SymmetricKeyAlgorithm::AES256
987            .encrypt(&mut rng, &key, &data[..])
988            .unwrap();
989
990        let elapsed = now.elapsed();
991        let elapsed_milli = elapsed.as_millis();
992        let mb_per_s = ((SIZE as f64) / 1000f64 / 1000f64 / elapsed_milli as f64) * 1000f64;
993        println!("Encryption: {elapsed_milli} ms, MByte/s: {mb_per_s:.2?}");
994
995        let now = Instant::now();
996
997        let mut decryptor = SymmetricKeyAlgorithm::AES256
998            .stream_decryptor_unprotected(&key, &output[..])
999            .unwrap();
1000        let mut res = Vec::with_capacity(SIZE);
1001        decryptor.read_to_end(&mut res).unwrap();
1002        let elapsed = now.elapsed();
1003
1004        assert_eq!(res, data);
1005
1006        let elapsed_milli = elapsed.as_millis();
1007        let mb_per_s = (SIZE as f64 / 1000f64 / 1000f64 / elapsed_milli as f64) * 1000f64;
1008        println!("Decryption: {elapsed_milli} ms, MByte/s: {mb_per_s:.2?}");
1009    }
1010}