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
25static const std::size_t MAC_LENGTH = 8;
26
27const struct _olm_cipher_aes_sha_256 olm_pk_cipher_aes_sha256 =
28 OLM_CIPHER_INIT_AES_SHA_256("");
29const struct _olm_cipher *olm_pk_cipher =
30 OLM_CIPHER_BASE(&olm_pk_cipher_aes_sha256);
31
32extern "C" {
33
34struct OlmPkEncryption {
35 OlmErrorCode last_error;
36 _olm_curve25519_public_key recipient_key;
37};
38
39const 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
46OlmErrorCode olm_pk_encryption_last_error_code(
47 const OlmPkEncryption * encryption
48) {
49 return encryption->last_error;
50}
51
52size_t olm_pk_encryption_size(void) {
53 return sizeof(OlmPkEncryption);
54}
55
56OlmPkEncryption *olm_pk_encryption(
57 void * memory
58) {
59 olm::unset(buffer: memory, buffer_length: sizeof(OlmPkEncryption));
60 return new(memory) OlmPkEncryption;
61}
62
63size_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
73size_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
92size_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
101size_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
107size_t olm_pk_encrypt_random_length(
108 const OlmPkEncryption *encryption
109) {
110 return CURVE25519_KEY_LENGTH;
111}
112
113size_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
165struct OlmPkDecryption {
166 OlmErrorCode last_error;
167 _olm_curve25519_key_pair key_pair;
168};
169
170const 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
177OlmErrorCode olm_pk_decryption_last_error_code(
178 const OlmPkDecryption * decryption
179) {
180 return decryption->last_error;
181}
182
183size_t olm_pk_decryption_size(void) {
184 return sizeof(OlmPkDecryption);
185}
186
187OlmPkDecryption *olm_pk_decryption(
188 void * memory
189) {
190 olm::unset(buffer: memory, buffer_length: sizeof(OlmPkDecryption));
191 return new(memory) OlmPkDecryption;
192}
193
194size_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
204size_t olm_pk_private_key_length(void) {
205 return CURVE25519_KEY_LENGTH;
206}
207
208size_t olm_pk_generate_key_random_length(void) {
209 return olm_pk_private_key_length();
210}
211
212size_t olm_pk_key_length(void) {
213 return olm::encode_base64_length(CURVE25519_KEY_LENGTH);
214}
215
216size_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
241size_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
249namespace {
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
294size_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
300size_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
318size_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
366size_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
375size_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
439size_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
456struct OlmPkSigning {
457 OlmErrorCode last_error;
458 _olm_ed25519_key_pair key_pair;
459};
460
461size_t olm_pk_signing_size(void) {
462 return sizeof(OlmPkSigning);
463}
464
465OlmPkSigning *olm_pk_signing(void * memory) {
466 olm::unset(buffer: memory, buffer_length: sizeof(OlmPkSigning));
467 return new(memory) OlmPkSigning;
468}
469
470const char * olm_pk_signing_last_error(const OlmPkSigning * sign) {
471 auto error = sign->last_error;
472 return _olm_error_to_string(error);
473}
474
475OlmErrorCode olm_pk_signing_last_error_code(const OlmPkSigning * sign) {
476 return sign->last_error;
477}
478
479size_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
487size_t olm_pk_signing_seed_length(void) {
488 return ED25519_RANDOM_LENGTH;
489}
490
491size_t olm_pk_signing_public_key_length(void) {
492 return olm::encode_base64_length(ED25519_PUBLIC_KEY_LENGTH);
493}
494
495size_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
520size_t olm_pk_signature_length(void) {
521 return olm::encode_base64_length(ED25519_SIGNATURE_LENGTH);
522}
523
524size_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