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
17#include "olm/megolm.h"
18
19#include <string.h>
20
21#include "olm/cipher.h"
22#include "olm/crypto.h"
23#include "olm/pickle.h"
24
25static const struct _olm_cipher_aes_sha_256 MEGOLM_CIPHER =
26 OLM_CIPHER_INIT_AES_SHA_256("MEGOLM_KEYS");
27const struct _olm_cipher *megolm_cipher = OLM_CIPHER_BASE(&MEGOLM_CIPHER);
28
29/* the seeds used in the HMAC-SHA-256 functions for each part of the ratchet.
30 */
31#define HASH_KEY_SEED_LENGTH 1
32static uint8_t HASH_KEY_SEEDS[MEGOLM_RATCHET_PARTS][HASH_KEY_SEED_LENGTH] = {
33 {0x00},
34 {0x01},
35 {0x02},
36 {0x03}
37};
38
39static void rehash_part(
40 uint8_t data[MEGOLM_RATCHET_PARTS][MEGOLM_RATCHET_PART_LENGTH],
41 int rehash_from_part, int rehash_to_part
42) {
43 _olm_crypto_hmac_sha256(
44 key: data[rehash_from_part],
45 MEGOLM_RATCHET_PART_LENGTH,
46 input: HASH_KEY_SEEDS[rehash_to_part], HASH_KEY_SEED_LENGTH,
47 output: data[rehash_to_part]
48 );
49}
50
51
52
53void megolm_init(Megolm *megolm, uint8_t const *random_data, uint32_t counter) {
54 megolm->counter = counter;
55 memcpy(dest: megolm->data, src: random_data, MEGOLM_RATCHET_LENGTH);
56}
57
58size_t megolm_pickle_length(const Megolm *megolm) {
59 size_t length = 0;
60 length += _olm_pickle_bytes_length(megolm_get_data(megolm), MEGOLM_RATCHET_LENGTH);
61 length += _olm_pickle_uint32_length(megolm->counter);
62 return length;
63
64}
65
66uint8_t * megolm_pickle(const Megolm *megolm, uint8_t *pos) {
67 pos = _olm_pickle_bytes(pos, megolm_get_data(megolm), MEGOLM_RATCHET_LENGTH);
68 pos = _olm_pickle_uint32(pos, value: megolm->counter);
69 return pos;
70}
71
72const uint8_t * megolm_unpickle(Megolm *megolm, const uint8_t *pos,
73 const uint8_t *end) {
74 pos = _olm_unpickle_bytes(pos, end, bytes: (uint8_t *)(megolm->data),
75 MEGOLM_RATCHET_LENGTH);
76 UNPICKLE_OK(pos);
77
78 pos = _olm_unpickle_uint32(pos, end, value: &megolm->counter);
79 UNPICKLE_OK(pos);
80
81 return pos;
82}
83
84/* simplistic implementation for a single step */
85void megolm_advance(Megolm *megolm) {
86 uint32_t mask = 0x00FFFFFF;
87 int h = 0;
88 int i;
89
90 megolm->counter++;
91
92 /* figure out how much we need to rekey */
93 while (h < (int)MEGOLM_RATCHET_PARTS) {
94 if (!(megolm->counter & mask))
95 break;
96 h++;
97 mask >>= 8;
98 }
99
100 /* now update R(h)...R(3) based on R(h) */
101 for (i = MEGOLM_RATCHET_PARTS-1; i >= h; i--) {
102 rehash_part(data: megolm->data, rehash_from_part: h, rehash_to_part: i);
103 }
104}
105
106void megolm_advance_to(Megolm *megolm, uint32_t advance_to) {
107 int j;
108
109 /* starting with R0, see if we need to update each part of the hash */
110 for (j = 0; j < (int)MEGOLM_RATCHET_PARTS; j++) {
111 int shift = (MEGOLM_RATCHET_PARTS-j-1) * 8;
112 uint32_t mask = (~(uint32_t)0) << shift;
113 int k;
114
115 /* how many times do we need to rehash this part?
116 *
117 * '& 0xff' ensures we handle integer wraparound correctly
118 */
119 unsigned int steps =
120 ((advance_to >> shift) - (megolm->counter >> shift)) & 0xff;
121
122 if (steps == 0) {
123 /* deal with the edge case where megolm->counter is slightly larger
124 * than advance_to. This should only happen for R(0), and implies
125 * that advance_to has wrapped around and we need to advance R(0)
126 * 256 times.
127 */
128 if (advance_to < megolm->counter) {
129 steps = 0x100;
130 } else {
131 continue;
132 }
133 }
134
135 /* for all but the last step, we can just bump R(j) without regard
136 * to R(j+1)...R(3).
137 */
138 while (steps > 1) {
139 rehash_part(data: megolm->data, rehash_from_part: j, rehash_to_part: j);
140 steps --;
141 }
142
143 /* on the last step we also need to bump R(j+1)...R(3).
144 *
145 * (Theoretically, we could skip bumping R(j+2) if we're going to bump
146 * R(j+1) again, but the code to figure that out is a bit baroque and
147 * doesn't save us much).
148 */
149 for (k = 3; k >= j; k--) {
150 rehash_part(data: megolm->data, rehash_from_part: j, rehash_to_part: k);
151 }
152 megolm->counter = advance_to & mask;
153 }
154}
155