pk.cpp 15.5 KB
Newer Older
1
/* Copyright 2018, 2019 New Vector Ltd
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "olm/pk.h"
#include "olm/cipher.h"
#include "olm/crypto.h"
#include "olm/ratchet.hh"
#include "olm/error.h"
#include "olm/memory.hh"
#include "olm/base64.hh"
22
23
#include "olm/pickle_encoding.h"
#include "olm/pickle.hh"
24
25
26

static const std::size_t MAC_LENGTH = 8;

27
const struct _olm_cipher_aes_sha_256 olm_pk_cipher_aes_sha256 =
28
29
30
31
    OLM_CIPHER_INIT_AES_SHA_256("");
const struct _olm_cipher *olm_pk_cipher =
    OLM_CIPHER_BASE(&olm_pk_cipher_aes_sha256);

32
33
extern "C" {

34
35
36
37
38
39
struct OlmPkEncryption {
    OlmErrorCode last_error;
    _olm_curve25519_public_key recipient_key;
};

const char * olm_pk_encryption_last_error(
40
    const OlmPkEncryption * encryption
41
42
43
44
45
) {
    auto error = encryption->last_error;
    return _olm_error_to_string(error);
}

46
47
48
49
50
51
OlmErrorCode olm_pk_encryption_last_error_code(
    const OlmPkEncryption * encryption
) {
    return encryption->last_error;
}

52
size_t olm_pk_encryption_size(void) {
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
    return sizeof(OlmPkEncryption);
}

OlmPkEncryption *olm_pk_encryption(
    void * memory
) {
    olm::unset(memory, sizeof(OlmPkEncryption));
    return new(memory) OlmPkEncryption;
}

size_t olm_clear_pk_encryption(
    OlmPkEncryption *encryption
) {
    /* Clear the memory backing the encryption */
    olm::unset(encryption, sizeof(OlmPkEncryption));
    /* Initialise a fresh encryption object in case someone tries to use it */
    new(encryption) OlmPkEncryption();
    return sizeof(OlmPkEncryption);
}

size_t olm_pk_encryption_set_recipient_key (
    OlmPkEncryption *encryption,
    void const * key, size_t key_length
) {
    if (key_length < olm_pk_key_length()) {
        encryption->last_error =
Hubert Chathi's avatar
Hubert Chathi committed
79
            OlmErrorCode::OLM_INPUT_BUFFER_TOO_SMALL;
80
81
        return std::size_t(-1);
    }
82

83
84
85
86
87
    olm::decode_base64(
        (const uint8_t*)key,
        olm_pk_key_length(),
        (uint8_t *)encryption->recipient_key.public_key
    );
88

89
90
91
92
    return 0;
}

size_t olm_pk_ciphertext_length(
93
    const OlmPkEncryption *encryption,
94
95
    size_t plaintext_length
) {
96
97
98
    return olm::encode_base64_length(
        _olm_cipher_aes_sha_256_ops.encrypt_ciphertext_length(olm_pk_cipher, plaintext_length)
    );
99
100
101
}

size_t olm_pk_mac_length(
102
    const OlmPkEncryption *encryption
103
104
105
106
107
) {
    return olm::encode_base64_length(_olm_cipher_aes_sha_256_ops.mac_length(olm_pk_cipher));
}

size_t olm_pk_encrypt_random_length(
108
    const OlmPkEncryption *encryption
109
110
111
112
113
114
115
116
117
118
) {
    return CURVE25519_KEY_LENGTH;
}

size_t olm_pk_encrypt(
    OlmPkEncryption *encryption,
    void const * plaintext, size_t plaintext_length,
    void * ciphertext, size_t ciphertext_length,
    void * mac, size_t mac_length,
    void * ephemeral_key, size_t ephemeral_key_size,
Hubert Chathi's avatar
Hubert Chathi committed
119
    const void * random, size_t random_length
120
121
122
) {
    if (ciphertext_length
            < 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
127
128
129
130
131
132
133
134
135
136
137
            < olm_pk_key_length()) {
        encryption->last_error =
            OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
        return std::size_t(-1);
    }
    if (random_length < olm_pk_encrypt_random_length(encryption)) {
        encryption->last_error =
            OlmErrorCode::OLM_NOT_ENOUGH_RANDOM;
        return std::size_t(-1);
    }

    _olm_curve25519_key_pair ephemeral_keypair;
Hubert Chathi's avatar
Hubert Chathi committed
138
    _olm_crypto_curve25519_generate_key((const uint8_t *) random, &ephemeral_keypair);
139
140
141
142
143
    olm::encode_base64(
        (const uint8_t *)ephemeral_keypair.public_key.public_key,
        CURVE25519_KEY_LENGTH,
        (uint8_t *)ephemeral_key
    );
144
145
146

    olm::SharedKey secret;
    _olm_crypto_curve25519_shared_secret(&ephemeral_keypair, &encryption->recipient_key, secret);
147
148
    size_t raw_ciphertext_length =
        _olm_cipher_aes_sha_256_ops.encrypt_ciphertext_length(olm_pk_cipher, plaintext_length);
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
    uint8_t *ciphertext_pos = (uint8_t *) ciphertext + ciphertext_length - raw_ciphertext_length;
    uint8_t raw_mac[MAC_LENGTH];
    size_t result = _olm_cipher_aes_sha_256_ops.encrypt(
        olm_pk_cipher,
        secret, sizeof(secret),
        (const uint8_t *) plaintext, plaintext_length,
        (uint8_t *) ciphertext_pos, raw_ciphertext_length,
        (uint8_t *) raw_mac, MAC_LENGTH
    );
    if (result != std::size_t(-1)) {
        olm::encode_base64(raw_mac, MAC_LENGTH, (uint8_t *)mac);
        olm::encode_base64(ciphertext_pos, raw_ciphertext_length, (uint8_t *)ciphertext);
    }
    return result;
}

struct OlmPkDecryption {
    OlmErrorCode last_error;
    _olm_curve25519_key_pair key_pair;
};

const char * olm_pk_decryption_last_error(
171
    const OlmPkDecryption * decryption
172
173
174
175
176
) {
    auto error = decryption->last_error;
    return _olm_error_to_string(error);
}

177
178
179
180
181
182
OlmErrorCode olm_pk_decryption_last_error_code(
    const OlmPkDecryption * decryption
) {
    return decryption->last_error;
}

183
size_t olm_pk_decryption_size(void) {
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
    return sizeof(OlmPkDecryption);
}

OlmPkDecryption *olm_pk_decryption(
    void * memory
) {
    olm::unset(memory, sizeof(OlmPkDecryption));
    return new(memory) OlmPkDecryption;
}

size_t olm_clear_pk_decryption(
    OlmPkDecryption *decryption
) {
    /* Clear the memory backing the decryption */
    olm::unset(decryption, sizeof(OlmPkDecryption));
    /* Initialise a fresh decryption object in case someone tries to use it */
    new(decryption) OlmPkDecryption();
    return sizeof(OlmPkDecryption);
}

204
size_t olm_pk_private_key_length(void) {
205
206
207
    return CURVE25519_KEY_LENGTH;
}

208
209
210
211
size_t olm_pk_generate_key_random_length(void) {
    return olm_pk_private_key_length();
}

212
size_t olm_pk_key_length(void) {
213
214
215
    return olm::encode_base64_length(CURVE25519_KEY_LENGTH);
}

216
size_t olm_pk_key_from_private(
217
218
    OlmPkDecryption * decryption,
    void * pubkey, size_t pubkey_length,
Hubert Chathi's avatar
Hubert Chathi committed
219
    const void * privkey, size_t privkey_length
220
) {
David Baker's avatar
David Baker committed
221
    if (pubkey_length < olm_pk_key_length()) {
222
223
224
225
        decryption->last_error =
            OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
        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
230
231
        return std::size_t(-1);
    }

Hubert Chathi's avatar
Hubert Chathi committed
232
    _olm_crypto_curve25519_generate_key((const uint8_t *) privkey, &decryption->key_pair);
233
234
235
236
237
    olm::encode_base64(
        (const uint8_t *)decryption->key_pair.public_key.public_key,
        CURVE25519_KEY_LENGTH,
        (uint8_t *)pubkey
    );
238
239
240
    return 0;
}

241
242
243
size_t olm_pk_generate_key(
    OlmPkDecryption * decryption,
    void * pubkey, size_t pubkey_length,
Hubert Chathi's avatar
Hubert Chathi committed
244
    const void * privkey, size_t privkey_length
245
246
247
248
) {
    return olm_pk_key_from_private(decryption, pubkey, pubkey_length, privkey, privkey_length);
}

249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
namespace {
    static const std::uint32_t PK_DECRYPTION_PICKLE_VERSION = 1;

    static std::size_t pickle_length(
        OlmPkDecryption const & value
    ) {
        std::size_t length = 0;
        length += olm::pickle_length(PK_DECRYPTION_PICKLE_VERSION);
        length += olm::pickle_length(value.key_pair);
        return length;
    }


    static std::uint8_t * pickle(
        std::uint8_t * pos,
        OlmPkDecryption const & value
    ) {
        pos = olm::pickle(pos, PK_DECRYPTION_PICKLE_VERSION);
        pos = olm::pickle(pos, value.key_pair);
        return pos;
    }


    static std::uint8_t const * unpickle(
        std::uint8_t const * pos, std::uint8_t const * end,
        OlmPkDecryption & value
    ) {
        uint32_t pickle_version;
Denis Kasak's avatar
Denis Kasak committed
277
        pos = olm::unpickle(pos, end, pickle_version); UNPICKLE_OK(pos);
278
279
280
281
282
283
284

        switch (pickle_version) {
        case 1:
            break;

        default:
            value.last_error = OlmErrorCode::OLM_UNKNOWN_PICKLE_VERSION;
Denis Kasak's avatar
Denis Kasak committed
285
            return nullptr;
286
287
        }

Denis Kasak's avatar
Denis Kasak committed
288
289
        pos = olm::unpickle(pos, end, value.key_pair); UNPICKLE_OK(pos);

290
291
292
293
294
        return pos;
    }
}

size_t olm_pickle_pk_decryption_length(
295
    const OlmPkDecryption * decryption
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
) {
    return _olm_enc_output_length(pickle_length(*decryption));
}

size_t olm_pickle_pk_decryption(
    OlmPkDecryption * decryption,
    void const * key, size_t key_length,
    void *pickled, size_t pickled_length
) {
    OlmPkDecryption & object = *decryption;
    std::size_t raw_length = pickle_length(object);
    if (pickled_length < _olm_enc_output_length(raw_length)) {
        object.last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
        return std::size_t(-1);
    }
    pickle(_olm_enc_output_pos(reinterpret_cast<std::uint8_t *>(pickled), raw_length), object);
312
313
314
315
    return _olm_enc_output(
        reinterpret_cast<std::uint8_t const *>(key), key_length,
        reinterpret_cast<std::uint8_t *>(pickled), raw_length
    );
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
}

size_t olm_unpickle_pk_decryption(
    OlmPkDecryption * decryption,
    void const * key, size_t key_length,
    void *pickled, size_t pickled_length,
    void *pubkey, size_t pubkey_length
) {
    OlmPkDecryption & object = *decryption;
    if (pubkey != NULL && pubkey_length < olm_pk_key_length()) {
        object.last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
        return std::size_t(-1);
    }
    std::uint8_t * const pos = reinterpret_cast<std::uint8_t *>(pickled);
    std::size_t raw_length = _olm_enc_input(
331
332
        reinterpret_cast<std::uint8_t const *>(key), key_length,
        pos, pickled_length, &object.last_error
333
334
335
336
337
    );
    if (raw_length == std::size_t(-1)) {
        return std::size_t(-1);
    }
    std::uint8_t * const end = pos + raw_length;
Denis Kasak's avatar
Denis Kasak committed
338
339

    if (!unpickle(pos, end, object)) {
340
341
342
343
344
345
        if (object.last_error == OlmErrorCode::OLM_SUCCESS) {
            object.last_error = OlmErrorCode::OLM_CORRUPTED_PICKLE;
        }
        return std::size_t(-1);
    }
    if (pubkey != NULL) {
346
347
348
349
350
        olm::encode_base64(
            (const uint8_t *)object.key_pair.public_key.public_key,
            CURVE25519_KEY_LENGTH,
            (uint8_t *)pubkey
        );
351
352
353
354
    }
    return pickled_length;
}

355
size_t olm_pk_max_plaintext_length(
356
    const OlmPkDecryption * decryption,
357
358
    size_t ciphertext_length
) {
359
360
361
    return _olm_cipher_aes_sha_256_ops.decrypt_max_plaintext_length(
        olm_pk_cipher, olm::decode_base64_length(ciphertext_length)
    );
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
}

size_t olm_pk_decrypt(
    OlmPkDecryption * decryption,
    void const * ephemeral_key, size_t ephemeral_key_length,
    void const * mac, size_t mac_length,
    void * ciphertext, size_t ciphertext_length,
    void * plaintext, size_t max_plaintext_length
) {
    if (max_plaintext_length
            < olm_pk_max_plaintext_length(decryption, ciphertext_length)) {
        decryption->last_error =
            OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
        return std::size_t(-1);
    }

378
379
380
381
382
383
384
385
386
    size_t raw_ciphertext_length = olm::decode_base64_length(ciphertext_length);

    if (ephemeral_key_length != olm::encode_base64_length(CURVE25519_KEY_LENGTH)
        || mac_length != olm::encode_base64_length(MAC_LENGTH)
        || raw_ciphertext_length == std::size_t(-1)) {
        decryption->last_error = OlmErrorCode::OLM_INVALID_BASE64;
        return std::size_t(-1);
    }

387
    struct _olm_curve25519_public_key ephemeral;
388
    olm::decode_base64(
389
390
        (const uint8_t*)ephemeral_key,
        olm::encode_base64_length(CURVE25519_KEY_LENGTH),
391
392
        (uint8_t *)ephemeral.public_key
    );
393

394
395
    olm::SharedKey secret;
    _olm_crypto_curve25519_shared_secret(&decryption->key_pair, &ephemeral, secret);
396

397
    uint8_t raw_mac[MAC_LENGTH];
398
399
400
401
402
403
404
405
406
407
408
409
    olm::decode_base64(
        (const uint8_t *)mac,
        olm::encode_base64_length(MAC_LENGTH),
        raw_mac
    );

    olm::decode_base64(
        (const uint8_t *)ciphertext,
        ciphertext_length,
        (uint8_t *)ciphertext
    );

410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
    size_t result = _olm_cipher_aes_sha_256_ops.decrypt(
        olm_pk_cipher,
        secret, sizeof(secret),
        (uint8_t *) raw_mac, MAC_LENGTH,
        (const uint8_t *) ciphertext, raw_ciphertext_length,
        (uint8_t *) plaintext, max_plaintext_length
    );
    if (result == std::size_t(-1)) {
        // we already checked the buffer sizes, so the only error that decrypt
        // will return is if the MAC is incorrect
        decryption->last_error =
            OlmErrorCode::OLM_BAD_MESSAGE_MAC;
        return std::size_t(-1);
    } else {
        return result;
    }
}

428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
size_t olm_pk_get_private_key(
    OlmPkDecryption * decryption,
    void *private_key, size_t private_key_length
) {
    if (private_key_length < olm_pk_private_key_length()) {
        decryption->last_error =
            OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
        return std::size_t(-1);
    }
    std::memcpy(
        private_key,
        decryption->key_pair.private_key.private_key,
        olm_pk_private_key_length()
    );
    return olm_pk_private_key_length();
}

445
446
447
448
449
450
451
452
453
454
455
456
457
458
struct OlmPkSigning {
    OlmErrorCode last_error;
    _olm_ed25519_key_pair key_pair;
};

size_t olm_pk_signing_size(void) {
    return sizeof(OlmPkSigning);
}

OlmPkSigning *olm_pk_signing(void * memory) {
    olm::unset(memory, sizeof(OlmPkSigning));
    return new(memory) OlmPkSigning;
}

459
const char * olm_pk_signing_last_error(const OlmPkSigning * sign) {
460
461
462
463
    auto error = sign->last_error;
    return _olm_error_to_string(error);
}

464
465
466
467
OlmErrorCode olm_pk_signing_last_error_code(const OlmPkSigning * sign) {
    return sign->last_error;
}

468
469
470
471
472
473
474
475
size_t olm_clear_pk_signing(OlmPkSigning *sign) {
    /* Clear the memory backing the signing */
    olm::unset(sign, sizeof(OlmPkSigning));
    /* Initialise a fresh signing object in case someone tries to use it */
    new(sign) OlmPkSigning();
    return sizeof(OlmPkSigning);
}

476
size_t olm_pk_signing_seed_length(void) {
477
478
479
    return ED25519_RANDOM_LENGTH;
}

480
size_t olm_pk_signing_public_key_length(void) {
481
482
483
484
485
486
    return olm::encode_base64_length(ED25519_PUBLIC_KEY_LENGTH);
}

size_t olm_pk_signing_key_from_seed(
    OlmPkSigning * signing,
    void * pubkey, size_t pubkey_length,
Hubert Chathi's avatar
Hubert Chathi committed
487
    const void * seed, size_t seed_length
488
) {
489
    if (pubkey_length < olm_pk_signing_public_key_length()) {
490
491
492
493
        signing->last_error =
            OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
        return std::size_t(-1);
    }
494
    if (seed_length < olm_pk_signing_seed_length()) {
495
496
497
498
499
        signing->last_error =
            OlmErrorCode::OLM_INPUT_BUFFER_TOO_SMALL;
        return std::size_t(-1);
    }

Hubert Chathi's avatar
Hubert Chathi committed
500
    _olm_crypto_ed25519_generate_key((const uint8_t *) seed, &signing->key_pair);
501
502
503
504
505
506
507
508
    olm::encode_base64(
        (const uint8_t *)signing->key_pair.public_key.public_key,
        ED25519_PUBLIC_KEY_LENGTH,
        (uint8_t *)pubkey
    );
    return 0;
}

Hubert Chathi's avatar
Hubert Chathi committed
509
size_t olm_pk_signature_length(void) {
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
    return olm::encode_base64_length(ED25519_SIGNATURE_LENGTH);
}

size_t olm_pk_sign(
    OlmPkSigning *signing,
    uint8_t const * message, size_t message_length,
    uint8_t * signature, size_t signature_length
) {
    if (signature_length < olm_pk_signature_length()) {
        signing->last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
        return std::size_t(-1);
    }
    uint8_t *raw_sig = signature + olm_pk_signature_length() - ED25519_SIGNATURE_LENGTH;
    _olm_crypto_ed25519_sign(
        &signing->key_pair,
        message, message_length, raw_sig
    );
    olm::encode_base64(raw_sig, ED25519_SIGNATURE_LENGTH, signature);
    return olm_pk_signature_length();
}

531
}