olm.cpp 20.9 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

manuroe's avatar
manuroe committed
107
size_t olm_error(void) {
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

manuroe's avatar
manuroe committed
134
size_t olm_account_size(void) {
135
    return sizeof(olm::Account);
136
137
138
}


manuroe's avatar
manuroe committed
139
size_t olm_session_size(void) {
140
    return sizeof(olm::Session);
141
142
}

manuroe's avatar
manuroe committed
143
size_t olm_utility_size(void) {
144
145
    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
}


Hubert Chathi's avatar
Hubert Chathi committed
420
421
422
423
424
425
426
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_generate_fallback_key_random_length(
    OlmAccount * account
) {
    return from_c(account)->generate_fallback_key_random_length();
}


size_t olm_account_generate_fallback_key(
    OlmAccount * account,
    void * random, size_t random_length
) {
    size_t result = from_c(account)->generate_fallback_key(
        from_c(random), random_length
    );
    olm::unset(random, random_length);
    return result;
}


size_t olm_account_fallback_key_length(
    OlmAccount * account
) {
    return from_c(account)->get_fallback_key_json_length();
}


size_t olm_account_fallback_key(
    OlmAccount * account,
    void * fallback_key_json, size_t fallback_key_json_length
) {
    return from_c(account)->get_fallback_key_json(
        from_c(fallback_key_json), fallback_key_json_length
    );
}


456
457
size_t olm_create_outbound_session_random_length(
    OlmSession * session
Mark Haines's avatar
Mark Haines committed
458
459
460
461
) {
    return from_c(session)->new_outbound_session_random_length();
}

462

463
464
465
size_t olm_create_outbound_session(
    OlmSession * session,
    OlmAccount * account,
466
467
    void const * their_identity_key, size_t their_identity_key_length,
    void const * their_one_time_key, size_t their_one_time_key_length,
468
    void * random, size_t random_length
469
) {
470
471
472
473
474
    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;

475
476
    if (olm::decode_base64_length(id_key_length) != CURVE25519_KEY_LENGTH
            || olm::decode_base64_length(ot_key_length) != CURVE25519_KEY_LENGTH
477
    ) {
478
        from_c(session)->last_error = OlmErrorCode::OLM_INVALID_BASE64;
479
480
        return std::size_t(-1);
    }
481
482
    _olm_curve25519_public_key identity_key;
    _olm_curve25519_public_key one_time_key;
483

484
485
    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);
486

487
    size_t result = from_c(session)->new_outbound_session(
488
489
490
        *from_c(account), identity_key, one_time_key,
        from_c(random), random_length
    );
491
492
    olm::unset(random, random_length);
    return result;
493
494
495
}


496
497
498
size_t olm_create_inbound_session(
    OlmSession * session,
    OlmAccount * account,
499
500
501
502
503
504
505
506
507
    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(
508
509
510
511
512
513
514
515
516
517
518
        *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
) {
519
520
521
    std::uint8_t const * id_key = from_c(their_identity_key);
    std::size_t id_key_length = their_identity_key_length;

522
    if (olm::decode_base64_length(id_key_length) != CURVE25519_KEY_LENGTH) {
523
        from_c(session)->last_error = OlmErrorCode::OLM_INVALID_BASE64;
524
525
        return std::size_t(-1);
    }
526
    _olm_curve25519_public_key identity_key;
527
    olm::decode_base64(id_key, id_key_length, identity_key.public_key);
528
529
530
531
532
533
534
535
536
537

    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
538
539
540
541
    );
}


542
543
544
545
546
547
548
549
550
551
552
553
554
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 =
555
                OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
556
557
558
559
560
561
562
563
564
565
566
567
        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);
}


568
569
570
571
572
573
int olm_session_has_received_message(
    OlmSession * session
) {
    return from_c(session)->received_message;
}

574
575
void olm_session_describe(
    OlmSession * session, char *buf, size_t buflen
David Baker's avatar
David Baker committed
576
) {
577
    from_c(session)->describe(buf, buflen);
David Baker's avatar
David Baker committed
578
579
}

580
581
size_t olm_matches_inbound_session(
    OlmSession * session,
582
583
584
585
586
587
588
589
590
    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(
591
592
593
594
595
596
597
598
599
600
601
        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
) {
602
603
604
    std::uint8_t const * id_key = from_c(their_identity_key);
    std::size_t id_key_length = their_identity_key_length;

605
    if (olm::decode_base64_length(id_key_length) != CURVE25519_KEY_LENGTH) {
606
        from_c(session)->last_error = OlmErrorCode::OLM_INVALID_BASE64;
607
608
        return std::size_t(-1);
    }
609
    _olm_curve25519_public_key identity_key;
610
    olm::decode_base64(id_key, id_key_length, identity_key.public_key);
611
612
613
614
615
616
617
618
619

    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
620
621
622
623
624
    );
    return matches ? 1 : 0;
}


625
626
627
size_t olm_remove_one_time_keys(
    OlmAccount * account,
    OlmSession * session
628
629
) {
    size_t result = from_c(account)->remove_key(
630
        from_c(session)->bob_one_time_key
631
632
    );
    if (result == std::size_t(-1)) {
633
        from_c(account)->last_error = OlmErrorCode::OLM_BAD_MESSAGE_KEY_ID;
634
635
636
637
638
    }
    return result;
}


639
640
size_t olm_encrypt_message_type(
    OlmSession * session
641
642
643
644
645
) {
    return size_t(from_c(session)->encrypt_message_type());
}


646
647
size_t olm_encrypt_random_length(
    OlmSession * session
648
649
650
651
652
) {
    return from_c(session)->encrypt_random_length();
}


653
654
size_t olm_encrypt_message_length(
    OlmSession * session,
655
656
657
658
659
660
661
662
    size_t plaintext_length
) {
    return b64_output_length(
        from_c(session)->encrypt_message_length(plaintext_length)
    );
}


663
664
size_t olm_encrypt(
    OlmSession * session,
665
    void const * plaintext, size_t plaintext_length,
666
    void * random, size_t random_length,
667
668
669
670
671
    void * message, size_t message_length
) {
    std::size_t raw_length = from_c(session)->encrypt_message_length(
        plaintext_length
    );
672
    if (message_length < b64_output_length(raw_length)) {
673
        from_c(session)->last_error =
674
            OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
675
676
        return std::size_t(-1);
    }
677
    std::size_t result = from_c(session)->encrypt(
678
679
680
681
        from_c(plaintext), plaintext_length,
        from_c(random), random_length,
        b64_output_pos(from_c(message), raw_length), raw_length
    );
682
    olm::unset(random, random_length);
683
684
685
    if (result == std::size_t(-1)) {
        return result;
    }
686
687
688
689
    return b64_output(from_c(message), raw_length);
}


690
691
size_t olm_decrypt_max_plaintext_length(
    OlmSession * session,
692
693
694
695
696
697
698
699
700
701
    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(
702
        olm::MessageType(message_type), from_c(message), raw_length
703
704
705
706
    );
}


707
708
size_t olm_decrypt(
    OlmSession * session,
709
710
711
712
713
714
715
716
717
718
719
    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(
720
        olm::MessageType(message_type), from_c(message), raw_length,
721
722
723
724
        from_c(plaintext), max_plaintext_length
    );
}

725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740

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 =
741
            OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
        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
) {
761
    if (olm::decode_base64_length(key_length) != CURVE25519_KEY_LENGTH) {
762
        from_c(utility)->last_error = OlmErrorCode::OLM_INVALID_BASE64;
763
764
        return std::size_t(-1);
    }
765
    _olm_ed25519_public_key verify_key;
766
    olm::decode_base64(from_c(key), key_length, verify_key.public_key);
767
768
769
770
771
772
773
774
775
776
777
778
779
    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
    );
}

780
}