diff options
-rw-r--r-- | libs/libsignal/src/signal.def | 54 | ||||
-rw-r--r-- | protocols/WhatsAppWeb/src/noise.cpp | 14 | ||||
-rw-r--r-- | protocols/WhatsAppWeb/src/proto.h | 2 | ||||
-rw-r--r-- | protocols/WhatsAppWeb/src/qrcode.cpp | 3 | ||||
-rw-r--r-- | protocols/WhatsAppWeb/src/server.cpp | 87 | ||||
-rw-r--r-- | protocols/WhatsAppWeb/src/utils.cpp | 99 | ||||
-rw-r--r-- | protocols/WhatsAppWeb/src/utils.h | 19 | ||||
-rw-r--r-- | protocols/WhatsAppWeb/src/wareader.cpp | 128 |
8 files changed, 242 insertions, 164 deletions
diff --git a/libs/libsignal/src/signal.def b/libs/libsignal/src/signal.def index 5a5e237806..e3e90e1976 100644 --- a/libs/libsignal/src/signal.def +++ b/libs/libsignal/src/signal.def @@ -70,34 +70,36 @@ EXPORTS signal_protocol_key_helper_key_list_element signal_protocol_key_helper_key_list_next signal_protocol_key_helper_key_list_free - session_builder_create - session_cipher_create - session_builder_free - session_cipher_free - curve_decode_point - session_pre_key_bundle_create - session_builder_process_pre_key_bundle - session_cipher_encrypt - ciphertext_message_get_serialized - session_cipher_decrypt_signal_message - signal_message_deserialize - session_cipher_decrypt_pre_key_signal_message - pre_key_signal_message_deserialize - pre_key_signal_message_get_pre_key_id - session_signed_pre_key_serialize - session_pre_key_serialize - session_pre_key_get_id - session_pre_key_deserialize - fingerprint_generator_create - fingerprint_generator_free - fingerprint_generator_create_for - fingerprint_get_displayable - displayable_fingerprint_text - displayable_fingerprint_local - displayable_fingerprint_remote - ciphertext_message_get_type + session_builder_create + session_cipher_create + session_builder_free + session_cipher_free + curve_decode_point + session_pre_key_bundle_create + session_builder_process_pre_key_bundle + session_cipher_encrypt + ciphertext_message_get_serialized + session_cipher_decrypt_signal_message + signal_message_deserialize + session_cipher_decrypt_pre_key_signal_message + pre_key_signal_message_deserialize + pre_key_signal_message_get_pre_key_id + session_signed_pre_key_serialize + session_pre_key_serialize + session_pre_key_get_id + session_pre_key_deserialize + fingerprint_generator_create + fingerprint_generator_free + fingerprint_generator_create_for + fingerprint_get_displayable + displayable_fingerprint_text + displayable_fingerprint_local + displayable_fingerprint_remote + ciphertext_message_get_type curve_calculate_agreement curve_decode_private_point + curve_calculate_signature + curve_verify_signature hkdf_create hkdf_expand hkdf_destroy diff --git a/protocols/WhatsAppWeb/src/noise.cpp b/protocols/WhatsAppWeb/src/noise.cpp index 3c2927edd6..72b64fcc8a 100644 --- a/protocols/WhatsAppWeb/src/noise.cpp +++ b/protocols/WhatsAppWeb/src/noise.cpp @@ -123,16 +123,16 @@ void WANoise::init() signal_protocol_key_helper_key_list_free(keys_root); } - ppro->getBlob(DBKEY_NOISE_PUB, noiseKeys.pub); - ppro->getBlob(DBKEY_NOISE_PRIV, noiseKeys.priv); + noiseKeys.pub = ppro->getBlob(DBKEY_NOISE_PUB); + noiseKeys.priv = ppro->getBlob(DBKEY_NOISE_PRIV); - ppro->getBlob(DBKEY_SIGNED_IDENTITY_PUB, signedIdentity.pub); - ppro->getBlob(DBKEY_SIGNED_IDENTITY_PRIV, signedIdentity.priv); + signedIdentity.pub = ppro->getBlob(DBKEY_SIGNED_IDENTITY_PUB); + signedIdentity.priv = ppro->getBlob(DBKEY_SIGNED_IDENTITY_PRIV); - ppro->getBlob(DBKEY_PREKEY_PUB, preKey.pub); - ppro->getBlob(DBKEY_PREKEY_PRIV, preKey.priv); - ppro->getBlob(DBKEY_PREKEY_SIGN, preKey.signature); + 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.h b/protocols/WhatsAppWeb/src/proto.h index 828c912406..307615636b 100644 --- a/protocols/WhatsAppWeb/src/proto.h +++ b/protocols/WhatsAppWeb/src/proto.h @@ -148,7 +148,7 @@ class WhatsAppProto : public PROTO<WhatsAppProto> EVP_PKEY *m_pKeys; // private & public keys WANoise *m_noise; - bool getBlob(const char *pSetting, MBinBuffer &buf); + MBinBuffer getBlob(const char *pSetting); // Contacts management ///////////////////////////////////////////////////////////////// diff --git a/protocols/WhatsAppWeb/src/qrcode.cpp b/protocols/WhatsAppWeb/src/qrcode.cpp index 46b71700c5..9dd69f92e3 100644 --- a/protocols/WhatsAppWeb/src/qrcode.cpp +++ b/protocols/WhatsAppWeb/src/qrcode.cpp @@ -113,8 +113,7 @@ bool WhatsAppProto::ShowQrCode(const CMStringA &ref) { CallFunctionSync(sttShowDialog, this); - MBinBuffer secret; - getBlob(DBKEY_SECRET_KEY, secret); + MBinBuffer secret(getBlob(DBKEY_SECRET_KEY)); ptrA s1(mir_base64_encode(m_noise->noiseKeys.pub)); ptrA s2(mir_base64_encode(m_noise->signedIdentity.pub)); diff --git a/protocols/WhatsAppWeb/src/server.cpp b/protocols/WhatsAppWeb/src/server.cpp index 1139aed2e5..63979d9cc3 100644 --- a/protocols/WhatsAppWeb/src/server.cpp +++ b/protocols/WhatsAppWeb/src/server.cpp @@ -74,7 +74,7 @@ void WhatsAppProto::OnIqPairDevice(const WANode &node) reply << CHAR_PARAM("to", S_WHATSAPP_NET) << CHAR_PARAM("type", "result") << CHAR_PARAM("id", node.getAttr("id")); WSSendNode(reply); - if (auto *pRef = node.children.front()->getChild("ref")) { + if (auto *pRef = node.getChild("pair-device")->getChild("ref")) { ShowQrCode(pRef->getBody()); } else { @@ -87,7 +87,7 @@ void WhatsAppProto::OnIqPairSuccess(const WANode &node) { CloseQrDialog(); - auto *pRoot = node.children.front(); + auto *pRoot = node.getChild("pair-success"); try { if (auto *pPlatform = pRoot->getChild("platform")) @@ -105,7 +105,7 @@ void WhatsAppProto::OnIqPairSuccess(const WANode &node) if (auto *pIdentity = pRoot->getChild("device-identity")) { proto::ADVSignedDeviceIdentityHMAC payload; - if (!payload.ParseFromArray(pIdentity->content.data(), pIdentity->content.length())) + if (!payload.ParseFromArray(pIdentity->content.data(), (int)pIdentity->content.length())) throw "OnIqPairSuccess: got reply with invalid identity, exiting"; auto &hmac = payload.hmac(); @@ -114,6 +114,16 @@ void WhatsAppProto::OnIqPairSuccess(const WANode &node) auto &details = payload.details(); Netlib_Dump(nullptr, details.c_str(), details.size(), false, 0); + // check details signature using HMAC + { + uint8_t signature[32]; + unsigned int out_len = sizeof(signature); + MBinBuffer secret(getBlob(DBKEY_SECRET_KEY)); + HMAC(EVP_sha256(), secret.data(), (int)secret.length(), (BYTE *)details.c_str(), (int)details.size(), signature, &out_len); + if (memcmp(hmac.c_str(), signature, sizeof(signature))) + throw "OnIqPairSuccess: got reply with invalid details signature, exiting"; + } + proto::ADVSignedDeviceIdentity account; if (!account.ParseFromString(details)) throw "OnIqPairSuccess: got reply with invalid account, exiting"; @@ -121,23 +131,62 @@ void WhatsAppProto::OnIqPairSuccess(const WANode &node) auto &deviceDetails = account.details(); auto &accountSignature = account.accountsignature(); auto &accountSignatureKey = account.accountsignaturekey(); + { + 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()); + + ec_public_key key = {}; + memcpy(key.data, accountSignatureKey.c_str(), sizeof(key.data)); + if (1 != curve_verify_signature(&key, (BYTE *)buf.data(), buf.length(), (BYTE *)accountSignature.c_str(), accountSignature.size())) + throw "OnIqPairSuccess: got reply with invalid account signature, exiting"; + } + debugLogA("Received valid account signature"); + { + 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(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()); + if (curve_calculate_signature(g_plugin.pCtx, &result, &key, (BYTE *)buf.data(), buf.length()) != 0) + throw "OnIqPairSuccess: cannot calculate account signature, exiting"; + + account.set_devicesignature(result->data, result->len); + signal_buffer_free(result); + } + + setDword("SignalDeviceId", 0); + { + MBinBuffer key; + if (accountSignatureKey.size() == 32) + key.append(KEY_BUNDLE_TYPE, 1); + key.append(accountSignatureKey.c_str(), accountSignatureKey.size()); + db_set_blob(0, m_szModuleName, "SignalIdentifierKey", key.data(), (int)key.length()); + } + + account.clear_accountsignaturekey(); + + MBinBuffer accountEnc(account.ByteSize()); + account.SerializeToArray(accountEnc.data(), (int)accountEnc.length()); + db_set_blob(0, m_szModuleName, "WAAccount", accountEnc.data(), (int)accountEnc.length()); + + proto::ADVDeviceIdentity deviceIdentity; + deviceIdentity.ParseFromString(deviceDetails); + + WANode reply("iq"); + reply << CHAR_PARAM("to", S_WHATSAPP_NET) << CHAR_PARAM("type", "result") << CHAR_PARAM("id", node.getAttr("id")); + + WANode *nodePair = reply.addChild("pair-device-sign"); - MBinBuffer accountMsg; - accountMsg.append("\x06\x00", 2); - accountMsg.append(deviceDetails.c_str(), deviceDetails.size()); - accountMsg.append(m_noise->signedIdentity.pub.data(), m_noise->signedIdentity.pub.length()); - - // Curve.verify(accountSignatureKey, accountMsg, accountSignature) - debugLogA("Received account signature"); - Netlib_Dump(nullptr, accountSignatureKey.c_str(), accountSignatureKey.size(), false, 0); - Netlib_Dump(nullptr, accountMsg.data(), accountMsg.length(), false, 0); - Netlib_Dump(nullptr, accountSignature.c_str(), accountSignature.size(), false, 0); - - MBinBuffer deviceMsg; - deviceMsg.append("\x06\x01", 2); - deviceMsg.append(deviceDetails.c_str(), deviceDetails.size()); - deviceMsg.append(m_noise->signedIdentity.pub.data(), m_noise->signedIdentity.pub.length()); - deviceMsg.append(accountSignatureKey.c_str(), accountSignatureKey.size()); + WANode *nodeDeviceIdentity = nodePair->addChild("device-identity"); + nodeDeviceIdentity->addAttr("key-index", deviceIdentity.keyindex()); + nodeDeviceIdentity->content.append(accountEnc.data(), accountEnc.length()); + WSSendNode(reply); } else throw "OnIqPairSuccess: got reply without identity, exiting"; } diff --git a/protocols/WhatsAppWeb/src/utils.cpp b/protocols/WhatsAppWeb/src/utils.cpp index ad4e2d1e7e..79e5c0217a 100644 --- a/protocols/WhatsAppWeb/src/utils.cpp +++ b/protocols/WhatsAppWeb/src/utils.cpp @@ -39,7 +39,8 @@ WAUser* WhatsAppProto::AddUser(const char *szId, bool bTemporary) WA_PKT_HANDLER WhatsAppProto::FindPersistentHandler(const WANode &pNode) { - CMStringA szTitle = (pNode.children.size() > 0) ? pNode.children.front()->title : ""; + auto *pChild = pNode.getFirstChild(); + CMStringA szTitle = (pChild) ? pChild->title : ""; CMStringA szType = pNode.title; CMStringA szXmlns = pNode.getAttr("xmlns"); @@ -58,15 +59,15 @@ WA_PKT_HANDLER WhatsAppProto::FindPersistentHandler(const WANode &pNode) ///////////////////////////////////////////////////////////////////////////////////////// -bool WhatsAppProto::getBlob(const char *szSetting, MBinBuffer &buf) +MBinBuffer WhatsAppProto::getBlob(const char *szSetting) { + MBinBuffer buf; DBVARIANT dbv = { DBVT_BLOB }; - if (db_get(0, m_szModuleName, szSetting, &dbv)) - return false; - - buf.assign(dbv.pbVal, dbv.cpbVal); - db_free(&dbv); - return true; + if (!db_get(0, m_szModuleName, szSetting, &dbv)) { + buf.assign(dbv.pbVal, dbv.cpbVal); + db_free(&dbv); + } + return buf; } ///////////////////////////////////////////////////////////////////////////////////////// @@ -116,88 +117,6 @@ int WhatsAppProto::WSSendNode(WANode &node, WA_PKT_HANDLER pHandler) } ///////////////////////////////////////////////////////////////////////////////////////// -// WANode members - -WANode::WANode() -{} - -WANode::WANode(const char *pszTitle) : - title(pszTitle) -{} - -WANode::~WANode() -{ - for (auto &p: attrs) - delete p; - - for (auto &p: children) - delete p; -} - -const char* WANode::getAttr(const char *pszName) const -{ - for (auto &p: attrs) - if (p->name == pszName) - return p->value.c_str(); - - return nullptr; -} - -void WANode::addAttr(const char *pszName, const char *pszValue) -{ - attrs.push_back(new Attr(pszName, pszValue)); -} - -CMStringA WANode::getBody() const -{ - return CMStringA((char *)content.data(), (int)content.length()); -} - -WANode* WANode::getChild(const char *pszName) const -{ - for (auto &it : children) - if (it->title == pszName) - return it; - - return nullptr; -} - -void WANode::print(CMStringA &dest, int level) const -{ - for (int i = 0; i < level; i++) - dest.Append(" "); - - dest.AppendFormat("<%s ", title.c_str()); - for (auto &p: attrs) - dest.AppendFormat("%s=\"%s\" ", p->name.c_str(), p->value.c_str()); - dest.Truncate(dest.GetLength() - 1); - - if (content.isEmpty() && children.empty()) { - dest.Append("/>\n"); - return; - } - - dest.Append(">"); - if (!content.isEmpty()) { - ptrA tmp((char *)mir_alloc(content.length() * 2 + 1)); - bin2hex(content.data(), content.length(), tmp); - dest.AppendFormat("%s", tmp.get()); - } - - if (!children.empty()) { - dest.Append("\n"); - - for (auto &p : children) - p->print(dest, level + 1); - - for (int i = 0; i < level; i++) - dest.Append(" "); - } - - dest.AppendFormat("</%s>\n", title.c_str()); -} - -///////////////////////////////////////////////////////////////////////////////////////// std::string encodeBigEndian(uint32_t num, size_t len) { diff --git a/protocols/WhatsAppWeb/src/utils.h b/protocols/WhatsAppWeb/src/utils.h index 21a5bd5efe..507c674c1f 100644 --- a/protocols/WhatsAppWeb/src/utils.h +++ b/protocols/WhatsAppWeb/src/utils.h @@ -25,19 +25,12 @@ Copyright © 2019-22 George Hazan class WANode // kinda XML { + friend class WAReader; friend class WAWriter; - struct Attr - { - Attr(const char *pszName, const char *pszValue) : - name(pszName), - value(pszValue) - {} - - CMStringA name, value; - }; - - std::list<Attr*> attrs; + WANode *pParent = nullptr; + OBJLIST<struct Attr> attrs; + OBJLIST<WANode> children; public: WANode(); @@ -45,17 +38,19 @@ public: ~WANode(); void addAttr(const char *pszName, const char *pszValue); + void addAttr(const char *pszName, int iValue); const char* getAttr(const char *pszName) const; CMStringA getBody() const; + WANode* addChild(const char *pszName); WANode* getChild(const char *pszName) const; + WANode* getFirstChild(void) const; void print(CMStringA &dest, int level = 0) const; CMStringA title; MBinBuffer content; - std::list<WANode*> children; }; __forceinline WANode& operator<<(WANode &node, const CHAR_PARAM ¶m) diff --git a/protocols/WhatsAppWeb/src/wareader.cpp b/protocols/WhatsAppWeb/src/wareader.cpp index 1c164d7148..8a7ab3f99d 100644 --- a/protocols/WhatsAppWeb/src/wareader.cpp +++ b/protocols/WhatsAppWeb/src/wareader.cpp @@ -8,6 +8,120 @@ Copyright © 2019-22 George Hazan #include "stdafx.h" #include "dicts.h" +struct Attr +{ + Attr(const char *pszName, const char *pszValue) : + name(pszName), + value(pszValue) + {} + + Attr(const char *pszName, int iValue) : + name(pszName), + value(FORMAT, "%d", iValue) + {} + + CMStringA name, value; +}; + +///////////////////////////////////////////////////////////////////////////////////////// +// WANode members + +WANode::WANode() : + attrs(1), + children(1) +{} + +WANode::WANode(const char *pszTitle) : + attrs(1), + children(1), + title(pszTitle) +{} + +WANode::~WANode() +{ +} + +const char *WANode::getAttr(const char *pszName) const +{ + for (auto &p : attrs) + if (p->name == pszName) + return p->value.c_str(); + + return nullptr; +} + +void WANode::addAttr(const char *pszName, const char *pszValue) +{ + attrs.insert(new Attr(pszName, pszValue)); +} + +void WANode::addAttr(const char *pszName, int iValue) +{ + attrs.insert(new Attr(pszName, iValue)); +} + +CMStringA WANode::getBody() const +{ + return CMStringA((char *)content.data(), (int)content.length()); +} + +WANode *WANode::addChild(const char *pszName) +{ + auto *pNew = new WANode(pszName); + pNew->pParent = this; + children.insert(pNew); + return pNew; +} + +WANode* WANode::getChild(const char *pszName) const +{ + for (auto &it : children) + if (it->title == pszName) + return it; + + return nullptr; +} + +WANode *WANode::getFirstChild(void) const +{ + return (children.getCount()) ? &children[0] : nullptr; +} + +void WANode::print(CMStringA &dest, int level) const +{ + for (int i = 0; i < level; i++) + dest.Append(" "); + + dest.AppendFormat("<%s ", title.c_str()); + for (auto &p : attrs) + dest.AppendFormat("%s=\"%s\" ", p->name.c_str(), p->value.c_str()); + dest.Truncate(dest.GetLength() - 1); + + if (content.isEmpty() && !children.getCount()) { + dest.Append("/>\n"); + return; + } + + dest.Append(">"); + if (!content.isEmpty()) { + ptrA tmp((char *)mir_alloc(content.length() * 2 + 1)); + bin2hex(content.data(), content.length(), tmp); + dest.AppendFormat("%s", tmp.get()); + } + + if (children.getCount()) { + dest.Append("\n"); + + for (auto &p : children) + p->print(dest, level + 1); + + for (int i = 0; i < level; i++) + dest.Append(" "); + } + + dest.AppendFormat("</%s>\n", title.c_str()); +} + ///////////////////////////////////////////////////////////////////////////////////////// // WAReader class members @@ -61,7 +175,7 @@ bool WAReader::readList(WANode *pParent, int tag) WANode *pNew = readNode(); if (pNew == nullptr) return false; - pParent->children.push_back(pNew); + pParent->children.insert(pNew); } return true; @@ -312,10 +426,11 @@ void WAWriter::writeListSize(int length) void WAWriter::writeNode(const WANode *pNode) { // we never send zipped content - writeByte(0); + if (pNode->pParent == nullptr) + writeByte(0); - int numAttrs = (int)pNode->attrs.size(); - int hasContent = pNode->content.length() != 0; + int numAttrs = (int)pNode->attrs.getCount(); + int hasContent = pNode->content.length() != 0 || pNode->children.getCount() != 0; writeListSize(2 * numAttrs + 1 + hasContent); writeString(pNode->title.c_str()); @@ -334,10 +449,9 @@ void WAWriter::writeNode(const WANode *pNode) writeLength((int)pNode->content.length()); body.append(pNode->content.data(), pNode->content.length()); } - // write children - if (pNode->children.size()) { - writeListSize((int)pNode->children.size()); + else if (pNode->children.getCount()) { + writeListSize(pNode->children.getCount()); for (auto &it : pNode->children) writeNode(it); } |