diff --git a/native/android/app/CMakeLists.txt b/native/android/app/CMakeLists.txt --- a/native/android/app/CMakeLists.txt +++ b/native/android/app/CMakeLists.txt @@ -171,6 +171,10 @@ ${RUST_NATIVE_CODE} ) +# Ensure Rust cxxbridge headers are generated before compiling C++ code +# Make package depend on both the Rust static library and cxxbridge target +add_dependencies(${PACKAGE_NAME} native_rust_library-static native_rust_library_cxxbridge) + set(BUILD_DIR ${CMAKE_SOURCE_DIR}/build) target_include_directories( diff --git a/native/cpp/CommonCpp/CryptoTools/CryptoModule.h b/native/cpp/CommonCpp/CryptoTools/CryptoModule.h --- a/native/cpp/CommonCpp/CryptoTools/CryptoModule.h +++ b/native/cpp/CommonCpp/CryptoTools/CryptoModule.h @@ -4,8 +4,6 @@ #include #include -#include "olm/olm.h" - #include "Persist.h" #include "Session.h" #include "Tools.h" @@ -21,14 +19,13 @@ class CryptoModule { - OlmBuffer accountBuffer; + ::rust::Box<::VodozemacAccount> vodozemacAccount; std::unordered_map> sessions = {}; Keys keys; + std::string secretKey; - OlmAccount *getOlmAccount(); - void createAccount(); void exposePublicIdentityKeys(); void generateOneTimeKeys(size_t oneTimeKeysAmount); std::string generateAndGetPrekey(); @@ -37,43 +34,38 @@ bool prekeyExistsAndOlderThan(uint64_t threshold); bool prekeyDoesntExist(); bool isPrekeySignatureValid(); - OlmBuffer pickleAccount(const std::string &secretKey); + std::string pickleAccount(const std::string &secretKey); public: - CryptoModule(); CryptoModule(std::string secretKey, Persist persist); // CryptoModule's accountBuffer cannot be safely copied // See explanation in https://phab.comm.dev/D9562 CryptoModule(const CryptoModule &) = delete; - static Keys keysFromStrings( - const std::string &identityKeys, - const std::string &oneTimeKeys); - std::string getIdentityKeys(); - std::string getOneTimeKeysForPublishing(size_t oneTimeKeysAmount = 10); + std::vector + getOneTimeKeysForPublishing(size_t oneTimeKeysAmount = 10); // Prekey rotation methods for X3DH - std::uint8_t getNumPrekeys(); std::string getPrekey(); std::string getPrekeySignature(); std::optional getUnpublishedPrekey(); void markPrekeyAsPublished(); void forgetOldPrekey(); - void initializeInboundForReceivingSession( + std::string initializeInboundForReceivingSession( const std::string &targetDeviceId, - const OlmBuffer &encryptedMessage, - const OlmBuffer &idKeys, + const crypto::EncryptedData &encryptedData, + const std::string &idKeys, int sessionVersion, const bool overwrite); int initializeOutboundForSendingSession( const std::string &targetDeviceId, - const OlmBuffer &idKeys, - const OlmBuffer &preKeys, - const OlmBuffer &preKeySignature, - const std::optional &oneTimeKey); + const std::string &idKeys, + const std::string &preKeys, + const std::string &preKeySignature, + const std::optional &oneTimeKey); bool hasSessionFor(const std::string &targetDeviceId); std::shared_ptr getSessionByDeviceId(const std::string &deviceId); void removeSessionByDeviceId(const std::string &deviceId); @@ -90,7 +82,7 @@ std::string signMessage(const std::string &message); static void verifySignature( const std::string &publicKey, - const OlmBuffer &message, + const std::string &message, const std::string &signature); std::optional validatePrekey(); }; diff --git a/native/cpp/CommonCpp/CryptoTools/CryptoModule.cpp b/native/cpp/CommonCpp/CryptoTools/CryptoModule.cpp --- a/native/cpp/CommonCpp/CryptoTools/CryptoModule.cpp +++ b/native/cpp/CommonCpp/CryptoTools/CryptoModule.cpp @@ -1,10 +1,5 @@ #include "CryptoModule.h" -#include "Base64.h" #include "Logger.h" -#include "OlmUtils.h" -#include "PlatformSpecificTools.h" -#include "olm/account.hh" -#include "olm/session.hh" #include #include @@ -26,96 +21,50 @@ const std::string SESSION_DOES_NOT_EXIST_ERROR{"SESSION_DOES_NOT_EXIST"}; const std::string INVALID_SESSION_VERSION_ERROR{"INVALID_SESSION_VERSION"}; -CryptoModule::CryptoModule() { - this->createAccount(); -} - -CryptoModule::CryptoModule(std::string secretKey, Persist persist) { - if (persist.isEmpty()) { - this->createAccount(); - } else { +CryptoModule::CryptoModule(std::string secretKey, Persist persist) + : vodozemacAccount(::account_new()) { + this->secretKey = secretKey; + if (!persist.isEmpty()) { this->restoreFromB64(secretKey, persist); } } -OlmAccount *CryptoModule::getOlmAccount() { - return reinterpret_cast(this->accountBuffer.data()); -} - -void CryptoModule::createAccount() { - this->accountBuffer.resize(::olm_account_size()); - ::olm_account(this->accountBuffer.data()); - - size_t randomSize = ::olm_create_account_random_length(this->getOlmAccount()); - OlmBuffer randomBuffer; - PlatformSpecificTools::generateSecureRandomBytes(randomBuffer, randomSize); - - if (-1 == - ::olm_create_account( - this->getOlmAccount(), randomBuffer.data(), randomSize)) { - throw std::runtime_error{ - "error createAccount => " + - std::string{::olm_account_last_error(this->getOlmAccount())}}; - }; -} - void CryptoModule::exposePublicIdentityKeys() { - size_t identityKeysSize = - ::olm_account_identity_keys_length(this->getOlmAccount()); - if (this->keys.identityKeys.size() == identityKeysSize) { + if (!this->keys.identityKeys.empty()) { return; } - this->keys.identityKeys.resize( - ::olm_account_identity_keys_length(this->getOlmAccount())); - if (-1 == - ::olm_account_identity_keys( - this->getOlmAccount(), - this->keys.identityKeys.data(), - this->keys.identityKeys.size())) { - throw std::runtime_error{ - "error generateIdentityKeys => " + - std::string{::olm_account_last_error(this->getOlmAccount())}}; - } + + std::string ed25519 = std::string(this->vodozemacAccount->ed25519_key()); + std::string curve25519 = + std::string(this->vodozemacAccount->curve25519_key()); + + // Format as JSON: {"ed25519":"...","curve25519":"..."} + folly::dynamic identityKeys = + folly::dynamic::object("ed25519", ed25519)("curve25519", curve25519); + + this->keys.identityKeys = folly::toJson(identityKeys); } void CryptoModule::generateOneTimeKeys(size_t oneTimeKeysAmount) { - size_t numRandomBytesRequired = - ::olm_account_generate_one_time_keys_random_length( - this->getOlmAccount(), oneTimeKeysAmount); - OlmBuffer random; - PlatformSpecificTools::generateSecureRandomBytes( - random, numRandomBytesRequired); - - if (-1 == - ::olm_account_generate_one_time_keys( - this->getOlmAccount(), - oneTimeKeysAmount, - random.data(), - random.size())) { - throw std::runtime_error{ - "error generateOneTimeKeys => " + - std::string{::olm_account_last_error(this->getOlmAccount())}}; - } + this->vodozemacAccount->generate_one_time_keys(oneTimeKeysAmount); } // returns number of published keys size_t CryptoModule::publishOneTimeKeys() { - this->keys.oneTimeKeys.resize( - ::olm_account_one_time_keys_length(this->getOlmAccount())); - if (-1 == - ::olm_account_one_time_keys( - this->getOlmAccount(), - this->keys.oneTimeKeys.data(), - this->keys.oneTimeKeys.size())) { - throw std::runtime_error{ - "error publishOneTimeKeys => " + - std::string{::olm_account_last_error(this->getOlmAccount())}}; + auto oneTimeKeysVec = this->vodozemacAccount->one_time_keys(); + + this->keys.oneTimeKeys.clear(); + for (const auto &key : oneTimeKeysVec) { + this->keys.oneTimeKeys.push_back(std::string(key)); } - return ::olm_account_mark_keys_as_published(this->getOlmAccount()); + + this->vodozemacAccount->mark_keys_as_published(); + + return oneTimeKeysVec.size(); } bool CryptoModule::prekeyExistsAndOlderThan(uint64_t threshold) { - // Our fork of Olm only remembers two prekeys at a time. + // Our fork of Vodozemac only remembers two prekeys at a time. // If the new one hasn't been published, then the old one is still active. // In that scenario, we need to avoid rotating the prekey because it will // result in the old active prekey being discarded. @@ -125,14 +74,14 @@ uint64_t currentTime = std::time(nullptr); uint64_t lastPrekeyPublishTime = - ::olm_account_get_last_prekey_publish_time(this->getOlmAccount()); + this->vodozemacAccount->last_prekey_publish_time(); return currentTime - lastPrekeyPublishTime >= threshold; } bool CryptoModule::prekeyDoesntExist() { - // Prekey generation when creating an Olm Account was added to clients - // after the initial launch of Olm. Because of that, there is a high + // Prekey generation when creating an Vodozemac Account was added to clients + // after the initial launch of Vodozemac. Because of that, there is a high // chance there are users who have the account without a generated prekey. // When prekey or signature is empty it contains bytes with only 0 values. @@ -149,18 +98,19 @@ } bool CryptoModule::isPrekeySignatureValid() { - const std::string signingPublicKey = - getSigningPublicKey(this->getIdentityKeys()); - const std::string prekey = parseOLMPrekey(this->getPrekey()); - const std::string preKeySignature = this->getPrekeySignature(); - - const OlmBuffer prekeyBytes = Base64::decode(prekey); try { - this->verifySignature(signingPublicKey, prekeyBytes, preKeySignature); + folly::dynamic identityKeysObj = folly::parseJson(this->getIdentityKeys()); + std::string signingPublicKey = identityKeysObj["ed25519"].asString(); + + std::string prekey = this->getPrekey(); + std::string prekeySignature = this->getPrekeySignature(); + + this->verifySignature(signingPublicKey, prekey, prekeySignature); return true; } catch (const std::exception &e) { std::string rawMessage{e.what()}; - if (rawMessage.find("BAD_MESSAGE_MAC") != std::string::npos) { + if (rawMessage.find("BAD_MESSAGE_MAC") != std::string::npos || + rawMessage.find("Signature") != std::string::npos) { return false; } @@ -171,147 +121,85 @@ } } -Keys CryptoModule::keysFromStrings( - const std::string &identityKeys, - const std::string &oneTimeKeys) { - return { - OlmBuffer(identityKeys.begin(), identityKeys.end()), - OlmBuffer(oneTimeKeys.begin(), oneTimeKeys.end())}; -} - std::string CryptoModule::getIdentityKeys() { this->exposePublicIdentityKeys(); - return std::string{ - this->keys.identityKeys.begin(), this->keys.identityKeys.end()}; + return this->keys.identityKeys; } -std::string +std::vector CryptoModule::getOneTimeKeysForPublishing(size_t oneTimeKeysAmount) { - OlmBuffer unpublishedOneTimeKeys; - unpublishedOneTimeKeys.resize( - ::olm_account_one_time_keys_length(this->getOlmAccount())); - if (-1 == - ::olm_account_one_time_keys( - this->getOlmAccount(), - unpublishedOneTimeKeys.data(), - unpublishedOneTimeKeys.size())) { - throw std::runtime_error{ - "error getOneTimeKeysForPublishing => " + - std::string{::olm_account_last_error(this->getOlmAccount())}}; - } - std::string unpublishedKeysString = - std::string{unpublishedOneTimeKeys.begin(), unpublishedOneTimeKeys.end()}; + try { + // Get current unpublished keys + auto oneTimeKeysVec = this->vodozemacAccount->one_time_keys(); + size_t numUnpublishedKeys = oneTimeKeysVec.size(); - folly::dynamic parsedUnpublishedKeys = - folly::parseJson(unpublishedKeysString); + // Generate more if needed + if (numUnpublishedKeys < oneTimeKeysAmount) { + this->generateOneTimeKeys(oneTimeKeysAmount - numUnpublishedKeys); + } - size_t numUnpublishedKeys = parsedUnpublishedKeys["curve25519"].size(); + this->publishOneTimeKeys(); - if (numUnpublishedKeys < oneTimeKeysAmount) { - this->generateOneTimeKeys(oneTimeKeysAmount - numUnpublishedKeys); + return this->keys.oneTimeKeys; + } catch (const std::exception &e) { + throw std::runtime_error( + "error getOneTimeKeysForPublishing => " + std::string(e.what())); } - - this->publishOneTimeKeys(); - - return std::string{ - this->keys.oneTimeKeys.begin(), this->keys.oneTimeKeys.end()}; -} - -std::uint8_t CryptoModule::getNumPrekeys() { - return reinterpret_cast(this->getOlmAccount())->num_prekeys; } std::string CryptoModule::getPrekey() { - OlmBuffer prekey; - prekey.resize(::olm_account_prekey_length(this->getOlmAccount())); - - if (-1 == - ::olm_account_prekey( - this->getOlmAccount(), prekey.data(), prekey.size())) { - throw std::runtime_error{ - "error getPrekey => " + - std::string{::olm_account_last_error(this->getOlmAccount())}}; + try { + return std::string(this->vodozemacAccount->prekey()); + } catch (const std::exception &e) { + throw std::runtime_error("error getPrekey => " + std::string(e.what())); } - - return std::string{std::string{prekey.begin(), prekey.end()}}; } std::string CryptoModule::getPrekeySignature() { - size_t signatureSize = ::olm_account_signature_length(this->getOlmAccount()); - - OlmBuffer signatureBuffer; - signatureBuffer.resize(signatureSize); - - if (-1 == - ::olm_account_prekey_signature( - this->getOlmAccount(), signatureBuffer.data())) { - throw std::runtime_error{ - "error getPrekeySignature => " + - std::string{::olm_account_last_error(this->getOlmAccount())}}; + try { + return std::string(this->vodozemacAccount->prekey_signature()); + } catch (const std::exception &e) { + throw std::runtime_error( + "error getPrekeySignature => " + std::string(e.what())); } - - return std::string{signatureBuffer.begin(), signatureBuffer.end()}; } std::optional CryptoModule::getUnpublishedPrekey() { - OlmBuffer prekey; - prekey.resize(::olm_account_prekey_length(this->getOlmAccount())); - - std::size_t retval = ::olm_account_unpublished_prekey( - this->getOlmAccount(), prekey.data(), prekey.size()); - - if (0 == retval) { - return std::nullopt; - } else if (-1 == retval) { - throw std::runtime_error{ - "error getUnpublishedPrekey => " + - std::string{::olm_account_last_error(this->getOlmAccount())}}; + try { + std::string unpublished = + std::string(this->vodozemacAccount->unpublished_prekey()); + if (unpublished.empty()) { + return std::nullopt; + } + return unpublished; + } catch (const std::exception &e) { + throw std::runtime_error( + "error getUnpublishedPrekey => " + std::string(e.what())); } - - return std::string{prekey.begin(), prekey.end()}; } std::string CryptoModule::generateAndGetPrekey() { - size_t prekeySize = - ::olm_account_generate_prekey_random_length(this->getOlmAccount()); - - OlmBuffer random; - PlatformSpecificTools::generateSecureRandomBytes(random, prekeySize); - - if (-1 == - ::olm_account_generate_prekey( - this->getOlmAccount(), random.data(), random.size())) { - throw std::runtime_error{ - "error generateAndGetPrekey => " + - std::string{::olm_account_last_error(this->getOlmAccount())}}; - } - - OlmBuffer prekey; - prekey.resize(::olm_account_prekey_length(this->getOlmAccount())); - - if (-1 == - ::olm_account_prekey( - this->getOlmAccount(), prekey.data(), prekey.size())) { - throw std::runtime_error{ - "error generateAndGetPrekey => " + - std::string{::olm_account_last_error(this->getOlmAccount())}}; + try { + this->vodozemacAccount->generate_prekey(); + return std::string(this->vodozemacAccount->prekey()); + } catch (const std::exception &e) { + throw std::runtime_error( + "error generateAndGetPrekey => " + std::string(e.what())); } - - return std::string{prekey.begin(), prekey.end()}; } void CryptoModule::markPrekeyAsPublished() { - ::olm_account_mark_prekey_as_published(this->getOlmAccount()); + this->vodozemacAccount->mark_prekey_as_published(); } void CryptoModule::forgetOldPrekey() { - ::olm_account_forget_old_prekey(this->getOlmAccount()); + this->vodozemacAccount->forget_old_prekey(); } -void CryptoModule::initializeInboundForReceivingSession( +std::string CryptoModule::initializeInboundForReceivingSession( const std::string &targetDeviceId, - const OlmBuffer &encryptedMessage, - const OlmBuffer &idKeys, + const crypto::EncryptedData &encryptedData, + const std::string &idKeys, int sessionVersion, const bool overwrite) { if (this->hasSessionFor(targetDeviceId)) { @@ -325,37 +213,39 @@ this->sessions.erase(this->sessions.find(targetDeviceId)); } - std::unique_ptr newSession = Session::createSessionAsResponder( - this->getOlmAccount(), - this->keys.identityKeys.data(), - encryptedMessage, - idKeys); + + auto [newSession, plaintext] = Session::createSessionAsResponder( + this->vodozemacAccount, encryptedData, idKeys); newSession->setVersion(sessionVersion); this->sessions.insert(make_pair(targetDeviceId, std::move(newSession))); + return plaintext; } int CryptoModule::initializeOutboundForSendingSession( const std::string &targetDeviceId, - const OlmBuffer &idKeys, - const OlmBuffer &preKeys, - const OlmBuffer &preKeySignature, - const std::optional &oneTimeKey) { + const std::string &idKeys, + const std::string &preKeys, + const std::string &preKeySignature, + const std::optional &oneTimeKey) { int newSessionVersion = 1; if (this->hasSessionFor(targetDeviceId)) { std::shared_ptr existingSession = getSessionByDeviceId(targetDeviceId); newSessionVersion = existingSession->getVersion() + 1; Logger::log( - "olm session overwritten for the device with id: " + targetDeviceId); + "session overwritten for the device with id: " + targetDeviceId); this->sessions.erase(this->sessions.find(targetDeviceId)); } + + // Use Olm compatibility mode = true for backward compatibility std::unique_ptr newSession = Session::createSessionAsInitializer( - this->getOlmAccount(), - this->keys.identityKeys.data(), + this->vodozemacAccount, idKeys, preKeys, preKeySignature, - oneTimeKey); + oneTimeKey, + true); // olmCompatibilityMode = true + newSession->setVersion(newSessionVersion); this->sessions.insert(make_pair(targetDeviceId, std::move(newSession))); return newSessionVersion; @@ -374,33 +264,28 @@ this->sessions.erase(deviceId); } -OlmBuffer CryptoModule::pickleAccount(const std::string &secretKey) { - OlmAccount *olmAccount = this->getOlmAccount(); - size_t accountPickleLength = ::olm_pickle_account_length(olmAccount); - OlmBuffer accountPickleBuffer(accountPickleLength); - - size_t result = ::olm_pickle_account( - olmAccount, - secretKey.data(), - secretKey.size(), - accountPickleBuffer.data(), - accountPickleLength); +std::string CryptoModule::pickleAccount(const std::string &secretKey) { + try { + // Convert secretKey to 32-byte array + std::array key; + std::copy_n( + secretKey.begin(), std::min(secretKey.size(), size_t(32)), key.begin()); - if (accountPickleLength != result) { - throw std::runtime_error( - "Error in pickleAccount => " + - std::string(::olm_account_last_error(olmAccount))); + return std::string(this->vodozemacAccount->pickle(key)); + } catch (const std::exception &e) { + throw std::runtime_error("error pickleAccount => " + std::string(e.what())); } - return accountPickleBuffer; } Persist CryptoModule::storeAsB64(const std::string &secretKey) { Persist persist; - persist.account = this->pickleAccount(secretKey); + std::string accountPickle = this->pickleAccount(secretKey); + persist.account = OlmBuffer(accountPickle.begin(), accountPickle.end()); std::unordered_map>::iterator it; for (it = this->sessions.begin(); it != this->sessions.end(); ++it) { - OlmBuffer buffer = it->second->storeAsB64(secretKey); + std::string sessionPickle = it->second->storeAsB64(secretKey); + OlmBuffer buffer(sessionPickle.begin(), sessionPickle.end()); SessionPersist sessionPersist{buffer, it->second->getVersion()}; persist.sessions.insert(make_pair(it->first, sessionPersist)); } @@ -409,33 +294,28 @@ } std::string CryptoModule::pickleAccountToString(const std::string &secretKey) { - OlmBuffer pickledAccount = this->pickleAccount(secretKey); - return std::string(pickledAccount.begin(), pickledAccount.end()); + return this->pickleAccount(secretKey); } void CryptoModule::restoreFromB64( const std::string &secretKey, Persist persist) { - this->accountBuffer.resize(::olm_account_size()); - ::olm_account(this->accountBuffer.data()); - if (-1 == - ::olm_unpickle_account( - this->getOlmAccount(), - secretKey.data(), - secretKey.size(), - persist.account.data(), - persist.account.size())) { - throw std::runtime_error{ - "error restoreFromB64 => " + - std::string{::olm_account_last_error(this->getOlmAccount())}}; - } - - std::unordered_map::iterator it; - for (it = persist.sessions.begin(); it != persist.sessions.end(); ++it) { - std::unique_ptr session = - session->restoreFromB64(secretKey, it->second.buffer); - session->setVersion(it->second.version); - this->sessions.insert(make_pair(it->first, move(session))); + try { + std::string accountPickle(persist.account.begin(), persist.account.end()); + this->vodozemacAccount = ::account_from_pickle(accountPickle, secretKey); + + std::unordered_map::iterator it; + for (it = persist.sessions.begin(); it != persist.sessions.end(); ++it) { + std::string sessionPickle( + it->second.buffer.begin(), it->second.buffer.end()); + std::unique_ptr session = + Session::restoreFromB64(secretKey, sessionPickle); + session->setVersion(it->second.version); + this->sessions.insert(make_pair(it->first, move(session))); + } + } catch (const std::exception &e) { + throw std::runtime_error( + "error restoreFromB64 => " + std::string(e.what())); } } @@ -463,40 +343,22 @@ } std::string CryptoModule::signMessage(const std::string &message) { - OlmBuffer signature; - signature.resize(::olm_account_signature_length(this->getOlmAccount())); - size_t signatureLength = ::olm_account_sign( - this->getOlmAccount(), - (uint8_t *)message.data(), - message.length(), - signature.data(), - signature.size()); - if (signatureLength == -1) { - throw std::runtime_error{ - "olm error: " + - std::string{::olm_account_last_error(this->getOlmAccount())}}; + try { + return std::string(this->vodozemacAccount->sign(message)); + } catch (const std::exception &e) { + throw std::runtime_error("error signMessage => " + std::string(e.what())); } - return std::string{(char *)signature.data(), signatureLength}; } void CryptoModule::verifySignature( const std::string &publicKey, - const OlmBuffer &message, + const std::string &message, const std::string &signature) { - OlmBuffer utilityBuffer; - utilityBuffer.resize(::olm_utility_size()); - OlmUtility *olmUtility = ::olm_utility(utilityBuffer.data()); - ssize_t verificationResult = ::olm_ed25519_verify( - olmUtility, - (uint8_t *)publicKey.data(), - publicKey.length(), - message.data(), - message.size(), - (uint8_t *)signature.data(), - signature.length()); - if (verificationResult == -1) { - throw std::runtime_error{ - "olm error: " + std::string{::olm_utility_last_error(olmUtility)}}; + try { + ::verify_ed25519_signature(publicKey, message, signature); + } catch (const std::exception &e) { + throw std::runtime_error( + "error verifySignature => " + std::string(e.what())); } } diff --git a/native/cpp/CommonCpp/CryptoTools/Session.h b/native/cpp/CommonCpp/CryptoTools/Session.h --- a/native/cpp/CommonCpp/CryptoTools/Session.h +++ b/native/cpp/CommonCpp/CryptoTools/Session.h @@ -6,8 +6,6 @@ #include "Tools.h" -#include "olm/olm.h" - #ifndef ANDROID #include "vodozemac_bindings.rs.h" #else @@ -18,26 +16,31 @@ namespace crypto { class Session { - OlmBuffer olmSessionBuffer; int version; public: + ::rust::Box<::VodozemacSession> vodozemacSession; + + // Constructor that takes a vodozemac session + Session(::rust::Box<::VodozemacSession> session) + : vodozemacSession(std::move(session)), version(0) { + } + static std::unique_ptr createSessionAsInitializer( - OlmAccount *account, - std::uint8_t *ownerIdentityKeys, - const OlmBuffer &idKeys, - const OlmBuffer &preKeys, - const OlmBuffer &preKeySignature, - const std::optional &oneTimeKey); - static std::unique_ptr createSessionAsResponder( - OlmAccount *account, - std::uint8_t *ownerIdentityKeys, - const OlmBuffer &encryptedMessage, - const OlmBuffer &idKeys); - OlmBuffer storeAsB64(const std::string &secretKey); + ::rust::Box<::VodozemacAccount> &account, + const std::string &idKeys, + const std::string &preKeys, + const std::string &preKeySignature, + const std::optional &oneTimeKey, + bool olmCompatibilityMode); + static std::pair, std::string> + createSessionAsResponder( + ::rust::Box<::VodozemacAccount> &account, + const crypto::EncryptedData &encryptedData, + const std::string &idKeys); + std::string storeAsB64(const std::string &secretKey); static std::unique_ptr - restoreFromB64(const std::string &secretKey, OlmBuffer &b64); - OlmSession *getOlmSession(); + restoreFromB64(const std::string &secretKey, const std::string &b64); std::string decrypt(EncryptedData &encryptedData); EncryptedData encrypt(const std::string &content); int getVersion(); diff --git a/native/cpp/CommonCpp/CryptoTools/Session.cpp b/native/cpp/CommonCpp/CryptoTools/Session.cpp --- a/native/cpp/CommonCpp/CryptoTools/Session.cpp +++ b/native/cpp/CommonCpp/CryptoTools/Session.cpp @@ -1,7 +1,7 @@ #include "Session.h" -#include "PlatformSpecificTools.h" -#include +#include +#include #include #ifndef ANDROID @@ -17,201 +17,113 @@ // lib/utils/olm-utils.js static const std::string olmErrorFlag = "OLM_ERROR"; -OlmSession *Session::getOlmSession() { - return reinterpret_cast(this->olmSessionBuffer.data()); -} - std::unique_ptr Session::createSessionAsInitializer( - OlmAccount *account, - std::uint8_t *ownerIdentityKeys, - const OlmBuffer &idKeys, - const OlmBuffer &preKeys, - const OlmBuffer &preKeySignature, - const std::optional &oneTimeKey) { - std::unique_ptr session(new Session()); - - session->olmSessionBuffer.resize(::olm_session_size()); - ::olm_session(session->olmSessionBuffer.data()); - - OlmBuffer randomBuffer; - PlatformSpecificTools::generateSecureRandomBytes( - randomBuffer, - ::olm_create_outbound_session_random_length(session->getOlmSession())); - - if (oneTimeKey) { - if (-1 == - ::olm_create_outbound_session( - session->getOlmSession(), - account, - idKeys.data() + ID_KEYS_PREFIX_OFFSET, - KEYSIZE, - idKeys.data() + SIGNING_KEYS_PREFIX_OFFSET, - KEYSIZE, - preKeys.data(), - KEYSIZE, - preKeySignature.data(), - SIGNATURESIZE, - oneTimeKey->data(), - KEYSIZE, - randomBuffer.data(), - randomBuffer.size())) { - throw std::runtime_error( - "error createOutbound => " + - std::string{::olm_session_last_error(session->getOlmSession())}); - } - return session; - } - if (-1 == - ::olm_create_outbound_session_without_otk( - session->getOlmSession(), - account, - idKeys.data() + ID_KEYS_PREFIX_OFFSET, - KEYSIZE, - idKeys.data() + SIGNING_KEYS_PREFIX_OFFSET, - KEYSIZE, - preKeys.data(), - KEYSIZE, - preKeySignature.data(), - SIGNATURESIZE, - randomBuffer.data(), - randomBuffer.size())) { + ::rust::Box<::VodozemacAccount> &account, + const std::string &idKeys, + const std::string &preKeys, + const std::string &preKeySignature, + const std::optional &oneTimeKey, + bool olmCompatibilityMode) { + + try { + // Parse identity keys JSON + folly::dynamic idKeysObj = folly::parseJson(idKeys); + std::string curve25519Key = idKeysObj["curve25519"].asString(); + std::string ed25519Key = idKeysObj["ed25519"].asString(); + + // Create outbound session + ::rust::Box<::VodozemacSession> vodozemacSession = + account->create_outbound_session( + curve25519Key, + ed25519Key, + oneTimeKey.value_or(""), + preKeys, + preKeySignature, + olmCompatibilityMode); + + return std::unique_ptr(new Session(std::move(vodozemacSession))); + } catch (const std::exception &e) { throw std::runtime_error( - "error createOutbound => " + - std::string{::olm_session_last_error(session->getOlmSession())}); + "error createOutbound => " + std::string(e.what())); } - return session; } -std::unique_ptr Session::createSessionAsResponder( - OlmAccount *account, - std::uint8_t *ownerIdentityKeys, - const OlmBuffer &encryptedMessage, - const OlmBuffer &idKeys) { - std::unique_ptr session(new Session()); - - OlmBuffer tmpEncryptedMessage(encryptedMessage); - session->olmSessionBuffer.resize(::olm_session_size()); - ::olm_session(session->olmSessionBuffer.data()); - if (-1 == - ::olm_create_inbound_session_from( - session->getOlmSession(), - account, - idKeys.data() + ID_KEYS_PREFIX_OFFSET, - KEYSIZE, - tmpEncryptedMessage.data(), - encryptedMessage.size())) { - throw std::runtime_error( - "error createInbound => " + - std::string{::olm_session_last_error(session->getOlmSession())}); - } +std::pair, std::string> +Session::createSessionAsResponder( + ::rust::Box<::VodozemacAccount> &account, + const crypto::EncryptedData &encryptedData, + const std::string &idKeys) { - if (-1 == ::olm_remove_one_time_keys(account, session->getOlmSession())) { - throw std::runtime_error( - "error createInbound (remove oneTimeKey) => " + - std::string{::olm_session_last_error(session->getOlmSession())}); + try { + // Parse identity keys JSON + folly::dynamic idKeysObj = folly::parseJson(idKeys); + std::string curve25519Key = idKeysObj["curve25519"].asString(); + + auto encryptResult = + ::encrypt_result_new(encryptedData.message, encryptedData.messageType); + auto result = + account->create_inbound_session(curve25519Key, *encryptResult); + + std::string plaintext = std::string(result->plaintext()); + + ::rust::Box<::VodozemacSession> vodozemacSession = result->take_session(); + + return std::make_pair( + std::unique_ptr(new Session(std::move(vodozemacSession))), + plaintext); + } catch (const std::exception &e) { + throw std::runtime_error("error createInbound => " + std::string(e.what())); } - return session; } -OlmBuffer Session::storeAsB64(const std::string &secretKey) { - size_t pickleLength = ::olm_pickle_session_length(this->getOlmSession()); - OlmBuffer pickle(pickleLength); - size_t res = ::olm_pickle_session( - this->getOlmSession(), - secretKey.data(), - secretKey.size(), - pickle.data(), - pickleLength); - if (pickleLength != res) { - throw std::runtime_error("error pickleSession => ::olm_pickle_session"); +std::string Session::storeAsB64(const std::string &secretKey) { + try { + // Convert secretKey to 32-byte array + std::array key; + std::copy_n( + secretKey.begin(), std::min(secretKey.size(), size_t(32)), key.begin()); + + return std::string(this->vodozemacSession->pickle(key)); + } catch (const std::exception &e) { + throw std::runtime_error("error pickleSession => " + std::string(e.what())); } - return pickle; } std::unique_ptr -Session::restoreFromB64(const std::string &secretKey, OlmBuffer &b64) { - std::unique_ptr session(new Session()); - - session->olmSessionBuffer.resize(::olm_session_size()); - ::olm_session(session->olmSessionBuffer.data()); - if (-1 == - ::olm_unpickle_session( - session->getOlmSession(), - secretKey.data(), - secretKey.size(), - b64.data(), - b64.size())) { - throw std::runtime_error("error pickleSession => ::olm_unpickle_session"); +Session::restoreFromB64(const std::string &secretKey, const std::string &b64) { + try { + ::rust::Box<::VodozemacSession> vodozemacSession = + ::session_from_pickle(b64, secretKey); + + return std::unique_ptr(new Session(std::move(vodozemacSession))); + } catch (const std::exception &e) { + throw std::runtime_error( + "error restoreFromB64 => " + std::string(e.what())); } - return session; } std::string Session::decrypt(EncryptedData &encryptedData) { - OlmSession *session = this->getOlmSession(); - - OlmBuffer utilityBuffer(::olm_utility_size()); - OlmUtility *olmUtility = ::olm_utility(utilityBuffer.data()); - - OlmBuffer messageHashBuffer(::olm_sha256_length(olmUtility)); - ::olm_sha256( - olmUtility, - encryptedData.message.data(), - encryptedData.message.size(), - messageHashBuffer.data(), - messageHashBuffer.size()); - - OlmBuffer tmpEncryptedMessage(encryptedData.message); - size_t maxSize = ::olm_decrypt_max_plaintext_length( - session, - encryptedData.messageType, - tmpEncryptedMessage.data(), - tmpEncryptedMessage.size()); - - if (maxSize == -1) { - throw std::runtime_error{ - "error decrypt_max_plaintext_length => " + - std::string{::olm_session_last_error(session)} + ". Hash: " + - std::string{messageHashBuffer.begin(), messageHashBuffer.end()}}; - } - - OlmBuffer decryptedMessage(maxSize); - size_t decryptedSize = ::olm_decrypt( - session, - encryptedData.messageType, - encryptedData.message.data(), - encryptedData.message.size(), - decryptedMessage.data(), - decryptedMessage.size()); - if (decryptedSize == -1) { - throw std::runtime_error{ - "error decrypt => " + olmErrorFlag + " " + - std::string{::olm_session_last_error(session)} + ". Hash: " + - std::string{messageHashBuffer.begin(), messageHashBuffer.end()}}; + try { + return std::string(this->vodozemacSession->decrypt( + encryptedData.message, + static_cast(encryptedData.messageType))); + } catch (const std::exception &e) { + throw std::runtime_error( + "error decrypt => " + olmErrorFlag + " " + std::string(e.what())); } - return std::string{(char *)decryptedMessage.data(), decryptedSize}; } EncryptedData Session::encrypt(const std::string &content) { - OlmSession *session = this->getOlmSession(); - OlmBuffer encryptedMessage( - ::olm_encrypt_message_length(session, content.size())); - OlmBuffer messageRandom; - PlatformSpecificTools::generateSecureRandomBytes( - messageRandom, ::olm_encrypt_random_length(session)); - size_t messageType = ::olm_encrypt_message_type(session); - if (-1 == - ::olm_encrypt( - session, - (uint8_t *)content.data(), - content.size(), - messageRandom.data(), - messageRandom.size(), - encryptedMessage.data(), - encryptedMessage.size())) { - throw std::runtime_error{ - "error encrypt => " + std::string{::olm_session_last_error(session)}}; + try { + auto result = this->vodozemacSession->encrypt(content); + + return EncryptedData{ + std::string(result->encrypted_message()), + result->message_type(), + this->getVersion()}; + } catch (const std::exception &e) { + throw std::runtime_error("error encrypt => " + std::string(e.what())); } - return {encryptedMessage, messageType, this->getVersion()}; } int Session::getVersion() { diff --git a/native/cpp/CommonCpp/CryptoTools/Tools.h b/native/cpp/CommonCpp/CryptoTools/Tools.h --- a/native/cpp/CommonCpp/CryptoTools/Tools.h +++ b/native/cpp/CommonCpp/CryptoTools/Tools.h @@ -18,12 +18,35 @@ typedef std::vector OlmBuffer; struct Keys { - OlmBuffer identityKeys; // size = 116 - OlmBuffer oneTimeKeys; // size = 43 each + std::string identityKeys; + std::vector oneTimeKeys; + + // Securely wipe keys from memory + void wipe() { + // Volatile pointer prevents compiler from optimizing away the zeroing + volatile char *p1 = const_cast(identityKeys.data()); + for (size_t i = 0; i < identityKeys.size(); ++i) { + p1[i] = 0; + } + identityKeys.clear(); + + for (auto &key : oneTimeKeys) { + volatile char *p2 = const_cast(key.data()); + for (size_t i = 0; i < key.size(); ++i) { + p2[i] = 0; + } + key.clear(); + } + oneTimeKeys.clear(); + } + + ~Keys() { + wipe(); + } }; struct EncryptedData { - OlmBuffer message; + std::string message; size_t messageType; std::optional sessionVersion; }; diff --git a/native/cpp/CommonCpp/NativeModules/CommCoreModule.cpp b/native/cpp/CommonCpp/NativeModules/CommCoreModule.cpp --- a/native/cpp/CommonCpp/NativeModules/CommCoreModule.cpp +++ b/native/cpp/CommonCpp/NativeModules/CommCoreModule.cpp @@ -7,7 +7,6 @@ #include "InternalModules/RustPromiseManager.h" #include "Logger.h" #include "NativeModuleUtils.h" -#include "OlmUtils.h" #include "TerminateApp.h" #include @@ -814,15 +813,27 @@ jsi::Object parseOneTimeKeysResult( jsi::Runtime &rt, - std::string contentOneTimeKeysBlob, - std::string notifOneTimeKeysBlob) { - auto contentOneTimeKeys = parseOLMOneTimeKeys(rt, contentOneTimeKeysBlob); - auto notifOneTimeKeys = parseOLMOneTimeKeys(rt, notifOneTimeKeysBlob); + std::vector contentOneTimeKeys, + std::vector notifOneTimeKeys) { + auto jsiContentOneTimeKeys = jsi::Array(rt, contentOneTimeKeys.size()); + size_t contentIdx = 0; + for (const auto &key : contentOneTimeKeys) { + jsiContentOneTimeKeys.setValueAtIndex( + rt, contentIdx++, jsi::String::createFromUtf8(rt, key)); + } + + auto jsiNotifOneTimeKeys = jsi::Array(rt, notifOneTimeKeys.size()); + size_t notifIdx = 0; + for (const auto &key : notifOneTimeKeys) { + jsiNotifOneTimeKeys.setValueAtIndex( + rt, notifIdx++, jsi::String::createFromUtf8(rt, key)); + } + auto jsiOneTimeKeysResult = jsi::Object(rt); jsiOneTimeKeysResult.setProperty( - rt, "contentOneTimeKeys", contentOneTimeKeys); + rt, "contentOneTimeKeys", jsiContentOneTimeKeys); jsiOneTimeKeysResult.setProperty( - rt, "notificationsOneTimeKeys", notifOneTimeKeys); + rt, "notificationsOneTimeKeys", jsiNotifOneTimeKeys); return jsiOneTimeKeysResult; } @@ -869,8 +880,8 @@ rt, [=](jsi::Runtime &innerRt, std::shared_ptr promise) { taskType job = [=, &innerRt]() { std::string error; - std::string contentResult; - std::string notifResult; + std::vector contentResult; + std::vector notifResult; if (this->contentCryptoModule == nullptr || !NotificationsCryptoModule::isNotificationsAccountInitialized()) { this->jsInvoker_->invokeAsync([=, &innerRt]() { @@ -1000,10 +1011,10 @@ auto currentID = RustPromiseManager::instance.addPromise( std::move(promiseInfo)); auto contentPrekeyToUploadRust = - rust::String(parseOLMPrekey(contentPrekeyToUpload)); + rust::String(contentPrekeyToUpload); auto prekeySignatureRust = rust::string(contentPrekeySignature); auto notifsPrekeyToUploadRust = - rust::String(parseOLMPrekey(notifsPrekeyToUpload)); + rust::String(notifsPrekeyToUpload); auto notificationsPrekeySignatureRust = rust::string(notifsPrekeySignature); ::identityRefreshUserPrekeys( @@ -1097,8 +1108,8 @@ notifPrekeySignature = notifsCryptoModuleWithPicklingKey.value() .first->getPrekeySignature(); - contentPrekey = parseOLMPrekey(contentPrekeyBlob.value()); - notifPrekey = parseOLMPrekey(notifPrekeyBlob.value()); + contentPrekey = contentPrekeyBlob.value(); + notifPrekey = notifPrekeyBlob.value(); } catch (const std::exception &e) { error = e.what(); } @@ -1163,21 +1174,14 @@ try { notifsCryptoModuleWithPicklingKey = NotificationsCryptoModule::fetchNotificationsAccount(); - std::optional oneTimeKeyBuffer; - if (oneTimeKeyCpp) { - oneTimeKeyBuffer = crypto::OlmBuffer( - oneTimeKeyCpp->begin(), oneTimeKeyCpp->end()); - } notifsCryptoModuleWithPicklingKey.value() .first->initializeOutboundForSendingSession( keyserverIDCpp, - std::vector( - identityKeysCpp.begin(), identityKeysCpp.end()), - std::vector(prekeyCpp.begin(), prekeyCpp.end()), - std::vector( - prekeySignatureCpp.begin(), prekeySignatureCpp.end()), - oneTimeKeyBuffer); + identityKeysCpp, + prekeyCpp, + prekeySignatureCpp, + oneTimeKeyCpp); result = notifsCryptoModuleWithPicklingKey.value().first->encrypt( keyserverIDCpp, @@ -1540,20 +1544,13 @@ crypto::EncryptedData initialEncryptedData; int sessionVersion; try { - std::optional oneTimeKeyBuffer; - if (oneTimeKeyCpp) { - oneTimeKeyBuffer = crypto::OlmBuffer( - oneTimeKeyCpp->begin(), oneTimeKeyCpp->end()); - } sessionVersion = this->contentCryptoModule->initializeOutboundForSendingSession( deviceIDCpp, - std::vector( - identityKeysCpp.begin(), identityKeysCpp.end()), - std::vector(prekeyCpp.begin(), prekeyCpp.end()), - std::vector( - prekeySignatureCpp.begin(), prekeySignatureCpp.end()), - oneTimeKeyBuffer); + identityKeysCpp, + prekeyCpp, + prekeySignatureCpp, + oneTimeKeyCpp); const std::string initMessage = "{\"type\": \"init\"}"; initialEncryptedData = @@ -1602,20 +1599,15 @@ std::string error; std::string decryptedMessage; try { - this->contentCryptoModule->initializeInboundForReceivingSession( - deviceIDCpp, - std::vector( - encryptedMessageCpp.begin(), encryptedMessageCpp.end()), - std::vector( - identityKeysCpp.begin(), identityKeysCpp.end()), - static_cast(sessionVersion), - overwrite); crypto::EncryptedData encryptedData{ - std::vector( - encryptedMessageCpp.begin(), encryptedMessageCpp.end()), - messageType}; + encryptedMessageCpp, messageType}; decryptedMessage = - this->contentCryptoModule->decrypt(deviceIDCpp, encryptedData); + this->contentCryptoModule->initializeInboundForReceivingSession( + deviceIDCpp, + encryptedData, + identityKeysCpp, + static_cast(sessionVersion), + overwrite); this->persistCryptoModules(true, std::nullopt); } catch (const std::exception &e) { error = e.what(); @@ -1697,20 +1689,13 @@ try { notifsCryptoModuleWithPicklingKey = NotificationsCryptoModule::fetchNotificationsAccount(); - std::optional oneTimeKeyBuffer; - if (oneTimeKeyCpp) { - oneTimeKeyBuffer = crypto::OlmBuffer( - oneTimeKeyCpp->begin(), oneTimeKeyCpp->end()); - } notifsCryptoModuleWithPicklingKey.value() .first->initializeOutboundForSendingSession( deviceIDCpp, - std::vector( - identityKeysCpp.begin(), identityKeysCpp.end()), - std::vector(prekeyCpp.begin(), prekeyCpp.end()), - std::vector( - prekeySignatureCpp.begin(), prekeySignatureCpp.end()), - oneTimeKeyBuffer); + identityKeysCpp, + prekeyCpp, + prekeySignatureCpp, + oneTimeKeyCpp); result = notifsCryptoModuleWithPicklingKey.value().first->encrypt( deviceIDCpp, @@ -1904,9 +1889,7 @@ std::string decryptedMessage; try { crypto::EncryptedData encryptedData{ - std::vector(message.begin(), message.end()), - messageType, - sessionVersion}; + message, messageType, sessionVersion}; decryptedMessage = this->contentCryptoModule->decrypt(deviceIDCpp, encryptedData); this->persistCryptoModules(true, std::nullopt); @@ -1953,9 +1936,7 @@ std::string decryptedMessage; try { crypto::EncryptedData encryptedData{ - std::vector(message.begin(), message.end()), - messageType, - sessionVersion}; + message, messageType, sessionVersion}; decryptedMessage = this->contentCryptoModule->decrypt(deviceIDCpp, encryptedData); @@ -2086,10 +2067,8 @@ taskType job = [=, &innerRt]() { std::string error; try { - crypto::OlmBuffer messageBuffer( - messageStr.begin(), messageStr.end()); crypto::CryptoModule::verifySignature( - keyStr, messageBuffer, signatureStr); + keyStr, messageStr, signatureStr); } catch (const std::exception &e) { error = "verifying signature failed with: " + std::string(e.what()); } diff --git a/native/cpp/CommonCpp/NativeModules/CommUtilsModule.h b/native/cpp/CommonCpp/NativeModules/CommUtilsModule.h --- a/native/cpp/CommonCpp/NativeModules/CommUtilsModule.h +++ b/native/cpp/CommonCpp/NativeModules/CommUtilsModule.h @@ -1,9 +1,7 @@ #pragma once -#include "../CryptoTools/Tools.h" #include "../Tools/WorkerThread.h" #include "../_generated/utilsJSI.h" -#include "olm/olm.h" #include #include #include @@ -12,15 +10,11 @@ namespace comm { namespace jsi = facebook::jsi; -using ::comm::crypto::OlmBuffer; class CommUtilsModule : public facebook::react::CommUtilsModuleSchemaCxxSpecJSI { std::unique_ptr utilsThread; - OlmBuffer olmUtilityBuffer; - ::OlmUtility *olmUtility; - virtual jsi::Value writeBufferToFile( jsi::Runtime &rt, jsi::String path, diff --git a/native/cpp/CommonCpp/NativeModules/CommUtilsModule.cpp b/native/cpp/CommonCpp/NativeModules/CommUtilsModule.cpp --- a/native/cpp/CommonCpp/NativeModules/CommUtilsModule.cpp +++ b/native/cpp/CommonCpp/NativeModules/CommUtilsModule.cpp @@ -1,6 +1,11 @@ #include "CommUtilsModule.h" #include "../Tools/Base64.h" -#include "olm/olm.h" + +#ifndef ANDROID +#include "vodozemac_bindings.rs.h" +#else +#include "lib.rs.h" +#endif #include #include @@ -12,9 +17,7 @@ CommUtilsModule::CommUtilsModule(std::shared_ptr jsInvoker) : CommUtilsModuleSchemaCxxSpecJSI(jsInvoker), - utilsThread(std::make_unique("utils")), - olmUtilityBuffer(::olm_utility_size()) { - this->olmUtility = ::olm_utility(olmUtilityBuffer.data()); + utilsThread(std::make_unique("utils")) { } jsi::Value CommUtilsModule::writeBufferToFile( @@ -135,19 +138,10 @@ auto inputPtr = arrayBuffer.data(rt); auto inputSize = arrayBuffer.size(rt); - auto sha256Size = ::olm_sha256_length(this->olmUtility); - OlmBuffer sha256Bytes(sha256Size); - auto outputLength = ::olm_sha256( - this->olmUtility, inputPtr, inputSize, sha256Bytes.data(), sha256Size); - if (outputLength == std::size_t(-1)) { - throw jsi::JSError( - rt, - "olm error: " + - std::string{::olm_utility_last_error(this->olmUtility)}); - } + rust::Slice input{inputPtr, inputSize}; + auto sha256Result = ::sha256(input); - std::string sha256String{sha256Bytes.begin(), sha256Bytes.end()}; - return jsi::String::createFromUtf8(rt, sha256String); + return jsi::String::createFromUtf8(rt, std::string(sha256Result)); } jsi::String CommUtilsModule::decodeUTF8ArrayBufferToString( diff --git a/native/cpp/CommonCpp/Notifications/BackgroundDataStorage/NotificationsCryptoModule.cpp b/native/cpp/CommonCpp/Notifications/BackgroundDataStorage/NotificationsCryptoModule.cpp --- a/native/cpp/CommonCpp/Notifications/BackgroundDataStorage/NotificationsCryptoModule.cpp +++ b/native/cpp/CommonCpp/Notifications/BackgroundDataStorage/NotificationsCryptoModule.cpp @@ -5,7 +5,6 @@ #include "../../Tools/CommSecureStore.h" #include "../../Tools/PlatformSpecificTools.h" #include "NotificationsInboundKeysProvider.h" -#include "olm/session.hh" #include "Logger.h" #include @@ -19,6 +18,10 @@ #include namespace comm { +// Message type constants (replacing Olm constants) +constexpr uint32_t MESSAGE_TYPE_PREKEY = 0; +constexpr uint32_t MESSAGE_TYPE_MESSAGE = 1; + const std::string NotificationsCryptoModule::secureStoreNotificationsAccountDataKey = "notificationsCryptoAccountDataKey"; @@ -166,9 +169,7 @@ std::string NotificationsCryptoModule::serializeNotificationsSession( std::shared_ptr session, std::string picklingKey) { - crypto::OlmBuffer pickledSessionBytes = session->storeAsB64(picklingKey); - std::string pickledSession = - std::string{pickledSessionBytes.begin(), pickledSessionBytes.end()}; + std::string pickledSession = session->storeAsB64(picklingKey); folly::dynamic serializedSessionJson = folly::dynamic::object( "session", pickledSession)("picklingKey", picklingKey); return folly::toJson(serializedSessionJson); @@ -187,11 +188,9 @@ } std::string pickledSession = serializedSessionJson["session"].asString(); - crypto::OlmBuffer pickledSessionBytes = - crypto::OlmBuffer{pickledSession.begin(), pickledSession.end()}; std::string picklingKey = serializedSessionJson["picklingKey"].asString(); std::unique_ptr session = - crypto::Session::restoreFromB64(picklingKey, pickledSessionBytes); + crypto::Session::restoreFromB64(picklingKey, pickledSession); return {std::move(session), picklingKey}; } @@ -548,8 +547,7 @@ std::string legacyNotificationsAccountPath = comm::PlatformSpecificTools::getNotificationsCryptoAccountPath(); - crypto::EncryptedData encryptedData{ - std::vector(data.begin(), data.end()), messageType}; + crypto::EncryptedData encryptedData{data, messageType}; auto cryptoModule = NotificationsCryptoModule::deserializeCryptoModule( legacyNotificationsAccountPath, picklingKey.value()); @@ -585,8 +583,7 @@ std::unique_ptr session = std::move(sessionWithPicklingKey.value().first); std::string picklingKey = sessionWithPicklingKey.value().second; - crypto::EncryptedData encryptedData{ - std::vector(data.begin(), data.end()), messageType}; + crypto::EncryptedData encryptedData{data, messageType}; std::string decryptedData = session->decrypt(encryptedData); NotificationsCryptoModule::persistNotificationsSessionInternal( @@ -629,8 +626,7 @@ std::unique_ptr session = std::move(sessionWithPicklingKey.value().first); std::string picklingKey = sessionWithPicklingKey.value().second; - crypto::EncryptedData encryptedData{ - std::vector(data.begin(), data.end()), messageType}; + crypto::EncryptedData encryptedData{data, messageType}; std::string decryptedData = session->decrypt(encryptedData); StatefulDecryptResult statefulDecryptResult( std::move(session), keyserverID, picklingKey, decryptedData); @@ -651,8 +647,8 @@ std::optional, std::string>> maybeAccountWithPicklingKey; - if (messageType != OLM_MESSAGE_TYPE_MESSAGE && - messageType != OLM_MESSAGE_TYPE_PRE_KEY) { + if (messageType != MESSAGE_TYPE_MESSAGE && + messageType != MESSAGE_TYPE_PREKEY) { throw std::runtime_error( "Received message of invalid type from device: " + deviceID); } else { @@ -665,32 +661,30 @@ } if (!maybeSessionWithPicklingKey.has_value() && - messageType == OLM_MESSAGE_TYPE_MESSAGE) { + messageType == MESSAGE_TYPE_MESSAGE) { throw std::runtime_error( "Received MESSAGE_TYPE_MESSAGE message from device: " + deviceID + " but session not initialized."); } - crypto::EncryptedData encryptedData{ - std::vector(data.begin(), data.end()), messageType}; + crypto::EncryptedData encryptedData{data, messageType}; bool isSenderChainEmpty = true; bool hasReceivedMessage = false; bool sessionExists = maybeSessionWithPicklingKey.has_value(); if (sessionExists) { - ::olm::Session *olmSessionAsCppClass = reinterpret_cast<::olm::Session *>( - maybeSessionWithPicklingKey.value().first->getOlmSession()); - isSenderChainEmpty = olmSessionAsCppClass->ratchet.sender_chain.empty(); - hasReceivedMessage = olmSessionAsCppClass->received_message; + isSenderChainEmpty = maybeSessionWithPicklingKey.value() + .first->vodozemacSession->is_sender_chain_empty(); + hasReceivedMessage = maybeSessionWithPicklingKey.value() + .first->vodozemacSession->has_received_message(); } // regular message - bool isRegularMessage = - sessionExists && messageType == OLM_MESSAGE_TYPE_MESSAGE; + bool isRegularMessage = sessionExists && messageType == MESSAGE_TYPE_MESSAGE; bool isRegularPrekeyMessage = sessionExists && - messageType == OLM_MESSAGE_TYPE_PRE_KEY && isSenderChainEmpty && + messageType == MESSAGE_TYPE_PREKEY && isSenderChainEmpty && hasReceivedMessage; if (isRegularMessage || isRegularPrekeyMessage) { @@ -728,17 +722,17 @@ } auto accountWithPicklingKey = maybeAccountWithPicklingKey.value(); - accountWithPicklingKey.first->initializeInboundForReceivingSession( - deviceID, - {data.begin(), data.end()}, - {notifInboundKeys.begin(), notifInboundKeys.end()}, - // The argument below is relevant for content only - 0, - true); + std::string decryptedData = + accountWithPicklingKey.first->initializeInboundForReceivingSession( + deviceID, + encryptedData, + {notifInboundKeys.begin(), notifInboundKeys.end()}, + // The argument below is relevant for content only + 0, + true); std::shared_ptr newInboundSession = accountWithPicklingKey.first->getSessionByDeviceId(deviceID); accountWithPicklingKey.first->removeSessionByDeviceId(deviceID); - std::string decryptedData = newInboundSession->decrypt(encryptedData); // session reset attempt or session initialization - handled the same bool sessionResetAttempt = diff --git a/native/cpp/CommonCpp/Tools/CMakeLists.txt b/native/cpp/CommonCpp/Tools/CMakeLists.txt --- a/native/cpp/CommonCpp/Tools/CMakeLists.txt +++ b/native/cpp/CommonCpp/Tools/CMakeLists.txt @@ -7,7 +7,6 @@ "Base64.h" "WorkerThread.h" "StaffUtils.h" - "OlmUtils.h" "StringUtils.h" ) @@ -16,7 +15,6 @@ "WorkerThread.cpp" "StaffUtils.cpp" "ServicesUtils.cpp" - "OlmUtils.cpp" "StringUtils.cpp" ) diff --git a/native/cpp/CommonCpp/Tools/OlmUtils.h b/native/cpp/CommonCpp/Tools/OlmUtils.h deleted file mode 100644 --- a/native/cpp/CommonCpp/Tools/OlmUtils.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include - -namespace comm { - -/** - * Parses a OLM prekey string from JSON format - * @param prekeyBlob JSON string with prekey - * @return The prekey string extracted from the JSON - * @throws std::runtime_error if parsing fails - */ -std::string parseOLMPrekey(std::string prekeyBlob); - -/** - * Extracts the ed25519 signing public key from an identity keys JSON blob - * @param identityKeysBlob JSON string with identity keys - * @return The ed25519 public key string - * @throws std::runtime_error if parsing fails - */ -std::string getSigningPublicKey(const std::string &identityKeysBlob); - -} // namespace comm diff --git a/native/cpp/CommonCpp/Tools/OlmUtils.cpp b/native/cpp/CommonCpp/Tools/OlmUtils.cpp deleted file mode 100644 --- a/native/cpp/CommonCpp/Tools/OlmUtils.cpp +++ /dev/null @@ -1,57 +0,0 @@ -#include "OlmUtils.h" -#include "Logger.h" - -#include -#include - -namespace comm { - -std::string parseOLMPrekey(std::string prekeyBlob) { - folly::dynamic parsedPrekey; - try { - parsedPrekey = folly::parseJson(prekeyBlob); - } catch (const folly::json::parse_error &e) { - std::string errorMessage{ - "parsing prekey failed with: " + std::string(e.what())}; - Logger::log(errorMessage); - throw std::runtime_error(errorMessage); - } - - folly::dynamic innerObject = parsedPrekey["curve25519"]; - if (!innerObject.isObject()) { - std::string errorMessage{"parsing prekey failed: inner object malformed"}; - Logger::log(errorMessage); - throw std::runtime_error(errorMessage); - } - - if (innerObject.values().begin() == innerObject.values().end()) { - std::string errorMessage{"parsing prekey failed: prekey missing"}; - Logger::log(errorMessage); - throw std::runtime_error(errorMessage); - } - - return parsedPrekey["curve25519"].values().begin()->asString(); -} - -std::string getSigningPublicKey(const std::string &identityKeysBlob) { - folly::dynamic parsedKeys; - try { - parsedKeys = folly::parseJson(identityKeysBlob); - } catch (const folly::json::parse_error &e) { - std::string errorMessage{ - "parsing identity keys failed with: " + std::string(e.what())}; - Logger::log(errorMessage); - throw std::runtime_error(errorMessage); - } - - if (!parsedKeys.count("ed25519") || !parsedKeys["ed25519"].isString()) { - std::string errorMessage{ - "parsing identity keys failed: ed25519 key missing or malformed"}; - Logger::log(errorMessage); - throw std::runtime_error(errorMessage); - } - - return parsedKeys["ed25519"].asString(); -} - -} // namespace comm diff --git a/native/crypto/olm-api.js b/native/crypto/olm-api.js --- a/native/crypto/olm-api.js +++ b/native/crypto/olm-api.js @@ -1,6 +1,5 @@ // @flow -import { getOneTimeKeyValues } from 'lib/shared/crypto-utils.js'; import { type AuthMetadata } from 'lib/shared/identity-client-context.js'; import { type OneTimeKeysResultValues, @@ -117,8 +116,8 @@ const { contentOneTimeKeys, notificationsOneTimeKeys } = await commCoreModule.getOneTimeKeys(numberOfKeys); return { - contentOneTimeKeys: getOneTimeKeyValues(contentOneTimeKeys), - notificationsOneTimeKeys: getOneTimeKeyValues(notificationsOneTimeKeys), + contentOneTimeKeys: contentOneTimeKeys, + notificationsOneTimeKeys: notificationsOneTimeKeys, }; }, async validateAndUploadPrekeys(authMetadata: AuthMetadata): Promise { diff --git a/native/identity-service/identity-service-context-provider.react.js b/native/identity-service/identity-service-context-provider.react.js --- a/native/identity-service/identity-service-context-provider.react.js +++ b/native/identity-service/identity-service-context-provider.react.js @@ -3,7 +3,6 @@ import * as React from 'react'; import { useInvalidCSATLogOut } from 'lib/actions/user-actions.js'; -import { getOneTimeKeyValues } from 'lib/shared/crypto-utils.js'; import { createAndSignSingletonDeviceList } from 'lib/shared/device-list-utils.js'; import { IdentityClientContext } from 'lib/shared/identity-client-context.js'; import type { @@ -456,8 +455,8 @@ prekeys.contentPrekeySignature, prekeys.notifPrekey, prekeys.notifPrekeySignature, - getOneTimeKeyValues(contentOneTimeKeys), - getOneTimeKeyValues(notificationsOneTimeKeys), + contentOneTimeKeys, + notificationsOneTimeKeys, fid ?? '', JSON.stringify(initialDeviceList), farcasterDCsToken ?? '', @@ -498,8 +497,8 @@ prekeys.contentPrekeySignature, prekeys.notifPrekey, prekeys.notifPrekeySignature, - getOneTimeKeyValues(contentOneTimeKeys), - getOneTimeKeyValues(notificationsOneTimeKeys), + contentOneTimeKeys, + notificationsOneTimeKeys, keyserverMessage, keyserverSignature, JSON.stringify(initialDeviceList), @@ -531,8 +530,8 @@ prekeys.contentPrekeySignature, prekeys.notifPrekey, prekeys.notifPrekeySignature, - getOneTimeKeyValues(contentOneTimeKeys), - getOneTimeKeyValues(notificationsOneTimeKeys), + contentOneTimeKeys, + notificationsOneTimeKeys, ); return await processAuthResult( @@ -570,8 +569,8 @@ prekeys.contentPrekeySignature, prekeys.notifPrekey, prekeys.notifPrekeySignature, - getOneTimeKeyValues(contentOneTimeKeys), - getOneTimeKeyValues(notificationsOneTimeKeys), + contentOneTimeKeys, + notificationsOneTimeKeys, fid ?? '', JSON.stringify(initialDeviceList), farcasterDCsToken ?? '', @@ -607,8 +606,8 @@ prekeys.contentPrekeySignature, prekeys.notifPrekey, prekeys.notifPrekeySignature, - getOneTimeKeyValues(contentOneTimeKeys), - getOneTimeKeyValues(notificationsOneTimeKeys), + contentOneTimeKeys, + notificationsOneTimeKeys, ); return await processAuthResult( @@ -643,8 +642,8 @@ prekeys.contentPrekeySignature, prekeys.notifPrekey, prekeys.notifPrekeySignature, - getOneTimeKeyValues(contentOneTimeKeys), - getOneTimeKeyValues(notificationsOneTimeKeys), + contentOneTimeKeys, + notificationsOneTimeKeys, JSON.stringify(deviceList), backupSecret, ); @@ -686,8 +685,8 @@ prekeys.contentPrekeySignature, prekeys.notifPrekey, prekeys.notifPrekeySignature, - getOneTimeKeyValues(contentOneTimeKeys), - getOneTimeKeyValues(notificationsOneTimeKeys), + contentOneTimeKeys, + notificationsOneTimeKeys, ); return await processAuthResult( diff --git a/native/ios/Comm.xcodeproj/project.pbxproj b/native/ios/Comm.xcodeproj/project.pbxproj --- a/native/ios/Comm.xcodeproj/project.pbxproj +++ b/native/ios/Comm.xcodeproj/project.pbxproj @@ -53,8 +53,6 @@ 7FBB2A7629E94539002C6493 /* utilsJSI-generated.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7FBB2A7529E94539002C6493 /* utilsJSI-generated.cpp */; }; 7FBB2A7829E945C2002C6493 /* CommUtilsModule.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7FBB2A7329E944FD002C6493 /* CommUtilsModule.cpp */; }; 7FBB2A7B29EEA2A4002C6493 /* Base64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7FBB2A7A29EEA2A4002C6493 /* Base64.cpp */; }; - 7FDFC0FE2DC0FEBD00B1D87F /* OlmUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7FDFC0FD2DC0FEBD00B1D87F /* OlmUtils.cpp */; }; - 7FDFC0FF2DC0FEBD00B1D87F /* OlmUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7FDFC0FD2DC0FEBD00B1D87F /* OlmUtils.cpp */; }; 7FDFC1002DC105E500B1D87F /* Base64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7FBB2A7A29EEA2A4002C6493 /* Base64.cpp */; }; 7FE179022E43980200973CAF /* ServicesUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7FE179012E43980200973CAF /* ServicesUtils.cpp */; }; 7FE179032E43980200973CAF /* ServicesUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7FE179012E43980200973CAF /* ServicesUtils.cpp */; }; @@ -262,8 +260,6 @@ 7FBB2A7A29EEA2A4002C6493 /* Base64.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Base64.cpp; sourceTree = ""; }; 7FCEA2DC2444010B004017B1 /* Comm-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Comm-Bridging-Header.h"; sourceTree = ""; }; 7FCFD8BD1E81B8DF00629B0E /* Comm.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = Comm.entitlements; path = Comm/Comm.entitlements; sourceTree = ""; }; - 7FDFC0FC2DC0FEBD00B1D87F /* OlmUtils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OlmUtils.h; sourceTree = ""; }; - 7FDFC0FD2DC0FEBD00B1D87F /* OlmUtils.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = OlmUtils.cpp; sourceTree = ""; }; 7FE179002E43980200973CAF /* ServicesUtils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ServicesUtils.h; sourceTree = ""; }; 7FE179012E43980200973CAF /* ServicesUtils.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ServicesUtils.cpp; sourceTree = ""; }; 7FE4D9F3291DFE9300667BF6 /* commJSI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = commJSI.h; sourceTree = ""; }; @@ -564,8 +560,6 @@ children = ( 7FE179002E43980200973CAF /* ServicesUtils.h */, 7FE179012E43980200973CAF /* ServicesUtils.cpp */, - 7FDFC0FC2DC0FEBD00B1D87F /* OlmUtils.h */, - 7FDFC0FD2DC0FEBD00B1D87F /* OlmUtils.cpp */, CBB0DF5E2B767FDF008E22FF /* CommMMKV.h */, DFD5E7802B05264F00C32B6A /* AESCrypto.h */, CBCA09052A8E0E6B00F75B3E /* StaffUtils.cpp */, @@ -1459,7 +1453,6 @@ CB74AB202B2B0C0A00CBB494 /* RustCSAMetadataEmitter.cpp in Sources */, CB38B48428771CAF00171182 /* EncryptedFileUtils.mm in Sources */, CBFE58292885852B003B94C9 /* ThreadOperations.cpp in Sources */, - 7FDFC0FF2DC0FEBD00B1D87F /* OlmUtils.cpp in Sources */, CB74AB1C2B2AFF6E00CBB494 /* CommServicesAuthMetadataEmitter.mm in Sources */, 8E3994552B039A7C00D5E950 /* UserStore.cpp in Sources */, CBFBEEBA2B4ED90600729F1D /* RustBackupExecutor.cpp in Sources */, @@ -1528,7 +1521,6 @@ files = ( CB99DB4E2C45329500B8055E /* NotificationsInboundKeysProvider.mm in Sources */, CBCF98502BA49A0500DBC3D9 /* CommIOSServicesClient.mm in Sources */, - 7FDFC0FE2DC0FEBD00B1D87F /* OlmUtils.cpp in Sources */, CBCA09072A8E0E7D00F75B3E /* StaffUtils.cpp in Sources */, CB3C0A3B2A125C8F009BD4DA /* NotificationsCryptoModule.cpp in Sources */, CB90951F29534B32002F2A7F /* CommSecureStore.mm in Sources */, diff --git a/native/native_rust_library/src/lib.rs b/native/native_rust_library/src/lib.rs --- a/native/native_rust_library/src/lib.rs +++ b/native/native_rust_library/src/lib.rs @@ -572,84 +572,81 @@ } // Vodozemac crypto functions + #[cfg(not(target_os = "ios"))] extern "Rust" { // NOTE: Keep in sync with Vodozemac crypto functions block // in native/vodozemac_bindings/src/lib.rs. // EncryptResult type - #[cfg(target_os = "android")] type EncryptResult; - #[cfg(target_os = "android")] + fn encrypt_result_new( encrypted_message: String, message_type: u32, ) -> Box; - #[cfg(target_os = "android")] + fn encrypted_message(self: &EncryptResult) -> String; - #[cfg(target_os = "android")] + fn message_type(self: &EncryptResult) -> u32; // VodozemacSession type - #[cfg(target_os = "android")] type VodozemacSession; - #[cfg(target_os = "android")] + fn pickle(self: &VodozemacSession, pickle_key: &[u8; 32]) -> String; - #[cfg(target_os = "android")] + fn encrypt( self: &mut VodozemacSession, plaintext: &str, ) -> Result>; - #[cfg(target_os = "android")] + fn decrypt( self: &mut VodozemacSession, encrypted_message: String, message_type: u32, ) -> Result; - #[cfg(target_os = "android")] + fn has_received_message(self: &VodozemacSession) -> bool; - #[cfg(target_os = "android")] + fn is_sender_chain_empty(self: &VodozemacSession) -> bool; - #[cfg(target_os = "android")] pub fn session_from_pickle( session_state: String, session_key: String, ) -> Result>; // VodozemacAccount type - #[cfg(target_os = "android")] type VodozemacAccount; - #[cfg(target_os = "android")] + fn pickle(self: &VodozemacAccount, pickle_key: &[u8; 32]) -> String; - #[cfg(target_os = "android")] + fn ed25519_key(self: &VodozemacAccount) -> String; - #[cfg(target_os = "android")] + fn curve25519_key(self: &VodozemacAccount) -> String; - #[cfg(target_os = "android")] + fn sign(self: &VodozemacAccount, message: &str) -> String; - #[cfg(target_os = "android")] + fn generate_one_time_keys(self: &mut VodozemacAccount, count: usize); - #[cfg(target_os = "android")] + fn one_time_keys(self: &VodozemacAccount) -> Vec; - #[cfg(target_os = "android")] + fn mark_keys_as_published(self: &mut VodozemacAccount); - #[cfg(target_os = "android")] + fn max_number_of_one_time_keys(self: &VodozemacAccount) -> usize; - #[cfg(target_os = "android")] + fn mark_prekey_as_published(self: &mut VodozemacAccount) -> bool; - #[cfg(target_os = "android")] + fn generate_prekey(self: &mut VodozemacAccount) -> bool; - #[cfg(target_os = "android")] + fn forget_old_prekey(self: &mut VodozemacAccount); - #[cfg(target_os = "android")] + fn last_prekey_publish_time(self: &mut VodozemacAccount) -> u64; - #[cfg(target_os = "android")] + fn prekey(self: &VodozemacAccount) -> String; - #[cfg(target_os = "android")] + fn unpublished_prekey(self: &VodozemacAccount) -> String; - #[cfg(target_os = "android")] + fn prekey_signature(self: &VodozemacAccount) -> String; - #[cfg(target_os = "android")] + fn create_outbound_session( self: &VodozemacAccount, identity_key: &str, @@ -659,38 +656,33 @@ pre_key_signature: &str, olm_compatibility_mode: bool, ) -> Result>; - #[cfg(target_os = "android")] + fn create_inbound_session( self: &mut VodozemacAccount, identity_key: &str, message: &EncryptResult, ) -> Result>; - #[cfg(target_os = "android")] pub fn account_new() -> Box; - #[cfg(target_os = "android")] pub fn account_from_pickle( account_state: String, session_key: String, ) -> Result>; - #[cfg(target_os = "android")] pub fn verify_ed25519_signature( public_key: &str, message: &str, signature: &str, ) -> Result<()>; - #[cfg(target_os = "android")] pub fn sha256(input: &[u8]) -> String; // InboundCreationResult type - #[cfg(target_os = "android")] type InboundCreationResult; - #[cfg(target_os = "android")] + fn plaintext(self: &InboundCreationResult) -> String; - #[cfg(target_os = "android")] + fn take_session(self: &mut InboundCreationResult) -> Box; } } diff --git a/native/schema/CommCoreModuleSchema.js b/native/schema/CommCoreModuleSchema.js --- a/native/schema/CommCoreModuleSchema.js +++ b/native/schema/CommCoreModuleSchema.js @@ -11,7 +11,7 @@ import type { ClientDBReportStoreOperation } from 'lib/ops/report-store-ops.js'; import type { ClientDBThreadStoreOperation } from 'lib/ops/thread-store-ops.js'; import type { - OneTimeKeysResult, + OneTimeKeysResultValues, SignedPrekeys, ClientPublicKeys, EncryptedData, @@ -58,7 +58,9 @@ ) => Promise; +initializeCryptoAccount: () => Promise; +getUserPublicKey: () => Promise; - +getOneTimeKeys: (oneTimeKeysAmount: number) => Promise; + +getOneTimeKeys: ( + oneTimeKeysAmount: number, + ) => Promise; +validateAndGetPrekeys: () => Promise; +validateAndUploadPrekeys: ( authUserID: string,