1
0
mirror of https://github.com/winscp/winscp.git synced 2026-10-24 02:28:55 +03:00
Files
winscp/source/core/Security.cpp
Martin Prikryl d725f98ec4 Issue 618 – Experimental 64-bit version of WinSCP
https://winscp.net/tracker/618

Source commit: 329fb51d696db6c1d37d5c5240ff253af0df941b
2026-05-11 18:39:51 +02:00

237 lines
8.4 KiB
C++

//---------------------------------------------------------------------------
#include <CorePCH.h>
#pragma hdrstop
#include "Security.h"
//---------------------------------------------------------------------------
#define PWALG_SIMPLE_INTERNAL 0x00
#define PWALG_SIMPLE_EXTERNAL 0x01
#define PWALG_SIMPLE_INTERNAL2 0x02
static RawByteString PWALG_SIMPLE_STRING("0123456789ABCDEF");
//---------------------------------------------------------------------------
RawByteString SimpleEncryptChar(unsigned char Ch)
{
Ch = static_cast<unsigned char>((~Ch) ^ PWALG_SIMPLE_MAGIC);
RawByteString Result("..");
Result[1] = PWALG_SIMPLE_STRING[((Ch & 0xF0) >> 4) + 1];
Result[2] = PWALG_SIMPLE_STRING[((Ch & 0x0F) >> 0) + 1];
return Result;
}
//---------------------------------------------------------------------------
unsigned char SimpleDecryptNextChar(RawByteString &Str)
{
if (Str.Length() > 0)
{
unsigned char Result = static_cast<unsigned char>(
~((((PWALG_SIMPLE_STRING.Pos(Str.c_str()[0])-1) << 4) +
((PWALG_SIMPLE_STRING.Pos(Str.c_str()[1])-1) << 0)) ^ PWALG_SIMPLE_MAGIC));
Str.Delete(1, 2);
return Result;
}
else return 0x00;
}
//---------------------------------------------------------------------------
RawByteString EncryptPassword(UnicodeString UnicodePassword, UnicodeString UnicodeKey, Integer /* Algorithm */)
{
UTF8String Password = UnicodePassword;
UTF8String Key = UnicodeKey;
RawByteString Result;
if (!RandSeed) Randomize();
Password = Key + Password;
Result += SimpleEncryptChar(static_cast<unsigned char>(PWALG_SIMPLE_FLAG)); // Flag
int Len = Password.Length();
if (Len > std::numeric_limits<unsigned char>::max())
{
Result += SimpleEncryptChar(static_cast<unsigned char>(PWALG_SIMPLE_INTERNAL2));
Result += SimpleEncryptChar(static_cast<unsigned char>(Len >> 8));
Result += SimpleEncryptChar(static_cast<unsigned char>(Len & 0xFF));
}
else
{
Result += SimpleEncryptChar(static_cast<unsigned char>(PWALG_SIMPLE_INTERNAL));
Result += SimpleEncryptChar(static_cast<unsigned char>(Len));
}
int DataLen =
(Result.Length() / 2) +
1 + // Shift
Password.Length();
int Shift = (DataLen < PWALG_SIMPLE_MAXLEN) ? random(PWALG_SIMPLE_MAXLEN - DataLen) : 0;
Result += SimpleEncryptChar(static_cast<unsigned char>(Shift));
for (int Index = 0; Index < Shift; Index++)
Result += SimpleEncryptChar(static_cast<unsigned char>(random(256)));
for (int Index = 0; Index < Password.Length(); Index++)
Result += SimpleEncryptChar(Password.c_str()[Index]);
while (Result.Length() < PWALG_SIMPLE_MAXLEN * 2)
Result += SimpleEncryptChar(static_cast<unsigned char>(random(256)));
return Result;
}
//---------------------------------------------------------------------------
UnicodeString DecryptPassword(RawByteString Password, UnicodeString UnicodeKey, Integer /* Algorithm */)
{
int Length;
unsigned char Flag = SimpleDecryptNextChar(Password);
if (Flag == PWALG_SIMPLE_FLAG)
{
unsigned char Version = SimpleDecryptNextChar(Password);
if (Version == PWALG_SIMPLE_INTERNAL)
{
Length = SimpleDecryptNextChar(Password);
}
else if (Version == PWALG_SIMPLE_INTERNAL2)
{
Length = (int(SimpleDecryptNextChar(Password)) << 8) + SimpleDecryptNextChar(Password);
}
else
{
Length = -1;
}
}
else
{
Length = Flag;
}
UTF8String Result;
if (Length >= 0)
{
Password.Delete(1, static_cast<Integer>(SimpleDecryptNextChar(Password))*2);
for (int Index = 0; Index < Length; Index++)
{
Result += static_cast<char>(SimpleDecryptNextChar(Password));
}
if (Flag == PWALG_SIMPLE_FLAG)
{
UTF8String Key = UnicodeKey;
if (Result.SubString(1, Key.Length()) != Key)
{
Result = UTF8String();
}
else
{
Result.Delete(1, Key.Length());
}
}
}
return UnicodeString(Result);
}
//---------------------------------------------------------------------------
RawByteString SetExternalEncryptedPassword(RawByteString Password)
{
RawByteString Result;
Result += SimpleEncryptChar(static_cast<unsigned char>(PWALG_SIMPLE_FLAG));
Result += SimpleEncryptChar(static_cast<unsigned char>(PWALG_SIMPLE_EXTERNAL));
Result += UTF8String(BytesToHex(reinterpret_cast<const unsigned char *>(Password.c_str()), Password.Length()));
return Result;
}
//---------------------------------------------------------------------------
bool GetExternalEncryptedPassword(RawByteString Encrypted, RawByteString & Password)
{
bool Result =
(SimpleDecryptNextChar(Encrypted) == PWALG_SIMPLE_FLAG) &&
(SimpleDecryptNextChar(Encrypted) == PWALG_SIMPLE_EXTERNAL);
if (Result)
{
Password = HexToBytes(UTF8ToString(Encrypted));
}
return Result;
}
//---------------------------------------------------------------------------
bool WindowsValidateCertificate(const unsigned char * Certificate, size_t Len, UnicodeString & Error)
{
bool Result = false;
// Parse the certificate into a context.
const CERT_CONTEXT * CertContext =
CertCreateCertificateContext(
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, Certificate, SizeToIntChecked(Len));
if (CertContext == NULL)
{
Error = L"Cannot create certificate context";
}
else
{
CERT_CHAIN_PARA ChainPara;
// Retrieve the certificate chain of the certificate
// (a certificate without a valid root does not have a chain).
memset(&ChainPara, 0, sizeof(ChainPara));
ChainPara.cbSize = sizeof(ChainPara);
CERT_CHAIN_ENGINE_CONFIG ChainConfig;
memset(&ChainConfig, 0, sizeof(ChainConfig));
// The hExclusiveRoot and hExclusiveTrustedPeople were added in Windows 7.
// The CertGetCertificateChain fails with E_INVALIDARG when we include them to ChainConfig.cbSize.
// The dwExclusiveFlags was added in Windows 8
const size_t ChainConfigSize = RTL_SIZEOF_THROUGH_FIELD(CERT_CHAIN_ENGINE_CONFIG, CycleDetectionModulus);
#ifdef _WIN64
DebugAssert(ChainConfigSize == 64);
#else
DebugAssert(ChainConfigSize == 40);
#endif
ChainConfig.cbSize = ChainConfigSize;
ChainConfig.hRestrictedRoot = NULL;
ChainConfig.hRestrictedTrust = NULL;
ChainConfig.hRestrictedOther = NULL;
ChainConfig.cAdditionalStore = 0;
ChainConfig.rghAdditionalStore = NULL;
ChainConfig.dwFlags = CERT_CHAIN_CACHE_END_CERT;
ChainConfig.dwUrlRetrievalTimeout = 0;
ChainConfig.MaximumCachedCertificates =0;
ChainConfig.CycleDetectionModulus = 0;
HCERTCHAINENGINE ChainEngine;
bool ChainEngineResult = CertCreateCertificateChainEngine(&ChainConfig, &ChainEngine);
if (!ChainEngineResult)
{
Error = L"Cannot create certificate chain engine";
}
else
{
const CERT_CHAIN_CONTEXT * ChainContext = NULL;
if (!CertGetCertificateChain(ChainEngine, CertContext, NULL, NULL, &ChainPara,
CERT_CHAIN_CACHE_END_CERT |
CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT,
NULL, &ChainContext))
{
Error = L"Cannot get certificate chain";
}
else
{
CERT_CHAIN_POLICY_PARA PolicyPara;
PolicyPara.cbSize = sizeof(PolicyPara);
PolicyPara.dwFlags = 0;
PolicyPara.pvExtraPolicyPara = NULL;
CERT_CHAIN_POLICY_STATUS PolicyStatus;
PolicyStatus.cbSize = sizeof(PolicyStatus);
if (!CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_SSL, ChainContext, &PolicyPara, &PolicyStatus))
{
Error = L"Cannot verify certificate chain policy";
}
else
{
int PolicyError = PolicyStatus.dwError;
// Windows thinks the certificate is valid.
Result = (PolicyError == S_OK);
if (!Result)
{
UnicodeString ErrorStr = SysErrorMessage(PolicyError);
Error = FORMAT(L"Error: %x (%s), Chain index: %d, Element index: %d", (PolicyError, ErrorStr, PolicyStatus.lChainIndex, PolicyStatus.lElementIndex));
}
}
CertFreeCertificateChain(ChainContext);
}
CertFreeCertificateChainEngine(ChainEngine);
}
CertFreeCertificateContext(CertContext);
}
return Result;
}
//---------------------------------------------------------------------------