aboutsummaryrefslogtreecommitdiff
path: root/src/ratchet.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/ratchet.cpp')
-rw-r--r--src/ratchet.cpp95
1 files changed, 81 insertions, 14 deletions
diff --git a/src/ratchet.cpp b/src/ratchet.cpp
index 5ef1e56..03a0478 100644
--- a/src/ratchet.cpp
+++ b/src/ratchet.cpp
@@ -1,4 +1,4 @@
-/* Copyright 2015 OpenMarket Ltd
+/* Copyright 2015, 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.
@@ -17,17 +17,31 @@
#include "olm/memory.hh"
#include "olm/cipher.hh"
#include "olm/pickle.hh"
-
+#include "olm/logging.hh"
#include <cstring>
namespace {
+static const char *LOG_CATEGORY = "olm::Ratchet";
+
static const std::uint8_t PROTOCOL_VERSION = 3;
static const std::uint8_t MESSAGE_KEY_SEED[1] = {0x01};
static const std::uint8_t CHAIN_KEY_SEED[1] = {0x02};
static const std::size_t MAX_MESSAGE_GAP = 2000;
+
+/**
+ * Advance the root key, creating a new message chain.
+ *
+ * @param root_key previous root key R(n-1)
+ * @param our_key our new ratchet key T(n)
+ * @param their_key their most recent ratchet key T(n-1)
+ * @param info table of constants for the ratchet function
+ * @param new_root_key[out] returns the new root key R(n)
+ * @param new_chain_key[out] returns the first chain key in the new chain
+ * C(n,0)
+ */
static void create_chain_key(
olm::SharedKey const & root_key,
olm::Curve25519KeyPair const & our_key,
@@ -55,6 +69,7 @@ static void create_chain_key(
static void advance_chain_key(
+ std::uint32_t chain_index,
olm::ChainKey const & chain_key,
olm::ChainKey & new_chain_key
) {
@@ -64,20 +79,24 @@ static void advance_chain_key(
new_chain_key.key
);
new_chain_key.index = chain_key.index + 1;
+ olm::logf(olm::LOG_DEBUG, LOG_CATEGORY, "Derived chain key C(%i,%i)",
+ chain_index, new_chain_key.index);
}
static void create_message_keys(
+ std::uint32_t chain_index,
olm::ChainKey const & chain_key,
olm::KdfInfo const & info,
- olm::MessageKey & message_key
-) {
+ olm::MessageKey & message_key) {
olm::hmac_sha256(
chain_key.key, sizeof(chain_key.key),
MESSAGE_KEY_SEED, sizeof(MESSAGE_KEY_SEED),
message_key.key
);
message_key.index = chain_key.index;
+ olm::logf(olm::LOG_DEBUG, LOG_CATEGORY, "Created message key with chain key C(%i,%i)",
+ chain_index, message_key.index);
}
@@ -98,6 +117,7 @@ static std::size_t verify_mac_and_decrypt(
static std::size_t verify_mac_and_decrypt_for_existing_chain(
olm::Ratchet const & session,
+ std::uint32_t chain_index,
olm::ChainKey const & chain,
olm::MessageReader const & reader,
std::uint8_t * plaintext, std::size_t max_plaintext_length
@@ -114,11 +134,11 @@ static std::size_t verify_mac_and_decrypt_for_existing_chain(
olm::ChainKey new_chain = chain;
while (new_chain.index < reader.counter) {
- advance_chain_key(new_chain, new_chain);
+ advance_chain_key(chain_index, new_chain, new_chain);
}
olm::MessageKey message_key;
- create_message_keys(new_chain, session.kdf_info, message_key);
+ create_message_keys(chain_index, new_chain, session.kdf_info, message_key);
std::size_t result = verify_mac_and_decrypt(
session.ratchet_cipher, message_key, reader,
@@ -150,14 +170,17 @@ static std::size_t verify_mac_and_decrypt_for_new_chain(
}
olm::load_array(new_chain.ratchet_key.public_key, reader.ratchet_key);
+ std::uint32_t chain_index = session.chain_index + 1;
create_chain_key(
session.root_key, session.sender_chain[0].ratchet_key,
new_chain.ratchet_key, session.kdf_info,
new_root_key, new_chain.chain_key
);
+ olm::logf(olm::LOG_DEBUG, LOG_CATEGORY, "Calculated new receiver chain R(%i)",
+ chain_index);
std::size_t result = verify_mac_and_decrypt_for_existing_chain(
- session, new_chain.chain_key, reader,
+ session, chain_index, new_chain.chain_key, reader,
plaintext, max_plaintext_length
);
olm::unset(new_root_key);
@@ -194,7 +217,9 @@ void olm::Ratchet::initialise_as_bob(
pos = olm::load_array(root_key, pos);
pos = olm::load_array(receiver_chains[0].chain_key.key, pos);
receiver_chains[0].ratchet_key = their_ratchet_key;
+ chain_index = 0;
olm::unset(derived_secrets);
+ olm::logf(olm::LOG_DEBUG, LOG_CATEGORY, "Initialised receiver chain R(0)");
}
@@ -215,7 +240,9 @@ void olm::Ratchet::initialise_as_alice(
pos = olm::load_array(root_key, pos);
pos = olm::load_array(sender_chain[0].chain_key.key, pos);
sender_chain[0].ratchet_key = our_ratchet_key;
+ chain_index = 0;
olm::unset(derived_secrets);
+ olm::logf(olm::LOG_DEBUG, LOG_CATEGORY, "Initialised sender chain R(0)");
}
namespace olm {
@@ -353,6 +380,7 @@ std::size_t olm::pickle_length(
length += olm::pickle_length(value.sender_chain);
length += olm::pickle_length(value.receiver_chains);
length += olm::pickle_length(value.skipped_message_keys);
+ length += olm::pickle_length(value.chain_index);
return length;
}
@@ -364,6 +392,7 @@ std::uint8_t * olm::pickle(
pos = pickle(pos, value.sender_chain);
pos = pickle(pos, value.receiver_chains);
pos = pickle(pos, value.skipped_message_keys);
+ pos = pickle(pos, value.chain_index);
return pos;
}
@@ -376,6 +405,7 @@ std::uint8_t const * olm::unpickle(
pos = unpickle(pos, end, value.sender_chain);
pos = unpickle(pos, end, value.receiver_chains);
pos = unpickle(pos, end, value.skipped_message_keys);
+ pos = unpickle(pos, end, value.chain_index);
return pos;
}
@@ -420,6 +450,9 @@ std::size_t olm::Ratchet::encrypt(
if (sender_chain.empty()) {
sender_chain.insert();
olm::curve25519_generate_key(random, sender_chain[0].ratchet_key);
+ olm::logf(olm::LOG_DEBUG, LOG_CATEGORY, "Created new ratchet key T(%i) %s",
+ chain_index + 1,
+ sender_chain[0].ratchet_key.to_string().c_str());
create_chain_key(
root_key,
sender_chain[0].ratchet_key,
@@ -427,11 +460,14 @@ std::size_t olm::Ratchet::encrypt(
kdf_info,
root_key, sender_chain[0].chain_key
);
+ olm::logf(olm::LOG_DEBUG, LOG_CATEGORY, "Initialised new sender chain R(%i)",
+ chain_index + 1);
+ chain_index++;
}
MessageKey keys;
- create_message_keys(sender_chain[0].chain_key, kdf_info, keys);
- advance_chain_key(sender_chain[0].chain_key, sender_chain[0].chain_key);
+ create_message_keys(chain_index, sender_chain[0].chain_key, kdf_info, keys);
+ advance_chain_key(chain_index, sender_chain[0].chain_key, sender_chain[0].chain_key);
std::size_t ciphertext_length = ratchet_cipher.encrypt_ciphertext_length(
plaintext_length
@@ -455,6 +491,14 @@ std::size_t olm::Ratchet::encrypt(
output, output_length
);
+ olm::logf(olm::LOG_TRACE, LOG_CATEGORY,
+ "Encoded message ver=%i ratchet_key=%s chain_idx=%i ciphertext=%s",
+ PROTOCOL_VERSION,
+ olm::bytes_to_string(writer.ratchet_key, olm::KEY_LENGTH).c_str(),
+ counter,
+ olm::bytes_to_string(writer.ciphertext, ciphertext_length).c_str()
+ );
+
olm::unset(keys);
return output_length;
}
@@ -481,6 +525,10 @@ std::size_t olm::Ratchet::decrypt(
std::uint8_t const * input, std::size_t input_length,
std::uint8_t * plaintext, std::size_t max_plaintext_length
) {
+ olm::logf(olm::LOG_TRACE, LOG_CATEGORY,
+ "Decrypting message %s",
+ olm::bytes_to_string(input, input_length).c_str());
+
olm::MessageReader reader;
olm::decode_message(
reader, input, input_length, ratchet_cipher.mac_length()
@@ -511,6 +559,13 @@ std::size_t olm::Ratchet::decrypt(
}
ReceiverChain * chain = nullptr;
+ auto receiver_chain_index = chain_index;
+ if (!sender_chain.empty()) {
+ // we've already advanced to the next (sender) chain; decrement to
+ // get back to the receiver chains
+ receiver_chain_index --;
+ }
+
for (olm::ReceiverChain & receiver_chain : receiver_chains) {
if (0 == std::memcmp(
receiver_chain.ratchet_key.public_key, reader.ratchet_key,
@@ -519,11 +574,14 @@ std::size_t olm::Ratchet::decrypt(
chain = &receiver_chain;
break;
}
+ receiver_chain_index -= 2;
}
std::size_t result = std::size_t(-1);
if (!chain) {
+ olm::logf(olm::LOG_DEBUG, LOG_CATEGORY,
+ "Sender ratchet key does not match known chain; starting new one");
result = verify_mac_and_decrypt_for_new_chain(
*this, reader, plaintext, max_plaintext_length
);
@@ -555,7 +613,8 @@ std::size_t olm::Ratchet::decrypt(
}
} else {
result = verify_mac_and_decrypt_for_existing_chain(
- *this, chain->chain_key, reader, plaintext, max_plaintext_length
+ *this, receiver_chain_index, chain->chain_key,
+ reader, plaintext, max_plaintext_length
);
}
@@ -565,28 +624,36 @@ std::size_t olm::Ratchet::decrypt(
}
if (!chain) {
- /* They have started using a new empheral ratchet key.
+ /* They have started using a new ephemeral ratchet key.
* We need to derive a new set of chain keys.
* We can discard our previous empheral ratchet key.
* We will generate a new key when we send the next message. */
+
chain = receiver_chains.insert();
olm::load_array(chain->ratchet_key.public_key, reader.ratchet_key);
+
+ // TODO: we've already done this once, in
+ // verify_mac_and_decrypt_for_new_chain(). we could reuse the result.
create_chain_key(
root_key, sender_chain[0].ratchet_key, chain->ratchet_key,
kdf_info, root_key, chain->chain_key
);
+
olm::unset(sender_chain[0]);
sender_chain.erase(sender_chain.begin());
+ receiver_chain_index = ++chain_index;
+ olm::logf(olm::LOG_DEBUG, LOG_CATEGORY, "Initialised new receiver chain R(%i)",
+ chain_index);
}
while (chain->chain_key.index < reader.counter) {
olm::SkippedMessageKey & key = *skipped_message_keys.insert();
- create_message_keys(chain->chain_key, kdf_info, key.message_key);
+ create_message_keys(receiver_chain_index, chain->chain_key, kdf_info, key.message_key);
key.ratchet_key = chain->ratchet_key;
- advance_chain_key(chain->chain_key, chain->chain_key);
+ advance_chain_key(receiver_chain_index, chain->chain_key, chain->chain_key);
}
- advance_chain_key(chain->chain_key, chain->chain_key);
+ advance_chain_key(receiver_chain_index, chain->chain_key, chain->chain_key);
return result;
}