Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F32081362
D15550.1765004424.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
65 KB
Referenced Files
None
Subscribers
None
D15550.1765004424.diff
View Options
diff --git a/lib/shared/crypto-utils.js b/lib/shared/crypto-utils.js
--- a/lib/shared/crypto-utils.js
+++ b/lib/shared/crypto-utils.js
@@ -110,7 +110,6 @@
}
export {
- getOneTimeKeyValues,
getOneTimeKeyValuesFromBlob,
getPrekeyValueFromBlob,
initialEncryptedMessageContent,
diff --git a/lib/types/crypto-types.js b/lib/types/crypto-types.js
--- a/lib/types/crypto-types.js
+++ b/lib/types/crypto-types.js
@@ -42,11 +42,6 @@
+curve25519: { +[string]: string },
};
-export type OneTimeKeysResult = {
- +contentOneTimeKeys: OLMOneTimeKeys,
- +notificationsOneTimeKeys: OLMOneTimeKeys,
-};
-
export type OneTimeKeysResultValues = {
+contentOneTimeKeys: $ReadOnlyArray<string>,
+notificationsOneTimeKeys: $ReadOnlyArray<string>,
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 <string>
#include <unordered_map>
-#include "olm/olm.h"
-
#include "Persist.h"
#include "Session.h"
#include "Tools.h"
@@ -21,14 +19,12 @@
class CryptoModule {
- OlmBuffer accountBuffer;
+ ::rust::Box<::VodozemacAccount> vodozemacAccount;
std::unordered_map<std::string, std::shared_ptr<Session>> sessions = {};
Keys keys;
- OlmAccount *getOlmAccount();
- void createAccount();
void exposePublicIdentityKeys();
void generateOneTimeKeys(size_t oneTimeKeysAmount);
std::string generateAndGetPrekey();
@@ -37,43 +33,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<std::string>
+ getOneTimeKeysForPublishing(size_t oneTimeKeysAmount = 10);
// Prekey rotation methods for X3DH
- std::uint8_t getNumPrekeys();
std::string getPrekey();
std::string getPrekeySignature();
std::optional<std::string> 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<OlmBuffer> &oneTimeKey);
+ const std::string &idKeys,
+ const std::string &preKeys,
+ const std::string &preKeySignature,
+ const std::optional<std::string> &oneTimeKey);
bool hasSessionFor(const std::string &targetDeviceId);
std::shared_ptr<Session> getSessionByDeviceId(const std::string &deviceId);
void removeSessionByDeviceId(const std::string &deviceId);
@@ -90,7 +81,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<std::string> 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,6 @@
#include "CryptoModule.h"
-#include "Base64.h"
+#include "../Tools/OlmUtils.h"
#include "Logger.h"
-#include "OlmUtils.h"
-#include "PlatformSpecificTools.h"
-#include "olm/account.hh"
-#include "olm/session.hh"
#include <folly/dynamic.h>
#include <folly/json.h>
@@ -26,96 +22,59 @@
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()) {
+ if (!persist.isEmpty()) {
this->restoreFromB64(secretKey, persist);
}
}
-OlmAccount *CryptoModule::getOlmAccount() {
- return reinterpret_cast<OlmAccount *>(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())}};
+ try {
+ this->vodozemacAccount->generate_one_time_keys(oneTimeKeysAmount);
+ } catch (const std::exception &e) {
+ throw std::runtime_error(
+ "error generateOneTimeKeys => " + std::string(e.what()));
}
}
// 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())}};
+ try {
+ auto oneTimeKeysVec = this->vodozemacAccount->one_time_keys();
+
+ this->keys.oneTimeKeys.clear();
+ for (const auto &key : oneTimeKeysVec) {
+ this->keys.oneTimeKeys.push_back(std::string(key));
+ }
+
+ this->vodozemacAccount->mark_keys_as_published();
+
+ return oneTimeKeysVec.size();
+ } catch (const std::exception &e) {
+ throw std::runtime_error(
+ "error publishOneTimeKeys => " + std::string(e.what()));
}
- return ::olm_account_mark_keys_as_published(this->getOlmAccount());
}
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 +84,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 +108,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);
+ const std::string signingPublicKey =
+ getSigningPublicKey(this->getIdentityKeys());
+
+ const std::string prekey = this->getPrekey();
+ const std::string prekeySignature = this->getPrekeySignature();
+
+ // Use the dedicated prekey verification function that decodes base64
+ ::verify_prekey_signature(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("The signature was invalid") != std::string::npos) {
return false;
}
@@ -171,147 +131,87 @@
}
}
-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<std::string>
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<olm::Account *>(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<std::string> 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());
+ // NOTE: We use an empty string to represent None because cxx doesn't
+ // support Option<&str> in FFI function signatures.
+ 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 +225,39 @@
this->sessions.erase(this->sessions.find(targetDeviceId));
}
- std::unique_ptr<Session> 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<OlmBuffer> &oneTimeKey) {
+ const std::string &idKeys,
+ const std::string &preKeys,
+ const std::string &preKeySignature,
+ const std::optional<std::string> &oneTimeKey) {
int newSessionVersion = 1;
if (this->hasSessionFor(targetDeviceId)) {
std::shared_ptr<Session> 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<Session> 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 +276,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<uint8_t, 32> 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<std::string, std::shared_ptr<Session>>::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 +306,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<std::string, SessionPersist>::iterator it;
- for (it = persist.sessions.begin(); it != persist.sessions.end(); ++it) {
- std::unique_ptr<Session> 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<std::string, SessionPersist>::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 =
+ 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 +355,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,30 +16,36 @@
namespace crypto {
class Session {
- OlmBuffer olmSessionBuffer;
int version;
+ ::rust::Box<::VodozemacSession> vodozemacSession;
public:
+ // Constructor that takes a vodozemac session
+ Session(::rust::Box<::VodozemacSession> session)
+ : vodozemacSession(std::move(session)), version(0) {
+ }
+
static std::unique_ptr<Session> createSessionAsInitializer(
- OlmAccount *account,
- std::uint8_t *ownerIdentityKeys,
- const OlmBuffer &idKeys,
- const OlmBuffer &preKeys,
- const OlmBuffer &preKeySignature,
- const std::optional<OlmBuffer> &oneTimeKey);
- static std::unique_ptr<Session> 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<std::string> &oneTimeKey,
+ bool olmCompatibilityMode);
+ static std::pair<std::unique_ptr<Session>, 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<Session>
- 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();
void setVersion(int newVersion);
+ bool isSenderChainEmpty() const;
+ bool hasReceivedMessage() const;
};
} // namespace crypto
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 <optional>
+#include <folly/dynamic.h>
+#include <folly/json.h>
#include <stdexcept>
#ifndef ANDROID
@@ -17,201 +17,122 @@
// lib/utils/olm-utils.js
static const std::string olmErrorFlag = "OLM_ERROR";
-OlmSession *Session::getOlmSession() {
- return reinterpret_cast<OlmSession *>(this->olmSessionBuffer.data());
-}
-
std::unique_ptr<Session> Session::createSessionAsInitializer(
- OlmAccount *account,
- std::uint8_t *ownerIdentityKeys,
- const OlmBuffer &idKeys,
- const OlmBuffer &preKeys,
- const OlmBuffer &preKeySignature,
- const std::optional<OlmBuffer> &oneTimeKey) {
- std::unique_ptr<Session> 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<std::string> &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,
+ // NOTE: We use an empty string to represent None because cxx
+ // doesn't support Option<&str> in FFI function signatures.
+ oneTimeKey.value_or(""),
+ preKeys,
+ preKeySignature,
+ olmCompatibilityMode);
+
+ return std::unique_ptr<Session>(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> Session::createSessionAsResponder(
- OlmAccount *account,
- std::uint8_t *ownerIdentityKeys,
- const OlmBuffer &encryptedMessage,
- const OlmBuffer &idKeys) {
- std::unique_ptr<Session> 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::unique_ptr<Session>, 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<Session>(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<uint8_t, 32> 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>
-Session::restoreFromB64(const std::string &secretKey, OlmBuffer &b64) {
- std::unique_ptr<Session> 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<Session>(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()}};
- }
+ try {
+ return std::string(this->vodozemacSession->decrypt(
+ encryptedData.message,
+ static_cast<uint32_t>(encryptedData.messageType)));
+ } catch (const std::exception &e) {
+ // Compute message hash for debugging
+ rust::Slice<const uint8_t> messageSlice{
+ reinterpret_cast<const uint8_t *>(encryptedData.message.data()),
+ encryptedData.message.size()};
+ std::string messageHash = std::string(::sha256(messageSlice));
- 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()}};
+ throw std::runtime_error(
+ "error decrypt => " + olmErrorFlag + " " + std::string(e.what()) +
+ ", message hash: " + messageHash);
}
- 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() {
@@ -222,5 +143,13 @@
this->version = newVersion;
}
+bool Session::isSenderChainEmpty() const {
+ return this->vodozemacSession->is_sender_chain_empty();
+}
+
+bool Session::hasReceivedMessage() const {
+ return this->vodozemacSession->has_received_message();
+}
+
} // namespace crypto
} // namespace comm
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<std::uint8_t> OlmBuffer;
struct Keys {
- OlmBuffer identityKeys; // size = 116
- OlmBuffer oneTimeKeys; // size = 43 each
+ std::string identityKeys;
+ std::vector<std::string> oneTimeKeys;
+
+ // Securely wipe keys from memory
+ void wipe() {
+ // Volatile pointer prevents compiler from optimizing away the zeroing
+ volatile char *p1 = const_cast<char *>(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<char *>(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<int> 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 <ReactCommon/TurboModuleUtils.h>
@@ -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<std::string> contentOneTimeKeys,
+ std::vector<std::string> 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> promise) {
taskType job = [=, &innerRt]() {
std::string error;
- std::string contentResult;
- std::string notifResult;
+ std::vector<std::string> contentResult;
+ std::vector<std::string> 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<crypto::OlmBuffer> oneTimeKeyBuffer;
- if (oneTimeKeyCpp) {
- oneTimeKeyBuffer = crypto::OlmBuffer(
- oneTimeKeyCpp->begin(), oneTimeKeyCpp->end());
- }
notifsCryptoModuleWithPicklingKey.value()
.first->initializeOutboundForSendingSession(
keyserverIDCpp,
- std::vector<uint8_t>(
- identityKeysCpp.begin(), identityKeysCpp.end()),
- std::vector<uint8_t>(prekeyCpp.begin(), prekeyCpp.end()),
- std::vector<uint8_t>(
- 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<crypto::OlmBuffer> oneTimeKeyBuffer;
- if (oneTimeKeyCpp) {
- oneTimeKeyBuffer = crypto::OlmBuffer(
- oneTimeKeyCpp->begin(), oneTimeKeyCpp->end());
- }
sessionVersion =
this->contentCryptoModule->initializeOutboundForSendingSession(
deviceIDCpp,
- std::vector<uint8_t>(
- identityKeysCpp.begin(), identityKeysCpp.end()),
- std::vector<uint8_t>(prekeyCpp.begin(), prekeyCpp.end()),
- std::vector<uint8_t>(
- 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<uint8_t>(
- encryptedMessageCpp.begin(), encryptedMessageCpp.end()),
- std::vector<uint8_t>(
- identityKeysCpp.begin(), identityKeysCpp.end()),
- static_cast<int>(sessionVersion),
- overwrite);
crypto::EncryptedData encryptedData{
- std::vector<uint8_t>(
- encryptedMessageCpp.begin(), encryptedMessageCpp.end()),
- messageType};
+ encryptedMessageCpp, messageType};
decryptedMessage =
- this->contentCryptoModule->decrypt(deviceIDCpp, encryptedData);
+ this->contentCryptoModule->initializeInboundForReceivingSession(
+ deviceIDCpp,
+ encryptedData,
+ identityKeysCpp,
+ static_cast<int>(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<crypto::OlmBuffer> oneTimeKeyBuffer;
- if (oneTimeKeyCpp) {
- oneTimeKeyBuffer = crypto::OlmBuffer(
- oneTimeKeyCpp->begin(), oneTimeKeyCpp->end());
- }
notifsCryptoModuleWithPicklingKey.value()
.first->initializeOutboundForSendingSession(
deviceIDCpp,
- std::vector<uint8_t>(
- identityKeysCpp.begin(), identityKeysCpp.end()),
- std::vector<uint8_t>(prekeyCpp.begin(), prekeyCpp.end()),
- std::vector<uint8_t>(
- 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<uint8_t>(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<uint8_t>(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 <ReactCommon/TurboModuleUtils.h>
#include <jsi/jsi.h>
#include <memory>
@@ -12,15 +10,11 @@
namespace comm {
namespace jsi = facebook::jsi;
-using ::comm::crypto::OlmBuffer;
class CommUtilsModule
: public facebook::react::CommUtilsModuleSchemaCxxSpecJSI {
std::unique_ptr<WorkerThread> 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 <ReactCommon/TurboModuleUtils.h>
#include <fstream>
@@ -12,9 +17,7 @@
CommUtilsModule::CommUtilsModule(std::shared_ptr<CallInvoker> jsInvoker)
: CommUtilsModuleSchemaCxxSpecJSI(jsInvoker),
- utilsThread(std::make_unique<WorkerThread>("utils")),
- olmUtilityBuffer(::olm_utility_size()) {
- this->olmUtility = ::olm_utility(olmUtilityBuffer.data());
+ utilsThread(std::make_unique<WorkerThread>("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<const uint8_t> 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 <fcntl.h>
@@ -19,6 +18,10 @@
#include <unordered_set>
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<crypto::Session> 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<crypto::Session> 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<uint8_t>(data.begin(), data.end()), messageType};
+ crypto::EncryptedData encryptedData{data, messageType};
auto cryptoModule = NotificationsCryptoModule::deserializeCryptoModule(
legacyNotificationsAccountPath, picklingKey.value());
@@ -585,8 +583,7 @@
std::unique_ptr<crypto::Session> session =
std::move(sessionWithPicklingKey.value().first);
std::string picklingKey = sessionWithPicklingKey.value().second;
- crypto::EncryptedData encryptedData{
- std::vector<uint8_t>(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<crypto::Session> session =
std::move(sessionWithPicklingKey.value().first);
std::string picklingKey = sessionWithPicklingKey.value().second;
- crypto::EncryptedData encryptedData{
- std::vector<uint8_t>(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::pair<std::shared_ptr<crypto::CryptoModule>, 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<uint8_t>(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->isSenderChainEmpty();
+ hasReceivedMessage =
+ maybeSessionWithPicklingKey.value().first->hasReceivedMessage();
}
// 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<crypto::Session> 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/OlmUtils.h b/native/cpp/CommonCpp/Tools/OlmUtils.h
--- a/native/cpp/CommonCpp/Tools/OlmUtils.h
+++ b/native/cpp/CommonCpp/Tools/OlmUtils.h
@@ -4,14 +4,6 @@
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
diff --git a/native/cpp/CommonCpp/Tools/OlmUtils.cpp b/native/cpp/CommonCpp/Tools/OlmUtils.cpp
--- a/native/cpp/CommonCpp/Tools/OlmUtils.cpp
+++ b/native/cpp/CommonCpp/Tools/OlmUtils.cpp
@@ -6,33 +6,6 @@
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 {
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<void> {
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/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<void>;
+initializeCryptoAccount: () => Promise<string>;
+getUserPublicKey: () => Promise<ClientPublicKeys>;
- +getOneTimeKeys: (oneTimeKeysAmount: number) => Promise<OneTimeKeysResult>;
+ +getOneTimeKeys: (
+ oneTimeKeysAmount: number,
+ ) => Promise<OneTimeKeysResultValues>;
+validateAndGetPrekeys: () => Promise<SignedPrekeys>;
+validateAndUploadPrekeys: (
authUserID: string,
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Dec 6, 7:00 AM (21 h, 12 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5829083
Default Alt Text
D15550.1765004424.diff (65 KB)
Attached To
Mode
D15550: [native] replace Olm with Vodozemac's Olm
Attached
Detach File
Event Timeline
Log In to Comment