1 | /* Copyright 2018, 2019 New Vector 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/pk.h" |
16 | #include "olm/cipher.h" |
17 | #include "olm/crypto.h" |
18 | #include "olm/ratchet.hh" |
19 | #include "olm/error.h" |
20 | #include "olm/memory.hh" |
21 | #include "olm/base64.hh" |
22 | #include "olm/pickle_encoding.h" |
23 | #include "olm/pickle.hh" |
24 | |
25 | static const std::size_t MAC_LENGTH = 8; |
26 | |
27 | const struct _olm_cipher_aes_sha_256 olm_pk_cipher_aes_sha256 = |
28 | OLM_CIPHER_INIT_AES_SHA_256("" ); |
29 | const struct _olm_cipher *olm_pk_cipher = |
30 | OLM_CIPHER_BASE(&olm_pk_cipher_aes_sha256); |
31 | |
32 | extern "C" { |
33 | |
34 | struct OlmPkEncryption { |
35 | OlmErrorCode last_error; |
36 | _olm_curve25519_public_key recipient_key; |
37 | }; |
38 | |
39 | const char * olm_pk_encryption_last_error( |
40 | const OlmPkEncryption * encryption |
41 | ) { |
42 | auto error = encryption->last_error; |
43 | return _olm_error_to_string(error); |
44 | } |
45 | |
46 | OlmErrorCode olm_pk_encryption_last_error_code( |
47 | const OlmPkEncryption * encryption |
48 | ) { |
49 | return encryption->last_error; |
50 | } |
51 | |
52 | size_t olm_pk_encryption_size(void) { |
53 | return sizeof(OlmPkEncryption); |
54 | } |
55 | |
56 | OlmPkEncryption *olm_pk_encryption( |
57 | void * memory |
58 | ) { |
59 | olm::unset(buffer: memory, buffer_length: sizeof(OlmPkEncryption)); |
60 | return new(memory) OlmPkEncryption; |
61 | } |
62 | |
63 | size_t olm_clear_pk_encryption( |
64 | OlmPkEncryption *encryption |
65 | ) { |
66 | /* Clear the memory backing the encryption */ |
67 | olm::unset(buffer: encryption, buffer_length: sizeof(OlmPkEncryption)); |
68 | /* Initialise a fresh encryption object in case someone tries to use it */ |
69 | new(encryption) OlmPkEncryption(); |
70 | return sizeof(OlmPkEncryption); |
71 | } |
72 | |
73 | size_t olm_pk_encryption_set_recipient_key ( |
74 | OlmPkEncryption *encryption, |
75 | void const * key, size_t key_length |
76 | ) { |
77 | if (key_length < olm_pk_key_length()) { |
78 | encryption->last_error = |
79 | OlmErrorCode::OLM_INPUT_BUFFER_TOO_SMALL; |
80 | return std::size_t(-1); |
81 | } |
82 | |
83 | olm::decode_base64( |
84 | input: (const uint8_t*)key, |
85 | input_length: olm_pk_key_length(), |
86 | output: (uint8_t *)encryption->recipient_key.public_key |
87 | ); |
88 | |
89 | return 0; |
90 | } |
91 | |
92 | size_t olm_pk_ciphertext_length( |
93 | const OlmPkEncryption *encryption, |
94 | size_t plaintext_length |
95 | ) { |
96 | return olm::encode_base64_length( |
97 | input_length: _olm_cipher_aes_sha_256_ops.encrypt_ciphertext_length(olm_pk_cipher, plaintext_length) |
98 | ); |
99 | } |
100 | |
101 | size_t olm_pk_mac_length( |
102 | const OlmPkEncryption *encryption |
103 | ) { |
104 | return olm::encode_base64_length(input_length: _olm_cipher_aes_sha_256_ops.mac_length(olm_pk_cipher)); |
105 | } |
106 | |
107 | size_t olm_pk_encrypt_random_length( |
108 | const OlmPkEncryption *encryption |
109 | ) { |
110 | return CURVE25519_KEY_LENGTH; |
111 | } |
112 | |
113 | size_t olm_pk_encrypt( |
114 | OlmPkEncryption *encryption, |
115 | void const * plaintext, size_t plaintext_length, |
116 | void * ciphertext, size_t ciphertext_length, |
117 | void * mac, size_t mac_length, |
118 | void * ephemeral_key, size_t ephemeral_key_size, |
119 | const void * random, size_t random_length |
120 | ) { |
121 | if (ciphertext_length |
122 | < olm_pk_ciphertext_length(encryption, plaintext_length) |
123 | || mac_length |
124 | < _olm_cipher_aes_sha_256_ops.mac_length(olm_pk_cipher) |
125 | || ephemeral_key_size |
126 | < olm_pk_key_length()) { |
127 | encryption->last_error = |
128 | OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL; |
129 | return std::size_t(-1); |
130 | } |
131 | if (random_length < olm_pk_encrypt_random_length(encryption)) { |
132 | encryption->last_error = |
133 | OlmErrorCode::OLM_NOT_ENOUGH_RANDOM; |
134 | return std::size_t(-1); |
135 | } |
136 | |
137 | _olm_curve25519_key_pair ephemeral_keypair; |
138 | _olm_crypto_curve25519_generate_key(random_32_bytes: (const uint8_t *) random, output: &ephemeral_keypair); |
139 | olm::encode_base64( |
140 | input: (const uint8_t *)ephemeral_keypair.public_key.public_key, |
141 | CURVE25519_KEY_LENGTH, |
142 | output: (uint8_t *)ephemeral_key |
143 | ); |
144 | |
145 | olm::SharedKey secret; |
146 | _olm_crypto_curve25519_shared_secret(our_key: &ephemeral_keypair, their_key: &encryption->recipient_key, output: secret); |
147 | size_t raw_ciphertext_length = |
148 | _olm_cipher_aes_sha_256_ops.encrypt_ciphertext_length(olm_pk_cipher, plaintext_length); |
149 | uint8_t *ciphertext_pos = (uint8_t *) ciphertext + ciphertext_length - raw_ciphertext_length; |
150 | uint8_t raw_mac[MAC_LENGTH]; |
151 | size_t result = _olm_cipher_aes_sha_256_ops.encrypt( |
152 | olm_pk_cipher, |
153 | secret, sizeof(secret), |
154 | (const uint8_t *) plaintext, plaintext_length, |
155 | (uint8_t *) ciphertext_pos, raw_ciphertext_length, |
156 | (uint8_t *) raw_mac, MAC_LENGTH |
157 | ); |
158 | if (result != std::size_t(-1)) { |
159 | olm::encode_base64(input: raw_mac, input_length: MAC_LENGTH, output: (uint8_t *)mac); |
160 | olm::encode_base64(input: ciphertext_pos, input_length: raw_ciphertext_length, output: (uint8_t *)ciphertext); |
161 | } |
162 | return result; |
163 | } |
164 | |
165 | struct OlmPkDecryption { |
166 | OlmErrorCode last_error; |
167 | _olm_curve25519_key_pair key_pair; |
168 | }; |
169 | |
170 | const char * olm_pk_decryption_last_error( |
171 | const OlmPkDecryption * decryption |
172 | ) { |
173 | auto error = decryption->last_error; |
174 | return _olm_error_to_string(error); |
175 | } |
176 | |
177 | OlmErrorCode olm_pk_decryption_last_error_code( |
178 | const OlmPkDecryption * decryption |
179 | ) { |
180 | return decryption->last_error; |
181 | } |
182 | |
183 | size_t olm_pk_decryption_size(void) { |
184 | return sizeof(OlmPkDecryption); |
185 | } |
186 | |
187 | OlmPkDecryption *olm_pk_decryption( |
188 | void * memory |
189 | ) { |
190 | olm::unset(buffer: memory, buffer_length: sizeof(OlmPkDecryption)); |
191 | return new(memory) OlmPkDecryption; |
192 | } |
193 | |
194 | size_t olm_clear_pk_decryption( |
195 | OlmPkDecryption *decryption |
196 | ) { |
197 | /* Clear the memory backing the decryption */ |
198 | olm::unset(buffer: decryption, buffer_length: sizeof(OlmPkDecryption)); |
199 | /* Initialise a fresh decryption object in case someone tries to use it */ |
200 | new(decryption) OlmPkDecryption(); |
201 | return sizeof(OlmPkDecryption); |
202 | } |
203 | |
204 | size_t olm_pk_private_key_length(void) { |
205 | return CURVE25519_KEY_LENGTH; |
206 | } |
207 | |
208 | size_t olm_pk_generate_key_random_length(void) { |
209 | return olm_pk_private_key_length(); |
210 | } |
211 | |
212 | size_t olm_pk_key_length(void) { |
213 | return olm::encode_base64_length(CURVE25519_KEY_LENGTH); |
214 | } |
215 | |
216 | size_t olm_pk_key_from_private( |
217 | OlmPkDecryption * decryption, |
218 | void * pubkey, size_t pubkey_length, |
219 | const void * privkey, size_t privkey_length |
220 | ) { |
221 | if (pubkey_length < olm_pk_key_length()) { |
222 | decryption->last_error = |
223 | OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL; |
224 | return std::size_t(-1); |
225 | } |
226 | if (privkey_length < olm_pk_private_key_length()) { |
227 | decryption->last_error = |
228 | OlmErrorCode::OLM_INPUT_BUFFER_TOO_SMALL; |
229 | return std::size_t(-1); |
230 | } |
231 | |
232 | _olm_crypto_curve25519_generate_key(random_32_bytes: (const uint8_t *) privkey, output: &decryption->key_pair); |
233 | olm::encode_base64( |
234 | input: (const uint8_t *)decryption->key_pair.public_key.public_key, |
235 | CURVE25519_KEY_LENGTH, |
236 | output: (uint8_t *)pubkey |
237 | ); |
238 | return 0; |
239 | } |
240 | |
241 | size_t olm_pk_generate_key( |
242 | OlmPkDecryption * decryption, |
243 | void * pubkey, size_t pubkey_length, |
244 | const void * privkey, size_t privkey_length |
245 | ) { |
246 | return olm_pk_key_from_private(decryption, pubkey, pubkey_length, privkey, privkey_length); |
247 | } |
248 | |
249 | namespace { |
250 | static const std::uint32_t PK_DECRYPTION_PICKLE_VERSION = 1; |
251 | |
252 | static std::size_t pickle_length( |
253 | OlmPkDecryption const & value |
254 | ) { |
255 | std::size_t length = 0; |
256 | length += olm::pickle_length(value: PK_DECRYPTION_PICKLE_VERSION); |
257 | length += olm::pickle_length(value: value.key_pair); |
258 | return length; |
259 | } |
260 | |
261 | |
262 | static std::uint8_t * pickle( |
263 | std::uint8_t * pos, |
264 | OlmPkDecryption const & value |
265 | ) { |
266 | pos = olm::pickle(pos, value: PK_DECRYPTION_PICKLE_VERSION); |
267 | pos = olm::pickle(pos, value: value.key_pair); |
268 | return pos; |
269 | } |
270 | |
271 | |
272 | static std::uint8_t const * unpickle( |
273 | std::uint8_t const * pos, std::uint8_t const * end, |
274 | OlmPkDecryption & value |
275 | ) { |
276 | uint32_t pickle_version; |
277 | pos = olm::unpickle(pos, end, value&: pickle_version); UNPICKLE_OK(pos); |
278 | |
279 | switch (pickle_version) { |
280 | case 1: |
281 | break; |
282 | |
283 | default: |
284 | value.last_error = OlmErrorCode::OLM_UNKNOWN_PICKLE_VERSION; |
285 | return nullptr; |
286 | } |
287 | |
288 | pos = olm::unpickle(pos, end, value&: value.key_pair); UNPICKLE_OK(pos); |
289 | |
290 | return pos; |
291 | } |
292 | } |
293 | |
294 | size_t olm_pickle_pk_decryption_length( |
295 | const OlmPkDecryption * decryption |
296 | ) { |
297 | return _olm_enc_output_length(raw_length: pickle_length(value: *decryption)); |
298 | } |
299 | |
300 | size_t olm_pickle_pk_decryption( |
301 | OlmPkDecryption * decryption, |
302 | void const * key, size_t key_length, |
303 | void *pickled, size_t pickled_length |
304 | ) { |
305 | OlmPkDecryption & object = *decryption; |
306 | std::size_t raw_length = pickle_length(value: object); |
307 | if (pickled_length < _olm_enc_output_length(raw_length)) { |
308 | object.last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL; |
309 | return std::size_t(-1); |
310 | } |
311 | pickle(pos: _olm_enc_output_pos(output: reinterpret_cast<std::uint8_t *>(pickled), raw_length), value: object); |
312 | return _olm_enc_output( |
313 | key: reinterpret_cast<std::uint8_t const *>(key), key_length, |
314 | pickle: reinterpret_cast<std::uint8_t *>(pickled), raw_length |
315 | ); |
316 | } |
317 | |
318 | size_t olm_unpickle_pk_decryption( |
319 | OlmPkDecryption * decryption, |
320 | void const * key, size_t key_length, |
321 | void *pickled, size_t pickled_length, |
322 | void *pubkey, size_t pubkey_length |
323 | ) { |
324 | OlmPkDecryption & object = *decryption; |
325 | if (pubkey != NULL && pubkey_length < olm_pk_key_length()) { |
326 | object.last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL; |
327 | return std::size_t(-1); |
328 | } |
329 | std::uint8_t * const input = reinterpret_cast<std::uint8_t *>(pickled); |
330 | std::size_t raw_length = _olm_enc_input( |
331 | key: reinterpret_cast<std::uint8_t const *>(key), key_length, |
332 | input, b64_length: pickled_length, last_error: &object.last_error |
333 | ); |
334 | if (raw_length == std::size_t(-1)) { |
335 | return std::size_t(-1); |
336 | } |
337 | |
338 | std::uint8_t const * pos = input; |
339 | std::uint8_t const * end = pos + raw_length; |
340 | |
341 | pos = unpickle(pos, end, value&: object); |
342 | |
343 | if (!pos) { |
344 | /* Input was corrupted. */ |
345 | if (object.last_error == OlmErrorCode::OLM_SUCCESS) { |
346 | object.last_error = OlmErrorCode::OLM_CORRUPTED_PICKLE; |
347 | } |
348 | return std::size_t(-1); |
349 | } else if (pos != end) { |
350 | /* Input was longer than expected. */ |
351 | object.last_error = OlmErrorCode::OLM_PICKLE_EXTRA_DATA; |
352 | return std::size_t(-1); |
353 | } |
354 | |
355 | if (pubkey != NULL) { |
356 | olm::encode_base64( |
357 | input: (const uint8_t *)object.key_pair.public_key.public_key, |
358 | CURVE25519_KEY_LENGTH, |
359 | output: (uint8_t *)pubkey |
360 | ); |
361 | } |
362 | |
363 | return pickled_length; |
364 | } |
365 | |
366 | size_t olm_pk_max_plaintext_length( |
367 | const OlmPkDecryption * decryption, |
368 | size_t ciphertext_length |
369 | ) { |
370 | return _olm_cipher_aes_sha_256_ops.decrypt_max_plaintext_length( |
371 | olm_pk_cipher, olm::decode_base64_length(input_length: ciphertext_length) |
372 | ); |
373 | } |
374 | |
375 | size_t olm_pk_decrypt( |
376 | OlmPkDecryption * decryption, |
377 | void const * ephemeral_key, size_t ephemeral_key_length, |
378 | void const * mac, size_t mac_length, |
379 | void * ciphertext, size_t ciphertext_length, |
380 | void * plaintext, size_t max_plaintext_length |
381 | ) { |
382 | if (max_plaintext_length |
383 | < olm_pk_max_plaintext_length(decryption, ciphertext_length)) { |
384 | decryption->last_error = |
385 | OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL; |
386 | return std::size_t(-1); |
387 | } |
388 | |
389 | size_t raw_ciphertext_length = olm::decode_base64_length(input_length: ciphertext_length); |
390 | |
391 | if (ephemeral_key_length != olm::encode_base64_length(CURVE25519_KEY_LENGTH) |
392 | || mac_length != olm::encode_base64_length(input_length: MAC_LENGTH) |
393 | || raw_ciphertext_length == std::size_t(-1)) { |
394 | decryption->last_error = OlmErrorCode::OLM_INVALID_BASE64; |
395 | return std::size_t(-1); |
396 | } |
397 | |
398 | struct _olm_curve25519_public_key ephemeral; |
399 | olm::decode_base64( |
400 | input: (const uint8_t*)ephemeral_key, |
401 | input_length: olm::encode_base64_length(CURVE25519_KEY_LENGTH), |
402 | output: (uint8_t *)ephemeral.public_key |
403 | ); |
404 | |
405 | olm::SharedKey secret; |
406 | _olm_crypto_curve25519_shared_secret(our_key: &decryption->key_pair, their_key: &ephemeral, output: secret); |
407 | |
408 | uint8_t raw_mac[MAC_LENGTH]; |
409 | olm::decode_base64( |
410 | input: (const uint8_t *)mac, |
411 | input_length: olm::encode_base64_length(input_length: MAC_LENGTH), |
412 | output: raw_mac |
413 | ); |
414 | |
415 | olm::decode_base64( |
416 | input: (const uint8_t *)ciphertext, |
417 | input_length: ciphertext_length, |
418 | output: (uint8_t *)ciphertext |
419 | ); |
420 | |
421 | size_t result = _olm_cipher_aes_sha_256_ops.decrypt( |
422 | olm_pk_cipher, |
423 | secret, sizeof(secret), |
424 | (uint8_t *) raw_mac, MAC_LENGTH, |
425 | (const uint8_t *) ciphertext, raw_ciphertext_length, |
426 | (uint8_t *) plaintext, max_plaintext_length |
427 | ); |
428 | if (result == std::size_t(-1)) { |
429 | // we already checked the buffer sizes, so the only error that decrypt |
430 | // will return is if the MAC is incorrect |
431 | decryption->last_error = |
432 | OlmErrorCode::OLM_BAD_MESSAGE_MAC; |
433 | return std::size_t(-1); |
434 | } else { |
435 | return result; |
436 | } |
437 | } |
438 | |
439 | size_t olm_pk_get_private_key( |
440 | OlmPkDecryption * decryption, |
441 | void *private_key, size_t private_key_length |
442 | ) { |
443 | if (private_key_length < olm_pk_private_key_length()) { |
444 | decryption->last_error = |
445 | OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL; |
446 | return std::size_t(-1); |
447 | } |
448 | std::memcpy( |
449 | dest: private_key, |
450 | src: decryption->key_pair.private_key.private_key, |
451 | n: olm_pk_private_key_length() |
452 | ); |
453 | return olm_pk_private_key_length(); |
454 | } |
455 | |
456 | struct OlmPkSigning { |
457 | OlmErrorCode last_error; |
458 | _olm_ed25519_key_pair key_pair; |
459 | }; |
460 | |
461 | size_t olm_pk_signing_size(void) { |
462 | return sizeof(OlmPkSigning); |
463 | } |
464 | |
465 | OlmPkSigning *olm_pk_signing(void * memory) { |
466 | olm::unset(buffer: memory, buffer_length: sizeof(OlmPkSigning)); |
467 | return new(memory) OlmPkSigning; |
468 | } |
469 | |
470 | const char * olm_pk_signing_last_error(const OlmPkSigning * sign) { |
471 | auto error = sign->last_error; |
472 | return _olm_error_to_string(error); |
473 | } |
474 | |
475 | OlmErrorCode olm_pk_signing_last_error_code(const OlmPkSigning * sign) { |
476 | return sign->last_error; |
477 | } |
478 | |
479 | size_t olm_clear_pk_signing(OlmPkSigning *sign) { |
480 | /* Clear the memory backing the signing */ |
481 | olm::unset(buffer: sign, buffer_length: sizeof(OlmPkSigning)); |
482 | /* Initialise a fresh signing object in case someone tries to use it */ |
483 | new(sign) OlmPkSigning(); |
484 | return sizeof(OlmPkSigning); |
485 | } |
486 | |
487 | size_t olm_pk_signing_seed_length(void) { |
488 | return ED25519_RANDOM_LENGTH; |
489 | } |
490 | |
491 | size_t olm_pk_signing_public_key_length(void) { |
492 | return olm::encode_base64_length(ED25519_PUBLIC_KEY_LENGTH); |
493 | } |
494 | |
495 | size_t olm_pk_signing_key_from_seed( |
496 | OlmPkSigning * signing, |
497 | void * pubkey, size_t pubkey_length, |
498 | const void * seed, size_t seed_length |
499 | ) { |
500 | if (pubkey_length < olm_pk_signing_public_key_length()) { |
501 | signing->last_error = |
502 | OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL; |
503 | return std::size_t(-1); |
504 | } |
505 | if (seed_length < olm_pk_signing_seed_length()) { |
506 | signing->last_error = |
507 | OlmErrorCode::OLM_INPUT_BUFFER_TOO_SMALL; |
508 | return std::size_t(-1); |
509 | } |
510 | |
511 | _olm_crypto_ed25519_generate_key(random_bytes: (const uint8_t *) seed, output: &signing->key_pair); |
512 | olm::encode_base64( |
513 | input: (const uint8_t *)signing->key_pair.public_key.public_key, |
514 | ED25519_PUBLIC_KEY_LENGTH, |
515 | output: (uint8_t *)pubkey |
516 | ); |
517 | return 0; |
518 | } |
519 | |
520 | size_t olm_pk_signature_length(void) { |
521 | return olm::encode_base64_length(ED25519_SIGNATURE_LENGTH); |
522 | } |
523 | |
524 | size_t olm_pk_sign( |
525 | OlmPkSigning *signing, |
526 | uint8_t const * message, size_t message_length, |
527 | uint8_t * signature, size_t signature_length |
528 | ) { |
529 | if (signature_length < olm_pk_signature_length()) { |
530 | signing->last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL; |
531 | return std::size_t(-1); |
532 | } |
533 | uint8_t *raw_sig = signature + olm_pk_signature_length() - ED25519_SIGNATURE_LENGTH; |
534 | _olm_crypto_ed25519_sign( |
535 | our_key: &signing->key_pair, |
536 | message, message_length, output: raw_sig |
537 | ); |
538 | olm::encode_base64(input: raw_sig, ED25519_SIGNATURE_LENGTH, output: signature); |
539 | return olm_pk_signature_length(); |
540 | } |
541 | |
542 | } |
543 | |