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