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 | |
25 | static const struct _olm_cipher_aes_sha_256 MEGOLM_CIPHER = |
26 | OLM_CIPHER_INIT_AES_SHA_256("MEGOLM_KEYS" ); |
27 | const 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 |
32 | static uint8_t HASH_KEY_SEEDS[MEGOLM_RATCHET_PARTS][HASH_KEY_SEED_LENGTH] = { |
33 | {0x00}, |
34 | {0x01}, |
35 | {0x02}, |
36 | {0x03} |
37 | }; |
38 | |
39 | static 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 | |
53 | void 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 | |
58 | size_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 | |
66 | uint8_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 | |
72 | const 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 */ |
85 | void 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 | |
106 | void 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 | |