1const MODULUS: u8 = 0x1b;
23
24#[inline(always)]
26pub const fn mul(mut a: u8, mut b: u8) -> u8 {
27 let mut p: u8 = 0;
28 let mut i = 0;
29 while i < 8 {
30 p ^= a & (b & 1).wrapping_neg();
32 let hi = (a >> 7) & 1;
34 a <<= 1;
35 a ^= MODULUS & hi.wrapping_neg();
36 b >>= 1;
37 i += 1;
38 }
39 p
40}
41
42#[inline(always)]
60pub const fn square(x: u8) -> u8 {
61 let t = x as u16;
63 let t = (t | (t << 4)) & 0x0f0f;
64 let t = (t | (t << 2)) & 0x3333;
65 let t = (t | (t << 1)) & 0x5555;
66
67 let mut r = (t & 0xff) as u8;
69 r ^= 0x1b & (((t >> 8) & 1) as u8).wrapping_neg();
70 r ^= 0x6c & (((t >> 10) & 1) as u8).wrapping_neg();
71 r ^= 0xab & (((t >> 12) & 1) as u8).wrapping_neg();
72 r ^= 0x9a & (((t >> 14) & 1) as u8).wrapping_neg();
73 r
74}
75
76#[inline(always)]
78pub const fn xtime(a: u8) -> u8 {
79 let hi = (a >> 7) & 1;
80 (a << 1) ^ (MODULUS & hi.wrapping_neg())
81}
82
83#[inline(always)]
89pub const fn inv(x: u8) -> u8 {
90 let mut r = x;
91 let mut bit = 6i32;
92 while bit >= 0 {
94 r = square(r);
95 if bit > 0 {
96 r = mul(r, x);
97 }
98 bit -= 1;
99 }
100 r
101}
102
103#[inline(always)]
105pub const fn sbox(x: u8) -> u8 {
106 let y = inv(x);
107 y ^ y.rotate_left(1) ^ y.rotate_left(2) ^ y.rotate_left(3) ^ y.rotate_left(4) ^ 0x63
108}
109
110#[inline(always)]
112pub const fn inv_sbox(y: u8) -> u8 {
113 let t = y.rotate_left(1) ^ y.rotate_left(3) ^ y.rotate_left(6) ^ 0x05;
114 inv(t)
115}
116
117#[cfg(test)]
118mod tests {
119 use super::*;
120
121 const SBOX_REFERENCE: [u8; 256] = [
124 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab,
125 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4,
126 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71,
127 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2,
128 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6,
129 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb,
130 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45,
131 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5,
132 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44,
133 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a,
134 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49,
135 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d,
136 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25,
137 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e,
138 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1,
139 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
140 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb,
141 0x16,
142 ];
143
144 #[test]
145 fn algebraic_sbox_matches_fips197() {
146 for x in 0..=255u8 {
147 assert_eq!(sbox(x), SBOX_REFERENCE[x as usize], "S({x:#04x})");
148 }
149 }
150
151 #[test]
152 fn inverse_sbox_undoes_sbox() {
153 for x in 0..=255u8 {
154 assert_eq!(inv_sbox(sbox(x)), x, "S^-1(S({x:#04x}))");
155 }
156 }
157
158 #[test]
159 fn field_inversion_is_correct() {
160 assert_eq!(inv(0), 0);
161 for x in 1..=255u8 {
162 assert_eq!(mul(x, inv(x)), 1, "x * x^-1 for {x:#04x}");
163 }
164 }
165
166 #[test]
167 fn xtime_matches_multiplication_by_two() {
168 for x in 0..=255u8 {
169 assert_eq!(xtime(x), mul(x, 2));
170 }
171 }
172
173 #[test]
179 fn squaring_matches_the_general_multiply() {
180 for x in 0..=u8::MAX {
181 assert_eq!(square(x), mul(x, x), "square({x:#04x})");
182 }
183 }
184}