Skip to main content

cryptography/ciphers/
serpent.rs

1//! Serpent block cipher — AES submission / FSE 1998.
2//!
3//! 128-bit block cipher with three standard key sizes:
4//!
5//! - `Serpent128` / `Serpent128Ct`
6//! - `Serpent192` / `Serpent192Ct`
7//! - `Serpent256` / `Serpent256Ct`
8//!
9//! The public API follows the original Serpent submission / reference-implementation
10//! byte order. That matches many existing Serpent libraries, but it differs
11//! from the byte-reversed NESSIE presentation. Internally, the core still works
12//! on 32-bit little-endian words, so the wrappers reverse the incoming key and
13//! block bytes around that native representation.
14//!
15//! The fast path uses direct 4-bit S-box lookups across the 32 parallel lanes
16//! of the bitslice round state. The `Ct` path evaluates the same 4->4 S-boxes
17//! in packed ANF form so substitution avoids secret-indexed table reads.
18
19use crate::ct::zeroize_slice;
20use crate::BlockCipher;
21
22// Serpent key-schedule constant PHI = floor(2^32 * (sqrt(5)-1)/2).
23const PHI: u32 = 0x9E37_79B9;
24
25// S-boxes from the Serpent AES submission (Anderson/Biham/Knudsen, 1998).
26const SBOXES: [[u8; 16]; 8] = [
27    [3, 8, 15, 1, 10, 6, 5, 11, 14, 13, 4, 2, 7, 0, 9, 12],
28    [15, 12, 2, 7, 9, 0, 5, 10, 1, 11, 14, 8, 6, 13, 3, 4],
29    [8, 6, 7, 9, 3, 12, 10, 15, 13, 1, 14, 4, 0, 11, 5, 2],
30    [0, 15, 11, 8, 12, 9, 6, 3, 13, 1, 2, 4, 10, 7, 5, 14],
31    [1, 15, 8, 3, 12, 0, 11, 6, 2, 5, 4, 10, 9, 14, 7, 13],
32    [15, 5, 2, 11, 4, 10, 9, 12, 0, 3, 14, 8, 13, 6, 7, 1],
33    [7, 2, 12, 5, 8, 4, 6, 11, 14, 9, 1, 15, 13, 3, 10, 0],
34    [1, 13, 15, 0, 14, 8, 2, 11, 7, 4, 12, 10, 9, 3, 5, 6],
35];
36
37// Inverse S-box tables from the same Serpent submission.
38const INV_SBOXES: [[u8; 16]; 8] = [
39    [13, 3, 11, 0, 10, 6, 5, 12, 1, 14, 4, 7, 15, 9, 8, 2],
40    [5, 8, 2, 14, 15, 6, 12, 3, 11, 4, 7, 9, 1, 13, 10, 0],
41    [12, 9, 15, 4, 11, 14, 1, 2, 0, 3, 6, 13, 5, 8, 10, 7],
42    [0, 9, 10, 7, 11, 14, 6, 13, 3, 5, 12, 2, 4, 8, 15, 1],
43    [5, 0, 8, 3, 10, 9, 7, 14, 2, 12, 11, 6, 4, 15, 13, 1],
44    [8, 15, 2, 9, 4, 1, 13, 14, 11, 6, 5, 3, 7, 12, 10, 0],
45    [15, 10, 1, 13, 5, 3, 6, 0, 4, 9, 14, 7, 2, 12, 8, 11],
46    [3, 0, 6, 13, 9, 14, 15, 8, 5, 12, 11, 7, 10, 1, 4, 2],
47];
48
49const fn build_sboxes_anf(sboxes: &[[u8; 16]; 8]) -> [[u16; 4]; 8] {
50    let mut out = [[0u16; 4]; 8];
51    let mut i = 0usize;
52    while i < 8 {
53        out[i] = crate::ct::build_nibble_sbox_anf(&sboxes[i]);
54        i += 1;
55    }
56    out
57}
58
59const SBOXES_ANF: [[u16; 4]; 8] = build_sboxes_anf(&SBOXES);
60const INV_SBOXES_ANF: [[u16; 4]; 8] = build_sboxes_anf(&INV_SBOXES);
61
62#[inline]
63fn sbox_ct_nibble(input: u8, sbox_anf: [u16; 4]) -> u8 {
64    crate::ct::eval_nibble_sbox(sbox_anf, input)
65}
66
67/// Apply a 4-bit S-box to all 32 bitslice lanes in parallel.
68///
69/// **Bitslice representation.**  Serpent stores one 128-bit block as four 32-bit
70/// words `[x0, x1, x2, x3]`.  Bit `i` of word `j` represents the `j`th input
71/// bit of the `i`th S-box "lane" — so there are 32 parallel 4-bit S-box
72/// instances, one per bit-position 0..31 across all four words.
73///
74/// To apply the S-box to lane `i`:
75///   1. Collect the 4-bit input nibble: bit `i` of each of x0..x3.
76///   2. Look up `table[nibble]` to get the 4-bit output.
77///   3. Distribute the output bits back to bit `i` of each output word.
78///
79/// This is the standard bitsliced evaluation loop over all 32 lanes.  The `Ct`
80/// variant ([`apply_sbox_ct`]) replaces the table lookup with the ANF evaluator
81/// from `crate::ct` to eliminate secret-indexed memory reads.
82#[inline]
83fn apply_sbox_table(words: [u32; 4], table: &[u8; 16]) -> [u32; 4] {
84    // Bitslice mapping: bit `i` from each word forms one 4-bit S-box input,
85    // so one call processes 32 parallel 4-bit S-box instances.
86    let [x0, x1, x2, x3] = words;
87    let mut out = [0u32; 4];
88    let mut bit = 0u32;
89    while bit < 32 {
90        let nibble = (((x0 >> bit) & 1)
91            | (((x1 >> bit) & 1) << 1)
92            | (((x2 >> bit) & 1) << 2)
93            | (((x3 >> bit) & 1) << 3)) as usize;
94        let s = table[nibble];
95        out[0] |= u32::from(s & 1) << bit;
96        out[1] |= u32::from((s >> 1) & 1) << bit;
97        out[2] |= u32::from((s >> 2) & 1) << bit;
98        out[3] |= u32::from((s >> 3) & 1) << bit;
99        bit += 1;
100    }
101    out
102}
103
104#[inline]
105fn apply_sbox_ct(words: [u32; 4], sbox_anf: [u16; 4]) -> [u32; 4] {
106    // Same 32-lane bitslice mapping as `apply_sbox_table`, but each nibble is
107    // evaluated through packed ANF coefficients instead of table lookups.
108    let [x0, x1, x2, x3] = words;
109    let mut out = [0u32; 4];
110    let mut bit = 0u32;
111    while bit < 32 {
112        let nibble = u8::try_from(
113            ((x0 >> bit) & 1)
114                | (((x1 >> bit) & 1) << 1)
115                | (((x2 >> bit) & 1) << 2)
116                | (((x3 >> bit) & 1) << 3),
117        )
118        .expect("bit-packed nibble fits in u8");
119        let s = sbox_ct_nibble(nibble, sbox_anf);
120        out[0] |= u32::from(s & 1) << bit;
121        out[1] |= u32::from((s >> 1) & 1) << bit;
122        out[2] |= u32::from((s >> 2) & 1) << bit;
123        out[3] |= u32::from((s >> 3) & 1) << bit;
124        bit += 1;
125    }
126    out
127}
128
129#[inline]
130fn apply_sbox_round(words: [u32; 4], round: usize, use_ct: bool) -> [u32; 4] {
131    if use_ct {
132        apply_sbox_ct(words, SBOXES_ANF[round & 7])
133    } else {
134        apply_sbox_table(words, &SBOXES[round & 7])
135    }
136}
137
138#[inline]
139fn apply_inv_sbox_round(words: [u32; 4], round: usize, use_ct: bool) -> [u32; 4] {
140    if use_ct {
141        apply_sbox_ct(words, INV_SBOXES_ANF[round & 7])
142    } else {
143        apply_sbox_table(words, &INV_SBOXES[round & 7])
144    }
145}
146
147#[inline]
148fn lt(words: [u32; 4]) -> [u32; 4] {
149    let mut x0 = words[0].rotate_left(13);
150    let mut x2 = words[2].rotate_left(3);
151    let mut x1 = words[1] ^ x0 ^ x2;
152    let mut x3 = words[3] ^ x2 ^ (x0 << 3);
153    x1 = x1.rotate_left(1);
154    x3 = x3.rotate_left(7);
155    x0 ^= x1 ^ x3;
156    x2 ^= x3 ^ (x1 << 7);
157    x0 = x0.rotate_left(5);
158    x2 = x2.rotate_left(22);
159    [x0, x1, x2, x3]
160}
161
162#[inline]
163fn inv_lt(words: [u32; 4]) -> [u32; 4] {
164    let mut x0 = words[0].rotate_right(5);
165    let mut x1 = words[1];
166    let mut x2 = words[2].rotate_right(22);
167    let mut x3 = words[3];
168    x2 ^= x3 ^ (x1 << 7);
169    x0 ^= x1 ^ x3;
170    x3 = x3.rotate_right(7);
171    x1 = x1.rotate_right(1);
172    x3 ^= x2 ^ (x0 << 3);
173    x1 ^= x0 ^ x2;
174    x2 = x2.rotate_right(3);
175    x0 = x0.rotate_right(13);
176    [x0, x1, x2, x3]
177}
178
179#[inline]
180fn reverse_bytes<const N: usize>(input: &[u8; N]) -> [u8; N] {
181    let mut out = [0u8; N];
182    let mut i = 0usize;
183    while i < N {
184        out[i] = input[N - 1 - i];
185        i += 1;
186    }
187    out
188}
189
190#[inline]
191fn words_from_block_internal(block: &[u8; 16]) -> [u32; 4] {
192    [
193        u32::from_le_bytes(block[0..4].try_into().unwrap()),
194        u32::from_le_bytes(block[4..8].try_into().unwrap()),
195        u32::from_le_bytes(block[8..12].try_into().unwrap()),
196        u32::from_le_bytes(block[12..16].try_into().unwrap()),
197    ]
198}
199
200#[inline]
201fn block_from_words_internal(words: [u32; 4]) -> [u8; 16] {
202    let mut out = [0u8; 16];
203    out[0..4].copy_from_slice(&words[0].to_le_bytes());
204    out[4..8].copy_from_slice(&words[1].to_le_bytes());
205    out[8..12].copy_from_slice(&words[2].to_le_bytes());
206    out[12..16].copy_from_slice(&words[3].to_le_bytes());
207    out
208}
209
210fn expand_round_keys<const N: usize>(user_key: &[u8; N], use_ct: bool) -> [[u32; 4]; 33] {
211    let key = reverse_bytes(user_key);
212    let mut padded = [0u8; 32];
213    padded[..N].copy_from_slice(&key);
214    if N < 32 {
215        padded[N] = 1;
216    }
217
218    let mut words = [0u32; 140];
219    let mut i = 0usize;
220    while i < 8 {
221        let off = 4 * i;
222        words[i] = u32::from_le_bytes(padded[off..off + 4].try_into().unwrap());
223        i += 1;
224    }
225    while i < 140 {
226        words[i] = (words[i - 8]
227            ^ words[i - 5]
228            ^ words[i - 3]
229            ^ words[i - 1]
230            ^ PHI
231            ^ u32::try_from(i - 8).expect("round-key index fits in u32"))
232        .rotate_left(11);
233        i += 1;
234    }
235
236    let mut out = [[0u32; 4]; 33];
237    let mut round = 0usize;
238    while round < 33 {
239        let sbox_idx = (3usize.wrapping_sub(round)) & 7;
240        let input = [
241            words[8 + 4 * round],
242            words[8 + 4 * round + 1],
243            words[8 + 4 * round + 2],
244            words[8 + 4 * round + 3],
245        ];
246        out[round] = if use_ct {
247            apply_sbox_ct(input, SBOXES_ANF[sbox_idx])
248        } else {
249            apply_sbox_table(input, &SBOXES[sbox_idx])
250        };
251        round += 1;
252    }
253
254    out
255}
256
257fn serpent_encrypt_words(
258    mut state: [u32; 4],
259    round_keys: &[[u32; 4]; 33],
260    use_ct: bool,
261) -> [u32; 4] {
262    let mut round = 0usize;
263    while round < 31 {
264        state[0] ^= round_keys[round][0];
265        state[1] ^= round_keys[round][1];
266        state[2] ^= round_keys[round][2];
267        state[3] ^= round_keys[round][3];
268        state = apply_sbox_round(state, round, use_ct);
269        state = lt(state);
270        round += 1;
271    }
272
273    state[0] ^= round_keys[31][0];
274    state[1] ^= round_keys[31][1];
275    state[2] ^= round_keys[31][2];
276    state[3] ^= round_keys[31][3];
277    state = apply_sbox_round(state, 31, use_ct);
278    state[0] ^= round_keys[32][0];
279    state[1] ^= round_keys[32][1];
280    state[2] ^= round_keys[32][2];
281    state[3] ^= round_keys[32][3];
282    state
283}
284
285fn serpent_decrypt_words(
286    mut state: [u32; 4],
287    round_keys: &[[u32; 4]; 33],
288    use_ct: bool,
289) -> [u32; 4] {
290    state[0] ^= round_keys[32][0];
291    state[1] ^= round_keys[32][1];
292    state[2] ^= round_keys[32][2];
293    state[3] ^= round_keys[32][3];
294    state = apply_inv_sbox_round(state, 31, use_ct);
295    state[0] ^= round_keys[31][0];
296    state[1] ^= round_keys[31][1];
297    state[2] ^= round_keys[31][2];
298    state[3] ^= round_keys[31][3];
299
300    let mut round = 31usize;
301    while round > 0 {
302        round -= 1;
303        state = inv_lt(state);
304        state = apply_inv_sbox_round(state, round, use_ct);
305        state[0] ^= round_keys[round][0];
306        state[1] ^= round_keys[round][1];
307        state[2] ^= round_keys[round][2];
308        state[3] ^= round_keys[round][3];
309    }
310
311    state
312}
313
314fn encrypt_block_words(round_keys: &[[u32; 4]; 33], block: &[u8; 16], use_ct: bool) -> [u8; 16] {
315    let internal = reverse_bytes(block);
316    let state = words_from_block_internal(&internal);
317    let out = serpent_encrypt_words(state, round_keys, use_ct);
318    reverse_bytes(&block_from_words_internal(out))
319}
320
321fn decrypt_block_words(round_keys: &[[u32; 4]; 33], block: &[u8; 16], use_ct: bool) -> [u8; 16] {
322    let internal = reverse_bytes(block);
323    let state = words_from_block_internal(&internal);
324    let out = serpent_decrypt_words(state, round_keys, use_ct);
325    reverse_bytes(&block_from_words_internal(out))
326}
327
328macro_rules! serpent_type {
329    ($name:ident, $name_ct:ident, $key_len:literal, $doc:literal, $doc_ct:literal) => {
330        #[doc = $doc]
331        pub struct $name {
332            round_keys: [[u32; 4]; 33],
333        }
334
335        impl $name {
336            /// Expand the user key into the 33 Serpent round-key words.
337            pub fn new(key: &[u8; $key_len]) -> Self {
338                Self {
339                    round_keys: expand_round_keys(key, false),
340                }
341            }
342
343            /// Expand the key and then wipe the caller-owned key buffer.
344            pub fn new_wiping(key: &mut [u8; $key_len]) -> Self {
345                let cipher = Self::new(key);
346                zeroize_slice(key);
347                cipher
348            }
349
350            /// Encrypt one 128-bit block.
351            pub fn encrypt_block(&self, block: &[u8; 16]) -> [u8; 16] {
352                encrypt_block_words(&self.round_keys, block, false)
353            }
354
355            /// Decrypt one 128-bit block.
356            pub fn decrypt_block(&self, block: &[u8; 16]) -> [u8; 16] {
357                decrypt_block_words(&self.round_keys, block, false)
358            }
359        }
360
361        impl BlockCipher for $name {
362            const BLOCK_LEN: usize = 16;
363
364            fn encrypt(&self, block: &mut [u8]) {
365                let arr: &[u8; 16] = (&*block).try_into().expect("wrong block length");
366                let ct = self.encrypt_block(arr);
367                block.copy_from_slice(&ct);
368            }
369
370            fn decrypt(&self, block: &mut [u8]) {
371                let arr: &[u8; 16] = (&*block).try_into().expect("wrong block length");
372                let pt = self.decrypt_block(arr);
373                block.copy_from_slice(&pt);
374            }
375        }
376
377        impl Drop for $name {
378            fn drop(&mut self) {
379                for rk in &mut self.round_keys {
380                    zeroize_slice(rk);
381                }
382            }
383        }
384
385        #[doc = $doc_ct]
386        pub struct $name_ct {
387            round_keys: [[u32; 4]; 33],
388        }
389
390        impl $name_ct {
391            /// Expand the user key into the 33 Serpent round-key words.
392            pub fn new(key: &[u8; $key_len]) -> Self {
393                Self {
394                    round_keys: expand_round_keys(key, true),
395                }
396            }
397
398            /// Expand the key and then wipe the caller-owned key buffer.
399            pub fn new_wiping(key: &mut [u8; $key_len]) -> Self {
400                let cipher = Self::new(key);
401                zeroize_slice(key);
402                cipher
403            }
404
405            /// Encrypt one 128-bit block with the software constant-time path.
406            pub fn encrypt_block(&self, block: &[u8; 16]) -> [u8; 16] {
407                encrypt_block_words(&self.round_keys, block, true)
408            }
409
410            /// Decrypt one 128-bit block with the software constant-time path.
411            pub fn decrypt_block(&self, block: &[u8; 16]) -> [u8; 16] {
412                decrypt_block_words(&self.round_keys, block, true)
413            }
414        }
415
416        impl BlockCipher for $name_ct {
417            const BLOCK_LEN: usize = 16;
418
419            fn encrypt(&self, block: &mut [u8]) {
420                let arr: &[u8; 16] = (&*block).try_into().expect("wrong block length");
421                let ct = self.encrypt_block(arr);
422                block.copy_from_slice(&ct);
423            }
424
425            fn decrypt(&self, block: &mut [u8]) {
426                let arr: &[u8; 16] = (&*block).try_into().expect("wrong block length");
427                let pt = self.decrypt_block(arr);
428                block.copy_from_slice(&pt);
429            }
430        }
431
432        impl Drop for $name_ct {
433            fn drop(&mut self) {
434                for rk in &mut self.round_keys {
435                    zeroize_slice(rk);
436                }
437            }
438        }
439    };
440}
441
442serpent_type!(
443    Serpent128,
444    Serpent128Ct,
445    16,
446    "Serpent with a 128-bit key (public byte order matches the original submission vectors).",
447    "Constant-time Serpent with a 128-bit key."
448);
449serpent_type!(
450    Serpent192,
451    Serpent192Ct,
452    24,
453    "Serpent with a 192-bit key (public byte order matches the original submission vectors).",
454    "Constant-time Serpent with a 192-bit key."
455);
456serpent_type!(
457    Serpent256,
458    Serpent256Ct,
459    32,
460    "Serpent with a 256-bit key (public byte order matches the original submission vectors).",
461    "Constant-time Serpent with a 256-bit key."
462);
463
464pub type Serpent = Serpent128;
465pub type SerpentCt = Serpent128Ct;
466
467#[cfg(test)]
468mod tests {
469    use super::*;
470
471    fn decode_hex(s: &str) -> Vec<u8> {
472        assert_eq!(s.len() % 2, 0);
473        let mut out = Vec::with_capacity(s.len() / 2);
474        let bytes = s.as_bytes();
475        let mut i = 0usize;
476        while i < bytes.len() {
477            let hi = (bytes[i] as char).to_digit(16).unwrap();
478            let lo = (bytes[i + 1] as char).to_digit(16).unwrap();
479            out.push(u8::try_from((hi << 4) | lo).expect("decoded hex byte fits in u8"));
480            i += 2;
481        }
482        out
483    }
484
485    #[test]
486    fn ct_sboxes_match_tables() {
487        for sbox in 0..8 {
488            for x in 0u8..16 {
489                assert_eq!(
490                    SBOXES[sbox][x as usize],
491                    sbox_ct_nibble(x, SBOXES_ANF[sbox])
492                );
493                assert_eq!(
494                    INV_SBOXES[sbox][x as usize],
495                    sbox_ct_nibble(x, INV_SBOXES_ANF[sbox])
496                );
497            }
498        }
499    }
500
501    #[test]
502    fn serpent128_kat() {
503        let key: [u8; 16] = decode_hex("80000000000000000000000000000000")
504            .try_into()
505            .unwrap();
506        let pt: [u8; 16] = decode_hex("00000000000000000000000000000000")
507            .try_into()
508            .unwrap();
509        let ct: [u8; 16] = decode_hex("49AFBFAD9D5A34052CD8FFA5986BD2DD")
510            .try_into()
511            .unwrap();
512        let cipher = Serpent128::new(&key);
513        assert_eq!(cipher.encrypt_block(&pt), ct);
514        assert_eq!(cipher.decrypt_block(&ct), pt);
515    }
516
517    #[test]
518    fn serpent128_ct_kat() {
519        let key: [u8; 16] = decode_hex("80000000000000000000000000000000")
520            .try_into()
521            .unwrap();
522        let pt: [u8; 16] = decode_hex("00000000000000000000000000000000")
523            .try_into()
524            .unwrap();
525        let ct: [u8; 16] = decode_hex("49AFBFAD9D5A34052CD8FFA5986BD2DD")
526            .try_into()
527            .unwrap();
528        let cipher = Serpent128Ct::new(&key);
529        assert_eq!(cipher.encrypt_block(&pt), ct);
530        assert_eq!(cipher.decrypt_block(&ct), pt);
531    }
532
533    #[test]
534    fn serpent128_standard_plaintext_vector() {
535        let key = [0u8; 16];
536        let pt: [u8; 16] = decode_hex("80000000000000000000000000000000")
537            .try_into()
538            .unwrap();
539        let ct: [u8; 16] = decode_hex("10B5FFB720B8CB9002A1142B0BA2E94A")
540            .try_into()
541            .unwrap();
542        let cipher = Serpent128::new(&key);
543        assert_eq!(cipher.encrypt_block(&pt), ct);
544        assert_eq!(cipher.decrypt_block(&ct), pt);
545    }
546
547    #[test]
548    fn serpent192_kat() {
549        let key: [u8; 24] = decode_hex("800000000000000000000000000000000000000000000000")
550            .try_into()
551            .unwrap();
552        let pt: [u8; 16] = decode_hex("00000000000000000000000000000000")
553            .try_into()
554            .unwrap();
555        let ct: [u8; 16] = decode_hex("E78E5402C7195568AC3678F7A3F60C66")
556            .try_into()
557            .unwrap();
558        let cipher = Serpent192::new(&key);
559        assert_eq!(cipher.encrypt_block(&pt), ct);
560        assert_eq!(cipher.decrypt_block(&ct), pt);
561    }
562
563    #[test]
564    fn serpent192_ct_kat() {
565        let key: [u8; 24] = decode_hex("800000000000000000000000000000000000000000000000")
566            .try_into()
567            .unwrap();
568        let pt: [u8; 16] = decode_hex("00000000000000000000000000000000")
569            .try_into()
570            .unwrap();
571        let ct: [u8; 16] = decode_hex("E78E5402C7195568AC3678F7A3F60C66")
572            .try_into()
573            .unwrap();
574        let cipher = Serpent192Ct::new(&key);
575        assert_eq!(cipher.encrypt_block(&pt), ct);
576        assert_eq!(cipher.decrypt_block(&ct), pt);
577    }
578
579    #[test]
580    fn serpent256_kat() {
581        let key: [u8; 32] =
582            decode_hex("8000000000000000000000000000000000000000000000000000000000000000")
583                .try_into()
584                .unwrap();
585        let pt: [u8; 16] = decode_hex("00000000000000000000000000000000")
586            .try_into()
587            .unwrap();
588        let ct: [u8; 16] = decode_hex("ABED96E766BF28CBC0EBD21A82EF0819")
589            .try_into()
590            .unwrap();
591        let cipher = Serpent256::new(&key);
592        assert_eq!(cipher.encrypt_block(&pt), ct);
593        assert_eq!(cipher.decrypt_block(&ct), pt);
594    }
595
596    #[test]
597    fn serpent256_ct_kat() {
598        let key: [u8; 32] =
599            decode_hex("8000000000000000000000000000000000000000000000000000000000000000")
600                .try_into()
601                .unwrap();
602        let pt: [u8; 16] = decode_hex("00000000000000000000000000000000")
603            .try_into()
604            .unwrap();
605        let ct: [u8; 16] = decode_hex("ABED96E766BF28CBC0EBD21A82EF0819")
606            .try_into()
607            .unwrap();
608        let cipher = Serpent256Ct::new(&key);
609        assert_eq!(cipher.encrypt_block(&pt), ct);
610        assert_eq!(cipher.decrypt_block(&ct), pt);
611    }
612}