1 | /* Copyright 2015 OpenMarket Ltd |
2 | * |
3 | * Licensed under the Apache License, Version 2.0 (the "License"); |
4 | * you may not use this file except in compliance with the License. |
5 | * You may obtain a copy of the License at |
6 | * |
7 | * http://www.apache.org/licenses/LICENSE-2.0 |
8 | * |
9 | * Unless required by applicable law or agreed to in writing, software |
10 | * distributed under the License is distributed on an "AS IS" BASIS, |
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | * See the License for the specific language governing permissions and |
13 | * limitations under the License. |
14 | */ |
15 | #include "olm/crypto.h" |
16 | #include "olm/memory.hh" |
17 | |
18 | #include <cstring> |
19 | |
20 | extern "C" { |
21 | |
22 | #include "crypto-algorithms/aes.h" |
23 | #include "crypto-algorithms/sha256.h" |
24 | |
25 | } |
26 | |
27 | #include "ed25519/src/ed25519.h" |
28 | #include "curve25519-donna.h" |
29 | |
30 | namespace { |
31 | |
32 | static const std::uint8_t CURVE25519_BASEPOINT[32] = {9}; |
33 | static const std::size_t AES_KEY_SCHEDULE_LENGTH = 60; |
34 | static const std::size_t AES_KEY_BITS = 8 * AES256_KEY_LENGTH; |
35 | static const std::size_t AES_BLOCK_LENGTH = 16; |
36 | static const std::size_t SHA256_BLOCK_LENGTH = 64; |
37 | static const std::uint8_t HKDF_DEFAULT_SALT[32] = {}; |
38 | |
39 | |
40 | template<std::size_t block_size> |
41 | inline static void xor_block( |
42 | std::uint8_t * block, |
43 | std::uint8_t const * input |
44 | ) { |
45 | for (std::size_t i = 0; i < block_size; ++i) { |
46 | block[i] ^= input[i]; |
47 | } |
48 | } |
49 | |
50 | |
51 | inline static void hmac_sha256_key( |
52 | std::uint8_t const * input_key, std::size_t input_key_length, |
53 | std::uint8_t * hmac_key |
54 | ) { |
55 | std::memset(s: hmac_key, c: 0, n: SHA256_BLOCK_LENGTH); |
56 | if (input_key_length > SHA256_BLOCK_LENGTH) { |
57 | ::SHA256_CTX context; |
58 | ::sha256_init(ctx: &context); |
59 | ::sha256_update(ctx: &context, data: input_key, len: input_key_length); |
60 | ::sha256_final(ctx: &context, hash: hmac_key); |
61 | } else { |
62 | std::memcpy(dest: hmac_key, src: input_key, n: input_key_length); |
63 | } |
64 | } |
65 | |
66 | |
67 | inline static void hmac_sha256_init( |
68 | ::SHA256_CTX * context, |
69 | std::uint8_t const * hmac_key |
70 | ) { |
71 | std::uint8_t i_pad[SHA256_BLOCK_LENGTH]; |
72 | std::memcpy(dest: i_pad, src: hmac_key, n: SHA256_BLOCK_LENGTH); |
73 | for (std::size_t i = 0; i < SHA256_BLOCK_LENGTH; ++i) { |
74 | i_pad[i] ^= 0x36; |
75 | } |
76 | ::sha256_init(ctx: context); |
77 | ::sha256_update(ctx: context, data: i_pad, len: SHA256_BLOCK_LENGTH); |
78 | olm::unset(value&: i_pad); |
79 | } |
80 | |
81 | |
82 | inline static void hmac_sha256_final( |
83 | ::SHA256_CTX * context, |
84 | std::uint8_t const * hmac_key, |
85 | std::uint8_t * output |
86 | ) { |
87 | std::uint8_t o_pad[SHA256_BLOCK_LENGTH + SHA256_OUTPUT_LENGTH]; |
88 | std::memcpy(dest: o_pad, src: hmac_key, n: SHA256_BLOCK_LENGTH); |
89 | for (std::size_t i = 0; i < SHA256_BLOCK_LENGTH; ++i) { |
90 | o_pad[i] ^= 0x5C; |
91 | } |
92 | ::sha256_final(ctx: context, hash: o_pad + SHA256_BLOCK_LENGTH); |
93 | ::SHA256_CTX final_context; |
94 | ::sha256_init(ctx: &final_context); |
95 | ::sha256_update(ctx: &final_context, data: o_pad, len: sizeof(o_pad)); |
96 | ::sha256_final(ctx: &final_context, hash: output); |
97 | olm::unset(value&: final_context); |
98 | olm::unset(value&: o_pad); |
99 | } |
100 | |
101 | } // namespace |
102 | |
103 | void _olm_crypto_curve25519_generate_key( |
104 | uint8_t const * random_32_bytes, |
105 | struct _olm_curve25519_key_pair *key_pair |
106 | ) { |
107 | std::memcpy( |
108 | dest: key_pair->private_key.private_key, src: random_32_bytes, |
109 | CURVE25519_KEY_LENGTH |
110 | ); |
111 | ::curve25519_donna( |
112 | output: key_pair->public_key.public_key, |
113 | a: key_pair->private_key.private_key, |
114 | b: CURVE25519_BASEPOINT |
115 | ); |
116 | } |
117 | |
118 | |
119 | void _olm_crypto_curve25519_shared_secret( |
120 | const struct _olm_curve25519_key_pair *our_key, |
121 | const struct _olm_curve25519_public_key * their_key, |
122 | std::uint8_t * output |
123 | ) { |
124 | ::curve25519_donna(output, a: our_key->private_key.private_key, b: their_key->public_key); |
125 | } |
126 | |
127 | |
128 | void _olm_crypto_ed25519_generate_key( |
129 | std::uint8_t const * random_32_bytes, |
130 | struct _olm_ed25519_key_pair *key_pair |
131 | ) { |
132 | ::ed25519_create_keypair( |
133 | public_key: key_pair->public_key.public_key, private_key: key_pair->private_key.private_key, |
134 | seed: random_32_bytes |
135 | ); |
136 | } |
137 | |
138 | |
139 | void _olm_crypto_ed25519_sign( |
140 | const struct _olm_ed25519_key_pair *our_key, |
141 | std::uint8_t const * message, std::size_t message_length, |
142 | std::uint8_t * output |
143 | ) { |
144 | ::ed25519_sign( |
145 | signature: output, |
146 | message, message_len: message_length, |
147 | public_key: our_key->public_key.public_key, |
148 | private_key: our_key->private_key.private_key |
149 | ); |
150 | } |
151 | |
152 | |
153 | int _olm_crypto_ed25519_verify( |
154 | const struct _olm_ed25519_public_key *their_key, |
155 | std::uint8_t const * message, std::size_t message_length, |
156 | std::uint8_t const * signature |
157 | ) { |
158 | return 0 != ::ed25519_verify( |
159 | signature, |
160 | message, message_len: message_length, |
161 | public_key: their_key->public_key |
162 | ); |
163 | } |
164 | |
165 | |
166 | std::size_t _olm_crypto_aes_encrypt_cbc_length( |
167 | std::size_t input_length |
168 | ) { |
169 | return input_length + AES_BLOCK_LENGTH - input_length % AES_BLOCK_LENGTH; |
170 | } |
171 | |
172 | |
173 | void _olm_crypto_aes_encrypt_cbc( |
174 | _olm_aes256_key const *key, |
175 | _olm_aes256_iv const *iv, |
176 | std::uint8_t const * input, std::size_t input_length, |
177 | std::uint8_t * output |
178 | ) { |
179 | std::uint32_t key_schedule[AES_KEY_SCHEDULE_LENGTH]; |
180 | ::aes_key_setup(key: key->key, w: key_schedule, keysize: AES_KEY_BITS); |
181 | std::uint8_t input_block[AES_BLOCK_LENGTH]; |
182 | std::memcpy(dest: input_block, src: iv->iv, n: AES_BLOCK_LENGTH); |
183 | while (input_length >= AES_BLOCK_LENGTH) { |
184 | xor_block<AES_BLOCK_LENGTH>(block: input_block, input); |
185 | ::aes_encrypt(in: input_block, out: output, key: key_schedule, keysize: AES_KEY_BITS); |
186 | std::memcpy(dest: input_block, src: output, n: AES_BLOCK_LENGTH); |
187 | input += AES_BLOCK_LENGTH; |
188 | output += AES_BLOCK_LENGTH; |
189 | input_length -= AES_BLOCK_LENGTH; |
190 | } |
191 | std::size_t i = 0; |
192 | for (; i < input_length; ++i) { |
193 | input_block[i] ^= input[i]; |
194 | } |
195 | for (; i < AES_BLOCK_LENGTH; ++i) { |
196 | input_block[i] ^= AES_BLOCK_LENGTH - input_length; |
197 | } |
198 | ::aes_encrypt(in: input_block, out: output, key: key_schedule, keysize: AES_KEY_BITS); |
199 | olm::unset(value&: key_schedule); |
200 | olm::unset(value&: input_block); |
201 | } |
202 | |
203 | |
204 | std::size_t _olm_crypto_aes_decrypt_cbc( |
205 | _olm_aes256_key const *key, |
206 | _olm_aes256_iv const *iv, |
207 | std::uint8_t const * input, std::size_t input_length, |
208 | std::uint8_t * output |
209 | ) { |
210 | std::uint32_t key_schedule[AES_KEY_SCHEDULE_LENGTH]; |
211 | ::aes_key_setup(key: key->key, w: key_schedule, keysize: AES_KEY_BITS); |
212 | std::uint8_t block1[AES_BLOCK_LENGTH]; |
213 | std::uint8_t block2[AES_BLOCK_LENGTH]; |
214 | std::memcpy(dest: block1, src: iv->iv, n: AES_BLOCK_LENGTH); |
215 | for (std::size_t i = 0; i < input_length; i += AES_BLOCK_LENGTH) { |
216 | std::memcpy(dest: block2, src: &input[i], n: AES_BLOCK_LENGTH); |
217 | ::aes_decrypt(in: &input[i], out: &output[i], key: key_schedule, keysize: AES_KEY_BITS); |
218 | xor_block<AES_BLOCK_LENGTH>(block: &output[i], input: block1); |
219 | std::memcpy(dest: block1, src: block2, n: AES_BLOCK_LENGTH); |
220 | } |
221 | olm::unset(value&: key_schedule); |
222 | olm::unset(value&: block1); |
223 | olm::unset(value&: block2); |
224 | std::size_t padding = output[input_length - 1]; |
225 | return (padding > input_length) ? std::size_t(-1) : (input_length - padding); |
226 | } |
227 | |
228 | |
229 | void _olm_crypto_sha256( |
230 | std::uint8_t const * input, std::size_t input_length, |
231 | std::uint8_t * output |
232 | ) { |
233 | ::SHA256_CTX context; |
234 | ::sha256_init(ctx: &context); |
235 | ::sha256_update(ctx: &context, data: input, len: input_length); |
236 | ::sha256_final(ctx: &context, hash: output); |
237 | olm::unset(value&: context); |
238 | } |
239 | |
240 | |
241 | void _olm_crypto_hmac_sha256( |
242 | std::uint8_t const * key, std::size_t key_length, |
243 | std::uint8_t const * input, std::size_t input_length, |
244 | std::uint8_t * output |
245 | ) { |
246 | std::uint8_t hmac_key[SHA256_BLOCK_LENGTH]; |
247 | ::SHA256_CTX context; |
248 | hmac_sha256_key(input_key: key, input_key_length: key_length, hmac_key); |
249 | hmac_sha256_init(context: &context, hmac_key); |
250 | ::sha256_update(ctx: &context, data: input, len: input_length); |
251 | hmac_sha256_final(context: &context, hmac_key, output); |
252 | olm::unset(value&: hmac_key); |
253 | olm::unset(value&: context); |
254 | } |
255 | |
256 | |
257 | void _olm_crypto_hkdf_sha256( |
258 | std::uint8_t const * input, std::size_t input_length, |
259 | std::uint8_t const * salt, std::size_t salt_length, |
260 | std::uint8_t const * info, std::size_t info_length, |
261 | std::uint8_t * output, std::size_t output_length |
262 | ) { |
263 | ::SHA256_CTX context; |
264 | std::uint8_t hmac_key[SHA256_BLOCK_LENGTH]; |
265 | std::uint8_t step_result[SHA256_OUTPUT_LENGTH]; |
266 | std::size_t bytes_remaining = output_length; |
267 | std::uint8_t iteration = 1; |
268 | if (!salt) { |
269 | salt = HKDF_DEFAULT_SALT; |
270 | salt_length = sizeof(HKDF_DEFAULT_SALT); |
271 | } |
272 | /* Extract */ |
273 | hmac_sha256_key(input_key: salt, input_key_length: salt_length, hmac_key); |
274 | hmac_sha256_init(context: &context, hmac_key); |
275 | ::sha256_update(ctx: &context, data: input, len: input_length); |
276 | hmac_sha256_final(context: &context, hmac_key, output: step_result); |
277 | hmac_sha256_key(input_key: step_result, SHA256_OUTPUT_LENGTH, hmac_key); |
278 | |
279 | /* Expand */ |
280 | hmac_sha256_init(context: &context, hmac_key); |
281 | ::sha256_update(ctx: &context, data: info, len: info_length); |
282 | ::sha256_update(ctx: &context, data: &iteration, len: 1); |
283 | hmac_sha256_final(context: &context, hmac_key, output: step_result); |
284 | while (bytes_remaining > SHA256_OUTPUT_LENGTH) { |
285 | std::memcpy(dest: output, src: step_result, SHA256_OUTPUT_LENGTH); |
286 | output += SHA256_OUTPUT_LENGTH; |
287 | bytes_remaining -= SHA256_OUTPUT_LENGTH; |
288 | iteration ++; |
289 | hmac_sha256_init(context: &context, hmac_key); |
290 | ::sha256_update(ctx: &context, data: step_result, SHA256_OUTPUT_LENGTH); |
291 | ::sha256_update(ctx: &context, data: info, len: info_length); |
292 | ::sha256_update(ctx: &context, data: &iteration, len: 1); |
293 | hmac_sha256_final(context: &context, hmac_key, output: step_result); |
294 | } |
295 | std::memcpy(dest: output, src: step_result, n: bytes_remaining); |
296 | olm::unset(value&: context); |
297 | olm::unset(value&: hmac_key); |
298 | olm::unset(value&: step_result); |
299 | } |
300 | |