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