diff options
author | George Hazan <ghazan@miranda.im> | 2022-10-20 22:10:25 +0300 |
---|---|---|
committer | George Hazan <ghazan@miranda.im> | 2022-10-20 22:10:25 +0300 |
commit | 51037c1032b29ac1090cf4ecb78f3dce1bda554a (patch) | |
tree | 98e1e0fbc012520797849f2918382094451e4b3f /protocols | |
parent | d8a0f722b56a1933761a4148e33e564b3c05c0a4 (diff) |
WhatsApp: correct decoding of patches
Diffstat (limited to 'protocols')
-rw-r--r-- | protocols/WhatsApp/src/iq.cpp | 68 | ||||
-rw-r--r-- | protocols/WhatsApp/src/message.cpp | 2 | ||||
-rw-r--r-- | protocols/WhatsApp/src/proto.h | 22 | ||||
-rw-r--r-- | protocols/WhatsApp/src/server.cpp | 7 |
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) |