]> gitweb.ps.run Git - matrix_esp_thesis/blob - ext/olm/src/outbound_group_session.c
changes to olm and esp
[matrix_esp_thesis] / ext / olm / src / outbound_group_session.c
1 /* Copyright 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
16 #include "olm/outbound_group_session.h"
17
18 #include <string.h>
19
20 #include "olm/base64.h"
21 #include "olm/cipher.h"
22 #include "olm/crypto.h"
23 #include "olm/error.h"
24 #include "olm/megolm.h"
25 #include "olm/memory.h"
26 #include "olm/message.h"
27 #include "olm/pickle.h"
28 #include "olm/pickle_encoding.h"
29
30 #define OLM_PROTOCOL_VERSION     3
31 #define GROUP_SESSION_ID_LENGTH  ED25519_PUBLIC_KEY_LENGTH
32 #define PICKLE_VERSION           1
33 #define SESSION_KEY_VERSION      2
34
35 struct OlmOutboundGroupSession {
36     /** the Megolm ratchet providing the encryption keys */
37     Megolm ratchet;
38
39     /** The ed25519 keypair used for signing the messages */
40     struct _olm_ed25519_key_pair signing_key;
41
42     enum OlmErrorCode last_error;
43 };
44
45
46 size_t olm_outbound_group_session_size(void) {
47     return sizeof(OlmOutboundGroupSession);
48 }
49
50 OlmOutboundGroupSession * olm_outbound_group_session(
51     void *memory
52 ) {
53     OlmOutboundGroupSession *session = memory;
54     olm_clear_outbound_group_session(session);
55     return session;
56 }
57
58 const char *olm_outbound_group_session_last_error(
59     const OlmOutboundGroupSession *session
60 ) {
61     return _olm_error_to_string(session->last_error);
62 }
63
64 enum OlmErrorCode olm_outbound_group_session_last_error_code(
65     const OlmOutboundGroupSession *session
66 ) {
67     return session->last_error;
68 }
69
70 size_t olm_clear_outbound_group_session(
71     OlmOutboundGroupSession *session
72 ) {
73     _olm_unset(session, sizeof(OlmOutboundGroupSession));
74     return sizeof(OlmOutboundGroupSession);
75 }
76
77 static size_t raw_pickle_length(
78     const OlmOutboundGroupSession *session
79 ) {
80     size_t length = 0;
81     length += _olm_pickle_uint32_length(PICKLE_VERSION);
82     length += megolm_pickle_length(&(session->ratchet));
83     length += _olm_pickle_ed25519_key_pair_length(&(session->signing_key));
84     return length;
85 }
86
87 size_t olm_pickle_outbound_group_session_length(
88     const OlmOutboundGroupSession *session
89 ) {
90     return _olm_enc_output_length(raw_pickle_length(session));
91 }
92
93 size_t olm_pickle_outbound_group_session(
94     OlmOutboundGroupSession *session,
95     void const * key, size_t key_length,
96     void * pickled, size_t pickled_length
97 ) {
98     size_t raw_length = raw_pickle_length(session);
99     uint8_t *pos;
100
101     if (pickled_length < _olm_enc_output_length(raw_length)) {
102         session->last_error = OLM_OUTPUT_BUFFER_TOO_SMALL;
103         return (size_t)-1;
104     }
105
106 #ifndef OLM_FUZZING
107     pos = _olm_enc_output_pos(pickled, raw_length);
108 #else
109     pos = pickled;
110 #endif
111
112     pos = _olm_pickle_uint32(pos, PICKLE_VERSION);
113     pos = megolm_pickle(&(session->ratchet), pos);
114     pos = _olm_pickle_ed25519_key_pair(pos, &(session->signing_key));
115
116 #ifndef OLM_FUZZING
117     return _olm_enc_output(key, key_length, pickled, raw_length);
118 #else
119     return raw_length;
120 #endif
121 }
122
123 size_t olm_unpickle_outbound_group_session(
124     OlmOutboundGroupSession *session,
125     void const * key, size_t key_length,
126     void * pickled, size_t pickled_length
127 ) {
128     const uint8_t *pos;
129     const uint8_t *end;
130     uint32_t pickle_version;
131
132 #ifndef OLM_FUZZING
133     size_t raw_length = _olm_enc_input(
134         key, key_length, pickled, pickled_length, &(session->last_error)
135     );
136 #else
137     size_t raw_length = pickled_length;
138 #endif
139
140     if (raw_length == (size_t)-1) {
141         return raw_length;
142     }
143
144     pos = pickled;
145     end = pos + raw_length;
146
147     pos = _olm_unpickle_uint32(pos, end, &pickle_version);
148     FAIL_ON_CORRUPTED_PICKLE(pos, session);
149
150     if (pickle_version != PICKLE_VERSION) {
151         session->last_error = OLM_UNKNOWN_PICKLE_VERSION;
152         return (size_t)-1;
153     }
154
155     pos = megolm_unpickle(&(session->ratchet), pos, end);
156     FAIL_ON_CORRUPTED_PICKLE(pos, session);
157
158     pos = _olm_unpickle_ed25519_key_pair(pos, end, &(session->signing_key));
159     FAIL_ON_CORRUPTED_PICKLE(pos, session);
160
161     if (pos != end) {
162         /* Input was longer than expected. */
163         session->last_error = OLM_PICKLE_EXTRA_DATA;
164         return (size_t)-1;
165     }
166
167     return pickled_length;
168 }
169
170
171 size_t olm_init_outbound_group_session_random_length(
172     const OlmOutboundGroupSession *session
173 ) {
174     /* we need data to initialize the megolm ratchet, plus some more for the
175      * session id.
176      */
177     return MEGOLM_RATCHET_LENGTH +
178         ED25519_RANDOM_LENGTH;
179 }
180
181 size_t olm_init_outbound_group_session(
182     OlmOutboundGroupSession *session,
183     uint8_t *random, size_t random_length
184 ) {
185     const uint8_t *random_ptr = random;
186
187     if (random_length < olm_init_outbound_group_session_random_length(session)) {
188         /* Insufficient random data for new session */
189         session->last_error = OLM_NOT_ENOUGH_RANDOM;
190         return (size_t)-1;
191     }
192
193     megolm_init(&(session->ratchet), random_ptr, 0);
194     random_ptr += MEGOLM_RATCHET_LENGTH;
195
196     _olm_crypto_ed25519_generate_key(random_ptr, &(session->signing_key));
197     random_ptr += ED25519_RANDOM_LENGTH;
198
199     _olm_unset(random, random_length);
200     return 0;
201 }
202
203 static size_t raw_message_length(
204     OlmOutboundGroupSession *session,
205     size_t plaintext_length)
206 {
207     size_t ciphertext_length, mac_length;
208
209     ciphertext_length = megolm_cipher->ops->encrypt_ciphertext_length(
210         megolm_cipher, plaintext_length
211     );
212
213     mac_length = megolm_cipher->ops->mac_length(megolm_cipher);
214
215     return _olm_encode_group_message_length(
216         session->ratchet.counter,
217         ciphertext_length, mac_length, ED25519_SIGNATURE_LENGTH
218     );
219 }
220
221 size_t olm_group_encrypt_message_length(
222     OlmOutboundGroupSession *session,
223     size_t plaintext_length
224 ) {
225     size_t message_length = raw_message_length(session, plaintext_length);
226     return _olm_encode_base64_length(message_length);
227 }
228
229 /** write an un-base64-ed message to the buffer */
230 static size_t _encrypt(
231     OlmOutboundGroupSession *session, uint8_t const * plaintext, size_t plaintext_length,
232     uint8_t * buffer
233 ) {
234     size_t ciphertext_length, mac_length, message_length;
235     size_t result;
236     uint8_t *ciphertext_ptr;
237
238     ciphertext_length = megolm_cipher->ops->encrypt_ciphertext_length(
239         megolm_cipher,
240         plaintext_length
241     );
242
243     mac_length = megolm_cipher->ops->mac_length(megolm_cipher);
244
245     /* first we build the message structure, then we encrypt
246      * the plaintext into it.
247      */
248     message_length = _olm_encode_group_message(
249         OLM_PROTOCOL_VERSION,
250         session->ratchet.counter,
251         ciphertext_length,
252         buffer,
253         &ciphertext_ptr);
254
255     message_length += mac_length;
256
257     result = megolm_cipher->ops->encrypt(
258         megolm_cipher,
259         megolm_get_data(&(session->ratchet)), MEGOLM_RATCHET_LENGTH,
260         plaintext, plaintext_length,
261         ciphertext_ptr, ciphertext_length,
262         buffer, message_length
263     );
264
265     if (result == (size_t)-1) {
266         return result;
267     }
268
269     megolm_advance(&(session->ratchet));
270
271     /* sign the whole thing with the ed25519 key. */
272     _olm_crypto_ed25519_sign(
273         &(session->signing_key),
274         buffer, message_length,
275         buffer + message_length
276     );
277
278     return result;
279 }
280
281 size_t olm_group_encrypt(
282     OlmOutboundGroupSession *session,
283     uint8_t const * plaintext, size_t plaintext_length,
284     uint8_t * message, size_t max_message_length
285 ) {
286     size_t rawmsglen;
287     size_t result;
288     uint8_t *message_pos;
289
290     rawmsglen = raw_message_length(session, plaintext_length);
291
292     if (max_message_length < _olm_encode_base64_length(rawmsglen)) {
293         session->last_error = OLM_OUTPUT_BUFFER_TOO_SMALL;
294         return (size_t)-1;
295     }
296
297     /* we construct the message at the end of the buffer, so that
298      * we have room to base64-encode it once we're done.
299      */
300     message_pos = message + _olm_encode_base64_length(rawmsglen) - rawmsglen;
301
302     /* write the message, and encrypt it, at message_pos */
303     result = _encrypt(session, plaintext, plaintext_length, message_pos);
304     if (result == (size_t)-1) {
305         return result;
306     }
307
308     /* bas64-encode it */
309     return _olm_encode_base64(
310         message_pos, rawmsglen, message
311     );
312 }
313
314
315 size_t olm_outbound_group_session_id_length(
316     const OlmOutboundGroupSession *session
317 ) {
318     return _olm_encode_base64_length(GROUP_SESSION_ID_LENGTH);
319 }
320
321 size_t olm_outbound_group_session_id(
322     OlmOutboundGroupSession *session,
323     uint8_t * id, size_t id_length
324 ) {
325     if (id_length < olm_outbound_group_session_id_length(session)) {
326         session->last_error = OLM_OUTPUT_BUFFER_TOO_SMALL;
327         return (size_t)-1;
328     }
329
330     return _olm_encode_base64(
331         session->signing_key.public_key.public_key, GROUP_SESSION_ID_LENGTH, id
332     );
333 }
334
335 uint32_t olm_outbound_group_session_message_index(
336     OlmOutboundGroupSession *session
337 ) {
338     return session->ratchet.counter;
339 }
340
341 #define SESSION_KEY_RAW_LENGTH \
342     (1 + 4 + MEGOLM_RATCHET_LENGTH + ED25519_PUBLIC_KEY_LENGTH\
343         + ED25519_SIGNATURE_LENGTH)
344
345 size_t olm_outbound_group_session_key_length(
346     const OlmOutboundGroupSession *session
347 ) {
348     return _olm_encode_base64_length(SESSION_KEY_RAW_LENGTH);
349 }
350
351 size_t olm_outbound_group_session_key(
352     OlmOutboundGroupSession *session,
353     uint8_t * key, size_t key_length
354 ) {
355     uint8_t *raw;
356     uint8_t *ptr;
357     size_t encoded_length = olm_outbound_group_session_key_length(session);
358
359     if (key_length < encoded_length) {
360         session->last_error = OLM_OUTPUT_BUFFER_TOO_SMALL;
361         return (size_t)-1;
362     }
363
364     /* put the raw data at the end of the output buffer. */
365     raw = ptr = key + encoded_length - SESSION_KEY_RAW_LENGTH;
366     *ptr++ = SESSION_KEY_VERSION;
367
368     uint32_t counter = session->ratchet.counter;
369     // Encode counter as a big endian 32-bit number.
370     for (unsigned i = 0; i < 4; i++) {
371         *ptr++ = 0xFF & (counter >> 24); counter <<= 8;
372     }
373
374     memcpy(ptr, megolm_get_data(&session->ratchet), MEGOLM_RATCHET_LENGTH);
375     ptr += MEGOLM_RATCHET_LENGTH;
376
377     memcpy(
378         ptr, session->signing_key.public_key.public_key,
379         ED25519_PUBLIC_KEY_LENGTH
380     );
381     ptr += ED25519_PUBLIC_KEY_LENGTH;
382
383     /* sign the whole thing with the ed25519 key. */
384     _olm_crypto_ed25519_sign(
385         &(session->signing_key),
386         raw, ptr - raw, ptr
387     );
388
389     return _olm_encode_base64(raw, SESSION_KEY_RAW_LENGTH, key);
390 }