1 /* Copyright 2018, 2019 New Vector Ltd
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
7 * http://www.apache.org/licenses/LICENSE-2.0
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.
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"
25 static const std::size_t MAC_LENGTH = 8;
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);
34 struct OlmPkEncryption {
35 OlmErrorCode last_error;
36 _olm_curve25519_public_key recipient_key;
39 const char * olm_pk_encryption_last_error(
40 const OlmPkEncryption * encryption
42 auto error = encryption->last_error;
43 return _olm_error_to_string(error);
46 OlmErrorCode olm_pk_encryption_last_error_code(
47 const OlmPkEncryption * encryption
49 return encryption->last_error;
52 size_t olm_pk_encryption_size(void) {
53 return sizeof(OlmPkEncryption);
56 OlmPkEncryption *olm_pk_encryption(
59 olm::unset(memory, sizeof(OlmPkEncryption));
60 return new(memory) OlmPkEncryption;
63 size_t olm_clear_pk_encryption(
64 OlmPkEncryption *encryption
66 /* Clear the memory backing the encryption */
67 olm::unset(encryption, sizeof(OlmPkEncryption));
68 /* Initialise a fresh encryption object in case someone tries to use it */
69 new(encryption) OlmPkEncryption();
70 return sizeof(OlmPkEncryption);
73 size_t olm_pk_encryption_set_recipient_key (
74 OlmPkEncryption *encryption,
75 void const * key, size_t key_length
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);
86 (uint8_t *)encryption->recipient_key.public_key
92 size_t olm_pk_ciphertext_length(
93 const OlmPkEncryption *encryption,
94 size_t plaintext_length
96 return olm::encode_base64_length(
97 _olm_cipher_aes_sha_256_ops.encrypt_ciphertext_length(olm_pk_cipher, plaintext_length)
101 size_t olm_pk_mac_length(
102 const OlmPkEncryption *encryption
104 return olm::encode_base64_length(_olm_cipher_aes_sha_256_ops.mac_length(olm_pk_cipher));
107 size_t olm_pk_encrypt_random_length(
108 const OlmPkEncryption *encryption
110 return CURVE25519_KEY_LENGTH;
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
121 if (ciphertext_length
122 < olm_pk_ciphertext_length(encryption, plaintext_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);
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);
137 _olm_curve25519_key_pair ephemeral_keypair;
138 _olm_crypto_curve25519_generate_key((const uint8_t *) random, &ephemeral_keypair);
140 (const uint8_t *)ephemeral_keypair.public_key.public_key,
141 CURVE25519_KEY_LENGTH,
142 (uint8_t *)ephemeral_key
145 olm::SharedKey secret;
146 _olm_crypto_curve25519_shared_secret(&ephemeral_keypair, &encryption->recipient_key, 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(
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
158 if (result != std::size_t(-1)) {
159 olm::encode_base64(raw_mac, MAC_LENGTH, (uint8_t *)mac);
160 olm::encode_base64(ciphertext_pos, raw_ciphertext_length, (uint8_t *)ciphertext);
165 struct OlmPkDecryption {
166 OlmErrorCode last_error;
167 _olm_curve25519_key_pair key_pair;
170 const char * olm_pk_decryption_last_error(
171 const OlmPkDecryption * decryption
173 auto error = decryption->last_error;
174 return _olm_error_to_string(error);
177 OlmErrorCode olm_pk_decryption_last_error_code(
178 const OlmPkDecryption * decryption
180 return decryption->last_error;
183 size_t olm_pk_decryption_size(void) {
184 return sizeof(OlmPkDecryption);
187 OlmPkDecryption *olm_pk_decryption(
190 olm::unset(memory, sizeof(OlmPkDecryption));
191 return new(memory) OlmPkDecryption;
194 size_t olm_clear_pk_decryption(
195 OlmPkDecryption *decryption
197 /* Clear the memory backing the decryption */
198 olm::unset(decryption, sizeof(OlmPkDecryption));
199 /* Initialise a fresh decryption object in case someone tries to use it */
200 new(decryption) OlmPkDecryption();
201 return sizeof(OlmPkDecryption);
204 size_t olm_pk_private_key_length(void) {
205 return CURVE25519_KEY_LENGTH;
208 size_t olm_pk_generate_key_random_length(void) {
209 return olm_pk_private_key_length();
212 size_t olm_pk_key_length(void) {
213 return olm::encode_base64_length(CURVE25519_KEY_LENGTH);
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
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);
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);
232 _olm_crypto_curve25519_generate_key((const uint8_t *) privkey, &decryption->key_pair);
234 (const uint8_t *)decryption->key_pair.public_key.public_key,
235 CURVE25519_KEY_LENGTH,
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
246 return olm_pk_key_from_private(decryption, pubkey, pubkey_length, privkey, privkey_length);
250 static const std::uint32_t PK_DECRYPTION_PICKLE_VERSION = 1;
252 static std::size_t pickle_length(
253 OlmPkDecryption const & value
255 std::size_t length = 0;
256 length += olm::pickle_length(PK_DECRYPTION_PICKLE_VERSION);
257 length += olm::pickle_length(value.key_pair);
262 static std::uint8_t * pickle(
264 OlmPkDecryption const & value
266 pos = olm::pickle(pos, PK_DECRYPTION_PICKLE_VERSION);
267 pos = olm::pickle(pos, value.key_pair);
272 static std::uint8_t const * unpickle(
273 std::uint8_t const * pos, std::uint8_t const * end,
274 OlmPkDecryption & value
276 uint32_t pickle_version;
277 pos = olm::unpickle(pos, end, pickle_version); UNPICKLE_OK(pos);
279 switch (pickle_version) {
284 value.last_error = OlmErrorCode::OLM_UNKNOWN_PICKLE_VERSION;
288 pos = olm::unpickle(pos, end, value.key_pair); UNPICKLE_OK(pos);
294 size_t olm_pickle_pk_decryption_length(
295 const OlmPkDecryption * decryption
297 return _olm_enc_output_length(pickle_length(*decryption));
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
305 OlmPkDecryption & object = *decryption;
306 std::size_t raw_length = pickle_length(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);
311 pickle(_olm_enc_output_pos(reinterpret_cast<std::uint8_t *>(pickled), raw_length), object);
312 return _olm_enc_output(
313 reinterpret_cast<std::uint8_t const *>(key), key_length,
314 reinterpret_cast<std::uint8_t *>(pickled), raw_length
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
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);
329 std::uint8_t * const input = reinterpret_cast<std::uint8_t *>(pickled);
330 std::size_t raw_length = _olm_enc_input(
331 reinterpret_cast<std::uint8_t const *>(key), key_length,
332 input, pickled_length, &object.last_error
334 if (raw_length == std::size_t(-1)) {
335 return std::size_t(-1);
338 std::uint8_t const * pos = input;
339 std::uint8_t const * end = pos + raw_length;
341 pos = unpickle(pos, end, object);
344 /* Input was corrupted. */
345 if (object.last_error == OlmErrorCode::OLM_SUCCESS) {
346 object.last_error = OlmErrorCode::OLM_CORRUPTED_PICKLE;
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);
355 if (pubkey != NULL) {
357 (const uint8_t *)object.key_pair.public_key.public_key,
358 CURVE25519_KEY_LENGTH,
363 return pickled_length;
366 size_t olm_pk_max_plaintext_length(
367 const OlmPkDecryption * decryption,
368 size_t ciphertext_length
370 return _olm_cipher_aes_sha_256_ops.decrypt_max_plaintext_length(
371 olm_pk_cipher, olm::decode_base64_length(ciphertext_length)
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
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);
389 size_t raw_ciphertext_length = olm::decode_base64_length(ciphertext_length);
391 if (ephemeral_key_length != olm::encode_base64_length(CURVE25519_KEY_LENGTH)
392 || mac_length != olm::encode_base64_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);
398 struct _olm_curve25519_public_key ephemeral;
400 (const uint8_t*)ephemeral_key,
401 olm::encode_base64_length(CURVE25519_KEY_LENGTH),
402 (uint8_t *)ephemeral.public_key
405 olm::SharedKey secret;
406 _olm_crypto_curve25519_shared_secret(&decryption->key_pair, &ephemeral, secret);
408 uint8_t raw_mac[MAC_LENGTH];
410 (const uint8_t *)mac,
411 olm::encode_base64_length(MAC_LENGTH),
416 (const uint8_t *)ciphertext,
418 (uint8_t *)ciphertext
421 size_t result = _olm_cipher_aes_sha_256_ops.decrypt(
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
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);
439 size_t olm_pk_get_private_key(
440 OlmPkDecryption * decryption,
441 void *private_key, size_t private_key_length
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);
450 decryption->key_pair.private_key.private_key,
451 olm_pk_private_key_length()
453 return olm_pk_private_key_length();
456 struct OlmPkSigning {
457 OlmErrorCode last_error;
458 _olm_ed25519_key_pair key_pair;
461 size_t olm_pk_signing_size(void) {
462 return sizeof(OlmPkSigning);
465 OlmPkSigning *olm_pk_signing(void * memory) {
466 olm::unset(memory, sizeof(OlmPkSigning));
467 return new(memory) OlmPkSigning;
470 const char * olm_pk_signing_last_error(const OlmPkSigning * sign) {
471 auto error = sign->last_error;
472 return _olm_error_to_string(error);
475 OlmErrorCode olm_pk_signing_last_error_code(const OlmPkSigning * sign) {
476 return sign->last_error;
479 size_t olm_clear_pk_signing(OlmPkSigning *sign) {
480 /* Clear the memory backing the signing */
481 olm::unset(sign, sizeof(OlmPkSigning));
482 /* Initialise a fresh signing object in case someone tries to use it */
483 new(sign) OlmPkSigning();
484 return sizeof(OlmPkSigning);
487 size_t olm_pk_signing_seed_length(void) {
488 return ED25519_RANDOM_LENGTH;
491 size_t olm_pk_signing_public_key_length(void) {
492 return olm::encode_base64_length(ED25519_PUBLIC_KEY_LENGTH);
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
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);
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);
511 _olm_crypto_ed25519_generate_key((const uint8_t *) seed, &signing->key_pair);
513 (const uint8_t *)signing->key_pair.public_key.public_key,
514 ED25519_PUBLIC_KEY_LENGTH,
520 size_t olm_pk_signature_length(void) {
521 return olm::encode_base64_length(ED25519_SIGNATURE_LENGTH);
525 OlmPkSigning *signing,
526 uint8_t const * message, size_t message_length,
527 uint8_t * signature, size_t signature_length
529 if (signature_length < olm_pk_signature_length()) {
530 signing->last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
531 return std::size_t(-1);
533 uint8_t *raw_sig = signature + olm_pk_signature_length() - ED25519_SIGNATURE_LENGTH;
534 _olm_crypto_ed25519_sign(
536 message, message_length, raw_sig
538 olm::encode_base64(raw_sig, ED25519_SIGNATURE_LENGTH, signature);
539 return olm_pk_signature_length();