diff --git a/include/matrixclient.h b/include/matrixclient.h index e6d78d9..4a37c57 100644 --- a/include/matrixclient.h +++ b/include/matrixclient.h @@ -1,12 +1,14 @@ #ifndef _matrixclient_h_ #define _matrixclient_h_ -#include -#include #include <3ds.h> +#include #include #include -#include +#include +#include +#include +#include "olm/account.hh" namespace Matrix { @@ -49,6 +51,7 @@ private: bool stopSyncing = false; bool isSyncing = false; Thread syncThread; + olm::Account acc; struct { eventCallback event = NULL; eventCallback leaveRoom = NULL; @@ -61,6 +64,8 @@ private: json_t* doSync(std::string token, std::string filter, u32 timeout, CURLcode* res); json_t* doRequest(const char* method, std::string path, json_t* body = NULL, u32 timeout = 5, CURLcode* retRes = NULL); json_t* doRequestCurl(const char* method, std::string url, json_t* body, u32 timeout, CURLcode* retRes); + + static void print_json(json_t *json); public: Client(std::string homeserverUrl, std::string matrixToken = "", Store* clientStore = NULL); std::string getToken() const; @@ -97,6 +102,19 @@ public: void setRoomLimitedCallback(roomLimitedCallback cb); void syncLoop(); void uploadKeys(json_t* body); + + // Crypto + void generate_otk(size_t otkcount); + void generate_otk(); + void generate_device_key(); + void generate_fallback_key(); + void sign_json(json_t* json); + json_t* get_device_keys(); + json_t* get_fallback_keys(); + json_t* get_unpublished_otk(); + void save_keys(); + void upload_keys(); + void start_encryption(); }; }; // namespace Matrix diff --git a/source/matrixclient.cpp b/source/matrixclient.cpp index cfe22cd..c5594c3 100644 --- a/source/matrixclient.cpp +++ b/source/matrixclient.cpp @@ -1,16 +1,21 @@ -#include -#include -#include #include <3ds.h> +#include #include #include -#include -#include -#include "util.h" -#include "memorystore.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include -#include +#include "memorystore.h" +#include "olm/base64.hh" +#include "util.h" #include @@ -877,4 +882,200 @@ json_t* Client::doRequestCurl(const char* method, std::string url, json_t* body, return content; } +void Client::print_json(json_t *json){ + char* data = json_dumps(json, 0); + puts(data); + free(data); +} + +void Client::generate_otk(size_t otkcount) { + size_t len = acc.generate_one_time_keys_random_length(otkcount); + std::unique_ptr otkrandom = std::make_unique(len); + PS_GenerateRandomBytes(otkrandom.get(), len); + acc.generate_one_time_keys(otkcount, otkrandom.get(), len); +} +void Client::generate_otk() { + size_t otkcount = acc.max_number_of_one_time_keys() / 2; + generate_otk(otkcount); +} +void Client::generate_device_key() { + size_t len = acc.new_account_random_length(); + std::unique_ptr random = std::make_unique(len); + PS_GenerateRandomBytes(random.get(), len); + acc.new_account(random.get(), len); +} +void Client::generate_fallback_key() { + size_t len = acc.generate_fallback_key_random_length(); + std::unique_ptr random = std::make_unique(len); + PS_GenerateRandomBytes(random.get(), len); + acc.generate_fallback_key(random.get(), len); +} +// Make sure to remove any unsigned and signatures block before doing this ! +void Client::sign_json(json_t* json) { + puts("Signing json"); + char* jsonStr = json_dumps(json, JSON_COMPACT | JSON_SORT_KEYS); + size_t signlen = acc.signature_length(); + size_t ptrlen = olm::encode_base64_length(signlen); + std::unique_ptr signature = std::make_unique(ptrlen); + acc.sign(reinterpret_cast(jsonStr), strlen(jsonStr), signature.get() + ptrlen - signlen, signlen); + free(jsonStr); + + olm::encode_base64(signature.get() + ptrlen - signlen, signlen, signature.get()); + + json_t* signitem = json_object(); + std::cout << std::string(reinterpret_cast(signature.get()), ptrlen) << std::endl; + json_object_set_new(signitem, ("ed25519:" + getDeviceId()).c_str(), json_stringn(reinterpret_cast(signature.get()), ptrlen)); + json_t* signobj = json_object(); + json_object_set_new(signobj, getUserId().c_str(), signitem); + json_object_set_new(json, "signatures", signobj); +} +json_t* Client::get_device_keys() { + json_t* device_keys = json_object(); + json_t* algorithms = json_array(); + json_array_append_new(algorithms, json_string("m.olm.v1.curve25519-aes-sha2")); + json_array_append_new(algorithms, json_string("m.megolm.v1.aes-sha2")); + json_object_set_new(device_keys, "algorithms", algorithms); + json_object_set_new(device_keys, "device_id", json_string(getDeviceId().c_str())); + json_object_set_new(device_keys, "user_id", json_string(getUserId().c_str())); + + // Extract keys + size_t acclen = acc.get_identity_json_length(); + std::unique_ptr acckeys = std::make_unique(acclen); + size_t bytes = acc.get_identity_json(acckeys.get(), acclen); + std::cout << "Parsing bytes" << std::endl; + json_error_t error; + sleep(5); + std::cout << "Heading into it..." << std::endl; + json_t* keys = json_loadb(reinterpret_cast(acckeys.get()), bytes, 0, &error); + if (keys == nullptr) { + printf("error: on line %d at char %d: %s\n", error.line, error.column, error.text); + sleep(10); + } + std::cout << "Reloaded data" << std::endl; + std::cout << "Adding bits" << std::endl; + + // Merge keys + json_object_set(device_keys, ("ed25519:" + getDeviceId()).c_str(), json_object_get(keys, "ed25519")); + json_object_set(device_keys, ("curve25519:" + getDeviceId()).c_str(), json_object_get(keys, "curve25519")); + json_decref(keys); + std::cout << "Finalizing" << std::endl; + return device_keys; +} +json_t* Client::get_fallback_keys() { + json_t* fallback_keys = json_object(); + // Extract keys + size_t acclen = acc.get_unpublished_fallback_key_json_length(); + auto acckeys = std::make_unique(acclen); + size_t bytes = acc.get_unpublished_fallback_key_json(acckeys.get(), acclen); + json_t* keys = json_loadb(reinterpret_cast(acckeys.get()), bytes, 0, nullptr); + json_t* keyobj = json_object_get(keys, "curve25519"); + void* iter = json_object_iter(keyobj); + + while (iter != nullptr) { + const char* keyid = json_object_iter_key(iter); + json_t* keyval = json_object_iter_value(iter); + json_t* tosign = json_object(); + json_object_set(tosign, "key", keyval); + // object is fallback key + json_object_set_new(tosign, "fallback", json_true()); + + sign_json(tosign); + json_object_set(fallback_keys, (std::string("signed_curve25519:") + keyid).c_str(), tosign); + iter = json_object_iter_next(keyobj, iter); + } + json_decref(keys); + return fallback_keys; +} +json_t* Client::get_unpublished_otk() { + json_t* otk_keys = json_object(); + // Extract keys + size_t acclen = acc.get_one_time_keys_json_length(); + auto acckeys = std::make_unique(acclen); + size_t bytes = acc.get_one_time_keys_json(acckeys.get(), acclen); + json_t* keys = json_loadb(reinterpret_cast(acckeys.get()), bytes, 0, nullptr); + + json_t* keyobj = json_object_get(keys, "curve25519"); + void* iter = json_object_iter(keyobj); + + while (iter != nullptr) { + const char* keyid = json_object_iter_key(iter); + json_t* keyval = json_object_iter_value(iter); + json_t* tosign = json_object(); + json_object_set(tosign, "key", keyval); + + sign_json(tosign); + json_object_set(otk_keys, (std::string("signed_curve25519:") + keyid).c_str(), tosign); + iter = json_object_iter_next(keyobj, iter); + } + json_decref(keys); + return otk_keys; +} +void Client::save_keys() { + size_t pacclen = olm::pickle_length(acc); + auto m = std::make_unique(pacclen); + olm::pickle(m.get(), acc); + FILE* file = fopen("secret-keys", "w"); + fwrite(m.get(), pacclen, 1, file); + fclose(file); +} +void Client::upload_keys() { // Upload the keys --- + // Build device key json + json_t* upload_keys = json_object(); + + json_t* device_keys = get_device_keys(); + std::cout << "Gathered device keys" << std::endl; + // Sign keys block + sign_json(device_keys); + std::cout << "Signed device keys" << std::endl; + json_object_set_new(upload_keys, "device_keys", device_keys); + + json_t* fallback_keys = get_fallback_keys(); + std::cout << "Signed fallback keys" << std::endl; + // fallback keys are already signed + json_object_set_new(upload_keys, "fallback_keys", fallback_keys); + + // OTK keys + json_t* otk = get_unpublished_otk(); + std::cout << "Signed OTK keys" << std::endl; + // OTK keys are already signed + json_object_set_new(upload_keys, "one_time_keys", otk); + + uploadKeys(upload_keys); + std::cout << "Keys uploaded !" << std::endl; + print_json(upload_keys); + json_decref(upload_keys); + acc.mark_keys_as_published(); + // Whether a key or not is published is saved, let's save again + save_keys(); +} +void Client::start_encryption() { + // std::FILE* file = std::fopen("secret-keys", "r"); + std::ifstream ifs("secret-keys", std::ios::binary | std::ios::ate); + if (!ifs.is_open()) { + std::cout << "No secret keys found, generating " << std::endl; + // Generate and save to file + // Device keys + generate_device_key(); + std::cout << "Generated device key" << std::endl; + // One time keys + generate_otk(); + std::cout << "Generated OTK key" << std::endl; + // Fallback keys + generate_fallback_key(); + std::cout << "Generated fallback key" << std::endl; + upload_keys(); + + } else { + // Load from file + std::ifstream::pos_type fsize = ifs.tellg(); + if (fsize == -1) { + std::cout << "What." << std::endl; + return; + } + auto data = std::make_unique(fsize); + ifs.seekg(0, std::ios::beg); + ifs.read(reinterpret_cast(data.get()), static_cast(fsize)); + olm::unpickle(data.get(), data.get() + fsize, acc); + } +} }; // namespace Matrix