Skip to main content

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