Skip to main content

aes/backends/fixslice/
word.rs

1use crate::Block;
2use cipher::{
3    Array,
4    array::ArraySize,
5    consts::{U2, U4},
6};
7use core::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Not, Shl, Shr};
8
9/// Width-abstracted machine word holding one row of a bitsliced AES state.
10pub(crate) trait Word:
11    Sized
12    + Copy
13    + Default
14    + 'static
15    + BitAnd<Output = Self>
16    + BitAndAssign
17    + BitOr<Output = Self>
18    + BitOrAssign
19    + BitXor<Output = Self>
20    + BitXorAssign
21    + Not<Output = Self>
22    + Shl<u32, Output = Self>
23    + Shr<u32, Output = Self>
24{
25    /// Number of 128-bit blocks bitsliced together in one state.
26    type Blocks: ArraySize;
27
28    /// Width in bits of one row of the bitsliced state (8 for `u32`, 16 for `u64`).
29    const ROW_BITS: u32 = (size_of::<Self>() * 2) as u32;
30
31    /// Half of `ROW_BITS`.
32    const HALF_ROW: u32 = Self::ROW_BITS / 2;
33    /// Quarter of `ROW_BITS`.
34    const QUARTER_ROW: u32 = Self::ROW_BITS / 4;
35
36    /// Distance in bits to rotate a state row by `(rows, cols)` positions.
37    #[inline(always)]
38    fn ror_distance(rows: u32, cols: u32) -> u32 {
39        rows * Self::ROW_BITS + cols * Self::QUARTER_ROW
40    }
41
42    /// Rotate right by `n` bits.
43    fn ror(self, n: u32) -> Self;
44
45    /// Pack the same byte across all 4 rows of the word.
46    fn uniform_row(b: u8) -> Self;
47
48    /// Place one byte at each of the 4 row positions of the word (row 0 = LSB).
49    fn pack_rows(r0: u8, r1: u8, r2: u8, r3: u8) -> Self;
50
51    /// Replicate byte `b` across every byte of the word.
52    fn byte_repeat(b: u8) -> Self;
53
54    /// Pack `Self::Blocks` input blocks into a bitsliced 8-row state slice.
55    fn bitslice(output: &mut [Self], input: &Array<Block, Self::Blocks>);
56
57    /// Unpack a bitsliced 8-row state slice into `Self::Blocks` output blocks.
58    fn inv_bitslice(input: &[Self]) -> Array<Block, Self::Blocks>;
59}
60
61impl Word for u32 {
62    type Blocks = U2;
63
64    #[inline(always)]
65    fn ror(self, n: u32) -> u32 {
66        self.rotate_right(n)
67    }
68
69    #[inline(always)]
70    fn uniform_row(b: u8) -> u32 {
71        (b as u32) * 0x01010101
72    }
73
74    #[inline(always)]
75    fn pack_rows(r0: u8, r1: u8, r2: u8, r3: u8) -> u32 {
76        (r0 as u32) | ((r1 as u32) << 8) | ((r2 as u32) << 16) | ((r3 as u32) << 24)
77    }
78
79    #[inline(always)]
80    fn byte_repeat(b: u8) -> u32 {
81        (b as u32) * 0x01010101
82    }
83
84    /// Bitslice two 128-bit input blocks into a 256-bit internal state.
85    fn bitslice(output: &mut [u32], input: &Array<Block, U2>) {
86        debug_assert_eq!(output.len(), 8);
87        let input0 = input[0].as_slice();
88        let input1 = input[1].as_slice();
89
90        // Bitslicing is a bit index manipulation. 256 bits of data means each bit is positioned at
91        // an 8-bit index. AES data is 2 blocks, each one a 4x4 column-major matrix of bytes, so the
92        // index is initially ([b]lock, [c]olumn, [r]ow, [p]osition):
93        //     b0 c1 c0 r1 r0 p2 p1 p0
94        //
95        // The desired bitsliced data groups first by bit position, then row, column, block:
96        //     p2 p1 p0 r1 r0 c1 c0 b0
97
98        // Interleave the columns on input (note the order of input)
99        //     b0 c1 c0 __ __ __ __ __ => c1 c0 b0 __ __ __ __ __
100        let mut t = [
101            u32::from_le_bytes(input0[0x00..0x04].try_into().unwrap()),
102            u32::from_le_bytes(input1[0x00..0x04].try_into().unwrap()),
103            u32::from_le_bytes(input0[0x04..0x08].try_into().unwrap()),
104            u32::from_le_bytes(input1[0x04..0x08].try_into().unwrap()),
105            u32::from_le_bytes(input0[0x08..0x0c].try_into().unwrap()),
106            u32::from_le_bytes(input1[0x08..0x0c].try_into().unwrap()),
107            u32::from_le_bytes(input0[0x0c..0x10].try_into().unwrap()),
108            u32::from_le_bytes(input1[0x0c..0x10].try_into().unwrap()),
109        ];
110
111        bitslice_swaps(&mut t);
112
113        // Final bitsliced bit index, as desired:
114        //     p2 p1 p0 r1 r0 c1 c0 b0
115        output[..8].copy_from_slice(&t);
116    }
117
118    /// Un-bitslice a 256-bit internal state into two 128-bit blocks.
119    fn inv_bitslice(input: &[u32]) -> Array<Block, U2> {
120        debug_assert_eq!(input.len(), 8);
121
122        // Unbitslicing is a bit index manipulation. 256 bits of data means each bit is positioned
123        // at an 8-bit index. AES data is 2 blocks, each one a 4x4 column-major matrix of bytes, so
124        // the desired index for the output is ([b]lock, [c]olumn, [r]ow, [p]osition):
125        //     b0 c1 c0 r1 r0 p2 p1 p0
126        //
127        // The initially bitsliced data groups first by bit position, then row, column, block:
128        //     p2 p1 p0 r1 r0 c1 c0 b0
129
130        let mut t = [
131            input[0], input[1], input[2], input[3], input[4], input[5], input[6], input[7],
132        ];
133
134        bitslice_swaps(&mut t);
135
136        let mut output = Array::<Block, U2>::default();
137        // De-interleave the columns on output (note the order of output)
138        //     c1 c0 b0 __ __ __ __ __ => b0 c1 c0 __ __ __ __ __
139        output[0][0x00..0x04].copy_from_slice(&t[0].to_le_bytes());
140        output[0][0x04..0x08].copy_from_slice(&t[2].to_le_bytes());
141        output[0][0x08..0x0c].copy_from_slice(&t[4].to_le_bytes());
142        output[0][0x0c..0x10].copy_from_slice(&t[6].to_le_bytes());
143        output[1][0x00..0x04].copy_from_slice(&t[1].to_le_bytes());
144        output[1][0x04..0x08].copy_from_slice(&t[3].to_le_bytes());
145        output[1][0x08..0x0c].copy_from_slice(&t[5].to_le_bytes());
146        output[1][0x0c..0x10].copy_from_slice(&t[7].to_le_bytes());
147
148        // Final AES bit index, as desired:
149        //     b0 c1 c0 r1 r0 p2 p1 p0
150        output
151    }
152}
153
154/// Expand an 8-bit row pattern to a 16-bit row pattern by doubling each bit:
155/// input bit `i` becomes output bits `2i` and `2i+1`. Branchless SWAR so LLVM
156/// folds it to a single 16-bit immediate when `b` is a constant.
157#[inline(always)]
158const fn double_bits(b: u8) -> u16 {
159    let x = b as u16;
160    // Spread the 8 bits of x to even positions 0,2,4,6,8,10,12,14.
161    let x = (x | (x << 4)) & 0x0f0f;
162    let x = (x | (x << 2)) & 0x3333;
163    let x = (x | (x << 1)) & 0x5555;
164    // Duplicate each spread bit to its adjacent odd position.
165    x | (x << 1)
166}
167
168impl Word for u64 {
169    type Blocks = U4;
170
171    #[inline(always)]
172    fn ror(self, n: u32) -> u64 {
173        self.rotate_right(n)
174    }
175
176    #[inline(always)]
177    fn uniform_row(b: u8) -> u64 {
178        (double_bits(b) as u64) * 0x0001_0001_0001_0001
179    }
180
181    #[inline(always)]
182    fn pack_rows(r0: u8, r1: u8, r2: u8, r3: u8) -> u64 {
183        (double_bits(r0) as u64)
184            | ((double_bits(r1) as u64) << 16)
185            | ((double_bits(r2) as u64) << 32)
186            | ((double_bits(r3) as u64) << 48)
187    }
188
189    #[inline(always)]
190    fn byte_repeat(b: u8) -> u64 {
191        (b as u64) * 0x0101010101010101
192    }
193
194    /// Bitslice four 128-bit input blocks into a 512-bit internal state.
195    fn bitslice(output: &mut [u64], input: &Array<Block, U4>) {
196        debug_assert_eq!(output.len(), 8);
197
198        // Bitslicing is a bit index manipulation. 512 bits of data means each bit is positioned at
199        // a 9-bit index. AES data is 4 blocks, each one a 4x4 column-major matrix of bytes, so the
200        // index is initially ([b]lock, [c]olumn, [r]ow, [p]osition):
201        //     b1 b0 c1 c0 r1 r0 p2 p1 p0
202        //
203        // The desired bitsliced data groups first by bit position, then row, column, block:
204        //     p2 p1 p0 r1 r0 c1 c0 b1 b0
205
206        #[rustfmt::skip]
207        fn read_reordered(input: &[u8]) -> u64 {
208            (u64::from(input[0x0])        ) |
209            (u64::from(input[0x1]) << 0x10) |
210            (u64::from(input[0x2]) << 0x20) |
211            (u64::from(input[0x3]) << 0x30) |
212            (u64::from(input[0x8]) << 0x08) |
213            (u64::from(input[0x9]) << 0x18) |
214            (u64::from(input[0xa]) << 0x28) |
215            (u64::from(input[0xb]) << 0x38)
216        }
217
218        // Reorder each block's bytes on input
219        //     __ __ c1 c0 r1 r0 __ __ __ => __ __ c0 r1 r0 c1 __ __ __
220        // Reorder by relabeling (note the order of input)
221        //     b1 b0 c0 __ __ __ __ __ __ => c0 b1 b0 __ __ __ __ __ __
222        let mut t = [
223            read_reordered(&input[0][0x00..0x0c]),
224            read_reordered(&input[1][0x00..0x0c]),
225            read_reordered(&input[2][0x00..0x0c]),
226            read_reordered(&input[3][0x00..0x0c]),
227            read_reordered(&input[0][0x04..0x10]),
228            read_reordered(&input[1][0x04..0x10]),
229            read_reordered(&input[2][0x04..0x10]),
230            read_reordered(&input[3][0x04..0x10]),
231        ];
232
233        bitslice_swaps(&mut t);
234
235        // Final bitsliced bit index, as desired:
236        //     p2 p1 p0 r1 r0 c1 c0 b1 b0
237        output[..8].copy_from_slice(&t);
238    }
239
240    /// Un-bitslice a 512-bit internal state into four 128-bit blocks.
241    fn inv_bitslice(input: &[u64]) -> Array<Block, U4> {
242        debug_assert_eq!(input.len(), 8);
243
244        // Unbitslicing is a bit index manipulation. 512 bits of data means each bit is positioned
245        // at a 9-bit index. AES data is 4 blocks, each one a 4x4 column-major matrix of bytes, so
246        // the desired index for the output is ([b]lock, [c]olumn, [r]ow, [p]osition):
247        //     b1 b0 c1 c0 r1 r0 p2 p1 p0
248        //
249        // The initially bitsliced data groups first by bit position, then row, column, block:
250        //     p2 p1 p0 r1 r0 c1 c0 b1 b0
251
252        let mut t = [
253            input[0], input[1], input[2], input[3], input[4], input[5], input[6], input[7],
254        ];
255
256        bitslice_swaps(&mut t);
257
258        #[rustfmt::skip]
259        fn write_reordered(columns: u64, output: &mut [u8]) {
260            output[0x0] = (columns        ) as u8;
261            output[0x1] = (columns >> 0x10) as u8;
262            output[0x2] = (columns >> 0x20) as u8;
263            output[0x3] = (columns >> 0x30) as u8;
264            output[0x8] = (columns >> 0x08) as u8;
265            output[0x9] = (columns >> 0x18) as u8;
266            output[0xa] = (columns >> 0x28) as u8;
267            output[0xb] = (columns >> 0x38) as u8;
268        }
269
270        let mut output = Array::<Block, U4>::default();
271        // Reorder by relabeling (note the order of output)
272        //     c0 b1 b0 __ __ __ __ __ __ => b1 b0 c0 __ __ __ __ __ __
273        // Reorder each block's bytes on output
274        //     __ __ c0 r1 r0 c1 __ __ __ => __ __ c1 c0 r1 r0 __ __ __
275        write_reordered(t[0], &mut output[0][0x00..0x0c]);
276        write_reordered(t[4], &mut output[0][0x04..0x10]);
277        write_reordered(t[1], &mut output[1][0x00..0x0c]);
278        write_reordered(t[5], &mut output[1][0x04..0x10]);
279        write_reordered(t[2], &mut output[2][0x00..0x0c]);
280        write_reordered(t[6], &mut output[2][0x04..0x10]);
281        write_reordered(t[3], &mut output[3][0x00..0x0c]);
282        write_reordered(t[7], &mut output[3][0x04..0x10]);
283
284        // Final AES bit index, as desired:
285        //     b1 b0 c1 c0 r1 r0 p2 p1 p0
286        output
287    }
288}
289
290/// Width-generic delta-swap pipeline shared by `bitslice` and `inv_bitslice`
291/// across every `Word` impl. The same three-pass sequence inverts itself, so
292/// `bitslice` and `inv_bitslice` invoke it identically.
293///
294/// The diagrams below describe the `u32` case (8-bit rows); for `u64` each
295/// bit position widens by one, but the swap structure is unchanged.
296#[inline(always)]
297fn bitslice_swaps<W: Word>(t: &mut [W; 8]) {
298    use super::utils::delta_swap_2;
299    let [t0, t1, t2, t3, t4, t5, t6, t7] = t;
300
301    // Bit Index Swap 5 <-> 0:
302    //     __ __ b0 __ __ __ __ p0 => __ __ p0 __ __ __ __ b0
303    let m0 = W::byte_repeat(0x55);
304    delta_swap_2(t1, t0, 1, m0);
305    delta_swap_2(t3, t2, 1, m0);
306    delta_swap_2(t5, t4, 1, m0);
307    delta_swap_2(t7, t6, 1, m0);
308
309    // Bit Index Swap 6 <-> 1:
310    //     __ c0 __ __ __ __ p1 __ => __ p1 __ __ __ __ c0 __
311    let m1 = W::byte_repeat(0x33);
312    delta_swap_2(t2, t0, 2, m1);
313    delta_swap_2(t3, t1, 2, m1);
314    delta_swap_2(t6, t4, 2, m1);
315    delta_swap_2(t7, t5, 2, m1);
316
317    // Bit Index Swap 7 <-> 2:
318    //     c1 __ __ __ __ p2 __ __ => p2 __ __ __ __ c1 __ __
319    let m2 = W::byte_repeat(0x0f);
320    delta_swap_2(t4, t0, 4, m2);
321    delta_swap_2(t5, t1, 4, m2);
322    delta_swap_2(t6, t2, 4, m2);
323    delta_swap_2(t7, t3, 4, m2);
324}