//--------------------------------------------------------------------------- #include #pragma hdrstop // CompThread.pas must be linked to project #include #define THREAD_CLASS TCompThread #else #include #define THREAD_CLASS TThread #endif #include #include "TextsCore.h" #include #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))); }