KeyGen.cpp 11 KB

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