summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGeorge Hazan <ghazan@miranda.im>2022-09-30 22:44:47 +0300
committerGeorge Hazan <ghazan@miranda.im>2022-09-30 22:44:47 +0300
commit6de31630215f88f3639efb834be02e5af9bfce57 (patch)
tree59b286f6fecdc3b96adfb42620057e3674155786
parentd31328a7ea6e180fa534155d7e0b1730784f5410 (diff)
WhatsApp:
- full preparation of key-pair response during device registration; - getBlob() now returns MBinBuffer; - fixed some WAWriter issues;
-rw-r--r--libs/libsignal/src/signal.def54
-rw-r--r--protocols/WhatsAppWeb/src/noise.cpp14
-rw-r--r--protocols/WhatsAppWeb/src/proto.h2
-rw-r--r--protocols/WhatsAppWeb/src/qrcode.cpp3
-rw-r--r--protocols/WhatsAppWeb/src/server.cpp87
-rw-r--r--protocols/WhatsAppWeb/src/utils.cpp99
-rw-r--r--protocols/WhatsAppWeb/src/utils.h19
-rw-r--r--protocols/WhatsAppWeb/src/wareader.cpp128
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 &param)
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);
}