2
0

ECDSA.pas 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  1. unit ECDSA;
  2. {
  3. Inno Setup
  4. Copyright (C) 1997-2025 Jordan Russell
  5. Portions by Martijn Laan
  6. For conditions of distribution and use, see LICENSE.TXT.
  7. ECDSA-P256 signing, verification, and key generation, based on CNG (BCrypt)
  8. }
  9. interface
  10. uses
  11. Windows, SysUtils;
  12. type
  13. TECDSAInt256 = array[0..31] of Byte;
  14. TECDSAPublicKey = packed record
  15. Public_x: TECDSAInt256;
  16. Public_y: TECDSAInt256;
  17. procedure Clear;
  18. end;
  19. TECDSAPrivateKey = packed record
  20. PublicKey: TECDSAPublicKey;
  21. Private_d: TECDSAInt256;
  22. procedure Clear;
  23. end;
  24. TECDSASignature = packed record
  25. Sig_r: TECDSAInt256;
  26. Sig_s: TECDSAInt256;
  27. procedure Clear;
  28. end;
  29. TECDSAKey = class
  30. private
  31. FAlgorithmHandle: THandle; { BCRYPT_ALG_HANDLE }
  32. FKeyHandle: THandle; { BCRYPT_KEY_HANDLE }
  33. class procedure CheckStatus(const AFunctionName: String;
  34. const AStatus: NTSTATUS); static;
  35. procedure KeyHandleRequired;
  36. public
  37. constructor Create;
  38. destructor Destroy; override;
  39. procedure DestroyKey;
  40. procedure ExportPrivateKey(out APrivateKey: TECDSAPrivateKey);
  41. procedure ExportPublicKey(out APublicKey: TECDSAPublicKey);
  42. procedure GenerateKeyPair;
  43. procedure ImportPrivateKey([ref] const APrivateKey: TECDSAPrivateKey);
  44. procedure ImportPublicKey([ref] const APublicKey: TECDSAPublicKey);
  45. procedure SignHash(const AHash: array of Byte;
  46. out ASignature: TECDSASignature);
  47. function VerifySignature(const AHash: array of Byte;
  48. const ASignature: TECDSASignature): Boolean;
  49. end;
  50. EECDSAError = class(Exception);
  51. implementation
  52. type
  53. BCRYPT_ALG_HANDLE = type THandle;
  54. BCRYPT_KEY_HANDLE = type THandle;
  55. BCRYPT_ECCKEY_BLOB = record
  56. dwMagic: ULONG;
  57. cbKey: ULONG;
  58. end;
  59. const
  60. BCRYPT_ECDSA_P256_ALGORITHM = 'ECDSA_P256';
  61. BCRYPT_ECCPRIVATE_BLOB = 'ECCPRIVATEBLOB';
  62. BCRYPT_ECCPUBLIC_BLOB = 'ECCPUBLICBLOB';
  63. BCRYPT_ECDSA_PRIVATE_P256_MAGIC = $32534345;
  64. BCRYPT_ECDSA_PUBLIC_P256_MAGIC = $31534345;
  65. STATUS_INVALID_SIGNATURE = NTSTATUS($C000A000);
  66. var
  67. BCryptFunctionsInitialized: BOOL;
  68. BCryptCloseAlgorithmProvider: function(hAlgorithm: BCRYPT_ALG_HANDLE;
  69. dwFlags: ULONG): NTSTATUS;
  70. stdcall;
  71. BCryptDestroyKey: function(hKey: BCRYPT_KEY_HANDLE): NTSTATUS;
  72. stdcall;
  73. BCryptExportKey: function(hKey: BCRYPT_KEY_HANDLE; hExportKey: BCRYPT_KEY_HANDLE;
  74. pszBlobType: LPCWSTR; var pbOutput; cbOutput: ULONG; out pcbResult: ULONG;
  75. dwFlags: ULONG): NTSTATUS;
  76. stdcall;
  77. BCryptFinalizeKeyPair: function(hKey: BCRYPT_KEY_HANDLE; dwFlags: ULONG): NTSTATUS;
  78. stdcall;
  79. BCryptGenerateKeyPair: function(hAlgorithm: BCRYPT_ALG_HANDLE;
  80. out phKey: BCRYPT_KEY_HANDLE; dwLength: ULONG; dwFlags: ULONG): NTSTATUS;
  81. stdcall;
  82. BCryptImportKeyPair: function(hAlgorithm: BCRYPT_ALG_HANDLE;
  83. hImportKey: BCRYPT_KEY_HANDLE; pszBlobType: LPCWSTR;
  84. out phKey: BCRYPT_KEY_HANDLE; const pbInput; cbInput: ULONG;
  85. dwFlags: ULONG): NTSTATUS;
  86. stdcall;
  87. BCryptOpenAlgorithmProvider: function(out phAlgorithm: BCRYPT_ALG_HANDLE;
  88. pszAlgId: LPCWSTR; pszImplementation: LPCWSTR; dwFlags: ULONG): NTSTATUS;
  89. stdcall;
  90. BCryptSignHash: function(hKey: BCRYPT_KEY_HANDLE; pPaddingInfo: Pointer;
  91. const pbInput; cbInput: ULONG; var pbOutput; cbOutput: ULONG;
  92. out pcbResult: ULONG; dwFlags: ULONG): NTSTATUS;
  93. stdcall;
  94. BCryptVerifySignature: function(hKey: BCRYPT_KEY_HANDLE; pPaddingInfo: Pointer;
  95. const pbHash; cbHash: ULONG; const pbSignature; cbSignature: ULONG;
  96. dwFlags: ULONG): NTSTATUS;
  97. stdcall;
  98. type
  99. { ECDSA-P256 key blob formats specific to BCrypt }
  100. TBCryptPrivateKeyBlob = record
  101. Header: BCRYPT_ECCKEY_BLOB;
  102. Public_x: TECDSAInt256;
  103. Public_y: TECDSAInt256;
  104. Private_d: TECDSAInt256;
  105. procedure Clear;
  106. end;
  107. TBCryptPublicKeyBlob = record
  108. Header: BCRYPT_ECCKEY_BLOB;
  109. Public_x: TECDSAInt256;
  110. Public_y: TECDSAInt256;
  111. procedure Clear;
  112. end;
  113. procedure InitBCryptFunctions;
  114. var
  115. M: HMODULE;
  116. procedure InitFunc(out AProc: Pointer; const AProcName: PAnsiChar);
  117. begin
  118. AProc := GetProcAddress(M, AProcName);
  119. if not Assigned(AProc) then
  120. raise EECDSAError.CreateFmt('Failed to get address of %s in bcrypt.dll',
  121. [AProcName]);
  122. end;
  123. var
  124. SystemDir: array[0..MAX_PATH-1] of Char;
  125. begin
  126. if BCryptFunctionsInitialized then begin
  127. MemoryBarrier;
  128. Exit;
  129. end;
  130. GetSystemDirectory(SystemDir, SizeOf(SystemDir) div SizeOf(SystemDir[0]));
  131. M := LoadLibrary(PChar(SystemDir + '\bcrypt.dll'));
  132. if M = 0 then
  133. raise EECDSAError.Create('Failed to load bcrypt.dll');
  134. InitFunc(@BCryptCloseAlgorithmProvider, 'BCryptCloseAlgorithmProvider');
  135. InitFunc(@BCryptDestroyKey, 'BCryptDestroyKey');
  136. InitFunc(@BCryptExportKey, 'BCryptExportKey');
  137. InitFunc(@BCryptFinalizeKeyPair, 'BCryptFinalizeKeyPair');
  138. InitFunc(@BCryptGenerateKeyPair, 'BCryptGenerateKeyPair');
  139. InitFunc(@BCryptImportKeyPair, 'BCryptImportKeyPair');
  140. InitFunc(@BCryptOpenAlgorithmProvider, 'BCryptOpenAlgorithmProvider');
  141. InitFunc(@BCryptSignHash, 'BCryptSignHash');
  142. InitFunc(@BCryptVerifySignature, 'BCryptVerifySignature');
  143. MemoryBarrier;
  144. BCryptFunctionsInitialized := True;
  145. end;
  146. { TBCryptPrivateKeyBlob }
  147. procedure TBCryptPrivateKeyBlob.Clear;
  148. begin
  149. FillChar(Self, SizeOf(Self), 0);
  150. end;
  151. { TBCryptPublicKeyBlob }
  152. procedure TBCryptPublicKeyBlob.Clear;
  153. begin
  154. FillChar(Self, SizeOf(Self), 0);
  155. end;
  156. { TECDSAPublicKey }
  157. procedure TECDSAPublicKey.Clear;
  158. begin
  159. FillChar(Self, SizeOf(Self), 0);
  160. end;
  161. { TECDSAPrivateKey }
  162. procedure TECDSAPrivateKey.Clear;
  163. begin
  164. FillChar(Self, SizeOf(Self), 0);
  165. end;
  166. { TECDSASignature }
  167. procedure TECDSASignature.Clear;
  168. begin
  169. FillChar(Self, SizeOf(Self), 0);
  170. end;
  171. { TECDSAKey }
  172. constructor TECDSAKey.Create;
  173. begin
  174. inherited;
  175. InitBCryptFunctions;
  176. var LAlgorithmHandle: BCRYPT_ALG_HANDLE;
  177. CheckStatus('BCryptOpenAlgorithmProvider',
  178. BCryptOpenAlgorithmProvider(LAlgorithmHandle, BCRYPT_ECDSA_P256_ALGORITHM,
  179. nil, 0));
  180. FAlgorithmHandle := LAlgorithmHandle; { assign only on success }
  181. end;
  182. destructor TECDSAKey.Destroy;
  183. begin
  184. DestroyKey;
  185. if FAlgorithmHandle <> 0 then
  186. BCryptCloseAlgorithmProvider(FAlgorithmHandle, 0);
  187. inherited;
  188. end;
  189. class procedure TECDSAKey.CheckStatus(const AFunctionName: String;
  190. const AStatus: NTSTATUS);
  191. begin
  192. if AStatus <> 0 then
  193. raise EECDSAError.CreateFmt('%s failed with error code 0x%x',
  194. [AFunctionName, AStatus]);
  195. end;
  196. procedure TECDSAKey.DestroyKey;
  197. begin
  198. const H = FKeyHandle;
  199. if H <> 0 then begin
  200. FKeyHandle := 0;
  201. BCryptDestroyKey(H);
  202. end;
  203. end;
  204. procedure TECDSAKey.ExportPrivateKey(out APrivateKey: TECDSAPrivateKey);
  205. begin
  206. KeyHandleRequired;
  207. var KeyBlob: TBCryptPrivateKeyBlob;
  208. { Initially clear KeyBlob just to make it easier to verify that
  209. BCryptExportKey overwrites the entire record }
  210. KeyBlob.Clear;
  211. try
  212. var ResultSize: ULONG;
  213. CheckStatus('BCryptExportKey',
  214. BCryptExportKey(FKeyHandle, 0, BCRYPT_ECCPRIVATE_BLOB, KeyBlob,
  215. SizeOf(KeyBlob), ResultSize, 0));
  216. if ResultSize <> SizeOf(KeyBlob) then
  217. raise EECDSAError.Create('BCryptExportKey result invalid (1)');
  218. if KeyBlob.Header.dwMagic <> BCRYPT_ECDSA_PRIVATE_P256_MAGIC then
  219. raise EECDSAError.Create('BCryptExportKey result invalid (2)');
  220. if KeyBlob.Header.cbKey <> SizeOf(KeyBlob.Public_x) then
  221. raise EECDSAError.Create('BCryptExportKey result invalid (3)');
  222. APrivateKey.PublicKey.Public_x := KeyBlob.Public_x;
  223. APrivateKey.PublicKey.Public_y := KeyBlob.Public_y;
  224. APrivateKey.Private_d := KeyBlob.Private_d;
  225. finally
  226. { Security: don't leave copy of private key on the stack }
  227. KeyBlob.Clear;
  228. end;
  229. end;
  230. procedure TECDSAKey.ExportPublicKey(out APublicKey: TECDSAPublicKey);
  231. begin
  232. KeyHandleRequired;
  233. var KeyBlob: TBCryptPublicKeyBlob;
  234. { Initially clear KeyBlob just to make it easier to verify that
  235. BCryptExportKey overwrites the entire record }
  236. KeyBlob.Clear;
  237. try
  238. var ResultSize: ULONG;
  239. CheckStatus('BCryptExportKey',
  240. BCryptExportKey(FKeyHandle, 0, BCRYPT_ECCPUBLIC_BLOB, KeyBlob,
  241. SizeOf(KeyBlob), ResultSize, 0));
  242. if ResultSize <> SizeOf(KeyBlob) then
  243. raise EECDSAError.Create('BCryptExportKey result invalid (1)');
  244. if KeyBlob.Header.dwMagic <> BCRYPT_ECDSA_PUBLIC_P256_MAGIC then
  245. raise EECDSAError.Create('BCryptExportKey result invalid (2)');
  246. if KeyBlob.Header.cbKey <> SizeOf(KeyBlob.Public_x) then
  247. raise EECDSAError.Create('BCryptExportKey result invalid (3)');
  248. APublicKey.Public_x := KeyBlob.Public_x;
  249. APublicKey.Public_y := KeyBlob.Public_y;
  250. finally
  251. { There's no private key, but clear anyway for consistency }
  252. KeyBlob.Clear;
  253. end;
  254. end;
  255. procedure TECDSAKey.GenerateKeyPair;
  256. begin
  257. DestroyKey;
  258. var LKeyHandle: BCRYPT_KEY_HANDLE;
  259. CheckStatus('BCryptGenerateKeyPair',
  260. BCryptGenerateKeyPair(FAlgorithmHandle, LKeyHandle, 256, 0));
  261. try
  262. CheckStatus('BCryptFinalizeKeyPair',
  263. BCryptFinalizeKeyPair(LKeyHandle, 0));
  264. except
  265. BCryptDestroyKey(LKeyHandle);
  266. raise;
  267. end;
  268. FKeyHandle := LKeyHandle; { assign only on success }
  269. end;
  270. procedure TECDSAKey.ImportPrivateKey([ref] const APrivateKey: TECDSAPrivateKey);
  271. begin
  272. DestroyKey;
  273. var KeyBlob: TBCryptPrivateKeyBlob;
  274. try
  275. KeyBlob.Header.dwMagic := BCRYPT_ECDSA_PRIVATE_P256_MAGIC;
  276. KeyBlob.Header.cbKey := SizeOf(KeyBlob.Public_x);
  277. KeyBlob.Public_x := APrivateKey.PublicKey.Public_x;
  278. KeyBlob.Public_y := APrivateKey.PublicKey.Public_y;
  279. KeyBlob.Private_d := APrivateKey.Private_d;
  280. var LKeyHandle: BCRYPT_KEY_HANDLE;
  281. CheckStatus('BCryptImportKeyPair',
  282. BCryptImportKeyPair(FAlgorithmHandle, 0, BCRYPT_ECCPRIVATE_BLOB,
  283. LKeyHandle, KeyBlob, SizeOf(KeyBlob), 0));
  284. FKeyHandle := LKeyHandle; { assign only on success }
  285. finally
  286. { Security: don't leave copy of private key on the stack }
  287. KeyBlob.Clear;
  288. end;
  289. end;
  290. procedure TECDSAKey.ImportPublicKey([ref] const APublicKey: TECDSAPublicKey);
  291. begin
  292. DestroyKey;
  293. var KeyBlob: TBCryptPublicKeyBlob;
  294. try
  295. KeyBlob.Header.dwMagic := BCRYPT_ECDSA_PUBLIC_P256_MAGIC;
  296. KeyBlob.Header.cbKey := SizeOf(KeyBlob.Public_x);
  297. KeyBlob.Public_x := APublicKey.Public_x;
  298. KeyBlob.Public_y := APublicKey.Public_y;
  299. var LKeyHandle: BCRYPT_KEY_HANDLE;
  300. CheckStatus('BCryptImportKeyPair',
  301. BCryptImportKeyPair(FAlgorithmHandle, 0, BCRYPT_ECCPUBLIC_BLOB,
  302. LKeyHandle, KeyBlob, SizeOf(KeyBlob), 0));
  303. FKeyHandle := LKeyHandle; { assign only on success }
  304. finally
  305. { There's no private key, but clear anyway for consistency }
  306. KeyBlob.Clear;
  307. end;
  308. end;
  309. procedure TECDSAKey.KeyHandleRequired;
  310. begin
  311. if FKeyHandle = 0 then
  312. raise EECDSAError.Create('No key has been assigned');
  313. end;
  314. procedure TECDSAKey.SignHash(const AHash: array of Byte;
  315. out ASignature: TECDSASignature);
  316. begin
  317. KeyHandleRequired;
  318. { Initially clear ASignature just to make it easier to verify that
  319. BCryptSignHash overwrites the entire record }
  320. ASignature.Clear;
  321. var ResultSize: ULONG;
  322. CheckStatus('BCryptSignHash',
  323. BCryptSignHash(FKeyHandle, nil, AHash[0], ULONG(Length(AHash)), ASignature,
  324. SizeOf(ASignature), ResultSize, 0));
  325. if ResultSize <> SizeOf(ASignature) then
  326. raise EECDSAError.Create('BCryptSignHash result size invalid');
  327. end;
  328. function TECDSAKey.VerifySignature(const AHash: array of Byte;
  329. const ASignature: TECDSASignature): Boolean;
  330. begin
  331. KeyHandleRequired;
  332. const Status = BCryptVerifySignature(FKeyHandle, nil, AHash[0],
  333. ULONG(Length(AHash)), ASignature, SizeOf(ASignature), 0);
  334. if Status = STATUS_INVALID_SIGNATURE then
  335. Result := False
  336. else begin
  337. CheckStatus('BCryptVerifySignature', Status);
  338. Result := True;
  339. end;
  340. end;
  341. end.