Skip to content

Commit 7619697

Browse files
authored
Add salsa streaming cipher (TheAlgorithms#363)
1 parent 7fe1388 commit 7619697

File tree

6 files changed

+289
-3
lines changed

6 files changed

+289
-3
lines changed

DIRECTORY.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
* [Transposition](https://github.com/TheAlgorithms/Rust/blob/master/src/ciphers/transposition.rs)
1313
* [Vigenere](https://github.com/TheAlgorithms/Rust/blob/master/src/ciphers/vigenere.rs)
1414
* [Xor](https://github.com/TheAlgorithms/Rust/blob/master/src/ciphers/xor.rs)
15+
* [Salsa20](https://github.com/TheAlgorithms/Rust/blob/master/src/ciphers/salsa.rs)
16+
* [HMAC](https://github.com/TheAlgorithms/Rust/blob/master/src/ciphers/hashing_traits.rs)
1517
* Data Structures
1618
* [Avl Tree](https://github.com/TheAlgorithms/Rust/blob/master/src/data_structures/avl_tree.rs)
1719
* [B Tree](https://github.com/TheAlgorithms/Rust/blob/master/src/data_structures/b_tree.rs)

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,8 @@ These are for demonstration purposes only.
149149
- [x] [Transposition](./src/ciphers/transposition.rs)
150150
- [x] [Vigenère](./src/ciphers/vigenere.rs)
151151
- [x] [XOR](./src/ciphers/xor.rs)
152+
- [x] [Salsa20](./src/ciphers/salsa.rs)
153+
- [x] [HMAC](./src/ciphers/hashing_traits.rs)
152154
- Rot13
153155
- [x] [Another Rot13](./src/ciphers/another_rot13.rs)
154156
- [x] [Rot13](./src/ciphers/rot13.rs)

src/ciphers/chacha.rs

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
/*
2+
* ChaCha20 implementation based on RFC8439
3+
* ChaCha20 is a stream cipher developed independently by Daniel J. Bernstein.
4+
* To use it, the `chacha20` function should be called with appropriate
5+
* parameters and the output of the function should be XORed with plain text.
6+
*/
7+
8+
macro_rules! quarter_round {
9+
($a:expr,$b:expr,$c:expr,$d:expr) => {
10+
$a = $a.wrapping_add($b);
11+
$d = ($d ^ $a).rotate_left(16);
12+
$c = $c.wrapping_add($d);
13+
$b = ($b ^ $c).rotate_left(12);
14+
$a = $a.wrapping_add($b);
15+
$d = ($d ^ $a).rotate_left(8);
16+
$c = $c.wrapping_add($d);
17+
$b = ($b ^ $c).rotate_left(7);
18+
};
19+
}
20+
21+
#[allow(dead_code)]
22+
// "expand 32-byte k", written in little-endian order
23+
pub const C: [u32; 4] = [0x61707865, 0x3320646e, 0x79622d32, 0x6b206574];
24+
25+
/**
26+
* `chacha20` function takes as input an array of 16 32-bit integers (512 bits)
27+
* of which 128 bits is the constant 'expand 32-byte k', 256 bits is the key,
28+
* and 128 bits are nonce and counter. According to RFC8439, the nonce should
29+
* be 96 bits long, which leaves 32 bits for the counter. Given that the block
30+
* length is 512 bits, this leaves enough counter values to encrypt 256GB of
31+
* data.
32+
*
33+
* The 16 input numbers can be thought of as the elements of a 4x4 matrix like
34+
* the one bellow, on which we do the main operations of the cipher.
35+
*
36+
* +----+----+----+----+
37+
* | 00 | 01 | 02 | 03 |
38+
* +----+----+----+----+
39+
* | 04 | 05 | 06 | 07 |
40+
* +----+----+----+----+
41+
* | 08 | 09 | 10 | 11 |
42+
* +----+----+----+----+
43+
* | 12 | 13 | 14 | 15 |
44+
* +----+----+----+----+
45+
*
46+
* As per the diagram bellow, input[0, 1, 2, 3] are the constants mentioned
47+
* above, input[4..=11] is filled with the key, and input[6..=9] should be
48+
* filled with nonce and counter values. The output of the function is stored
49+
* in `output` variable and can be XORed with the plain text to produce the
50+
* cipher text.
51+
*
52+
* +------+------+------+------+
53+
* | | | | |
54+
* | C[0] | C[1] | C[2] | C[3] |
55+
* | | | | |
56+
* +------+------+------+------+
57+
* | | | | |
58+
* | key0 | key1 | key2 | key3 |
59+
* | | | | |
60+
* +------+------+------+------+
61+
* | | | | |
62+
* | key4 | key5 | key6 | key7 |
63+
* | | | | |
64+
* +------+------+------+------+
65+
* | | | | |
66+
* | ctr0 | no.0 | no.1 | no.2 |
67+
* | | | | |
68+
* +------+------+------+------+
69+
*
70+
* Note that the constants, the key, and the nonce should be written in
71+
* little-endian order, meaning that for example if the key is 01:02:03:04
72+
* (in hex), it corresponds to the integer 0x04030201. It is important to
73+
* know that the hex value of the counter is meaningless, and only its integer
74+
* value matters, and it should start with (for example) 0x00000000, and then
75+
* 0x00000001 and so on until 0xffffffff. Keep in mind that as soon as we get
76+
* from bytes to words, we stop caring about their representation in memory,
77+
* and we only need the math to be correct.
78+
*
79+
* The output of the function can be used without any change, as long as the
80+
* plain text has the same endianness. For example if the plain text is
81+
* "hello world", and the first word of the output is 0x01020304, then the
82+
* first byte of plain text ('h') should be XORed with the least-significant
83+
* byte of 0x01020304, which is 0x04.
84+
*/
85+
pub fn chacha20(input: &[u32; 16], output: &mut [u32; 16]) {
86+
output.copy_from_slice(&input[..]);
87+
for _ in 0..10 {
88+
// Odd round (column round)
89+
quarter_round!(output[0], output[4], output[8], output[12]); // column 1
90+
quarter_round!(output[1], output[5], output[9], output[13]); // column 2
91+
quarter_round!(output[2], output[6], output[10], output[14]); // column 3
92+
quarter_round!(output[3], output[7], output[11], output[15]); // column 4
93+
94+
// Even round (diagonal round)
95+
quarter_round!(output[0], output[5], output[10], output[15]); // diag 1
96+
quarter_round!(output[1], output[6], output[11], output[12]); // diag 2
97+
quarter_round!(output[2], output[7], output[8], output[13]); // diag 3
98+
quarter_round!(output[3], output[4], output[9], output[14]); // diag 4
99+
}
100+
for (a, &b) in output.iter_mut().zip(input.iter()) {
101+
*a = a.wrapping_add(b);
102+
}
103+
}
104+
105+
#[cfg(test)]
106+
mod tests {
107+
use super::*;
108+
use std::fmt::Write;
109+
110+
fn output_hex(inp: &[u32; 16]) -> String {
111+
let mut res = String::new();
112+
res.reserve(512 / 4);
113+
for &x in inp {
114+
write!(&mut res, "{x:08x}").unwrap();
115+
}
116+
res
117+
}
118+
119+
#[test]
120+
// test vector 1
121+
fn basic_tv1() {
122+
let mut inp = [0u32; 16];
123+
let mut out = [0u32; 16];
124+
inp[0] = C[0];
125+
inp[1] = C[1];
126+
inp[2] = C[2];
127+
inp[3] = C[3];
128+
inp[4] = 0x03020100; // The key is 00:01:02:..:1f (hex)
129+
inp[5] = 0x07060504;
130+
inp[6] = 0x0b0a0908;
131+
inp[7] = 0x0f0e0d0c;
132+
inp[8] = 0x13121110;
133+
inp[9] = 0x17161514;
134+
inp[10] = 0x1b1a1918;
135+
inp[11] = 0x1f1e1d1c;
136+
inp[12] = 0x00000001; // The value of counter is 1 (an integer). Nonce:
137+
inp[13] = 0x09000000; // 00:00:00:09
138+
inp[14] = 0x4a000000; // 00:00:00:4a
139+
inp[15] = 0x00000000; // 00:00:00:00
140+
chacha20(&inp, &mut out);
141+
assert_eq!(
142+
output_hex(&out),
143+
concat!(
144+
"e4e7f11015593bd11fdd0f50c47120a3c7f4d1c70368c0339aaa22044e6cd4c3",
145+
"466482d209aa9f0705d7c214a2028bd9d19c12b5b94e16dee883d0cb4e3c50a2"
146+
)
147+
);
148+
}
149+
}

src/ciphers/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
mod aes;
22
mod another_rot13;
33
mod caesar;
4+
mod chacha;
45
mod hashing_traits;
56
mod morse_code;
67
mod polybius;
78
mod rot13;
9+
mod salsa;
810
mod sha256;
911
mod tea;
1012
mod theoretical_rot13;
@@ -15,11 +17,13 @@ mod xor;
1517
pub use self::aes::{aes_decrypt, aes_encrypt, AesKey};
1618
pub use self::another_rot13::another_rot13;
1719
pub use self::caesar::caesar;
20+
pub use self::chacha::chacha20;
1821
pub use self::hashing_traits::Hasher;
1922
pub use self::hashing_traits::HMAC;
2023
pub use self::morse_code::{decode, encode};
2124
pub use self::polybius::{decode_ascii, encode_ascii};
2225
pub use self::rot13::rot13;
26+
pub use self::salsa::salsa20;
2327
pub use self::sha256::SHA256;
2428
pub use self::tea::{tea_decrypt, tea_encrypt};
2529
pub use self::theoretical_rot13::theoretical_rot13;

src/ciphers/salsa.rs

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
/*
2+
* Salsa20 implementation based on https://en.wikipedia.org/wiki/Salsa20
3+
* Salsa20 is a stream cipher developed by Daniel J. Bernstein. To use it, the
4+
* `salsa20` function should be called with appropriate parameters and the
5+
* output of the function should be XORed with plain text.
6+
*/
7+
8+
macro_rules! quarter_round {
9+
($v1:expr,$v2:expr,$v3:expr,$v4:expr) => {
10+
$v2 ^= ($v1.wrapping_add($v4).rotate_left(7));
11+
$v3 ^= ($v2.wrapping_add($v1).rotate_left(9));
12+
$v4 ^= ($v3.wrapping_add($v2).rotate_left(13));
13+
$v1 ^= ($v4.wrapping_add($v3).rotate_left(18));
14+
};
15+
}
16+
17+
#[allow(dead_code)]
18+
pub const C: [u32; 4] = [0x65787061, 0x6e642033, 0x322d6279, 0x7465206b];
19+
20+
/**
21+
* `salsa20` function takes as input an array of 16 32-bit integers (512 bits)
22+
* of which 128 bits is the constant 'expand 32-byte k', 256 bits is the key,
23+
* and 128 bits are nonce and counter. It is up to the user to determine how
24+
* many bits each of nonce and counter take, but a default of 64 bits each
25+
* seems to be a sane choice.
26+
*
27+
* The 16 input numbers can be thought of as the elements of a 4x4 matrix like
28+
* the one bellow, on which we do the main operations of the cipher.
29+
*
30+
* +----+----+----+----+
31+
* | 00 | 01 | 02 | 03 |
32+
* +----+----+----+----+
33+
* | 04 | 05 | 06 | 07 |
34+
* +----+----+----+----+
35+
* | 08 | 09 | 10 | 11 |
36+
* +----+----+----+----+
37+
* | 12 | 13 | 14 | 15 |
38+
* +----+----+----+----+
39+
*
40+
* As per the diagram bellow, input[0, 5, 10, 15] are the constants mentioned
41+
* above, input[1, 2, 3, 4, 11, 12, 13, 14] is filled with the key, and
42+
* input[6, 7, 8, 9] should be filled with nonce and counter values. The output
43+
* of the function is stored in `output` variable and can be XORed with the
44+
* plain text to produce the cipher text.
45+
*
46+
* +------+------+------+------+
47+
* | | | | |
48+
* | C[0] | key1 | key2 | key3 |
49+
* | | | | |
50+
* +------+------+------+------+
51+
* | | | | |
52+
* | key4 | C[1] | no1 | no2 |
53+
* | | | | |
54+
* +------+------+------+------+
55+
* | | | | |
56+
* | ctr1 | ctr2 | C[2] | key5 |
57+
* | | | | |
58+
* +------+------+------+------+
59+
* | | | | |
60+
* | key6 | key7 | key8 | C[3] |
61+
* | | | | |
62+
* +------+------+------+------+
63+
*/
64+
pub fn salsa20(input: &[u32; 16], output: &mut [u32; 16]) {
65+
output.copy_from_slice(&input[..]);
66+
for _ in 0..10 {
67+
// Odd round
68+
quarter_round!(output[0], output[4], output[8], output[12]); // column 1
69+
quarter_round!(output[5], output[9], output[13], output[1]); // column 2
70+
quarter_round!(output[10], output[14], output[2], output[6]); // column 3
71+
quarter_round!(output[15], output[3], output[7], output[11]); // column 4
72+
73+
// Even round
74+
quarter_round!(output[0], output[1], output[2], output[3]); // row 1
75+
quarter_round!(output[5], output[6], output[7], output[4]); // row 2
76+
quarter_round!(output[10], output[11], output[8], output[9]); // row 3
77+
quarter_round!(output[15], output[12], output[13], output[14]); // row 4
78+
}
79+
for (a, &b) in output.iter_mut().zip(input.iter()) {
80+
*a = a.wrapping_add(b);
81+
}
82+
}
83+
84+
#[cfg(test)]
85+
mod tests {
86+
use super::*;
87+
use std::fmt::Write;
88+
89+
fn output_hex(inp: &[u32; 16]) -> String {
90+
let mut res = String::new();
91+
res.reserve(512 / 4);
92+
for &x in inp {
93+
write!(&mut res, "{x:08x}").unwrap();
94+
}
95+
res
96+
}
97+
#[test]
98+
// test vector 1
99+
fn basic_tv1() {
100+
let mut inp = [0u32; 16];
101+
let mut out = [0u32; 16];
102+
inp[0] = C[0];
103+
inp[1] = 0x01020304; // 1, 2, 3, 4
104+
inp[2] = 0x05060708; // 5, 6, 7, 8, ...
105+
inp[3] = 0x090a0b0c;
106+
inp[4] = 0x0d0e0f10;
107+
inp[5] = C[1];
108+
inp[6] = 0x65666768; // 101, 102, 103, 104
109+
inp[7] = 0x696a6b6c; // 105, 106, 107, 108, ...
110+
inp[8] = 0x6d6e6f70;
111+
inp[9] = 0x71727374;
112+
inp[10] = C[2];
113+
inp[11] = 0xc9cacbcc; // 201, 202, 203, 204
114+
inp[12] = 0xcdcecfd0; // 205, 206, 207, 208, ...
115+
inp[13] = 0xd1d2d3d4;
116+
inp[14] = 0xd5d6d7d8;
117+
inp[15] = C[3];
118+
salsa20(&inp, &mut out);
119+
// Checked with wikipedia implementation, does not agree with
120+
// "https://cr.yp.to/snuffle/spec.pdf"
121+
assert_eq!(
122+
output_hex(&out),
123+
concat!(
124+
"de1d6f8d91dbf69d0db4b70c8b4320d236694432896d98b05aa7b76d5738ca13",
125+
"04e5a170c8e479af1542ed2f30f26ba57da20203cfe955c66f4cc7a06dd34359"
126+
)
127+
);
128+
}
129+
}

src/graph/depth_first_search_tic_tac_toe.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ use std::io;
3030
//#[cfg(not(test))]
3131
//use rand::Rng;
3232

33-
#[derive(Copy, Clone, PartialEq, Debug)]
33+
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
3434
struct Position {
3535
x: u8,
3636
y: u8,
@@ -43,13 +43,13 @@ pub enum Players {
4343
PlayerO,
4444
}
4545

46-
#[derive(Copy, Clone, PartialEq, Debug)]
46+
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
4747
struct SinglePlayAction {
4848
position: Position,
4949
side: Players,
5050
}
5151

52-
#[derive(Clone, PartialEq, Debug)]
52+
#[derive(Clone, PartialEq, Eq, Debug)]
5353
pub struct PlayActions {
5454
positions: Vec<Position>,
5555
side: Players,

0 commit comments

Comments
 (0)