]> gitweb.ps.run Git - matrix_esp_thesis/blobdiff - ext/olm/src/session.cpp
add dependencies to repo
[matrix_esp_thesis] / ext / olm / src / session.cpp
diff --git a/ext/olm/src/session.cpp b/ext/olm/src/session.cpp
new file mode 100644 (file)
index 0000000..732e0c0
--- /dev/null
@@ -0,0 +1,531 @@
+/* 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.
+ * 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/session.hh"
+#include "olm/cipher.h"
+#include "olm/crypto.h"
+#include "olm/account.hh"
+#include "olm/memory.hh"
+#include "olm/message.hh"
+#include "olm/pickle.hh"
+
+#include <cstring>
+#include <stdio.h>
+
+namespace {
+
+static const std::uint8_t PROTOCOL_VERSION = 0x3;
+
+static const std::uint8_t ROOT_KDF_INFO[] = "OLM_ROOT";
+static const std::uint8_t RATCHET_KDF_INFO[] = "OLM_RATCHET";
+static const std::uint8_t CIPHER_KDF_INFO[] = "OLM_KEYS";
+
+static const olm::KdfInfo OLM_KDF_INFO = {
+    ROOT_KDF_INFO, sizeof(ROOT_KDF_INFO) - 1,
+    RATCHET_KDF_INFO, sizeof(RATCHET_KDF_INFO) - 1
+};
+
+static const struct _olm_cipher_aes_sha_256 OLM_CIPHER =
+    OLM_CIPHER_INIT_AES_SHA_256(CIPHER_KDF_INFO);
+
+} // namespace
+
+olm::Session::Session(
+) : ratchet(OLM_KDF_INFO, OLM_CIPHER_BASE(&OLM_CIPHER)),
+    last_error(OlmErrorCode::OLM_SUCCESS),
+    received_message(false) {
+
+}
+
+
+std::size_t olm::Session::new_outbound_session_random_length() const {
+    return CURVE25519_RANDOM_LENGTH * 2;
+}
+
+
+std::size_t olm::Session::new_outbound_session(
+    olm::Account const & local_account,
+    _olm_curve25519_public_key const & identity_key,
+    _olm_curve25519_public_key const & one_time_key,
+    std::uint8_t const * random, std::size_t random_length
+) {
+    if (random_length < new_outbound_session_random_length()) {
+        last_error = OlmErrorCode::OLM_NOT_ENOUGH_RANDOM;
+        return std::size_t(-1);
+    }
+
+    _olm_curve25519_key_pair base_key;
+    _olm_crypto_curve25519_generate_key(random, &base_key);
+
+    _olm_curve25519_key_pair ratchet_key;
+    _olm_crypto_curve25519_generate_key(random + CURVE25519_RANDOM_LENGTH, &ratchet_key);
+
+    _olm_curve25519_key_pair const & alice_identity_key_pair = (
+        local_account.identity_keys.curve25519_key
+    );
+
+    received_message = false;
+    alice_identity_key = alice_identity_key_pair.public_key;
+    alice_base_key = base_key.public_key;
+    bob_one_time_key = one_time_key;
+
+    // Calculate the shared secret S via triple DH
+    std::uint8_t secret[3 * CURVE25519_SHARED_SECRET_LENGTH];
+    std::uint8_t * pos = secret;
+
+    _olm_crypto_curve25519_shared_secret(&alice_identity_key_pair, &one_time_key, pos);
+    pos += CURVE25519_SHARED_SECRET_LENGTH;
+    _olm_crypto_curve25519_shared_secret(&base_key, &identity_key, pos);
+    pos += CURVE25519_SHARED_SECRET_LENGTH;
+    _olm_crypto_curve25519_shared_secret(&base_key, &one_time_key, pos);
+
+    ratchet.initialise_as_alice(secret, sizeof(secret), ratchet_key);
+
+    olm::unset(base_key);
+    olm::unset(ratchet_key);
+    olm::unset(secret);
+
+    return std::size_t(0);
+}
+
+namespace {
+
+static bool check_message_fields(
+    olm::PreKeyMessageReader & reader, bool have_their_identity_key
+) {
+    bool ok = true;
+    ok = ok && (have_their_identity_key || reader.identity_key);
+    if (reader.identity_key) {
+        ok = ok && reader.identity_key_length == CURVE25519_KEY_LENGTH;
+    }
+    ok = ok && reader.message;
+    ok = ok && reader.base_key;
+    ok = ok && reader.base_key_length == CURVE25519_KEY_LENGTH;
+    ok = ok && reader.one_time_key;
+    ok = ok && reader.one_time_key_length == CURVE25519_KEY_LENGTH;
+    return ok;
+}
+
+} // namespace
+
+
+std::size_t olm::Session::new_inbound_session(
+    olm::Account & local_account,
+    _olm_curve25519_public_key const * their_identity_key,
+    std::uint8_t const * one_time_key_message, std::size_t message_length
+) {
+    olm::PreKeyMessageReader reader;
+    decode_one_time_key_message(reader, one_time_key_message, message_length);
+
+    if (!check_message_fields(reader, their_identity_key)) {
+        last_error = OlmErrorCode::OLM_BAD_MESSAGE_FORMAT;
+        return std::size_t(-1);
+    }
+
+    if (reader.identity_key && their_identity_key) {
+        bool same = 0 == std::memcmp(
+            their_identity_key->public_key, reader.identity_key, CURVE25519_KEY_LENGTH
+        );
+        if (!same) {
+            last_error = OlmErrorCode::OLM_BAD_MESSAGE_KEY_ID;
+            return std::size_t(-1);
+        }
+    }
+
+    olm::load_array(alice_identity_key.public_key, reader.identity_key);
+    olm::load_array(alice_base_key.public_key, reader.base_key);
+    olm::load_array(bob_one_time_key.public_key, reader.one_time_key);
+
+    olm::MessageReader message_reader;
+    decode_message(
+        message_reader, reader.message, reader.message_length,
+        ratchet.ratchet_cipher->ops->mac_length(ratchet.ratchet_cipher)
+    );
+
+    if (!message_reader.ratchet_key
+            || message_reader.ratchet_key_length != CURVE25519_KEY_LENGTH) {
+        last_error = OlmErrorCode::OLM_BAD_MESSAGE_FORMAT;
+        return std::size_t(-1);
+    }
+
+    _olm_curve25519_public_key ratchet_key;
+    olm::load_array(ratchet_key.public_key, message_reader.ratchet_key);
+
+    olm::OneTimeKey const * our_one_time_key = local_account.lookup_key(
+        bob_one_time_key
+    );
+
+    if (!our_one_time_key) {
+        last_error = OlmErrorCode::OLM_BAD_MESSAGE_KEY_ID;
+        return std::size_t(-1);
+    }
+
+    _olm_curve25519_key_pair const & bob_identity_key = (
+        local_account.identity_keys.curve25519_key
+    );
+    _olm_curve25519_key_pair const & bob_one_time_key = our_one_time_key->key;
+
+    // Calculate the shared secret S via triple DH
+    std::uint8_t secret[CURVE25519_SHARED_SECRET_LENGTH * 3];
+    std::uint8_t * pos = secret;
+    _olm_crypto_curve25519_shared_secret(&bob_one_time_key, &alice_identity_key, pos);
+    pos += CURVE25519_SHARED_SECRET_LENGTH;
+    _olm_crypto_curve25519_shared_secret(&bob_identity_key, &alice_base_key, pos);
+    pos += CURVE25519_SHARED_SECRET_LENGTH;
+    _olm_crypto_curve25519_shared_secret(&bob_one_time_key, &alice_base_key, pos);
+
+    ratchet.initialise_as_bob(secret, sizeof(secret), ratchet_key);
+
+    olm::unset(secret);
+
+    return std::size_t(0);
+}
+
+
+std::size_t olm::Session::session_id_length() const {
+    return SHA256_OUTPUT_LENGTH;
+}
+
+
+std::size_t olm::Session::session_id(
+    std::uint8_t * id, std::size_t id_length
+) {
+    if (id_length < session_id_length()) {
+        last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
+        return std::size_t(-1);
+    }
+    std::uint8_t tmp[CURVE25519_KEY_LENGTH * 3];
+    std::uint8_t * pos = tmp;
+    pos = olm::store_array(pos, alice_identity_key.public_key);
+    pos = olm::store_array(pos, alice_base_key.public_key);
+    pos = olm::store_array(pos, bob_one_time_key.public_key);
+    _olm_crypto_sha256(tmp, sizeof(tmp), id);
+    return session_id_length();
+}
+
+
+bool olm::Session::matches_inbound_session(
+    _olm_curve25519_public_key const * their_identity_key,
+    std::uint8_t const * one_time_key_message, std::size_t message_length
+) const {
+    olm::PreKeyMessageReader reader;
+    decode_one_time_key_message(reader, one_time_key_message, message_length);
+
+    if (!check_message_fields(reader, their_identity_key)) {
+        return false;
+    }
+
+    bool same = true;
+    if (reader.identity_key) {
+        same = same && 0 == std::memcmp(
+            reader.identity_key, alice_identity_key.public_key, CURVE25519_KEY_LENGTH
+        );
+    }
+    if (their_identity_key) {
+        same = same && 0 == std::memcmp(
+            their_identity_key->public_key, alice_identity_key.public_key,
+            CURVE25519_KEY_LENGTH
+        );
+    }
+    same = same && 0 == std::memcmp(
+        reader.base_key, alice_base_key.public_key, CURVE25519_KEY_LENGTH
+    );
+    same = same && 0 == std::memcmp(
+        reader.one_time_key, bob_one_time_key.public_key, CURVE25519_KEY_LENGTH
+    );
+    return same;
+}
+
+
+olm::MessageType olm::Session::encrypt_message_type() const {
+    if (received_message) {
+        return olm::MessageType::MESSAGE;
+    } else {
+        return olm::MessageType::PRE_KEY;
+    }
+}
+
+
+std::size_t olm::Session::encrypt_message_length(
+    std::size_t plaintext_length
+) const {
+    std::size_t message_length = ratchet.encrypt_output_length(
+        plaintext_length
+    );
+
+    if (received_message) {
+        return message_length;
+    }
+
+    return encode_one_time_key_message_length(
+        CURVE25519_KEY_LENGTH,
+        CURVE25519_KEY_LENGTH,
+        CURVE25519_KEY_LENGTH,
+        message_length
+    );
+}
+
+
+std::size_t olm::Session::encrypt_random_length() const {
+    return ratchet.encrypt_random_length();
+}
+
+
+std::size_t olm::Session::encrypt(
+    std::uint8_t const * plaintext, std::size_t plaintext_length,
+    std::uint8_t const * random, std::size_t random_length,
+    std::uint8_t * message, std::size_t message_length
+) {
+    if (message_length < encrypt_message_length(plaintext_length)) {
+        last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
+        return std::size_t(-1);
+    }
+    std::uint8_t * message_body;
+    std::size_t message_body_length = ratchet.encrypt_output_length(
+        plaintext_length
+    );
+
+    if (received_message) {
+        message_body = message;
+    } else {
+        olm::PreKeyMessageWriter writer;
+        encode_one_time_key_message(
+            writer,
+            PROTOCOL_VERSION,
+            CURVE25519_KEY_LENGTH,
+            CURVE25519_KEY_LENGTH,
+            CURVE25519_KEY_LENGTH,
+            message_body_length,
+            message
+        );
+        olm::store_array(writer.one_time_key, bob_one_time_key.public_key);
+        olm::store_array(writer.identity_key, alice_identity_key.public_key);
+        olm::store_array(writer.base_key, alice_base_key.public_key);
+        message_body = writer.message;
+    }
+
+    std::size_t result = ratchet.encrypt(
+        plaintext, plaintext_length,
+        random, random_length,
+        message_body, message_body_length
+    );
+
+    if (result == std::size_t(-1)) {
+        last_error = ratchet.last_error;
+        ratchet.last_error = OlmErrorCode::OLM_SUCCESS;
+        return result;
+    }
+
+    return result;
+}
+
+
+std::size_t olm::Session::decrypt_max_plaintext_length(
+    MessageType message_type,
+    std::uint8_t const * message, std::size_t message_length
+) {
+    std::uint8_t const * message_body;
+    std::size_t message_body_length;
+    if (message_type == olm::MessageType::MESSAGE) {
+        message_body = message;
+        message_body_length = message_length;
+    } else {
+        olm::PreKeyMessageReader reader;
+        decode_one_time_key_message(reader, message, message_length);
+        if (!reader.message) {
+            last_error = OlmErrorCode::OLM_BAD_MESSAGE_FORMAT;
+            return std::size_t(-1);
+        }
+        message_body = reader.message;
+        message_body_length = reader.message_length;
+    }
+
+    std::size_t result = ratchet.decrypt_max_plaintext_length(
+        message_body, message_body_length
+    );
+
+    if (result == std::size_t(-1)) {
+        last_error = ratchet.last_error;
+        ratchet.last_error = OlmErrorCode::OLM_SUCCESS;
+    }
+    return result;
+}
+
+
+std::size_t olm::Session::decrypt(
+    olm::MessageType message_type,
+    std::uint8_t const * message, std::size_t message_length,
+    std::uint8_t * plaintext, std::size_t max_plaintext_length
+) {
+    std::uint8_t const * message_body;
+    std::size_t message_body_length;
+    if (message_type == olm::MessageType::MESSAGE) {
+        message_body = message;
+        message_body_length = message_length;
+    } else {
+        olm::PreKeyMessageReader reader;
+        decode_one_time_key_message(reader, message, message_length);
+        if (!reader.message) {
+            last_error = OlmErrorCode::OLM_BAD_MESSAGE_FORMAT;
+            return std::size_t(-1);
+        }
+        message_body = reader.message;
+        message_body_length = reader.message_length;
+    }
+
+    std::size_t result = ratchet.decrypt(
+        message_body, message_body_length, plaintext, max_plaintext_length
+    );
+
+    if (result == std::size_t(-1)) {
+        last_error = ratchet.last_error;
+        ratchet.last_error = OlmErrorCode::OLM_SUCCESS;
+        return result;
+    }
+
+    received_message = true;
+    return result;
+}
+
+// make the description end with "..." instead of stopping abruptly with no
+// warning
+void elide_description(char *end) {
+    end[-3] = '.';
+    end[-2] = '.';
+    end[-1] = '.';
+    end[0] = '\0';
+}
+
+void olm::Session::describe(char *describe_buffer, size_t buflen) {
+    // how much of the buffer is remaining (this is an int rather than a size_t
+    // because it will get compared to the return value from snprintf)
+    int remaining = buflen;
+    // do nothing if we have a zero-length buffer, or if buflen > INT_MAX,
+    // resulting in an overflow
+    if (remaining <= 0) return;
+
+    describe_buffer[0] = '\0';
+    // we need at least 23 characters to get any sort of meaningful
+    // information, so bail if we don't have that.  (But more importantly, we
+    // need it to be at least 4 so that elide_description doesn't go out of
+    // bounds.)
+    if (remaining < 23) return;
+
+    int size;
+
+    // check that snprintf didn't return an error or reach the end of the buffer
+#define CHECK_SIZE_AND_ADVANCE                                          \
+    if (size > remaining) {                                             \
+        return elide_description(describe_buffer + remaining - 1);      \
+    } else if (size > 0) {                                              \
+        describe_buffer += size;                                        \
+        remaining -= size;                                              \
+    } else {                                                            \
+        return;                                                         \
+    }
+
+    size = snprintf(
+        describe_buffer, remaining,
+        "sender chain index: %ld ", ratchet.sender_chain[0].chain_key.index
+    );
+    CHECK_SIZE_AND_ADVANCE;
+
+    size = snprintf(describe_buffer, remaining, "receiver chain indices:");
+    CHECK_SIZE_AND_ADVANCE;
+
+    for (size_t i = 0; i < ratchet.receiver_chains.size(); ++i) {
+        size = snprintf(
+            describe_buffer, remaining,
+            " %ld", ratchet.receiver_chains[i].chain_key.index
+        );
+        CHECK_SIZE_AND_ADVANCE;
+    }
+
+    size = snprintf(describe_buffer, remaining, " skipped message keys:");
+    CHECK_SIZE_AND_ADVANCE;
+
+    for (size_t i = 0; i < ratchet.skipped_message_keys.size(); ++i) {
+        size = snprintf(
+            describe_buffer, remaining,
+            " %ld", ratchet.skipped_message_keys[i].message_key.index
+        );
+        CHECK_SIZE_AND_ADVANCE;
+    }
+#undef CHECK_SIZE_AND_ADVANCE
+}
+
+namespace {
+// the master branch writes pickle version 1; the logging_enabled branch writes
+// 0x80000001.
+static const std::uint32_t SESSION_PICKLE_VERSION = 1;
+}
+
+std::size_t olm::pickle_length(
+    Session const & value
+) {
+    std::size_t length = 0;
+    length += olm::pickle_length(SESSION_PICKLE_VERSION);
+    length += olm::pickle_length(value.received_message);
+    length += olm::pickle_length(value.alice_identity_key);
+    length += olm::pickle_length(value.alice_base_key);
+    length += olm::pickle_length(value.bob_one_time_key);
+    length += olm::pickle_length(value.ratchet);
+    return length;
+}
+
+
+std::uint8_t * olm::pickle(
+    std::uint8_t * pos,
+    Session const & value
+) {
+    pos = olm::pickle(pos, SESSION_PICKLE_VERSION);
+    pos = olm::pickle(pos, value.received_message);
+    pos = olm::pickle(pos, value.alice_identity_key);
+    pos = olm::pickle(pos, value.alice_base_key);
+    pos = olm::pickle(pos, value.bob_one_time_key);
+    pos = olm::pickle(pos, value.ratchet);
+    return pos;
+}
+
+
+std::uint8_t const * olm::unpickle(
+    std::uint8_t const * pos, std::uint8_t const * end,
+    Session & value
+) {
+    uint32_t pickle_version;
+    pos = olm::unpickle(pos, end, pickle_version); UNPICKLE_OK(pos);
+
+    bool includes_chain_index;
+    switch (pickle_version) {
+        case 1:
+            includes_chain_index = false;
+            break;
+
+        case 0x80000001UL:
+            includes_chain_index = true;
+            break;
+
+        default:
+            value.last_error = OlmErrorCode::OLM_UNKNOWN_PICKLE_VERSION;
+            return nullptr;
+    }
+
+    pos = olm::unpickle(pos, end, value.received_message); UNPICKLE_OK(pos);
+    pos = olm::unpickle(pos, end, value.alice_identity_key); UNPICKLE_OK(pos);
+    pos = olm::unpickle(pos, end, value.alice_base_key); UNPICKLE_OK(pos);
+    pos = olm::unpickle(pos, end, value.bob_one_time_key); UNPICKLE_OK(pos);
+    pos = olm::unpickle(pos, end, value.ratchet, includes_chain_index); UNPICKLE_OK(pos);
+
+    return pos;
+}