summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGeorge Hazan <ghazan@miranda.im>2022-10-05 21:52:15 +0300
committerGeorge Hazan <ghazan@miranda.im>2022-10-05 21:52:15 +0300
commit92735a345ee6837a6c6b5941fd0d7cda7b7a8c2b (patch)
tree76ddc6cb543ced575b778169723ceac8ac66021d
parenteec4dbe75110ce06a44998ddaaa625d7e4378543 (diff)
WhatsApp:
- all signal storage data extracted to the separate class MSignalStore; - MSignalSession added to handle session ciphers - signal store callbacks registered - all libsignal related code moved to signal.cpp
-rw-r--r--libs/libsignal/src/signal.def1
-rw-r--r--protocols/WhatsAppWeb/WhatsAppWeb.vcxproj1
-rw-r--r--protocols/WhatsAppWeb/WhatsAppWeb.vcxproj.filters3
-rw-r--r--protocols/WhatsAppWeb/src/iq.cpp83
-rw-r--r--protocols/WhatsAppWeb/src/noise.cpp64
-rw-r--r--protocols/WhatsAppWeb/src/proto.cpp1
-rw-r--r--protocols/WhatsAppWeb/src/proto.h67
-rw-r--r--protocols/WhatsAppWeb/src/qrcode.cpp2
-rw-r--r--protocols/WhatsAppWeb/src/signal.cpp461
-rw-r--r--protocols/WhatsAppWeb/src/stdafx.h2
-rw-r--r--protocols/WhatsAppWeb/src/utils.cpp13
-rw-r--r--protocols/WhatsAppWeb/src/utils.h4
12 files changed, 594 insertions, 108 deletions
diff --git a/libs/libsignal/src/signal.def b/libs/libsignal/src/signal.def
index e3e90e1976..24fa4e6706 100644
--- a/libs/libsignal/src/signal.def
+++ b/libs/libsignal/src/signal.def
@@ -84,6 +84,7 @@ EXPORTS
session_cipher_decrypt_pre_key_signal_message
pre_key_signal_message_deserialize
pre_key_signal_message_get_pre_key_id
+ pre_key_signal_message_destroy
session_signed_pre_key_serialize
session_pre_key_serialize
session_pre_key_get_id
diff --git a/protocols/WhatsAppWeb/WhatsAppWeb.vcxproj b/protocols/WhatsAppWeb/WhatsAppWeb.vcxproj
index a10c683edb..50ae4808dd 100644
--- a/protocols/WhatsAppWeb/WhatsAppWeb.vcxproj
+++ b/protocols/WhatsAppWeb/WhatsAppWeb.vcxproj
@@ -70,6 +70,7 @@
<ClCompile Include="src\proto.cpp" />
<ClCompile Include="src\qrcode.cpp" />
<ClCompile Include="src\server.cpp" />
+ <ClCompile Include="src\signal.cpp" />
<ClCompile Include="src\stdafx.cxx">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
diff --git a/protocols/WhatsAppWeb/WhatsAppWeb.vcxproj.filters b/protocols/WhatsAppWeb/WhatsAppWeb.vcxproj.filters
index 4b97a898d3..31a86f3851 100644
--- a/protocols/WhatsAppWeb/WhatsAppWeb.vcxproj.filters
+++ b/protocols/WhatsAppWeb/WhatsAppWeb.vcxproj.filters
@@ -41,6 +41,9 @@
<ClCompile Include="src\iq.cpp">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="src\signal.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="src\db.h">
diff --git a/protocols/WhatsAppWeb/src/iq.cpp b/protocols/WhatsAppWeb/src/iq.cpp
index 9c32103016..07134c0cb8 100644
--- a/protocols/WhatsAppWeb/src/iq.cpp
+++ b/protocols/WhatsAppWeb/src/iq.cpp
@@ -53,7 +53,7 @@ void WhatsAppProto::OnIqCountPrekeys(const WANode &node)
iq.addChild("registration")->content.append(regId.c_str(), regId.size());
iq.addChild("type")->content.append(KEY_BUNDLE_TYPE, 1);
- iq.addChild("identity")->content.append(m_noise->signedIdentity.pub);
+ iq.addChild("identity")->content.append(m_signalStore.signedIdentity.pub);
auto *n = iq.addChild("list");
for (auto &keyId : ids) {
@@ -66,10 +66,10 @@ void WhatsAppProto::OnIqCountPrekeys(const WANode &node)
auto *skey = iq.addChild("skey");
- auto encId = encodeBigEndian(m_noise->preKey.keyid, 3);
+ auto encId = encodeBigEndian(m_signalStore.preKey.keyid, 3);
skey->addChild("id")->content.append(encId.c_str(), encId.size());
- skey->addChild("value")->content.append(m_noise->preKey.pub);
- skey->addChild("signature")->content.append(m_noise->preKey.signature);
+ skey->addChild("value")->content.append(m_signalStore.preKey.pub);
+ skey->addChild("signature")->content.append(m_signalStore.preKey.signature);
WSSendNode(iq, &WhatsAppProto::OnIqDoNothing);
}
@@ -110,7 +110,7 @@ void WhatsAppProto::OnReceiveMessage(const WANode &node)
}
else szChatId = msgFrom;
- type = WAMSG::Chat;
+ type = WAMSG::PrivateChat;
szAuthor = msgFrom;
}
else if (jid.isGroup()) {
@@ -142,7 +142,7 @@ void WhatsAppProto::OnReceiveMessage(const WANode &node)
return;
}
- CMStringA szSender = (type == WAMSG::Chat) ? szAuthor : szChatId;
+ CMStringA szSender = (type == WAMSG::PrivateChat) ? szAuthor : szChatId;
bool bFromMe = (m_szJid == msgFrom);
if (!bFromMe && participant)
bFromMe = m_szJid == participant;
@@ -160,16 +160,57 @@ void WhatsAppProto::OnReceiveMessage(const WANode &node)
if (bFromMe)
msg.set_status(proto::WebMessageInfo_Status_SERVER_ACK);
- if (auto *pEnc = node.getChild("enc")) {
- int iVer = pEnc->getAttrInt("v");
- auto *pszType = pEnc->getAttr("type");
- if (iVer == 2 && !mir_strcmp(pszType, "pkmsg")) {
- proto::Message msg;
- if (msg.ParseFromArray(pEnc->content.data(), (int)pEnc->content.length())) {
- int i = 0;
+ int iDecryptable = 0;
+
+ for (auto &it: node.getChildren()) {
+ if (it->title == "verified_name") {
+ proto::VerifiedNameCertificate cert;
+ cert << it->content;
+
+ proto::VerifiedNameCertificate::Details details;
+ details.ParseFromString(cert.details());
+
+ msg.set_verifiedbizname(details.verifiedname());
+ continue;
+ }
+
+ if (it->title != "enc" || it->content.length() == 0)
+ continue;
+
+ MBinBuffer msgBody;
+ auto *pszType = it->getAttr("type");
+ try {
+ if (!mir_strcmp(pszType, "pkmsg") || !mir_strcmp(pszType, "msg")) {
+ CMStringA szUser = (WAJid(szSender).isUser()) ? szSender : szAuthor;
+ msgBody = m_signalStore.decryptSignalProto(szUser, pszType, it->content);
}
+ else if (!mir_strcmp(pszType, "skmsg")) {
+ msgBody = m_signalStore.decryptGroupSignalProto(szSender, szAuthor, it->content);
+ }
+ else throw "Invalid e2e type";
+
+ iDecryptable++;
+
+ proto::Message encMsg;
+ encMsg << msgBody;
+ if (encMsg.devicesentmessage().has_message())
+ encMsg = encMsg.devicesentmessage().message();
+
+ if (encMsg.has_senderkeydistributionmessage())
+ m_signalStore.processSenderKeyMessage(encMsg.senderkeydistributionmessage());
+
+ msg.set_allocated_message(new proto::Message(encMsg));
+ }
+ catch (const char *pszError) {
+ debugLogA("Message cannot be parsed: %s", pszError);
+ msg.set_messagestubtype(proto::WebMessageInfo_StubType::WebMessageInfo_StubType_CIPHERTEXT);
}
- }
+
+ if (!iDecryptable) {
+ debugLogA("Nothing to decrypt");
+ msg.set_messagestubtype(proto::WebMessageInfo_StubType::WebMessageInfo_StubType_CIPHERTEXT);
+ }
+ }
}
/////////////////////////////////////////////////////////////////////////////////////////
@@ -294,7 +335,7 @@ void WhatsAppProto::OnIqPairSuccess(const WANode &node)
MBinBuffer buf;
buf.append("\x06\x00", 2);
buf.append(deviceDetails.c_str(), deviceDetails.size());
- buf.append(m_noise->signedIdentity.pub.data(), m_noise->signedIdentity.pub.length());
+ buf.append(m_signalStore.signedIdentity.pub);
ec_public_key key = {};
memcpy(key.data, accountSignatureKey.c_str(), sizeof(key.data));
@@ -306,12 +347,12 @@ void WhatsAppProto::OnIqPairSuccess(const WANode &node)
MBinBuffer buf;
buf.append("\x06\x01", 2);
buf.append(deviceDetails.c_str(), deviceDetails.size());
- buf.append(m_noise->signedIdentity.pub.data(), m_noise->signedIdentity.pub.length());
+ buf.append(m_signalStore.signedIdentity.pub);
buf.append(accountSignatureKey.c_str(), accountSignatureKey.size());
signal_buffer *result;
ec_private_key key = {};
- memcpy(key.data, m_noise->signedIdentity.priv.data(), m_noise->signedIdentity.priv.length());
+ memcpy(key.data, m_signalStore.signedIdentity.priv.data(), m_signalStore.signedIdentity.priv.length());
if (curve_calculate_signature(g_plugin.pCtx, &result, &key, (BYTE *)buf.data(), buf.length()) != 0)
throw "OnIqPairSuccess: cannot calculate account signature, exiting";
@@ -420,10 +461,10 @@ LBL_Error:
pPairingData->set_buildhash(appVersion, sizeof(appVersion));
pPairingData->set_eregid(encodeBigEndian(getDword(DBKEY_REG_ID)));
pPairingData->set_ekeytype(KEY_BUNDLE_TYPE);
- pPairingData->set_eident(m_noise->signedIdentity.pub.data(), m_noise->signedIdentity.pub.length());
- pPairingData->set_eskeyid(encodeBigEndian(m_noise->preKey.keyid));
- pPairingData->set_eskeyval(m_noise->preKey.pub.data(), m_noise->preKey.pub.length());
- pPairingData->set_eskeysig(m_noise->preKey.signature.data(), m_noise->preKey.signature.length());
+ pPairingData->set_eident(m_signalStore.signedIdentity.pub.data(), m_signalStore.signedIdentity.pub.length());
+ pPairingData->set_eskeyid(encodeBigEndian(m_signalStore.preKey.keyid));
+ pPairingData->set_eskeyval(m_signalStore.preKey.pub.data(), m_signalStore.preKey.pub.length());
+ pPairingData->set_eskeysig(m_signalStore.preKey.signature.data(), m_signalStore.preKey.signature.length());
node.set_allocated_devicepairingdata(pPairingData);
node.set_passive(false);
diff --git a/protocols/WhatsAppWeb/src/noise.cpp b/protocols/WhatsAppWeb/src/noise.cpp
index ecc7921b2d..4d9dc23956 100644
--- a/protocols/WhatsAppWeb/src/noise.cpp
+++ b/protocols/WhatsAppWeb/src/noise.cpp
@@ -65,72 +65,8 @@ void WANoise::init()
ec_key_pair_destroy(pKeys);
}
- if (ppro->getDword(DBKEY_PREKEY_KEYID, 0xFFFF) == 0xFFFF) {
- // generate pre keys
- const unsigned int signed_pre_key_id = 1;
- ppro->setDword(DBKEY_PREKEY_KEYID, 1);
- ppro->setDword(DBKEY_PREKEY_NEXT_ID, 1);
- ppro->setDword(DBKEY_PREKEY_UPLOAD_ID, 1);
-
- // generate signed identity keys (private & public)
- ratchet_identity_key_pair *keyPair;
- signal_protocol_key_helper_generate_identity_key_pair(&keyPair, g_plugin.pCtx);
-
- auto *pPubKey = ratchet_identity_key_pair_get_public(keyPair);
- db_set_blob(0, ppro->m_szModuleName, DBKEY_SIGNED_IDENTITY_PUB, pPubKey->data, sizeof(pPubKey->data));
-
- auto *pPrivKey = ratchet_identity_key_pair_get_private(keyPair);
- db_set_blob(0, ppro->m_szModuleName, DBKEY_SIGNED_IDENTITY_PRIV, pPrivKey->data, sizeof(pPrivKey->data));
-
- session_signed_pre_key *signed_pre_key;
- signal_protocol_key_helper_generate_signed_pre_key(&signed_pre_key, keyPair, signed_pre_key_id, time(0), g_plugin.pCtx);
- SIGNAL_UNREF(keyPair);
-
- signal_buffer *serialized_signed_pre_key;
- session_signed_pre_key_serialize(&serialized_signed_pre_key, signed_pre_key);
-
- ec_key_pair *pKeys = session_signed_pre_key_get_key_pair(signed_pre_key);
- pPubKey = ec_key_pair_get_public(pKeys);
- db_set_blob(0, ppro->m_szModuleName, DBKEY_PREKEY_PUB, pPubKey->data, sizeof(pPubKey->data));
-
- pPrivKey = ec_key_pair_get_private(pKeys);
- db_set_blob(0, ppro->m_szModuleName, DBKEY_PREKEY_PRIV, pPrivKey->data, sizeof(pPrivKey->data));
-
- db_set_blob(0, ppro->m_szModuleName, DBKEY_PREKEY_SIGN, (void*)session_signed_pre_key_get_signature(signed_pre_key), (int)session_signed_pre_key_get_signature_len(signed_pre_key));
-
- // generate and save pre keys set
- CMStringA szSetting;
- signal_protocol_key_helper_pre_key_list_node *keys_root;
- signal_protocol_key_helper_generate_pre_keys(&keys_root, 1, 20, g_plugin.pCtx);
- for (auto *it = keys_root; it; it = signal_protocol_key_helper_key_list_next(it)) {
- session_pre_key *pre_key = signal_protocol_key_helper_key_list_element(it);
- uint32_t pre_key_id = session_pre_key_get_id(pre_key);
- {
- signal_buffer *serialized_pre_key;
- session_pre_key_serialize(&serialized_pre_key, pre_key);
- szSetting.Format("PreKey%d", pre_key_id);
- db_set_blob(0, ppro->m_szModuleName, szSetting, signal_buffer_data(serialized_pre_key), (unsigned int)signal_buffer_len(serialized_pre_key));
- SIGNAL_UNREF(serialized_pre_key);
- }
-
- ec_key_pair *pre_key_pair = session_pre_key_get_key_pair(pre_key);
- pPubKey = ec_key_pair_get_public(pre_key_pair);
- szSetting.Format("PreKey%dPublic", pre_key_id);
- db_set_blob(0, ppro->m_szModuleName, szSetting, pPubKey->data, sizeof(pPubKey->data));
- }
- signal_protocol_key_helper_key_list_free(keys_root);
- }
-
noiseKeys.pub = ppro->getBlob(DBKEY_NOISE_PUB);
noiseKeys.priv = ppro->getBlob(DBKEY_NOISE_PRIV);
-
- signedIdentity.pub = ppro->getBlob(DBKEY_SIGNED_IDENTITY_PUB);
- signedIdentity.priv = ppro->getBlob(DBKEY_SIGNED_IDENTITY_PRIV);
-
- preKey.pub = ppro->getBlob(DBKEY_PREKEY_PUB);
- preKey.priv = ppro->getBlob(DBKEY_PREKEY_PRIV);
- preKey.keyid = ppro->getDword(DBKEY_PREKEY_KEYID);
- preKey.signature = ppro->getBlob(DBKEY_PREKEY_SIGN);
}
void WANoise::finish()
diff --git a/protocols/WhatsAppWeb/src/proto.cpp b/protocols/WhatsAppWeb/src/proto.cpp
index 4ba2d8bf7f..3766f8f625 100644
--- a/protocols/WhatsAppWeb/src/proto.cpp
+++ b/protocols/WhatsAppWeb/src/proto.cpp
@@ -30,6 +30,7 @@ static int CompareUsers(const WAUser *p1, const WAUser *p2)
WhatsAppProto::WhatsAppProto(const char *proto_name, const wchar_t *username) :
PROTO<WhatsAppProto>(proto_name, username),
m_impl(*this),
+ m_signalStore(this, ""),
m_szJid(getMStringA(DBKEY_JID)),
m_tszDefaultGroup(getWStringA(DBKEY_DEF_GROUP)),
m_arUsers(10, CompareUsers),
diff --git a/protocols/WhatsAppWeb/src/proto.h b/protocols/WhatsAppWeb/src/proto.h
index 2ff8731eb9..cbd766ea1c 100644
--- a/protocols/WhatsAppWeb/src/proto.h
+++ b/protocols/WhatsAppWeb/src/proto.h
@@ -17,7 +17,7 @@ typedef void (WhatsAppProto:: *WA_PKT_HANDLER)(const WANode &node);
enum WAMSG
{
- Chat,
+ PrivateChat,
GroupChat,
DirectStatus,
OtherStatus,
@@ -113,12 +113,7 @@ class WANoise
struct {
MBinBuffer priv, pub;
- } noiseKeys, signedIdentity, ephemeral;
-
- struct {
- MBinBuffer priv, pub, signature;
- uint32_t keyid;
- } preKey;
+ } noiseKeys, ephemeral;
void deriveKey(const void *pData, size_t cbLen, MBinBuffer &write, MBinBuffer &read);
void mixIntoKey(const void *n, const void *p);
@@ -137,6 +132,59 @@ public:
MBinBuffer encodeFrame(const void *pData, size_t cbLen);
};
+class MSignalSession : public MZeroedObject
+{
+ friend class MSignalStore;
+ signal_protocol_address address;
+ session_cipher *cipher = nullptr;
+
+public:
+ CMStringA szName;
+
+ MSignalSession(const CMStringA &_1, int _2);
+ ~MSignalSession();
+
+ CMStringA getSetting(const MSignalStore*) const;
+
+ __forceinline session_cipher* getCipher(void) const { return cipher; }
+ __forceinline int getDeviceId() const { return address.device_id; }
+};
+
+class MSignalStore
+{
+ void init();
+
+ signal_protocol_store_context *m_pContext;
+
+public:
+ PROTO_INTERFACE *pProto;
+ const char *prefix;
+
+ OBJLIST<MSignalSession> arSessions;
+
+ struct
+ {
+ MBinBuffer priv, pub;
+ }
+ signedIdentity;
+
+ struct
+ {
+ MBinBuffer priv, pub, signature;
+ uint32_t keyid;
+ } preKey;
+
+ MSignalStore(PROTO_INTERFACE *_1, const char *_2);
+ ~MSignalStore();
+
+ MSignalSession *createSession(const CMStringA &szName, int deviceId);
+
+ MBinBuffer decryptSignalProto(const CMStringA &from, const char *pszType, const MBinBuffer &encrypted);
+ MBinBuffer decryptGroupSignalProto(const CMStringA &from, const CMStringA &author, const MBinBuffer &encrypted);
+
+ void processSenderKeyMessage(const proto::Message_SenderKeyDistributionMessage &msg);
+};
+
class WhatsAppProto : public PROTO<WhatsAppProto>
{
friend class WANoise;
@@ -169,8 +217,6 @@ class WhatsAppProto : public PROTO<WhatsAppProto>
EVP_PKEY *m_pKeys; // private & public keys
WANoise *m_noise;
- MBinBuffer getBlob(const char *pSetting);
-
// Contacts management /////////////////////////////////////////////////////////////////
mir_cs m_csUsers;
@@ -248,6 +294,9 @@ class WhatsAppProto : public PROTO<WhatsAppProto>
void OnStreamError(const WANode &node);
void OnSuccess(const WANode &node);
+ // Signal
+ MSignalStore m_signalStore;
+
// Binary packets
void ProcessBinaryPacket(const void *pData, size_t cbLen);
diff --git a/protocols/WhatsAppWeb/src/qrcode.cpp b/protocols/WhatsAppWeb/src/qrcode.cpp
index 9dd69f92e3..daea9d5380 100644
--- a/protocols/WhatsAppWeb/src/qrcode.cpp
+++ b/protocols/WhatsAppWeb/src/qrcode.cpp
@@ -116,7 +116,7 @@ bool WhatsAppProto::ShowQrCode(const CMStringA &ref)
MBinBuffer secret(getBlob(DBKEY_SECRET_KEY));
ptrA s1(mir_base64_encode(m_noise->noiseKeys.pub));
- ptrA s2(mir_base64_encode(m_noise->signedIdentity.pub));
+ ptrA s2(mir_base64_encode(m_signalStore.signedIdentity.pub));
ptrA s3(mir_base64_encode(secret));
CMStringA szQrData(FORMAT, "%s,%s,%s,%s", ref.c_str(), s1.get(), s2.get(), s3.get());
m_pQRDlg->SetData(szQrData);
diff --git a/protocols/WhatsAppWeb/src/signal.cpp b/protocols/WhatsAppWeb/src/signal.cpp
new file mode 100644
index 0000000000..9d744d0d69
--- /dev/null
+++ b/protocols/WhatsAppWeb/src/signal.cpp
@@ -0,0 +1,461 @@
+/*
+
+WhatsAppWeb plugin for Miranda NG
+Copyright © 2019-22 George Hazan
+
+*/
+
+#include "stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// MSignalStore members
+
+static int CompareSessions(const MSignalSession *p1, const MSignalSession *p2)
+{
+ if (int ret = mir_strcmp(p1->szName, p2->szName))
+ return ret;
+
+ return p1->getDeviceId() - p2->getDeviceId();
+}
+
+MSignalStore::MSignalStore(PROTO_INTERFACE *_1, const char *_2) :
+ pProto(_1),
+ prefix(_2),
+ arSessions(1, &CompareSessions)
+{
+ if (pProto->getDword(DBKEY_PREKEY_KEYID, 0xFFFF) == 0xFFFF) {
+ // generate pre keys
+ const unsigned int signed_pre_key_id = 1;
+ pProto->setDword(DBKEY_PREKEY_KEYID, 1);
+ pProto->setDword(DBKEY_PREKEY_NEXT_ID, 1);
+ pProto->setDword(DBKEY_PREKEY_UPLOAD_ID, 1);
+
+ // generate signed identity keys (private & public)
+ ratchet_identity_key_pair *keyPair;
+ signal_protocol_key_helper_generate_identity_key_pair(&keyPair, g_plugin.pCtx);
+
+ auto *pPubKey = ratchet_identity_key_pair_get_public(keyPair);
+ db_set_blob(0, pProto->m_szModuleName, DBKEY_SIGNED_IDENTITY_PUB, pPubKey->data, sizeof(pPubKey->data));
+
+ auto *pPrivKey = ratchet_identity_key_pair_get_private(keyPair);
+ db_set_blob(0, pProto->m_szModuleName, DBKEY_SIGNED_IDENTITY_PRIV, pPrivKey->data, sizeof(pPrivKey->data));
+
+ session_signed_pre_key *signed_pre_key;
+ signal_protocol_key_helper_generate_signed_pre_key(&signed_pre_key, keyPair, signed_pre_key_id, time(0), g_plugin.pCtx);
+ SIGNAL_UNREF(keyPair);
+
+ signal_buffer *serialized_signed_pre_key;
+ session_signed_pre_key_serialize(&serialized_signed_pre_key, signed_pre_key);
+
+ ec_key_pair *pKeys = session_signed_pre_key_get_key_pair(signed_pre_key);
+ pPubKey = ec_key_pair_get_public(pKeys);
+ db_set_blob(0, pProto->m_szModuleName, DBKEY_PREKEY_PUB, pPubKey->data, sizeof(pPubKey->data));
+
+ pPrivKey = ec_key_pair_get_private(pKeys);
+ db_set_blob(0, pProto->m_szModuleName, DBKEY_PREKEY_PRIV, pPrivKey->data, sizeof(pPrivKey->data));
+
+ db_set_blob(0, pProto->m_szModuleName, DBKEY_PREKEY_SIGN, (void *)session_signed_pre_key_get_signature(signed_pre_key), (int)session_signed_pre_key_get_signature_len(signed_pre_key));
+
+ // generate and save pre keys set
+ CMStringA szSetting;
+ signal_protocol_key_helper_pre_key_list_node *keys_root;
+ signal_protocol_key_helper_generate_pre_keys(&keys_root, 1, 20, g_plugin.pCtx);
+ for (auto *it = keys_root; it; it = signal_protocol_key_helper_key_list_next(it)) {
+ session_pre_key *pre_key = signal_protocol_key_helper_key_list_element(it);
+ uint32_t pre_key_id = session_pre_key_get_id(pre_key);
+ {
+ signal_buffer *serialized_pre_key;
+ session_pre_key_serialize(&serialized_pre_key, pre_key);
+ szSetting.Format("PreKey%d", pre_key_id);
+ db_set_blob(0, pProto->m_szModuleName, szSetting, signal_buffer_data(serialized_pre_key), (unsigned int)signal_buffer_len(serialized_pre_key));
+ SIGNAL_UNREF(serialized_pre_key);
+ }
+
+ ec_key_pair *pre_key_pair = session_pre_key_get_key_pair(pre_key);
+ pPubKey = ec_key_pair_get_public(pre_key_pair);
+ szSetting.Format("PreKey%dPublic", pre_key_id);
+ db_set_blob(0, pProto->m_szModuleName, szSetting, pPubKey->data, sizeof(pPubKey->data));
+ }
+ signal_protocol_key_helper_key_list_free(keys_root);
+ }
+
+ // read resident data from database
+ signedIdentity.pub = pProto->getBlob(DBKEY_SIGNED_IDENTITY_PUB);
+ signedIdentity.priv = pProto->getBlob(DBKEY_SIGNED_IDENTITY_PRIV);
+
+ preKey.pub = pProto->getBlob(DBKEY_PREKEY_PUB);
+ preKey.priv = pProto->getBlob(DBKEY_PREKEY_PRIV);
+ preKey.keyid = pProto->getDword(DBKEY_PREKEY_KEYID);
+ preKey.signature = pProto->getBlob(DBKEY_PREKEY_SIGN);
+
+ // context cretion
+ init();
+}
+
+MSignalStore::~MSignalStore()
+{
+ signal_protocol_store_context_destroy(m_pContext);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static int contains_session_func(const signal_protocol_address *address, void *user_data)
+{
+ auto *pStore = (MSignalStore *)user_data;
+
+ MSignalSession tmp(CMStringA(address->name, (int)address->name_len), address->device_id);
+ return pStore->arSessions.find(&tmp) == nullptr;
+}
+
+static int delete_all_sessions_func(const char *name, size_t name_len, void *user_data)
+{
+ auto *pStore = (MSignalStore *)user_data;
+
+ int count = 0;
+ auto &pList = pStore->arSessions;
+ CMStringA szName(name, (int)name_len);
+ for (auto &it : pList.rev_iter())
+ if (it->szName == szName) {
+ pStore->pProto->delSetting(it->getSetting(pStore));
+ pList.remove(pList.indexOf(&it));
+ count++;
+ }
+
+ return count;
+}
+
+int delete_session_func(const signal_protocol_address *address, void *user_data)
+{
+ auto *pStore = (MSignalStore *)user_data;
+ auto &pList = pStore->arSessions;
+
+ MSignalSession tmp(CMStringA(address->name, (int)address->name_len), address->device_id);
+ int idx = pList.getIndex(&tmp);
+ if (idx == -1)
+ return 0;
+
+ pStore->pProto->delSetting(tmp.getSetting(pStore));
+ pList.remove(idx);
+ return 0;
+}
+
+static int get_sub_device_sessions_func(signal_int_list **sessions, const char *name, size_t name_len, void *user_data)
+{
+ auto *pStore = (MSignalStore *)user_data;
+ CMStringA szName(name, (int)name_len);
+
+ signal_int_list *l = signal_int_list_alloc();
+ unsigned int array_size = 0;
+
+ for (auto &it : pStore->arSessions)
+ if (it->szName == szName) {
+ array_size++;
+ signal_int_list_push_back(l, it->getDeviceId());
+ }
+
+ *sessions = l;
+ return array_size;
+}
+
+static void destroy_func(void *)
+{}
+
+int load_session_func(signal_buffer **record, signal_buffer ** /*user_data_storage*/, const signal_protocol_address *address, void *user_data)
+{
+ auto *pStore = (MSignalStore *)user_data;
+
+ MSignalSession tmp(CMStringA(address->name, (int)address->name_len), address->device_id);
+
+ MBinBuffer blob(pStore->pProto->getBlob(tmp.getSetting(pStore)));
+ if (blob.data() == 0)
+ return 0;
+
+ *record = signal_buffer_create((uint8_t *)blob.data(), blob.length());
+ return 1;
+}
+
+static int store_session_func(const signal_protocol_address *address, uint8_t *record, size_t record_len, uint8_t * /*user_record*/, size_t /*user_record_len*/, void *user_data)
+{
+ auto *pStore = (MSignalStore *)user_data;
+
+ MSignalSession tmp(CMStringA(address->name, (int)address->name_len), address->device_id);
+ db_set_blob(0, pStore->pProto->m_szModuleName, tmp.getSetting(pStore), record, (unsigned int)record_len); //TODO: check return value
+ return 0;
+}
+
+static int contains_pre_key(uint32_t pre_key_id, void *user_data)
+{
+ auto *pStore = (MSignalStore *)user_data;
+
+ CMStringA szSetting(FORMAT, "%s_%d", "SignalPreKey", pre_key_id);
+ MBinBuffer blob(pStore->pProto->getBlob(szSetting));
+ return (blob.data() != 0);
+}
+
+static int load_pre_key(signal_buffer **record, uint32_t pre_key_id, void *user_data)
+{
+ auto *pStore = (MSignalStore *)user_data;
+
+ CMStringA szSetting(FORMAT, "%s_%d", "SignalPreKey", pre_key_id);
+ MBinBuffer blob(pStore->pProto->getBlob(szSetting));
+ if (blob.data() == 0)
+ return SG_ERR_INVALID_KEY_ID;
+
+ *record = signal_buffer_create((uint8_t *)blob.data(), blob.length());
+ return SG_SUCCESS; //key exists and succesfully loaded
+}
+
+static int remove_pre_key(uint32_t pre_key_id, void *user_data)
+{
+ auto *pStore = (MSignalStore *)user_data;
+
+ CMStringA szSetting(FORMAT, "%s_%d", "SignalPreKey", pre_key_id);
+ pStore->pProto->delSetting(szSetting);
+
+ szSetting.Format("PreKey%uPublic", pre_key_id);
+ pStore->pProto->delSetting(szSetting);
+
+ szSetting.Format("PreKey%uPrivate", pre_key_id);
+ pStore->pProto->delSetting(szSetting);
+ return 0;
+}
+
+static int store_pre_key(uint32_t pre_key_id, uint8_t *record, size_t record_len, void *user_data)
+{
+ auto *pStore = (MSignalStore *)user_data;
+
+ CMStringA szSetting(FORMAT, "%s_%d", "SignalPreKey", pre_key_id);
+ db_set_blob(0, pStore->pProto->m_szModuleName, szSetting, record, (unsigned int)record_len);
+
+ session_pre_key *prekey = nullptr;
+ session_pre_key_deserialize(&prekey, record, record_len, g_plugin.pCtx); //TODO: handle error
+ if (prekey) {
+ ec_key_pair *pre_key_pair = session_pre_key_get_key_pair(prekey);
+ signal_buffer *key_buf = nullptr;
+ ec_public_key *public_key = ec_key_pair_get_public(pre_key_pair);
+ ec_public_key_serialize(&key_buf, public_key);
+ SIGNAL_UNREF(public_key);
+
+ szSetting.Format("PreKey%uPublic", pre_key_id);
+ db_set_blob(0, pStore->pProto->m_szModuleName, szSetting, signal_buffer_data(key_buf), (int)signal_buffer_len(key_buf));
+ signal_buffer_free(key_buf);
+ }
+
+ return 0;
+}
+
+static int contains_signed_pre_key(uint32_t signed_pre_key_id, void *user_data)
+{
+ auto *pStore = (MSignalStore *)user_data;
+
+ CMStringA szSetting(FORMAT, "%s%d", "SignalSignedPreKey_", signed_pre_key_id);
+ DBVARIANT dbv = {};
+ dbv.type = DBVT_BLOB;
+ if (db_get(0, pStore->pProto->m_szModuleName, szSetting, &dbv))
+ return 0;
+
+ db_free(&dbv);
+ return 1;
+}
+
+static int load_signed_pre_key(signal_buffer **record, uint32_t signed_pre_key_id, void *user_data)
+{
+ auto *pStore = (MSignalStore *)user_data;
+
+ CMStringA szSetting(FORMAT, "%s%d", "SignalSignedPreKey_", signed_pre_key_id);
+ DBVARIANT dbv = {};
+ dbv.type = DBVT_BLOB;
+ if (db_get(0, pStore->pProto->m_szModuleName, szSetting, &dbv))
+ return SG_ERR_INVALID_KEY_ID;
+
+ *record = signal_buffer_create(dbv.pbVal, dbv.cpbVal);
+ db_free(&dbv);
+ return SG_SUCCESS; //key exist and succesfully loaded
+
+}
+
+static int store_signed_pre_key(uint32_t signed_pre_key_id, uint8_t *record, size_t record_len, void *user_data)
+{
+ auto *pStore = (MSignalStore *)user_data;
+
+ CMStringA szSetting(FORMAT, "%s%d", "SignalSignedPreKey_", signed_pre_key_id);
+ db_set_blob(0, pStore->pProto->m_szModuleName, szSetting, record, (unsigned int)record_len);
+ return 0;
+}
+
+static int remove_signed_pre_key(uint32_t signed_pre_key_id, void *user_data)
+{
+ auto *pStore = (MSignalStore *)user_data;
+
+ CMStringA szSetting(FORMAT, "%s%d", "SignalSignedPreKey_", signed_pre_key_id);
+ pStore->pProto->delSetting(szSetting);
+ return 0;
+}
+
+int get_identity_key_pair(signal_buffer **public_data, signal_buffer **private_data, void *user_data)
+{
+ auto *pStore = (MSignalStore *)user_data;
+
+ *public_data = signal_buffer_create((uint8_t *)pStore->preKey.pub.data(), (int)pStore->preKey.pub.length());
+ *private_data = signal_buffer_create((uint8_t *)pStore->preKey.priv.data(), (int)pStore->preKey.priv.length());
+ return 0;
+}
+
+int get_local_registration_id(void *user_data, uint32_t *registration_id)
+{
+ auto *pStore = (MSignalStore *)user_data;
+ *registration_id = pStore->pProto->getDword(DBKEY_REG_ID);
+ return 0;
+}
+
+int save_identity(const signal_protocol_address *address, uint8_t *key_data, size_t key_len, void *user_data)
+{
+ auto *pStore = (MSignalStore *)user_data;
+
+ CMStringA szSetting(FORMAT, "%s_%s_%d", "SignalIdentity", CMStringA(address->name, (int)address->name_len).c_str(), address->device_id);
+
+ if (key_data != nullptr)
+ db_set_blob(0, pStore->pProto->m_szModuleName, szSetting, key_data, (unsigned int)key_len); //TODO: check return value
+ else
+ pStore->pProto->delSetting(szSetting);
+ return 0;
+}
+
+int is_trusted_identity(const signal_protocol_address * /*address*/, uint8_t * /*key_data*/, size_t /*key_len*/, void * /*user_data*/)
+{
+ return 1;
+}
+
+void MSignalStore::init()
+{
+ signal_protocol_store_context_create(&m_pContext, g_plugin.pCtx);
+
+ signal_protocol_session_store ss;
+ ss.contains_session_func = &contains_session_func;
+ ss.delete_all_sessions_func = &delete_all_sessions_func;
+ ss.delete_session_func = &delete_session_func;
+ ss.destroy_func = &destroy_func;
+ ss.get_sub_device_sessions_func = &get_sub_device_sessions_func;
+ ss.load_session_func = &load_session_func;
+ ss.store_session_func = &store_session_func;
+ ss.user_data = this;
+ signal_protocol_store_context_set_session_store(m_pContext, &ss);
+
+ signal_protocol_pre_key_store sp;
+ sp.contains_pre_key = &contains_pre_key;
+ sp.destroy_func = &destroy_func;
+ sp.load_pre_key = &load_pre_key;
+ sp.remove_pre_key = &remove_pre_key;
+ sp.store_pre_key = &store_pre_key;
+ sp.user_data = this;
+ signal_protocol_store_context_set_pre_key_store(m_pContext, &sp);
+
+ signal_protocol_signed_pre_key_store ssp;
+ ssp.contains_signed_pre_key = &contains_signed_pre_key;
+ ssp.destroy_func = &destroy_func;
+ ssp.load_signed_pre_key = &load_signed_pre_key;
+ ssp.remove_signed_pre_key = &remove_signed_pre_key;
+ ssp.store_signed_pre_key = &store_signed_pre_key;
+ ssp.user_data = this;
+ signal_protocol_store_context_set_signed_pre_key_store(m_pContext, &ssp);
+
+ signal_protocol_identity_key_store sip;
+ sip.destroy_func = &destroy_func;
+ sip.get_identity_key_pair = &get_identity_key_pair;
+ sip.get_local_registration_id = &get_local_registration_id;
+ sip.is_trusted_identity = &is_trusted_identity;
+ sip.save_identity = &save_identity;
+ sip.user_data = this;
+ signal_protocol_store_context_set_identity_key_store(m_pContext, &sip);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// MSignalSession members
+
+MSignalSession::MSignalSession(const CMStringA &_1, int _2) :
+ szName(_1)
+{
+ address.name = szName.GetBuffer();
+ address.name_len = szName.GetLength();
+ address.device_id = _2;
+}
+
+MSignalSession::~MSignalSession()
+{
+ session_cipher_free(cipher);
+}
+
+CMStringA MSignalSession::getSetting(const MSignalStore *pStore) const
+{
+ return CMStringA(FORMAT, "%s%s_%s_%d",
+ (pStore) ? pStore->prefix : "", "SignalSession", szName.c_str(), getDeviceId());
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MSignalSession *MSignalStore::createSession(const CMStringA &szName, int deviceId)
+{
+ MSignalSession tmp(szName, deviceId);
+ auto *pSession = arSessions.find(&tmp);
+ if (pSession == nullptr) {
+ pSession = new MSignalSession(szName, deviceId);
+ arSessions.insert(pSession);
+ }
+
+ if (pSession->cipher == nullptr)
+ if (session_cipher_create(&pSession->cipher, m_pContext, &pSession->address, g_plugin.pCtx) < 0)
+ throw "session_cipher_create failure";
+
+ return pSession;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MBinBuffer MSignalStore::decryptSignalProto(const CMStringA &from, const char *pszType, const MBinBuffer &encrypted)
+{
+ WAJid jid(from);
+ auto *pSession = createSession(jid.user, 0);
+
+ signal_buffer *result = nullptr;
+ if (!mir_strcmp(pszType, "pkmsg")) {
+ ec_public_key pBaseKey, pIdentityKey;
+ memcpy(&pBaseKey.data, preKey.pub.data(), 32);
+ memcpy(&pIdentityKey.data, signedIdentity.pub.data(), 32);
+
+ pre_key_signal_message *pMsg;
+ if (pre_key_signal_message_deserialize(&pMsg, (BYTE *)encrypted.data(), encrypted.length(), g_plugin.pCtx) < 0)
+ throw "unable to deserialize prekey message";
+
+ if (session_cipher_decrypt_pre_key_signal_message(pSession->getCipher(), pMsg, 0, &result) < 0)
+ throw "unable to decrypt prekey message";
+
+ pre_key_signal_message_destroy((signal_type_base*)pMsg);
+ }
+ else {
+ signal_message *pMsg;
+ if (signal_message_deserialize(&pMsg, (BYTE *)encrypted.data(), encrypted.length(), g_plugin.pCtx) < 0)
+ throw "unable to deserialize signal message";
+
+ if (session_cipher_decrypt_signal_message(pSession->getCipher(), pMsg, 0, &result) < 0)
+ throw "unable to decrypt signal message";
+
+ pre_key_signal_message_destroy((signal_type_base *)pMsg);
+ }
+
+ MBinBuffer ret;
+ if (result != nullptr) {
+ ret.append(result->data, result->len);
+ signal_buffer_free(result);
+ }
+ return ret;
+}
+
+MBinBuffer MSignalStore::decryptGroupSignalProto(const CMStringA &from, const CMStringA &author, const MBinBuffer &encrypted)
+{
+ MBinBuffer ret;
+ return ret;
+}
+
+void MSignalStore::processSenderKeyMessage(const proto::Message_SenderKeyDistributionMessage &msg)
+{
+}
diff --git a/protocols/WhatsAppWeb/src/stdafx.h b/protocols/WhatsAppWeb/src/stdafx.h
index a463e5ea64..29a708c101 100644
--- a/protocols/WhatsAppWeb/src/stdafx.h
+++ b/protocols/WhatsAppWeb/src/stdafx.h
@@ -55,6 +55,8 @@ Copyright © 2019-22 George Hazan
#include "../../libs/libsignal/src/curve.h"
#include "../../libs/libsignal/src/hkdf.h"
#include "../../libs/libsignal/src/key_helper.h"
+#include "../../libs/libsignal/src/protocol.h"
+#include "../../libs/libsignal/src/session_cipher.h"
#include "../../libs/libsignal/src/signal_protocol.h"
#include "../../libs/libsodium/src/include/sodium.h"
diff --git a/protocols/WhatsAppWeb/src/utils.cpp b/protocols/WhatsAppWeb/src/utils.cpp
index 3f20216b13..fdf2721c17 100644
--- a/protocols/WhatsAppWeb/src/utils.cpp
+++ b/protocols/WhatsAppWeb/src/utils.cpp
@@ -133,19 +133,6 @@ CMStringA WhatsAppProto::generateMessageId()
}
/////////////////////////////////////////////////////////////////////////////////////////
-
-MBinBuffer WhatsAppProto::getBlob(const char *szSetting)
-{
- MBinBuffer buf;
- DBVARIANT dbv = { DBVT_BLOB };
- if (!db_get(0, m_szModuleName, szSetting, &dbv)) {
- buf.assign(dbv.pbVal, dbv.cpbVal);
- db_free(&dbv);
- }
- return buf;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
// sends a piece of JSON to a server via a websocket, masked
int WhatsAppProto::WSSend(const MessageLite &msg)
diff --git a/protocols/WhatsAppWeb/src/utils.h b/protocols/WhatsAppWeb/src/utils.h
index d3adfb3dc7..5428eb8188 100644
--- a/protocols/WhatsAppWeb/src/utils.h
+++ b/protocols/WhatsAppWeb/src/utils.h
@@ -169,6 +169,10 @@ struct WAJid
std::string encodeBigEndian(uint32_t num, size_t len = sizeof(uint32_t));
void generateIV(uint8_t *iv, int &pVar);
+__forceinline bool operator<<(MessageLite &msg, const MBinBuffer &buf)
+{ return msg.ParseFromArray(buf.data(), (int)buf.length());
+}
+
unsigned char* HKDF(const EVP_MD *evp_md,
const unsigned char *salt, size_t salt_len,
const unsigned char *key, size_t key_len,