Skip to main content

fast_des/
lib.rs

1use bitsliced_op::{ALL_ONES, transpose_64x64};
2use core::arch::x86_64::__m512i;
3use std::{
4    arch::x86_64::{
5        _mm512_set_epi64, _mm512_set1_epi64, _mm512_setzero_si512, _mm512_storeu_si512,
6    },
7    mem::transmute,
8};
9use wide::u64x8;
10
11use crate::{
12    constants::IP_INVO,
13    des::{compute_pc1, create_subkeys, encrypt},
14    des_optimized::{
15        compute_pc1_optimized, create_subkeys_optimized, encrypt_optimized, encrypt_simd,
16        encrypt_simd_avx, feistel_function_avx,
17    },
18};
19
20pub mod benchmark;
21mod constants;
22pub mod des;
23pub mod des_optimized;
24pub mod sbox_optimized;
25pub mod sbox_simd;
26mod utils;
27
28pub const ZERO: u64x8 = u64x8::ZERO;
29
30pub fn des(plaintext: u64, key: u64) -> u64 {
31    let (c0, d0) = compute_pc1(key);
32    let subkeys = create_subkeys(c0, d0);
33    let encrypted = encrypt(plaintext, subkeys);
34    encrypted
35}
36
37pub fn bitsliced_des_simd(plaintext: u64, keys: &[[u64; 64]; 8]) -> [[u64; 64]; 8] {
38    let mut k_slice = transpose(keys);
39
40    encrypt_simd(plaintext, &mut k_slice);
41    let ciphertext = transpose_back(&k_slice);
42    ciphertext
43}
44
45//inline expects keys that are already transposed
46pub fn bitsliced_des_inline_simd(plaintext: u64, keys: &mut [u64x8; 64]) {
47    encrypt_simd(plaintext, keys);
48}
49
50//expects plaintexts of 56bits, 8 MSB of all u64's are ignored
51pub fn bitsliced_netntlmv1_simd(plaintext: u64, keys: &[[u64; 64]; 8]) -> [[u64; 64]; 8] {
52    let transposed = transpose(keys);
53    let mut keys = convert_to_key::<u64x8>(&transposed, ALL_ONES);
54    bitsliced_des_inline_simd(plaintext, &mut keys);
55    let ciphertext = transpose_back(&keys);
56    ciphertext
57}
58
59//expects plaintexts of 56bits, 8 MSB of all u64's are ignored
60pub fn bitsliced_netntlmv1_inline_simd(plaintext: u64, keys: &mut [u64x8; 64]) {
61    *keys = convert_to_key::<u64x8>(&keys, ALL_ONES);
62    bitsliced_des_inline_simd(plaintext, keys);
63}
64
65#[target_feature(enable = "avx512f,avx512vl,avx512bw")]
66pub unsafe fn bitsliced_des_simd_avx(keys: &[[u64; 64]; 8]) -> [[u64; 64]; 8] {
67    unsafe {
68        let mut k_slice = transpose_avx(keys);
69        encrypt_simd_avx(&mut k_slice);
70        transpose_back_avx(&k_slice)
71    }
72}
73
74#[target_feature(enable = "avx512f,avx512vl,avx512bw")]
75pub unsafe fn bitsliced_des_inline_simd_avx(keys: &mut [__m512i; 64]) {
76    encrypt_simd_avx(keys);
77}
78
79#[target_feature(enable = "avx512f,avx512vl,avx512bw")]
80pub unsafe fn bitsliced_netntlmv1_inline_simd_avx(keys: &mut [__m512i; 64]) {
81    *keys = convert_to_key::<__m512i>(&keys, _mm512_set1_epi64(-1i64));
82    bitsliced_des_inline_simd_avx(keys);
83}
84
85#[target_feature(enable = "avx512f,avx512vl,avx512bw")]
86pub unsafe fn bitsliced_netntlmv1_simd_avx(keys: &[[u64; 64]; 8]) -> [[u64; 64]; 8] {
87    unsafe {
88        let k_slice = transpose_avx(keys);
89        let mut converted = convert_to_key::<__m512i>(&k_slice, _mm512_set1_epi64(-1i64));
90        encrypt_simd_avx(&mut converted);
91        transpose_back_avx(&converted)
92    }
93}
94
95const DES_KEY_PERM: [i8; 64] = [
96    0, 1, 2, 3, 4, 5, 6, -1, 7, 8, 9, 10, 11, 12, 13, -1, 14, 15, 16, 17, 18, 19, 20, -1, 21, 22,
97    23, 24, 25, 26, 27, -1, 28, 29, 30, 31, 32, 33, 34, -1, 35, 36, 37, 38, 39, 40, 41, -1, 42, 43,
98    44, 45, 46, 47, 48, -1, 49, 50, 51, 52, 53, 54, 55, -1,
99];
100
101//verbose for better performance
102fn convert_to_key<T: Copy>(plaintexts: &[T; 64], all_ones: T) -> [T; 64] {
103    [
104        plaintexts[8],
105        plaintexts[9],
106        plaintexts[10],
107        plaintexts[11],
108        plaintexts[12],
109        plaintexts[13],
110        plaintexts[14],
111        all_ones,
112        plaintexts[15],
113        plaintexts[16],
114        plaintexts[17],
115        plaintexts[18],
116        plaintexts[19],
117        plaintexts[20],
118        plaintexts[21],
119        all_ones,
120        plaintexts[22],
121        plaintexts[23],
122        plaintexts[24],
123        plaintexts[25],
124        plaintexts[26],
125        plaintexts[27],
126        plaintexts[28],
127        all_ones,
128        plaintexts[29],
129        plaintexts[30],
130        plaintexts[31],
131        plaintexts[32],
132        plaintexts[33],
133        plaintexts[34],
134        plaintexts[35],
135        all_ones,
136        plaintexts[36],
137        plaintexts[37],
138        plaintexts[38],
139        plaintexts[39],
140        plaintexts[40],
141        plaintexts[41],
142        plaintexts[42],
143        all_ones,
144        plaintexts[43],
145        plaintexts[44],
146        plaintexts[45],
147        plaintexts[46],
148        plaintexts[47],
149        plaintexts[48],
150        plaintexts[49],
151        all_ones,
152        plaintexts[50],
153        plaintexts[51],
154        plaintexts[52],
155        plaintexts[53],
156        plaintexts[54],
157        plaintexts[55],
158        plaintexts[56],
159        all_ones,
160        plaintexts[57],
161        plaintexts[58],
162        plaintexts[59],
163        plaintexts[60],
164        plaintexts[61],
165        plaintexts[62],
166        plaintexts[63],
167        all_ones,
168    ]
169}
170
171//TODO: optimize AVX transpose
172unsafe fn transpose_avx(blocks: &[[u64; 64]; 8]) -> [__m512i; 64] {
173    let mut outputs = [_mm512_setzero_si512(); 64];
174    let mut blocks_transposed = [[0u64; 64]; 8];
175
176    // 1. Transpose each block individually using your existing function
177    for b in 0..8 {
178        blocks_transposed[b] = bitsliced_op::transpose_64x64(&blocks[b]);
179    }
180    for bit_idx in 0..64 {
181        outputs[bit_idx] = _mm512_set_epi64(
182            blocks_transposed[7][bit_idx] as i64,
183            blocks_transposed[6][bit_idx] as i64,
184            blocks_transposed[5][bit_idx] as i64,
185            blocks_transposed[4][bit_idx] as i64,
186            blocks_transposed[3][bit_idx] as i64,
187            blocks_transposed[2][bit_idx] as i64,
188            blocks_transposed[1][bit_idx] as i64,
189            blocks_transposed[0][bit_idx] as i64,
190        );
191    }
192
193    outputs
194}
195
196unsafe fn transpose_back_avx(outputs_simd: &[__m512i; 64]) -> [[u64; 64]; 8] {
197    let mut blocks_transposed = [[0u64; 64]; 8];
198    let mut final_blocks = [[0u64; 64]; 8];
199
200    // 1. Extract the lanes back into 8 intermediate blocks
201    // Each register contains Bit i for all 8 blocks.
202    for bit_idx in 0..64 {
203        let reg = outputs_simd[bit_idx];
204
205        // Use store to get the data out of the SIMD register into a temporary buffer
206        let mut lanes = [0u64; 8];
207        _mm512_storeu_si512(lanes.as_mut_ptr() as *mut _, reg);
208
209        // Map lanes back to their respective blocks
210        // Note: set_epi64(7, 6, 5, 4, 3, 2, 1, 0) stores as [0, 1, 2, 3, 4, 5, 6, 7] in memory
211        for b in 0..8 {
212            blocks_transposed[b][bit_idx] = lanes[b];
213        }
214    }
215
216    // 2. Transpose each block one last time to return to original row-major format
217    for b in 0..8 {
218        final_blocks[b] = bitsliced_op::transpose_64x64(&blocks_transposed[b]);
219    }
220
221    final_blocks
222}
223
224fn transpose(input: &[[u64; 64]; 8]) -> [u64x8; 64] {
225    let mut out: [u64x8; 64] = [u64x8::ZERO; 64];
226    for k in 0..8 {
227        let tmp = input[k];
228        let tmp = transpose_64x64(&tmp);
229
230        for r in 0..64 {
231            out[r].as_mut_array()[k] = tmp[r];
232        }
233    }
234    out
235}
236
237fn transpose_back(input: &[u64x8; 64]) -> [[u64; 64]; 8] {
238    let mut out = [[0u64; 64]; 8];
239
240    for j in 0..64 {
241        let tmp = input[j].as_array();
242        out[0][j] = tmp[0];
243        out[1][j] = tmp[1];
244        out[2][j] = tmp[2];
245        out[3][j] = tmp[3];
246        out[4][j] = tmp[4];
247        out[5][j] = tmp[5];
248        out[6][j] = tmp[6];
249        out[7][j] = tmp[7];
250    }
251    for k in 0..8 {
252        let tmp = out[k];
253        out[k] = transpose_64x64(&tmp);
254    }
255
256    out
257}
258
259#[cfg(test)]
260mod tests {
261
262    use std::arch::x86_64::{_mm512_set1_epi64, _mm512_setzero_si512};
263
264    // Note this useful idiom: importing names from outer (for mod tests) scope.
265    use super::*;
266
267    #[test]
268    fn test_encrypt_works_correctly() {
269        //test data taken from https://page.math.tu-berlin.de/~kant/teaching/hess/krypto-ws2006/des.htm
270        let ciphertext = des(0x0123456789ABCDEF, 0x133457799BBCDFF1);
271        assert_eq!(ciphertext, 0x85E813540F0AB405);
272    }
273
274    #[test]
275    fn test_encrypt_optimized_works_correctly() {
276        //test data taken from https://page.math.tu-berlin.de/~kant/teaching/hess/krypto-ws2006/des.htm
277        let k = 0x133457799BBCDFF1u64;
278        let mut keys = [[k; 64]; 8];
279        let ciphertexts = bitsliced_des_simd(0x0123456789ABCDEF, &mut keys);
280        assert_eq!(ciphertexts[0][0], 0x85E813540F0AB405);
281        assert_eq!(ciphertexts[0][63], 0x85E813540F0AB405);
282    }
283
284    #[test]
285    fn test_encrypt_optimized_inline_works_correctly() {
286        //test data taken from https://page.math.tu-berlin.de/~kant/teaching/hess/krypto-ws2006/des.htm
287        let k = 0x133457799BBCDFF1u64;
288        let mut keys = [[k; 64]; 8];
289        let mut transposed = transpose(&keys);
290        bitsliced_des_inline_simd(0x0123456789ABCDEF, &mut transposed);
291        let ciphertexts = transpose_back(&transposed);
292        assert_eq!(ciphertexts[0][0], 0x85E813540F0AB405);
293        assert_eq!(ciphertexts[0][63], 0x85E813540F0AB405);
294    }
295
296    #[test]
297    fn test_encrypt_optimized_inline_avx_works_correctly() {
298        unsafe {
299            let k = 0x8923BDFDAF753F63u64;
300            let keys = [k; 64];
301            let transposed = transpose_64x64(&keys);
302            let mut keys = [_mm512_setzero_si512(); 64];
303            for (i, t) in transposed.iter().enumerate() {
304                if *t != 0 {
305                    keys[i] = _mm512_set1_epi64(-1);
306                }
307            }
308            bitsliced_des_inline_simd_avx(&mut keys);
309            let mut output = Box::new([0u64; 64]);
310            for (i, k) in keys.iter().enumerate() {
311                let lanes: [u64; 8] = transmute(*k);
312                let first = lanes[0];
313                output[i] = first;
314            }
315            let ciphertexts = transpose_64x64(&output);
316            assert_eq!(ciphertexts[0], 0x727B4E35F947129E);
317            assert_eq!(ciphertexts[0], 0x727B4E35F947129E);
318        }
319    }
320
321    #[test]
322    fn test_encrypt_optimized_avx_works_correctly() {
323        unsafe {
324            let k = 0x8923BDFDAF753F63u64;
325            let keys = [[k; 64]; 8];
326
327            let output = bitsliced_des_simd_avx(&keys);
328            assert_eq!(output[0][0], 0x727B4E35F947129E);
329            assert_eq!(output[7][63], 0x727B4E35F947129E);
330        }
331    }
332
333    #[test]
334    fn test_netntlmv1_encrypt_works_correctly() {
335        let k = 0x8846F7EAEE8FB1u64;
336        let keys = [[k; 64]; 8];
337        let ciphertexts = bitsliced_netntlmv1_simd(0x1122334455667788, &keys);
338        assert_eq!(ciphertexts[0][0], 0x727B4E35F947129E);
339        assert_eq!(ciphertexts[0][63], 0x727B4E35F947129E);
340    }
341
342    #[test]
343    fn test_netntlmv1_inline_encrypt_works_correctly() {
344        let k = 0x8846F7EAEE8FB1u64;
345        let keys = [[k; 64]; 8];
346        let mut transposed = transpose(&keys);
347        bitsliced_netntlmv1_inline_simd(0x1122334455667788, &mut transposed);
348        let ciphertexts = transpose_back(&transposed);
349        assert_eq!(ciphertexts[0][0], 0x727B4E35F947129E);
350        assert_eq!(ciphertexts[0][63], 0x727B4E35F947129E);
351    }
352
353    #[test]
354    fn test_netntlmv1_encrypt_avx_works_correctly() {
355        unsafe {
356            let k = 0x8846F7EAEE8FB1u64;
357            let keys = [[k; 64]; 8];
358            let ciphertexts = bitsliced_netntlmv1_simd_avx(&keys);
359            assert_eq!(ciphertexts[0][0], 0x727B4E35F947129E);
360            assert_eq!(ciphertexts[0][63], 0x727B4E35F947129E);
361        }
362    }
363
364    #[test]
365    fn test_netntlmv1_inline_encrypt_avx_works_correctly() {
366        unsafe {
367            let k = 0x8846F7EAEE8FB1u64;
368            let keys = [[k; 64]; 8];
369            let mut transposed = transpose_avx(&keys);
370            bitsliced_netntlmv1_inline_simd_avx(&mut transposed);
371            let ciphertexts = transpose_back_avx(&transposed);
372            assert_eq!(ciphertexts[0][0], 0x727B4E35F947129E);
373            assert_eq!(ciphertexts[7][63], 0x727B4E35F947129E);
374        }
375    }
376}