summaryrefslogtreecommitdiff
path: root/protocols/WhatsApp/src
diff options
context:
space:
mode:
authorGeorge Hazan <ghazan@miranda.im>2022-10-20 22:10:25 +0300
committerGeorge Hazan <ghazan@miranda.im>2022-10-20 22:10:25 +0300
commit51037c1032b29ac1090cf4ecb78f3dce1bda554a (patch)
tree98e1e0fbc012520797849f2918382094451e4b3f /protocols/WhatsApp/src
parentd8a0f722b56a1933761a4148e33e564b3c05c0a4 (diff)
WhatsApp: correct decoding of patches
Diffstat (limited to 'protocols/WhatsApp/src')
-rw-r--r--protocols/WhatsApp/src/iq.cpp68
-rw-r--r--protocols/WhatsApp/src/message.cpp2
-rw-r--r--protocols/WhatsApp/src/proto.h22
-rw-r--r--protocols/WhatsApp/src/server.cpp7
4 files changed, 76 insertions, 23 deletions
diff --git a/protocols/WhatsApp/src/iq.cpp b/protocols/WhatsApp/src/iq.cpp
index 46e10e3b6f..2f42dcf2a4 100644
--- a/protocols/WhatsApp/src/iq.cpp
+++ b/protocols/WhatsApp/src/iq.cpp
@@ -91,8 +91,13 @@ void WhatsAppProto::OnNotifyEncrypt(const WANode &node)
void WhatsAppProto::OnReceiveInfo(const WANode &node)
{
if (auto *pChild = node.getFirstChild()) {
- if (pChild->title == "offline")
+ if (pChild->title == "offline") {
debugLogA("Processed %d offline events", pChild->getAttrInt("count"));
+ if (m_arCollections.getCount() == 0) {
+ m_impl.m_resyncApp.Stop();
+ m_impl.m_resyncApp.Start(1000);
+ }
+ }
}
}
@@ -571,6 +576,17 @@ void WhatsAppProto::OnServerSync(const WANode &node)
ResyncServer(task);
}
+void WhatsAppProto::ResyncAll()
+{
+ OBJLIST<WACollection> task(1);
+ task.insert(new WACollection("regular", 11));
+ task.insert(new WACollection("regular_high", 9));
+ task.insert(new WACollection("regular_low", 11));
+ task.insert(new WACollection("critical_block", 11));
+ task.insert(new WACollection("critical_unblock_low", 11));
+ ResyncServer(task);
+}
+
void WhatsAppProto::ResyncServer(const OBJLIST<WACollection> &task)
{
WANodeIq iq(IQ::SET, "w:sync:app:state");
@@ -632,7 +648,7 @@ void WhatsAppProto::OnIqServerSync(const WANode &node)
pCollection->hash.assign(hash.c_str(), hash.size());
for (auto &it : snapshot.records())
- pCollection->parseRecord(it, true);
+ ParsePatch(pCollection, it, true);
}
}
@@ -644,7 +660,7 @@ void WhatsAppProto::OnIqServerSync(const WANode &node)
dwVersion = patch.version().version();
if (dwVersion > pCollection->version)
for (auto &jt : patch.mutations())
- pCollection->parseRecord(jt.record(), jt.operation() == proto::SyncdMutation_SyncdOperation::SyncdMutation_SyncdOperation_SET);
+ ParsePatch(pCollection, jt.record(), jt.operation() == proto::SyncdMutation_SyncdOperation::SyncdMutation_SyncdOperation_SET);
}
}
@@ -656,20 +672,56 @@ void WhatsAppProto::OnIqServerSync(const WANode &node)
jsonMap << CHAR_PARAM(ptrA(mir_base64_encode(it.first.c_str(), it.first.size())), ptrA(mir_base64_encode(it.second.c_str(), it.second.size())));
jsonRoot << INT_PARAM("version", dwVersion) << JSON_PARAM("indexValueMap", jsonMap);
- // string2file(jsonRoot.write(), GetTmpFileName("collection", CMStringA(pszName) + ".json"));
+ string2file(jsonRoot.write(), GetTmpFileName("collection", CMStringA(pszName) + ".json"));
}
}
-void WACollection::parseRecord(const ::proto::SyncdRecord &rec, bool bSet)
+static char sttMutationInfo[] = "WhatsApp Mutation Keys";
+
+void WhatsAppProto::ParsePatch(WACollection *pColl, const ::proto::SyncdRecord &rec, bool bSet)
{
- // auto &id = rec.keyid().id();
+ int id = decodeBigEndian(rec.keyid().id());
auto &index = rec.index().blob();
auto &value = rec.value().blob();
+ MBinBuffer key(getBlob(CMStringA(FORMAT, "AppSyncKey%d", id)));
+ if (!key.data()) {
+ debugLogA("No key with id=%d to decode a patch");
+ return;
+ }
+
+ struct
+ {
+ uint8_t indexKey[32];
+ uint8_t encKey[32];
+ uint8_t macKey[32];
+ uint8_t snapshotMacKey[32];
+ uint8_t patchMacKey[32];
+
+ } mutationKeys;
+
+ HKDF(EVP_sha256(), (BYTE *)"", 0, key.data(), key.length(), (BYTE *)sttMutationInfo, sizeof(sttMutationInfo) - 1, (BYTE*)&mutationKeys, sizeof(mutationKeys));
+
+ MBinBuffer decoded = aesDecrypt(EVP_aes_256_cbc(), mutationKeys.encKey, (uint8_t *)value.c_str(), value.c_str() + 16, value.size() - 32);
+ if (!decoded.data()) {
+ debugLogA("Unable to decode patch with key id=%d", id);
+ return;
+ }
+
+ proto::SyncActionData data;
+ data << decoded;
+
+ debugLogA("Applying patch for %s: %d -> %d", pColl->szName.get(), pColl->version, data.version());
+
if (bSet) {
- indexValueMap[index] = value.substr(0, value.size() - 32);
+ auto &patchIndex = data.index();
+ auto &patchVal = data.value();
+ debugLogA("Got patch: %s", patchVal.Utf8DebugString().c_str());
+ pColl->indexValueMap[index] = value.substr(0, value.size() - 32);
}
- else indexValueMap.erase(index);
+ else pColl->indexValueMap.erase(index);
+
+ pColl->version = data.version();
}
/////////////////////////////////////////////////////////////////////////////////////////
diff --git a/protocols/WhatsApp/src/message.cpp b/protocols/WhatsApp/src/message.cpp
index e3440a7207..806fbf1198 100644
--- a/protocols/WhatsApp/src/message.cpp
+++ b/protocols/WhatsApp/src/message.cpp
@@ -77,6 +77,8 @@ void WhatsAppProto::ProcessMessage(WAMSG type, const proto::WebMessageInfo &msg)
case proto::Message_ProtocolMessage_Type_HISTORY_SYNC_NOTIFICATION:
debugLogA("History sync notification");
+ m_impl.m_resyncApp.Stop();
+ m_impl.m_resyncApp.Start(10000);
break;
case proto::Message_ProtocolMessage_Type_REVOKE:
diff --git a/protocols/WhatsApp/src/proto.h b/protocols/WhatsApp/src/proto.h
index 475f564027..38d697f5ee 100644
--- a/protocols/WhatsApp/src/proto.h
+++ b/protocols/WhatsApp/src/proto.h
@@ -120,8 +120,6 @@ struct WACollection
MBinBuffer hash;
std::map<std::string, std::string> indexValueMap;
-
- void parseRecord(const proto::SyncdRecord &rec, bool bSet);
};
class WANoise
@@ -225,20 +223,26 @@ class WhatsAppProto : public PROTO<WhatsAppProto>
friend class WhatsAppProto;
WhatsAppProto &m_proto;
- CTimer m_keepAlive;
- void OnKeepAlive(CTimer *) {
- m_proto.SendKeepAlive();
+ CTimer m_keepAlive, m_resyncApp;
+ void OnKeepAlive(CTimer *)
+ { m_proto.SendKeepAlive();
+ }
+ void OnResync(CTimer *pTimer)
+ {
+ pTimer->Stop();
+ m_proto.ResyncAll();
}
CWhatsAppProtoImpl(WhatsAppProto &pro) :
m_proto(pro),
- m_keepAlive(Miranda_GetSystemWindow(), UINT_PTR(this))
+ m_keepAlive(Miranda_GetSystemWindow(), UINT_PTR(this)),
+ m_resyncApp(Miranda_GetSystemWindow(), UINT_PTR(this)+1)
{
m_keepAlive.OnEvent = Callback(this, &CWhatsAppProtoImpl::OnKeepAlive);
+ m_resyncApp.OnEvent = Callback(this, &CWhatsAppProtoImpl::OnResync);
}
} m_impl;
-
bool m_bTerminated, m_bRespawn;
ptrW m_tszDefaultGroup;
@@ -253,8 +257,10 @@ class WhatsAppProto : public PROTO<WhatsAppProto>
// App state management
OBJLIST<WACollection> m_arCollections;
- void InitCollections();
+ void InitCollections(void);
+ void ParsePatch(WACollection *pColl, const proto::SyncdRecord &rec, bool bSet);
void ResyncServer(const OBJLIST<WACollection> &task);
+ void ResyncAll(void);
__forceinline WACollection *FindCollection(const char *pszName)
{ return m_arCollections.find((WACollection *)&pszName);
diff --git a/protocols/WhatsApp/src/server.cpp b/protocols/WhatsApp/src/server.cpp
index b61b5d193a..f638f1a8b3 100644
--- a/protocols/WhatsApp/src/server.cpp
+++ b/protocols/WhatsApp/src/server.cpp
@@ -251,13 +251,6 @@ void WhatsAppProto::OnLoggedIn()
WSSendNode(
WANodeIq(IQ::GET, "privacy") << XCHILD("privacy"),
&WhatsAppProto::OnIqDoNothing);
-
- /*
- OBJLIST<WACollection> task(1);
- task.insert(new WACollection("regular_high", 9));
- task.insert(new WACollection("regular_low", 11));
- ResyncServer(task);
- */
}
void WhatsAppProto::OnLoggedOut(void)