summaryrefslogtreecommitdiff
path: root/protocols/WhatsAppWeb/src
diff options
context:
space:
mode:
authorGeorge Hazan <ghazan@miranda.im>2022-09-17 13:45:05 +0300
committerGeorge Hazan <ghazan@miranda.im>2022-09-17 13:45:05 +0300
commit0c63ec79606a42d944aeed0fd52596c55d6a0768 (patch)
treec5c05944aca61028db0ad044f435b21452eae379 /protocols/WhatsAppWeb/src
parentdccf65f5a9a66f72a9765b6d26b1ddee2374d61d (diff)
WhatsApp: cleaning old garbage (macKey, encKey)
Diffstat (limited to 'protocols/WhatsAppWeb/src')
-rw-r--r--protocols/WhatsAppWeb/src/db.h3
-rw-r--r--protocols/WhatsAppWeb/src/proto.h21
-rw-r--r--protocols/WhatsAppWeb/src/server.cpp173
-rw-r--r--protocols/WhatsAppWeb/src/utils.cpp127
4 files changed, 116 insertions, 208 deletions
diff --git a/protocols/WhatsAppWeb/src/db.h b/protocols/WhatsAppWeb/src/db.h
index 88c1808ac4..d2153b3305 100644
--- a/protocols/WhatsAppWeb/src/db.h
+++ b/protocols/WhatsAppWeb/src/db.h
@@ -20,9 +20,6 @@ Copyright © 2019-22 George Hazan
#define DBKEY_PUB_KEY "PublicKey"
#define DBKEY_PRIVATE_KEY "PrivateKey"
-#define DBKEY_MAC_KEY "MacKey"
-#define DBKEY_ENC_KEY "EncKey"
-
#define DBKEY_NICK "Nick"
#define DBKEY_DEF_GROUP "DefaultGroup"
#define DBKEY_AUTORUNCHATS "AutoRunChats"
diff --git a/protocols/WhatsAppWeb/src/proto.h b/protocols/WhatsAppWeb/src/proto.h
index 9406c414e9..e633e21ebf 100644
--- a/protocols/WhatsAppWeb/src/proto.h
+++ b/protocols/WhatsAppWeb/src/proto.h
@@ -8,6 +8,8 @@ Copyright © 2019-22 George Hazan
#if !defined(PROTO_H)
#define PROTO_H
+#define APP_VERSION "2.2230.15"
+
class WhatsAppProto;
typedef void (WhatsAppProto:: *WA_PKT_HANDLER)(const JSONNode &node, void*);
@@ -62,30 +64,34 @@ struct WAOwnMessage
class WANoise
{
+ WhatsAppProto *ppro;
int readCounter = 0, writeCounter = 0;
bool bInitFinished = false, bSendIntro = false;
MBinBuffer pubKey, privKey, salt, encKey, decKey;
uint8_t hash[32];
- void decrypt(const void *pData, size_t cbLen, MBinBuffer &dest);
- void encrypt(const void *pData, size_t cbLen, MBinBuffer &dest);
void deriveKey(const void *pData, size_t cbLen, MBinBuffer &write, MBinBuffer &read);
- void mixIntoKey(const void *pData, size_t cbLen);
+ void mixIntoKey(const void *n, const void *p);
void updateHash(const void *pData, size_t cbLen);
public:
- WANoise();
+ WANoise(WhatsAppProto *_ppro);
void finish() { bInitFinished = true; }
const MBinBuffer& getPub() const { return pubKey; }
+ void decrypt(const void *pData, size_t cbLen, MBinBuffer &dest);
+ void encrypt(const void *pData, size_t cbLen, MBinBuffer &dest);
+
bool decodeFrame(const void *pData, size_t cbLen);
void encodeFrame(const void *pData, size_t cbLen, MBinBuffer &dest);
};
class WhatsAppProto : public PROTO<WhatsAppProto>
{
+ friend class WANoise;
+
class CWhatsAppProtoImpl
{
friend class WhatsAppProto;
@@ -112,8 +118,6 @@ class WhatsAppProto : public PROTO<WhatsAppProto>
CMStringW m_tszAvatarFolder;
EVP_PKEY *m_pKeys; // private & public keys
- MBinBuffer mac_key, enc_key;
-
WANoise *m_noise;
bool getBlob(const char *pSetting, MBinBuffer &buf);
@@ -156,10 +160,7 @@ class WhatsAppProto : public PROTO<WhatsAppProto>
bool ServerThreadWorker(void);
void ShutdownSession(void);
- bool ProcessChallenge(const CMStringA &szChallenge);
- bool ProcessSecret(const CMStringA &szSecret);
-
- bool decryptBinaryMessage(size_t cbSize, const void *buf, MBinBuffer &res);
+ bool ProcessHandshake(const MBinBuffer &szkeyEnc);
void SendKeepAlive();
diff --git a/protocols/WhatsAppWeb/src/server.cpp b/protocols/WhatsAppWeb/src/server.cpp
index ea34e3e372..515aa86458 100644
--- a/protocols/WhatsAppWeb/src/server.cpp
+++ b/protocols/WhatsAppWeb/src/server.cpp
@@ -52,99 +52,71 @@ void WhatsAppProto::SendKeepAlive()
/////////////////////////////////////////////////////////////////////////////////////////
-bool WhatsAppProto::ProcessChallenge(const CMStringA &szChallenge)
+bool WhatsAppProto::ProcessHandshake(const MBinBuffer &keyEnc)
{
- if (szChallenge.IsEmpty() || mac_key.isEmpty())
- return false;
-
- size_t cbLen;
- void *pChallenge = mir_base64_decode(szChallenge, &cbLen);
-
- BYTE digest[32];
- unsigned cbResult = sizeof(digest);
- HMAC(EVP_sha256(), mac_key.data(), (int)mac_key.length(), (BYTE*)pChallenge, (int)cbLen, digest, &cbResult);
-
- ptrA szServer(getStringA(DBKEY_SERVER_TOKEN));
- //CMStringA payload(FORMAT, "[\"admin\",\"challenge\",\"%s\",\"%s\",\"%s\"]",
-// ptrA(mir_base64_encode(digest, cbResult)).get(), szServer.get(), m_szClientId.c_str());
- //WSSend(payload);
- return true;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
+ proto::ClientPayload node;
-bool WhatsAppProto::ProcessSecret(const CMStringA &szSecret)
-{
- if (szSecret.IsEmpty())
- return false;
+ MFileVersion v;
+ Miranda_GetFileVersion(&v);
- size_t secretLen = 0;
- mir_ptr<BYTE> pSecret((BYTE *)mir_base64_decode(szSecret, &secretLen));
- if (pSecret == nullptr || secretLen != 144) {
- debugLogA("Invalid secret key, dropping session (secret len = %u", (unsigned)secretLen);
- return false;
- }
+ // generate registration packet
+ if (m_szClientToken.IsEmpty()) {
+ uint8_t appVersion[16];
+ mir_md5_hash((BYTE*)APP_VERSION, sizeof(APP_VERSION) - 1, appVersion);
- ec_public_key pPeerPublic;
- memcpy(pPeerPublic.data, pSecret, 32);
+ auto *pAppVersion = new proto::DeviceProps_AppVersion();
+ pAppVersion->set_primary(v[0]);
+ pAppVersion->set_secondary(v[1]);
+ pAppVersion->set_tertiary(v[2]);
+ pAppVersion->set_quaternary(v[3]);
- MBinBuffer privKey;
- if (!getBlob(DBKEY_PRIVATE_KEY, privKey))
- return false;
+ proto::DeviceProps pCompanion;
+ pCompanion.set_os("Miranda");
+ pCompanion.set_allocated_version(pAppVersion);
+ pCompanion.set_platformtype(proto::DeviceProps_PlatformType_DESKTOP);
+ pCompanion.set_requirefullsync(true);
- ec_private_key pMyPrivate;
- memcpy(pMyPrivate.data, privKey.data(), 32);
+ //MBinBuffer buf(pCompanion.ByteSize());
+ //pCompanion.SerializeToArray(buf.data(), (int)buf.length());
- uint8_t *pSharedKey, *pSharedExpanded;
- int sharedLen = curve_calculate_agreement(&pSharedKey, &pPeerPublic, &pMyPrivate);
- {
- BYTE salt[32], md[32];
- unsigned int md_len = 32;
- memset(salt, 0, sizeof(salt));
- HMAC(EVP_sha256(), salt, sizeof(salt), pSharedKey, sharedLen, md, &md_len);
-
- hkdf_context *pHKDF;
- hkdf_create(&pHKDF, 3, g_plugin.pCtx);
- hkdf_expand(pHKDF, &pSharedExpanded, md, sizeof(md), 0, 0, 80);
- hkdf_destroy(pHKDF);
- }
+ auto *pPairingData = new proto::ClientPayload_DevicePairingRegistrationData();
+ //pPairingData->set_deviceprops(buf.data(), buf.length());
+ pPairingData->set_buildhash(appVersion, sizeof(appVersion));
+ pPairingData->set_eregid("");
- // validation
- {
- unsigned int md_len = 32;
- BYTE sum[32], md[32], enc[112], *key = pSharedExpanded + 32;
- memcpy(enc, pSecret, 32);
- memcpy(enc + 32, (BYTE *)pSecret + 64, 80);
- memcpy(sum, (BYTE *)pSecret + 32, 32);
- HMAC(EVP_sha256(), key, 32, enc, sizeof(enc), md, &md_len);
- if (memcmp(md, sum, 32)) {
- debugLogA("Secret key validation failed, exiting");
- return false;
- }
+ node.set_allocated_devicepairingdata(pPairingData);
}
-
- // woohoo, everything is ok, decrypt keys
- {
- BYTE enc[80], dec[112], key[32], iv[16];
- memcpy(key, pSharedExpanded, sizeof(key));
- memcpy(iv, pSharedExpanded+64, sizeof(iv));
- memcpy(enc, pSecret.get() + 64, sizeof(enc));
-
- int dec_len = 0, final_len = 0;
- EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
- EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv);
- EVP_DecryptUpdate(ctx, dec, &dec_len, enc, sizeof(enc));
- EVP_DecryptFinal_ex(ctx, dec + dec_len, &final_len);
- dec_len += final_len;
- EVP_CIPHER_CTX_free(ctx);
-
- enc_key.assign(dec, 32);
- mac_key.assign(dec + 32, 32);
-
- db_set_blob(0, m_szModuleName, DBKEY_ENC_KEY, enc_key.data(), (int)enc_key.length());
- db_set_blob(0, m_szModuleName, DBKEY_MAC_KEY, mac_key.data(), (int)mac_key.length());
+ // generate login packet
+ else {
+ node.set_passive(true);
}
+ auto *pUserVersion = new proto::ClientPayload_UserAgent_AppVersion();
+ pUserVersion->set_primary(v[0]);
+ pUserVersion->set_secondary(v[1]);
+ pUserVersion->set_tertiary(v[2]);
+ pUserVersion->set_quaternary(v[3]);
+
+ auto *pUserAgent = new proto::ClientPayload_UserAgent();
+ pUserAgent->set_allocated_appversion(pUserVersion);
+ pUserAgent->set_platform(proto::ClientPayload_UserAgent_Platform_WINDOWS);
+ pUserAgent->set_releasechannel(proto::ClientPayload_UserAgent_ReleaseChannel_RELEASE);
+ pUserAgent->set_mcc("000");
+ pUserAgent->set_mnc("000");
+ pUserAgent->set_osversion("10.0");
+ pUserAgent->set_osbuildnumber("10.0");
+ pUserAgent->set_manufacturer("");
+ pUserAgent->set_device("Desktop");
+ pUserAgent->set_localelanguageiso6391("en");
+ pUserAgent->set_localecountryiso31661alpha2("US");
+
+ auto *pWebInfo = new proto::ClientPayload_WebInfo();
+ pWebInfo->set_websubplatform(proto::ClientPayload_WebInfo_WebSubPlatform_WINDA);
+
+ node.set_connecttype(proto::ClientPayload_ConnectType_WIFI_UNKNOWN);
+ node.set_connectreason(proto::ClientPayload_ConnectReason_USER_ACTIVATED);
+ node.set_allocated_useragent(pUserAgent);
+ node.set_allocated_webinfo(pWebInfo);
return true;
}
@@ -162,26 +134,6 @@ void WhatsAppProto::OnRestoreSession1(const JSONNode&, void*)
// WSSend(payload, &WhatsAppProto::OnRestoreSession2);
}
-void WhatsAppProto::OnRestoreSession2(const JSONNode &root, void*)
-{
- int status = root["status"].as_int();
- if (status != 200) {
- debugLogA("Attempt to restore session failed with error %d", status);
-
- if (status == 401 || status == 419) {
- POPUPDATAW Popup = {};
- Popup.lchIcon = IcoLib_GetIconByHandle(Skin_GetIconHandle(SKINICON_ERROR));
- wcsncpy_s(Popup.lpwzText, TranslateT("You need to launch WhatsApp on your phone"), _TRUNCATE);
- wcsncpy_s(Popup.lpwzContactName, m_tszUserName, _TRUNCATE);
- Popup.iSeconds = 10;
- PUAddPopupW(&Popup);
- }
-
- ShutdownSession();
- return;
- }
-}
-
/////////////////////////////////////////////////////////////////////////////////////////
void WhatsAppProto::ShutdownSession()
@@ -266,7 +218,7 @@ bool WhatsAppProto::ServerThreadWorker()
return false;
delete m_noise;
- m_noise = new WANoise();
+ m_noise = new WANoise(this);
debugLogA("Server connection succeeded");
m_hServerConn = pReply->nlc;
@@ -274,9 +226,6 @@ bool WhatsAppProto::ServerThreadWorker()
m_iPktNumber = 0;
m_szClientToken = getMStringA(DBKEY_CLIENT_TOKEN);
- MFileVersion v;
- Miranda_GetFileVersion(&v);
-
auto &pubKey = m_noise->getPub();
ptrA szPubKey(mir_base64_encode(pubKey.data(), pubKey.length()));
auto *client = new proto::HandshakeMessage::ClientHello(); client->set_ephemeral(pubKey.data(), pubKey.length());
@@ -454,11 +403,6 @@ void WhatsAppProto::ProcessCmd(const JSONNode &root)
{
CMStringW wszType(root["type"].as_mstring());
if (wszType == L"challenge") {
- CMStringA szChallenge(root["challenge"].as_mstring());
- if (!ProcessChallenge(szChallenge)) {
- ShutdownSession();
- return;
- }
}
}
@@ -469,13 +413,6 @@ void WhatsAppProto::ProcessConn(const JSONNode &root)
writeStr(DBKEY_ID, root["wid"]);
m_szJid = getMStringA(DBKEY_ID);
- CMStringA szSecret(root["secret"].as_mstring());
- if (!szSecret.IsEmpty())
- if (!ProcessSecret(szSecret)) {
- ShutdownSession();
- return;
- }
-
writeStr(DBKEY_NICK, root["pushname"]);
writeStr(DBKEY_CLIENT_TOKEN, root["clientToken"]);
writeStr(DBKEY_SERVER_TOKEN, root["serverToken"]);
diff --git a/protocols/WhatsAppWeb/src/utils.cpp b/protocols/WhatsAppWeb/src/utils.cpp
index fba42e8b8d..73ceb1ea94 100644
--- a/protocols/WhatsAppWeb/src/utils.cpp
+++ b/protocols/WhatsAppWeb/src/utils.cpp
@@ -7,8 +7,6 @@ Copyright © 2019 George Hazan
#include "stdafx.h"
-#define sharedKey(A, B, C) crypto_scalarmult((unsigned char*)A, (const unsigned char*)B, (const unsigned char*)C)
-
WAUser* WhatsAppProto::FindUser(const char *szId)
{
mir_cslock lck(m_csUsers);
@@ -100,28 +98,6 @@ int WhatsAppProto::WSSendNode(const char *pszPrefix, int flags, WANode &node, WA
BYTE iv[16];
Utils_GetRandom(iv, sizeof(iv));
- // allocate the buffer of the same size + 32 bytes for temporary operations
- MBinBuffer enc;
- enc.assign(writer.body.data(), writer.body.length());
- enc.append(mac_key.data(), mac_key.length());
-
- int enc_len = 0, final_len = 0;
- EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
- EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, (BYTE *)enc_key.data(), iv);
- EVP_EncryptUpdate(ctx, (BYTE *)enc.data(), &enc_len, (BYTE *)writer.body.data(), (int)writer.body.length());
- EVP_EncryptFinal_ex(ctx, (BYTE *)enc.data() + enc_len, &final_len);
- EVP_CIPHER_CTX_free(ctx);
-
- // build the resulting buffer of the following structure:
- // - packet prefix
- // - 32 bytes of HMAC
- // - 16 bytes of iv
- // - rest of encoded data
-
- BYTE hmac[32];
- unsigned int hmac_len = 32;
- HMAC(EVP_sha256(), mac_key.data(), (int)mac_key.length(), (BYTE *)enc.data(), enc_len, hmac, &hmac_len);
-
int pktId = ++m_iPktNumber;
if (pHandler != nullptr) {
@@ -138,57 +114,19 @@ int WhatsAppProto::WSSendNode(const char *pszPrefix, int flags, WANode &node, WA
MBinBuffer ret;
ret.append(pszPrefix, strlen(pszPrefix));
ret.append(postPrefix, sizeof(postPrefix));
- ret.append(hmac, sizeof(hmac));
- ret.append(iv, sizeof(iv));
- ret.append(enc.data(), enc_len);
WebSocket_SendBinary(m_hServerConn, ret.data(), ret.length());
return pktId;
}
/////////////////////////////////////////////////////////////////////////////////////////
-
-bool WhatsAppProto::decryptBinaryMessage(size_t cbSize, const void *buf, MBinBuffer &res)
-{
- if (cbSize <= 32)
- return false;
-
- // validate message first
- {
- unsigned int md_len = 32;
- BYTE md[32];
- HMAC(EVP_sha256(), mac_key.data(), (int)mac_key.length(), (unsigned char *)buf+32, (int)cbSize-32, md, &md_len);
- if (memcmp(buf, md, sizeof(md))) {
- debugLogA("Message cannot be decrypted, check your keys");
- return false;
- }
- }
-
- // okay, let's decrypt this thing
- auto *pBuf = (const unsigned char *)buf;
- {
- BYTE iv[16];
- memcpy(iv, pBuf + 32, sizeof(iv));
- res.assign(pBuf + 48, cbSize - 48);
- res.append(mac_key.data(), 32); // reserve 32 more bytes for temp data
-
- int dec_len = 0, final_len = 0;
- EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
- EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, (BYTE*)enc_key.data(), iv);
- EVP_DecryptUpdate(ctx, (BYTE*)res.data(), &dec_len, pBuf + 48, (int)cbSize - 48);
- EVP_DecryptFinal_ex(ctx, (BYTE*)res.data() + dec_len, &final_len);
- EVP_CIPHER_CTX_free(ctx);
- }
- return true;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
// WANoise members
static uint8_t intro_header[] = { 87, 65, 6, DICT_VERSION };
static uint8_t noise_init[] = "Noise_XX_25519_AESGCM_SHA256\0\0\0\0";
-WANoise::WANoise()
+WANoise::WANoise(WhatsAppProto *_ppro) :
+ ppro(_ppro)
{
salt.assign(noise_init, 32);
encKey.assign(noise_init, 32);
@@ -229,18 +167,24 @@ void WANoise::deriveKey(const void *pData, size_t cbLen, MBinBuffer &write, MBin
read.assign(out + 32, 32);
}
-void WANoise::mixIntoKey(const void *pData, size_t cbLen)
+void WANoise::mixIntoKey(const void *n, const void *p)
{
- deriveKey(pData, cbLen, salt, encKey);
+ uint8_t tmp[32];
+ crypto_scalarmult((unsigned char *)tmp, (const unsigned char *)n, (const unsigned char *)p);
+
+ deriveKey(tmp, sizeof(tmp), salt, encKey);
decKey.assign(encKey.data(), encKey.length());
readCounter = writeCounter = 0;
}
void WANoise::decrypt(const void *pData, size_t cbLen, MBinBuffer &dest)
{
+ auto &pVar = (bInitFinished) ? readCounter : writeCounter;
+
uint8_t iv[12];
memset(iv, 0, 8);
- memcpy(iv + 8, (bInitFinished) ? &readCounter : &writeCounter, sizeof(int));
+ memcpy(iv + 8, &pVar, sizeof(int));
+ pVar++;
uint8_t outbuf[1024 + EVP_MAX_BLOCK_LENGTH];
@@ -249,7 +193,7 @@ void WANoise::decrypt(const void *pData, size_t cbLen, MBinBuffer &dest)
EVP_DecryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, (BYTE *)decKey.data(), iv);
for (size_t len = 0; len < cbLen; len += 1024) {
size_t portionSize = cbLen - len;
- EVP_DecryptUpdate(ctx, outbuf, &dec_len, (BYTE*)pData + len, min(portionSize, 1024));
+ EVP_DecryptUpdate(ctx, outbuf, &dec_len, (BYTE*)pData + len, (int)min(portionSize, 1024));
if (len == 0)
dest.assign(outbuf, dec_len);
else
@@ -259,6 +203,8 @@ void WANoise::decrypt(const void *pData, size_t cbLen, MBinBuffer &dest)
if (final_len)
dest.append(outbuf, final_len);
EVP_CIPHER_CTX_free(ctx);
+
+ updateHash(pData, cbLen);
}
bool WANoise::decodeFrame(const void *pData, size_t cbLen)
@@ -270,25 +216,29 @@ bool WANoise::decodeFrame(const void *pData, size_t cbLen)
auto &static_ = msg.serverhello().static_();
auto &payload = msg.serverhello().payload();
- uint8_t tmp[32];
-
updateHash(ephemeral.c_str(), ephemeral.size());
-
- sharedKey(tmp, privKey.data(), ephemeral.c_str());
- mixIntoKey(tmp, sizeof(tmp));
+ mixIntoKey(privKey.data(), ephemeral.c_str());
MBinBuffer decryptedStatic, decryptedCert;
decrypt(static_.c_str(), static_.size(), decryptedStatic);
-
- sharedKey(tmp, privKey.data(), decryptedStatic.data());
- mixIntoKey(tmp, sizeof(tmp));
+ mixIntoKey(privKey.data(), decryptedStatic.data());
decrypt(payload.c_str(), payload.size(), decryptedCert);
proto::CertChain cert; cert.ParseFromArray(decryptedCert.data(), (int)decryptedCert.length());
proto::CertChain::NoiseCertificate::Details details; details.ParseFromString(cert.intermediate().details());
- if (details.issuerserial() != 0)
+ if (details.issuerserial() != 0) {
+ ppro->ShutdownSession();
return false;
+ }
+
+ MBinBuffer mainPub, mainPriv;
+ ppro->getBlob(DBKEY_PUB_KEY, mainPub);
+ ppro->getBlob(DBKEY_PRIVATE_KEY, mainPriv);
+
+ MBinBuffer encryptedPub;
+ encrypt(mainPub.data(), mainPub.length(), encryptedPub);
+ mixIntoKey(mainPriv.data(), ephemeral.c_str());
}
return true;
}
@@ -315,7 +265,30 @@ void WANoise::encodeFrame(const void *pData, size_t cbLen, MBinBuffer &res)
void WANoise::encrypt(const void *pData, size_t cbLen, MBinBuffer &dest)
{
+ uint8_t iv[12];
+ memset(iv, 0, 8);
+ memcpy(iv + 8, &writeCounter, sizeof(int));
+ writeCounter++;
+
+ uint8_t outbuf[1024 + 64];
+
+ int enc_len = 0, final_len = 0;
+ EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
+ EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, (BYTE *)encKey.data(), iv);
+ for (size_t len = 0; len < cbLen; len += 1024) {
+ size_t portionSize = cbLen - len;
+ EVP_EncryptUpdate(ctx, outbuf, &enc_len, (BYTE *)pData + len, (int)min(portionSize, 1024));
+ if (len == 0)
+ dest.assign(outbuf, enc_len);
+ else
+ dest.append(outbuf, enc_len);
+ }
+ EVP_EncryptFinal_ex(ctx, outbuf, &final_len);
+ if (final_len)
+ dest.append(outbuf, final_len);
+ EVP_CIPHER_CTX_free(ctx);
+ updateHash(dest.data(), dest.length());
}
void WANoise::updateHash(const void *pData, size_t cbLen)