From 82c5b215999c29361f26107e67a08a5ec7c64872 Mon Sep 17 00:00:00 2001
From: George Hazan <george.hazan@gmail.com>
Date: Sun, 18 Jun 2023 12:21:15 +0300
Subject: Steam: "Device name" option

---
 protocols/Steam/src/resource.h         |   9 +--
 protocols/Steam/src/steam_contacts.cpp |   4 +-
 protocols/Steam/src/steam_crypt.cpp    | 108 ++++-----------------------------
 protocols/Steam/src/steam_events.cpp   |   2 -
 protocols/Steam/src/steam_login.cpp    |  43 ++++---------
 protocols/Steam/src/steam_options.cpp  |  23 ++-----
 protocols/Steam/src/steam_proto.cpp    |   9 +--
 protocols/Steam/src/steam_proto.h      |  13 ++--
 8 files changed, 49 insertions(+), 162 deletions(-)

(limited to 'protocols/Steam/src')

diff --git a/protocols/Steam/src/resource.h b/protocols/Steam/src/resource.h
index db1260e482..639b4f013f 100644
--- a/protocols/Steam/src/resource.h
+++ b/protocols/Steam/src/resource.h
@@ -1,6 +1,6 @@
 //{{NO_DEPENDENCIES}}
 // Microsoft Visual C++ generated include file.
-// Used by D:\Projects\c++\miranda-ng\protocols\Steam\res\Resource.rc
+// Used by W:\miranda-ng\protocols\Steam\res\Resource.rc
 //
 #define IDD_ACCMGR                      9
 #define IDD_OPT_MAIN                    10
@@ -20,8 +20,9 @@
 #define IDC_SL                          1003
 #define IDC_USERNAME                    1003
 #define IDC_SAVEPASS                    1004
-#define IDC_PROTOPIC                    1004
-#define IDC_CAPTCHA                     1004
+#define IDC_PROTOPIC                    1005
+#define IDC_CAPTCHA                     1006
+#define IDC_DEVICE_NAME                 1007
 #define IDC_REGISTER                    1018
 #define IDC_DEFAULT_GROUP               1020
 #define IDC_GROUP                       1021
@@ -41,7 +42,7 @@
 // 
 #ifdef APSTUDIO_INVOKED
 #ifndef APSTUDIO_READONLY_SYMBOLS
-#define _APS_NEXT_RESOURCE_VALUE        126
+#define _APS_NEXT_RESOURCE_VALUE        127
 #define _APS_NEXT_COMMAND_VALUE         40001
 #define _APS_NEXT_CONTROL_VALUE         1087
 #define _APS_NEXT_SYMED_VALUE           101
diff --git a/protocols/Steam/src/steam_contacts.cpp b/protocols/Steam/src/steam_contacts.cpp
index 378ab1933d..7f7979e825 100644
--- a/protocols/Steam/src/steam_contacts.cpp
+++ b/protocols/Steam/src/steam_contacts.cpp
@@ -346,7 +346,9 @@ MCONTACT CSteamProto::AddContact(const char *steamId, const wchar_t *nick, bool
 	setByte(hContact, "Auth", 1);
 
 	// move to default group
-	Clist_SetGroup(hContact, m_defaultGroup);
+	if (!Clist_GroupExists(m_wszGroupName))
+		Clist_GroupCreate(0, m_wszGroupName);
+	Clist_SetGroup(hContact, m_wszGroupName);
 
 	return hContact;
 }
diff --git a/protocols/Steam/src/steam_crypt.cpp b/protocols/Steam/src/steam_crypt.cpp
index 042e979cc5..e89dd46a34 100644
--- a/protocols/Steam/src/steam_crypt.cpp
+++ b/protocols/Steam/src/steam_crypt.cpp
@@ -1,104 +1,22 @@
 #include "stdafx.h"
 
-#pragma comment(lib, "crypt32.lib")
+#include <openssl/rsa.h>
 
-int CSteamProto::RsaEncrypt(const char *pszModulus, DWORD &exponent, const char *data, uint8_t *encryptedData, DWORD &encryptedSize)
+MBinBuffer RsaEncrypt(const char *pszModulus, const char *exponent, const char *data)
 {
-	uint32_t cchModulus = (uint32_t)mir_strlen(pszModulus);
-	int result;
-	HCRYPTKEY phKey = 0;
-	HCRYPTPROV hCSP = 0;
+	//pszModulus = "cb23284f3078f97f9667624b4f882cd7d68aefd6b22f136b5808dfc3ae19d6df7f278d71049a4d61d2bedb4fe958e84140e3ba261b80cf29b37d2aca3ab456cf26fbeca4eded69d51982b38f9ec1003c3e41b22c757150736d2df976908abfdc5da7c9bbc5f4626f6752c41141534867d14cddc4e4278aa456824bfe131880aaf17a125569a365f802af859107a9e916e2442ceff6ff2feb77c6dc0b87639c9f1b34f681f2383a599f8dca8f6558cc60cdb5318fe58888604d4b66ab5175e0dadf1deb499937cb090094adb46b52752954ffc915fbbf41999bb5c301c40e1f1a6e45c23bb10529a356d753ee0d42003d82bf3e5eb5556fa27e394780034dcf5b";
 
-	__try {
-		// convert hex string to byte array
-		DWORD cbLen = 0, dwSkip = 0, dwFlags = 0;
-		if (!CryptStringToBinaryA(pszModulus, cchModulus, CRYPT_STRING_HEX, nullptr, &cbLen, &dwSkip, &dwFlags)) {
-			result = GetLastError();
-			__leave;
-		}
+	BIGNUM *N = BN_new(), *E = BN_new();
+	BN_hex2bn(&N, pszModulus);
+	BN_hex2bn(&E, exponent);
 
-		// allocate a new buffer.
-		mir_ptr<uint8_t> pbBuffer((uint8_t *)mir_alloc(cbLen));
-		if (!CryptStringToBinaryA(pszModulus, cchModulus, CRYPT_STRING_HEX, pbBuffer, &cbLen, &dwSkip, &dwFlags)) {
-			result = GetLastError();
-			__leave;
-		}
+	auto rsa = RSA_new();
+	RSA_set0_key(rsa, N, E, NULL);
 
-		// reverse byte array, because of microsoft
-		for (int i = 0; i < (int)(cbLen / 2); ++i) {
-			uint8_t temp = pbBuffer[cbLen - i - 1];
-			pbBuffer[cbLen - i - 1] = pbBuffer[i];
-			pbBuffer[i] = temp;
-		}
+	MBinBuffer ret(RSA_size(rsa));
+	memset(ret.data(), 0, ret.length());
+	RSA_public_encrypt((int)mir_strlen(data), (BYTE*)data, ret.data(), rsa, RSA_PKCS1_PADDING);
 
-		if (!CryptAcquireContext(&hCSP, nullptr, nullptr, PROV_RSA_AES, CRYPT_SILENT) &&
-			!CryptAcquireContext(&hCSP, nullptr, nullptr, PROV_RSA_AES, CRYPT_SILENT | CRYPT_NEWKEYSET)) {
-			result = GetLastError();
-			__leave;
-		}
-
-		// Move the key into the key container.
-		uint32_t cbKeyBlob = sizeof(PUBLICKEYSTRUC) + sizeof(RSAPUBKEY) + cbLen;
-		mir_ptr<uint8_t> pKeyBlob((uint8_t *)mir_alloc(cbKeyBlob));
-
-		// Fill in the data.
-		PUBLICKEYSTRUC *pPublicKey = (PUBLICKEYSTRUC *)pKeyBlob.get();
-		pPublicKey->bType = PUBLICKEYBLOB;
-		pPublicKey->bVersion = CUR_BLOB_VERSION;  // Always use this value.
-		pPublicKey->reserved = 0;                 // Must be zero.
-		pPublicKey->aiKeyAlg = CALG_RSA_KEYX;     // RSA public-key key exchange. 
-
-		// The next block of data is the RSAPUBKEY structure.
-		RSAPUBKEY *pRsaPubKey = (RSAPUBKEY *)(pKeyBlob + sizeof(PUBLICKEYSTRUC));
-		pRsaPubKey->magic = 0x31415352; // RSA1 // Use public key
-		pRsaPubKey->bitlen = cbLen * 8;  // Number of bits in the modulus.
-		pRsaPubKey->pubexp = exponent; // Exponent.
-
-		// Copy the modulus into the blob. Put the modulus directly after the
-		// RSAPUBKEY structure in the blob.
-		uint8_t *pKey = (uint8_t *)(((uint8_t *)pRsaPubKey) + sizeof(RSAPUBKEY));
-		memcpy(pKey, pbBuffer, cbLen);
-
-		// Now import public key	
-		if (!CryptImportKey(hCSP, pKeyBlob, cbKeyBlob, 0, 0, &phKey)) {
-			result = GetLastError();
-			__leave;
-		}
-
-		DWORD dataSize = (DWORD)mir_strlen(data);
-
-		// if data is not allocated just renurn size
-		if (encryptedData == nullptr) {
-			// get length of encrypted data
-			if (!CryptEncrypt(phKey, 0, TRUE, 0, nullptr, &encryptedSize, dataSize))
-				result = GetLastError();
-			__leave;
-		}
-
-		// encrypt password
-		memcpy(encryptedData, data, dataSize);
-		if (!CryptEncrypt(phKey, 0, TRUE, 0, encryptedData, &dataSize, encryptedSize)) {
-			result = GetLastError();
-			__leave;
-		}
-
-		// reverse byte array again
-		for (int i = 0; i < (int)(encryptedSize / 2); ++i) {
-			uint8_t temp = encryptedData[encryptedSize - i - 1];
-			encryptedData[encryptedSize - i - 1] = encryptedData[i];
-			encryptedData[i] = temp;
-		}
-	}
-	__finally
-	{
-		result = 0;
-	};
-
-	if (phKey)
-		CryptDestroyKey(phKey);
-
-	if (hCSP)
-		CryptReleaseContext(hCSP, 0);
-
-	return 0;
+	RSA_free(rsa);
+	return ret;
 }
diff --git a/protocols/Steam/src/steam_events.cpp b/protocols/Steam/src/steam_events.cpp
index 1db7ae6480..7c379448c7 100644
--- a/protocols/Steam/src/steam_events.cpp
+++ b/protocols/Steam/src/steam_events.cpp
@@ -2,8 +2,6 @@
 
 void CSteamProto::OnModulesLoaded()
 {
-	Clist_GroupCreate(0, m_defaultGroup);
-
 	HookProtoEvent(ME_IDLE_CHANGED, &CSteamProto::OnIdleChanged);
 	HookProtoEvent(ME_MSG_PRECREATEEVENT, &CSteamProto::OnPreCreateMessage);
 
diff --git a/protocols/Steam/src/steam_login.cpp b/protocols/Steam/src/steam_login.cpp
index 6bf3bfb89e..ce235b635c 100644
--- a/protocols/Steam/src/steam_login.cpp
+++ b/protocols/Steam/src/steam_login.cpp
@@ -138,13 +138,6 @@ void CSteamProto::Login()
 	hello.protocol_version = STEAM_PROTOCOL_VERSION; hello.has_protocol_version = true;
 	WSSend(EMsg::ClientHello, hello);
 
-	ptrA token(getStringA("TokenSecret"));
-	ptrA sessionId(getStringA("SessionID"));
-	if (mir_strlen(token) > 0 && mir_strlen(sessionId) > 0) {
-		// SendRequest(new LogonRequest(token), &CSteamProto::OnLoggedOn);
-		return;
-	}
-
 	ptrA username(getUStringA("Username"));
 	if (username == NULL)
 		LoginFailed();
@@ -193,48 +186,34 @@ void CSteamProto::OnGotRsaKey(const uint8_t *buf, size_t cbLen)
 		return;
 	}
 
-	// load rsa key parts
-	DWORD exponent = strtoul(reply->publickey_exp, nullptr, 16); // default "010001" = 0x10001
-
 	// encrypt password
 	ptrA szPassword(getStringA("Password"));
 
-	DWORD error = 0;
-	DWORD encryptedSize = 0;
-	if ((error = RsaEncrypt(reply->publickey_mod, exponent, szPassword, nullptr, encryptedSize)) != 0) {
-		debugLogA(__FUNCTION__ ": encryption error (%lu)", error);
-		SetStatus(ID_STATUS_OFFLINE);
-		return;
-	}
-
-	uint8_t *encryptedPassword = (uint8_t *)mir_calloc(encryptedSize);
-	if ((error = RsaEncrypt(reply->publickey_mod, exponent, szPassword, encryptedPassword, encryptedSize)) != 0) {
-		debugLogA(__FUNCTION__ ": encryption error (%lu)", error);
-		SetStatus(ID_STATUS_OFFLINE);
-		return;
-	}
-
-	ptrA base64RsaEncryptedPassword(mir_base64_encode(encryptedPassword, encryptedSize));
-	mir_free(encryptedPassword);
+	MBinBuffer encPassword(RsaEncrypt(reply->publickey_mod, reply->publickey_exp, szPassword));
+	ptrA base64RsaEncryptedPassword(mir_base64_encode(encPassword.data(), encPassword.length()));
 
 	// run authorization request
 	ptrA userName(getUStringA("Username"));
-	ptrA deviceName(getUStringA("DeviceName"));
+	T2Utf deviceName(m_wszDeviceName);
 	
 	CAuthenticationDeviceDetails details;
 	details.device_friendly_name = deviceName.get();
-	details.os_type = 1; details.has_os_type = true;
+	details.os_type = 16; details.has_os_type = true;
 	details.platform_type = EAUTH_TOKEN_PLATFORM_TYPE__k_EAuthTokenPlatformType_SteamClient; details.has_platform_type = true;
 
 	CAuthenticationBeginAuthSessionViaCredentialsRequest request;
 	request.account_name = userName.get();
-	request.device_friendly_name = deviceName.get();
+	request.website_id = "Client";
 	request.encrypted_password = base64RsaEncryptedPassword;
 	request.encryption_timestamp = reply->timestamp; request.has_encryption_timestamp = true;
-	request.persistence = ESESSION_PERSISTENCE__k_ESessionPersistence_Ephemeral; request.has_persistence = true;
-	request.platform_type = EAUTH_TOKEN_PLATFORM_TYPE__k_EAuthTokenPlatformType_SteamClient; request.has_platform_type = true;
+	request.persistence = ESESSION_PERSISTENCE__k_ESessionPersistence_Persistent; request.has_persistence = true;
 	request.remember_login = 0; request.has_remember_login = true;
+	request.language = 1; request.has_language = true;
+	request.qos_level = 2; request.has_qos_level = true;
+
 	request.device_details = &details;
+	request.device_friendly_name = details.device_friendly_name;
+	request.platform_type = details.platform_type; request.has_platform_type = true;
 
 	WSSendService("Authentication.BeginAuthSessionViaCredentials#1", request, &CSteamProto::OnAuthorization);
 }
diff --git a/protocols/Steam/src/steam_options.cpp b/protocols/Steam/src/steam_options.cpp
index 2e52b6da46..40dadd20ff 100644
--- a/protocols/Steam/src/steam_options.cpp
+++ b/protocols/Steam/src/steam_options.cpp
@@ -2,16 +2,17 @@
 
 class CSteamOptionsMain : public CSteamDlgBase
 {
-	CCtrlEdit m_username, m_password, m_group;
+	CCtrlEdit m_username, m_password, m_group, m_deviceName;
 	CCtrlCheck m_biggerAvatars, m_showChatEvents;
 	CCtrlSpin m_pollingErrorLimit;
 
 public:
 	CSteamOptionsMain(CSteamProto *proto, int idDialog, HWND hwndParent = NULL) :
 		CSteamDlgBase(proto, idDialog),
+		m_group(this, IDC_GROUP),
 		m_username(this, IDC_USERNAME),
 		m_password(this, IDC_PASSWORD),
-		m_group(this, IDC_GROUP),
+		m_deviceName(this, IDC_DEVICE_NAME),		
 		m_biggerAvatars(this, IDC_BIGGER_AVATARS),
 		m_showChatEvents(this, IDC_SHOW_CHAT_EVENTS),
 		m_pollingErrorLimit(this, IDC_POLLINGERRORLIMITSPIN, 255)
@@ -20,7 +21,8 @@ public:
 
 		CreateLink(m_username, "Username", L"");
 		CreateLink(m_password, "Password", L"");
-		CreateLink(m_group, "DefaultGroup", L"Steam");
+		CreateLink(m_group, m_proto->m_wszGroupName);
+		CreateLink(m_deviceName, m_proto->m_wszDeviceName);
 
 		if (idDialog == IDD_OPT_MAIN) {
 			CreateLink(m_biggerAvatars, "UseBigAvatars", DBVT_BYTE, FALSE);
@@ -41,21 +43,8 @@ public:
 
 	bool OnApply() override
 	{
-		ptrW group(m_group.GetText());
-		if (mir_wstrcmp(group, m_proto->m_defaultGroup)) {
-			m_proto->m_defaultGroup = mir_wstrdup(group);
-			Clist_GroupCreate(0, group);
-		}
-
-		if (m_proto->IsOnline()) // may be we should show message box with warning?
-			m_proto->SetStatus(ID_STATUS_OFFLINE);
-
-		if (m_username.IsChanged()) {
+		if (m_username.IsChanged())
 			m_proto->delSetting(DBKEY_STEAM_ID);
-			m_proto->delSetting("TokenSecret");
-		}
-		if (m_password.IsChanged())
-			m_proto->delSetting("TokenSecret");
 		return true;
 	}
 };
diff --git a/protocols/Steam/src/steam_proto.cpp b/protocols/Steam/src/steam_proto.cpp
index e57c2af8c2..5475a4368e 100644
--- a/protocols/Steam/src/steam_proto.cpp
+++ b/protocols/Steam/src/steam_proto.cpp
@@ -9,13 +9,10 @@ static int CompareRequests(const ProtoRequest *p1, const ProtoRequest *p2)
 
 CSteamProto::CSteamProto(const char *protoName, const wchar_t *userName) :
 	PROTO<CSteamProto>(protoName, userName),
-	m_arRequests(10, CompareRequests)
+	m_arRequests(10, CompareRequests),
+	m_wszGroupName(this, "DefaultGroup", L"Steam"),
+	m_wszDeviceName(this, "DeviceName", L"Miranda NG")
 {
-	// default group
-	m_defaultGroup = getWStringA("DefaultGroup");
-	if (m_defaultGroup == nullptr)
-		m_defaultGroup = mir_wstrdup(L"Steam");
-
 	// icons
 	wchar_t filePath[MAX_PATH];
 	GetModuleFileName(g_plugin.getInst(), filePath, MAX_PATH);
diff --git a/protocols/Steam/src/steam_proto.h b/protocols/Steam/src/steam_proto.h
index 8edf2c788a..8b93aa0e7f 100644
--- a/protocols/Steam/src/steam_proto.h
+++ b/protocols/Steam/src/steam_proto.h
@@ -68,7 +68,6 @@ class CSteamProto : public PROTO<CSteamProto>
 	friend class PollRequest;
 
 	ptrW m_password;
-	ptrW m_defaultGroup;
 	bool m_bTerminated;
 	HWND m_hwndGuard;
 	time_t m_idleTS;
@@ -218,9 +217,7 @@ class CSteamProto : public PROTO<CSteamProto>
 	// utils
 	static uint16_t SteamToMirandaStatus(PersonaState state);
 	static PersonaState MirandaToSteamState(int status);
-
-	static int RsaEncrypt(const char *pszModulus, DWORD &exponent, const char *data, uint8_t *encrypted, DWORD &encryptedSize);
-
+		
 	static void ShowNotification(const wchar_t *message, int flags = 0, MCONTACT hContact = NULL);
 	static void ShowNotification(const wchar_t *caption, const wchar_t *message, int flags = 0, MCONTACT hContact = NULL);
 
@@ -258,10 +255,14 @@ class CSteamProto : public PROTO<CSteamProto>
 	}
 
 public:
-	// PROTO_INTERFACE
+	// constructor
 	CSteamProto(const char *protoName, const wchar_t *userName);
 	~CSteamProto();
 
+	// options
+	CMOption<wchar_t*> m_wszGroupName;   // default group for this account's contacts
+	CMOption<wchar_t*> m_wszDeviceName;  // how do you see this account in the Device List
+
 	// PROTO_INTERFACE
 	MCONTACT AddToList(int flags, PROTOSEARCHRESULT *psr) override;
 	MCONTACT AddToListByEvent(int flags, int iContact, MEVENT hDbEvent) override;
@@ -301,4 +302,6 @@ struct CMPlugin : public ACCPROTOPLUGIN<CSteamProto>
 int OnReloadIcons(WPARAM wParam, LPARAM lParam);
 void SetContactExtraIcon(MCONTACT hContact, int status);
 
+MBinBuffer RsaEncrypt(const char *pszModulus, const char *exponent, const char *data);
+
 #endif //_STEAM_PROTO_H_
-- 
cgit v1.2.3