1use buffer::{BufferResult, RefReadBuffer, RefWriteBuffer};
8use symmetriccipher::{Encryptor, Decryptor, SynchronousStreamCipher, SymmetricCipherError};
9use cryptoutil::{read_u32_le, symm_enc_or_dec, write_u32_le, xor_keystream};
10use simd::u32x4;
11
12use std::cmp;
13
14#[derive(Clone, Copy)]
15struct SalsaState {
16 a: u32x4,
17 b: u32x4,
18 c: u32x4,
19 d: u32x4
20}
21
22#[derive(Copy)]
23pub struct Salsa20 {
24 state: SalsaState,
25 output: [u8; 64],
26 offset: usize,
27}
28
29impl Clone for Salsa20 { fn clone(&self) -> Salsa20 { *self } }
30
31const S7:u32x4 = u32x4(7, 7, 7, 7);
32const S9:u32x4 = u32x4(9, 9, 9, 9);
33const S13:u32x4 = u32x4(13, 13, 13, 13);
34const S18:u32x4 = u32x4(18, 18, 18, 18);
35const S32:u32x4 = u32x4(32, 32, 32, 32);
36
37macro_rules! prepare_rowround {
38 ($a: expr, $b: expr, $c: expr) => {{
39 let u32x4(a10, a11, a12, a13) = $a;
40 $a = u32x4(a13, a10, a11, a12);
41 let u32x4(b10, b11, b12, b13) = $b;
42 $b = u32x4(b12, b13, b10, b11);
43 let u32x4(c10, c11, c12, c13) = $c;
44 $c = u32x4(c11, c12, c13, c10);
45 }}
46}
47
48macro_rules! prepare_columnround {
49 ($a: expr, $b: expr, $c: expr) => {{
50 let u32x4(a13, a10, a11, a12) = $a;
51 $a = u32x4(a10, a11, a12, a13);
52 let u32x4(b12, b13, b10, b11) = $b;
53 $b = u32x4(b10, b11, b12, b13);
54 let u32x4(c11, c12, c13, c10) = $c;
55 $c = u32x4(c10, c11, c12, c13);
56 }}
57}
58
59macro_rules! add_rotate_xor {
60 ($dst: expr, $a: expr, $b: expr, $shift: expr) => {{
61 let v = $a + $b;
62 let r = S32 - $shift;
63 let right = v >> r;
64 $dst = $dst ^ (v << $shift) ^ right
65 }}
66}
67
68fn columnround(state: &mut SalsaState) -> () {
69 add_rotate_xor!(state.a, state.d, state.c, S7);
70 add_rotate_xor!(state.b, state.a, state.d, S9);
71 add_rotate_xor!(state.c, state.b, state.a, S13);
72 add_rotate_xor!(state.d, state.c, state.b, S18);
73}
74
75fn rowround(state: &mut SalsaState) -> () {
76 add_rotate_xor!(state.c, state.d, state.a, S7);
77 add_rotate_xor!(state.b, state.c, state.d, S9);
78 add_rotate_xor!(state.a, state.c, state.b, S13);
79 add_rotate_xor!(state.d, state.a, state.b, S18);
80}
81
82impl Salsa20 {
83 pub fn new(key: &[u8], nonce: &[u8]) -> Salsa20 {
84 assert!(key.len() == 16 || key.len() == 32);
85 assert!(nonce.len() == 8);
86 Salsa20 { state: Salsa20::expand(key, nonce), output: [0; 64], offset: 64 }
87 }
88
89 pub fn new_xsalsa20(key: &[u8], nonce: &[u8]) -> Salsa20 {
90 assert!(key.len() == 32);
91 assert!(nonce.len() == 24);
92 let mut xsalsa20 = Salsa20 { state: Salsa20::expand(key, &nonce[0..16]), output: [0; 64], offset: 64 };
93
94 let mut new_key = [0; 32];
95 xsalsa20.hsalsa20_hash(&mut new_key);
96 xsalsa20.state = Salsa20::expand(&new_key, &nonce[16..24]);
97
98 xsalsa20
99 }
100
101 fn expand(key: &[u8], nonce: &[u8]) -> SalsaState {
102 let constant = match key.len() {
103 16 => b"expand 16-byte k",
104 32 => b"expand 32-byte k",
105 _ => unreachable!(),
106 };
107
108 let key_tail; if key.len() == 16 {
117 key_tail = key;
118 } else {
119 key_tail = &key[16..32];
120 }
121
122 let x8; let x9; if nonce.len() == 16 {
124 x8 = read_u32_le(&nonce[8..12]);
126 x9 = read_u32_le(&nonce[12..16]);
127 } else {
128 x8 = 0;
129 x9 = 0;
130 }
131
132 SalsaState {
133 a: u32x4(
134 read_u32_le(&key[12..16]), x9, read_u32_le(&key_tail[12..16]), read_u32_le(&key[8..12]), ),
139 b: u32x4(
140 x8, read_u32_le(&key_tail[8..12]), read_u32_le(&key[4..8]), read_u32_le(&nonce[4..8]) ),
145 c: u32x4(
146 read_u32_le(&key_tail[4..8]), read_u32_le(&key[0..4]), read_u32_le(&nonce[0..4]), read_u32_le(&key_tail[0..4]) ),
151 d: u32x4(
152 read_u32_le(&constant[0..4]), read_u32_le(&constant[4..8]), read_u32_le(&constant[8..12]), read_u32_le(&constant[12..16]), )
157 }
158 }
159
160 fn hash(&mut self) {
161 let mut state = self.state;
162 for _ in 0..10 {
163 columnround(&mut state);
164 prepare_rowround!(state.a, state.b, state.c);
165 rowround(&mut state);
166 prepare_columnround!(state.a, state.b, state.c);
167 }
168 let u32x4(x4, x9, x14, x3) = self.state.a + state.a;
169 let u32x4(x8, x13, x2, x7) = self.state.b + state.b;
170 let u32x4(x12, x1, x6, x11) = self.state.c + state.c;
171 let u32x4(x0, x5, x10, x15) = self.state.d + state.d;
172 let lens = [
173 x0, x1, x2, x3,
174 x4, x5, x6, x7,
175 x8, x9, x10, x11,
176 x12, x13, x14, x15
177 ];
178 for i in 0..lens.len() {
179 write_u32_le(&mut self.output[i*4..(i+1)*4], lens[i]);
180 }
181
182 self.state.b = self.state.b + u32x4(1, 0, 0, 0);
183 let u32x4(_, _, _, ctr_lo) = self.state.b;
184 if ctr_lo == 0 {
185 self.state.a = self.state.a + u32x4(0, 1, 0, 0);
186 }
187
188 self.offset = 0;
189 }
190
191 fn hsalsa20_hash(&mut self, out: &mut [u8]) {
192 let mut state = self.state;
193 for _ in 0..10 {
194 columnround(&mut state);
195 prepare_rowround!(state.a, state.b, state.c);
196 rowround(&mut state);
197 prepare_columnround!(state.a, state.b, state.c);
198 }
199 let u32x4(_, x9, _, _) = state.a;
200 let u32x4(x8, _, _, x7) = state.b;
201 let u32x4(_, _, x6, _) = state.c;
202 let u32x4(x0, x5, x10, x15) = state.d;
203 let lens = [
204 x0, x5, x10, x15,
205 x6, x7, x8, x9
206 ];
207 for i in 0..lens.len() {
208 write_u32_le(&mut out[i*4..(i+1)*4], lens[i]);
209 }
210 }
211}
212
213impl SynchronousStreamCipher for Salsa20 {
214 fn process(&mut self, input: &[u8], output: &mut [u8]) {
215 assert!(input.len() == output.len());
216 let len = input.len();
217 let mut i = 0;
218 while i < len {
219 if self.offset == 64 {
222 self.hash();
223 }
224
225 let count = cmp::min(64 - self.offset, len - i);
227 xor_keystream(&mut output[i..i+count], &input[i..i+count], &self.output[self.offset..]);
228 i += count;
229 self.offset += count;
230 }
231 }
232}
233
234impl Encryptor for Salsa20 {
235 fn encrypt(&mut self, input: &mut RefReadBuffer, output: &mut RefWriteBuffer, _: bool)
236 -> Result<BufferResult, SymmetricCipherError> {
237 symm_enc_or_dec(self, input, output)
238 }
239}
240
241impl Decryptor for Salsa20 {
242 fn decrypt(&mut self, input: &mut RefReadBuffer, output: &mut RefWriteBuffer, _: bool)
243 -> Result<BufferResult, SymmetricCipherError> {
244 symm_enc_or_dec(self, input, output)
245 }
246}
247
248pub fn hsalsa20(key: &[u8], nonce: &[u8], out: &mut [u8]) {
249 assert!(key.len() == 32);
250 assert!(nonce.len() == 16);
251 let mut h = Salsa20 { state: Salsa20::expand(key, nonce), output: [0; 64], offset: 64 };
252 h.hsalsa20_hash(out);
253}
254
255#[cfg(test)]
256mod test {
257 use std::iter::repeat;
258
259 use salsa20::Salsa20;
260 use symmetriccipher::SynchronousStreamCipher;
261
262 use digest::Digest;
263 use sha2::Sha256;
264
265 #[test]
266 fn test_salsa20_128bit_ecrypt_set_1_vector_0() {
267 let key = [128u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
268 let nonce = [0u8; 8];
269 let input = [0u8; 64];
270 let mut stream = [0u8; 64];
271 let result =
272 [0x4D, 0xFA, 0x5E, 0x48, 0x1D, 0xA2, 0x3E, 0xA0,
273 0x9A, 0x31, 0x02, 0x20, 0x50, 0x85, 0x99, 0x36,
274 0xDA, 0x52, 0xFC, 0xEE, 0x21, 0x80, 0x05, 0x16,
275 0x4F, 0x26, 0x7C, 0xB6, 0x5F, 0x5C, 0xFD, 0x7F,
276 0x2B, 0x4F, 0x97, 0xE0, 0xFF, 0x16, 0x92, 0x4A,
277 0x52, 0xDF, 0x26, 0x95, 0x15, 0x11, 0x0A, 0x07,
278 0xF9, 0xE4, 0x60, 0xBC, 0x65, 0xEF, 0x95, 0xDA,
279 0x58, 0xF7, 0x40, 0xB7, 0xD1, 0xDB, 0xB0, 0xAA];
280
281 let mut salsa20 = Salsa20::new(&key, &nonce);
282 salsa20.process(&input, &mut stream);
283 assert!(stream[..] == result[..]);
284 }
285
286 #[test]
287 fn test_salsa20_256bit_ecrypt_set_1_vector_0() {
288 let key =
289 [128u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
290 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
291 let nonce = [0u8; 8];
292 let input = [0u8; 64];
293 let mut stream = [0u8; 64];
294 let result =
295 [0xE3, 0xBE, 0x8F, 0xDD, 0x8B, 0xEC, 0xA2, 0xE3,
296 0xEA, 0x8E, 0xF9, 0x47, 0x5B, 0x29, 0xA6, 0xE7,
297 0x00, 0x39, 0x51, 0xE1, 0x09, 0x7A, 0x5C, 0x38,
298 0xD2, 0x3B, 0x7A, 0x5F, 0xAD, 0x9F, 0x68, 0x44,
299 0xB2, 0x2C, 0x97, 0x55, 0x9E, 0x27, 0x23, 0xC7,
300 0xCB, 0xBD, 0x3F, 0xE4, 0xFC, 0x8D, 0x9A, 0x07,
301 0x44, 0x65, 0x2A, 0x83, 0xE7, 0x2A, 0x9C, 0x46,
302 0x18, 0x76, 0xAF, 0x4D, 0x7E, 0xF1, 0xA1, 0x17];
303
304 let mut salsa20 = Salsa20::new(&key, &nonce);
305 salsa20.process(&input, &mut stream);
306 assert!(stream[..] == result[..]);
307 }
308
309 #[test]
310 fn test_salsa20_256bit_nacl_vector_2() {
311 let key = [
312 0xdc,0x90,0x8d,0xda,0x0b,0x93,0x44,0xa9,
313 0x53,0x62,0x9b,0x73,0x38,0x20,0x77,0x88,
314 0x80,0xf3,0xce,0xb4,0x21,0xbb,0x61,0xb9,
315 0x1c,0xbd,0x4c,0x3e,0x66,0x25,0x6c,0xe4
316 ];
317 let nonce = [
318 0x82,0x19,0xe0,0x03,0x6b,0x7a,0x0b,0x37
319 ];
320 let input: Vec<u8> = repeat(0).take(4194304).collect();
321 let mut stream: Vec<u8> = repeat(0).take(input.len()).collect();
322 let output_str = "662b9d0e3463029156069b12f918691a98f7dfb2ca0393c96bbfc6b1fbd630a2";
323
324 let mut salsa20 = Salsa20::new(&key, &nonce);
325 salsa20.process(input.as_ref(), &mut stream);
326
327 let mut sh = Sha256::new();
328 sh.input(stream.as_ref());
329 let out_str = sh.result_str();
330 assert!(&out_str[..] == output_str);
331 }
332
333 #[test]
334 fn test_xsalsa20_cryptopp() {
335 let key =
336 [0x1b, 0x27, 0x55, 0x64, 0x73, 0xe9, 0x85, 0xd4,
337 0x62, 0xcd, 0x51, 0x19, 0x7a, 0x9a, 0x46, 0xc7,
338 0x60, 0x09, 0x54, 0x9e, 0xac, 0x64, 0x74, 0xf2,
339 0x06, 0xc4, 0xee, 0x08, 0x44, 0xf6, 0x83, 0x89];
340 let nonce =
341 [0x69, 0x69, 0x6e, 0xe9, 0x55, 0xb6, 0x2b, 0x73,
342 0xcd, 0x62, 0xbd, 0xa8, 0x75, 0xfc, 0x73, 0xd6,
343 0x82, 0x19, 0xe0, 0x03, 0x6b, 0x7a, 0x0b, 0x37];
344 let input = [0u8; 139];
345 let mut stream = [0u8; 139];
346 let result =
347 [0xee, 0xa6, 0xa7, 0x25, 0x1c, 0x1e, 0x72, 0x91,
348 0x6d, 0x11, 0xc2, 0xcb, 0x21, 0x4d, 0x3c, 0x25,
349 0x25, 0x39, 0x12, 0x1d, 0x8e, 0x23, 0x4e, 0x65,
350 0x2d, 0x65, 0x1f, 0xa4, 0xc8, 0xcf, 0xf8, 0x80,
351 0x30, 0x9e, 0x64, 0x5a, 0x74, 0xe9, 0xe0, 0xa6,
352 0x0d, 0x82, 0x43, 0xac, 0xd9, 0x17, 0x7a, 0xb5,
353 0x1a, 0x1b, 0xeb, 0x8d, 0x5a, 0x2f, 0x5d, 0x70,
354 0x0c, 0x09, 0x3c, 0x5e, 0x55, 0x85, 0x57, 0x96,
355 0x25, 0x33, 0x7b, 0xd3, 0xab, 0x61, 0x9d, 0x61,
356 0x57, 0x60, 0xd8, 0xc5, 0xb2, 0x24, 0xa8, 0x5b,
357 0x1d, 0x0e, 0xfe, 0x0e, 0xb8, 0xa7, 0xee, 0x16,
358 0x3a, 0xbb, 0x03, 0x76, 0x52, 0x9f, 0xcc, 0x09,
359 0xba, 0xb5, 0x06, 0xc6, 0x18, 0xe1, 0x3c, 0xe7,
360 0x77, 0xd8, 0x2c, 0x3a, 0xe9, 0xd1, 0xa6, 0xf9,
361 0x72, 0xd4, 0x16, 0x02, 0x87, 0xcb, 0xfe, 0x60,
362 0xbf, 0x21, 0x30, 0xfc, 0x0a, 0x6f, 0xf6, 0x04,
363 0x9d, 0x0a, 0x5c, 0x8a, 0x82, 0xf4, 0x29, 0x23,
364 0x1f, 0x00, 0x80];
365
366 let mut xsalsa20 = Salsa20::new_xsalsa20(&key, &nonce);
367 xsalsa20.process(&input, &mut stream);
368 assert!(stream[..] == result[..]);
369 }
370}
371
372#[cfg(all(test, feature = "with-bench"))]
373mod bench {
374 use test::Bencher;
375 use symmetriccipher::SynchronousStreamCipher;
376 use salsa20::Salsa20;
377
378 #[bench]
379 pub fn salsa20_10(bh: & mut Bencher) {
380 let mut salsa20 = Salsa20::new(&[0; 32], &[0; 8]);
381 let input = [1u8; 10];
382 let mut output = [0u8; 10];
383 bh.iter( || {
384 salsa20.process(&input, &mut output);
385 });
386 bh.bytes = input.len() as u64;
387 }
388
389 #[bench]
390 pub fn salsa20_1k(bh: & mut Bencher) {
391 let mut salsa20 = Salsa20::new(&[0; 32], &[0; 8]);
392 let input = [1u8; 1024];
393 let mut output = [0u8; 1024];
394 bh.iter( || {
395 salsa20.process(&input, &mut output);
396 });
397 bh.bytes = input.len() as u64;
398 }
399
400 #[bench]
401 pub fn salsa20_64k(bh: & mut Bencher) {
402 let mut salsa20 = Salsa20::new(&[0; 32], &[0; 8]);
403 let input = [1u8; 65536];
404 let mut output = [0u8; 65536];
405 bh.iter( || {
406 salsa20.process(&input, &mut output);
407 });
408 bh.bytes = input.len() as u64;
409 }
410}