Skip to main content

eme2_extended/
eme2.rs

1use cipher::{
2    Block, BlockCipherDecrypt, BlockCipherEncrypt, BlockSizeUser, Iv, Key, KeyInit, KeyIvInit,
3    KeySizeUser, IvSizeUser, Array, typenum::{U16, U32, U64, U128, Sum, Unsigned}
4};
5use hybrid_array::ArraySize;
6use core::fmt;
7
8#[cfg(feature = "zeroize")]
9use zeroize::{Zeroize, ZeroizeOnDrop};
10
11/// Error types for EME2 operations.
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum Error {
14    /// The provided data is shorter than the minimum block size.
15    DataTooShort,
16}
17
18impl fmt::Display for Error {
19    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
20        match self {
21            Self::DataTooShort => write!(f, "Data must be at least one block size for EME2"),
22        }
23    }
24}
25
26impl core::error::Error for Error {}
27
28#[inline]
29fn xor_blocks<C: BlockSizeUser>(out: &mut Block<C>, a: &Block<C>, b: &Block<C>) {
30    for (o, (x, y)) in out.iter_mut().zip(a.iter().zip(b.iter())) {
31        *o = *x ^ *y;
32    }
33}
34
35#[inline]
36fn xor_into<C: BlockSizeUser>(out: &mut Block<C>, b: &Block<C>) {
37    for (o, x) in out.iter_mut().zip(b.iter()) {
38        *o ^= *x;
39    }
40}
41
42/// A trait for block sizes that support EME2 Galois Field polynomial arithmetic.
43pub trait EmePoly: ArraySize {
44    /// Multiplies the given value by the polynomial `x` within GF(2^n).
45    fn mult_by_two(val: &Array<u8, Self>) -> Array<u8, Self>;
46}
47
48// 64-bit Limb GF(2^n) Arithmetic for massive performance gains
49macro_rules! impl_eme_poly {
50    ($size:ty, $limbs:expr, $mask_bytes:expr) => {
51        impl EmePoly for $size {
52            #[inline]
53            fn mult_by_two(val: &Array<u8, Self>) -> Array<u8, Self> {
54                let mut res = Array::<u8, Self>::default();
55                let mut v = [0u64; $limbs];
56
57                // Load little-endian 64-bit limbs
58                for i in 0..$limbs {
59                    let mut buf = [0u8; 8];
60                    buf.copy_from_slice(&val[i * 8..(i + 1) * 8]);
61                    v[i] = u64::from_le_bytes(buf);
62                }
63
64                // Shift left by 1, carrying overflow to the next limb
65                let carry_out = (v[$limbs - 1] >> 63) as u8;
66                for i in (1..$limbs).rev() {
67                    v[i] = (v[i] << 1) | (v[i - 1] >> 63);
68                }
69                v[0] <<= 1;
70
71                // Store back to little-endian bytes
72                for i in 0..$limbs {
73                    res[i * 8..(i + 1) * 8].copy_from_slice(&v[i].to_le_bytes());
74                }
75
76                // Apply polynomial mask in a constant-time manner if there was a carry
77                let mask_val = 0u8.wrapping_sub(carry_out);
78                let mask: &[u8] = &$mask_bytes;
79                for (r, &m) in res.iter_mut().zip(mask.iter()) {
80                    *r ^= m & mask_val;
81                }
82                res
83            }
84        }
85    };
86}
87
88impl_eme_poly!(U16, 2, [0x87, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
89impl_eme_poly!(
90    U32,
91    4,
92    [
93        0x25, 0x04, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
94        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
95    ]
96);
97impl_eme_poly!(
98    U64,
99    8,
100    [
101        0x25, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
102        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
103        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
104        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
105    ]
106);
107impl_eme_poly!(
108    U128,
109    16,
110    [
111        0xa3, 0x03, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
112        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
113        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
114        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
115        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
116        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
117        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
118        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
119    ]
120);
121
122#[inline]
123fn encrypt_block<C>(cipher: &C, block: &mut Block<C>)
124where
125    C: BlockCipherEncrypt + BlockSizeUser,
126{
127    cipher.encrypt_block(block);
128}
129
130#[inline]
131fn decrypt_block<C>(cipher: &C, block: &mut Block<C>)
132where
133    C: BlockCipherDecrypt + BlockSizeUser,
134{
135    cipher.decrypt_block(block);
136}
137
138/// EME2 cipher mode.
139#[derive(Clone)]
140#[cfg_attr(feature = "zeroize", derive(ZeroizeOnDrop))]
141pub struct Eme2<C>
142where
143    C: BlockCipherEncrypt + BlockCipherDecrypt + BlockSizeUser + KeySizeUser,
144    C::BlockSize: EmePoly + core::ops::Add<C::BlockSize>,
145    Sum<C::BlockSize, C::BlockSize>: ArraySize,
146{
147    #[cfg_attr(feature = "zeroize", zeroize(skip))]
148    cipher: C,
149    key2: Block<C>,
150    key3: Block<C>,
151    tweak: Block<C>,
152}
153
154impl<C> BlockSizeUser for Eme2<C>
155where
156    C: BlockCipherEncrypt + BlockCipherDecrypt + BlockSizeUser + KeySizeUser,
157    C::BlockSize: EmePoly + core::ops::Add<C::BlockSize>,
158    Sum<C::BlockSize, C::BlockSize>: ArraySize,
159{
160    type BlockSize = C::BlockSize;
161}
162
163impl<C> IvSizeUser for Eme2<C>
164where
165    C: BlockCipherEncrypt + BlockCipherDecrypt + BlockSizeUser + KeySizeUser,
166    C::BlockSize: EmePoly + core::ops::Add<C::BlockSize>,
167    Sum<C::BlockSize, C::BlockSize>: ArraySize,
168{
169    type IvSize = C::BlockSize;
170}
171
172impl<C> KeySizeUser for Eme2<C>
173where
174    C: BlockCipherEncrypt + BlockCipherDecrypt + BlockSizeUser + KeySizeUser,
175    C::BlockSize: EmePoly + core::ops::Add<C::BlockSize>,
176    Sum<C::BlockSize, C::BlockSize>: ArraySize,
177    C::KeySize: core::ops::Add<Sum<C::BlockSize, C::BlockSize>>,
178    Sum<C::KeySize, Sum<C::BlockSize, C::BlockSize>>: ArraySize,
179{
180    type KeySize = Sum<C::KeySize, Sum<C::BlockSize, C::BlockSize>>;
181}
182
183impl<C> KeyInit for Eme2<C>
184where
185    C: BlockCipherEncrypt + BlockCipherDecrypt + BlockSizeUser + KeyInit,
186    C::BlockSize: EmePoly + core::ops::Add<C::BlockSize>,
187    Sum<C::BlockSize, C::BlockSize>: ArraySize,
188    C::KeySize: core::ops::Add<Sum<C::BlockSize, C::BlockSize>>,
189    Sum<C::KeySize, Sum<C::BlockSize, C::BlockSize>>: ArraySize,
190{
191    fn new(key: &Key<Self>) -> Self {
192        let bs = C::BlockSize::USIZE;
193        let ks = C::KeySize::USIZE;
194
195        let mut key1 = Key::<C>::default();
196        key1.copy_from_slice(&key[..ks]);
197
198        let mut key2 = Block::<C>::default();
199        key2.copy_from_slice(&key[ks..ks + bs]);
200
201        let mut key3 = Block::<C>::default();
202        key3.copy_from_slice(&key[ks + bs..ks + 2 * bs]);
203
204        let cipher = C::new(&key1);
205        let tweak = Block::<C>::default();
206
207        #[cfg(feature = "zeroize")]
208        {
209            let mut key1_mut = key1;
210            key1_mut.zeroize();
211        }
212
213        Self {
214            cipher,
215            key2,
216            key3,
217            tweak,
218        }
219    }
220}
221
222impl<C> KeyIvInit for Eme2<C>
223where
224    C: BlockCipherEncrypt + BlockCipherDecrypt + BlockSizeUser + KeyInit,
225    C::BlockSize: EmePoly + core::ops::Add<C::BlockSize>,
226    Sum<C::BlockSize, C::BlockSize>: ArraySize,
227    C::KeySize: core::ops::Add<Sum<C::BlockSize, C::BlockSize>>,
228    Sum<C::KeySize, Sum<C::BlockSize, C::BlockSize>>: ArraySize,
229{
230    #[inline]
231    fn new(key: &Key<Self>, iv: &Iv<Self>) -> Self {
232        let mut mode = <Self as KeyInit>::new(key);
233        mode.tweak = mode.hash_ad(iv.as_slice());
234        mode
235    }
236}
237
238impl<C> cipher::AlgorithmName for Eme2<C>
239where
240    C: BlockCipherEncrypt + BlockCipherDecrypt + BlockSizeUser + KeySizeUser + cipher::AlgorithmName,
241    C::BlockSize: EmePoly + core::ops::Add<C::BlockSize>,
242    Sum<C::BlockSize, C::BlockSize>: ArraySize,
243{
244    fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
245        f.write_str("Eme2<")?;
246        <C as cipher::AlgorithmName>::write_alg_name(f)?;
247        f.write_str(">")
248    }
249}
250
251impl<C> fmt::Debug for Eme2<C>
252where
253    C: BlockCipherEncrypt + BlockCipherDecrypt + BlockSizeUser + KeySizeUser + cipher::AlgorithmName,
254    C::BlockSize: EmePoly + core::ops::Add<C::BlockSize>,
255    Sum<C::BlockSize, C::BlockSize>: ArraySize,
256{
257    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
258        f.write_str("Eme2<")?;
259        <C as cipher::AlgorithmName>::write_alg_name(f)?;
260        f.write_str("> { ... }")
261    }
262}
263
264impl<C> Eme2<C>
265where
266    C: BlockCipherEncrypt + BlockCipherDecrypt + BlockSizeUser + KeySizeUser,
267    C::BlockSize: EmePoly + core::ops::Add<C::BlockSize>,
268    Sum<C::BlockSize, C::BlockSize>: ArraySize,
269{
270    /// Hashes the associated data (T) using Key3 and Key1 to compute T_star.
271    pub fn hash_ad(&self, ad: &[u8]) -> Block<C> {
272        let bs = C::BlockSize::USIZE;
273        let mut t_star = Block::<C>::default();
274        if ad.is_empty() {
275            t_star.copy_from_slice(self.key3.as_slice());
276            encrypt_block(&self.cipher, &mut t_star);
277            return t_star;
278        }
279
280        let mut current_key3 = self.key3.clone();
281        let mut tt = Block::<C>::default();
282        let chunks = ad.chunks(bs);
283        let r = chunks.len();
284
285        current_key3 = C::BlockSize::mult_by_two(&current_key3);
286
287        for (i, chunk) in chunks.enumerate() {
288            let is_last = i == r - 1;
289            if !is_last {
290                let mut block = Block::<C>::default();
291                block.copy_from_slice(chunk);
292                xor_into::<C>(&mut block, &current_key3);
293                encrypt_block(&self.cipher, &mut block);
294                xor_into::<C>(&mut block, &current_key3);
295                xor_into::<C>(&mut tt, &block);
296                current_key3 = C::BlockSize::mult_by_two(&current_key3);
297            } else {
298                let mut block = Block::<C>::default();
299                if chunk.len() < bs {
300                    block[..chunk.len()].copy_from_slice(chunk);
301                    block[chunk.len()] = 0x80;
302                    current_key3 = C::BlockSize::mult_by_two(&current_key3);
303                } else {
304                    block.copy_from_slice(chunk);
305                }
306                xor_into::<C>(&mut block, &current_key3);
307                encrypt_block(&self.cipher, &mut block);
308                xor_into::<C>(&mut block, &current_key3);
309                xor_into::<C>(&mut tt, &block);
310            }
311        }
312
313        #[cfg(feature = "zeroize")]
314        {
315            current_key3.zeroize();
316        }
317
318        tt
319    }
320
321    /// Returns a reference to the current tweak.
322    pub fn tweak(&self) -> &Block<C> {
323        &self.tweak
324    }
325
326    /// Sets the current tweak.
327    pub fn set_tweak(&mut self, tweak: Block<C>) {
328        self.tweak = tweak;
329    }
330
331    /// Encrypts the `data` in-place using EME2 mode with pre-computed tweak.
332    /// `data` must be at least one block size.
333    pub fn encrypt(&self, data: &mut [u8]) -> Result<(), Error> {
334        self.encrypt_core(&self.tweak, data)
335    }
336
337    /// Decrypts the `data` in-place using EME2 mode with pre-computed tweak.
338    /// `data` must be at least one block size.
339    pub fn decrypt(&self, data: &mut [u8]) -> Result<(), Error> {
340        self.decrypt_core(&self.tweak, data)
341    }
342
343    /// Encrypts the `data` in-place using EME2 mode with arbitrary associated data (IEEE P1619.2 compliance).
344    /// `data` must be at least one block size.
345    pub fn encrypt_with_ad(&self, associated_data: &[u8], data: &mut [u8]) -> Result<(), Error> {
346        let t_star = self.hash_ad(associated_data);
347        let res = self.encrypt_core(&t_star, data);
348
349        #[cfg(feature = "zeroize")]
350        {
351            let mut t_star_mut = t_star;
352            t_star_mut.zeroize();
353        }
354
355        res
356    }
357
358    /// Decrypts the `data` in-place using EME2 mode with arbitrary associated data (IEEE P1619.2 compliance).
359    /// `data` must be at least one block size.
360    pub fn decrypt_with_ad(&self, associated_data: &[u8], data: &mut [u8]) -> Result<(), Error> {
361        let t_star = self.hash_ad(associated_data);
362        let res = self.decrypt_core(&t_star, data);
363
364        #[cfg(feature = "zeroize")]
365        {
366            let mut t_star_mut = t_star;
367            t_star_mut.zeroize();
368        }
369
370        res
371    }
372
373    fn encrypt_core(&self, tweak_block: &Block<C>, data: &mut [u8]) -> Result<(), Error> {
374        let bs = C::BlockSize::USIZE;
375        let len = data.len();
376        if len < bs {
377            return Err(Error::DataTooShort);
378        }
379
380        let m = len.div_ceil(bs);
381        let last_full = if len.is_multiple_of(bs) { m } else { m - 1 };
382
383        // PASS 1: data[i] = ppp_i
384        let mut l_current = self.key2.clone();
385
386        for i in 0..last_full {
387            let block: &mut Block<C> = (&mut data[i * bs..(i + 1) * bs]).try_into().expect("slice bounds match");
388            xor_into::<C>(block, &l_current);
389            encrypt_block(&self.cipher, block);
390            l_current = C::BlockSize::mult_by_two(&l_current);
391        }
392
393        let mut ppp_m = Block::<C>::default();
394        if last_full < m {
395            let rem = len % bs;
396            ppp_m[..rem].copy_from_slice(&data[last_full * bs..len]);
397            ppp_m[rem] = 0x80;
398        }
399
400        let mut sp = Block::<C>::default();
401        for i in 1..last_full {
402            let ppp_i: &Block<C> = (&data[i * bs..(i + 1) * bs]).try_into().expect("slice bounds match");
403            xor_into::<C>(&mut sp, ppp_i);
404        }
405        if last_full < m {
406            xor_into::<C>(&mut sp, &ppp_m);
407        }
408
409        let ppp_0: &Block<C> = (&data[0..bs]).try_into().expect("slice bounds match");
410
411        let mut mp_1 = Block::<C>::default();
412        xor_blocks::<C>(&mut mp_1, ppp_0, &sp);
413        xor_into::<C>(&mut mp_1, tweak_block);
414
415        let mut mc_1;
416        let mut ccc_m = Block::<C>::default();
417        let mut c_m = Block::<C>::default();
418        let mut mm = Block::<C>::default();
419
420        if last_full < m {
421            mm.copy_from_slice(&mp_1);
422            encrypt_block(&self.cipher, &mut mm);
423            mc_1 = mm.clone();
424            encrypt_block(&self.cipher, &mut mc_1);
425
426            let rem = len % bs;
427            for i in 0..rem {
428                c_m[i] = data[last_full * bs + i] ^ mm[i];
429            }
430            ccc_m[..rem].copy_from_slice(&c_m[..rem]);
431            ccc_m[rem] = 0x80;
432        } else {
433            mc_1 = mp_1.clone();
434            encrypt_block(&self.cipher, &mut mc_1);
435        }
436
437        let mut m_1 = Block::<C>::default();
438        xor_blocks::<C>(&mut m_1, &mp_1, &mc_1);
439
440        let mut current_m_j = m_1.clone();
441        let mut current_m_k = m_1.clone();
442
443        // PASS 2: data[i] = ccc_i
444        for i in 1..last_full {
445            let block: &mut Block<C> = (&mut data[i * bs..(i + 1) * bs]).try_into().expect("slice bounds match");
446
447            let k = i & 127;
448            if k == 0 {
449                let mut mp_j = Block::<C>::default();
450                xor_blocks::<C>(&mut mp_j, block, &m_1);
451                let mut mc_j = mp_j.clone();
452                encrypt_block(&self.cipher, &mut mc_j);
453                xor_blocks::<C>(&mut current_m_j, &mp_j, &mc_j);
454                xor_blocks::<C>(block, &mc_j, &m_1);
455                current_m_k = current_m_j.clone();
456
457                #[cfg(feature = "zeroize")]
458                {
459                    mp_j.zeroize();
460                    mc_j.zeroize();
461                }
462            } else {
463                current_m_k = C::BlockSize::mult_by_two(&current_m_k);
464                xor_into::<C>(block, &current_m_k);
465            }
466        }
467
468        let mut sc = Block::<C>::default();
469        for i in 1..last_full {
470            let ccc_i: &Block<C> = (&data[i * bs..(i + 1) * bs]).try_into().expect("slice bounds match");
471            xor_into::<C>(&mut sc, ccc_i);
472        }
473        if last_full < m {
474            xor_into::<C>(&mut sc, &ccc_m);
475        }
476
477        let mut ccc_0 = Block::<C>::default();
478        xor_blocks::<C>(&mut ccc_0, &mc_1, &sc);
479        xor_into::<C>(&mut ccc_0, tweak_block);
480        data[0..bs].copy_from_slice(&ccc_0);
481
482        // PASS 3: data[i] = ciphertext
483        l_current = self.key2.clone();
484        for i in 0..last_full {
485            let block: &mut Block<C> = (&mut data[i * bs..(i + 1) * bs]).try_into().expect("slice bounds match");
486            encrypt_block(&self.cipher, block);
487            xor_into::<C>(block, &l_current);
488            l_current = C::BlockSize::mult_by_two(&l_current);
489        }
490
491        if last_full < m {
492            let rem = len % bs;
493            data[last_full * bs..len].copy_from_slice(&c_m[..rem]);
494        }
495
496        #[cfg(feature = "zeroize")]
497        {
498            l_current.zeroize();
499            ppp_m.zeroize();
500            sp.zeroize();
501            mp_1.zeroize();
502            mc_1.zeroize();
503            ccc_m.zeroize();
504            c_m.zeroize();
505            m_1.zeroize();
506            current_m_j.zeroize();
507            current_m_k.zeroize();
508            sc.zeroize();
509            ccc_0.zeroize();
510            mm.zeroize();
511        }
512
513        Ok(())
514    }
515
516    fn decrypt_core(&self, tweak_block: &Block<C>, data: &mut [u8]) -> Result<(), Error> {
517        let bs = C::BlockSize::USIZE;
518        let len = data.len();
519        if len < bs {
520            return Err(Error::DataTooShort);
521        }
522
523        let m = len.div_ceil(bs);
524        let last_full = if len.is_multiple_of(bs) { m } else { m - 1 };
525
526        let mut l_current = self.key2.clone();
527
528        // PASS 1: data[i] = ccc_i
529        for i in 0..last_full {
530            let block: &mut Block<C> = (&mut data[i * bs..(i + 1) * bs]).try_into().expect("slice bounds match");
531            xor_into::<C>(block, &l_current);
532            decrypt_block(&self.cipher, block);
533            l_current = C::BlockSize::mult_by_two(&l_current);
534        }
535
536        let mut ccc_m = Block::<C>::default();
537        if last_full < m {
538            let rem = len % bs;
539            ccc_m[..rem].copy_from_slice(&data[last_full * bs..len]);
540            ccc_m[rem] = 0x80;
541        }
542
543        let mut sc = Block::<C>::default();
544        for i in 1..last_full {
545            let ccc_i: &Block<C> = (&data[i * bs..(i + 1) * bs]).try_into().expect("slice bounds match");
546            xor_into::<C>(&mut sc, ccc_i);
547        }
548        if last_full < m {
549            xor_into::<C>(&mut sc, &ccc_m);
550        }
551
552        let ccc_0: &Block<C> = (&data[0..bs]).try_into().expect("slice bounds match");
553        let mut mc_1 = Block::<C>::default();
554        xor_blocks::<C>(&mut mc_1, ccc_0, &sc);
555        xor_into::<C>(&mut mc_1, tweak_block);
556
557        let mut mp_1;
558        let mut ppp_m = Block::<C>::default();
559        let mut p_m = Block::<C>::default();
560        let mut mm = Block::<C>::default();
561
562        if last_full < m {
563            mm.copy_from_slice(&mc_1);
564            decrypt_block(&self.cipher, &mut mm);
565            mp_1 = mm.clone();
566            decrypt_block(&self.cipher, &mut mp_1);
567
568            let rem = len % bs;
569            for i in 0..rem {
570                p_m[i] = data[last_full * bs + i] ^ mm[i];
571            }
572            ppp_m[..rem].copy_from_slice(&p_m[..rem]);
573            ppp_m[rem] = 0x80;
574        } else {
575            mp_1 = mc_1.clone();
576            decrypt_block(&self.cipher, &mut mp_1);
577        }
578
579        let mut m_1 = Block::<C>::default();
580        xor_blocks::<C>(&mut m_1, &mp_1, &mc_1);
581
582        let mut current_m_j = m_1.clone();
583        let mut current_m_k = m_1.clone();
584
585        // PASS 2: data[i] = ppp_i
586        for i in 1..last_full {
587            let block: &mut Block<C> = (&mut data[i * bs..(i + 1) * bs]).try_into().expect("slice bounds match");
588
589            let k = i & 127;
590            if k == 0 {
591                let mut mc_j = Block::<C>::default();
592                xor_blocks::<C>(&mut mc_j, block, &m_1);
593                let mut mp_j = mc_j.clone();
594                decrypt_block(&self.cipher, &mut mp_j);
595                xor_blocks::<C>(&mut current_m_j, &mp_j, &mc_j);
596                xor_blocks::<C>(block, &mp_j, &m_1);
597                current_m_k = current_m_j.clone();
598
599                #[cfg(feature = "zeroize")]
600                {
601                    mc_j.zeroize();
602                    mp_j.zeroize();
603                }
604            } else {
605                current_m_k = C::BlockSize::mult_by_two(&current_m_k);
606                xor_into::<C>(block, &current_m_k);
607            }
608        }
609
610        let mut sp = Block::<C>::default();
611        for i in 1..last_full {
612            let ppp_i: &Block<C> = (&data[i * bs..(i + 1) * bs]).try_into().expect("slice bounds match");
613            xor_into::<C>(&mut sp, ppp_i);
614        }
615        if last_full < m {
616            xor_into::<C>(&mut sp, &ppp_m);
617        }
618
619        let mut ppp_0 = Block::<C>::default();
620        xor_blocks::<C>(&mut ppp_0, &mp_1, &sp);
621        xor_into::<C>(&mut ppp_0, tweak_block);
622        data[0..bs].copy_from_slice(&ppp_0);
623
624        // PASS 3: data[i] = plaintext
625        l_current = self.key2.clone();
626        for i in 0..last_full {
627            let block: &mut Block<C> = (&mut data[i * bs..(i + 1) * bs]).try_into().expect("slice bounds match");
628            decrypt_block(&self.cipher, block);
629            xor_into::<C>(block, &l_current);
630            l_current = C::BlockSize::mult_by_two(&l_current);
631        }
632
633        if last_full < m {
634            let rem = len % bs;
635            data[last_full * bs..len].copy_from_slice(&p_m[..rem]);
636        }
637
638        #[cfg(feature = "zeroize")]
639        {
640            l_current.zeroize();
641            ccc_m.zeroize();
642            sc.zeroize();
643            mc_1.zeroize();
644            mp_1.zeroize();
645            ppp_m.zeroize();
646            p_m.zeroize();
647            m_1.zeroize();
648            current_m_j.zeroize();
649            current_m_k.zeroize();
650            sp.zeroize();
651            ppp_0.zeroize();
652            mm.zeroize();
653        }
654
655        Ok(())
656    }
657}