session.cpp 12.2 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.
 */
15
16
17
18
19
20
21
#include "olm/session.hh"
#include "olm/cipher.hh"
#include "olm/crypto.hh"
#include "olm/account.hh"
#include "olm/memory.hh"
#include "olm/message.hh"
#include "olm/pickle.hh"
22
23
24
25
26
27
28
29

#include <cstring>

namespace {

static const std::size_t KEY_LENGTH = 32;
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::CipherAesSha256 OLM_CIPHER(
35
36
37
    CIPHER_KDF_INFO, sizeof(CIPHER_KDF_INFO) -1
);

38
static const olm::KdfInfo OLM_KDF_INFO = {
39
40
41
42
43
44
    ROOT_KDF_INFO, sizeof(ROOT_KDF_INFO) - 1,
    RATCHET_KDF_INFO, sizeof(RATCHET_KDF_INFO) - 1
};

} // namespace

45
46
47
olm::Session::Session(
) : ratchet(OLM_KDF_INFO, OLM_CIPHER),
    last_error(olm::ErrorCode::SUCCESS),
48
    received_message(false) {
49
50
51
52

}


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


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

    Curve25519KeyPair base_key;
70
    olm::curve25519_generate_key(random, base_key);
71

72
    Curve25519KeyPair ratchet_key;
73
    olm::curve25519_generate_key(random + 32, ratchet_key);
74

75
    received_message = false;
76
    alice_identity_key = local_account.identity_keys.curve25519_key;
77
    alice_base_key = base_key;
78
    bob_one_time_key = one_time_key;
79

80
    std::uint8_t shared_secret[96];
81

82
    olm::curve25519_shared_secret(
83
        local_account.identity_keys.curve25519_key,
84
        one_time_key, shared_secret
85
    );
86
    olm::curve25519_shared_secret(
87
         base_key, identity_key, shared_secret + 32
88
    );
89
    olm::curve25519_shared_secret(
90
         base_key, one_time_key, shared_secret + 64
91
92
    );

93
94
    ratchet.initialise_as_alice(shared_secret, 96, ratchet_key);

95
96
97
    olm::unset(base_key);
    olm::unset(ratchet_key);
    olm::unset(shared_secret);
98
99
100
101
102
103
104

    return std::size_t(0);
}

namespace {

bool check_message_fields(
105
    olm::PreKeyMessageReader & reader, bool have_their_identity_key
106
107
) {
    bool ok = true;
108
109
110
111
    ok = ok && (have_their_identity_key || reader.identity_key);
    if (reader.identity_key) {
        ok = ok && reader.identity_key_length == KEY_LENGTH;
    }
112
113
114
    ok = ok && reader.message;
    ok = ok && reader.base_key;
    ok = ok && reader.base_key_length == KEY_LENGTH;
115
116
    ok = ok && reader.one_time_key;
    ok = ok && reader.one_time_key_length == 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::Curve25519PublicKey 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 = olm::ErrorCode::BAD_MESSAGE_FORMAT;
133
134
135
        return std::size_t(-1);
    }

136
137
138
139
140
141
142
143
144
145
    if (reader.identity_key && their_identity_key) {
        bool same = 0 == std::memcmp(
            their_identity_key->public_key, reader.identity_key, KEY_LENGTH
        );
        if (!same) {
            last_error = olm::ErrorCode::BAD_MESSAGE_KEY_ID;
            return std::size_t(-1);
        }
    }

146
    olm::MessageReader message_reader;
147
148
149
150
151
152
153
    decode_message(
        message_reader, reader.message, reader.message_length,
        ratchet.ratchet_cipher.mac_length()
    );

    if (!message_reader.ratchet_key
            || message_reader.ratchet_key_length != KEY_LENGTH) {
154
        last_error = olm::ErrorCode::BAD_MESSAGE_FORMAT;
155
156
157
        return std::size_t(-1);
    }

158
    std::memcpy(alice_identity_key.public_key, reader.identity_key, 32);
159
    std::memcpy(alice_base_key.public_key, reader.base_key, 32);
160
    std::memcpy(bob_one_time_key.public_key, reader.one_time_key, 32);
161
    olm::Curve25519PublicKey ratchet_key;
162
    std::memcpy(ratchet_key.public_key, message_reader.ratchet_key, 32);
163

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

168
    if (!our_one_time_key) {
169
        last_error = olm::ErrorCode::BAD_MESSAGE_KEY_ID;
170
171
172
        return std::size_t(-1);
    }

173
    std::uint8_t shared_secret[96];
174

175
    olm::curve25519_shared_secret(
176
        our_one_time_key->key, alice_identity_key, shared_secret
177
    );
178
    olm::curve25519_shared_secret(
179
180
        local_account.identity_keys.curve25519_key,
        alice_base_key, shared_secret + 32
181
    );
182
    olm::curve25519_shared_secret(
183
        our_one_time_key->key, alice_base_key, shared_secret + 64
184
185
    );

186
    ratchet.initialise_as_bob(shared_secret, 96, ratchet_key);
187
188
189
190
191

    return std::size_t(0);
}


192
bool olm::Session::matches_inbound_session(
193
    olm::Curve25519PublicKey const * their_identity_key,
194
195
    std::uint8_t const * one_time_key_message, std::size_t message_length
) {
196
    olm::PreKeyMessageReader reader;
197
198
    decode_one_time_key_message(reader, one_time_key_message, message_length);

199
    if (!check_message_fields(reader, their_identity_key)) {
200
201
202
203
        return false;
    }

    bool same = true;
204
205
206
207
208
209
210
211
212
213
214
    if (reader.identity_key) {
        same = same && 0 == std::memcmp(
            reader.identity_key, alice_identity_key.public_key, KEY_LENGTH
        );
    }
    if (their_identity_key) {
        same = same && 0 == std::memcmp(
            their_identity_key->public_key, alice_identity_key.public_key,
            KEY_LENGTH
        );
    }
215
216
217
    same = same && 0 == std::memcmp(
        reader.base_key, alice_base_key.public_key, KEY_LENGTH
    );
218
219
220
    same = same && 0 == std::memcmp(
        reader.one_time_key, bob_one_time_key.public_key, KEY_LENGTH
    );
221
222
223
224
    return same;
}


225
olm::MessageType olm::Session::encrypt_message_type() {
226
    if (received_message) {
227
        return olm::MessageType::MESSAGE;
228
    } else {
229
        return olm::MessageType::PRE_KEY;
230
231
232
233
    }
}


234
std::size_t olm::Session::encrypt_message_length(
235
236
237
238
239
240
241
242
243
244
245
    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(
246
        KEY_LENGTH,
247
248
249
250
251
252
253
        KEY_LENGTH,
        KEY_LENGTH,
        message_length
    );
}


254
std::size_t olm::Session::encrypt_random_length() {
255
256
257
258
    return ratchet.encrypt_random_length();
}


259
std::size_t olm::Session::encrypt(
260
261
262
263
264
    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)) {
265
        last_error = olm::ErrorCode::OUTPUT_BUFFER_TOO_SMALL;
266
267
268
269
270
271
272
273
274
275
        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 {
276
        olm::PreKeyMessageWriter writer;
277
278
279
        encode_one_time_key_message(
            writer,
            PROTOCOL_VERSION,
280
            KEY_LENGTH,
281
282
283
284
285
            KEY_LENGTH,
            KEY_LENGTH,
            message_body_length,
            message
        );
286
287
288
        std::memcpy(
            writer.one_time_key, bob_one_time_key.public_key, KEY_LENGTH
        );
289
        std::memcpy(
290
            writer.identity_key, alice_identity_key.public_key, KEY_LENGTH
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
        );
        std::memcpy(
            writer.base_key, alice_base_key.public_key, KEY_LENGTH
        );
        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;
306
        ratchet.last_error = olm::ErrorCode::SUCCESS;
307
308
309
310
311
    }
    return result;
}


312
std::size_t olm::Session::decrypt_max_plaintext_length(
313
314
315
316
317
    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;
318
    if (message_type == olm::MessageType::MESSAGE) {
319
320
321
        message_body = message;
        message_body_length = message_length;
    } else {
322
        olm::PreKeyMessageReader reader;
323
324
        decode_one_time_key_message(reader, message, message_length);
        if (!reader.message) {
325
            last_error = olm::ErrorCode::BAD_MESSAGE_FORMAT;
326
327
328
329
330
331
332
333
334
335
336
337
            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;
338
        ratchet.last_error = olm::ErrorCode::SUCCESS;
339
340
341
342
343
    }
    return result;
}


344
345
std::size_t olm::Session::decrypt(
    olm::MessageType message_type,
346
347
348
349
350
    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;
351
    if (message_type == olm::MessageType::MESSAGE) {
352
353
354
        message_body = message;
        message_body_length = message_length;
    } else {
355
        olm::PreKeyMessageReader reader;
356
357
        decode_one_time_key_message(reader, message, message_length);
        if (!reader.message) {
358
            last_error = olm::ErrorCode::BAD_MESSAGE_FORMAT;
359
360
361
362
363
364
365
366
367
368
369
370
            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;
371
        ratchet.last_error = olm::ErrorCode::SUCCESS;
Mark Haines's avatar
Mark Haines committed
372
373
    } else {
        received_message = true;
374
375
376
    }
    return result;
}
377

378
379
380
namespace {
static const std::uint32_t SESSION_PICKLE_VERSION = 1;
}
381

382
std::size_t olm::pickle_length(
383
384
385
    Session const & value
) {
    std::size_t length = 0;
386
    length += olm::pickle_length(SESSION_PICKLE_VERSION);
387
    length += olm::pickle_length(value.received_message);
388
    length += olm::pickle_length(value.alice_identity_key);
389
    length += olm::pickle_length(value.alice_base_key);
390
    length += olm::pickle_length(value.bob_one_time_key);
391
    length += olm::pickle_length(value.ratchet);
392
393
394
395
    return length;
}


396
std::uint8_t * olm::pickle(
397
398
399
    std::uint8_t * pos,
    Session const & value
) {
400
    pos = olm::pickle(pos, SESSION_PICKLE_VERSION);
401
    pos = olm::pickle(pos, value.received_message);
402
    pos = olm::pickle(pos, value.alice_identity_key);
403
    pos = olm::pickle(pos, value.alice_base_key);
404
    pos = olm::pickle(pos, value.bob_one_time_key);
405
    pos = olm::pickle(pos, value.ratchet);
406
407
408
409
    return pos;
}


410
std::uint8_t const * olm::unpickle(
411
412
413
    std::uint8_t const * pos, std::uint8_t const * end,
    Session & value
) {
414
415
416
417
418
419
    uint32_t pickle_version;
    pos = olm::unpickle(pos, end, pickle_version);
    if (pickle_version != SESSION_PICKLE_VERSION) {
        value.last_error = olm::ErrorCode::UNKNOWN_PICKLE_VERSION;
        return end;
    }
420
    pos = olm::unpickle(pos, end, value.received_message);
421
    pos = olm::unpickle(pos, end, value.alice_identity_key);
422
    pos = olm::unpickle(pos, end, value.alice_base_key);
423
    pos = olm::unpickle(pos, end, value.bob_one_time_key);
424
    pos = olm::unpickle(pos, end, value.ratchet);
425
426
427
    return pos;
}