olm.cpp 19.8 KB
Newer Older
Mark Haines's avatar
Mark Haines committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/* Copyright 2015 OpenMarket Ltd
 *
 * 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.
 */
Richard van der Hoff's avatar
Richard van der Hoff committed
15
#include "olm/olm.h"
16
17
#include "olm/session.hh"
#include "olm/account.hh"
18
#include "olm/cipher.h"
19
#include "olm/pickle_encoding.h"
20
#include "olm/utility.hh"
21
#include "olm/base64.hh"
22
#include "olm/memory.hh"
23
24
25
26
27
28

#include <new>
#include <cstring>

namespace {

29
30
static OlmAccount * to_c(olm::Account * account) {
    return reinterpret_cast<OlmAccount *>(account);
31
32
}

33
34
35
36
37
38
static OlmSession * to_c(olm::Session * session) {
    return reinterpret_cast<OlmSession *>(session);
}

static OlmUtility * to_c(olm::Utility * utility) {
    return reinterpret_cast<OlmUtility *>(utility);
39
40
}

41
42
static olm::Account * from_c(OlmAccount * account) {
    return reinterpret_cast<olm::Account *>(account);
43
44
}

45
46
47
48
49
50
static olm::Session * from_c(OlmSession * session) {
    return reinterpret_cast<olm::Session *>(session);
}

static olm::Utility * from_c(OlmUtility * utility) {
    return reinterpret_cast<olm::Utility *>(utility);
51
52
53
54
55
56
57
58
59
60
61
62
63
}

static std::uint8_t * from_c(void * bytes) {
    return reinterpret_cast<std::uint8_t *>(bytes);
}

static std::uint8_t const * from_c(void const * bytes) {
    return reinterpret_cast<std::uint8_t const *>(bytes);
}

std::size_t b64_output_length(
    size_t raw_length
) {
64
    return olm::encode_base64_length(raw_length);
65
66
67
68
69
70
}

std::uint8_t * b64_output_pos(
    std::uint8_t * output,
    size_t raw_length
) {
71
    return output + olm::encode_base64_length(raw_length) - raw_length;
72
73
74
75
76
}

std::size_t b64_output(
    std::uint8_t * output, size_t raw_length
) {
77
    std::size_t base64_length = olm::encode_base64_length(raw_length);
78
    std::uint8_t * raw_output = output + base64_length - raw_length;
79
    olm::encode_base64(raw_output, raw_length, output);
80
81
82
83
84
    return base64_length;
}

std::size_t b64_input(
    std::uint8_t * input, size_t b64_length,
85
    OlmErrorCode & last_error
86
) {
87
    std::size_t raw_length = olm::decode_base64_length(b64_length);
88
    if (raw_length == std::size_t(-1)) {
89
        last_error = OlmErrorCode::OLM_INVALID_BASE64;
90
91
        return std::size_t(-1);
    }
92
    olm::decode_base64(input, b64_length, input);
93
94
95
96
97
98
99
100
    return raw_length;
}

} // namespace


extern "C" {

101
102
103
104
105
void olm_get_library_version(uint8_t *major, uint8_t *minor, uint8_t *patch) {
    if (major != NULL) *major = OLMLIB_VERSION_MAJOR;
    if (minor != NULL) *minor = OLMLIB_VERSION_MINOR;
    if (patch != NULL) *patch = OLMLIB_VERSION_PATCH;
}
106

107
size_t olm_error() {
108
109
110
111
    return std::size_t(-1);
}


112
const char * olm_account_last_error(
113
    OlmAccount * account
114
) {
115
116
    auto error = from_c(account)->last_error;
    return _olm_error_to_string(error);
117
118
119
}


120
121
const char * olm_session_last_error(
    OlmSession * session
122
) {
123
124
    auto error = from_c(session)->last_error;
    return _olm_error_to_string(error);
125
126
}

127
128
129
const char * olm_utility_last_error(
    OlmUtility * utility
) {
130
131
    auto error = from_c(utility)->last_error;
    return _olm_error_to_string(error);
132
}
133

134
135
size_t olm_account_size() {
    return sizeof(olm::Account);
136
137
138
}


139
140
size_t olm_session_size() {
    return sizeof(olm::Session);
141
142
}

143
144
145
size_t olm_utility_size() {
    return sizeof(olm::Utility);
}
146

147
OlmAccount * olm_account(
148
149
    void * memory
) {
150
    olm::unset(memory, sizeof(olm::Account));
151
    return to_c(new(memory) olm::Account());
152
153
154
}


155
OlmSession * olm_session(
156
157
    void * memory
) {
158
    olm::unset(memory, sizeof(olm::Session));
159
    return to_c(new(memory) olm::Session());
160
161
162
}


163
164
165
166
167
168
169
170
OlmUtility * olm_utility(
    void * memory
) {
    olm::unset(memory, sizeof(olm::Utility));
    return to_c(new(memory) olm::Utility());
}


171
size_t olm_clear_account(
172
    OlmAccount * account
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
) {
    /* Clear the memory backing the account  */
    olm::unset(account, sizeof(olm::Account));
    /* Initialise a fresh account object in case someone tries to use it */
    new(account) olm::Account();
    return sizeof(olm::Account);
}


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


193
194
195
196
197
198
199
200
201
202
203
size_t olm_clear_utility(
    OlmUtility * utility
) {
    /* Clear the memory backing the session */
    olm::unset(utility, sizeof(olm::Utility));
    /* Initialise a fresh session object in case someone tries to use it */
    new(utility) olm::Utility();
    return sizeof(olm::Utility);
}


204
205
size_t olm_pickle_account_length(
    OlmAccount * account
206
) {
207
    return _olm_enc_output_length(pickle_length(*from_c(account)));
208
209
210
}


211
212
size_t olm_pickle_session_length(
    OlmSession * session
213
) {
214
    return _olm_enc_output_length(pickle_length(*from_c(session)));
215
216
217
}


218
219
size_t olm_pickle_account(
    OlmAccount * account,
220
221
222
    void const * key, size_t key_length,
    void * pickled, size_t pickled_length
) {
223
    olm::Account & object = *from_c(account);
224
    std::size_t raw_length = pickle_length(object);
225
    if (pickled_length < _olm_enc_output_length(raw_length)) {
226
        object.last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
227
228
        return size_t(-1);
    }
229
230
    pickle(_olm_enc_output_pos(from_c(pickled), raw_length), object);
    return _olm_enc_output(from_c(key), key_length, from_c(pickled), raw_length);
231
232
233
}


234
235
size_t olm_pickle_session(
    OlmSession * session,
236
237
238
    void const * key, size_t key_length,
    void * pickled, size_t pickled_length
) {
239
    olm::Session & object = *from_c(session);
240
    std::size_t raw_length = pickle_length(object);
241
    if (pickled_length < _olm_enc_output_length(raw_length)) {
242
        object.last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
243
244
        return size_t(-1);
    }
245
246
    pickle(_olm_enc_output_pos(from_c(pickled), raw_length), object);
    return _olm_enc_output(from_c(key), key_length, from_c(pickled), raw_length);
247
248
249
}


250
251
size_t olm_unpickle_account(
    OlmAccount * account,
252
253
254
    void const * key, size_t key_length,
    void * pickled, size_t pickled_length
) {
255
    olm::Account & object = *from_c(account);
256
    std::uint8_t * const pos = from_c(pickled);
257
258
    std::size_t raw_length = _olm_enc_input(
        from_c(key), key_length, pos, pickled_length, &object.last_error
259
260
261
262
263
    );
    if (raw_length == std::size_t(-1)) {
        return std::size_t(-1);
    }
    std::uint8_t * const end = pos + raw_length;
264
265
266
267
268
    /* On success unpickle will return (pos + raw_length). If unpickling
     * terminates too soon then it will return a pointer before
     * (pos + raw_length). On error unpickle will return (pos + raw_length + 1).
     */
    if (end != unpickle(pos, end + 1, object)) {
269
270
        if (object.last_error == OlmErrorCode::OLM_SUCCESS) {
            object.last_error = OlmErrorCode::OLM_CORRUPTED_PICKLE;
271
272
273
        }
        return std::size_t(-1);
    }
274
275
276
277
    return pickled_length;
}


278
279
size_t olm_unpickle_session(
    OlmSession * session,
280
281
282
    void const * key, size_t key_length,
    void * pickled, size_t pickled_length
) {
283
    olm::Session & object = *from_c(session);
284
    std::uint8_t * const pos = from_c(pickled);
285
286
    std::size_t raw_length = _olm_enc_input(
        from_c(key), key_length, pos, pickled_length, &object.last_error
287
288
289
290
    );
    if (raw_length == std::size_t(-1)) {
        return std::size_t(-1);
    }
291

292
    std::uint8_t * const end = pos + raw_length;
293
294
295
296
297
    /* On success unpickle will return (pos + raw_length). If unpickling
     * terminates too soon then it will return a pointer before
     * (pos + raw_length). On error unpickle will return (pos + raw_length + 1).
     */
    if (end != unpickle(pos, end + 1, object)) {
298
299
        if (object.last_error == OlmErrorCode::OLM_SUCCESS) {
            object.last_error = OlmErrorCode::OLM_CORRUPTED_PICKLE;
300
301
302
        }
        return std::size_t(-1);
    }
303
304
305
306
    return pickled_length;
}


307
308
size_t olm_create_account_random_length(
    OlmAccount * account
309
310
311
312
313
) {
    return from_c(account)->new_account_random_length();
}


314
315
size_t olm_create_account(
    OlmAccount * account,
316
    void * random, size_t random_length
317
) {
318
319
320
    size_t result = from_c(account)->new_account(from_c(random), random_length);
    olm::unset(random, random_length);
    return result;
321
322
}

323

324
size_t olm_account_identity_keys_length(
325
326
327
    OlmAccount * account
) {
    return from_c(account)->get_identity_json_length();
328
329
}

330

331
332
size_t olm_account_identity_keys(
    OlmAccount * account,
333
334
    void * identity_keys, size_t identity_key_length
) {
335
336
337
    return from_c(account)->get_identity_json(
        from_c(identity_keys), identity_key_length
    );
338
339
340
}


341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
size_t olm_account_signature_length(
    OlmAccount * account
) {
    return b64_output_length(from_c(account)->signature_length());
}


size_t olm_account_sign(
    OlmAccount * account,
    void const * message, size_t message_length,
    void * signature, size_t signature_length
) {
    std::size_t raw_length = from_c(account)->signature_length();
    if (signature_length < b64_output_length(raw_length)) {
        from_c(account)->last_error =
356
            OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
357
358
359
360
361
362
363
364
365
366
        return std::size_t(-1);
    }
    from_c(account)->sign(
         from_c(message), message_length,
         b64_output_pos(from_c(signature), raw_length), raw_length
    );
    return b64_output(from_c(signature), raw_length);
}


367
368
size_t olm_account_one_time_keys_length(
    OlmAccount * account
369
) {
370
    return from_c(account)->get_one_time_keys_json_length();
371
372
373
}


374
375
size_t olm_account_one_time_keys(
    OlmAccount * account,
376
    void * one_time_keys_json, size_t one_time_key_json_length
377
) {
378
379
380
    return from_c(account)->get_one_time_keys_json(
        from_c(one_time_keys_json), one_time_key_json_length
    );
381
382
383
}


384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
size_t olm_account_mark_keys_as_published(
    OlmAccount * account
) {
    return from_c(account)->mark_keys_as_published();
}


size_t olm_account_max_number_of_one_time_keys(
    OlmAccount * account
) {
    return from_c(account)->max_number_of_one_time_keys();
}


size_t olm_account_generate_one_time_keys_random_length(
    OlmAccount * account,
    size_t number_of_keys
) {
    return from_c(account)->generate_one_time_keys_random_length(number_of_keys);
}


size_t olm_account_generate_one_time_keys(
    OlmAccount * account,
    size_t number_of_keys,
409
    void * random, size_t random_length
410
) {
411
    size_t result = from_c(account)->generate_one_time_keys(
412
413
414
        number_of_keys,
        from_c(random), random_length
    );
415
416
    olm::unset(random, random_length);
    return result;
417
418
419
}


420
421
size_t olm_create_outbound_session_random_length(
    OlmSession * session
Mark Haines's avatar
Mark Haines committed
422
423
424
425
) {
    return from_c(session)->new_outbound_session_random_length();
}

426

427
428
429
size_t olm_create_outbound_session(
    OlmSession * session,
    OlmAccount * account,
430
431
    void const * their_identity_key, size_t their_identity_key_length,
    void const * their_one_time_key, size_t their_one_time_key_length,
432
    void * random, size_t random_length
433
) {
434
435
436
437
438
439
440
    std::uint8_t const * id_key = from_c(their_identity_key);
    std::uint8_t const * ot_key = from_c(their_one_time_key);
    std::size_t id_key_length = their_identity_key_length;
    std::size_t ot_key_length = their_one_time_key_length;

    if (olm::decode_base64_length(id_key_length) != olm::KEY_LENGTH
            || olm::decode_base64_length(ot_key_length) != olm::KEY_LENGTH
441
    ) {
442
        from_c(session)->last_error = OlmErrorCode::OLM_INVALID_BASE64;
443
444
        return std::size_t(-1);
    }
445
    olm::Curve25519PublicKey identity_key;
446
    olm::Curve25519PublicKey one_time_key;
447

448
449
    olm::decode_base64(id_key, id_key_length, identity_key.public_key);
    olm::decode_base64(ot_key, ot_key_length, one_time_key.public_key);
450

451
    size_t result = from_c(session)->new_outbound_session(
452
453
454
        *from_c(account), identity_key, one_time_key,
        from_c(random), random_length
    );
455
456
    olm::unset(random, random_length);
    return result;
457
458
459
}


460
461
462
size_t olm_create_inbound_session(
    OlmSession * session,
    OlmAccount * account,
463
464
465
466
467
468
469
470
471
    void * one_time_key_message, size_t message_length
) {
    std::size_t raw_length = b64_input(
        from_c(one_time_key_message), message_length, from_c(session)->last_error
    );
    if (raw_length == std::size_t(-1)) {
        return std::size_t(-1);
    }
    return from_c(session)->new_inbound_session(
472
473
474
475
476
477
478
479
480
481
482
        *from_c(account), nullptr, from_c(one_time_key_message), raw_length
    );
}


size_t olm_create_inbound_session_from(
    OlmSession * session,
    OlmAccount * account,
    void const * their_identity_key, size_t their_identity_key_length,
    void * one_time_key_message, size_t message_length
) {
483
484
485
486
    std::uint8_t const * id_key = from_c(their_identity_key);
    std::size_t id_key_length = their_identity_key_length;

    if (olm::decode_base64_length(id_key_length) != olm::KEY_LENGTH) {
487
        from_c(session)->last_error = OlmErrorCode::OLM_INVALID_BASE64;
488
489
490
        return std::size_t(-1);
    }
    olm::Curve25519PublicKey identity_key;
491
    olm::decode_base64(id_key, id_key_length, identity_key.public_key);
492
493
494
495
496
497
498
499
500
501

    std::size_t raw_length = b64_input(
        from_c(one_time_key_message), message_length, from_c(session)->last_error
    );
    if (raw_length == std::size_t(-1)) {
        return std::size_t(-1);
    }
    return from_c(session)->new_inbound_session(
        *from_c(account), &identity_key,
        from_c(one_time_key_message), raw_length
502
503
504
505
    );
}


506
507
508
509
510
511
512
513
514
515
516
517
518
size_t olm_session_id_length(
    OlmSession * session
) {
    return b64_output_length(from_c(session)->session_id_length());
}

size_t olm_session_id(
    OlmSession * session,
    void * id, size_t id_length
) {
    std::size_t raw_length = from_c(session)->session_id_length();
    if (id_length < b64_output_length(raw_length)) {
        from_c(session)->last_error =
519
                OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
520
521
522
523
524
525
526
527
528
529
530
531
        return std::size_t(-1);
    }
    std::size_t result = from_c(session)->session_id(
       b64_output_pos(from_c(id), raw_length), raw_length
    );
    if (result == std::size_t(-1)) {
        return result;
    }
    return b64_output(from_c(id), raw_length);
}


532
533
size_t olm_matches_inbound_session(
    OlmSession * session,
534
535
536
537
538
539
540
541
542
    void * one_time_key_message, size_t message_length
) {
    std::size_t raw_length = b64_input(
        from_c(one_time_key_message), message_length, from_c(session)->last_error
    );
    if (raw_length == std::size_t(-1)) {
        return std::size_t(-1);
    }
    bool matches = from_c(session)->matches_inbound_session(
543
544
545
546
547
548
549
550
551
552
553
        nullptr, from_c(one_time_key_message), raw_length
    );
    return matches ? 1 : 0;
}


size_t olm_matches_inbound_session_from(
    OlmSession * session,
    void const * their_identity_key, size_t their_identity_key_length,
    void * one_time_key_message, size_t message_length
) {
554
555
556
557
    std::uint8_t const * id_key = from_c(their_identity_key);
    std::size_t id_key_length = their_identity_key_length;

    if (olm::decode_base64_length(id_key_length) != olm::KEY_LENGTH) {
558
        from_c(session)->last_error = OlmErrorCode::OLM_INVALID_BASE64;
559
560
561
        return std::size_t(-1);
    }
    olm::Curve25519PublicKey identity_key;
562
    olm::decode_base64(id_key, id_key_length, identity_key.public_key);
563
564
565
566
567
568
569
570
571

    std::size_t raw_length = b64_input(
        from_c(one_time_key_message), message_length, from_c(session)->last_error
    );
    if (raw_length == std::size_t(-1)) {
        return std::size_t(-1);
    }
    bool matches = from_c(session)->matches_inbound_session(
        &identity_key, from_c(one_time_key_message), raw_length
572
573
574
575
576
    );
    return matches ? 1 : 0;
}


577
578
579
size_t olm_remove_one_time_keys(
    OlmAccount * account,
    OlmSession * session
580
581
) {
    size_t result = from_c(account)->remove_key(
582
        from_c(session)->bob_one_time_key
583
584
    );
    if (result == std::size_t(-1)) {
585
        from_c(account)->last_error = OlmErrorCode::OLM_BAD_MESSAGE_KEY_ID;
586
587
588
589
590
    }
    return result;
}


591
592
size_t olm_encrypt_message_type(
    OlmSession * session
593
594
595
596
597
) {
    return size_t(from_c(session)->encrypt_message_type());
}


598
599
size_t olm_encrypt_random_length(
    OlmSession * session
600
601
602
603
604
) {
    return from_c(session)->encrypt_random_length();
}


605
606
size_t olm_encrypt_message_length(
    OlmSession * session,
607
608
609
610
611
612
613
614
    size_t plaintext_length
) {
    return b64_output_length(
        from_c(session)->encrypt_message_length(plaintext_length)
    );
}


615
616
size_t olm_encrypt(
    OlmSession * session,
617
    void const * plaintext, size_t plaintext_length,
618
    void * random, size_t random_length,
619
620
621
622
623
    void * message, size_t message_length
) {
    std::size_t raw_length = from_c(session)->encrypt_message_length(
        plaintext_length
    );
624
    if (message_length < b64_output_length(raw_length)) {
625
        from_c(session)->last_error =
626
            OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
627
628
        return std::size_t(-1);
    }
629
    std::size_t result = from_c(session)->encrypt(
630
631
632
633
        from_c(plaintext), plaintext_length,
        from_c(random), random_length,
        b64_output_pos(from_c(message), raw_length), raw_length
    );
634
    olm::unset(random, random_length);
635
636
637
    if (result == std::size_t(-1)) {
        return result;
    }
638
639
640
641
    return b64_output(from_c(message), raw_length);
}


642
643
size_t olm_decrypt_max_plaintext_length(
    OlmSession * session,
644
645
646
647
648
649
650
651
652
653
    size_t message_type,
    void * message, size_t message_length
) {
    std::size_t raw_length = b64_input(
        from_c(message), message_length, from_c(session)->last_error
    );
    if (raw_length == std::size_t(-1)) {
        return std::size_t(-1);
    }
    return from_c(session)->decrypt_max_plaintext_length(
654
        olm::MessageType(message_type), from_c(message), raw_length
655
656
657
658
    );
}


659
660
size_t olm_decrypt(
    OlmSession * session,
661
662
663
664
665
666
667
668
669
670
671
    size_t message_type,
    void * message, size_t message_length,
    void * plaintext, size_t max_plaintext_length
) {
    std::size_t raw_length = b64_input(
        from_c(message), message_length, from_c(session)->last_error
    );
    if (raw_length == std::size_t(-1)) {
        return std::size_t(-1);
    }
    return from_c(session)->decrypt(
672
        olm::MessageType(message_type), from_c(message), raw_length,
673
674
675
676
        from_c(plaintext), max_plaintext_length
    );
}

677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692

size_t olm_sha256_length(
   OlmUtility * utility
) {
    return b64_output_length(from_c(utility)->sha256_length());
}


size_t olm_sha256(
    OlmUtility * utility,
    void const * input, size_t input_length,
    void * output, size_t output_length
) {
    std::size_t raw_length = from_c(utility)->sha256_length();
    if (output_length < b64_output_length(raw_length)) {
        from_c(utility)->last_error =
693
            OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
        return std::size_t(-1);
    }
    std::size_t result = from_c(utility)->sha256(
       from_c(input), input_length,
       b64_output_pos(from_c(output), raw_length), raw_length
    );
    if (result == std::size_t(-1)) {
        return result;
    }
    return b64_output(from_c(output), raw_length);
}


size_t olm_ed25519_verify(
    OlmUtility * utility,
    void const * key, size_t key_length,
    void const * message, size_t message_length,
    void * signature, size_t signature_length
) {
713
    if (olm::decode_base64_length(key_length) != olm::KEY_LENGTH) {
714
        from_c(utility)->last_error = OlmErrorCode::OLM_INVALID_BASE64;
715
716
717
        return std::size_t(-1);
    }
    olm::Ed25519PublicKey verify_key;
718
    olm::decode_base64(from_c(key), key_length, verify_key.public_key);
719
720
721
722
723
724
725
726
727
728
729
730
731
    std::size_t raw_signature_length = b64_input(
        from_c(signature), signature_length, from_c(utility)->last_error
    );
    if (raw_signature_length == std::size_t(-1)) {
        return std::size_t(-1);
    }
    return from_c(utility)->ed25519_verify(
        verify_key,
        from_c(message), message_length,
        from_c(signature), raw_signature_length
    );
}

732
}