1
0
mirror of https://github.com/winscp/winscp.git synced 2026-10-24 02:28:55 +03:00
Files
winscp/source/core/KeyGen.cpp
Martin Prikryl dc4a08d325 USE_COMPATIBLE_THREAD directive does not exist anymore
Source commit: 70e88ebf4bece852cf5a8f217d3d4d036d1a0760
2019-04-25 01:13:09 +02:00

394 lines
11 KiB
C++

//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
// CompThread.pas must be linked to project
#include <CompThread.hpp>
#define THREAD_CLASS TCompThread
#else
#include <Classes.hpp>
#define THREAD_CLASS TThread
#endif
#include <Common.h>
#include "TextsCore.h"
#include <PuttyIntf.h>
#include "KeyGen.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
//---------------------------------------------------------------------------
extern "C" void KeyGenerationProgressUpdate(void * Thread,
int Action, int Phase, int IProgress);
//---------------------------------------------------------------------------
class TKeyGenerationThread : public THREAD_CLASS
{
public:
#define PROGRESSRANGE 65535
#define MAXPHASE 5
struct
{
int NPhases;
struct
{
bool Exponential;
unsigned StartPoint, Total;
unsigned Param, Current, N; /* if exponential */
unsigned Mult; /* if linear */
} Phases[MAXPHASE];
unsigned Total, Divisor, Range;
unsigned Position;
TKeyGenerationComplete Complete;
} Progress;
TKeyGenerator * FGenerator;
__fastcall TKeyGenerationThread(TKeyGenerator * AGenerator):
THREAD_CLASS(true)
{
FGenerator = AGenerator;
Resume();
}
void __fastcall DistributeProgressUpdate()
{
FGenerator->ProgressUpdate(Progress.Range, Progress.Position, Progress.Complete);
}
void __fastcall ProgressUpdate(int Action, int Phase, unsigned IProgress)
{
int Position;
if (Action < PROGFN_READY && Progress.NPhases < Phase)
Progress.NPhases = Phase;
switch (Action)
{
case PROGFN_INITIALISE:
Progress.NPhases = 0;
Progress.Complete = kgInProgress;
break;
case PROGFN_LIN_PHASE:
Progress.Phases[Phase-1].Exponential = false;
Progress.Phases[Phase-1].Mult = Progress.Phases[Phase].Total / IProgress;
break;
case PROGFN_EXP_PHASE:
Progress.Phases[Phase-1].Exponential = true;
Progress.Phases[Phase-1].Param = 0x10000 + IProgress;
Progress.Phases[Phase-1].Current = Progress.Phases[Phase-1].Total;
Progress.Phases[Phase-1].N = 0;
break;
case PROGFN_PHASE_EXTENT:
Progress.Phases[Phase-1].Total = IProgress;
break;
case PROGFN_READY:
{
unsigned Total = 0;
int i;
for (i = 0; i < Progress.NPhases; i++)
{
Progress.Phases[i].StartPoint = Total;
Total += Progress.Phases[i].Total;
}
Progress.Total = Total;
Progress.Divisor = ((Progress.Total + PROGRESSRANGE - 1) / PROGRESSRANGE);
Progress.Range = Progress.Total / Progress.Divisor;
Synchronize(DistributeProgressUpdate);
}
break;
case PROGFN_PROGRESS:
if (Progress.Phases[Phase-1].Exponential)
{
while (Progress.Phases[Phase-1].N < IProgress)
{
Progress.Phases[Phase-1].N++;
Progress.Phases[Phase-1].Current *= Progress.Phases[Phase-1].Param;
Progress.Phases[Phase-1].Current /= 0x10000;
}
Position = (Progress.Phases[Phase-1].StartPoint +
Progress.Phases[Phase-1].Total - Progress.Phases[Phase-1].Current);
}
else
{
Position = (Progress.Phases[Phase-1].StartPoint +
IProgress * Progress.Phases[Phase-1].Mult);
}
Progress.Position = Position / Progress.Divisor;
Synchronize(DistributeProgressUpdate);
break;
}
}
virtual void __fastcall Execute()
{
try
{
ProgressUpdate(PROGFN_INITIALISE, 0, 0);
if (FGenerator->KeyType == ktDSA)
{
dsa_generate(FGenerator->FDSSKey, FGenerator->KeySize,
KeyGenerationProgressUpdate, this);
}
else
{
rsa_generate(FGenerator->FRSAKey, FGenerator->KeySize,
KeyGenerationProgressUpdate, this);
}
Progress.Complete = kgSuccess;
}
catch(...)
{
Progress.Complete = kgFailure;
}
Synchronize(DistributeProgressUpdate);
}
#pragma warn +lvc
};
//---------------------------------------------------------------------------
void KeyGenerationProgressUpdate(void * Thread,
int Action, int Phase, int IProgress)
{
DebugAssert(Thread);
((TKeyGenerationThread*)Thread)->ProgressUpdate(Action, Phase, IProgress);
}
//---------------------------------------------------------------------------
__fastcall TKeyGenerator::TKeyGenerator():
TObject()
{
FSSH2Key = new ssh2_userkey;
FRSAKey = new RSAKey;
FDSSKey = new dss_key;
FEntropy = NULL;
FState = kgInitializing;
FOnGenerating = NULL;
FGenerationRange = -1;
KeyType = ktRSA2;
KeySize = 1024;
}
//---------------------------------------------------------------------------
__fastcall TKeyGenerator::~TKeyGenerator()
{
DebugAssert(FState != kgGenerating);
if (FEntropy) delete FEntropy;
delete FSSH2Key;
delete FRSAKey;
delete FDSSKey;
}
//---------------------------------------------------------------------------
void __fastcall TKeyGenerator::SetKeySize(int value)
{
DebugAssert(FState != kgGenerating);
FState = kgInitializing;
FKeySize = value;
FEntropyRequired = (KeySize / 2) * 2;
FEntropyGot = 0;
if (FEntropy) delete FEntropy;
FEntropy = new TEntropyBit[FEntropyRequired];
}
//---------------------------------------------------------------------------
void __fastcall TKeyGenerator::SetKeyType(TKeyType value)
{
DebugAssert(FState != kgGenerating);
FState = kgInitializing;
FKeyType = value;
}
//---------------------------------------------------------------------------
void __fastcall TKeyGenerator::AddEntropy(TEntropyBit Entropy)
{
DebugAssert(FState == kgInitializing);
DebugAssert(FEntropy);
DebugAssert(FEntropyGot < FEntropyRequired);
FEntropy[FEntropyGot++] = Entropy;
if (FEntropyGot == FEntropyRequired)
{
FState = kgInitialized;
random_add_heavynoise(FEntropy, FEntropyRequired * sizeof(TEntropyBit));
delete FEntropy;
FEntropy = NULL;
}
}
//---------------------------------------------------------------------------
void __fastcall TKeyGenerator::ResetKey()
{
DebugAssert(FSSH2Key);
switch (KeyType) {
case ktDSA:
FSSH2Key->data = FDSSKey;
FSSH2Key->alg = &ssh_dss;
break;
case ktRSA2:
FSSH2Key->data = FRSAKey;
FSSH2Key->alg = &ssh_rsa;
break;
}
FFingerprint = "";
FPublicKey = "";
if (Comment.IsEmpty())
{
Comment = FORMAT("%s-key-%s",
((KeyType == ktDSA ? "dsa" : "rsa"), FormatDateTime("yyyymmdd", Now())));
}
}
//---------------------------------------------------------------------------
void __fastcall TKeyGenerator::StartGenerationThread()
{
DebugAssert(FState == kgInitialized);
FState = kgGenerating;
new TKeyGenerationThread(this);
}
//---------------------------------------------------------------------------
void __fastcall TKeyGenerator::Generate()
{
DebugAssert(FState == kgInitialized);
THREAD_CLASS * Thread;
FState = kgGenerating;
Thread = new TKeyGenerationThread(this);
Thread->WaitFor();
}
//---------------------------------------------------------------------------
void __fastcall TKeyGenerator::ProgressUpdate(int Range, int Position,
TKeyGenerationComplete Complete)
{
DebugAssert(FState == kgGenerating);
if (Complete == kgSuccess)
{
FState = kgComplete;
ResetKey();
}
FGenerationRange = Range;
FGenerationPosition = Position;
if (FOnGenerating) FOnGenerating(this, Range, Position, Complete);
}
//---------------------------------------------------------------------------
int __fastcall TKeyGenerator::GetPercentGenerated()
{
switch (FState) {
case kgComplete: return 100;
case kgGenerating: return (FGenerationPosition * 100) / FGenerationRange;
default: return 0;
}
}
//---------------------------------------------------------------------------
void __fastcall TKeyGenerator::SetComment(AnsiString value)
{
if (FComment != value)
{
FComment = value;
FPublicKey = "";
DebugAssert(FSSH2Key);
FSSH2Key->comment = FComment.c_str();
FRSAKey->comment = FComment.c_str();
}
}
//---------------------------------------------------------------------------
AnsiString __fastcall TKeyGenerator::GetFingerprint()
{
if (FFingerprint.IsEmpty())
{
if (IsSSH2)
{
DebugAssert(FSSH2Key);
FFingerprint = FSSH2Key->alg->fingerprint(FSSH2Key->data);
}
else
{
char Buf[128];
rsa_fingerprint(Buf, sizeof(Buf), FRSAKey);
FFingerprint = Buf;
}
}
return FFingerprint;
}
//---------------------------------------------------------------------------
bool __fastcall TKeyGenerator::GetIsSSH2()
{
return (KeyType == ktDSA || KeyType == ktRSA2);
}
//---------------------------------------------------------------------------
AnsiString __fastcall TKeyGenerator::GetPublicKey()
{
if (FPublicKey.IsEmpty())
{
unsigned char *pub_blob;
char *buffer, *p;
int pub_len;
int i;
DebugAssert(FSSH2Key);
pub_blob = FSSH2Key->alg->public_blob(FSSH2Key->data, &pub_len);
buffer = new char[4 * ((pub_len + 2) / 3) + 1];
p = buffer;
i = 0;
while (i < pub_len)
{
int n = (pub_len - i < 3 ? pub_len - i : 3);
base64_encode_atom(pub_blob + i, n, p);
i += n;
p += 4;
}
*p = '\0';
FPublicKey = buffer;
sfree(pub_blob);
delete [] buffer;
}
return FPublicKey;
}
//---------------------------------------------------------------------------
AnsiString __fastcall TKeyGenerator::GetAuthorizedKeysLine()
{
DebugAssert(FSSH2Key);
return FORMAT("%s %s %s", (FSSH2Key->alg->name, PublicKey, Comment));
}
//---------------------------------------------------------------------------
void __fastcall TKeyGenerator::SaveKey(const AnsiString FileName,
const AnsiString Passphrase, TKeyFormat Format)
{
DebugAssert(FSSH2Key);
DebugAssert(FState == kgComplete);
DebugAssert((Format != kfOpenSSH && Format != kfSSHCom) || IsSSH2);
int Result;
if (IsSSH2)
{
switch (Format)
{
case kfPutty:
Result = ssh2_save_userkey(FileName.c_str(), FSSH2Key,
(char*)Passphrase.data());
break;
case kfOpenSSH:
Result = export_ssh2(FileName.c_str(), SSH_KEYTYPE_OPENSSH, FSSH2Key,
(char*)Passphrase.data());
break;
case kfSSHCom:
Result = export_ssh2(FileName.c_str(), SSH_KEYTYPE_SSHCOM, FSSH2Key,
(char*)Passphrase.data());
break;
default:
DebugFail();
}
}
else
{
DebugAssert(Format == kfPutty);
Result = saversakey(FileName.c_str(), FRSAKey,
(char*)Passphrase.data());
}
if (Result <= 0)
throw Exception(FMTLOAD(SAVE_KEY_ERROR, (FileName)));
}