session.cpp 15 KB
Newer Older
1
/* Copyright 2015, 2016 OpenMarket Ltd
Mark Haines's avatar
Mark Haines committed
2
3
4
5
6
7
8
9
10
11
12
13
14
 *
 * 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
#include "olm/session.hh"
16
#include "olm/cipher.h"
17
#include "olm/crypto.h"
18
19
20
21
#include "olm/account.hh"
#include "olm/memory.hh"
#include "olm/message.hh"
#include "olm/pickle.hh"
22
23

#include <cstring>
David Baker's avatar
David Baker committed
24
#include <stdio.h>
25
26
27
28
29

namespace {

static const std::uint8_t PROTOCOL_VERSION = 0x3;

30
31
32
static const std::uint8_t ROOT_KDF_INFO[] = "OLM_ROOT";
static const std::uint8_t RATCHET_KDF_INFO[] = "OLM_RATCHET";
static const std::uint8_t CIPHER_KDF_INFO[] = "OLM_KEYS";
33

34
static const olm::KdfInfo OLM_KDF_INFO = {
35
36
37
38
    ROOT_KDF_INFO, sizeof(ROOT_KDF_INFO) - 1,
    RATCHET_KDF_INFO, sizeof(RATCHET_KDF_INFO) - 1
};

39
40
static const struct _olm_cipher_aes_sha_256 OLM_CIPHER =
    OLM_CIPHER_INIT_AES_SHA_256(CIPHER_KDF_INFO);
41

42
43
} // namespace

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

}


52
std::size_t olm::Session::new_outbound_session_random_length() {
53
    return CURVE25519_RANDOM_LENGTH * 2;
54
55
56
}


57
58
std::size_t olm::Session::new_outbound_session(
    olm::Account const & local_account,
59
60
    _olm_curve25519_public_key const & identity_key,
    _olm_curve25519_public_key const & one_time_key,
61
62
63
    std::uint8_t const * random, std::size_t random_length
) {
    if (random_length < new_outbound_session_random_length()) {
64
        last_error = OlmErrorCode::OLM_NOT_ENOUGH_RANDOM;
65
66
67
        return std::size_t(-1);
    }

68
69
    _olm_curve25519_key_pair base_key;
    _olm_crypto_curve25519_generate_key(random, &base_key);
70

71
72
    _olm_curve25519_key_pair ratchet_key;
    _olm_crypto_curve25519_generate_key(random + CURVE25519_RANDOM_LENGTH, &ratchet_key);
73

74
    _olm_curve25519_key_pair const & alice_identity_key_pair = (
75
76
        local_account.identity_keys.curve25519_key
    );
77

78
    received_message = false;
79
80
    alice_identity_key = alice_identity_key_pair.public_key;
    alice_base_key = base_key.public_key;
81
    bob_one_time_key = one_time_key;
82

83
84
    // Calculate the shared secret S via triple DH
    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
96
    olm::unset(base_key);
    olm::unset(ratchet_key);
97
    olm::unset(secret);
98
99
100
101
102
103

    return std::size_t(0);
}

namespace {

104
static bool check_message_fields(
105
    olm::PreKeyMessageReader & reader, bool have_their_identity_key
106
107
) {
    bool ok = true;
108
109
    ok = ok && (have_their_identity_key || reader.identity_key);
    if (reader.identity_key) {
110
        ok = ok && reader.identity_key_length == CURVE25519_KEY_LENGTH;
111
    }
112
113
    ok = ok && reader.message;
    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
118
119
120
121
122
    return ok;
}

} // namespace


123
124
std::size_t olm::Session::new_inbound_session(
    olm::Account & local_account,
125
    _olm_curve25519_public_key const * their_identity_key,
126
127
    std::uint8_t const * one_time_key_message, std::size_t message_length
) {
128
    olm::PreKeyMessageReader reader;
129
130
    decode_one_time_key_message(reader, one_time_key_message, message_length);

131
    if (!check_message_fields(reader, their_identity_key)) {
132
        last_error = OlmErrorCode::OLM_BAD_MESSAGE_FORMAT;
133
134
135
        return std::size_t(-1);
    }

136
137
    if (reader.identity_key && their_identity_key) {
        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
143
144
145
            return std::size_t(-1);
        }
    }

146
147
148
149
    olm::load_array(alice_identity_key.public_key, reader.identity_key);
    olm::load_array(alice_base_key.public_key, reader.base_key);
    olm::load_array(bob_one_time_key.public_key, reader.one_time_key);

150
    olm::MessageReader message_reader;
151
152
    decode_message(
        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
160
161
        return std::size_t(-1);
    }

162
    _olm_curve25519_public_key ratchet_key;
163
    olm::load_array(ratchet_key.public_key, message_reader.ratchet_key);
164

165
166
    olm::OneTimeKey const * our_one_time_key = local_account.lookup_key(
        bob_one_time_key
167
168
    );

169
    if (!our_one_time_key) {
170
        last_error = OlmErrorCode::OLM_BAD_MESSAGE_KEY_ID;
171
172
173
        return std::size_t(-1);
    }

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
180
    // Calculate the shared secret S via triple DH
    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
193
194
195
    return std::size_t(0);
}


196
std::size_t olm::Session::session_id_length() {
197
    return SHA256_OUTPUT_LENGTH;
198
199
200
201
202
203
204
}


std::size_t olm::Session::session_id(
    std::uint8_t * id, std::size_t id_length
) {
    if (id_length < session_id_length()) {
205
        last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
206
207
        return std::size_t(-1);
    }
208
    std::uint8_t tmp[CURVE25519_KEY_LENGTH * 3];
209
210
211
212
    std::uint8_t * pos = tmp;
    pos = olm::store_array(pos, alice_identity_key.public_key);
    pos = olm::store_array(pos, alice_base_key.public_key);
    pos = olm::store_array(pos, bob_one_time_key.public_key);
213
    _olm_crypto_sha256(tmp, sizeof(tmp), id);
214
215
216
217
    return session_id_length();
}


218
bool olm::Session::matches_inbound_session(
219
    _olm_curve25519_public_key const * their_identity_key,
220
221
    std::uint8_t const * one_time_key_message, std::size_t message_length
) {
222
    olm::PreKeyMessageReader reader;
223
224
    decode_one_time_key_message(reader, one_time_key_message, message_length);

225
    if (!check_message_fields(reader, their_identity_key)) {
226
227
228
229
        return false;
    }

    bool same = true;
230
231
    if (reader.identity_key) {
        same = same && 0 == std::memcmp(
232
            reader.identity_key, alice_identity_key.public_key, CURVE25519_KEY_LENGTH
233
234
235
236
237
        );
    }
    if (their_identity_key) {
        same = same && 0 == std::memcmp(
            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
248
249
250
    return same;
}


251
olm::MessageType olm::Session::encrypt_message_type() {
252
    if (received_message) {
253
        return olm::MessageType::MESSAGE;
254
    } else {
255
        return olm::MessageType::PRE_KEY;
256
257
258
259
    }
}


260
std::size_t olm::Session::encrypt_message_length(
261
262
263
264
265
266
267
268
269
270
271
    std::size_t plaintext_length
) {
    std::size_t message_length = ratchet.encrypt_output_length(
        plaintext_length
    );

    if (received_message) {
        return message_length;
    }

    return encode_one_time_key_message_length(
272
273
274
        CURVE25519_KEY_LENGTH,
        CURVE25519_KEY_LENGTH,
        CURVE25519_KEY_LENGTH,
275
276
277
278
279
        message_length
    );
}


280
std::size_t olm::Session::encrypt_random_length() {
281
282
283
284
    return ratchet.encrypt_random_length();
}


285
std::size_t olm::Session::encrypt(
286
287
288
289
290
    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
) {
    if (message_length < encrypt_message_length(plaintext_length)) {
291
        last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
292
293
294
295
296
297
298
299
300
301
        return std::size_t(-1);
    }
    std::uint8_t * message_body;
    std::size_t message_body_length = ratchet.encrypt_output_length(
        plaintext_length
    );

    if (received_message) {
        message_body = message;
    } else {
302
        olm::PreKeyMessageWriter writer;
303
304
305
        encode_one_time_key_message(
            writer,
            PROTOCOL_VERSION,
306
307
308
            CURVE25519_KEY_LENGTH,
            CURVE25519_KEY_LENGTH,
            CURVE25519_KEY_LENGTH,
309
310
311
            message_body_length,
            message
        );
312
313
314
        olm::store_array(writer.one_time_key, bob_one_time_key.public_key);
        olm::store_array(writer.identity_key, alice_identity_key.public_key);
        olm::store_array(writer.base_key, alice_base_key.public_key);
315
316
317
318
319
320
321
322
323
324
325
        message_body = writer.message;
    }

    std::size_t result = ratchet.encrypt(
        plaintext, plaintext_length,
        random, random_length,
        message_body, message_body_length
    );

    if (result == std::size_t(-1)) {
        last_error = ratchet.last_error;
326
        ratchet.last_error = OlmErrorCode::OLM_SUCCESS;
327
        return result;
328
    }
329

330
331
332
333
    return result;
}


334
std::size_t olm::Session::decrypt_max_plaintext_length(
335
336
337
338
339
    MessageType message_type,
    std::uint8_t const * message, std::size_t message_length
) {
    std::uint8_t const * message_body;
    std::size_t message_body_length;
340
    if (message_type == olm::MessageType::MESSAGE) {
341
342
343
        message_body = message;
        message_body_length = message_length;
    } else {
344
        olm::PreKeyMessageReader reader;
345
346
        decode_one_time_key_message(reader, message, message_length);
        if (!reader.message) {
347
            last_error = OlmErrorCode::OLM_BAD_MESSAGE_FORMAT;
348
349
350
351
352
353
354
355
356
357
358
359
            return std::size_t(-1);
        }
        message_body = reader.message;
        message_body_length = reader.message_length;
    }

    std::size_t result = ratchet.decrypt_max_plaintext_length(
        message_body, message_body_length
    );

    if (result == std::size_t(-1)) {
        last_error = ratchet.last_error;
360
        ratchet.last_error = OlmErrorCode::OLM_SUCCESS;
361
362
363
364
365
    }
    return result;
}


366
367
std::size_t olm::Session::decrypt(
    olm::MessageType message_type,
368
369
370
371
372
    std::uint8_t const * message, std::size_t message_length,
    std::uint8_t * plaintext, std::size_t max_plaintext_length
) {
    std::uint8_t const * message_body;
    std::size_t message_body_length;
373
    if (message_type == olm::MessageType::MESSAGE) {
374
375
376
        message_body = message;
        message_body_length = message_length;
    } else {
377
        olm::PreKeyMessageReader reader;
378
379
        decode_one_time_key_message(reader, message, message_length);
        if (!reader.message) {
380
            last_error = OlmErrorCode::OLM_BAD_MESSAGE_FORMAT;
381
382
383
384
385
386
387
388
389
390
391
392
            return std::size_t(-1);
        }
        message_body = reader.message;
        message_body_length = reader.message_length;
    }

    std::size_t result = ratchet.decrypt(
        message_body, message_body_length, plaintext, max_plaintext_length
    );

    if (result == std::size_t(-1)) {
        last_error = ratchet.last_error;
393
        ratchet.last_error = OlmErrorCode::OLM_SUCCESS;
394
        return result;
395
    }
396
397

    received_message = true;
398
399
    return result;
}
400

401
402
403
void olm::Session::describe(char *describe_buffer, size_t buflen) {
    if (buflen == 0) return;

David Baker's avatar
David Baker committed
404
405
406
407
    describe_buffer[0] = '\0';
    char *buf_pos = describe_buffer;

    buf_pos += snprintf(
408
        buf_pos, buflen - (buf_pos - describe_buffer),
David Baker's avatar
David Baker committed
409
410
411
        "sender chain index: %d ", ratchet.sender_chain[0].chain_key.index
    );

412
    buf_pos += snprintf(buf_pos, buflen - (buf_pos - describe_buffer), "receiver chain indcies:");
David Baker's avatar
David Baker committed
413
414
    for (size_t i = 0; i < ratchet.receiver_chains.size(); ++i) {
        buf_pos += snprintf(
415
            buf_pos, buflen - (buf_pos - describe_buffer),
David Baker's avatar
David Baker committed
416
417
418
419
            " %d", ratchet.receiver_chains[i].chain_key.index
        );
    }

420
    buf_pos += snprintf(buf_pos, buflen - (buf_pos - describe_buffer), " skipped message keys:");
David Baker's avatar
David Baker committed
421
422
    for (size_t i = 0; i < ratchet.skipped_message_keys.size(); ++i) {
        buf_pos += snprintf(
423
            buf_pos, buflen - (buf_pos - describe_buffer),
David Baker's avatar
David Baker committed
424
425
426
427
428
            " %d", ratchet.skipped_message_keys[i].message_key.index
        );
    }
}

429
namespace {
Richard van der Hoff's avatar
Richard van der Hoff committed
430
431
432
// the master branch writes pickle version 1; the logging_enabled branch writes
// 0x80000001.
static const std::uint32_t SESSION_PICKLE_VERSION = 1;
433
}
434

435
std::size_t olm::pickle_length(
436
437
438
    Session const & value
) {
    std::size_t length = 0;
439
    length += olm::pickle_length(SESSION_PICKLE_VERSION);
440
    length += olm::pickle_length(value.received_message);
441
    length += olm::pickle_length(value.alice_identity_key);
442
    length += olm::pickle_length(value.alice_base_key);
443
    length += olm::pickle_length(value.bob_one_time_key);
444
    length += olm::pickle_length(value.ratchet);
445
446
447
448
    return length;
}


449
std::uint8_t * olm::pickle(
450
451
452
    std::uint8_t * pos,
    Session const & value
) {
453
    pos = olm::pickle(pos, SESSION_PICKLE_VERSION);
454
    pos = olm::pickle(pos, value.received_message);
455
    pos = olm::pickle(pos, value.alice_identity_key);
456
    pos = olm::pickle(pos, value.alice_base_key);
457
    pos = olm::pickle(pos, value.bob_one_time_key);
458
    pos = olm::pickle(pos, value.ratchet);
459
460
461
462
    return pos;
}


463
std::uint8_t const * olm::unpickle(
464
465
466
    std::uint8_t const * pos, std::uint8_t const * end,
    Session & value
) {
467
468
    uint32_t pickle_version;
    pos = olm::unpickle(pos, end, pickle_version);
469
470
471
472
473
474
475

    bool includes_chain_index;
    switch (pickle_version) {
        case 1:
            includes_chain_index = false;
            break;

Richard van der Hoff's avatar
Richard van der Hoff committed
476
        case 0x80000001UL:
477
478
479
480
481
482
            includes_chain_index = true;
            break;

        default:
            value.last_error = OlmErrorCode::OLM_UNKNOWN_PICKLE_VERSION;
            return end;
483
    }
484

485
    pos = olm::unpickle(pos, end, value.received_message);
486
    pos = olm::unpickle(pos, end, value.alice_identity_key);
487
    pos = olm::unpickle(pos, end, value.alice_base_key);
488
    pos = olm::unpickle(pos, end, value.bob_one_time_key);
489
    pos = olm::unpickle(pos, end, value.ratchet, includes_chain_index);
490
491
    return pos;
}