]> gitweb.ps.run Git - matrix_esp_thesis/blobdiff - ext/olm/src/account.cpp
add dependencies to repo
[matrix_esp_thesis] / ext / olm / src / account.cpp
diff --git a/ext/olm/src/account.cpp b/ext/olm/src/account.cpp
new file mode 100644 (file)
index 0000000..41b7188
--- /dev/null
@@ -0,0 +1,580 @@
+/* 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/account.hh"
+#include "olm/base64.hh"
+#include "olm/pickle.h"
+#include "olm/pickle.hh"
+#include "olm/memory.hh"
+
+olm::Account::Account(
+) : num_fallback_keys(0),
+    next_one_time_key_id(0),
+    last_error(OlmErrorCode::OLM_SUCCESS) {
+}
+
+
+olm::OneTimeKey const * olm::Account::lookup_key(
+    _olm_curve25519_public_key const & public_key
+) {
+    for (olm::OneTimeKey const & key : one_time_keys) {
+        if (olm::array_equal(key.key.public_key.public_key, public_key.public_key)) {
+            return &key;
+        }
+    }
+    if (num_fallback_keys >= 1
+            && olm::array_equal(
+                current_fallback_key.key.public_key.public_key, public_key.public_key
+            )
+    ) {
+        return &current_fallback_key;
+    }
+    if (num_fallback_keys >= 2
+            && olm::array_equal(
+                prev_fallback_key.key.public_key.public_key, public_key.public_key
+            )
+    ) {
+        return &prev_fallback_key;
+    }
+    return 0;
+}
+
+std::size_t olm::Account::remove_key(
+    _olm_curve25519_public_key const & public_key
+) {
+    OneTimeKey * i;
+    for (i = one_time_keys.begin(); i != one_time_keys.end(); ++i) {
+        if (olm::array_equal(i->key.public_key.public_key, public_key.public_key)) {
+            std::uint32_t id = i->id;
+            one_time_keys.erase(i);
+            return id;
+        }
+    }
+    // check if the key is a fallback key, to avoid returning an error, but
+    // don't actually remove it
+    if (num_fallback_keys >= 1
+            && olm::array_equal(
+                current_fallback_key.key.public_key.public_key, public_key.public_key
+            )
+    ) {
+        return current_fallback_key.id;
+    }
+    if (num_fallback_keys >= 2
+            && olm::array_equal(
+                prev_fallback_key.key.public_key.public_key, public_key.public_key
+            )
+    ) {
+        return prev_fallback_key.id;
+    }
+    return std::size_t(-1);
+}
+
+std::size_t olm::Account::new_account_random_length() const {
+    return ED25519_RANDOM_LENGTH + CURVE25519_RANDOM_LENGTH;
+}
+
+std::size_t olm::Account::new_account(
+    uint8_t const * random, std::size_t random_length
+) {
+    if (random_length < new_account_random_length()) {
+        last_error = OlmErrorCode::OLM_NOT_ENOUGH_RANDOM;
+        return std::size_t(-1);
+    }
+
+    _olm_crypto_ed25519_generate_key(random, &identity_keys.ed25519_key);
+    random += ED25519_RANDOM_LENGTH;
+    _olm_crypto_curve25519_generate_key(random, &identity_keys.curve25519_key);
+
+    return 0;
+}
+
+namespace {
+
+uint8_t KEY_JSON_ED25519[] = "\"ed25519\":";
+uint8_t KEY_JSON_CURVE25519[] = "\"curve25519\":";
+
+template<typename T>
+static std::uint8_t * write_string(
+    std::uint8_t * pos,
+    T const & value
+) {
+    std::memcpy(pos, value, sizeof(T) - 1);
+    return pos + (sizeof(T) - 1);
+}
+
+}
+
+
+std::size_t olm::Account::get_identity_json_length() const {
+    std::size_t length = 0;
+    length += 1; /* { */
+    length += sizeof(KEY_JSON_CURVE25519) - 1;
+    length += 1; /* " */
+    length += olm::encode_base64_length(
+        sizeof(identity_keys.curve25519_key.public_key)
+    );
+    length += 2; /* ", */
+    length += sizeof(KEY_JSON_ED25519) - 1;
+    length += 1; /* " */
+    length += olm::encode_base64_length(
+        sizeof(identity_keys.ed25519_key.public_key)
+    );
+    length += 2; /* "} */
+    return length;
+}
+
+
+std::size_t olm::Account::get_identity_json(
+    std::uint8_t * identity_json, std::size_t identity_json_length
+) {
+    std::uint8_t * pos = identity_json;
+    size_t expected_length = get_identity_json_length();
+
+    if (identity_json_length < expected_length) {
+        last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
+        return std::size_t(-1);
+    }
+
+    *(pos++) = '{';
+    pos = write_string(pos, KEY_JSON_CURVE25519);
+    *(pos++) = '\"';
+    pos = olm::encode_base64(
+        identity_keys.curve25519_key.public_key.public_key,
+        sizeof(identity_keys.curve25519_key.public_key.public_key),
+        pos
+    );
+    *(pos++) = '\"'; *(pos++) = ',';
+    pos = write_string(pos, KEY_JSON_ED25519);
+    *(pos++) = '\"';
+    pos = olm::encode_base64(
+        identity_keys.ed25519_key.public_key.public_key,
+        sizeof(identity_keys.ed25519_key.public_key.public_key),
+        pos
+    );
+    *(pos++) = '\"'; *(pos++) = '}';
+    return pos - identity_json;
+}
+
+
+std::size_t olm::Account::signature_length(
+) const {
+    return ED25519_SIGNATURE_LENGTH;
+}
+
+
+std::size_t olm::Account::sign(
+    std::uint8_t const * message, std::size_t message_length,
+    std::uint8_t * signature, std::size_t signature_length
+) {
+    if (signature_length < this->signature_length()) {
+        last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
+        return std::size_t(-1);
+    }
+    _olm_crypto_ed25519_sign(
+        &identity_keys.ed25519_key, message, message_length, signature
+    );
+    return this->signature_length();
+}
+
+
+std::size_t olm::Account::get_one_time_keys_json_length(
+) const {
+    std::size_t length = 0;
+    bool is_empty = true;
+    for (auto const & key : one_time_keys) {
+        if (key.published) {
+            continue;
+        }
+        is_empty = false;
+        length += 2; /* {" */
+        length += olm::encode_base64_length(_olm_pickle_uint32_length(key.id));
+        length += 3; /* ":" */
+        length += olm::encode_base64_length(sizeof(key.key.public_key));
+        length += 1; /* " */
+    }
+    if (is_empty) {
+        length += 1; /* { */
+    }
+    length += 3; /* }{} */
+    length += sizeof(KEY_JSON_CURVE25519) - 1;
+    return length;
+}
+
+
+std::size_t olm::Account::get_one_time_keys_json(
+    std::uint8_t * one_time_json, std::size_t one_time_json_length
+) {
+    std::uint8_t * pos = one_time_json;
+    if (one_time_json_length < get_one_time_keys_json_length()) {
+        last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
+        return std::size_t(-1);
+    }
+    *(pos++) = '{';
+    pos = write_string(pos, KEY_JSON_CURVE25519);
+    std::uint8_t sep = '{';
+    for (auto const & key : one_time_keys) {
+        if (key.published) {
+            continue;
+        }
+        *(pos++) = sep;
+        *(pos++) = '\"';
+        std::uint8_t key_id[_olm_pickle_uint32_length(key.id)];
+        _olm_pickle_uint32(key_id, key.id);
+        pos = olm::encode_base64(key_id, sizeof(key_id), pos);
+        *(pos++) = '\"'; *(pos++) = ':'; *(pos++) = '\"';
+        pos = olm::encode_base64(
+            key.key.public_key.public_key, sizeof(key.key.public_key.public_key), pos
+        );
+        *(pos++) = '\"';
+        sep = ',';
+    }
+    if (sep != ',') {
+        /* The list was empty */
+        *(pos++) = sep;
+    }
+    *(pos++) = '}';
+    *(pos++) = '}';
+    return pos - one_time_json;
+}
+
+
+std::size_t olm::Account::mark_keys_as_published(
+) {
+    std::size_t count = 0;
+    for (auto & key : one_time_keys) {
+        if (!key.published) {
+            key.published = true;
+            count++;
+        }
+    }
+    current_fallback_key.published = true;
+    return count;
+}
+
+
+std::size_t olm::Account::max_number_of_one_time_keys(
+) const {
+    return olm::MAX_ONE_TIME_KEYS;
+}
+
+std::size_t olm::Account::generate_one_time_keys_random_length(
+    std::size_t number_of_keys
+) const {
+    return CURVE25519_RANDOM_LENGTH * number_of_keys;
+}
+
+std::size_t olm::Account::generate_one_time_keys(
+    std::size_t number_of_keys,
+    std::uint8_t const * random, std::size_t random_length
+) {
+    if (random_length < generate_one_time_keys_random_length(number_of_keys)) {
+        last_error = OlmErrorCode::OLM_NOT_ENOUGH_RANDOM;
+        return std::size_t(-1);
+    }
+    for (unsigned i = 0; i < number_of_keys; ++i) {
+        OneTimeKey & key = *one_time_keys.insert(one_time_keys.begin());
+        key.id = ++next_one_time_key_id;
+        key.published = false;
+        _olm_crypto_curve25519_generate_key(random, &key.key);
+        random += CURVE25519_RANDOM_LENGTH;
+    }
+    return number_of_keys;
+}
+
+std::size_t olm::Account::generate_fallback_key_random_length() const {
+    return CURVE25519_RANDOM_LENGTH;
+}
+
+std::size_t olm::Account::generate_fallback_key(
+    std::uint8_t const * random, std::size_t random_length
+) {
+    if (random_length < generate_fallback_key_random_length()) {
+        last_error = OlmErrorCode::OLM_NOT_ENOUGH_RANDOM;
+        return std::size_t(-1);
+    }
+    if (num_fallback_keys < 2) {
+        num_fallback_keys++;
+    }
+    prev_fallback_key = current_fallback_key;
+    current_fallback_key.id = ++next_one_time_key_id;
+    current_fallback_key.published = false;
+    _olm_crypto_curve25519_generate_key(random, &current_fallback_key.key);
+    return 1;
+}
+
+
+std::size_t olm::Account::get_fallback_key_json_length(
+) const {
+    std::size_t length = 4 + sizeof(KEY_JSON_CURVE25519) - 1; /* {"curve25519":{}} */
+    if (num_fallback_keys >= 1) {
+        const OneTimeKey & key = current_fallback_key;
+        length += 1; /* " */
+        length += olm::encode_base64_length(_olm_pickle_uint32_length(key.id));
+        length += 3; /* ":" */
+        length += olm::encode_base64_length(sizeof(key.key.public_key));
+        length += 1; /* " */
+    }
+    return length;
+}
+
+std::size_t olm::Account::get_fallback_key_json(
+    std::uint8_t * fallback_json, std::size_t fallback_json_length
+) {
+    std::uint8_t * pos = fallback_json;
+    if (fallback_json_length < get_fallback_key_json_length()) {
+        last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
+        return std::size_t(-1);
+    }
+    *(pos++) = '{';
+    pos = write_string(pos, KEY_JSON_CURVE25519);
+    *(pos++) = '{';
+    OneTimeKey & key = current_fallback_key;
+    if (num_fallback_keys >= 1) {
+        *(pos++) = '\"';
+        std::uint8_t key_id[_olm_pickle_uint32_length(key.id)];
+        _olm_pickle_uint32(key_id, key.id);
+        pos = olm::encode_base64(key_id, sizeof(key_id), pos);
+        *(pos++) = '\"'; *(pos++) = ':'; *(pos++) = '\"';
+        pos = olm::encode_base64(
+            key.key.public_key.public_key, sizeof(key.key.public_key.public_key), pos
+        );
+        *(pos++) = '\"';
+    }
+    *(pos++) = '}';
+    *(pos++) = '}';
+    return pos - fallback_json;
+}
+
+std::size_t olm::Account::get_unpublished_fallback_key_json_length(
+) const {
+    std::size_t length = 4 + sizeof(KEY_JSON_CURVE25519) - 1; /* {"curve25519":{}} */
+    const OneTimeKey & key = current_fallback_key;
+    if (num_fallback_keys >= 1 && !key.published) {
+        length += 1; /* " */
+        length += olm::encode_base64_length(_olm_pickle_uint32_length(key.id));
+        length += 3; /* ":" */
+        length += olm::encode_base64_length(sizeof(key.key.public_key));
+        length += 1; /* " */
+    }
+    return length;
+}
+
+std::size_t olm::Account::get_unpublished_fallback_key_json(
+    std::uint8_t * fallback_json, std::size_t fallback_json_length
+) {
+    std::uint8_t * pos = fallback_json;
+    if (fallback_json_length < get_unpublished_fallback_key_json_length()) {
+        last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
+        return std::size_t(-1);
+    }
+    *(pos++) = '{';
+    pos = write_string(pos, KEY_JSON_CURVE25519);
+    *(pos++) = '{';
+    OneTimeKey & key = current_fallback_key;
+    if (num_fallback_keys >= 1 && !key.published) {
+        *(pos++) = '\"';
+        std::uint8_t key_id[_olm_pickle_uint32_length(key.id)];
+        _olm_pickle_uint32(key_id, key.id);
+        pos = olm::encode_base64(key_id, sizeof(key_id), pos);
+        *(pos++) = '\"'; *(pos++) = ':'; *(pos++) = '\"';
+        pos = olm::encode_base64(
+            key.key.public_key.public_key, sizeof(key.key.public_key.public_key), pos
+        );
+        *(pos++) = '\"';
+    }
+    *(pos++) = '}';
+    *(pos++) = '}';
+    return pos - fallback_json;
+}
+
+void olm::Account::forget_old_fallback_key(
+) {
+    if (num_fallback_keys >= 2) {
+        num_fallback_keys = 1;
+        olm::unset(&prev_fallback_key, sizeof(prev_fallback_key));
+    }
+}
+
+namespace olm {
+
+static std::size_t pickle_length(
+    olm::IdentityKeys const & value
+) {
+    size_t length = 0;
+    length += _olm_pickle_ed25519_key_pair_length(&value.ed25519_key);
+    length += olm::pickle_length(value.curve25519_key);
+    return length;
+}
+
+
+static std::uint8_t * pickle(
+    std::uint8_t * pos,
+    olm::IdentityKeys const & value
+) {
+    pos = _olm_pickle_ed25519_key_pair(pos, &value.ed25519_key);
+    pos = olm::pickle(pos, value.curve25519_key);
+    return pos;
+}
+
+
+static std::uint8_t const * unpickle(
+    std::uint8_t const * pos, std::uint8_t const * end,
+    olm::IdentityKeys & value
+) {
+    pos = _olm_unpickle_ed25519_key_pair(pos, end, &value.ed25519_key); UNPICKLE_OK(pos);
+    pos = olm::unpickle(pos, end, value.curve25519_key); UNPICKLE_OK(pos);
+    return pos;
+}
+
+
+static std::size_t pickle_length(
+    olm::OneTimeKey const & value
+) {
+    std::size_t length = 0;
+    length += olm::pickle_length(value.id);
+    length += olm::pickle_length(value.published);
+    length += olm::pickle_length(value.key);
+    return length;
+}
+
+
+static std::uint8_t * pickle(
+    std::uint8_t * pos,
+    olm::OneTimeKey const & value
+) {
+    pos = olm::pickle(pos, value.id);
+    pos = olm::pickle(pos, value.published);
+    pos = olm::pickle(pos, value.key);
+    return pos;
+}
+
+
+static std::uint8_t const * unpickle(
+    std::uint8_t const * pos, std::uint8_t const * end,
+    olm::OneTimeKey & value
+) {
+    pos = olm::unpickle(pos, end, value.id); UNPICKLE_OK(pos);
+    pos = olm::unpickle(pos, end, value.published); UNPICKLE_OK(pos);
+    pos = olm::unpickle(pos, end, value.key); UNPICKLE_OK(pos);
+    return pos;
+}
+
+} // namespace olm
+
+namespace {
+// pickle version 1 used only 32 bytes for the ed25519 private key.
+// Any keys thus used should be considered compromised.
+// pickle version 2 does not have fallback keys.
+// pickle version 3 does not store whether the current fallback key is published.
+static const std::uint32_t ACCOUNT_PICKLE_VERSION = 4;
+}
+
+
+std::size_t olm::pickle_length(
+    olm::Account const & value
+) {
+    std::size_t length = 0;
+    length += olm::pickle_length(ACCOUNT_PICKLE_VERSION);
+    length += olm::pickle_length(value.identity_keys);
+    length += olm::pickle_length(value.one_time_keys);
+    length += olm::pickle_length(value.num_fallback_keys);
+    if (value.num_fallback_keys >= 1) {
+        length += olm::pickle_length(value.current_fallback_key);
+        if (value.num_fallback_keys >= 2) {
+            length += olm::pickle_length(value.prev_fallback_key);
+        }
+    }
+    length += olm::pickle_length(value.next_one_time_key_id);
+    return length;
+}
+
+
+std::uint8_t * olm::pickle(
+    std::uint8_t * pos,
+    olm::Account const & value
+) {
+    pos = olm::pickle(pos, ACCOUNT_PICKLE_VERSION);
+    pos = olm::pickle(pos, value.identity_keys);
+    pos = olm::pickle(pos, value.one_time_keys);
+    pos = olm::pickle(pos, value.num_fallback_keys);
+    if (value.num_fallback_keys >= 1) {
+        pos = olm::pickle(pos, value.current_fallback_key);
+        if (value.num_fallback_keys >= 2) {
+            pos = olm::pickle(pos, value.prev_fallback_key);
+        }
+    }
+    pos = olm::pickle(pos, value.next_one_time_key_id);
+    return pos;
+}
+
+
+std::uint8_t const * olm::unpickle(
+    std::uint8_t const * pos, std::uint8_t const * end,
+    olm::Account & value
+) {
+    uint32_t pickle_version;
+
+    pos = olm::unpickle(pos, end, pickle_version); UNPICKLE_OK(pos);
+
+    switch (pickle_version) {
+        case ACCOUNT_PICKLE_VERSION:
+        case 3:
+        case 2:
+            break;
+        case 1:
+            value.last_error = OlmErrorCode::OLM_BAD_LEGACY_ACCOUNT_PICKLE;
+            return nullptr;
+        default:
+            value.last_error = OlmErrorCode::OLM_UNKNOWN_PICKLE_VERSION;
+            return nullptr;
+    }
+
+    pos = olm::unpickle(pos, end, value.identity_keys); UNPICKLE_OK(pos);
+    pos = olm::unpickle(pos, end, value.one_time_keys); UNPICKLE_OK(pos);
+
+    if (pickle_version <= 2) {
+        // version 2 did not have fallback keys
+        value.num_fallback_keys = 0;
+    } else if (pickle_version == 3) {
+        // version 3 used the published flag to indicate how many fallback keys
+        // were present (we'll have to assume that the keys were published)
+        pos = olm::unpickle(pos, end, value.current_fallback_key); UNPICKLE_OK(pos);
+        pos = olm::unpickle(pos, end, value.prev_fallback_key); UNPICKLE_OK(pos);
+        if (value.current_fallback_key.published) {
+            if (value.prev_fallback_key.published) {
+                value.num_fallback_keys = 2;
+            } else {
+                value.num_fallback_keys = 1;
+            }
+        } else  {
+            value.num_fallback_keys = 0;
+        }
+    } else {
+        pos = olm::unpickle(pos, end, value.num_fallback_keys); UNPICKLE_OK(pos);
+        if (value.num_fallback_keys >= 1) {
+            pos = olm::unpickle(pos, end, value.current_fallback_key); UNPICKLE_OK(pos);
+            if (value.num_fallback_keys >= 2) {
+                pos = olm::unpickle(pos, end, value.prev_fallback_key); UNPICKLE_OK(pos);
+                if (value.num_fallback_keys >= 3) {
+                    value.last_error = OlmErrorCode::OLM_CORRUPTED_PICKLE;
+                    return nullptr;
+                }
+            }
+        }
+    }
+
+    pos = olm::unpickle(pos, end, value.next_one_time_key_id); UNPICKLE_OK(pos);
+
+    return pos;
+}