| 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 | |