crypto/
aesni.rs

1// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
2// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
3// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
4// option. This file may not be copied, modified, or distributed
5// except according to those terms.
6
7use aes::KeySize;
8use aes::KeySize::{KeySize128, KeySize192, KeySize256};
9use symmetriccipher::{BlockEncryptor, BlockDecryptor};
10use util::supports_aesni;
11
12#[derive(Copy)]
13pub struct AesNiEncryptor {
14    rounds: u8,
15    round_keys: [u8; 240]
16}
17
18impl Clone for AesNiEncryptor { fn clone(&self) -> AesNiEncryptor { *self } }
19
20#[derive(Copy)]
21pub struct AesNiDecryptor {
22    rounds: u8,
23    round_keys: [u8; 240]
24}
25
26impl Clone for AesNiDecryptor { fn clone(&self) -> AesNiDecryptor { *self } }
27
28/// The number of rounds as well as a function to setup an appropriately sized key.
29type RoundSetupInfo = (u8, fn(&[u8], KeyType, &mut [u8]));
30
31impl AesNiEncryptor {
32    pub fn new(key_size: KeySize, key: &[u8]) -> AesNiEncryptor {
33        if !supports_aesni() {
34            panic!("AES-NI not supported on this architecture. If you are \
35                using the MSVC toolchain, this is because the AES-NI method's \
36                have not been ported, yet");
37        }
38        let (rounds, setup_function): RoundSetupInfo = match key_size {
39            KeySize128 => (10, setup_working_key_aesni_128),
40            KeySize192 => (12, setup_working_key_aesni_192),
41            KeySize256 => (14, setup_working_key_aesni_256)
42        };
43        let mut e = AesNiEncryptor {
44            rounds: rounds,
45            round_keys: [0u8; 240]
46        };
47        setup_function(key, KeyType::Encryption, &mut e.round_keys[0..size(e.rounds)]);
48        e
49    }
50}
51
52impl AesNiDecryptor {
53    pub fn new(key_size: KeySize, key: &[u8]) -> AesNiDecryptor {
54        if !supports_aesni() {
55            panic!("AES-NI not supported on this architecture. If you are \
56                using the MSVC toolchain, this is because the AES-NI method's \
57                have not been ported, yet");
58        }
59        let (rounds, setup_function): RoundSetupInfo = match key_size {
60            KeySize128 => (10, setup_working_key_aesni_128),
61            KeySize192 => (12, setup_working_key_aesni_192),
62            KeySize256 => (14, setup_working_key_aesni_256)
63        };
64        let mut d = AesNiDecryptor {
65            rounds: rounds,
66            round_keys: [0u8; 240]
67        };
68        setup_function(key, KeyType::Decryption, &mut d.round_keys[0..size(d.rounds)]);
69        d
70    }
71
72}
73
74impl BlockEncryptor for AesNiEncryptor {
75    fn block_size(&self) -> usize { 16 }
76    fn encrypt_block(&self, input: &[u8], output: &mut [u8]) {
77        encrypt_block_aesni(self.rounds, input, &self.round_keys[0..size(self.rounds)], output);
78    }
79}
80
81impl BlockDecryptor for AesNiDecryptor {
82    fn block_size(&self) -> usize { 16 }
83    fn decrypt_block(&self, input: &[u8], output: &mut [u8]) {
84        decrypt_block_aesni(self.rounds, input, &self.round_keys[0..size(self.rounds)], output);
85    }
86}
87
88enum KeyType {
89    Encryption,
90    Decryption
91}
92
93#[inline(always)]
94fn size(rounds: u8) -> usize { 16 * ((rounds as usize) + 1) }
95
96extern {
97    fn rust_crypto_aesni_aesimc(round_keys: *mut u8);
98    fn rust_crypto_aesni_setup_working_key_128(key: *const u8, round_key: *mut u8);
99    fn rust_crypto_aesni_setup_working_key_192(key: *const u8, round_key: *mut u8);
100    fn rust_crypto_aesni_setup_working_key_256(key: *const u8, round_key: *mut u8);
101    fn rust_crypto_aesni_encrypt_block(
102            rounds: u8,
103            input: *const u8,
104            round_keys: *const u8,
105            output: *mut u8);
106    fn rust_crypto_aesni_decrypt_block(
107            rounds: u8,
108            input: *const u8,
109            round_keys: *const u8,
110            output: *mut u8);
111}
112
113fn setup_working_key_aesni_128(key: &[u8], key_type: KeyType, round_key: &mut [u8]) {
114    unsafe {
115        rust_crypto_aesni_setup_working_key_128(key.as_ptr(), round_key.as_mut_ptr());
116
117        match key_type {
118            KeyType::Decryption => {
119                // range of rounds keys from #1 to #9; skip the first and last key
120                for i in 1..10 {
121                    rust_crypto_aesni_aesimc(round_key.get_unchecked_mut(16 * i));
122                }
123            }
124            KeyType::Encryption => { /* nothing more to do */ }
125        }
126    }
127}
128
129fn setup_working_key_aesni_192(key: &[u8], key_type: KeyType, round_key: &mut [u8]) {
130    unsafe {
131        rust_crypto_aesni_setup_working_key_192(key.as_ptr(), round_key.as_mut_ptr());
132
133        match key_type {
134            KeyType::Decryption => {
135                // range of rounds keys from #1 to #11; skip the first and last key
136                for i in 1..12 {
137                    rust_crypto_aesni_aesimc(round_key.get_unchecked_mut(16 * i));
138                }
139            }
140            KeyType::Encryption => { /* nothing more to do */ }
141        }
142    }
143}
144
145fn setup_working_key_aesni_256(key: &[u8], key_type: KeyType, round_key: &mut [u8]) {
146    unsafe {
147        rust_crypto_aesni_setup_working_key_256(key.as_ptr(), round_key.as_mut_ptr());
148
149        match key_type {
150            KeyType::Decryption => {
151                // range of rounds keys from #1 to #13; skip the first and last key
152                for i in 1..14 {
153                    rust_crypto_aesni_aesimc(round_key.get_unchecked_mut(16 * i));
154                }
155            }
156            KeyType::Encryption => { /* nothing more to do */ }
157        }
158    }
159}
160
161fn encrypt_block_aesni(rounds: u8, input: &[u8], round_keys: &[u8], output: &mut [u8]) {
162    unsafe {
163        rust_crypto_aesni_encrypt_block(
164                rounds,
165                input.as_ptr(),
166                round_keys.as_ptr(),
167                output.as_mut_ptr());
168    }
169}
170
171fn decrypt_block_aesni(rounds: u8, input: &[u8], round_keys: &[u8], output: &mut [u8]) {
172    unsafe {
173        rust_crypto_aesni_decrypt_block(
174                rounds as u8,
175                input.as_ptr(),
176                round_keys.get_unchecked(round_keys.len() - 16),
177                output.as_mut_ptr());
178    }
179}