]> gitweb.ps.run Git - matrix_esp_thesis/blob - ext/olm/src/account.cpp
add dependencies to repo
[matrix_esp_thesis] / ext / olm / src / account.cpp
1 /* Copyright 2015, 2016 OpenMarket Ltd
2  *
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 #include "olm/account.hh"
16 #include "olm/base64.hh"
17 #include "olm/pickle.h"
18 #include "olm/pickle.hh"
19 #include "olm/memory.hh"
20
21 olm::Account::Account(
22 ) : num_fallback_keys(0),
23     next_one_time_key_id(0),
24     last_error(OlmErrorCode::OLM_SUCCESS) {
25 }
26
27
28 olm::OneTimeKey const * olm::Account::lookup_key(
29     _olm_curve25519_public_key const & public_key
30 ) {
31     for (olm::OneTimeKey const & key : one_time_keys) {
32         if (olm::array_equal(key.key.public_key.public_key, public_key.public_key)) {
33             return &key;
34         }
35     }
36     if (num_fallback_keys >= 1
37             && olm::array_equal(
38                 current_fallback_key.key.public_key.public_key, public_key.public_key
39             )
40     ) {
41         return &current_fallback_key;
42     }
43     if (num_fallback_keys >= 2
44             && olm::array_equal(
45                 prev_fallback_key.key.public_key.public_key, public_key.public_key
46             )
47     ) {
48         return &prev_fallback_key;
49     }
50     return 0;
51 }
52
53 std::size_t olm::Account::remove_key(
54     _olm_curve25519_public_key const & public_key
55 ) {
56     OneTimeKey * i;
57     for (i = one_time_keys.begin(); i != one_time_keys.end(); ++i) {
58         if (olm::array_equal(i->key.public_key.public_key, public_key.public_key)) {
59             std::uint32_t id = i->id;
60             one_time_keys.erase(i);
61             return id;
62         }
63     }
64     // check if the key is a fallback key, to avoid returning an error, but
65     // don't actually remove it
66     if (num_fallback_keys >= 1
67             && olm::array_equal(
68                 current_fallback_key.key.public_key.public_key, public_key.public_key
69             )
70     ) {
71         return current_fallback_key.id;
72     }
73     if (num_fallback_keys >= 2
74             && olm::array_equal(
75                 prev_fallback_key.key.public_key.public_key, public_key.public_key
76             )
77     ) {
78         return prev_fallback_key.id;
79     }
80     return std::size_t(-1);
81 }
82
83 std::size_t olm::Account::new_account_random_length() const {
84     return ED25519_RANDOM_LENGTH + CURVE25519_RANDOM_LENGTH;
85 }
86
87 std::size_t olm::Account::new_account(
88     uint8_t const * random, std::size_t random_length
89 ) {
90     if (random_length < new_account_random_length()) {
91         last_error = OlmErrorCode::OLM_NOT_ENOUGH_RANDOM;
92         return std::size_t(-1);
93     }
94
95     _olm_crypto_ed25519_generate_key(random, &identity_keys.ed25519_key);
96     random += ED25519_RANDOM_LENGTH;
97     _olm_crypto_curve25519_generate_key(random, &identity_keys.curve25519_key);
98
99     return 0;
100 }
101
102 namespace {
103
104 uint8_t KEY_JSON_ED25519[] = "\"ed25519\":";
105 uint8_t KEY_JSON_CURVE25519[] = "\"curve25519\":";
106
107 template<typename T>
108 static std::uint8_t * write_string(
109     std::uint8_t * pos,
110     T const & value
111 ) {
112     std::memcpy(pos, value, sizeof(T) - 1);
113     return pos + (sizeof(T) - 1);
114 }
115
116 }
117
118
119 std::size_t olm::Account::get_identity_json_length() const {
120     std::size_t length = 0;
121     length += 1; /* { */
122     length += sizeof(KEY_JSON_CURVE25519) - 1;
123     length += 1; /* " */
124     length += olm::encode_base64_length(
125         sizeof(identity_keys.curve25519_key.public_key)
126     );
127     length += 2; /* ", */
128     length += sizeof(KEY_JSON_ED25519) - 1;
129     length += 1; /* " */
130     length += olm::encode_base64_length(
131         sizeof(identity_keys.ed25519_key.public_key)
132     );
133     length += 2; /* "} */
134     return length;
135 }
136
137
138 std::size_t olm::Account::get_identity_json(
139     std::uint8_t * identity_json, std::size_t identity_json_length
140 ) {
141     std::uint8_t * pos = identity_json;
142     size_t expected_length = get_identity_json_length();
143
144     if (identity_json_length < expected_length) {
145         last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
146         return std::size_t(-1);
147     }
148
149     *(pos++) = '{';
150     pos = write_string(pos, KEY_JSON_CURVE25519);
151     *(pos++) = '\"';
152     pos = olm::encode_base64(
153         identity_keys.curve25519_key.public_key.public_key,
154         sizeof(identity_keys.curve25519_key.public_key.public_key),
155         pos
156     );
157     *(pos++) = '\"'; *(pos++) = ',';
158     pos = write_string(pos, KEY_JSON_ED25519);
159     *(pos++) = '\"';
160     pos = olm::encode_base64(
161         identity_keys.ed25519_key.public_key.public_key,
162         sizeof(identity_keys.ed25519_key.public_key.public_key),
163         pos
164     );
165     *(pos++) = '\"'; *(pos++) = '}';
166     return pos - identity_json;
167 }
168
169
170 std::size_t olm::Account::signature_length(
171 ) const {
172     return ED25519_SIGNATURE_LENGTH;
173 }
174
175
176 std::size_t olm::Account::sign(
177     std::uint8_t const * message, std::size_t message_length,
178     std::uint8_t * signature, std::size_t signature_length
179 ) {
180     if (signature_length < this->signature_length()) {
181         last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
182         return std::size_t(-1);
183     }
184     _olm_crypto_ed25519_sign(
185         &identity_keys.ed25519_key, message, message_length, signature
186     );
187     return this->signature_length();
188 }
189
190
191 std::size_t olm::Account::get_one_time_keys_json_length(
192 ) const {
193     std::size_t length = 0;
194     bool is_empty = true;
195     for (auto const & key : one_time_keys) {
196         if (key.published) {
197             continue;
198         }
199         is_empty = false;
200         length += 2; /* {" */
201         length += olm::encode_base64_length(_olm_pickle_uint32_length(key.id));
202         length += 3; /* ":" */
203         length += olm::encode_base64_length(sizeof(key.key.public_key));
204         length += 1; /* " */
205     }
206     if (is_empty) {
207         length += 1; /* { */
208     }
209     length += 3; /* }{} */
210     length += sizeof(KEY_JSON_CURVE25519) - 1;
211     return length;
212 }
213
214
215 std::size_t olm::Account::get_one_time_keys_json(
216     std::uint8_t * one_time_json, std::size_t one_time_json_length
217 ) {
218     std::uint8_t * pos = one_time_json;
219     if (one_time_json_length < get_one_time_keys_json_length()) {
220         last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
221         return std::size_t(-1);
222     }
223     *(pos++) = '{';
224     pos = write_string(pos, KEY_JSON_CURVE25519);
225     std::uint8_t sep = '{';
226     for (auto const & key : one_time_keys) {
227         if (key.published) {
228             continue;
229         }
230         *(pos++) = sep;
231         *(pos++) = '\"';
232         std::uint8_t key_id[_olm_pickle_uint32_length(key.id)];
233         _olm_pickle_uint32(key_id, key.id);
234         pos = olm::encode_base64(key_id, sizeof(key_id), pos);
235         *(pos++) = '\"'; *(pos++) = ':'; *(pos++) = '\"';
236         pos = olm::encode_base64(
237             key.key.public_key.public_key, sizeof(key.key.public_key.public_key), pos
238         );
239         *(pos++) = '\"';
240         sep = ',';
241     }
242     if (sep != ',') {
243         /* The list was empty */
244         *(pos++) = sep;
245     }
246     *(pos++) = '}';
247     *(pos++) = '}';
248     return pos - one_time_json;
249 }
250
251
252 std::size_t olm::Account::mark_keys_as_published(
253 ) {
254     std::size_t count = 0;
255     for (auto & key : one_time_keys) {
256         if (!key.published) {
257             key.published = true;
258             count++;
259         }
260     }
261     current_fallback_key.published = true;
262     return count;
263 }
264
265
266 std::size_t olm::Account::max_number_of_one_time_keys(
267 ) const {
268     return olm::MAX_ONE_TIME_KEYS;
269 }
270
271 std::size_t olm::Account::generate_one_time_keys_random_length(
272     std::size_t number_of_keys
273 ) const {
274     return CURVE25519_RANDOM_LENGTH * number_of_keys;
275 }
276
277 std::size_t olm::Account::generate_one_time_keys(
278     std::size_t number_of_keys,
279     std::uint8_t const * random, std::size_t random_length
280 ) {
281     if (random_length < generate_one_time_keys_random_length(number_of_keys)) {
282         last_error = OlmErrorCode::OLM_NOT_ENOUGH_RANDOM;
283         return std::size_t(-1);
284     }
285     for (unsigned i = 0; i < number_of_keys; ++i) {
286         OneTimeKey & key = *one_time_keys.insert(one_time_keys.begin());
287         key.id = ++next_one_time_key_id;
288         key.published = false;
289         _olm_crypto_curve25519_generate_key(random, &key.key);
290         random += CURVE25519_RANDOM_LENGTH;
291     }
292     return number_of_keys;
293 }
294
295 std::size_t olm::Account::generate_fallback_key_random_length() const {
296     return CURVE25519_RANDOM_LENGTH;
297 }
298
299 std::size_t olm::Account::generate_fallback_key(
300     std::uint8_t const * random, std::size_t random_length
301 ) {
302     if (random_length < generate_fallback_key_random_length()) {
303         last_error = OlmErrorCode::OLM_NOT_ENOUGH_RANDOM;
304         return std::size_t(-1);
305     }
306     if (num_fallback_keys < 2) {
307         num_fallback_keys++;
308     }
309     prev_fallback_key = current_fallback_key;
310     current_fallback_key.id = ++next_one_time_key_id;
311     current_fallback_key.published = false;
312     _olm_crypto_curve25519_generate_key(random, &current_fallback_key.key);
313     return 1;
314 }
315
316
317 std::size_t olm::Account::get_fallback_key_json_length(
318 ) const {
319     std::size_t length = 4 + sizeof(KEY_JSON_CURVE25519) - 1; /* {"curve25519":{}} */
320     if (num_fallback_keys >= 1) {
321         const OneTimeKey & key = current_fallback_key;
322         length += 1; /* " */
323         length += olm::encode_base64_length(_olm_pickle_uint32_length(key.id));
324         length += 3; /* ":" */
325         length += olm::encode_base64_length(sizeof(key.key.public_key));
326         length += 1; /* " */
327     }
328     return length;
329 }
330
331 std::size_t olm::Account::get_fallback_key_json(
332     std::uint8_t * fallback_json, std::size_t fallback_json_length
333 ) {
334     std::uint8_t * pos = fallback_json;
335     if (fallback_json_length < get_fallback_key_json_length()) {
336         last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
337         return std::size_t(-1);
338     }
339     *(pos++) = '{';
340     pos = write_string(pos, KEY_JSON_CURVE25519);
341     *(pos++) = '{';
342     OneTimeKey & key = current_fallback_key;
343     if (num_fallback_keys >= 1) {
344         *(pos++) = '\"';
345         std::uint8_t key_id[_olm_pickle_uint32_length(key.id)];
346         _olm_pickle_uint32(key_id, key.id);
347         pos = olm::encode_base64(key_id, sizeof(key_id), pos);
348         *(pos++) = '\"'; *(pos++) = ':'; *(pos++) = '\"';
349         pos = olm::encode_base64(
350             key.key.public_key.public_key, sizeof(key.key.public_key.public_key), pos
351         );
352         *(pos++) = '\"';
353     }
354     *(pos++) = '}';
355     *(pos++) = '}';
356     return pos - fallback_json;
357 }
358
359 std::size_t olm::Account::get_unpublished_fallback_key_json_length(
360 ) const {
361     std::size_t length = 4 + sizeof(KEY_JSON_CURVE25519) - 1; /* {"curve25519":{}} */
362     const OneTimeKey & key = current_fallback_key;
363     if (num_fallback_keys >= 1 && !key.published) {
364         length += 1; /* " */
365         length += olm::encode_base64_length(_olm_pickle_uint32_length(key.id));
366         length += 3; /* ":" */
367         length += olm::encode_base64_length(sizeof(key.key.public_key));
368         length += 1; /* " */
369     }
370     return length;
371 }
372
373 std::size_t olm::Account::get_unpublished_fallback_key_json(
374     std::uint8_t * fallback_json, std::size_t fallback_json_length
375 ) {
376     std::uint8_t * pos = fallback_json;
377     if (fallback_json_length < get_unpublished_fallback_key_json_length()) {
378         last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
379         return std::size_t(-1);
380     }
381     *(pos++) = '{';
382     pos = write_string(pos, KEY_JSON_CURVE25519);
383     *(pos++) = '{';
384     OneTimeKey & key = current_fallback_key;
385     if (num_fallback_keys >= 1 && !key.published) {
386         *(pos++) = '\"';
387         std::uint8_t key_id[_olm_pickle_uint32_length(key.id)];
388         _olm_pickle_uint32(key_id, key.id);
389         pos = olm::encode_base64(key_id, sizeof(key_id), pos);
390         *(pos++) = '\"'; *(pos++) = ':'; *(pos++) = '\"';
391         pos = olm::encode_base64(
392             key.key.public_key.public_key, sizeof(key.key.public_key.public_key), pos
393         );
394         *(pos++) = '\"';
395     }
396     *(pos++) = '}';
397     *(pos++) = '}';
398     return pos - fallback_json;
399 }
400
401 void olm::Account::forget_old_fallback_key(
402 ) {
403     if (num_fallback_keys >= 2) {
404         num_fallback_keys = 1;
405         olm::unset(&prev_fallback_key, sizeof(prev_fallback_key));
406     }
407 }
408
409 namespace olm {
410
411 static std::size_t pickle_length(
412     olm::IdentityKeys const & value
413 ) {
414     size_t length = 0;
415     length += _olm_pickle_ed25519_key_pair_length(&value.ed25519_key);
416     length += olm::pickle_length(value.curve25519_key);
417     return length;
418 }
419
420
421 static std::uint8_t * pickle(
422     std::uint8_t * pos,
423     olm::IdentityKeys const & value
424 ) {
425     pos = _olm_pickle_ed25519_key_pair(pos, &value.ed25519_key);
426     pos = olm::pickle(pos, value.curve25519_key);
427     return pos;
428 }
429
430
431 static std::uint8_t const * unpickle(
432     std::uint8_t const * pos, std::uint8_t const * end,
433     olm::IdentityKeys & value
434 ) {
435     pos = _olm_unpickle_ed25519_key_pair(pos, end, &value.ed25519_key); UNPICKLE_OK(pos);
436     pos = olm::unpickle(pos, end, value.curve25519_key); UNPICKLE_OK(pos);
437     return pos;
438 }
439
440
441 static std::size_t pickle_length(
442     olm::OneTimeKey const & value
443 ) {
444     std::size_t length = 0;
445     length += olm::pickle_length(value.id);
446     length += olm::pickle_length(value.published);
447     length += olm::pickle_length(value.key);
448     return length;
449 }
450
451
452 static std::uint8_t * pickle(
453     std::uint8_t * pos,
454     olm::OneTimeKey const & value
455 ) {
456     pos = olm::pickle(pos, value.id);
457     pos = olm::pickle(pos, value.published);
458     pos = olm::pickle(pos, value.key);
459     return pos;
460 }
461
462
463 static std::uint8_t const * unpickle(
464     std::uint8_t const * pos, std::uint8_t const * end,
465     olm::OneTimeKey & value
466 ) {
467     pos = olm::unpickle(pos, end, value.id); UNPICKLE_OK(pos);
468     pos = olm::unpickle(pos, end, value.published); UNPICKLE_OK(pos);
469     pos = olm::unpickle(pos, end, value.key); UNPICKLE_OK(pos);
470     return pos;
471 }
472
473 } // namespace olm
474
475 namespace {
476 // pickle version 1 used only 32 bytes for the ed25519 private key.
477 // Any keys thus used should be considered compromised.
478 // pickle version 2 does not have fallback keys.
479 // pickle version 3 does not store whether the current fallback key is published.
480 static const std::uint32_t ACCOUNT_PICKLE_VERSION = 4;
481 }
482
483
484 std::size_t olm::pickle_length(
485     olm::Account const & value
486 ) {
487     std::size_t length = 0;
488     length += olm::pickle_length(ACCOUNT_PICKLE_VERSION);
489     length += olm::pickle_length(value.identity_keys);
490     length += olm::pickle_length(value.one_time_keys);
491     length += olm::pickle_length(value.num_fallback_keys);
492     if (value.num_fallback_keys >= 1) {
493         length += olm::pickle_length(value.current_fallback_key);
494         if (value.num_fallback_keys >= 2) {
495             length += olm::pickle_length(value.prev_fallback_key);
496         }
497     }
498     length += olm::pickle_length(value.next_one_time_key_id);
499     return length;
500 }
501
502
503 std::uint8_t * olm::pickle(
504     std::uint8_t * pos,
505     olm::Account const & value
506 ) {
507     pos = olm::pickle(pos, ACCOUNT_PICKLE_VERSION);
508     pos = olm::pickle(pos, value.identity_keys);
509     pos = olm::pickle(pos, value.one_time_keys);
510     pos = olm::pickle(pos, value.num_fallback_keys);
511     if (value.num_fallback_keys >= 1) {
512         pos = olm::pickle(pos, value.current_fallback_key);
513         if (value.num_fallback_keys >= 2) {
514             pos = olm::pickle(pos, value.prev_fallback_key);
515         }
516     }
517     pos = olm::pickle(pos, value.next_one_time_key_id);
518     return pos;
519 }
520
521
522 std::uint8_t const * olm::unpickle(
523     std::uint8_t const * pos, std::uint8_t const * end,
524     olm::Account & value
525 ) {
526     uint32_t pickle_version;
527
528     pos = olm::unpickle(pos, end, pickle_version); UNPICKLE_OK(pos);
529
530     switch (pickle_version) {
531         case ACCOUNT_PICKLE_VERSION:
532         case 3:
533         case 2:
534             break;
535         case 1:
536             value.last_error = OlmErrorCode::OLM_BAD_LEGACY_ACCOUNT_PICKLE;
537             return nullptr;
538         default:
539             value.last_error = OlmErrorCode::OLM_UNKNOWN_PICKLE_VERSION;
540             return nullptr;
541     }
542
543     pos = olm::unpickle(pos, end, value.identity_keys); UNPICKLE_OK(pos);
544     pos = olm::unpickle(pos, end, value.one_time_keys); UNPICKLE_OK(pos);
545
546     if (pickle_version <= 2) {
547         // version 2 did not have fallback keys
548         value.num_fallback_keys = 0;
549     } else if (pickle_version == 3) {
550         // version 3 used the published flag to indicate how many fallback keys
551         // were present (we'll have to assume that the keys were published)
552         pos = olm::unpickle(pos, end, value.current_fallback_key); UNPICKLE_OK(pos);
553         pos = olm::unpickle(pos, end, value.prev_fallback_key); UNPICKLE_OK(pos);
554         if (value.current_fallback_key.published) {
555             if (value.prev_fallback_key.published) {
556                 value.num_fallback_keys = 2;
557             } else {
558                 value.num_fallback_keys = 1;
559             }
560         } else  {
561             value.num_fallback_keys = 0;
562         }
563     } else {
564         pos = olm::unpickle(pos, end, value.num_fallback_keys); UNPICKLE_OK(pos);
565         if (value.num_fallback_keys >= 1) {
566             pos = olm::unpickle(pos, end, value.current_fallback_key); UNPICKLE_OK(pos);
567             if (value.num_fallback_keys >= 2) {
568                 pos = olm::unpickle(pos, end, value.prev_fallback_key); UNPICKLE_OK(pos);
569                 if (value.num_fallback_keys >= 3) {
570                     value.last_error = OlmErrorCode::OLM_CORRUPTED_PICKLE;
571                     return nullptr;
572                 }
573             }
574         }
575     }
576
577     pos = olm::unpickle(pos, end, value.next_one_time_key_id); UNPICKLE_OK(pos);
578
579     return pos;
580 }