KeyGen.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393
  1. //---------------------------------------------------------------------------
  2. #include <vcl.h>
  3. #pragma hdrstop
  4. // CompThread.pas must be linked to project
  5. #include <CompThread.hpp>
  6. #define THREAD_CLASS TCompThread
  7. #else
  8. #include <Classes.hpp>
  9. #define THREAD_CLASS TThread
  10. #endif
  11. #include <Common.h>
  12. #include "TextsCore.h"
  13. #include <PuttyIntf.h>
  14. #include "KeyGen.h"
  15. //---------------------------------------------------------------------------
  16. #pragma package(smart_init)
  17. //---------------------------------------------------------------------------
  18. extern "C" void KeyGenerationProgressUpdate(void * Thread,
  19. int Action, int Phase, int IProgress);
  20. //---------------------------------------------------------------------------
  21. class TKeyGenerationThread : public THREAD_CLASS
  22. {
  23. public:
  24. #define PROGRESSRANGE 65535
  25. #define MAXPHASE 5
  26. struct
  27. {
  28. int NPhases;
  29. struct
  30. {
  31. bool Exponential;
  32. unsigned StartPoint, Total;
  33. unsigned Param, Current, N; /* if exponential */
  34. unsigned Mult; /* if linear */
  35. } Phases[MAXPHASE];
  36. unsigned Total, Divisor, Range;
  37. unsigned Position;
  38. TKeyGenerationComplete Complete;
  39. } Progress;
  40. TKeyGenerator * FGenerator;
  41. __fastcall TKeyGenerationThread(TKeyGenerator * AGenerator):
  42. THREAD_CLASS(true)
  43. {
  44. FGenerator = AGenerator;
  45. Resume();
  46. }
  47. void __fastcall DistributeProgressUpdate()
  48. {
  49. FGenerator->ProgressUpdate(Progress.Range, Progress.Position, Progress.Complete);
  50. }
  51. void __fastcall ProgressUpdate(int Action, int Phase, unsigned IProgress)
  52. {
  53. int Position;
  54. if (Action < PROGFN_READY && Progress.NPhases < Phase)
  55. Progress.NPhases = Phase;
  56. switch (Action)
  57. {
  58. case PROGFN_INITIALISE:
  59. Progress.NPhases = 0;
  60. Progress.Complete = kgInProgress;
  61. break;
  62. case PROGFN_LIN_PHASE:
  63. Progress.Phases[Phase-1].Exponential = false;
  64. Progress.Phases[Phase-1].Mult = Progress.Phases[Phase].Total / IProgress;
  65. break;
  66. case PROGFN_EXP_PHASE:
  67. Progress.Phases[Phase-1].Exponential = true;
  68. Progress.Phases[Phase-1].Param = 0x10000 + IProgress;
  69. Progress.Phases[Phase-1].Current = Progress.Phases[Phase-1].Total;
  70. Progress.Phases[Phase-1].N = 0;
  71. break;
  72. case PROGFN_PHASE_EXTENT:
  73. Progress.Phases[Phase-1].Total = IProgress;
  74. break;
  75. case PROGFN_READY:
  76. {
  77. unsigned Total = 0;
  78. int i;
  79. for (i = 0; i < Progress.NPhases; i++)
  80. {
  81. Progress.Phases[i].StartPoint = Total;
  82. Total += Progress.Phases[i].Total;
  83. }
  84. Progress.Total = Total;
  85. Progress.Divisor = ((Progress.Total + PROGRESSRANGE - 1) / PROGRESSRANGE);
  86. Progress.Range = Progress.Total / Progress.Divisor;
  87. Synchronize(DistributeProgressUpdate);
  88. }
  89. break;
  90. case PROGFN_PROGRESS:
  91. if (Progress.Phases[Phase-1].Exponential)
  92. {
  93. while (Progress.Phases[Phase-1].N < IProgress)
  94. {
  95. Progress.Phases[Phase-1].N++;
  96. Progress.Phases[Phase-1].Current *= Progress.Phases[Phase-1].Param;
  97. Progress.Phases[Phase-1].Current /= 0x10000;
  98. }
  99. Position = (Progress.Phases[Phase-1].StartPoint +
  100. Progress.Phases[Phase-1].Total - Progress.Phases[Phase-1].Current);
  101. }
  102. else
  103. {
  104. Position = (Progress.Phases[Phase-1].StartPoint +
  105. IProgress * Progress.Phases[Phase-1].Mult);
  106. }
  107. Progress.Position = Position / Progress.Divisor;
  108. Synchronize(DistributeProgressUpdate);
  109. break;
  110. }
  111. }
  112. virtual void __fastcall Execute()
  113. {
  114. try
  115. {
  116. ProgressUpdate(PROGFN_INITIALISE, 0, 0);
  117. if (FGenerator->KeyType == ktDSA)
  118. {
  119. dsa_generate(FGenerator->FDSSKey, FGenerator->KeySize,
  120. KeyGenerationProgressUpdate, this);
  121. }
  122. else
  123. {
  124. rsa_generate(FGenerator->FRSAKey, FGenerator->KeySize,
  125. KeyGenerationProgressUpdate, this);
  126. }
  127. Progress.Complete = kgSuccess;
  128. }
  129. catch(...)
  130. {
  131. Progress.Complete = kgFailure;
  132. }
  133. Synchronize(DistributeProgressUpdate);
  134. }
  135. #pragma warn +lvc
  136. };
  137. //---------------------------------------------------------------------------
  138. void KeyGenerationProgressUpdate(void * Thread,
  139. int Action, int Phase, int IProgress)
  140. {
  141. DebugAssert(Thread);
  142. ((TKeyGenerationThread*)Thread)->ProgressUpdate(Action, Phase, IProgress);
  143. }
  144. //---------------------------------------------------------------------------
  145. __fastcall TKeyGenerator::TKeyGenerator():
  146. TObject()
  147. {
  148. FSSH2Key = new ssh2_userkey;
  149. FRSAKey = new RSAKey;
  150. FDSSKey = new dss_key;
  151. FEntropy = NULL;
  152. FState = kgInitializing;
  153. FOnGenerating = NULL;
  154. FGenerationRange = -1;
  155. KeyType = ktRSA2;
  156. KeySize = 1024;
  157. }
  158. //---------------------------------------------------------------------------
  159. __fastcall TKeyGenerator::~TKeyGenerator()
  160. {
  161. DebugAssert(FState != kgGenerating);
  162. if (FEntropy) delete FEntropy;
  163. delete FSSH2Key;
  164. delete FRSAKey;
  165. delete FDSSKey;
  166. }
  167. //---------------------------------------------------------------------------
  168. void __fastcall TKeyGenerator::SetKeySize(int value)
  169. {
  170. DebugAssert(FState != kgGenerating);
  171. FState = kgInitializing;
  172. FKeySize = value;
  173. FEntropyRequired = (KeySize / 2) * 2;
  174. FEntropyGot = 0;
  175. if (FEntropy) delete FEntropy;
  176. FEntropy = new TEntropyBit[FEntropyRequired];
  177. }
  178. //---------------------------------------------------------------------------
  179. void __fastcall TKeyGenerator::SetKeyType(TKeyType value)
  180. {
  181. DebugAssert(FState != kgGenerating);
  182. FState = kgInitializing;
  183. FKeyType = value;
  184. }
  185. //---------------------------------------------------------------------------
  186. void __fastcall TKeyGenerator::AddEntropy(TEntropyBit Entropy)
  187. {
  188. DebugAssert(FState == kgInitializing);
  189. DebugAssert(FEntropy);
  190. DebugAssert(FEntropyGot < FEntropyRequired);
  191. FEntropy[FEntropyGot++] = Entropy;
  192. if (FEntropyGot == FEntropyRequired)
  193. {
  194. FState = kgInitialized;
  195. random_add_heavynoise(FEntropy, FEntropyRequired * sizeof(TEntropyBit));
  196. delete FEntropy;
  197. FEntropy = NULL;
  198. }
  199. }
  200. //---------------------------------------------------------------------------
  201. void __fastcall TKeyGenerator::ResetKey()
  202. {
  203. DebugAssert(FSSH2Key);
  204. switch (KeyType) {
  205. case ktDSA:
  206. FSSH2Key->data = FDSSKey;
  207. FSSH2Key->alg = &ssh_dss;
  208. break;
  209. case ktRSA2:
  210. FSSH2Key->data = FRSAKey;
  211. FSSH2Key->alg = &ssh_rsa;
  212. break;
  213. }
  214. FFingerprint = "";
  215. FPublicKey = "";
  216. if (Comment.IsEmpty())
  217. {
  218. Comment = FORMAT("%s-key-%s",
  219. ((KeyType == ktDSA ? "dsa" : "rsa"), FormatDateTime("yyyymmdd", Now())));
  220. }
  221. }
  222. //---------------------------------------------------------------------------
  223. void __fastcall TKeyGenerator::StartGenerationThread()
  224. {
  225. DebugAssert(FState == kgInitialized);
  226. FState = kgGenerating;
  227. new TKeyGenerationThread(this);
  228. }
  229. //---------------------------------------------------------------------------
  230. void __fastcall TKeyGenerator::Generate()
  231. {
  232. DebugAssert(FState == kgInitialized);
  233. THREAD_CLASS * Thread;
  234. FState = kgGenerating;
  235. Thread = new TKeyGenerationThread(this);
  236. Thread->WaitFor();
  237. }
  238. //---------------------------------------------------------------------------
  239. void __fastcall TKeyGenerator::ProgressUpdate(int Range, int Position,
  240. TKeyGenerationComplete Complete)
  241. {
  242. DebugAssert(FState == kgGenerating);
  243. if (Complete == kgSuccess)
  244. {
  245. FState = kgComplete;
  246. ResetKey();
  247. }
  248. FGenerationRange = Range;
  249. FGenerationPosition = Position;
  250. if (FOnGenerating) FOnGenerating(this, Range, Position, Complete);
  251. }
  252. //---------------------------------------------------------------------------
  253. int __fastcall TKeyGenerator::GetPercentGenerated()
  254. {
  255. switch (FState) {
  256. case kgComplete: return 100;
  257. case kgGenerating: return (FGenerationPosition * 100) / FGenerationRange;
  258. default: return 0;
  259. }
  260. }
  261. //---------------------------------------------------------------------------
  262. void __fastcall TKeyGenerator::SetComment(AnsiString value)
  263. {
  264. if (FComment != value)
  265. {
  266. FComment = value;
  267. FPublicKey = "";
  268. DebugAssert(FSSH2Key);
  269. FSSH2Key->comment = FComment.c_str();
  270. FRSAKey->comment = FComment.c_str();
  271. }
  272. }
  273. //---------------------------------------------------------------------------
  274. AnsiString __fastcall TKeyGenerator::GetFingerprint()
  275. {
  276. if (FFingerprint.IsEmpty())
  277. {
  278. if (IsSSH2)
  279. {
  280. DebugAssert(FSSH2Key);
  281. FFingerprint = FSSH2Key->alg->fingerprint(FSSH2Key->data);
  282. }
  283. else
  284. {
  285. char Buf[128];
  286. rsa_fingerprint(Buf, sizeof(Buf), FRSAKey);
  287. FFingerprint = Buf;
  288. }
  289. }
  290. return FFingerprint;
  291. }
  292. //---------------------------------------------------------------------------
  293. bool __fastcall TKeyGenerator::GetIsSSH2()
  294. {
  295. return (KeyType == ktDSA || KeyType == ktRSA2);
  296. }
  297. //---------------------------------------------------------------------------
  298. AnsiString __fastcall TKeyGenerator::GetPublicKey()
  299. {
  300. if (FPublicKey.IsEmpty())
  301. {
  302. unsigned char *pub_blob;
  303. char *buffer, *p;
  304. int pub_len;
  305. int i;
  306. DebugAssert(FSSH2Key);
  307. pub_blob = FSSH2Key->alg->public_blob(FSSH2Key->data, &pub_len);
  308. buffer = new char[4 * ((pub_len + 2) / 3) + 1];
  309. p = buffer;
  310. i = 0;
  311. while (i < pub_len)
  312. {
  313. int n = (pub_len - i < 3 ? pub_len - i : 3);
  314. base64_encode_atom(pub_blob + i, n, p);
  315. i += n;
  316. p += 4;
  317. }
  318. *p = '\0';
  319. FPublicKey = buffer;
  320. sfree(pub_blob);
  321. delete [] buffer;
  322. }
  323. return FPublicKey;
  324. }
  325. //---------------------------------------------------------------------------
  326. AnsiString __fastcall TKeyGenerator::GetAuthorizedKeysLine()
  327. {
  328. DebugAssert(FSSH2Key);
  329. return FORMAT("%s %s %s", (FSSH2Key->alg->name, PublicKey, Comment));
  330. }
  331. //---------------------------------------------------------------------------
  332. void __fastcall TKeyGenerator::SaveKey(const AnsiString FileName,
  333. const AnsiString Passphrase, TKeyFormat Format)
  334. {
  335. DebugAssert(FSSH2Key);
  336. DebugAssert(FState == kgComplete);
  337. DebugAssert((Format != kfOpenSSH && Format != kfSSHCom) || IsSSH2);
  338. int Result;
  339. if (IsSSH2)
  340. {
  341. switch (Format)
  342. {
  343. case kfPutty:
  344. Result = ssh2_save_userkey(FileName.c_str(), FSSH2Key,
  345. (char*)Passphrase.data());
  346. break;
  347. case kfOpenSSH:
  348. Result = export_ssh2(FileName.c_str(), SSH_KEYTYPE_OPENSSH, FSSH2Key,
  349. (char*)Passphrase.data());
  350. break;
  351. case kfSSHCom:
  352. Result = export_ssh2(FileName.c_str(), SSH_KEYTYPE_SSHCOM, FSSH2Key,
  353. (char*)Passphrase.data());
  354. break;
  355. default:
  356. DebugFail();
  357. }
  358. }
  359. else
  360. {
  361. DebugAssert(Format == kfPutty);
  362. Result = saversakey(FileName.c_str(), FRSAKey,
  363. (char*)Passphrase.data());
  364. }
  365. if (Result <= 0)
  366. throw Exception(FMTLOAD(SAVE_KEY_ERROR, (FileName)));
  367. }