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