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/session.hh"
16 #include "olm/cipher.h"
17 #include "olm/crypto.h"
18 #include "olm/account.hh"
19 #include "olm/memory.hh"
20 #include "olm/message.hh"
21 #include "olm/pickle.hh"
22 
23 #include <cstring>
24 #include <stdio.h>
25 
26 namespace {
27 
28 static const std::uint8_t PROTOCOL_VERSION = 0x3;
29 
30 static const std::uint8_t ROOT_KDF_INFO[] = "OLM_ROOT";
31 static const std::uint8_t RATCHET_KDF_INFO[] = "OLM_RATCHET";
32 static const std::uint8_t CIPHER_KDF_INFO[] = "OLM_KEYS";
33 
34 static const olm::KdfInfo OLM_KDF_INFO = {
35     ROOT_KDF_INFO, sizeof(ROOT_KDF_INFO) - 1,
36     RATCHET_KDF_INFO, sizeof(RATCHET_KDF_INFO) - 1
37 };
38 
39 static const struct _olm_cipher_aes_sha_256 OLM_CIPHER =
40     OLM_CIPHER_INIT_AES_SHA_256(CIPHER_KDF_INFO);
41 
42 } // namespace
43 
Session()44 olm::Session::Session(
45 ) : ratchet(OLM_KDF_INFO, OLM_CIPHER_BASE(&OLM_CIPHER)),
46     last_error(OlmErrorCode::OLM_SUCCESS),
47     received_message(false) {
48 
49 }
50 
51 
new_outbound_session_random_length() const52 std::size_t olm::Session::new_outbound_session_random_length() const {
53     return CURVE25519_RANDOM_LENGTH * 2;
54 }
55 
56 
new_outbound_session(olm::Account const & local_account,_olm_curve25519_public_key const & identity_key,_olm_curve25519_public_key const & one_time_key,std::uint8_t const * random,std::size_t random_length)57 std::size_t olm::Session::new_outbound_session(
58     olm::Account const & local_account,
59     _olm_curve25519_public_key const & identity_key,
60     _olm_curve25519_public_key const & one_time_key,
61     std::uint8_t const * random, std::size_t random_length
62 ) {
63     if (random_length < new_outbound_session_random_length()) {
64         last_error = OlmErrorCode::OLM_NOT_ENOUGH_RANDOM;
65         return std::size_t(-1);
66     }
67 
68     _olm_curve25519_key_pair base_key;
69     _olm_crypto_curve25519_generate_key(random, &base_key);
70 
71     _olm_curve25519_key_pair ratchet_key;
72     _olm_crypto_curve25519_generate_key(random + CURVE25519_RANDOM_LENGTH, &ratchet_key);
73 
74     _olm_curve25519_key_pair const & alice_identity_key_pair = (
75         local_account.identity_keys.curve25519_key
76     );
77 
78     received_message = false;
79     alice_identity_key = alice_identity_key_pair.public_key;
80     alice_base_key = base_key.public_key;
81     bob_one_time_key = one_time_key;
82 
83     // Calculate the shared secret S via triple DH
84     std::uint8_t secret[3 * CURVE25519_SHARED_SECRET_LENGTH];
85     std::uint8_t * pos = secret;
86 
87     _olm_crypto_curve25519_shared_secret(&alice_identity_key_pair, &one_time_key, pos);
88     pos += CURVE25519_SHARED_SECRET_LENGTH;
89     _olm_crypto_curve25519_shared_secret(&base_key, &identity_key, pos);
90     pos += CURVE25519_SHARED_SECRET_LENGTH;
91     _olm_crypto_curve25519_shared_secret(&base_key, &one_time_key, pos);
92 
93     ratchet.initialise_as_alice(secret, sizeof(secret), ratchet_key);
94 
95     olm::unset(base_key);
96     olm::unset(ratchet_key);
97     olm::unset(secret);
98 
99     return std::size_t(0);
100 }
101 
102 namespace {
103 
check_message_fields(olm::PreKeyMessageReader & reader,bool have_their_identity_key)104 static bool check_message_fields(
105     olm::PreKeyMessageReader & reader, bool have_their_identity_key
106 ) {
107     bool ok = true;
108     ok = ok && (have_their_identity_key || reader.identity_key);
109     if (reader.identity_key) {
110         ok = ok && reader.identity_key_length == CURVE25519_KEY_LENGTH;
111     }
112     ok = ok && reader.message;
113     ok = ok && reader.base_key;
114     ok = ok && reader.base_key_length == CURVE25519_KEY_LENGTH;
115     ok = ok && reader.one_time_key;
116     ok = ok && reader.one_time_key_length == CURVE25519_KEY_LENGTH;
117     return ok;
118 }
119 
120 } // namespace
121 
122 
new_inbound_session(olm::Account & local_account,_olm_curve25519_public_key const * their_identity_key,std::uint8_t const * one_time_key_message,std::size_t message_length)123 std::size_t olm::Session::new_inbound_session(
124     olm::Account & local_account,
125     _olm_curve25519_public_key const * their_identity_key,
126     std::uint8_t const * one_time_key_message, std::size_t message_length
127 ) {
128     olm::PreKeyMessageReader reader;
129     decode_one_time_key_message(reader, one_time_key_message, message_length);
130 
131     if (!check_message_fields(reader, their_identity_key)) {
132         last_error = OlmErrorCode::OLM_BAD_MESSAGE_FORMAT;
133         return std::size_t(-1);
134     }
135 
136     if (reader.identity_key && their_identity_key) {
137         bool same = 0 == std::memcmp(
138             their_identity_key->public_key, reader.identity_key, CURVE25519_KEY_LENGTH
139         );
140         if (!same) {
141             last_error = OlmErrorCode::OLM_BAD_MESSAGE_KEY_ID;
142             return std::size_t(-1);
143         }
144     }
145 
146     olm::load_array(alice_identity_key.public_key, reader.identity_key);
147     olm::load_array(alice_base_key.public_key, reader.base_key);
148     olm::load_array(bob_one_time_key.public_key, reader.one_time_key);
149 
150     olm::MessageReader message_reader;
151     decode_message(
152         message_reader, reader.message, reader.message_length,
153         ratchet.ratchet_cipher->ops->mac_length(ratchet.ratchet_cipher)
154     );
155 
156     if (!message_reader.ratchet_key
157             || message_reader.ratchet_key_length != CURVE25519_KEY_LENGTH) {
158         last_error = OlmErrorCode::OLM_BAD_MESSAGE_FORMAT;
159         return std::size_t(-1);
160     }
161 
162     _olm_curve25519_public_key ratchet_key;
163     olm::load_array(ratchet_key.public_key, message_reader.ratchet_key);
164 
165     olm::OneTimeKey const * our_one_time_key = local_account.lookup_key(
166         bob_one_time_key
167     );
168 
169     if (!our_one_time_key) {
170         last_error = OlmErrorCode::OLM_BAD_MESSAGE_KEY_ID;
171         return std::size_t(-1);
172     }
173 
174     _olm_curve25519_key_pair const & bob_identity_key = (
175         local_account.identity_keys.curve25519_key
176     );
177     _olm_curve25519_key_pair const & bob_one_time_key = our_one_time_key->key;
178 
179     // Calculate the shared secret S via triple DH
180     std::uint8_t secret[CURVE25519_SHARED_SECRET_LENGTH * 3];
181     std::uint8_t * pos = secret;
182     _olm_crypto_curve25519_shared_secret(&bob_one_time_key, &alice_identity_key, pos);
183     pos += CURVE25519_SHARED_SECRET_LENGTH;
184     _olm_crypto_curve25519_shared_secret(&bob_identity_key, &alice_base_key, pos);
185     pos += CURVE25519_SHARED_SECRET_LENGTH;
186     _olm_crypto_curve25519_shared_secret(&bob_one_time_key, &alice_base_key, pos);
187 
188     ratchet.initialise_as_bob(secret, sizeof(secret), ratchet_key);
189 
190     olm::unset(secret);
191 
192     return std::size_t(0);
193 }
194 
195 
session_id_length() const196 std::size_t olm::Session::session_id_length() const {
197     return SHA256_OUTPUT_LENGTH;
198 }
199 
200 
session_id(std::uint8_t * id,std::size_t id_length)201 std::size_t olm::Session::session_id(
202     std::uint8_t * id, std::size_t id_length
203 ) {
204     if (id_length < session_id_length()) {
205         last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
206         return std::size_t(-1);
207     }
208     std::uint8_t tmp[CURVE25519_KEY_LENGTH * 3];
209     std::uint8_t * pos = tmp;
210     pos = olm::store_array(pos, alice_identity_key.public_key);
211     pos = olm::store_array(pos, alice_base_key.public_key);
212     pos = olm::store_array(pos, bob_one_time_key.public_key);
213     _olm_crypto_sha256(tmp, sizeof(tmp), id);
214     return session_id_length();
215 }
216 
217 
matches_inbound_session(_olm_curve25519_public_key const * their_identity_key,std::uint8_t const * one_time_key_message,std::size_t message_length) const218 bool olm::Session::matches_inbound_session(
219     _olm_curve25519_public_key const * their_identity_key,
220     std::uint8_t const * one_time_key_message, std::size_t message_length
221 ) const {
222     olm::PreKeyMessageReader reader;
223     decode_one_time_key_message(reader, one_time_key_message, message_length);
224 
225     if (!check_message_fields(reader, their_identity_key)) {
226         return false;
227     }
228 
229     bool same = true;
230     if (reader.identity_key) {
231         same = same && 0 == std::memcmp(
232             reader.identity_key, alice_identity_key.public_key, CURVE25519_KEY_LENGTH
233         );
234     }
235     if (their_identity_key) {
236         same = same && 0 == std::memcmp(
237             their_identity_key->public_key, alice_identity_key.public_key,
238             CURVE25519_KEY_LENGTH
239         );
240     }
241     same = same && 0 == std::memcmp(
242         reader.base_key, alice_base_key.public_key, CURVE25519_KEY_LENGTH
243     );
244     same = same && 0 == std::memcmp(
245         reader.one_time_key, bob_one_time_key.public_key, CURVE25519_KEY_LENGTH
246     );
247     return same;
248 }
249 
250 
encrypt_message_type() const251 olm::MessageType olm::Session::encrypt_message_type() const {
252     if (received_message) {
253         return olm::MessageType::MESSAGE;
254     } else {
255         return olm::MessageType::PRE_KEY;
256     }
257 }
258 
259 
encrypt_message_length(std::size_t plaintext_length) const260 std::size_t olm::Session::encrypt_message_length(
261     std::size_t plaintext_length
262 ) const {
263     std::size_t message_length = ratchet.encrypt_output_length(
264         plaintext_length
265     );
266 
267     if (received_message) {
268         return message_length;
269     }
270 
271     return encode_one_time_key_message_length(
272         CURVE25519_KEY_LENGTH,
273         CURVE25519_KEY_LENGTH,
274         CURVE25519_KEY_LENGTH,
275         message_length
276     );
277 }
278 
279 
encrypt_random_length() const280 std::size_t olm::Session::encrypt_random_length() const {
281     return ratchet.encrypt_random_length();
282 }
283 
284 
encrypt(std::uint8_t const * plaintext,std::size_t plaintext_length,std::uint8_t const * random,std::size_t random_length,std::uint8_t * message,std::size_t message_length)285 std::size_t olm::Session::encrypt(
286     std::uint8_t const * plaintext, std::size_t plaintext_length,
287     std::uint8_t const * random, std::size_t random_length,
288     std::uint8_t * message, std::size_t message_length
289 ) {
290     if (message_length < encrypt_message_length(plaintext_length)) {
291         last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
292         return std::size_t(-1);
293     }
294     std::uint8_t * message_body;
295     std::size_t message_body_length = ratchet.encrypt_output_length(
296         plaintext_length
297     );
298 
299     if (received_message) {
300         message_body = message;
301     } else {
302         olm::PreKeyMessageWriter writer;
303         encode_one_time_key_message(
304             writer,
305             PROTOCOL_VERSION,
306             CURVE25519_KEY_LENGTH,
307             CURVE25519_KEY_LENGTH,
308             CURVE25519_KEY_LENGTH,
309             message_body_length,
310             message
311         );
312         olm::store_array(writer.one_time_key, bob_one_time_key.public_key);
313         olm::store_array(writer.identity_key, alice_identity_key.public_key);
314         olm::store_array(writer.base_key, alice_base_key.public_key);
315         message_body = writer.message;
316     }
317 
318     std::size_t result = ratchet.encrypt(
319         plaintext, plaintext_length,
320         random, random_length,
321         message_body, message_body_length
322     );
323 
324     if (result == std::size_t(-1)) {
325         last_error = ratchet.last_error;
326         ratchet.last_error = OlmErrorCode::OLM_SUCCESS;
327         return result;
328     }
329 
330     return result;
331 }
332 
333 
decrypt_max_plaintext_length(MessageType message_type,std::uint8_t const * message,std::size_t message_length)334 std::size_t olm::Session::decrypt_max_plaintext_length(
335     MessageType message_type,
336     std::uint8_t const * message, std::size_t message_length
337 ) {
338     std::uint8_t const * message_body;
339     std::size_t message_body_length;
340     if (message_type == olm::MessageType::MESSAGE) {
341         message_body = message;
342         message_body_length = message_length;
343     } else {
344         olm::PreKeyMessageReader reader;
345         decode_one_time_key_message(reader, message, message_length);
346         if (!reader.message) {
347             last_error = OlmErrorCode::OLM_BAD_MESSAGE_FORMAT;
348             return std::size_t(-1);
349         }
350         message_body = reader.message;
351         message_body_length = reader.message_length;
352     }
353 
354     std::size_t result = ratchet.decrypt_max_plaintext_length(
355         message_body, message_body_length
356     );
357 
358     if (result == std::size_t(-1)) {
359         last_error = ratchet.last_error;
360         ratchet.last_error = OlmErrorCode::OLM_SUCCESS;
361     }
362     return result;
363 }
364 
365 
decrypt(olm::MessageType message_type,std::uint8_t const * message,std::size_t message_length,std::uint8_t * plaintext,std::size_t max_plaintext_length)366 std::size_t olm::Session::decrypt(
367     olm::MessageType message_type,
368     std::uint8_t const * message, std::size_t message_length,
369     std::uint8_t * plaintext, std::size_t max_plaintext_length
370 ) {
371     std::uint8_t const * message_body;
372     std::size_t message_body_length;
373     if (message_type == olm::MessageType::MESSAGE) {
374         message_body = message;
375         message_body_length = message_length;
376     } else {
377         olm::PreKeyMessageReader reader;
378         decode_one_time_key_message(reader, message, message_length);
379         if (!reader.message) {
380             last_error = OlmErrorCode::OLM_BAD_MESSAGE_FORMAT;
381             return std::size_t(-1);
382         }
383         message_body = reader.message;
384         message_body_length = reader.message_length;
385     }
386 
387     std::size_t result = ratchet.decrypt(
388         message_body, message_body_length, plaintext, max_plaintext_length
389     );
390 
391     if (result == std::size_t(-1)) {
392         last_error = ratchet.last_error;
393         ratchet.last_error = OlmErrorCode::OLM_SUCCESS;
394         return result;
395     }
396 
397     received_message = true;
398     return result;
399 }
400 
describe(char * describe_buffer,size_t buflen)401 void olm::Session::describe(char *describe_buffer, size_t buflen) {
402     if (buflen == 0) return;
403 
404     describe_buffer[0] = '\0';
405     char *buf_pos = describe_buffer;
406 
407     int size;
408 
409     size = snprintf(
410         buf_pos, buflen - (buf_pos - describe_buffer),
411         "sender chain index: %d ", ratchet.sender_chain[0].chain_key.index
412     );
413     if (size > 0) buf_pos += size;
414 
415     size = snprintf(buf_pos, buflen - (buf_pos - describe_buffer), "receiver chain indices:");
416     if (size > 0) buf_pos += size;
417     for (size_t i = 0; i < ratchet.receiver_chains.size(); ++i) {
418         size = snprintf(
419             buf_pos, buflen - (buf_pos - describe_buffer),
420             " %d", ratchet.receiver_chains[i].chain_key.index
421         );
422         if (size > 0) buf_pos += size;
423     }
424 
425     size = snprintf(buf_pos, buflen - (buf_pos - describe_buffer), " skipped message keys:");
426     if (size >= 0) buf_pos += size;
427     for (size_t i = 0; i < ratchet.skipped_message_keys.size(); ++i) {
428         size = snprintf(
429             buf_pos, buflen - (buf_pos - describe_buffer),
430             " %d", ratchet.skipped_message_keys[i].message_key.index
431         );
432         if (size > 0) buf_pos += size;
433     }
434 }
435 
436 namespace {
437 // the master branch writes pickle version 1; the logging_enabled branch writes
438 // 0x80000001.
439 static const std::uint32_t SESSION_PICKLE_VERSION = 1;
440 }
441 
pickle_length(Session const & value)442 std::size_t olm::pickle_length(
443     Session const & value
444 ) {
445     std::size_t length = 0;
446     length += olm::pickle_length(SESSION_PICKLE_VERSION);
447     length += olm::pickle_length(value.received_message);
448     length += olm::pickle_length(value.alice_identity_key);
449     length += olm::pickle_length(value.alice_base_key);
450     length += olm::pickle_length(value.bob_one_time_key);
451     length += olm::pickle_length(value.ratchet);
452     return length;
453 }
454 
455 
pickle(std::uint8_t * pos,Session const & value)456 std::uint8_t * olm::pickle(
457     std::uint8_t * pos,
458     Session const & value
459 ) {
460     pos = olm::pickle(pos, SESSION_PICKLE_VERSION);
461     pos = olm::pickle(pos, value.received_message);
462     pos = olm::pickle(pos, value.alice_identity_key);
463     pos = olm::pickle(pos, value.alice_base_key);
464     pos = olm::pickle(pos, value.bob_one_time_key);
465     pos = olm::pickle(pos, value.ratchet);
466     return pos;
467 }
468 
469 
unpickle(std::uint8_t const * pos,std::uint8_t const * end,Session & value)470 std::uint8_t const * olm::unpickle(
471     std::uint8_t const * pos, std::uint8_t const * end,
472     Session & value
473 ) {
474     uint32_t pickle_version;
475     pos = olm::unpickle(pos, end, pickle_version); UNPICKLE_OK(pos);
476 
477     bool includes_chain_index;
478     switch (pickle_version) {
479         case 1:
480             includes_chain_index = false;
481             break;
482 
483         case 0x80000001UL:
484             includes_chain_index = true;
485             break;
486 
487         default:
488             value.last_error = OlmErrorCode::OLM_UNKNOWN_PICKLE_VERSION;
489             return nullptr;
490     }
491 
492     pos = olm::unpickle(pos, end, value.received_message); UNPICKLE_OK(pos);
493     pos = olm::unpickle(pos, end, value.alice_identity_key); UNPICKLE_OK(pos);
494     pos = olm::unpickle(pos, end, value.alice_base_key); UNPICKLE_OK(pos);
495     pos = olm::unpickle(pos, end, value.bob_one_time_key); UNPICKLE_OK(pos);
496     pos = olm::unpickle(pos, end, value.ratchet, includes_chain_index); UNPICKLE_OK(pos);
497 
498     return pos;
499 }
500