olm.cpp 22.6 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/utility.hh"
20
#include "olm/base64.hh"
21
#include "olm/memory.hh"
22
23
24
25
26
27

#include <new>
#include <cstring>

namespace {

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

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

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

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

44
45
46
47
48
49
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);
50
51
52
53
54
55
56
57
58
59
60
61
}

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);
}

static const std::uint8_t CIPHER_KDF_INFO[] = "Pickle";

62
63
64
65
66
67
68
69
70
71
72
const olm_cipher *get_pickle_cipher() {
    static olm_cipher *cipher = NULL;
    static olm_cipher_aes_sha_256 PICKLE_CIPHER;
    if (!cipher) {
        cipher = olm_cipher_aes_sha_256_init(
            &PICKLE_CIPHER,
            CIPHER_KDF_INFO, sizeof(CIPHER_KDF_INFO) - 1
        );
    }
    return cipher;
}
73
74
75
76

std::size_t enc_output_length(
    size_t raw_length
) {
77
78
79
    auto *cipher = get_pickle_cipher();
    std::size_t length = cipher->ops->encrypt_ciphertext_length(cipher, raw_length);
    length += cipher->ops->mac_length(cipher);
80
    return olm::encode_base64_length(length);
81
82
83
84
85
86
87
}


std::uint8_t * enc_output_pos(
    std::uint8_t * output,
    size_t raw_length
) {
88
89
90
    auto *cipher = get_pickle_cipher();
    std::size_t length = cipher->ops->encrypt_ciphertext_length(cipher, raw_length);
    length += cipher->ops->mac_length(cipher);
91
    return output + olm::encode_base64_length(length) - length;
92
93
94
95
96
97
}

std::size_t enc_output(
    std::uint8_t const * key, std::size_t key_length,
    std::uint8_t * output, size_t raw_length
) {
98
99
100
    auto *cipher = get_pickle_cipher();
    std::size_t ciphertext_length = cipher->ops->encrypt_ciphertext_length(
        cipher, raw_length
101
    );
102
    std::size_t length = ciphertext_length + cipher->ops->mac_length(cipher);
103
    std::size_t base64_length = olm::encode_base64_length(length);
104
    std::uint8_t * raw_output = output + base64_length - length;
105
106
    cipher->ops->encrypt(
        cipher,
107
108
109
110
111
        key, key_length,
        raw_output, raw_length,
        raw_output, ciphertext_length,
        raw_output, length
    );
112
    olm::encode_base64(raw_output, length, output);
113
114
115
116
117
118
    return raw_length;
}

std::size_t enc_input(
    std::uint8_t const * key, std::size_t key_length,
    std::uint8_t * input, size_t b64_length,
119
    OlmErrorCode & last_error
120
) {
121
    std::size_t enc_length = olm::decode_base64_length(b64_length);
122
    if (enc_length == std::size_t(-1)) {
123
        last_error = OlmErrorCode::OLM_INVALID_BASE64;
124
125
        return std::size_t(-1);
    }
126
    olm::decode_base64(input, b64_length, input);
127
128
129
130
    auto *cipher = get_pickle_cipher();
    std::size_t raw_length = enc_length - cipher->ops->mac_length(cipher);
    std::size_t result = cipher->ops->decrypt(
        cipher,
131
132
133
134
135
136
        key, key_length,
        input, enc_length,
        input, raw_length,
        input, raw_length
    );
    if (result == std::size_t(-1)) {
137
        last_error = OlmErrorCode::OLM_BAD_ACCOUNT_KEY;
138
139
140
141
142
143
144
145
    }
    return result;
}


std::size_t b64_output_length(
    size_t raw_length
) {
146
    return olm::encode_base64_length(raw_length);
147
148
149
150
151
152
}

std::uint8_t * b64_output_pos(
    std::uint8_t * output,
    size_t raw_length
) {
153
    return output + olm::encode_base64_length(raw_length) - raw_length;
154
155
156
157
158
}

std::size_t b64_output(
    std::uint8_t * output, size_t raw_length
) {
159
    std::size_t base64_length = olm::encode_base64_length(raw_length);
160
    std::uint8_t * raw_output = output + base64_length - raw_length;
161
    olm::encode_base64(raw_output, raw_length, output);
162
163
164
165
166
    return base64_length;
}

std::size_t b64_input(
    std::uint8_t * input, size_t b64_length,
167
    OlmErrorCode & last_error
168
) {
169
    std::size_t raw_length = olm::decode_base64_length(b64_length);
170
    if (raw_length == std::size_t(-1)) {
171
        last_error = OlmErrorCode::OLM_INVALID_BASE64;
172
173
        return std::size_t(-1);
    }
174
    olm::decode_base64(input, b64_length, input);
175
176
177
    return raw_length;
}

178
static const char * ERRORS[11] {
179
180
181
182
183
184
185
186
187
    "SUCCESS",
    "NOT_ENOUGH_RANDOM",
    "OUTPUT_BUFFER_TOO_SMALL",
    "BAD_MESSAGE_VERSION",
    "BAD_MESSAGE_FORMAT",
    "BAD_MESSAGE_MAC",
    "BAD_MESSAGE_KEY_ID",
    "INVALID_BASE64",
    "BAD_ACCOUNT_KEY",
188
189
    "UNKNOWN_PICKLE_VERSION",
    "CORRUPTED_PICKLE",
190
191
192
193
194
195
196
197
};

} // namespace


extern "C" {


198
size_t olm_error() {
199
200
201
202
    return std::size_t(-1);
}


203
const char * olm_account_last_error(
204
    OlmAccount * account
205
206
) {
    unsigned error = unsigned(from_c(account)->last_error);
207
    if (error < (sizeof(ERRORS)/sizeof(ERRORS[0]))) {
208
        return ERRORS[error];
209
210
211
212
213
214
    } else {
        return "UNKNOWN_ERROR";
    }
}


215
216
const char * olm_session_last_error(
    OlmSession * session
217
218
) {
    unsigned error = unsigned(from_c(session)->last_error);
219
    if (error < (sizeof(ERRORS)/sizeof(ERRORS[0]))) {
220
        return ERRORS[error];
221
222
223
224
225
    } else {
        return "UNKNOWN_ERROR";
    }
}

226
227
228
229
const char * olm_utility_last_error(
    OlmUtility * utility
) {
    unsigned error = unsigned(from_c(utility)->last_error);
230
    if (error < (sizeof(ERRORS)/sizeof(ERRORS[0]))) {
231
232
233
234
235
        return ERRORS[error];
    } else {
        return "UNKNOWN_ERROR";
    }
}
236

237
238
size_t olm_account_size() {
    return sizeof(olm::Account);
239
240
241
}


242
243
size_t olm_session_size() {
    return sizeof(olm::Session);
244
245
}

246
247
248
size_t olm_utility_size() {
    return sizeof(olm::Utility);
}
249

250
OlmAccount * olm_account(
251
252
    void * memory
) {
253
    olm::unset(memory, sizeof(olm::Account));
254
    return to_c(new(memory) olm::Account());
255
256
257
}


258
OlmSession * olm_session(
259
260
    void * memory
) {
261
    olm::unset(memory, sizeof(olm::Session));
262
    return to_c(new(memory) olm::Session());
263
264
265
}


266
267
268
269
270
271
272
273
OlmUtility * olm_utility(
    void * memory
) {
    olm::unset(memory, sizeof(olm::Utility));
    return to_c(new(memory) olm::Utility());
}


274
size_t olm_clear_account(
275
    OlmAccount * account
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
) {
    /* 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);
}


296
297
298
299
300
301
302
303
304
305
306
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);
}


307
308
size_t olm_pickle_account_length(
    OlmAccount * account
309
310
311
312
313
) {
    return enc_output_length(pickle_length(*from_c(account)));
}


314
315
size_t olm_pickle_session_length(
    OlmSession * session
316
317
318
319
320
) {
    return enc_output_length(pickle_length(*from_c(session)));
}


321
322
size_t olm_pickle_account(
    OlmAccount * account,
323
324
325
    void const * key, size_t key_length,
    void * pickled, size_t pickled_length
) {
326
    olm::Account & object = *from_c(account);
327
328
    std::size_t raw_length = pickle_length(object);
    if (pickled_length < enc_output_length(raw_length)) {
329
        object.last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
330
331
332
333
334
335
336
        return size_t(-1);
    }
    pickle(enc_output_pos(from_c(pickled), raw_length), object);
    return enc_output(from_c(key), key_length, from_c(pickled), raw_length);
}


337
338
size_t olm_pickle_session(
    OlmSession * session,
339
340
341
    void const * key, size_t key_length,
    void * pickled, size_t pickled_length
) {
342
    olm::Session & object = *from_c(session);
343
344
    std::size_t raw_length = pickle_length(object);
    if (pickled_length < enc_output_length(raw_length)) {
345
        object.last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
346
347
348
349
350
351
352
        return size_t(-1);
    }
    pickle(enc_output_pos(from_c(pickled), raw_length), object);
    return enc_output(from_c(key), key_length, from_c(pickled), raw_length);
}


353
354
size_t olm_unpickle_account(
    OlmAccount * account,
355
356
357
    void const * key, size_t key_length,
    void * pickled, size_t pickled_length
) {
358
    olm::Account & object = *from_c(account);
359
360
361
362
363
364
365
366
    std::uint8_t * const pos = from_c(pickled);
    std::size_t raw_length = enc_input(
        from_c(key), key_length, pos, pickled_length, object.last_error
    );
    if (raw_length == std::size_t(-1)) {
        return std::size_t(-1);
    }
    std::uint8_t * const end = pos + raw_length;
367
368
369
370
371
    /* 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)) {
372
373
        if (object.last_error == OlmErrorCode::OLM_SUCCESS) {
            object.last_error = OlmErrorCode::OLM_CORRUPTED_PICKLE;
374
375
376
        }
        return std::size_t(-1);
    }
377
378
379
380
    return pickled_length;
}


381
382
size_t olm_unpickle_session(
    OlmSession * session,
383
384
385
    void const * key, size_t key_length,
    void * pickled, size_t pickled_length
) {
386
    olm::Session & object = *from_c(session);
387
388
389
390
391
392
393
    std::uint8_t * const pos = from_c(pickled);
    std::size_t raw_length = enc_input(
        from_c(key), key_length, pos, pickled_length, object.last_error
    );
    if (raw_length == std::size_t(-1)) {
        return std::size_t(-1);
    }
394

395
    std::uint8_t * const end = pos + raw_length;
396
397
398
399
400
    /* 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)) {
401
402
        if (object.last_error == OlmErrorCode::OLM_SUCCESS) {
            object.last_error = OlmErrorCode::OLM_CORRUPTED_PICKLE;
403
404
405
        }
        return std::size_t(-1);
    }
406
407
408
409
    return pickled_length;
}


410
411
size_t olm_create_account_random_length(
    OlmAccount * account
412
413
414
415
416
) {
    return from_c(account)->new_account_random_length();
}


417
418
size_t olm_create_account(
    OlmAccount * account,
419
    void * random, size_t random_length
420
) {
421
422
423
    size_t result = from_c(account)->new_account(from_c(random), random_length);
    olm::unset(random, random_length);
    return result;
424
425
}

426

427
size_t olm_account_identity_keys_length(
428
429
430
    OlmAccount * account
) {
    return from_c(account)->get_identity_json_length();
431
432
}

433

434
435
size_t olm_account_identity_keys(
    OlmAccount * account,
436
437
    void * identity_keys, size_t identity_key_length
) {
438
439
440
    return from_c(account)->get_identity_json(
        from_c(identity_keys), identity_key_length
    );
441
442
443
}


444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
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 =
459
            OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
460
461
462
463
464
465
466
467
468
469
        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);
}


470
471
size_t olm_account_one_time_keys_length(
    OlmAccount * account
472
) {
473
    return from_c(account)->get_one_time_keys_json_length();
474
475
476
}


477
478
size_t olm_account_one_time_keys(
    OlmAccount * account,
479
    void * one_time_keys_json, size_t one_time_key_json_length
480
) {
481
482
483
    return from_c(account)->get_one_time_keys_json(
        from_c(one_time_keys_json), one_time_key_json_length
    );
484
485
486
}


487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
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,
512
    void * random, size_t random_length
513
) {
514
    size_t result = from_c(account)->generate_one_time_keys(
515
516
517
        number_of_keys,
        from_c(random), random_length
    );
518
519
    olm::unset(random, random_length);
    return result;
520
521
522
}


523
524
size_t olm_create_outbound_session_random_length(
    OlmSession * session
Mark Haines's avatar
Mark Haines committed
525
526
527
528
) {
    return from_c(session)->new_outbound_session_random_length();
}

529

530
531
532
size_t olm_create_outbound_session(
    OlmSession * session,
    OlmAccount * account,
533
534
    void const * their_identity_key, size_t their_identity_key_length,
    void const * their_one_time_key, size_t their_one_time_key_length,
535
    void * random, size_t random_length
536
) {
537
538
539
540
541
542
543
    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
544
    ) {
545
        from_c(session)->last_error = OlmErrorCode::OLM_INVALID_BASE64;
546
547
        return std::size_t(-1);
    }
548
    olm::Curve25519PublicKey identity_key;
549
    olm::Curve25519PublicKey one_time_key;
550

551
552
    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);
553

554
    size_t result = from_c(session)->new_outbound_session(
555
556
557
        *from_c(account), identity_key, one_time_key,
        from_c(random), random_length
    );
558
559
    olm::unset(random, random_length);
    return result;
560
561
562
}


563
564
565
size_t olm_create_inbound_session(
    OlmSession * session,
    OlmAccount * account,
566
567
568
569
570
571
572
573
574
    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(
575
576
577
578
579
580
581
582
583
584
585
        *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
) {
586
587
588
589
    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) {
590
        from_c(session)->last_error = OlmErrorCode::OLM_INVALID_BASE64;
591
592
593
        return std::size_t(-1);
    }
    olm::Curve25519PublicKey identity_key;
594
    olm::decode_base64(id_key, id_key_length, identity_key.public_key);
595
596
597
598
599
600
601
602
603
604

    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
605
606
607
608
    );
}


609
610
611
612
613
614
615
616
617
618
619
620
621
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 =
622
                OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
623
624
625
626
627
628
629
630
631
632
633
634
        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);
}


635
636
size_t olm_matches_inbound_session(
    OlmSession * session,
637
638
639
640
641
642
643
644
645
    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(
646
647
648
649
650
651
652
653
654
655
656
        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
) {
657
658
659
660
    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) {
661
        from_c(session)->last_error = OlmErrorCode::OLM_INVALID_BASE64;
662
663
664
        return std::size_t(-1);
    }
    olm::Curve25519PublicKey identity_key;
665
    olm::decode_base64(id_key, id_key_length, identity_key.public_key);
666
667
668
669
670
671
672
673
674

    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
675
676
677
678
679
    );
    return matches ? 1 : 0;
}


680
681
682
size_t olm_remove_one_time_keys(
    OlmAccount * account,
    OlmSession * session
683
684
) {
    size_t result = from_c(account)->remove_key(
685
        from_c(session)->bob_one_time_key
686
687
    );
    if (result == std::size_t(-1)) {
688
        from_c(account)->last_error = OlmErrorCode::OLM_BAD_MESSAGE_KEY_ID;
689
690
691
692
693
    }
    return result;
}


694
695
size_t olm_encrypt_message_type(
    OlmSession * session
696
697
698
699
700
) {
    return size_t(from_c(session)->encrypt_message_type());
}


701
702
size_t olm_encrypt_random_length(
    OlmSession * session
703
704
705
706
707
) {
    return from_c(session)->encrypt_random_length();
}


708
709
size_t olm_encrypt_message_length(
    OlmSession * session,
710
711
712
713
714
715
716
717
    size_t plaintext_length
) {
    return b64_output_length(
        from_c(session)->encrypt_message_length(plaintext_length)
    );
}


718
719
size_t olm_encrypt(
    OlmSession * session,
720
    void const * plaintext, size_t plaintext_length,
721
    void * random, size_t random_length,
722
723
724
725
726
    void * message, size_t message_length
) {
    std::size_t raw_length = from_c(session)->encrypt_message_length(
        plaintext_length
    );
727
    if (message_length < b64_output_length(raw_length)) {
728
        from_c(session)->last_error =
729
            OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
730
731
        return std::size_t(-1);
    }
732
    std::size_t result = from_c(session)->encrypt(
733
734
735
736
        from_c(plaintext), plaintext_length,
        from_c(random), random_length,
        b64_output_pos(from_c(message), raw_length), raw_length
    );
737
    olm::unset(random, random_length);
738
739
740
    if (result == std::size_t(-1)) {
        return result;
    }
741
742
743
744
    return b64_output(from_c(message), raw_length);
}


745
746
size_t olm_decrypt_max_plaintext_length(
    OlmSession * session,
747
748
749
750
751
752
753
754
755
756
    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(
757
        olm::MessageType(message_type), from_c(message), raw_length
758
759
760
761
    );
}


762
763
size_t olm_decrypt(
    OlmSession * session,
764
765
766
767
768
769
770
771
772
773
774
    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(
775
        olm::MessageType(message_type), from_c(message), raw_length,
776
777
778
779
        from_c(plaintext), max_plaintext_length
    );
}

780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795

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 =
796
            OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
        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
) {
816
    if (olm::decode_base64_length(key_length) != olm::KEY_LENGTH) {
817
        from_c(utility)->last_error = OlmErrorCode::OLM_INVALID_BASE64;
818
819
820
        return std::size_t(-1);
    }
    olm::Ed25519PublicKey verify_key;
821
    olm::decode_base64(from_c(key), key_length, verify_key.public_key);
822
823
824
825
826
827
828
829
830
831
832
833
834
    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
    );
}

835
}