spank-olm

WIP Do not look
git clone git://archive.git.mtrnord.blog/MTRNord/spank-olm.git
Log | Files | Refs | README | LICENSE

megolm.cpp (5749B)


      1 #include "megolm.hpp"
      2 #include "pickle.hpp"
      3 
      4 #include <botan/auto_rng.h>
      5 #include <botan/hash.h>
      6 #include <botan/mac.h>
      7 
      8 
      9 /* Convenience macro for checking the return value of internal unpickling
     10  * functions and returning early on failure. */
     11 #ifndef UNPICKLE_OK
     12 #define UNPICKLE_OK(x)                                                                                                 \
     13     do                                                                                                                 \
     14     {                                                                                                                  \
     15         if (!(x))                                                                                                      \
     16             return nullptr;                                                                                            \
     17     }                                                                                                                  \
     18     while (0)
     19 #endif
     20 
     21 /* the seeds used in the HMAC-SHA-256 functions for each part of the ratchet.
     22  */
     23 #define HASH_KEY_SEED_LENGTH 1
     24 static uint8_t HASH_KEY_SEEDS[MEGOLM_RATCHET_PARTS][HASH_KEY_SEED_LENGTH] = {{0x00}, {0x01}, {0x02}, {0x03}};
     25 
     26 
     27 namespace spank_olm
     28 {
     29     constexpr size_t UINT32_LENGTH = 4;
     30 
     31     std::uint8_t *_olm_pickle_uint32(std::uint8_t *pos, uint32_t const value) { return pickle(pos, value); }
     32 
     33 
     34     std::uint8_t const *_olm_unpickle_uint32(std::uint8_t const *pos, std::uint8_t const *end, std::uint32_t *value)
     35     {
     36         return unpickle(pos, end, *value);
     37     }
     38 
     39 
     40     std::uint8_t *_olm_pickle_bytes(std::uint8_t *pos, std::uint8_t const *bytes, size_t bytes_length)
     41     {
     42         return pickle_bytes(pos, bytes, bytes_length);
     43     }
     44 
     45     std::uint8_t const *_olm_unpickle_bytes(std::uint8_t const *pos, std::uint8_t const *end, std::uint8_t *bytes,
     46                                             size_t bytes_length)
     47     {
     48         return unpickle_bytes(pos, end, bytes, bytes_length);
     49     }
     50 
     51 
     52     static void
     53     rehash_part(std::array<std::array<std::uint8_t, MEGOLM_RATCHET_PART_LENGTH>, MEGOLM_RATCHET_PARTS> &data,
     54                 const int rehash_from_part, const int rehash_to_part)
     55     {
     56         const auto hmac = Botan::MessageAuthenticationCode::create_or_throw("HMAC(SHA-256)");
     57 
     58         hmac->set_key(HASH_KEY_SEEDS[rehash_to_part], HASH_KEY_SEED_LENGTH);
     59         hmac->update(data[rehash_from_part].data(), MEGOLM_RATCHET_PART_LENGTH);
     60         hmac->final(data[rehash_to_part].data());
     61     }
     62 
     63 
     64     void Megolm::init(Botan::RandomNumberGenerator &rng, const unsigned int counter)
     65     {
     66         this->counter = counter;
     67         for (auto &part : data)
     68         {
     69             rng.randomize(part.data(), part.size());
     70         }
     71     }
     72 
     73     size_t Megolm::pickle_length() const { return data.size() * data[0].size() + UINT32_LENGTH; }
     74 
     75     std::uint8_t *Megolm::pickle(std::uint8_t *pos) const
     76     {
     77 
     78         pos = _olm_pickle_bytes(pos, get_data(), MEGOLM_RATCHET_LENGTH);
     79         pos = _olm_pickle_uint32(pos, counter);
     80         return pos;
     81     }
     82 
     83     std::uint8_t const *Megolm::unpickle(std::uint8_t const *pos, const std::uint8_t *end)
     84     {
     85         pos = _olm_unpickle_bytes(pos, end, const_cast<std::uint8_t *>(get_data()), MEGOLM_RATCHET_LENGTH);
     86         UNPICKLE_OK(pos);
     87 
     88         pos = _olm_unpickle_uint32(pos, end, &counter);
     89         UNPICKLE_OK(pos);
     90 
     91         return pos;
     92     }
     93 
     94     void Megolm::advance()
     95     {
     96         counter++;
     97         uint32_t mask = 0x00FFFFFF;
     98         int h = 0;
     99 
    100         /* figure out how much we need to rekey */
    101         while (h < MEGOLM_RATCHET_PARTS && (counter & mask))
    102         {
    103             h++;
    104             mask >>= 8;
    105         }
    106 
    107         /* now update R(h)...R(3) based on R(h) */
    108         for (int i = MEGOLM_RATCHET_PARTS - 1; i >= h; i--)
    109         {
    110             rehash_part(data, h, i);
    111         }
    112     }
    113 
    114     void Megolm::advance(const unsigned int advance_to)
    115     {
    116         /* starting with R0, see if we need to update each part of the hash */
    117         for (int j = 0; j < MEGOLM_RATCHET_PARTS; j++)
    118         {
    119             const int shift = (MEGOLM_RATCHET_PARTS - j - 1) * 8;
    120             const uint32_t mask = (~static_cast<uint32_t>(0)) << shift;
    121 
    122 
    123             /* how many times do we need to rehash this part?
    124              *
    125              * '& 0xff' ensures we handle integer wraparound correctly
    126              */
    127             unsigned int steps = ((advance_to >> shift) - (counter >> shift)) & 0xff;
    128 
    129             if (steps == 0)
    130             {
    131                 /* deal with the edge case where megolm->counter is slightly larger
    132                  * than advance_to. This should only happen for R(0), and implies
    133                  * that advance_to has wrapped around and we need to advance R(0)
    134                  * 256 times.
    135                  */
    136                 if (advance_to < counter)
    137                 {
    138                     steps = 0x100;
    139                 }
    140                 else
    141                 {
    142                     continue;
    143                 }
    144             }
    145 
    146             /* for all but the last step, we can just bump R(j) without regard
    147              * to R(j+1)...R(3).
    148              */
    149             while (steps-- > 1)
    150             {
    151                 rehash_part(data, j, j);
    152             }
    153 
    154             /* on the last step we also need to bump R(j+1)...R(3).
    155              *
    156              * (Theoretically, we could skip bumping R(j+2) if we're going to bump
    157              * R(j+1) again, but the code to figure that out is a bit baroque and
    158              * doesn't save us much).
    159              */
    160             for (int k = 3; k >= j; k--)
    161             {
    162                 rehash_part(data, j, k);
    163             }
    164             counter = advance_to & mask;
    165         }
    166     }
    167 
    168 } // namespace spank_olm