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
Account()21 olm::Account::Account(
22 ) : next_one_time_key_id(0),
23 last_error(OlmErrorCode::OLM_SUCCESS) {
24 // since we don't need to keep track of whether the fallback keys are
25 // published, use the published flag as in indication for whether the keys
26 // were generated
27 current_fallback_key.published = false;
28 prev_fallback_key.published = false;
29 }
30
31
lookup_key(_olm_curve25519_public_key const & public_key)32 olm::OneTimeKey const * olm::Account::lookup_key(
33 _olm_curve25519_public_key const & public_key
34 ) {
35 for (olm::OneTimeKey const & key : one_time_keys) {
36 if (olm::array_equal(key.key.public_key.public_key, public_key.public_key)) {
37 return &key;
38 }
39 }
40 if (current_fallback_key.published
41 && olm::array_equal(
42 current_fallback_key.key.public_key.public_key, public_key.public_key
43 )
44 ) {
45 return ¤t_fallback_key;
46 }
47 if (prev_fallback_key.published
48 && olm::array_equal(
49 prev_fallback_key.key.public_key.public_key, public_key.public_key
50 )
51 ) {
52 return &prev_fallback_key;
53 }
54 return 0;
55 }
56
remove_key(_olm_curve25519_public_key const & public_key)57 std::size_t olm::Account::remove_key(
58 _olm_curve25519_public_key const & public_key
59 ) {
60 OneTimeKey * i;
61 for (i = one_time_keys.begin(); i != one_time_keys.end(); ++i) {
62 if (olm::array_equal(i->key.public_key.public_key, public_key.public_key)) {
63 std::uint32_t id = i->id;
64 one_time_keys.erase(i);
65 return id;
66 }
67 }
68 // check if the key is a fallback key, to avoid returning an error, but
69 // don't actually remove it
70 if (current_fallback_key.published
71 && olm::array_equal(
72 current_fallback_key.key.public_key.public_key, public_key.public_key
73 )
74 ) {
75 return current_fallback_key.id;
76 }
77 if (prev_fallback_key.published
78 && olm::array_equal(
79 prev_fallback_key.key.public_key.public_key, public_key.public_key
80 )
81 ) {
82 return prev_fallback_key.id;
83 }
84 return std::size_t(-1);
85 }
86
new_account_random_length() const87 std::size_t olm::Account::new_account_random_length() const {
88 return ED25519_RANDOM_LENGTH + CURVE25519_RANDOM_LENGTH;
89 }
90
new_account(uint8_t const * random,std::size_t random_length)91 std::size_t olm::Account::new_account(
92 uint8_t const * random, std::size_t random_length
93 ) {
94 if (random_length < new_account_random_length()) {
95 last_error = OlmErrorCode::OLM_NOT_ENOUGH_RANDOM;
96 return std::size_t(-1);
97 }
98
99 _olm_crypto_ed25519_generate_key(random, &identity_keys.ed25519_key);
100 random += ED25519_RANDOM_LENGTH;
101 _olm_crypto_curve25519_generate_key(random, &identity_keys.curve25519_key);
102
103 return 0;
104 }
105
106 namespace {
107
108 uint8_t KEY_JSON_ED25519[] = "\"ed25519\":";
109 uint8_t KEY_JSON_CURVE25519[] = "\"curve25519\":";
110
111 template<typename T>
write_string(std::uint8_t * pos,T const & value)112 static std::uint8_t * write_string(
113 std::uint8_t * pos,
114 T const & value
115 ) {
116 std::memcpy(pos, value, sizeof(T) - 1);
117 return pos + (sizeof(T) - 1);
118 }
119
120 }
121
122
get_identity_json_length() const123 std::size_t olm::Account::get_identity_json_length() const {
124 std::size_t length = 0;
125 length += 1; /* { */
126 length += sizeof(KEY_JSON_CURVE25519) - 1;
127 length += 1; /* " */
128 length += olm::encode_base64_length(
129 sizeof(identity_keys.curve25519_key.public_key)
130 );
131 length += 2; /* ", */
132 length += sizeof(KEY_JSON_ED25519) - 1;
133 length += 1; /* " */
134 length += olm::encode_base64_length(
135 sizeof(identity_keys.ed25519_key.public_key)
136 );
137 length += 2; /* "} */
138 return length;
139 }
140
141
get_identity_json(std::uint8_t * identity_json,std::size_t identity_json_length)142 std::size_t olm::Account::get_identity_json(
143 std::uint8_t * identity_json, std::size_t identity_json_length
144 ) {
145 std::uint8_t * pos = identity_json;
146 size_t expected_length = get_identity_json_length();
147
148 if (identity_json_length < expected_length) {
149 last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
150 return std::size_t(-1);
151 }
152
153 *(pos++) = '{';
154 pos = write_string(pos, KEY_JSON_CURVE25519);
155 *(pos++) = '\"';
156 pos = olm::encode_base64(
157 identity_keys.curve25519_key.public_key.public_key,
158 sizeof(identity_keys.curve25519_key.public_key.public_key),
159 pos
160 );
161 *(pos++) = '\"'; *(pos++) = ',';
162 pos = write_string(pos, KEY_JSON_ED25519);
163 *(pos++) = '\"';
164 pos = olm::encode_base64(
165 identity_keys.ed25519_key.public_key.public_key,
166 sizeof(identity_keys.ed25519_key.public_key.public_key),
167 pos
168 );
169 *(pos++) = '\"'; *(pos++) = '}';
170 return pos - identity_json;
171 }
172
173
signature_length() const174 std::size_t olm::Account::signature_length(
175 ) const {
176 return ED25519_SIGNATURE_LENGTH;
177 }
178
179
sign(std::uint8_t const * message,std::size_t message_length,std::uint8_t * signature,std::size_t signature_length)180 std::size_t olm::Account::sign(
181 std::uint8_t const * message, std::size_t message_length,
182 std::uint8_t * signature, std::size_t signature_length
183 ) {
184 if (signature_length < this->signature_length()) {
185 last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
186 return std::size_t(-1);
187 }
188 _olm_crypto_ed25519_sign(
189 &identity_keys.ed25519_key, message, message_length, signature
190 );
191 return this->signature_length();
192 }
193
194
get_one_time_keys_json_length() const195 std::size_t olm::Account::get_one_time_keys_json_length(
196 ) const {
197 std::size_t length = 0;
198 bool is_empty = true;
199 for (auto const & key : one_time_keys) {
200 if (key.published) {
201 continue;
202 }
203 is_empty = false;
204 length += 2; /* {" */
205 length += olm::encode_base64_length(_olm_pickle_uint32_length(key.id));
206 length += 3; /* ":" */
207 length += olm::encode_base64_length(sizeof(key.key.public_key));
208 length += 1; /* " */
209 }
210 if (is_empty) {
211 length += 1; /* { */
212 }
213 length += 3; /* }{} */
214 length += sizeof(KEY_JSON_CURVE25519) - 1;
215 return length;
216 }
217
218
get_one_time_keys_json(std::uint8_t * one_time_json,std::size_t one_time_json_length)219 std::size_t olm::Account::get_one_time_keys_json(
220 std::uint8_t * one_time_json, std::size_t one_time_json_length
221 ) {
222 std::uint8_t * pos = one_time_json;
223 if (one_time_json_length < get_one_time_keys_json_length()) {
224 last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
225 return std::size_t(-1);
226 }
227 *(pos++) = '{';
228 pos = write_string(pos, KEY_JSON_CURVE25519);
229 std::uint8_t sep = '{';
230 for (auto const & key : one_time_keys) {
231 if (key.published) {
232 continue;
233 }
234 *(pos++) = sep;
235 *(pos++) = '\"';
236 std::uint8_t key_id[_olm_pickle_uint32_length(key.id)];
237 _olm_pickle_uint32(key_id, key.id);
238 pos = olm::encode_base64(key_id, sizeof(key_id), pos);
239 *(pos++) = '\"'; *(pos++) = ':'; *(pos++) = '\"';
240 pos = olm::encode_base64(
241 key.key.public_key.public_key, sizeof(key.key.public_key.public_key), pos
242 );
243 *(pos++) = '\"';
244 sep = ',';
245 }
246 if (sep != ',') {
247 /* The list was empty */
248 *(pos++) = sep;
249 }
250 *(pos++) = '}';
251 *(pos++) = '}';
252 return pos - one_time_json;
253 }
254
255
mark_keys_as_published()256 std::size_t olm::Account::mark_keys_as_published(
257 ) {
258 std::size_t count = 0;
259 for (auto & key : one_time_keys) {
260 if (!key.published) {
261 key.published = true;
262 count++;
263 }
264 }
265 return count;
266 }
267
268
max_number_of_one_time_keys() const269 std::size_t olm::Account::max_number_of_one_time_keys(
270 ) const {
271 return olm::MAX_ONE_TIME_KEYS;
272 }
273
generate_one_time_keys_random_length(std::size_t number_of_keys) const274 std::size_t olm::Account::generate_one_time_keys_random_length(
275 std::size_t number_of_keys
276 ) const {
277 return CURVE25519_RANDOM_LENGTH * number_of_keys;
278 }
279
generate_one_time_keys(std::size_t number_of_keys,std::uint8_t const * random,std::size_t random_length)280 std::size_t olm::Account::generate_one_time_keys(
281 std::size_t number_of_keys,
282 std::uint8_t const * random, std::size_t random_length
283 ) {
284 if (random_length < generate_one_time_keys_random_length(number_of_keys)) {
285 last_error = OlmErrorCode::OLM_NOT_ENOUGH_RANDOM;
286 return std::size_t(-1);
287 }
288 for (unsigned i = 0; i < number_of_keys; ++i) {
289 OneTimeKey & key = *one_time_keys.insert(one_time_keys.begin());
290 key.id = ++next_one_time_key_id;
291 key.published = false;
292 _olm_crypto_curve25519_generate_key(random, &key.key);
293 random += CURVE25519_RANDOM_LENGTH;
294 }
295 return number_of_keys;
296 }
297
generate_fallback_key_random_length() const298 std::size_t olm::Account::generate_fallback_key_random_length() const {
299 return CURVE25519_RANDOM_LENGTH;
300 }
301
generate_fallback_key(std::uint8_t const * random,std::size_t random_length)302 std::size_t olm::Account::generate_fallback_key(
303 std::uint8_t const * random, std::size_t random_length
304 ) {
305 if (random_length < generate_fallback_key_random_length()) {
306 last_error = OlmErrorCode::OLM_NOT_ENOUGH_RANDOM;
307 return std::size_t(-1);
308 }
309 prev_fallback_key = current_fallback_key;
310 current_fallback_key.id = ++next_one_time_key_id;
311 current_fallback_key.published = true;
312 _olm_crypto_curve25519_generate_key(random, ¤t_fallback_key.key);
313 return 1;
314 }
315
316
get_fallback_key_json_length() const317 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 const OneTimeKey & key = current_fallback_key;
321 if (key.published) {
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
get_fallback_key_json(std::uint8_t * fallback_json,std::size_t fallback_json_length)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 (key.published) {
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 namespace olm {
360
pickle_length(olm::IdentityKeys const & value)361 static std::size_t pickle_length(
362 olm::IdentityKeys const & value
363 ) {
364 size_t length = 0;
365 length += _olm_pickle_ed25519_key_pair_length(&value.ed25519_key);
366 length += olm::pickle_length(value.curve25519_key);
367 return length;
368 }
369
370
pickle(std::uint8_t * pos,olm::IdentityKeys const & value)371 static std::uint8_t * pickle(
372 std::uint8_t * pos,
373 olm::IdentityKeys const & value
374 ) {
375 pos = _olm_pickle_ed25519_key_pair(pos, &value.ed25519_key);
376 pos = olm::pickle(pos, value.curve25519_key);
377 return pos;
378 }
379
380
unpickle(std::uint8_t const * pos,std::uint8_t const * end,olm::IdentityKeys & value)381 static std::uint8_t const * unpickle(
382 std::uint8_t const * pos, std::uint8_t const * end,
383 olm::IdentityKeys & value
384 ) {
385 pos = _olm_unpickle_ed25519_key_pair(pos, end, &value.ed25519_key); UNPICKLE_OK(pos);
386 pos = olm::unpickle(pos, end, value.curve25519_key); UNPICKLE_OK(pos);
387 return pos;
388 }
389
390
pickle_length(olm::OneTimeKey const & value)391 static std::size_t pickle_length(
392 olm::OneTimeKey const & value
393 ) {
394 std::size_t length = 0;
395 length += olm::pickle_length(value.id);
396 length += olm::pickle_length(value.published);
397 length += olm::pickle_length(value.key);
398 return length;
399 }
400
401
pickle(std::uint8_t * pos,olm::OneTimeKey const & value)402 static std::uint8_t * pickle(
403 std::uint8_t * pos,
404 olm::OneTimeKey const & value
405 ) {
406 pos = olm::pickle(pos, value.id);
407 pos = olm::pickle(pos, value.published);
408 pos = olm::pickle(pos, value.key);
409 return pos;
410 }
411
412
unpickle(std::uint8_t const * pos,std::uint8_t const * end,olm::OneTimeKey & value)413 static std::uint8_t const * unpickle(
414 std::uint8_t const * pos, std::uint8_t const * end,
415 olm::OneTimeKey & value
416 ) {
417 pos = olm::unpickle(pos, end, value.id); UNPICKLE_OK(pos);
418 pos = olm::unpickle(pos, end, value.published); UNPICKLE_OK(pos);
419 pos = olm::unpickle(pos, end, value.key); UNPICKLE_OK(pos);
420 return pos;
421 }
422
423 } // namespace olm
424
425 namespace {
426 // pickle version 1 used only 32 bytes for the ed25519 private key.
427 // Any keys thus used should be considered compromised.
428 // pickle version 2 does not have fallback keys.
429 static const std::uint32_t ACCOUNT_PICKLE_VERSION = 3;
430 }
431
432
pickle_length(olm::Account const & value)433 std::size_t olm::pickle_length(
434 olm::Account const & value
435 ) {
436 std::size_t length = 0;
437 length += olm::pickle_length(ACCOUNT_PICKLE_VERSION);
438 length += olm::pickle_length(value.identity_keys);
439 length += olm::pickle_length(value.one_time_keys);
440 length += olm::pickle_length(value.current_fallback_key);
441 length += olm::pickle_length(value.prev_fallback_key);
442 length += olm::pickle_length(value.next_one_time_key_id);
443 return length;
444 }
445
446
pickle(std::uint8_t * pos,olm::Account const & value)447 std::uint8_t * olm::pickle(
448 std::uint8_t * pos,
449 olm::Account const & value
450 ) {
451 pos = olm::pickle(pos, ACCOUNT_PICKLE_VERSION);
452 pos = olm::pickle(pos, value.identity_keys);
453 pos = olm::pickle(pos, value.one_time_keys);
454 pos = olm::pickle(pos, value.current_fallback_key);
455 pos = olm::pickle(pos, value.prev_fallback_key);
456 pos = olm::pickle(pos, value.next_one_time_key_id);
457 return pos;
458 }
459
460
unpickle(std::uint8_t const * pos,std::uint8_t const * end,olm::Account & value)461 std::uint8_t const * olm::unpickle(
462 std::uint8_t const * pos, std::uint8_t const * end,
463 olm::Account & value
464 ) {
465 uint32_t pickle_version;
466
467 pos = olm::unpickle(pos, end, pickle_version); UNPICKLE_OK(pos);
468
469 switch (pickle_version) {
470 case ACCOUNT_PICKLE_VERSION:
471 case 2:
472 break;
473 case 1:
474 value.last_error = OlmErrorCode::OLM_BAD_LEGACY_ACCOUNT_PICKLE;
475 return nullptr;
476 default:
477 value.last_error = OlmErrorCode::OLM_UNKNOWN_PICKLE_VERSION;
478 return nullptr;
479 }
480
481 pos = olm::unpickle(pos, end, value.identity_keys); UNPICKLE_OK(pos);
482 pos = olm::unpickle(pos, end, value.one_time_keys); UNPICKLE_OK(pos);
483
484 if (pickle_version == 2) {
485 // version 2 did not have fallback keys
486 value.current_fallback_key.published = false;
487 value.prev_fallback_key.published = false;
488 } else {
489 pos = olm::unpickle(pos, end, value.current_fallback_key); UNPICKLE_OK(pos);
490 pos = olm::unpickle(pos, end, value.prev_fallback_key); UNPICKLE_OK(pos);
491 }
492
493 pos = olm::unpickle(pos, end, value.next_one_time_key_id); UNPICKLE_OK(pos);
494
495 return pos;
496 }
497