From a8f17cd96a4d9d802f7f425ba4cb9283e262b9cd Mon Sep 17 00:00:00 2001 From: George Hazan Date: Fri, 3 Jun 2022 13:31:18 +0300 Subject: Jabber: better behaviour of OMEMO keys pudlishing --- protocols/JabberG/src/jabber_iqid.cpp | 94 +++++++++++++++---------- protocols/JabberG/src/jabber_omemo.cpp | 113 ++++++++++++------------------ protocols/JabberG/src/jabber_proto.h | 5 +- protocols/JabberG/src/jabber_userinfo.cpp | 89 +++++++++++------------ 4 files changed, 142 insertions(+), 159 deletions(-) (limited to 'protocols') diff --git a/protocols/JabberG/src/jabber_iqid.cpp b/protocols/JabberG/src/jabber_iqid.cpp index 4056dca118..9060fb3823 100644 --- a/protocols/JabberG/src/jabber_iqid.cpp +++ b/protocols/JabberG/src/jabber_iqid.cpp @@ -51,8 +51,14 @@ void CJabberProto::OnIqResultServerDiscoInfo(const TiXmlElement *iqNode, CJabber if (!mir_strcmp(tmp.category, "pubsub") && !mir_strcmp(tmp.type, "pep")) { m_bPepSupported = true; - if (m_bUseOMEMO) // publish ndes, precreation is not required - OmemoPublishNodes(); + if (m_bUseOMEMO) { + XmlNodeIq iq(AddIQ(&CJabberProto::OnIqResultGetOmemodevicelist, JABBER_IQ_TYPE_GET)); + iq << XATTR("from", m_ThreadInfo->fullJID); + iq << XCHILDNS("pubsub", "http://jabber.org/protocol/pubsub") + << XCHILD("items") << XATTR("node", JABBER_FEAT_OMEMO ".devicelist"); + + m_ThreadInfo->send(iq); + } EnableMenuItems(true); RebuildInfoFrame(); @@ -1072,51 +1078,63 @@ void CJabberProto::OnIqResultSetVcard(const TiXmlElement *iqNode, CJabberIqInfo* void CJabberProto::OnIqResultGetOmemodevicelist(const TiXmlElement* iqNode, CJabberIqInfo*) { - if (const char *from = XmlGetAttr(iqNode, "from")) - if (auto *pubsubNode = XmlGetChildByTag(iqNode, "pubsub", "xmlns", JABBER_FEAT_PUBSUB)) - if (auto *itemsNode = XmlGetChildByTag(pubsubNode, "items", "node", JABBER_FEAT_OMEMO ".devicelist")) - OmemoHandleDeviceList(from, itemsNode); + const char *from = XmlGetAttr(iqNode, "from"); //replies for our jid don't contain "from" + bool res = false; + if (auto *pubsubNode = XmlGetChildByTag(iqNode, "pubsub", "xmlns", JABBER_FEAT_PUBSUB)) + if (auto *itemsNode = XmlGetChildByTag(pubsubNode, "items", "node", JABBER_FEAT_OMEMO ".devicelist")) + res = OmemoHandleDeviceList(from, itemsNode); + + if (!from && !res) { + for (int i = 0;; i++) { + CMStringA szSetting(FORMAT, "%s%d", omemo::DevicePrefix, i); + if (!getDword(szSetting, 0)) + break; + + delSetting(szSetting); + } + + OmemoAnnounceDevice(false); //Publish own device if we can't retrieve up to date list + OmemoSendBundle(); + } } void CJabberProto::OnIqResultSetSearch(const TiXmlElement *iqNode, CJabberIqInfo*) { - const TiXmlElement *queryNode; - const char *type; - int id; - debugLogA(" iqIdGetSearch"); - if ((type = XmlGetAttr(iqNode, "type")) == nullptr) return; - if ((id = JabberGetPacketID(iqNode)) == -1) return; - if (!mir_strcmp(type, "result")) { - if ((queryNode = XmlFirstChild(iqNode, "query")) == nullptr) - return; - - PROTOSEARCHRESULT psr = {}; - psr.cbSize = sizeof(psr); - for (auto *itemNode : TiXmlFilter(queryNode, "item")) { - if (auto *jid = XmlGetAttr(itemNode, "jid")) { - psr.id.w = mir_utf8decodeW(jid); - debugLogA("Result jid = %s", jid); - if (auto *p = XmlGetChildText(itemNode, "nick")) - psr.nick.w = mir_utf8decodeW(p); - if (auto *p = XmlGetChildText(itemNode, "first")) - psr.firstName.w = mir_utf8decodeW(p); - if (auto *p = XmlGetChildText(itemNode, "last")) - psr.lastName.w = mir_utf8decodeW(p); - if (auto *p = XmlGetChildText(itemNode, "email")) - psr.email.w = mir_utf8decodeW(p); - ProtoBroadcastAck(0, ACKTYPE_SEARCH, ACKRESULT_DATA, (HANDLE)id, (LPARAM)&psr); + const char *type = XmlGetAttr(iqNode, "type"); + int id = JabberGetPacketID(iqNode); + if (type == nullptr || id == -1) + return; - replaceStrW(psr.id.w, 0); - replaceStrW(psr.nick.w, 0); - replaceStrW(psr.firstName.w, 0); - replaceStrW(psr.lastName.w, 0); - replaceStrW(psr.email.w, 0); + if (!mir_strcmp(type, "result")) { + if (auto *queryNode = XmlFirstChild(iqNode, "query")) { + PROTOSEARCHRESULT psr = {}; + psr.cbSize = sizeof(psr); + for (auto *itemNode : TiXmlFilter(queryNode, "item")) { + if (auto *jid = XmlGetAttr(itemNode, "jid")) { + psr.id.w = mir_utf8decodeW(jid); + debugLogA("Result jid = %s", jid); + if (auto *p = XmlGetChildText(itemNode, "nick")) + psr.nick.w = mir_utf8decodeW(p); + if (auto *p = XmlGetChildText(itemNode, "first")) + psr.firstName.w = mir_utf8decodeW(p); + if (auto *p = XmlGetChildText(itemNode, "last")) + psr.lastName.w = mir_utf8decodeW(p); + if (auto *p = XmlGetChildText(itemNode, "email")) + psr.email.w = mir_utf8decodeW(p); + ProtoBroadcastAck(0, ACKTYPE_SEARCH, ACKRESULT_DATA, (HANDLE)id, (LPARAM)&psr); + + replaceStrW(psr.id.w, 0); + replaceStrW(psr.nick.w, 0); + replaceStrW(psr.firstName.w, 0); + replaceStrW(psr.lastName.w, 0); + replaceStrW(psr.email.w, 0); + } } - } - ProtoBroadcastAck(0, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, (HANDLE)id); + ProtoBroadcastAck(0, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, (HANDLE)id); + } } else if (!mir_strcmp(type, "error")) ProtoBroadcastAck(0, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, (HANDLE)id); diff --git a/protocols/JabberG/src/jabber_omemo.cpp b/protocols/JabberG/src/jabber_omemo.cpp index 490d370ce0..9a31f43009 100644 --- a/protocols/JabberG/src/jabber_omemo.cpp +++ b/protocols/JabberG/src/jabber_omemo.cpp @@ -36,22 +36,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "..\..\libs\libsignal\src\session_cipher.h" #include "..\..\libs\libsignal\src\protocol.h" -namespace utils { - // code from http://stackoverflow.com/questions/3368883/how-does-this-size-of-array-template-function-work - template - struct type_of_size - { - typedef char type[N]; - }; - - template - typename type_of_size::type& sizeof_array_helper(T(&)[Size]); - - #define _countof_portable(pArray) sizeof(sizeof_array_helper(pArray)) -} - -using namespace utils; - namespace omemo { int random_func(uint8_t *data, size_t len, void * /*user_data*/) @@ -1650,39 +1634,36 @@ bool CJabberProto::OmemoHandleMessage(const TiXmlElement *node, const char *jid, return true; } -void CJabberProto::OmemoHandleDeviceList(const char *from, const TiXmlElement *node) +bool CJabberProto::OmemoHandleDeviceList(const char *from, const TiXmlElement *node) { if (!node) - return; - - MCONTACT hContact = HContactFromJID(from); + return false; + node = XmlFirstChild(node, "item"); //get node if (!node) { debugLogA("Jabber OMEMO: error: omemo devicelist does not have node"); - return; + return false; } node = XmlGetChildByTag(node, "list", "xmlns", JABBER_FEAT_OMEMO); // if (!node) { debugLogA("Jabber OMEMO: error: omemo devicelist does not have node"); - return; + return false; } CMStringA szSetting; - bool own_jid = false; - if (strstr(m_ThreadInfo->fullJID, from)) - own_jid = true; - - if (own_jid) { + if (from == nullptr) { //check if our device exist bool own_device_listed = false; uint32_t own_id = m_omemo.GetOwnDeviceId(); int i = 0; for (auto *list_item : TiXmlFilter(node, "device")) { uint32_t current_id = list_item->IntAttribute("id"); - if (current_id == own_id) + if (current_id == own_id) { own_device_listed = true; - szSetting.Format("OmemoDeviceId%d", i++); - setDword(szSetting, current_id); + } else { + szSetting.Format("OmemoDeviceId%d", i++); + setDword(szSetting, current_id); + } } uint32_t val = 0; @@ -1694,11 +1675,17 @@ void CJabberProto::OmemoHandleDeviceList(const char *from, const TiXmlElement *n szSetting.Format("OmemoDeviceId%d", i); val = getDword(szSetting, 0); } - if (!own_device_listed) - OmemoAnnounceDevice(); + if (!own_device_listed) { + OmemoAnnounceDevice(true); + OmemoSendBundle(); + } } else { // store device id's + MCONTACT hContact = HContactFromJID(from); + if (!hContact) + return true; //unknown jid + int i = 0; for (auto *list_item : TiXmlFilter(node, "device")) { uint32_t current_id = list_item->IntAttribute("id"); @@ -1715,45 +1702,39 @@ void CJabberProto::OmemoHandleDeviceList(const char *from, const TiXmlElement *n val = getDword(hContact, szSetting, 0); } } + + return true; } -void CJabberProto::OmemoAnnounceDevice() +void CJabberProto::OmemoAnnounceDevice(bool include_cache) { // check "OmemoDeviceId%d" for own id and send updated list if not exist unsigned int own_id = m_omemo.GetOwnDeviceId(); - CMStringA szSetting; - for (int i = 0;; ++i) { - szSetting.Format("OmemoDeviceId%d", i); - uint32_t val = getDword(szSetting); - if (val == 0) - break; - if (val == own_id) - return; // nothing to do, list is fresh enough - } - // add own device id // construct node char szBareJid[JABBER_MAX_JID_LEN]; XmlNodeIq iq("set", SerialNext()); - iq << XATTR("from", JabberStripJid(m_ThreadInfo->fullJID, szBareJid, _countof_portable(szBareJid))); + iq << XATTR("from", JabberStripJid(m_ThreadInfo->fullJID, szBareJid, _countof(szBareJid))); TiXmlElement *publish_node = iq << XCHILDNS("pubsub", "http://jabber.org/protocol/pubsub") << XCHILD("publish") << XATTR("node", JABBER_FEAT_OMEMO ".devicelist"); TiXmlElement *list_node = publish_node << XCHILD("item") << XATTR ("id", "current") << XCHILDNS("list", JABBER_FEAT_OMEMO); + if (include_cache) { //use cache only if it is fresh + CMStringA szSetting; + for (int i = 0; ; ++i) { + szSetting.Format("OmemoDeviceId%d", i); + uint32_t val = getDword(szSetting); + if (val == 0) + break; - for (int i = 0; ; ++i) { - szSetting.Format("OmemoDeviceId%d", i); - uint32_t val = getDword(szSetting); - if (val == 0) - break; - - list_node << XCHILD("device") << XATTRI64("id", val); + list_node << XCHILD("device") << XATTRI64("id", val); + } } list_node << XCHILD("device") << XATTRI64("id", own_id); // send device list back - //TODOL handle response + // TODO handle response m_ThreadInfo->send(iq); } @@ -1779,7 +1760,7 @@ void CJabberProto::OmemoSendBundle() // construct bundle node char szBareJid[JABBER_MAX_JID_LEN]; XmlNodeIq iq("set", SerialNext()); - iq << XATTR("from", JabberStripJid(m_ThreadInfo->fullJID, szBareJid, _countof_portable(szBareJid))); + iq << XATTR("from", JabberStripJid(m_ThreadInfo->fullJID, szBareJid, _countof(szBareJid))); TiXmlElement *publish_node = iq << XCHILDNS("pubsub", "http://jabber.org/protocol/pubsub") << XCHILD("publish"); { @@ -1838,12 +1819,6 @@ void CJabberProto::OmemoSendBundle() m_ThreadInfo->send(iq); } -void CJabberProto::OmemoPublishNodes() -{ - OmemoAnnounceDevice(); - OmemoSendBundle(); -} - bool CJabberProto::OmemoCheckSession(MCONTACT hContact) { if (m_omemo.session_checked[hContact]) @@ -1866,7 +1841,7 @@ bool CJabberProto::OmemoCheckSession(MCONTACT hContact) XmlNodeIq iq(AddIQ(&CJabberProto::OmemoOnIqResultGetBundle, JABBER_IQ_TYPE_GET, nullptr, _id)); char szBareJid[JABBER_MAX_JID_LEN]; - iq << XATTR("from", JabberStripJid(m_ThreadInfo->fullJID, szBareJid, _countof_portable(szBareJid))); + iq << XATTR("from", JabberStripJid(m_ThreadInfo->fullJID, szBareJid, _countof(szBareJid))); char *jid = ContactToJID(hContact); iq << XATTR("to", jid); @@ -2029,18 +2004,18 @@ int CJabberProto::OmemoEncryptMessage(XmlNode &msg, const char *msg_text, MCONTA const EVP_CIPHER *cipher = EVP_aes_128_gcm(); unsigned char key[16], iv[12], tag[16] /*, aad[48]*/; - Utils_GetRandom(key, _countof_portable(key)); - Utils_GetRandom(iv, _countof_portable(iv)); - Utils_GetRandom(tag, _countof_portable(tag)); - //Utils_GetRandom(aad, _countof_portable(aad)); + Utils_GetRandom(key, _countof(key)); + Utils_GetRandom(iv, _countof(iv)); + Utils_GetRandom(tag, _countof(tag)); + //Utils_GetRandom(aad, _countof(aad)); EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); - EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, _countof_portable(iv), nullptr); + EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, _countof(iv), nullptr); EVP_EncryptInit(ctx, cipher, key, iv); char *out; const size_t inl = strlen(msg_text); int tmp_len = 0, outl; - //EVP_EncryptUpdate(ctx, nullptr, &outl, aad, _countof_portable(aad)); - out = (char*)mir_alloc(inl + _countof_portable(key) - 1); + //EVP_EncryptUpdate(ctx, nullptr, &outl, aad, _countof(aad)); + out = (char*)mir_alloc(inl + _countof(key) - 1); for (;;) { EVP_EncryptUpdate(ctx, (unsigned char*)(out + tmp_len), &outl, (unsigned char*)(msg_text + tmp_len), (int)(inl - tmp_len)); tmp_len += outl; @@ -2050,7 +2025,7 @@ int CJabberProto::OmemoEncryptMessage(XmlNode &msg, const char *msg_text, MCONTA EVP_EncryptFinal(ctx, (unsigned char*)(msg_text + tmp_len), &outl); tmp_len += outl; - EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, _countof_portable(tag), tag); + EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, _countof(tag), tag); EVP_CIPHER_CTX_free(ctx); TiXmlElement *encrypted = msg << XCHILDNS("encrypted", JABBER_FEAT_OMEMO); @@ -2095,7 +2070,7 @@ int CJabberProto::OmemoEncryptMessage(XmlNode &msg, const char *msg_text, MCONTA } TiXmlElement *iv_node = header << XCHILD("iv"); - iv_node->SetText(ptrA(mir_base64_encode(iv, _countof_portable(iv))).get()); + iv_node->SetText(ptrA(mir_base64_encode(iv, _countof(iv))).get()); msg << XCHILDNS("store", "urn:xmpp:hints"); if (!session_count) diff --git a/protocols/JabberG/src/jabber_proto.h b/protocols/JabberG/src/jabber_proto.h index 05d8a6f4ee..8cd4ac4b64 100644 --- a/protocols/JabberG/src/jabber_proto.h +++ b/protocols/JabberG/src/jabber_proto.h @@ -713,11 +713,10 @@ struct CJabberProto : public PROTO, public IJabberInterface void OmemoPutMessageToOutgoingQueue(MCONTACT hContact, int, const char *pszSrc); void OmemoPutMessageToIncommingQueue(const TiXmlElement *node, const char *jid, time_t msgTime); void OmemoHandleMessageQueue(); - void OmemoHandleDeviceList(const char *from, const TiXmlElement *node); + bool OmemoHandleDeviceList(const char *from, const TiXmlElement *node); void OmemoInitDevice(); - void OmemoAnnounceDevice(); + void OmemoAnnounceDevice(bool include_cache); void OmemoSendBundle(); - void OmemoPublishNodes(); bool OmemoCheckSession(MCONTACT hContact); int OmemoEncryptMessage(XmlNode &msg, const char *msg_text, MCONTACT hContact); bool OmemoIsEnabled(MCONTACT hContact); diff --git a/protocols/JabberG/src/jabber_userinfo.cpp b/protocols/JabberG/src/jabber_userinfo.cpp index 17ee16a485..5fe597a2f8 100644 --- a/protocols/JabberG/src/jabber_userinfo.cpp +++ b/protocols/JabberG/src/jabber_userinfo.cpp @@ -785,6 +785,7 @@ static void AddListItem(HWND hwndList, const CMStringA &pszStr1, const wchar_t * LVITEM lvi = {}; lvi.mask = LVIF_TEXT; lvi.pszText = mir_a2u(pszStr1); + lvi.iItem = 100500; int idx = ListView_InsertItem(hwndList, &lvi); mir_free(lvi.pszText); @@ -848,61 +849,51 @@ INT_PTR CALLBACK JabberUserOmemoDlgProc(HWND hwndDlg, UINT msg, WPARAM, LPARAM l ListView_DeleteAllItems(hwndList); if (pInfo->hContact == 0) { - uint32_t ownDeviceId = pInfo->ppro->getDword("OmemoDeviceId"); - CMStringA str1(FORMAT, "%d", ownDeviceId); + // GetOwnDeviceId() creates own keys if they don't exist + CMStringA str1(FORMAT, "%d", pInfo->ppro->m_omemo.GetOwnDeviceId()); CMStringA str2(pInfo->ppro->getMStringA("OmemoFingerprintOwn")); - // AddListItem(hwndList, "Other devices", "Not implemented yet", ""); AddListItem(hwndList, str1, TranslateT("Own device"), str2.Mid(2)); -/* - LIST arSettings(1); - db_enum_settings(pInfo->hContact, EnumOwnSessions, pInfo->ppro->m_szModuleName, &arSettings); - - str2 = ""; - for (auto &it : arSettings) { - str1.Format("%d", pInfo->ppro->getDword(it)); - AddListItem(hwndList, str1, str2); - }*/ } - else { - for (int i=0;; i++) { - CMStringA szSetting(FORMAT, "%s%d", omemo::DevicePrefix, i); - uint32_t device_id = pInfo->ppro->getDword(pInfo->hContact, szSetting, 0); - if (device_id == 0) - break; - - char* jiddev = pInfo->ppro->getStringA(pInfo->hContact, "jid"); - if (jiddev == 0) - continue; - - size_t len = strlen(jiddev); - jiddev = (char*)mir_realloc(jiddev, len + sizeof(int32_t)); - memcpy(jiddev+len, &device_id, sizeof(int32_t)); - - szSetting = omemo::IdentityPrefix; - szSetting.Append(ptrA(mir_base64_encode(jiddev, len + sizeof(int32_t)))); - mir_free(jiddev); - - const wchar_t *pwszStatus = L""; - CMStringA fp_hex; - DBVARIANT dbv = { 0 }; - dbv.type = DBVT_BLOB; - db_get(pInfo->hContact, pInfo->ppro->m_szModuleName, szSetting, &dbv); - if (dbv.cpbVal == 33) { - fp_hex.Truncate(33 * 2); - bin2hex(dbv.pbVal, 33, fp_hex.GetBuffer()); - uint8_t trusted = pInfo->ppro->getByte(pInfo->hContact, "OmemoFingerprintTrusted_" + fp_hex); - pwszStatus = trusted ? TranslateT("Trusted") : TranslateT("UNTRUSTED"); - //TODO: 3 states Trusted, Untrusted, TOFU - fp_hex = fp_hex.Mid(2); - } - else if (dbv.cpbVal) - pwszStatus = TranslateT("Unknown"); + + for (int i=0;; i++) { + CMStringA szSetting(FORMAT, "%s%d", omemo::DevicePrefix, i); + uint32_t device_id = pInfo->ppro->getDword(pInfo->hContact, szSetting, 0); + if (device_id == 0) + break; + + char* jiddev = pInfo->ppro->getStringA(pInfo->hContact, "jid"); + if (jiddev == 0) + continue; + + size_t len = strlen(jiddev); + jiddev = (char*)mir_realloc(jiddev, len + sizeof(int32_t)); + memcpy(jiddev+len, &device_id, sizeof(int32_t)); + + szSetting = omemo::IdentityPrefix; + szSetting.Append(ptrA(mir_base64_encode(jiddev, len + sizeof(int32_t)))); + mir_free(jiddev); + + const wchar_t *pwszStatus = L""; + CMStringA fp_hex; + DBVARIANT dbv = { 0 }; + dbv.type = DBVT_BLOB; + db_get(pInfo->hContact, pInfo->ppro->m_szModuleName, szSetting, &dbv); + if (dbv.cpbVal == 33) { + fp_hex.Truncate(33 * 2); + bin2hex(dbv.pbVal, 33, fp_hex.GetBuffer()); + uint8_t trusted = pInfo->ppro->getByte(pInfo->hContact, "OmemoFingerprintTrusted_" + fp_hex); + pwszStatus = trusted ? TranslateT("Trusted") : TranslateT("UNTRUSTED"); + //TODO: 3 states Trusted, Untrusted, TOFU + fp_hex = fp_hex.Mid(2); + } + else if (dbv.cpbVal) + pwszStatus = TranslateT("Unknown"); - db_free(&dbv); + db_free(&dbv); - AddListItem(hwndList, CMStringA(FORMAT, "%d", device_id), pwszStatus, fp_hex); - } + AddListItem(hwndList, CMStringA(FORMAT, "%d", device_id), pwszStatus, fp_hex); } + break; } return FALSE; -- cgit v1.2.3