olm.cpp 22.1 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.
 */
15
16
17
#include "olm/olm.hh"
#include "olm/session.hh"
#include "olm/account.hh"
18
#include "olm/utility.hh"
19
20
#include "olm/base64.hh"
#include "olm/cipher.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
static const olm::CipherAesSha256 PICKLE_CIPHER(
63
64
65
66
67
68
69
70
    CIPHER_KDF_INFO, sizeof(CIPHER_KDF_INFO) -1
);

std::size_t enc_output_length(
    size_t raw_length
) {
    std::size_t length = PICKLE_CIPHER.encrypt_ciphertext_length(raw_length);
    length += PICKLE_CIPHER.mac_length();
71
    return olm::encode_base64_length(length);
72
73
74
75
76
77
78
79
80
}


std::uint8_t * enc_output_pos(
    std::uint8_t * output,
    size_t raw_length
) {
    std::size_t length = PICKLE_CIPHER.encrypt_ciphertext_length(raw_length);
    length += PICKLE_CIPHER.mac_length();
81
    return output + olm::encode_base64_length(length) - length;
82
83
84
85
86
87
88
89
90
91
}

std::size_t enc_output(
    std::uint8_t const * key, std::size_t key_length,
    std::uint8_t * output, size_t raw_length
) {
    std::size_t ciphertext_length = PICKLE_CIPHER.encrypt_ciphertext_length(
        raw_length
    );
    std::size_t length = ciphertext_length + PICKLE_CIPHER.mac_length();
92
    std::size_t base64_length = olm::encode_base64_length(length);
93
94
95
96
97
98
99
    std::uint8_t * raw_output = output + base64_length - length;
    PICKLE_CIPHER.encrypt(
        key, key_length,
        raw_output, raw_length,
        raw_output, ciphertext_length,
        raw_output, length
    );
100
    olm::encode_base64(raw_output, length, output);
101
102
103
104
105
106
    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,
107
    olm::ErrorCode & last_error
108
) {
109
    std::size_t enc_length = olm::decode_base64_length(b64_length);
110
    if (enc_length == std::size_t(-1)) {
111
        last_error = olm::ErrorCode::INVALID_BASE64;
112
113
        return std::size_t(-1);
    }
114
    olm::decode_base64(input, b64_length, input);
115
116
117
118
119
120
121
122
    std::size_t raw_length = enc_length - PICKLE_CIPHER.mac_length();
    std::size_t result = PICKLE_CIPHER.decrypt(
        key, key_length,
        input, enc_length,
        input, raw_length,
        input, raw_length
    );
    if (result == std::size_t(-1)) {
123
        last_error = olm::ErrorCode::BAD_ACCOUNT_KEY;
124
125
126
127
128
129
130
131
    }
    return result;
}


std::size_t b64_output_length(
    size_t raw_length
) {
132
    return olm::encode_base64_length(raw_length);
133
134
135
136
137
138
}

std::uint8_t * b64_output_pos(
    std::uint8_t * output,
    size_t raw_length
) {
139
    return output + olm::encode_base64_length(raw_length) - raw_length;
140
141
142
143
144
}

std::size_t b64_output(
    std::uint8_t * output, size_t raw_length
) {
145
    std::size_t base64_length = olm::encode_base64_length(raw_length);
146
    std::uint8_t * raw_output = output + base64_length - raw_length;
147
    olm::encode_base64(raw_output, raw_length, output);
148
149
150
151
152
    return base64_length;
}

std::size_t b64_input(
    std::uint8_t * input, size_t b64_length,
153
    olm::ErrorCode & last_error
154
) {
155
    std::size_t raw_length = olm::decode_base64_length(b64_length);
156
    if (raw_length == std::size_t(-1)) {
157
        last_error = olm::ErrorCode::INVALID_BASE64;
158
159
        return std::size_t(-1);
    }
160
    olm::decode_base64(input, b64_length, input);
161
162
163
    return raw_length;
}

164
static const char * ERRORS[11] {
165
166
167
168
169
170
171
172
173
    "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",
174
175
    "UNKNOWN_PICKLE_VERSION",
    "CORRUPTED_PICKLE",
176
177
178
179
180
181
182
183
};

} // namespace


extern "C" {


184
size_t olm_error() {
185
186
187
188
    return std::size_t(-1);
}


189
const char * olm_account_last_error(
190
    OlmAccount * account
191
192
) {
    unsigned error = unsigned(from_c(account)->last_error);
193
    if (error < (sizeof(ERRORS)/sizeof(ERRORS[0]))) {
194
        return ERRORS[error];
195
196
197
198
199
200
    } else {
        return "UNKNOWN_ERROR";
    }
}


201
202
const char * olm_session_last_error(
    OlmSession * session
203
204
) {
    unsigned error = unsigned(from_c(session)->last_error);
205
    if (error < (sizeof(ERRORS)/sizeof(ERRORS[0]))) {
206
        return ERRORS[error];
207
208
209
210
211
    } else {
        return "UNKNOWN_ERROR";
    }
}

212
213
214
215
const char * olm_utility_last_error(
    OlmUtility * utility
) {
    unsigned error = unsigned(from_c(utility)->last_error);
216
    if (error < (sizeof(ERRORS)/sizeof(ERRORS[0]))) {
217
218
219
220
221
        return ERRORS[error];
    } else {
        return "UNKNOWN_ERROR";
    }
}
222

223
224
size_t olm_account_size() {
    return sizeof(olm::Account);
225
226
227
}


228
229
size_t olm_session_size() {
    return sizeof(olm::Session);
230
231
}

232
233
234
size_t olm_utility_size() {
    return sizeof(olm::Utility);
}
235

236
OlmAccount * olm_account(
237
238
    void * memory
) {
239
    olm::unset(memory, sizeof(olm::Account));
240
    return to_c(new(memory) olm::Account());
241
242
243
}


244
OlmSession * olm_session(
245
246
    void * memory
) {
247
    olm::unset(memory, sizeof(olm::Session));
248
    return to_c(new(memory) olm::Session());
249
250
251
}


252
253
254
255
256
257
258
259
OlmUtility * olm_utility(
    void * memory
) {
    olm::unset(memory, sizeof(olm::Utility));
    return to_c(new(memory) olm::Utility());
}


260
size_t olm_clear_account(
261
    OlmAccount * account
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
) {
    /* 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);
}


282
283
284
285
286
287
288
289
290
291
292
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);
}


293
294
size_t olm_pickle_account_length(
    OlmAccount * account
295
296
297
298
299
) {
    return enc_output_length(pickle_length(*from_c(account)));
}


300
301
size_t olm_pickle_session_length(
    OlmSession * session
302
303
304
305
306
) {
    return enc_output_length(pickle_length(*from_c(session)));
}


307
308
size_t olm_pickle_account(
    OlmAccount * account,
309
310
311
    void const * key, size_t key_length,
    void * pickled, size_t pickled_length
) {
312
    olm::Account & object = *from_c(account);
313
314
    std::size_t raw_length = pickle_length(object);
    if (pickled_length < enc_output_length(raw_length)) {
315
        object.last_error = olm::ErrorCode::OUTPUT_BUFFER_TOO_SMALL;
316
317
318
319
320
321
322
        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);
}


323
324
size_t olm_pickle_session(
    OlmSession * session,
325
326
327
    void const * key, size_t key_length,
    void * pickled, size_t pickled_length
) {
328
    olm::Session & object = *from_c(session);
329
330
    std::size_t raw_length = pickle_length(object);
    if (pickled_length < enc_output_length(raw_length)) {
331
        object.last_error = olm::ErrorCode::OUTPUT_BUFFER_TOO_SMALL;
332
333
334
335
336
337
338
        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);
}


339
340
size_t olm_unpickle_account(
    OlmAccount * account,
341
342
343
    void const * key, size_t key_length,
    void * pickled, size_t pickled_length
) {
344
    olm::Account & object = *from_c(account);
345
346
347
348
349
350
351
352
    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;
353
354
355
356
357
358
359
360
361
362
    /* 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)) {
        if (object.last_error == olm::ErrorCode::SUCCESS) {
            object.last_error = olm::ErrorCode::CORRUPTED_PICKLE;
        }
        return std::size_t(-1);
    }
363
364
365
366
    return pickled_length;
}


367
368
size_t olm_unpickle_session(
    OlmSession * session,
369
370
371
    void const * key, size_t key_length,
    void * pickled, size_t pickled_length
) {
372
    olm::Session & object = *from_c(session);
373
374
375
376
377
378
379
    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);
    }
380

381
    std::uint8_t * const end = pos + raw_length;
382
383
384
385
386
387
388
389
390
391
    /* 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)) {
        if (object.last_error == olm::ErrorCode::SUCCESS) {
            object.last_error = olm::ErrorCode::CORRUPTED_PICKLE;
        }
        return std::size_t(-1);
    }
392
393
394
395
    return pickled_length;
}


396
397
size_t olm_create_account_random_length(
    OlmAccount * account
398
399
400
401
402
) {
    return from_c(account)->new_account_random_length();
}


403
404
size_t olm_create_account(
    OlmAccount * account,
405
    void * random, size_t random_length
406
) {
407
408
409
    size_t result = from_c(account)->new_account(from_c(random), random_length);
    olm::unset(random, random_length);
    return result;
410
411
}

412

413
size_t olm_account_identity_keys_length(
414
415
416
    OlmAccount * account
) {
    return from_c(account)->get_identity_json_length();
417
418
}

419

420
421
size_t olm_account_identity_keys(
    OlmAccount * account,
422
423
    void * identity_keys, size_t identity_key_length
) {
424
425
426
    return from_c(account)->get_identity_json(
        from_c(identity_keys), identity_key_length
    );
427
428
429
}


430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
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 =
            olm::ErrorCode::OUTPUT_BUFFER_TOO_SMALL;
        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);
}


456
457
size_t olm_account_one_time_keys_length(
    OlmAccount * account
458
) {
459
    return from_c(account)->get_one_time_keys_json_length();
460
461
462
}


463
464
size_t olm_account_one_time_keys(
    OlmAccount * account,
465
    void * one_time_keys_json, size_t one_time_key_json_length
466
) {
467
468
469
    return from_c(account)->get_one_time_keys_json(
        from_c(one_time_keys_json), one_time_key_json_length
    );
470
471
472
}


473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
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,
498
    void * random, size_t random_length
499
) {
500
    size_t result = from_c(account)->generate_one_time_keys(
501
502
503
        number_of_keys,
        from_c(random), random_length
    );
504
505
    olm::unset(random, random_length);
    return result;
506
507
508
}


509
510
size_t olm_create_outbound_session_random_length(
    OlmSession * session
Mark Haines's avatar
Mark Haines committed
511
512
513
514
) {
    return from_c(session)->new_outbound_session_random_length();
}

515

516
517
518
size_t olm_create_outbound_session(
    OlmSession * session,
    OlmAccount * account,
519
520
    void const * their_identity_key, size_t their_identity_key_length,
    void const * their_one_time_key, size_t their_one_time_key_length,
521
    void * random, size_t random_length
522
) {
523
524
525
526
527
528
529
    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
530
    ) {
531
        from_c(session)->last_error = olm::ErrorCode::INVALID_BASE64;
532
533
        return std::size_t(-1);
    }
534
    olm::Curve25519PublicKey identity_key;
535
    olm::Curve25519PublicKey one_time_key;
536

537
538
    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);
539

540
    size_t result = from_c(session)->new_outbound_session(
541
542
543
        *from_c(account), identity_key, one_time_key,
        from_c(random), random_length
    );
544
545
    olm::unset(random, random_length);
    return result;
546
547
548
}


549
550
551
size_t olm_create_inbound_session(
    OlmSession * session,
    OlmAccount * account,
552
553
554
555
556
557
558
559
560
    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(
561
562
563
564
565
566
567
568
569
570
571
        *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
) {
572
573
574
575
    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) {
576
577
578
579
        from_c(session)->last_error = olm::ErrorCode::INVALID_BASE64;
        return std::size_t(-1);
    }
    olm::Curve25519PublicKey identity_key;
580
    olm::decode_base64(id_key, id_key_length, identity_key.public_key);
581
582
583
584
585
586
587
588
589
590

    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
591
592
593
594
    );
}


595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
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 =
                olm::ErrorCode::OUTPUT_BUFFER_TOO_SMALL;
        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);
}


621
622
size_t olm_matches_inbound_session(
    OlmSession * session,
623
624
625
626
627
628
629
630
631
    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(
632
633
634
635
636
637
638
639
640
641
642
        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
) {
643
644
645
646
    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) {
647
648
649
650
        from_c(session)->last_error = olm::ErrorCode::INVALID_BASE64;
        return std::size_t(-1);
    }
    olm::Curve25519PublicKey identity_key;
651
    olm::decode_base64(id_key, id_key_length, identity_key.public_key);
652
653
654
655
656
657
658
659
660

    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
661
662
663
664
665
    );
    return matches ? 1 : 0;
}


666
667
668
size_t olm_remove_one_time_keys(
    OlmAccount * account,
    OlmSession * session
669
670
) {
    size_t result = from_c(account)->remove_key(
671
        from_c(session)->bob_one_time_key
672
673
    );
    if (result == std::size_t(-1)) {
674
        from_c(account)->last_error = olm::ErrorCode::BAD_MESSAGE_KEY_ID;
675
676
677
678
679
    }
    return result;
}


680
681
size_t olm_encrypt_message_type(
    OlmSession * session
682
683
684
685
686
) {
    return size_t(from_c(session)->encrypt_message_type());
}


687
688
size_t olm_encrypt_random_length(
    OlmSession * session
689
690
691
692
693
) {
    return from_c(session)->encrypt_random_length();
}


694
695
size_t olm_encrypt_message_length(
    OlmSession * session,
696
697
698
699
700
701
702
703
    size_t plaintext_length
) {
    return b64_output_length(
        from_c(session)->encrypt_message_length(plaintext_length)
    );
}


704
705
size_t olm_encrypt(
    OlmSession * session,
706
    void const * plaintext, size_t plaintext_length,
707
    void * random, size_t random_length,
708
709
710
711
712
    void * message, size_t message_length
) {
    std::size_t raw_length = from_c(session)->encrypt_message_length(
        plaintext_length
    );
713
    if (message_length < b64_output_length(raw_length)) {
714
        from_c(session)->last_error =
715
            olm::ErrorCode::OUTPUT_BUFFER_TOO_SMALL;
716
717
        return std::size_t(-1);
    }
718
    std::size_t result = from_c(session)->encrypt(
719
720
721
722
        from_c(plaintext), plaintext_length,
        from_c(random), random_length,
        b64_output_pos(from_c(message), raw_length), raw_length
    );
723
    olm::unset(random, random_length);
724
725
726
    if (result == std::size_t(-1)) {
        return result;
    }
727
728
729
730
    return b64_output(from_c(message), raw_length);
}


731
732
size_t olm_decrypt_max_plaintext_length(
    OlmSession * session,
733
734
735
736
737
738
739
740
741
742
    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(
743
        olm::MessageType(message_type), from_c(message), raw_length
744
745
746
747
    );
}


748
749
size_t olm_decrypt(
    OlmSession * session,
750
751
752
753
754
755
756
757
758
759
760
    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(
761
        olm::MessageType(message_type), from_c(message), raw_length,
762
763
764
765
        from_c(plaintext), max_plaintext_length
    );
}

766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801

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 =
            olm::ErrorCode::OUTPUT_BUFFER_TOO_SMALL;
        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
) {
802
    if (olm::decode_base64_length(key_length) != olm::KEY_LENGTH) {
803
804
805
806
        from_c(utility)->last_error = olm::ErrorCode::INVALID_BASE64;
        return std::size_t(-1);
    }
    olm::Ed25519PublicKey verify_key;
807
    olm::decode_base64(from_c(key), key_length, verify_key.public_key);
808
809
810
811
812
813
814
815
816
817
818
819
820
    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
    );
}

821
}