aes_frast 0.2.2

An easy-to-use lib for AES encryption and decryption, coded in pure safe Rust-lang.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
//! This build script contains computations to generate S-box and other lookup
//! tables that excutes only once, before compiling.
//! The algorithms here are meant to demonstrate how the computations are done,
//! not high-performance Galois Field operations. In other words, the Galois
//! field algorithm here is not optimized.
fn main() {
    println!("cargo:rerun-if-changed=build.rs");
    let dest_path = std::path::Path::new(&std::env::var_os("OUT_DIR").unwrap()).join("tables.rs");
    let tables_txt = calculate_tables();
    std::fs::write(dest_path, tables_txt).unwrap();
}
fn calculate_tables() -> String {
    use crate::aes_affine_mapping::aes_sbox_affine_mapping as affine_mapping;
    use crate::aes_affine_mapping::aes_sbox_inverse_affine_mapping as inverse_mapping;
    use crate::gf28::GF2_8;
    use crate::t_box::*;
    // A GF2_8 for computing the tables.
    let mut gf = GF2_8::default();
    // Another two GF2_8 for storing the S-Boxed `gf` and inverse-S-Boxed `gf`.
    let mut substitued_gf = GF2_8::default();
    let mut invrsboxed_gf: GF2_8;
    // Three strings for Rust code of SBox and something output.
    let mut sboxtable_code = String::with_capacity(2048);
    sboxtable_code.push_str("// S-Box\nstatic SBOX: &[u8; 256] = &[\n");
    let mut inversetable_code = String::with_capacity(2048);
    inversetable_code.push_str("// Inversed S-Box\nstatic SINV: &[u8; 256] = &[\n");
    //Eight strings for Rust code of TBoxes output.
    let mut e_t0_code = String::with_capacity(4096);
    e_t0_code.push_str("// T-Box 0 for encryption\nstatic TE0: &[u32; 256] = &[\n");
    let mut e_t1_code = String::with_capacity(4096);
    e_t1_code.push_str("// T-Box 1 for encryption\nstatic TE1: &[u32; 256] = &[\n");
    let mut e_t2_code = String::with_capacity(4096);
    e_t2_code.push_str("// T-Box 2 for encryption\nstatic TE2: &[u32; 256] = &[\n");
    let mut e_t3_code = String::with_capacity(4096);
    e_t3_code.push_str("// T-Box 3 for encryption\nstatic TE3: &[u32; 256] = &[\n");
    let mut d_t0_code = String::with_capacity(4096);
    d_t0_code.push_str("// T-Box 0 for decryption\nstatic TD0: &[u32; 256] = &[\n");
    let mut d_t1_code = String::with_capacity(4096);
    d_t1_code.push_str("// T-Box 1 for decryption\nstatic TD1: &[u32; 256] = &[\n");
    let mut d_t2_code = String::with_capacity(4096);
    d_t2_code.push_str("// T-Box 2 for decryption\nstatic TD2: &[u32; 256] = &[\n");
    let mut d_t3_code = String::with_capacity(4096);
    d_t3_code.push_str("// T-Box 3 for decryption\nstatic TD3: &[u32; 256] = &[\n");
    // Three variables to temporarily store numbers.
    let mut mapped_inverse: u8;
    let mut invsbox: u8;
    let mut number: u8;
    for m in 0..16 {
        sboxtable_code.push_str("    ");
        inversetable_code.push_str("    ");
        e_t0_code.push_str("    ");
        e_t1_code.push_str("    ");
        e_t2_code.push_str("    ");
        e_t3_code.push_str("    ");
        d_t0_code.push_str("    ");
        d_t1_code.push_str("    ");
        d_t2_code.push_str("    ");
        d_t3_code.push_str("    ");
        for n in 0..16 {
            number = (m << 4) | n;
            gf.set(number);
            mapped_inverse = affine_mapping(gf.inverse().unwrap_or_default().get_byte());
            substitued_gf.set(mapped_inverse);
            // Although the table of the inversed S-Box can be Obtained by looking up
            // S-Box table inversely, it doesn't hurt to know compute it with matrix
            // for studying AES.
            gf.set(inverse_mapping(number));
            invrsboxed_gf = gf.inverse().unwrap_or_default();
            invsbox = invrsboxed_gf.get_byte();
            sboxtable_code.push_str(format!("0x{:02X}, ", mapped_inverse).as_str());
            inversetable_code.push_str(format!("0x{:02X}, ", invsbox).as_str());
            e_t0_code.push_str(format!("0x{:08X}, ", e_t0_element(&substitued_gf)).as_str());
            e_t1_code.push_str(format!("0x{:08X}, ", e_t1_element(&substitued_gf)).as_str());
            e_t2_code.push_str(format!("0x{:08X}, ", e_t2_element(&substitued_gf)).as_str());
            e_t3_code.push_str(format!("0x{:08X}, ", e_t3_element(&substitued_gf)).as_str());
            d_t0_code.push_str(format!("0x{:08X}, ", d_t0_element(&invrsboxed_gf)).as_str());
            d_t1_code.push_str(format!("0x{:08X}, ", d_t1_element(&invrsboxed_gf)).as_str());
            d_t2_code.push_str(format!("0x{:08X}, ", d_t2_element(&invrsboxed_gf)).as_str());
            d_t3_code.push_str(format!("0x{:08X}, ", d_t3_element(&invrsboxed_gf)).as_str());
            if n == 7 {
                e_t0_code.pop();
                e_t0_code.push_str("\n    ");
                e_t1_code.pop();
                e_t1_code.push_str("\n    ");
                e_t2_code.pop();
                e_t2_code.push_str("\n    ");
                e_t3_code.pop();
                e_t3_code.push_str("\n    ");
                d_t0_code.pop();
                d_t0_code.push_str("\n    ");
                d_t1_code.pop();
                d_t1_code.push_str("\n    ");
                d_t2_code.pop();
                d_t2_code.push_str("\n    ");
                d_t3_code.pop();
                d_t3_code.push_str("\n    ");
            }
        }
        sboxtable_code.pop();
        sboxtable_code.push('\n');
        inversetable_code.pop();
        inversetable_code.push('\n');
        e_t0_code.pop();
        e_t0_code.push('\n');
        e_t1_code.pop();
        e_t1_code.push('\n');
        e_t2_code.pop();
        e_t2_code.push('\n');
        e_t3_code.pop();
        e_t3_code.push('\n');
        d_t0_code.pop();
        d_t0_code.push('\n');
        d_t1_code.pop();
        d_t1_code.push('\n');
        d_t2_code.pop();
        d_t2_code.push('\n');
        d_t3_code.pop();
        d_t3_code.push('\n');
    }
    sboxtable_code.push_str("];\n");
    inversetable_code.push_str("];\n");
    e_t0_code.push_str("];\n");
    e_t1_code.push_str("];\n");
    e_t2_code.push_str("];\n");
    e_t3_code.push_str("];\n");
    d_t0_code.push_str("];\n");
    d_t1_code.push_str("];\n");
    d_t2_code.push_str("];\n");
    d_t3_code.push_str("];\n");
    let mut final_out = String::with_capacity(32768);
    final_out.push_str(&sboxtable_code);
    final_out.push_str(&inversetable_code);
    final_out.push_str(&e_t0_code);
    final_out.push_str(&e_t1_code);
    final_out.push_str(&e_t2_code);
    final_out.push_str(&e_t3_code);
    final_out.push_str(&d_t0_code);
    final_out.push_str(&d_t1_code);
    final_out.push_str(&d_t2_code);
    final_out.push_str(&d_t3_code);
    // Compute the addend (round coefficient) needed in the round function.
    let x = GF2_8::new(0b_0000_0010);
    let mut a = GF2_8::new(0b_0000_0001);
    final_out.push_str(
        "// Addend (round coefficient) needed in the round function\nconst RC: &[u8; 10] = &[\n    ",
    );
    final_out.push_str(format!("0x{:02X},\n", a.get_byte()).as_str());
    for _ in 1..10 {
        a = x * a;
        final_out.push_str(format!("    0x{:02X},\n", a.get_byte()).as_str());
    }
    final_out.push_str("];\n");
    final_out
}
mod gf28 {
    //! # gf28
    //! `GF2_8` is one of foundamental structures for low-level compution of AES S-box, though many fast
    //! implements now use look-up-table.
    use std::fmt;
    use std::ops::{Add, Div, Mul, Sub};
    // There is a small difference between `derive` and `impl` manually.
    // https://doc.rust-lang.org/std/marker/trait.Copy.html#how-can-i-implement-copy
    #[derive(Copy, Clone)]
    pub struct GF2_8 {
        inner: u8,
    }
    impl Default for GF2_8 {
        /// The default value of GF2_8 is `GF2_8 {inner: 0b_0000_0000u8}`.
        fn default() -> Self {
            Self { inner: 0u8 }
        }
    }
    // AES standard use this inrreducible polynomial:
    // x^8 + x^4 + x^3 + x + 1
    const IRREDU_POLYNO: u16 = 0b1_0001_1011;
    /// Bitwise multipl and mod for inner u16 computing.  
    /// Like a function parameters, the pattern should be `(left: u16, right: u16)`.  
    /// And this returns `u16`.
    macro_rules! bitwise_multiply_with_mod {
        ($left:expr, $right:expr) => {{
            let mut result: u16 = 0;
            let mut digit: u16 = 1;
            for i in 0..8 {
                if (digit & $right) != 0 {
                    result ^= $left << i;
                }
                digit <<= 1;
            }
            // when result >= 2^8 need MOD
            if result >= 0b_1_0000_0000 {
                let modp = IRREDU_POLYNO << 6;
                digit <<= 6;
                for i in 0..7 {
                    if (digit & result) != 0 {
                        result ^= modp >> i;
                    }
                    digit >>= 1;
                }
            }
            result
        }};
    }
    /// Bitwise divide for inner u16 computing.  
    /// This function retures `(quotient, reminder)`.  
    /// Like a function parameters, the pattern should be `(dividend: u16, divisor: u16)`.  
    /// And this returns `(quotient: u16, reminder: u16)`.
    macro_rules! bitwise_divide {
        ($dividend:expr, $divisor:expr) => {{
            let mut shift = $divisor.leading_zeros() - 7;
            let divisor: u16 = $divisor << shift;
            shift += 1;
            let mut reminder: u16 = $dividend;
            let mut quotient: u16 = 0;
            let mut digit: u16 = 0b_1_0000_0000;
            for i in 0..shift {
                if (digit & reminder) != 0 {
                    reminder ^= divisor >> i;
                    // Bitwise operator is a little faster than add operator. They achieve the same
                    // effect because there is no need to carry. So I use `^=` instead of `+=` here.
                    // In fact, in this case, because there will never be `1 op 1` (as a result of
                    // shifting bits), using `|=` as the `op` is ok.
                    quotient ^= 1;
                }
                quotient <<= 1;
                digit >>= 1;
            }
            quotient >>= 1;
            (quotient, reminder)
        }};
    }
    impl GF2_8 {
        /// Create a new GF2_8 from a u8 value.
        pub fn new(value: u8) -> Self {
            Self { inner: value }
        }
        /// Get the inner value of a GF2_8 as u8.
        pub fn get_byte(&self) -> u8 {
            self.inner
        }
        /// Set the inner value of a GF2_8.
        pub fn set(&mut self, value: u8) {
            self.inner = value;
        }
        /// Get the inverse of a GF2_8.  
        /// This is implemented by calculating using the Extended Euclidean Algorithm.  
        /// Note that 0 has no inverse, so this function will return `None`.
        pub fn inverse(&self) -> Option<Self> {
            if self.inner >= 2u8 {
                let mut t2: u16 = 0u16;
                let mut t1: u16 = 1u16;
                let mut r2: u16 = IRREDU_POLYNO;
                let mut r1: u16 = self.inner as u16;
                let mut tmp: u16;
                while r1 != 0u16 {
                    let (quotient, reminder) = bitwise_divide!(r2, r1);
                    tmp = t2 ^ bitwise_multiply_with_mod!(quotient, t1);
                    r2 = r1;
                    r1 = reminder;
                    t2 = t1;
                    t1 = tmp;
                }
                Some(Self { inner: t2 as u8 })
            } else if self.inner == 1u8 {
                Some(*self)
            } else {
                None
            }
        }
    }
    impl fmt::Display for GF2_8 {
        /// Format the inner value of GF2_8 as 8bit binary.
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            write!(f, "{:08b}", self.inner)
        }
    }
    impl Add for GF2_8 {
        type Output = Self;
        /// Add operator.
        fn add(self, other: Self) -> Self {
            Self {
                inner: self.inner ^ other.inner,
            }
        }
    }
    impl Sub for GF2_8 {
        type Output = Self;
        /// Subtact operator. In fact this is the same as its add operator.
        fn sub(self, other: Self) -> Self {
            Self {
                inner: self.inner ^ other.inner,
            }
        }
    }
    impl Mul for GF2_8 {
        type Output = Self;
        /// Multiply operator. MOD by AES standard inrreducible polynomial.  
        /// This is implemented by multiplying bitwisely, rather than looking up table calculated
        /// from GF generators.
        fn mul(self, other: Self) -> Self {
            Self {
                inner: bitwise_multiply_with_mod!(self.inner as u16, other.inner as u16) as u8,
            }
        }
    }
    impl Div for GF2_8 {
        type Output = Self;
        /// Divide operator.  
        /// This uses the definition that `a / b == a * b.inverse()`.  
        /// Note that 'divided by zero' will lead to `panic!`.
        fn div(self, other: Self) -> Self {
            if other.inner != 1 {
                self * other.inverse().expect("0 has no inverse.")
            } else {
                Self { inner: self.inner }
            }
        }
    }
    #[cfg(test)]
    mod tests {
        use super::GF2_8;
        // There are some redundant code.
        // To make the code easy to understand, I didn't put them in a Rust-lang macro.
        #[test]
        fn basics_works() {
            let mut foo = GF2_8::new(0b_1000_0000);
            assert_eq!(foo.inner, 0b_1000_0000);
            foo.set(0b_0000_0010);
            assert_eq!(foo.get_byte(), 0b_0000_0010);
        }
        #[test]
        fn add_works() {
            let mut foo = GF2_8::new(0b_1000_0000);
            let mut bar = GF2_8::new(0b_0000_0010);
            assert_eq!((foo + bar).inner, 0b_1000_0010);
            foo = GF2_8::new(0b_1100_0010);
            bar = GF2_8::new(0b_0010_1111);
            assert_eq!((foo + bar).inner, 0b_1110_1101);
        }
        #[test]
        fn sub_works() {
            let mut foo = GF2_8::new(0b_1000_0000);
            let mut bar = GF2_8::new(0b_0000_0010);
            assert_eq!((foo - bar).inner, 0b_1000_0010);
            foo = GF2_8::new(0b_1100_0010);
            bar = GF2_8::new(0b_0010_1111);
            assert_eq!((foo - bar).inner, 0b_1110_1101);
        }
        #[test]
        fn mul_works() {
            let mut foo = GF2_8::new(0b_1000_0000);
            let mut bar = GF2_8::new(0b_0000_0010);
            assert_eq!((foo * bar).inner, 0b_0001_1011);
            foo = GF2_8::new(0b_1100_0010);
            bar = GF2_8::new(0b_0010_1111);
            assert_eq!((foo * bar).inner, 0b_0000_0001);
        }
        #[test]
        fn inverse_works() {
            let mut foo = GF2_8::new(0b_1000_0000);
            let mut bar = GF2_8::new(0b_0000_0010);
            assert_eq!(foo.inverse().unwrap().inner, 0x83);
            assert_eq!(bar.inverse().unwrap().inner, 0x8D);
            foo = GF2_8::new(0b_1100_0010);
            bar = GF2_8::new(0b_0010_1111);
            assert_eq!(foo.inverse().unwrap().inner, 0x2F);
            assert_eq!(bar.inverse().unwrap().inner, 0xC2);
            foo = GF2_8::new(0);
            assert_eq!(foo.inverse().unwrap_or_default().inner, 0);
        }
        #[test]
        fn div_works() {
            let mut foo = GF2_8::new(0b_1000_0000);
            let mut bar = GF2_8::new(0b_0000_0010);
            assert_eq!((foo / bar).inner, 0b_0100_0000);
            foo = GF2_8::new(0b_1100_0010);
            bar = GF2_8::new(0b_0010_1111);
            assert_eq!((foo / bar).inner, 0b_0011_0101);
            foo = GF2_8::new(0b_1010_0110);
            bar = GF2_8::new(1);
            assert_eq!((foo / bar).inner, 0b_1010_0110);
        }
        #[test]
        #[should_panic]
        fn div_zero_panic() {
            let foo = GF2_8::new(1);
            let bar = GF2_8::new(0);
            let _p = foo / bar;
        }
    }
}
mod aes_affine_mapping {
    const AES_AFFINE_MAPPING_MATRIX: [u8; 8] = [
        0b_1000_1111,
        0b_1100_0111,
        0b_1110_0011,
        0b_1111_0001,
        0b_1111_1000,
        0b_0111_1100,
        0b_0011_1110,
        0b_0001_1111,
    ];
    const AES_AFFINE_MAPPING_VECTOR: [u8; 8] = [1, 1, 0, 0, 0, 1, 1, 0];
    // This matrix isn't a part of AES standard, nether a part in the original paper.
    // Because AES standard just gave the table of inversed S-Box directly.
    // This is from the book "The design of Rijndael: AES -- the Advanced Encryption Standard".
    const AES_INVERSE_AFFINE_MAPPING_MATRIX: [u8; 8] = [
        0b_0010_0101,
        0b_1001_0010,
        0b_0100_1001,
        0b_1010_0100,
        0b_0101_0010,
        0b_0010_1001,
        0b_1001_0100,
        0b_0100_1010,
    ];
    const AES_INVERSE_AFFINE_MAPPING_VECTOR: [u8; 8] = [1, 0, 1, 0, 0, 0, 0, 0];
    macro_rules! swap_binary {
        ($input:expr) => {{
            let mut input: u8 = $input;
            let mut output: u8 = input & 0b_0000_0001;
            for _ in 1..8 {
                output <<= 1;
                input >>= 1;
                // Bitwise operator is a little faster than add operator. They achieve the same effect
                // because there is no need to carry. So I use `^=` instead of `+=` here.
                output ^= input & 0b_0000_0001;
            }
            output
        }};
    }
    macro_rules! affine_mapping_operate {
        ($value:ident, $matrix:ident, $vector:ident) => {{
            let mut result: u8 = 0;
            for i in (0..8).rev() {
                // I use `^=` instead of `+=` because there is no need to carry.
                // Also `^` for `+`.
                // For a unsigned integer, `% 2` only depends on LSB, so I use faster bitwise operator
                // rather than remainder operator.
                result ^= ((swap_binary!($matrix[i]) & $value).count_ones() as u8 ^ $vector[i])
                    & 0b_0000_0001;
                if i != 0 {
                    result <<= 1;
                }
            }
            result
        }};
    }
    pub fn aes_sbox_affine_mapping(value: u8) -> u8 {
        affine_mapping_operate!(value, AES_AFFINE_MAPPING_MATRIX, AES_AFFINE_MAPPING_VECTOR)
    }
    pub fn aes_sbox_inverse_affine_mapping(value: u8) -> u8 {
        affine_mapping_operate!(
            value,
            AES_INVERSE_AFFINE_MAPPING_MATRIX,
            AES_INVERSE_AFFINE_MAPPING_VECTOR
        )
    }
    #[cfg(test)]
    mod tests {
        use super::aes_sbox_affine_mapping as affine_mapping;
        use super::aes_sbox_inverse_affine_mapping as inverse_mapping;
        #[test]
        fn it_works() {
            for i in 0..255 {
                assert_eq!(i, inverse_mapping(affine_mapping(i)));
            }
            assert_eq!(255, inverse_mapping(affine_mapping(255)));
        }
    }
}
mod t_box {
    use super::gf28::GF2_8;
    // The matrix in MixColumn transformation is broken down into column vectors.
    const ENCRYPT_T0_VECTOR: [u8; 4] = [0x02, 0x01, 0x01, 0x03];
    const ENCRYPT_T1_VECTOR: [u8; 4] = [0x03, 0x02, 0x01, 0x01];
    const ENCRYPT_T2_VECTOR: [u8; 4] = [0x01, 0x03, 0x02, 0x01];
    const ENCRYPT_T3_VECTOR: [u8; 4] = [0x01, 0x01, 0x03, 0x02];
    const DECRYPT_T0_VECTOR: [u8; 4] = [0x0E, 0x09, 0x0D, 0x0B];
    const DECRYPT_T1_VECTOR: [u8; 4] = [0x0B, 0x0E, 0x09, 0x0D];
    const DECRYPT_T2_VECTOR: [u8; 4] = [0x0D, 0x0B, 0x0E, 0x09];
    const DECRYPT_T3_VECTOR: [u8; 4] = [0x09, 0x0D, 0x0B, 0x0E];
    macro_rules! do_the_calculation {
        ($gf:ident, $vector:ident) => {{
            let mut multiplier = GF2_8::default();
            let mut values = [0_u8; 4];
            for i in 0..4 {
                multiplier.set($vector[i]);
                values[i] = (multiplier * (*$gf)).get_byte();
            }
            ::std::primitive::u32::from_le_bytes(values)
        }};
    }
    pub fn e_t0_element(gf: &GF2_8) -> u32 {
        do_the_calculation!(gf, ENCRYPT_T0_VECTOR)
    }
    pub fn e_t1_element(gf: &GF2_8) -> u32 {
        do_the_calculation!(gf, ENCRYPT_T1_VECTOR)
    }
    pub fn e_t2_element(gf: &GF2_8) -> u32 {
        do_the_calculation!(gf, ENCRYPT_T2_VECTOR)
    }
    pub fn e_t3_element(gf: &GF2_8) -> u32 {
        do_the_calculation!(gf, ENCRYPT_T3_VECTOR)
    }
    pub fn d_t0_element(gf: &GF2_8) -> u32 {
        do_the_calculation!(gf, DECRYPT_T0_VECTOR)
    }
    pub fn d_t1_element(gf: &GF2_8) -> u32 {
        do_the_calculation!(gf, DECRYPT_T1_VECTOR)
    }
    pub fn d_t2_element(gf: &GF2_8) -> u32 {
        do_the_calculation!(gf, DECRYPT_T2_VECTOR)
    }
    pub fn d_t3_element(gf: &GF2_8) -> u32 {
        do_the_calculation!(gf, DECRYPT_T3_VECTOR)
    }
    #[cfg(test)]
    mod tests {
        use super::*;
        #[test]
        fn et0_works() {
            let tester = GF2_8::new(0xC2).inverse().unwrap();
            assert_eq!(e_t0_element(&tester), 0x712F2F5E);
        }
        #[test]
        fn dt0_works() {
            let tester = GF2_8::new(0x02);
            assert_eq!(d_t0_element(&tester), 0x161A121C);
        }
    }
}