/src/botan/src/lib/stream/salsa20/salsa20.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Salsa20 / XSalsa20 |
3 | | * (C) 1999-2010,2014 Jack Lloyd |
4 | | * |
5 | | * Botan is released under the Simplified BSD License (see license.txt) |
6 | | */ |
7 | | |
8 | | #include <botan/salsa20.h> |
9 | | #include <botan/exceptn.h> |
10 | | #include <botan/loadstor.h> |
11 | | #include <botan/rotate.h> |
12 | | |
13 | | namespace Botan { |
14 | | |
15 | | #define SALSA20_QUARTER_ROUND(x1, x2, x3, x4) \ |
16 | 0 | do { \ |
17 | 0 | x2 ^= rotl<7>(x1 + x4); \ |
18 | 0 | x3 ^= rotl<9>(x2 + x1); \ |
19 | 0 | x4 ^= rotl<13>(x3 + x2); \ |
20 | 0 | x1 ^= rotl<18>(x4 + x3); \ |
21 | 0 | } while(0) |
22 | | |
23 | | /* |
24 | | * Generate HSalsa20 cipher stream (for XSalsa20 IV setup) |
25 | | */ |
26 | | //static |
27 | | void Salsa20::hsalsa20(uint32_t output[8], const uint32_t input[16]) |
28 | 0 | { |
29 | 0 | uint32_t x00 = input[ 0], x01 = input[ 1], x02 = input[ 2], x03 = input[ 3], |
30 | 0 | x04 = input[ 4], x05 = input[ 5], x06 = input[ 6], x07 = input[ 7], |
31 | 0 | x08 = input[ 8], x09 = input[ 9], x10 = input[10], x11 = input[11], |
32 | 0 | x12 = input[12], x13 = input[13], x14 = input[14], x15 = input[15]; |
33 | 0 |
|
34 | 0 | for(size_t i = 0; i != 10; ++i) |
35 | 0 | { |
36 | 0 | SALSA20_QUARTER_ROUND(x00, x04, x08, x12); |
37 | 0 | SALSA20_QUARTER_ROUND(x05, x09, x13, x01); |
38 | 0 | SALSA20_QUARTER_ROUND(x10, x14, x02, x06); |
39 | 0 | SALSA20_QUARTER_ROUND(x15, x03, x07, x11); |
40 | 0 |
|
41 | 0 | SALSA20_QUARTER_ROUND(x00, x01, x02, x03); |
42 | 0 | SALSA20_QUARTER_ROUND(x05, x06, x07, x04); |
43 | 0 | SALSA20_QUARTER_ROUND(x10, x11, x08, x09); |
44 | 0 | SALSA20_QUARTER_ROUND(x15, x12, x13, x14); |
45 | 0 | } |
46 | 0 |
|
47 | 0 | output[0] = x00; |
48 | 0 | output[1] = x05; |
49 | 0 | output[2] = x10; |
50 | 0 | output[3] = x15; |
51 | 0 | output[4] = x06; |
52 | 0 | output[5] = x07; |
53 | 0 | output[6] = x08; |
54 | 0 | output[7] = x09; |
55 | 0 | } |
56 | | |
57 | | /* |
58 | | * Generate Salsa20 cipher stream |
59 | | */ |
60 | | //static |
61 | | void Salsa20::salsa_core(uint8_t output[64], const uint32_t input[16], size_t rounds) |
62 | 0 | { |
63 | 0 | BOTAN_ASSERT_NOMSG(rounds % 2 == 0); |
64 | 0 |
|
65 | 0 | uint32_t x00 = input[ 0], x01 = input[ 1], x02 = input[ 2], x03 = input[ 3], |
66 | 0 | x04 = input[ 4], x05 = input[ 5], x06 = input[ 6], x07 = input[ 7], |
67 | 0 | x08 = input[ 8], x09 = input[ 9], x10 = input[10], x11 = input[11], |
68 | 0 | x12 = input[12], x13 = input[13], x14 = input[14], x15 = input[15]; |
69 | 0 |
|
70 | 0 | for(size_t i = 0; i != rounds / 2; ++i) |
71 | 0 | { |
72 | 0 | SALSA20_QUARTER_ROUND(x00, x04, x08, x12); |
73 | 0 | SALSA20_QUARTER_ROUND(x05, x09, x13, x01); |
74 | 0 | SALSA20_QUARTER_ROUND(x10, x14, x02, x06); |
75 | 0 | SALSA20_QUARTER_ROUND(x15, x03, x07, x11); |
76 | 0 |
|
77 | 0 | SALSA20_QUARTER_ROUND(x00, x01, x02, x03); |
78 | 0 | SALSA20_QUARTER_ROUND(x05, x06, x07, x04); |
79 | 0 | SALSA20_QUARTER_ROUND(x10, x11, x08, x09); |
80 | 0 | SALSA20_QUARTER_ROUND(x15, x12, x13, x14); |
81 | 0 | } |
82 | 0 |
|
83 | 0 | store_le(x00 + input[ 0], output + 4 * 0); |
84 | 0 | store_le(x01 + input[ 1], output + 4 * 1); |
85 | 0 | store_le(x02 + input[ 2], output + 4 * 2); |
86 | 0 | store_le(x03 + input[ 3], output + 4 * 3); |
87 | 0 | store_le(x04 + input[ 4], output + 4 * 4); |
88 | 0 | store_le(x05 + input[ 5], output + 4 * 5); |
89 | 0 | store_le(x06 + input[ 6], output + 4 * 6); |
90 | 0 | store_le(x07 + input[ 7], output + 4 * 7); |
91 | 0 | store_le(x08 + input[ 8], output + 4 * 8); |
92 | 0 | store_le(x09 + input[ 9], output + 4 * 9); |
93 | 0 | store_le(x10 + input[10], output + 4 * 10); |
94 | 0 | store_le(x11 + input[11], output + 4 * 11); |
95 | 0 | store_le(x12 + input[12], output + 4 * 12); |
96 | 0 | store_le(x13 + input[13], output + 4 * 13); |
97 | 0 | store_le(x14 + input[14], output + 4 * 14); |
98 | 0 | store_le(x15 + input[15], output + 4 * 15); |
99 | 0 | } |
100 | | |
101 | | #undef SALSA20_QUARTER_ROUND |
102 | | |
103 | | /* |
104 | | * Combine cipher stream with message |
105 | | */ |
106 | | void Salsa20::cipher(const uint8_t in[], uint8_t out[], size_t length) |
107 | 0 | { |
108 | 0 | verify_key_set(m_state.empty() == false); |
109 | 0 |
|
110 | 0 | while(length >= m_buffer.size() - m_position) |
111 | 0 | { |
112 | 0 | const size_t available = m_buffer.size() - m_position; |
113 | 0 |
|
114 | 0 | xor_buf(out, in, &m_buffer[m_position], available); |
115 | 0 | salsa_core(m_buffer.data(), m_state.data(), 20); |
116 | 0 |
|
117 | 0 | ++m_state[8]; |
118 | 0 | m_state[9] += (m_state[8] == 0); |
119 | 0 |
|
120 | 0 | length -= available; |
121 | 0 | in += available; |
122 | 0 | out += available; |
123 | 0 |
|
124 | 0 | m_position = 0; |
125 | 0 | } |
126 | 0 |
|
127 | 0 | xor_buf(out, in, &m_buffer[m_position], length); |
128 | 0 |
|
129 | 0 | m_position += length; |
130 | 0 | } |
131 | | |
132 | | void Salsa20::initialize_state() |
133 | 0 | { |
134 | 0 | static const uint32_t TAU[] = |
135 | 0 | { 0x61707865, 0x3120646e, 0x79622d36, 0x6b206574 }; |
136 | 0 |
|
137 | 0 | static const uint32_t SIGMA[] = |
138 | 0 | { 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574 }; |
139 | 0 |
|
140 | 0 | m_state[1] = m_key[0]; |
141 | 0 | m_state[2] = m_key[1]; |
142 | 0 | m_state[3] = m_key[2]; |
143 | 0 | m_state[4] = m_key[3]; |
144 | 0 |
|
145 | 0 | if(m_key.size() == 4) |
146 | 0 | { |
147 | 0 | m_state[0] = TAU[0]; |
148 | 0 | m_state[5] = TAU[1]; |
149 | 0 | m_state[10] = TAU[2]; |
150 | 0 | m_state[15] = TAU[3]; |
151 | 0 | m_state[11] = m_key[0]; |
152 | 0 | m_state[12] = m_key[1]; |
153 | 0 | m_state[13] = m_key[2]; |
154 | 0 | m_state[14] = m_key[3]; |
155 | 0 | } |
156 | 0 | else |
157 | 0 | { |
158 | 0 | m_state[0] = SIGMA[0]; |
159 | 0 | m_state[5] = SIGMA[1]; |
160 | 0 | m_state[10] = SIGMA[2]; |
161 | 0 | m_state[15] = SIGMA[3]; |
162 | 0 | m_state[11] = m_key[4]; |
163 | 0 | m_state[12] = m_key[5]; |
164 | 0 | m_state[13] = m_key[6]; |
165 | 0 | m_state[14] = m_key[7]; |
166 | 0 | } |
167 | 0 |
|
168 | 0 | m_state[6] = 0; |
169 | 0 | m_state[7] = 0; |
170 | 0 | m_state[8] = 0; |
171 | 0 | m_state[9] = 0; |
172 | 0 |
|
173 | 0 | m_position = 0; |
174 | 0 | } |
175 | | |
176 | | /* |
177 | | * Salsa20 Key Schedule |
178 | | */ |
179 | | void Salsa20::key_schedule(const uint8_t key[], size_t length) |
180 | 0 | { |
181 | 0 | m_key.resize(length / 4); |
182 | 0 | load_le<uint32_t>(m_key.data(), key, m_key.size()); |
183 | 0 |
|
184 | 0 | m_state.resize(16); |
185 | 0 | m_buffer.resize(64); |
186 | 0 |
|
187 | 0 | set_iv(nullptr, 0); |
188 | 0 | } |
189 | | |
190 | | /* |
191 | | * Set the Salsa IV |
192 | | */ |
193 | | void Salsa20::set_iv(const uint8_t iv[], size_t length) |
194 | 0 | { |
195 | 0 | verify_key_set(m_state.empty() == false); |
196 | 0 |
|
197 | 0 | if(!valid_iv_length(length)) |
198 | 0 | throw Invalid_IV_Length(name(), length); |
199 | 0 | |
200 | 0 | initialize_state(); |
201 | 0 |
|
202 | 0 | if(length == 0) |
203 | 0 | { |
204 | | // Salsa20 null IV |
205 | 0 | m_state[6] = 0; |
206 | 0 | m_state[7] = 0; |
207 | 0 | } |
208 | 0 | else if(length == 8) |
209 | 0 | { |
210 | | // Salsa20 |
211 | 0 | m_state[6] = load_le<uint32_t>(iv, 0); |
212 | 0 | m_state[7] = load_le<uint32_t>(iv, 1); |
213 | 0 | } |
214 | 0 | else |
215 | 0 | { |
216 | | // XSalsa20 |
217 | 0 | m_state[6] = load_le<uint32_t>(iv, 0); |
218 | 0 | m_state[7] = load_le<uint32_t>(iv, 1); |
219 | 0 | m_state[8] = load_le<uint32_t>(iv, 2); |
220 | 0 | m_state[9] = load_le<uint32_t>(iv, 3); |
221 | 0 |
|
222 | 0 | secure_vector<uint32_t> hsalsa(8); |
223 | 0 | hsalsa20(hsalsa.data(), m_state.data()); |
224 | 0 |
|
225 | 0 | m_state[ 1] = hsalsa[0]; |
226 | 0 | m_state[ 2] = hsalsa[1]; |
227 | 0 | m_state[ 3] = hsalsa[2]; |
228 | 0 | m_state[ 4] = hsalsa[3]; |
229 | 0 | m_state[ 6] = load_le<uint32_t>(iv, 4); |
230 | 0 | m_state[ 7] = load_le<uint32_t>(iv, 5); |
231 | 0 | m_state[11] = hsalsa[4]; |
232 | 0 | m_state[12] = hsalsa[5]; |
233 | 0 | m_state[13] = hsalsa[6]; |
234 | 0 | m_state[14] = hsalsa[7]; |
235 | 0 | } |
236 | 0 |
|
237 | 0 | m_state[8] = 0; |
238 | 0 | m_state[9] = 0; |
239 | 0 |
|
240 | 0 | salsa_core(m_buffer.data(), m_state.data(), 20); |
241 | 0 | ++m_state[8]; |
242 | 0 | m_state[9] += (m_state[8] == 0); |
243 | 0 |
|
244 | 0 | m_position = 0; |
245 | 0 | } |
246 | | |
247 | | bool Salsa20::valid_iv_length(size_t iv_len) const |
248 | 0 | { |
249 | 0 | return (iv_len == 0 || iv_len == 8 || iv_len == 24); |
250 | 0 | } |
251 | | |
252 | | size_t Salsa20::default_iv_length() const |
253 | 0 | { |
254 | 0 | return 24; |
255 | 0 | } |
256 | | |
257 | | Key_Length_Specification Salsa20::key_spec() const |
258 | 0 | { |
259 | 0 | return Key_Length_Specification(16, 32, 16); |
260 | 0 | } |
261 | | |
262 | | StreamCipher* Salsa20::clone() const |
263 | 0 | { |
264 | 0 | return new Salsa20; |
265 | 0 | } |
266 | | |
267 | | std::string Salsa20::name() const |
268 | 0 | { |
269 | 0 | return "Salsa20"; |
270 | 0 | } |
271 | | |
272 | | /* |
273 | | * Clear memory of sensitive data |
274 | | */ |
275 | | void Salsa20::clear() |
276 | 0 | { |
277 | 0 | zap(m_key); |
278 | 0 | zap(m_state); |
279 | 0 | zap(m_buffer); |
280 | 0 | m_position = 0; |
281 | 0 | } |
282 | | |
283 | | void Salsa20::seek(uint64_t offset) |
284 | 0 | { |
285 | 0 | verify_key_set(m_state.empty() == false); |
286 | 0 |
|
287 | | // Find the block offset |
288 | 0 | const uint64_t counter = offset / 64; |
289 | 0 | uint8_t counter8[8]; |
290 | 0 | store_le(counter, counter8); |
291 | 0 |
|
292 | 0 | m_state[8] = load_le<uint32_t>(counter8, 0); |
293 | 0 | m_state[9] += load_le<uint32_t>(counter8, 1); |
294 | 0 |
|
295 | 0 | salsa_core(m_buffer.data(), m_state.data(), 20); |
296 | 0 |
|
297 | 0 | ++m_state[8]; |
298 | 0 | m_state[9] += (m_state[8] == 0); |
299 | 0 |
|
300 | 0 | m_position = offset % 64; |
301 | 0 | } |
302 | | } |