From d7fce164afd45b632cbf8aa0f30837d7e9b9a50e Mon Sep 17 00:00:00 2001 From: Konstantinos Sideris <sideris.konstantin@gmail.com> Date: Sun, 1 Apr 2018 18:29:09 +0300 Subject: [PATCH] Upload signed one time keys fixes #21 --- src/client.cpp | 25 ++++++++++--- src/client.hpp | 9 ++++- src/crypto.cpp | 36 +++++++++++++++++++ src/crypto.hpp | 11 ++++++ tests/e2ee.cpp | 98 +++++++++++++++++++++++++++++++++++++++++++++----- 5 files changed, 165 insertions(+), 14 deletions(-) diff --git a/src/client.cpp b/src/client.cpp index e1e359b4a..b370b0fdc 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -561,11 +561,14 @@ Client::flow_response(const std::string &user, // void -Client::upload_identity_keys( +Client::upload_keys( const nlohmann::json &identity_keys, + const std::map<std::string, nlohmann::json> &one_time_keys, std::function<void(const mtx::responses::UploadKeys &res, RequestErr err)> callback) { mtx::requests::UploadKeys req; + req.one_time_keys = one_time_keys; + req.device_keys.user_id = user_id().to_string(); req.device_keys.device_id = device_id(); req.device_keys.keys.emplace("curve25519:" + device_id(), identity_keys["curve25519"]); @@ -576,17 +579,29 @@ Client::upload_identity_keys( } void -Client::upload_one_time_keys( +Client::upload_identity_keys( const nlohmann::json &identity_keys, std::function<void(const mtx::responses::UploadKeys &res, RequestErr err)> callback) { mtx::requests::UploadKeys req; req.device_keys.user_id = user_id().to_string(); req.device_keys.device_id = device_id(); + req.device_keys.keys.emplace("curve25519:" + device_id(), identity_keys["curve25519"]); + req.device_keys.keys.emplace("ed25519:" + device_id(), identity_keys["ed25519"]); - auto obj = identity_keys.at("curve25519"); - for (auto it = obj.begin(); it != obj.end(); ++it) - req.one_time_keys.emplace("curve25519:" + it.key(), it.value()); + post<mtx::requests::UploadKeys, mtx::responses::UploadKeys>( + "/client/r0/keys/upload", req, callback); +} + +void +Client::upload_one_time_keys( + const std::map<std::string, nlohmann::json> &one_time_keys, + std::function<void(const mtx::responses::UploadKeys &res, RequestErr err)> callback) +{ + mtx::requests::UploadKeys req; + req.device_keys.user_id = user_id().to_string(); + req.device_keys.device_id = device_id(); + req.one_time_keys = one_time_keys; post<mtx::requests::UploadKeys, mtx::responses::UploadKeys>( "/client/r0/keys/upload", req, callback); diff --git a/src/client.hpp b/src/client.hpp index be4f16c1c..19ea23c35 100644 --- a/src/client.hpp +++ b/src/client.hpp @@ -200,6 +200,13 @@ public: // Encryption related endpoints. // + //! Upload identity keys & one time keys. + // TODO: Replace json with a proper type. API methods shouldn't throw. + void upload_keys( + const nlohmann::json &identity_keys, + const std::map<std::string, nlohmann::json> &one_time_keys, + std::function<void(const mtx::responses::UploadKeys &res, RequestErr err)> cb); + //! Upload identity keys. // TODO: Replace json with a proper type. API methods shouldn't throw. void upload_identity_keys( @@ -209,7 +216,7 @@ public: //! Upload one time keys. // TODO: Replace json with a proper type. API methods shouldn't throw. void upload_one_time_keys( - const nlohmann::json &one_time_keys, + const std::map<std::string, nlohmann::json> &one_time_keys, std::function<void(const mtx::responses::UploadKeys &res, RequestErr err)> cb); private: diff --git a/src/crypto.cpp b/src/crypto.cpp index 17c8a003c..c8282c313 100644 --- a/src/crypto.cpp +++ b/src/crypto.cpp @@ -1,6 +1,8 @@ +#include <iostream> #include <sodium.h> #include "crypto.hpp" +#include "olm/base64.hh" using json = nlohmann::json; using namespace mtx::client::crypto; @@ -71,3 +73,37 @@ mtx::client::crypto::one_time_keys(std::shared_ptr<olm::Account> account) return json::parse(data); } + +std::string +mtx::client::crypto::sign_one_time_key(std::shared_ptr<olm::Account> account, + const std::string &key) +{ + json j{{"key", key}}; + auto str_json = j.dump(); + + constexpr std::size_t SIGNATURE_SIZE = 64; + + // Message + std::vector<std::uint8_t> tmp(str_json.begin(), str_json.end()); + std::uint8_t *buf = &tmp[0]; + std::size_t nbytes = str_json.size(); + + // Signature + auto signature_buf = create_buffer(SIGNATURE_SIZE); + account->sign(buf, nbytes, signature_buf.get(), SIGNATURE_SIZE); + + auto encoded_buf = create_buffer(SIGNATURE_SIZE); + olm::encode_base64(signature_buf.get(), SIGNATURE_SIZE, encoded_buf.get()); + + return std::string(encoded_buf.get(), encoded_buf.get() + SIGNATURE_SIZE); +} + +json +mtx::client::crypto::signed_one_time_key_json(const mtx::identifiers::User &user_id, + const std::string &device_id, + const std::string &key, + const std::string &signature) +{ + return json{{"key", key}, + {"signatures", {{user_id.to_string(), {{"ed25519:" + device_id, signature}}}}}}; +} diff --git a/src/crypto.hpp b/src/crypto.hpp index 4f033cbef..68d930b33 100644 --- a/src/crypto.hpp +++ b/src/crypto.hpp @@ -4,6 +4,7 @@ #include <memory> #include <json.hpp> +#include <mtx/identifiers.hpp> #include <olm/account.hh> #include <olm/error.h> @@ -49,6 +50,16 @@ one_time_keys(std::shared_ptr<olm::Account> user); std::unique_ptr<uint8_t[]> create_buffer(std::size_t nbytes); +//! Sign the given one time keys. +std::string +sign_one_time_key(std::shared_ptr<olm::Account> account, const std::string &key); + +//! Generate the json structure for the signed one time key. +nlohmann::json +signed_one_time_key_json(const mtx::identifiers::User &user_id, + const std::string &device_id, + const std::string &key, + const std::string &signature); } // namespace crypto } // namespace client } // namespace mtx diff --git a/tests/e2ee.cpp b/tests/e2ee.cpp index 9af76e64a..10573c8b6 100644 --- a/tests/e2ee.cpp +++ b/tests/e2ee.cpp @@ -80,17 +80,99 @@ TEST(Encryption, UploadOneTimeKeys) while (alice->access_token().empty()) std::this_thread::sleep_for(std::chrono::milliseconds(100)); - auto number_of_keys = mtx::client::crypto::generate_one_time_keys(olm_account, 5); - EXPECT_EQ(number_of_keys, 5); + auto nkeys = mtx::client::crypto::generate_one_time_keys(olm_account, 5); + EXPECT_EQ(nkeys, 5); auto one_time_keys = mtx::client::crypto::one_time_keys(olm_account); - alice->upload_one_time_keys(one_time_keys, - [](const mtx::responses::UploadKeys &res, ErrType err) { - check_error(err); - EXPECT_EQ(res.one_time_key_counts.size(), 1); - EXPECT_EQ(res.one_time_key_counts.at("curve25519"), 5); - }); + // Create the proper structure for uploading. + std::map<std::string, json> keys; + + auto obj = one_time_keys.at("curve25519"); + for (auto it = obj.begin(); it != obj.end(); ++it) + keys["curve25519:" + it.key()] = it.value(); + + alice->upload_one_time_keys(keys, [](const mtx::responses::UploadKeys &res, ErrType err) { + check_error(err); + EXPECT_EQ(res.one_time_key_counts.size(), 1); + EXPECT_EQ(res.one_time_key_counts.at("curve25519"), 5); + }); + + alice->close(); +} + +TEST(Encryption, UploadSignedOneTimeKeys) +{ + auto alice = std::make_shared<Client>("localhost"); + auto olm_account = mtx::client::crypto::olm_new_account(); + + alice->login( + "alice", "secret", [](const mtx::responses::Login &, ErrType err) { check_error(err); }); + + while (alice->access_token().empty()) + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + auto nkeys = mtx::client::crypto::generate_one_time_keys(olm_account, 5); + EXPECT_EQ(nkeys, 5); + + auto one_time_keys = mtx::client::crypto::one_time_keys(olm_account); + + // Create the proper structure for uploading. + std::map<std::string, json> signed_keys; + + auto obj = one_time_keys.at("curve25519"); + for (auto it = obj.begin(); it != obj.end(); ++it) { + auto sig = mtx::client::crypto::sign_one_time_key(olm_account, it.value()); + + signed_keys["signed_curve25519:" + it.key()] = + mtx::client::crypto::signed_one_time_key_json( + alice->user_id(), alice->device_id(), it.value(), sig); + } + + alice->upload_one_time_keys( + signed_keys, [nkeys](const mtx::responses::UploadKeys &res, ErrType err) { + check_error(err); + EXPECT_EQ(res.one_time_key_counts.size(), 1); + EXPECT_EQ(res.one_time_key_counts.at("signed_curve25519"), nkeys); + }); + + alice->close(); +} + +TEST(Encryption, UploadKeys) +{ + auto alice = std::make_shared<Client>("localhost"); + auto olm_account = mtx::client::crypto::olm_new_account(); + + alice->login( + "alice", "secret", [](const mtx::responses::Login &, ErrType err) { check_error(err); }); + + while (alice->access_token().empty()) + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + json identity_keys = mtx::client::crypto::identity_keys(olm_account); + + mtx::client::crypto::generate_one_time_keys(olm_account, 1); + auto one_time_keys = mtx::client::crypto::one_time_keys(olm_account); + + // Create the proper structure for uploading. + std::map<std::string, json> signed_keys; + + auto obj = one_time_keys.at("curve25519"); + for (auto it = obj.begin(); it != obj.end(); ++it) { + auto sig = mtx::client::crypto::sign_one_time_key(olm_account, it.value()); + + signed_keys["signed_curve25519:" + it.key()] = + mtx::client::crypto::signed_one_time_key_json( + alice->user_id(), alice->device_id(), it.value(), sig); + } + + alice->upload_keys( + identity_keys, signed_keys, [](const mtx::responses::UploadKeys &res, ErrType err) { + check_error(err); + EXPECT_EQ(res.one_time_key_counts.size(), 1); + EXPECT_EQ(res.one_time_key_counts.at("signed_curve25519"), 1); + }); alice->close(); } -- GitLab