megolm.c 4.13 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/* Copyright 2016 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.
 */


#include "olm/megolm.h"

#include <string.h>

21
#include "olm/cipher.h"
22
#include "olm/crypto.h"
23
#include "olm/pickle.h"
24

25
26
27
static const struct _olm_cipher_aes_sha_256 MEGOLM_CIPHER =
    OLM_CIPHER_INIT_AES_SHA_256("MEGOLM_KEYS");
const struct _olm_cipher *megolm_cipher = OLM_CIPHER_BASE(&MEGOLM_CIPHER);
28

29
30
31
32
33
34
35
36
37
38
39
40
/* the seeds used in the HMAC-SHA-256 functions for each part of the ratchet.
 */
#define HASH_KEY_SEED_LENGTH 1
static uint8_t HASH_KEY_SEEDS[MEGOLM_RATCHET_PARTS][HASH_KEY_SEED_LENGTH] = {
    {0x00},
    {0x01},
    {0x02},
    {0x03}
};

static void rehash_part(
    uint8_t data[MEGOLM_RATCHET_PARTS][MEGOLM_RATCHET_PART_LENGTH],
41
    int rehash_from_part, int rehash_to_part
42
43
44
45
46
47
48
49
50
51
52
) {
    _olm_crypto_hmac_sha256(
        data[rehash_from_part],
        MEGOLM_RATCHET_PART_LENGTH,
        HASH_KEY_SEEDS[rehash_to_part], HASH_KEY_SEED_LENGTH,
        data[rehash_to_part]
    );
}



53
void megolm_init(Megolm *megolm, uint8_t const *random_data, uint32_t counter) {
54
55
56
57
    megolm->counter = counter;
    memcpy(megolm->data, random_data, MEGOLM_RATCHET_LENGTH);
}

58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
size_t megolm_pickle_length(const Megolm *megolm) {
    size_t length = 0;
    length += _olm_pickle_bytes_length(megolm_get_data(megolm), MEGOLM_RATCHET_LENGTH);
    length += _olm_pickle_uint32_length(megolm->counter);
    return length;

}

uint8_t * megolm_pickle(const Megolm *megolm,  uint8_t *pos) {
    pos = _olm_pickle_bytes(pos, megolm_get_data(megolm), MEGOLM_RATCHET_LENGTH);
    pos = _olm_pickle_uint32(pos, megolm->counter);
    return pos;
}

const uint8_t * megolm_unpickle(Megolm *megolm, const uint8_t *pos,
                                const uint8_t *end) {
    pos = _olm_unpickle_bytes(pos, end, (uint8_t *)(megolm->data),
                             MEGOLM_RATCHET_LENGTH);
    pos = _olm_unpickle_uint32(pos, end, &megolm->counter);
    return pos;
}
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97

/* simplistic implementation for a single step */
void megolm_advance(Megolm *megolm) {
    uint32_t mask = 0x00FFFFFF;
    int h = 0;
    int i;

    megolm->counter++;

    /* figure out how much we need to rekey */
    while (h < (int)MEGOLM_RATCHET_PARTS) {
        if (!(megolm->counter & mask))
            break;
        h++;
        mask >>= 8;
    }

    /* now update R(h)...R(3) based on R(h) */
    for (i = MEGOLM_RATCHET_PARTS-1; i >= h; i--) {
98
        rehash_part(megolm->data, h, i);
99
100
101
102
103
104
105
106
107
    }
}

void megolm_advance_to(Megolm *megolm, uint32_t advance_to) {
    int j;

    /* starting with R0, see if we need to update each part of the hash */
    for (j = 0; j < (int)MEGOLM_RATCHET_PARTS; j++) {
        int shift = (MEGOLM_RATCHET_PARTS-j-1) * 8;
108
        uint32_t mask = (~(uint32_t)0) << shift;
109
        int k;
110

111
112
113
114
115
116
        /* how many times do we need to rehash this part?
         *
         * '& 0xff' ensures we handle integer wraparound correctly
         */
        unsigned int steps =
            ((advance_to >> shift) - (megolm->counter >> shift)) & 0xff;
117

118
119
120
121
122
123
124
125
        if (steps == 0) {
            continue;
        }

        /* for all but the last step, we can just bump R(j) without regard
         * to R(j+1)...R(3).
         */
        while (steps > 1) {
126
            rehash_part(megolm->data, j, j);
127
128
129
            steps --;
        }

130
131
132
133
134
        /* on the last step we also need to bump R(j+1)...R(3).
         *
         * (Theoretically, we could skip bumping R(j+2) if we're going to bump
         * R(j+1) again, but the code to figure that out is a bit baroque and
         * doesn't save us much).
135
         */
136
        for (k = 3; k >= j; k--) {
137
            rehash_part(megolm->data, j, k);
138
        }
139
        megolm->counter = advance_to & mask;
140
141
    }
}