rsa.d 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653
  1. module secured.rsa;
  2. import core.memory;
  3. import secured.openssl;
  4. import deimos.openssl.evp;
  5. import deimos.openssl.rand;
  6. import deimos.openssl.pem;
  7. import deimos.openssl.bio;
  8. import deimos.openssl.rsa;
  9. import deimos.openssl.engine;
  10. import secured.random;
  11. import secured.symmetric;
  12. import secured.util;
  13. // ----------------------------------------------------------
  14. @trusted:
  15. public class RSA
  16. {
  17. private bool _hasPrivateKey;
  18. public @property bool hasPrivateKey() { return _hasPrivateKey; }
  19. static EVP_PKEY *keypair;
  20. public this(const int RSA_KEYLEN = 4096)
  21. {
  22. // Reseed the OpenSSL RNG every time we create a new RSA Key to ensure that the result is truely random in threading/forking scenarios.
  23. ubyte[] seedbuf = random(32);
  24. RAND_seed(seedbuf.ptr, cast(int)seedbuf.length);
  25. EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, null);
  26. if (ctx is null) {
  27. throw new CryptographicException("EVP_PKEY_CTX_new_id failed.");
  28. }
  29. scope(exit) {
  30. if (ctx !is null)
  31. EVP_PKEY_CTX_free(ctx);
  32. }
  33. if(EVP_PKEY_keygen_init(ctx) <= 0) {
  34. throw new CryptographicException("EVP_PKEY_keygen_init failed.");
  35. }
  36. if(EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, RSA_KEYLEN) <= 0) {
  37. throw new CryptographicException("EVP_PKEY_CTX_set_rsa_keygen_bits failed.");
  38. }
  39. if(EVP_PKEY_keygen(ctx, &keypair) <= 0) {
  40. throw new CryptographicException("EVP_PKEY_keygen failed.");
  41. }
  42. _hasPrivateKey = true;
  43. }
  44. public this(ubyte[] privateKey, ubyte[] password)
  45. {
  46. // Reseed the OpenSSL RNG every time we load an existing RSA key to ensure that the result is truely random in threading/forking scenarios.
  47. ubyte[] seedbuf = random(32);
  48. RAND_seed(seedbuf.ptr, cast(int)seedbuf.length);
  49. _hasPrivateKey = true;
  50. ubyte[] pk = cast(ubyte[])privateKey;
  51. BIO* bio = BIO_new_mem_buf(pk.ptr, cast(int)pk.length);
  52. if (password is null) {
  53. keypair = PEM_read_bio_PrivateKey(bio, null, null, null);
  54. } else {
  55. ubyte[] pwd = cast(ubyte[])password;
  56. pwd = pwd ~ '\0';
  57. keypair = PEM_read_bio_PrivateKey(bio, null, null, pwd.ptr);
  58. }
  59. BIO_free_all(bio);
  60. }
  61. public this(ubyte[] publicKey)
  62. {
  63. // Reseed the OpenSSL RNG every time we load an existing RSA key to ensure that the result is truely random in threading/forking scenarios.
  64. ubyte[] seedbuf = random(32);
  65. RAND_seed(seedbuf.ptr, cast(int)seedbuf.length);
  66. _hasPrivateKey = false;
  67. ubyte[] pk = cast(ubyte[])publicKey;
  68. BIO* bio = BIO_new_mem_buf(pk.ptr, cast(int)pk.length);
  69. keypair = PEM_read_bio_PUBKEY(bio, null, null, null);
  70. BIO_free_all(bio);
  71. }
  72. public ~this()
  73. {
  74. }
  75. ubyte[] seal(const ubyte[] plaintext)
  76. {
  77. return this.seal(plaintext, SymmetricAlgorithm.AES256_CTR);
  78. }
  79. ubyte[] seal(const ubyte[] plaintext, SymmetricAlgorithm algorithm)
  80. {
  81. ubyte* _encMsg;
  82. ubyte* _ek;
  83. size_t _ekl;
  84. ubyte* _iv;
  85. size_t _ivl;
  86. ubyte** encMsg = &_encMsg;
  87. ubyte** ek = &_ek;
  88. size_t* ekl = &_ekl;
  89. ubyte** iv = &_iv;
  90. size_t* ivl = &_ivl;
  91. // The header, symmetric encryption key ek and initialisation vector iv are prefixed the encrypted message
  92. // Having four length bytes in header imposes a 4 GB limit on the plaintext
  93. const ubyte* msg = plaintext.ptr;
  94. size_t msgLen = plaintext.length;
  95. static if(size_t.sizeof == 8) {
  96. size_t maxHeaderL = 2 + 2 + 4; // 2 bytes for actual ekl, 2 bytes for actual ivl and 4 bytes for actual length
  97. size_t maxEKL = EVP_PKEY_get_size(keypair);
  98. size_t maxIVL = EVP_MAX_IV_LENGTH;
  99. size_t maxEncMsgLen = msgLen + EVP_MAX_IV_LENGTH;
  100. size_t maxTotalSize = maxHeaderL + maxEKL + maxIVL + maxEncMsgLen;
  101. size_t encMsgLen = 0;
  102. size_t blockLen = 0;
  103. *ivl = EVP_MAX_IV_LENGTH;
  104. ubyte* buffer = cast(ubyte*)GC.malloc(maxTotalSize);
  105. if(buffer == null)
  106. throw new CryptographicException("Malloc failed.");
  107. *ek = buffer + maxHeaderL;
  108. *iv = buffer + maxHeaderL + maxEKL;
  109. *encMsg = buffer + maxHeaderL + maxEKL + maxIVL;
  110. version(OpenSSL10) {
  111. EVP_CIPHER_CTX *rsaEncryptCtx = cast(EVP_CIPHER_CTX*)GC.malloc(EVP_CIPHER_CTX.sizeof);
  112. if(rsaEncryptCtx == null)
  113. throw new CryptographicException("Malloc failed.");
  114. EVP_CIPHER_CTX_init(rsaEncryptCtx);
  115. } else {
  116. EVP_CIPHER_CTX *rsaEncryptCtx = EVP_CIPHER_CTX_new();
  117. }
  118. scope(exit) {
  119. if (rsaEncryptCtx !is null) {
  120. version(OpenSSL10) {
  121. EVP_CIPHER_CTX_cleanup(rsaEncryptCtx);
  122. } else {
  123. EVP_CIPHER_CTX_free(rsaEncryptCtx);
  124. }
  125. }
  126. }
  127. if(!EVP_SealInit(rsaEncryptCtx, getOpenSslCipher(algorithm), ek, cast(int*)ekl, *iv, &keypair, 1))
  128. throw new CryptographicException("CEVP_SealInit failed.");
  129. if(!EVP_SealUpdate(rsaEncryptCtx, *encMsg + encMsgLen, cast(int*)&blockLen, cast(const ubyte*)msg, cast(int)msgLen))
  130. throw new CryptographicException("EVP_SealUpdate failed.");
  131. encMsgLen += blockLen;
  132. if(!EVP_SealFinal(rsaEncryptCtx, *encMsg + encMsgLen, cast(int*)&blockLen))
  133. throw new CryptographicException("EVP_SealFinal failed.");
  134. encMsgLen += blockLen;
  135. buffer[0 .. 2] = (cast(ubyte*)ekl)[0..2];
  136. buffer[2 .. 4] = (cast(ubyte*)ivl)[0..2];
  137. ubyte* encMsgLenTemp = cast(ubyte*)(&encMsgLen);
  138. buffer[4..8] = encMsgLenTemp[0..4];
  139. assert(*ekl == maxEKL);
  140. assert(*ivl == maxIVL);
  141. return buffer[0 .. maxHeaderL + maxEKL + maxIVL + encMsgLen];
  142. }
  143. else
  144. assert(0);
  145. }
  146. ubyte[] open(ubyte[] encMessage)
  147. {
  148. return this.open(encMessage, SymmetricAlgorithm.AES256_CTR);
  149. }
  150. ubyte[] open(ubyte[] encMessage, SymmetricAlgorithm algorithm)
  151. {
  152. assert(encMessage.length > 8); // Encrypted message must be larger than header = ekl + ivl + messageLength
  153. static if(size_t.sizeof == 8) {
  154. // Header: 2 bytes for actual ekl, 2 bytes for actual ivl and 4 bytes for actual length
  155. size_t maxHeaderL = 2 + 2 + 4;
  156. size_t maxEKL = EVP_PKEY_get_size(keypair);
  157. size_t maxIVL = EVP_MAX_IV_LENGTH;
  158. ubyte* ek = encMessage.ptr + maxHeaderL;
  159. ubyte[8] temp = 0;
  160. temp[0..2] = encMessage[0..2];
  161. int ekl = (cast(int[])temp)[0];
  162. ubyte* iv = encMessage.ptr + maxHeaderL + maxEKL;
  163. temp = 0;
  164. temp[0..2] = encMessage[2..4];
  165. size_t ivl = (cast(int[])temp)[0];
  166. ubyte* encMsg = encMessage.ptr + maxHeaderL + maxEKL + maxIVL;
  167. temp = 0;
  168. temp[0..4] = encMessage[4..8];
  169. size_t encMsgLen = (cast(size_t[])temp)[0];
  170. size_t decLen = 0;
  171. size_t blockLen = 0;
  172. EVP_PKEY *key;
  173. ubyte* _decMsg;
  174. auto decMsg = &_decMsg;
  175. *decMsg = cast(ubyte*)GC.malloc(encMsgLen + ivl);
  176. if(decMsg == null) {
  177. throw new CryptographicException("Malloc failed.");
  178. }
  179. version(OpenSSL10) {
  180. EVP_CIPHER_CTX *rsaDecryptCtx = cast(EVP_CIPHER_CTX*)GC.malloc(EVP_CIPHER_CTX.sizeof);
  181. if(rsaDecryptCtx == null) {
  182. throw new CryptographicException("Malloc failed.");
  183. }
  184. EVP_CIPHER_CTX_init(rsaDecryptCtx);
  185. } else {
  186. EVP_CIPHER_CTX *rsaDecryptCtx = EVP_CIPHER_CTX_new();
  187. }
  188. scope(exit) {
  189. if (rsaDecryptCtx !is null) {
  190. version(OpenSSL10) {
  191. EVP_CIPHER_CTX_cleanup(rsaDecryptCtx);
  192. } else {
  193. EVP_CIPHER_CTX_free(rsaDecryptCtx);
  194. }
  195. }
  196. }
  197. if(!EVP_OpenInit(rsaDecryptCtx, getOpenSslCipher(algorithm), ek, ekl, iv, keypair))
  198. throw new CryptographicException("EVP_OpenInit failed.");
  199. if(!EVP_OpenUpdate(rsaDecryptCtx, cast(ubyte*)*decMsg + decLen, cast(int*)&blockLen, encMsg, cast(int)encMsgLen))
  200. throw new CryptographicException("EVP_OpenUpdate failed.");
  201. decLen += blockLen;
  202. if(!EVP_OpenFinal(rsaDecryptCtx, cast(ubyte*)*decMsg + decLen, cast(int*)&blockLen))
  203. throw new CryptographicException("EVP_OpenFinal failed.");
  204. decLen += blockLen;
  205. return (*decMsg)[0 .. decLen];
  206. }
  207. else
  208. assert(0);
  209. }
  210. ubyte[] getPublicKey()
  211. {
  212. BIO* bio = BIO_new(BIO_s_mem());
  213. PEM_write_bio_PUBKEY(bio, keypair);
  214. ubyte[] buffer = new ubyte[BIO_ctrl_pending(bio)];
  215. BIO_read(bio, buffer.ptr, cast(int)buffer.length);
  216. BIO_free_all(bio);
  217. return buffer;
  218. }
  219. public ubyte[] getPrivateKey(string password, int iterations = 25000, bool use3Des = false)
  220. {
  221. if (!_hasPrivateKey) {
  222. return null;
  223. }
  224. BIO* bio = BIO_new(BIO_s_mem());
  225. if (password is null) {
  226. PEM_write_bio_PKCS8PrivateKey(bio, keypair, null, null, 0, null, null);
  227. } else {
  228. ubyte[] pwd = cast(ubyte[])password;
  229. pwd = pwd ~ '\0';
  230. PEM_write_bio_PKCS8PrivateKey(
  231. bio,
  232. keypair,
  233. !use3Des ? EVP_aes_256_cbc() : EVP_des_ede3_cbc(),
  234. null,
  235. 0,
  236. null,
  237. pwd.ptr);
  238. }
  239. if(BIO_ctrl_pending(bio) == 0) {
  240. throw new CryptographicException("No private key written.");
  241. }
  242. ubyte[] buffer = new ubyte[BIO_ctrl_pending(bio)];
  243. BIO_read(bio, buffer.ptr, cast(int)buffer.length);
  244. BIO_free_all(bio);
  245. return buffer;
  246. }
  247. ubyte[] encrypt(const ubyte[] inMessage)
  248. in
  249. {
  250. import std.exception: enforce;
  251. enforce(inMessage.length <= (EVP_PKEY_get_size(keypair) - 42), new CryptographicException("Plainttext length exceeds allowance")); // 42 being the padding overhead for OAEP padding using SHA-1
  252. }
  253. body
  254. {
  255. EVP_PKEY_CTX *ctx;
  256. ENGINE *eng = null; // Use default RSA implementation
  257. ubyte *out2;
  258. const ubyte *in2 = inMessage.ptr;
  259. size_t outlen;
  260. size_t inlen = inMessage.length;
  261. ctx = EVP_PKEY_CTX_new(keypair,eng);
  262. if (!ctx) {
  263. throw new CryptographicException("EVP_PKEY_CTX_new.");
  264. }
  265. scope(exit) {
  266. if (ctx !is null) {
  267. EVP_PKEY_CTX_free(ctx);
  268. }
  269. }
  270. if (EVP_PKEY_encrypt_init(ctx) <= 0) {
  271. throw new CryptographicException("EVP_PKEY_encrypt_init failed.");
  272. }
  273. if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING) <= 0) {
  274. throw new CryptographicException("EVP_PKEY_CTX_set_rsa_padding failed.");
  275. }
  276. if (EVP_PKEY_encrypt(ctx, null, &outlen, in2, inlen) <= 0) {
  277. throw new CryptographicException("EVP_PKEY_encrypt failed.");
  278. }
  279. out2 = cast(ubyte*)GC.malloc(outlen);
  280. if(out2 == null) {
  281. throw new CryptographicException("Malloc failed.");
  282. }
  283. if (EVP_PKEY_encrypt(ctx, out2, &outlen, in2, inlen) <= 0) {
  284. throw new CryptographicException("EVP_PKEY_encrypt failed.");
  285. }
  286. return (out2)[0 .. outlen];
  287. }
  288. ubyte[] decrypt(const ubyte[] inMessage)
  289. in
  290. {
  291. assert(inMessage.length == EVP_PKEY_get_size(keypair)); // Should always hold as padding was added during encryption
  292. }
  293. body
  294. {
  295. EVP_PKEY_CTX *ctx;
  296. ENGINE *eng = null; // Use default RSA implementation
  297. ubyte *out2;
  298. const ubyte *in2 = inMessage.ptr;
  299. size_t outlen;
  300. size_t inlen = inMessage.length;
  301. ctx = EVP_PKEY_CTX_new(keypair,eng);
  302. if (!ctx) {
  303. throw new CryptographicException("EVP_PKEY_CTX_new failed");
  304. }
  305. scope(exit) {
  306. if (ctx !is null) {
  307. EVP_PKEY_CTX_free(ctx);
  308. }
  309. }
  310. if (EVP_PKEY_decrypt_init(ctx) <= 0) {
  311. throw new CryptographicException("EVP_PKEY_decrypt_init failed.");
  312. }
  313. if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING) <= 0) {
  314. throw new CryptographicException("EVP_PKEY_CTX_set_rsa_padding failed.");
  315. }
  316. if (EVP_PKEY_decrypt(ctx, null, &outlen, in2, inlen) <= 0) {
  317. throw new CryptographicException("EVP_PKEY_decrypt failed.");
  318. }
  319. out2 = cast(ubyte*)GC.malloc(outlen);
  320. if(out2 == null) {
  321. throw new CryptographicException("Malloc failed.");
  322. }
  323. if (EVP_PKEY_decrypt(ctx, out2, &outlen, in2, inlen) <= 0) {
  324. throw new CryptographicException("EVP_PKEY_encrypt failed.");
  325. }
  326. return (out2)[0 .. outlen];
  327. }
  328. public ubyte[] sign(ubyte[] data, bool useSha256 = false)
  329. out (signature)
  330. {
  331. assert(signature.length == EVP_PKEY_get_size(keypair));
  332. }
  333. body
  334. {
  335. EVP_MD_CTX *mdctx = null;
  336. mdctx = EVP_MD_CTX_new();
  337. if (mdctx is null) {
  338. throw new CryptographicException("Unable to create the MD signing context.");
  339. }
  340. scope(exit) {
  341. if (mdctx !is null) {
  342. EVP_MD_CTX_free(mdctx);
  343. }
  344. }
  345. auto alg = (!useSha256 ? EVP_sha384() : EVP_sha256());
  346. if (EVP_DigestSignInit(mdctx, null, alg, null, keypair) != 1) {
  347. throw new CryptographicException("Unable to initialize the signing digest.");
  348. }
  349. if (EVP_DigestSignUpdate(mdctx, data.ptr, data.length) != 1) {
  350. throw new CryptographicException("Unable to set sign data.");
  351. }
  352. size_t signlen = 0;
  353. if (EVP_DigestSignFinal(mdctx, null, &signlen) != 1) {
  354. throw new CryptographicException("Unable to calculate signature length.");
  355. }
  356. ubyte[] sign = new ubyte[signlen];
  357. if (EVP_DigestSignFinal(mdctx, sign.ptr, &signlen) != 1) {
  358. throw new CryptographicException("Unable to finalize signature");
  359. }
  360. return sign[0..signlen];
  361. }
  362. public bool verify(ubyte[] data, ubyte[] signature, bool useSha256 = false)
  363. in
  364. {
  365. assert(signature.length == EVP_PKEY_get_size(keypair));
  366. }
  367. body
  368. {
  369. EVP_MD_CTX *mdctx = null;
  370. mdctx = EVP_MD_CTX_new();
  371. if (mdctx is null) {
  372. throw new CryptographicException("Unable to create the MD signing context.");
  373. }
  374. scope(exit) {
  375. if (mdctx !is null) {
  376. EVP_MD_CTX_free(mdctx);
  377. }
  378. }
  379. auto alg = (!useSha256 ? EVP_sha384() : EVP_sha256());
  380. if (EVP_DigestVerifyInit(mdctx, null, alg, null, keypair) != 1) {
  381. throw new CryptographicException("Unable to initialize the verification digest.");
  382. }
  383. if (EVP_DigestVerifyUpdate(mdctx, data.ptr, data.length) != 1) {
  384. throw new CryptographicException("Unable to set verify data.");
  385. }
  386. int ret = EVP_DigestVerifyFinal(mdctx, signature.ptr, signature.length);
  387. return ret == 1;
  388. } // verify()
  389. } // class RSA
  390. // ----------------------------------------------------------
  391. // UNITTESTING BELOW
  392. // ----------------------------------------------------------
  393. unittest
  394. {
  395. import std.stdio;
  396. writeln("Testing seal and open functions:");
  397. auto keypair = new RSA();
  398. scope(exit) keypair.destroy();
  399. ubyte[] plaintext = cast(ubyte[])"This is a test This is a test This is a test This is a test";
  400. ubyte[] encMessage = keypair.seal(plaintext);
  401. ubyte[] decMessage = keypair.open(encMessage);
  402. assert(plaintext.length == decMessage.length);
  403. assert(plaintext == decMessage);
  404. }
  405. // ----------------------------------------------------------
  406. unittest
  407. {
  408. import std.stdio;
  409. writeln("Testing getXxxKey functions and constructors:");
  410. auto keypairA = new RSA();
  411. scope(exit) keypairA.destroy();
  412. auto privateKeyA = keypairA.getPrivateKey(null);
  413. auto publicKeyA = keypairA.getPublicKey();
  414. ubyte[] plaintext = cast(ubyte[])"This is a test This is a test This is a test This is a test";
  415. // Creating key from public key only
  416. auto keypairB = new RSA(publicKeyA);
  417. scope(exit) keypairB.destroy();
  418. auto privateKeyB = keypairB.getPrivateKey(null);
  419. auto publicKeyB = keypairB.getPublicKey();
  420. assert(privateKeyA != privateKeyB, "Private keys A and B match - they should NOT do so");
  421. assert(publicKeyA == publicKeyB, "Public keys A and B does not match");
  422. // Creating key from private key only
  423. auto keypairC = new RSA(privateKeyA, null);
  424. scope(exit) keypairC.destroy();
  425. auto publicKeyC = keypairC.getPublicKey();
  426. auto privateKeyC = keypairC.getPrivateKey(null);
  427. assert(privateKeyA == privateKeyC, "Private keys A and C does not match");
  428. assert(publicKeyA == publicKeyC, "Public keys A and C does not match");
  429. }
  430. // ----------------------------------------------------------
  431. unittest
  432. {
  433. import std.stdio;
  434. writeln("Testing sealing and opening with keys, which have been constructed on getXxxKey output:");
  435. auto keypairA = new RSA();
  436. scope(exit) keypairA.destroy();
  437. auto privateKeyA = keypairA.getPrivateKey(null);
  438. auto publicKeyA = keypairA.getPublicKey();
  439. ubyte[] plaintext = cast(ubyte[])"This is a test This is a test This is a test This is a test";
  440. // Creating key from public key only
  441. auto keypairB = new RSA(publicKeyA);
  442. scope(exit) keypairB.destroy();
  443. auto publicKeyB = keypairB.getPublicKey();
  444. assert(publicKeyA == publicKeyB, "Public keys A and B does not match");
  445. // Creating key from private key only
  446. auto keypairC = new RSA(privateKeyA, null);
  447. scope(exit) keypairC.destroy();
  448. auto privateKeyC = keypairC.getPrivateKey(null);
  449. assert(privateKeyA == privateKeyC, "Private keys A and C does not match");
  450. // Sealing plaintext using public key
  451. ubyte[] encMessage = keypairB.seal(plaintext);
  452. // Opening encrypted message using private key
  453. ubyte[] decMessage = keypairC.open(encMessage);
  454. assert(plaintext.length == decMessage.length);
  455. assert(plaintext == decMessage);
  456. }
  457. // ----------------------------------------------------------
  458. unittest
  459. {
  460. import std.stdio;
  461. writeln("Testing RSA only encrypt/decrypt functions:");
  462. auto keypair = new RSA();
  463. scope(exit) keypair.destroy();
  464. ubyte[48] plaintext = [ 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF,
  465. 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF,
  466. 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF ];
  467. ubyte[] encMessage = keypair.encrypt(plaintext);
  468. ubyte[] decMessage = keypair.decrypt(encMessage);
  469. assert(plaintext.length == decMessage.length);
  470. assert(plaintext == decMessage);
  471. }
  472. // ----------------------------------------------------------
  473. unittest
  474. {
  475. import std.stdio;
  476. writeln("Testing RSA encrypt/decrypt limit:");
  477. auto keypair = new RSA(2048); // Only allows for (2048/8)-42 = 214 bytes to be asymmetrically RSA encrypted
  478. scope(exit) keypair.destroy();
  479. // This should work
  480. ubyte[214] plaintext214 = 2; // 2 being an arbitrary value
  481. ubyte[] encMessage214 = keypair.encrypt(plaintext214);
  482. assert(encMessage214.length == 2048 / 8);
  483. ubyte[] decMessage214 = keypair.decrypt(encMessage214);
  484. assert(plaintext214.length == decMessage214.length);
  485. assert(plaintext214 == decMessage214);
  486. // This should NOT work, as the plaintext is larger that allowed for this 2048 bit RSA keypair
  487. ubyte[215] plaintext215 = 2; // 2 being an arbitrary value
  488. import std.exception: assertThrown;
  489. assertThrown!CryptographicException(keypair.encrypt(plaintext215));
  490. }
  491. // ----------------------------------------------------------
  492. unittest
  493. {
  494. import std.stdio;
  495. writeln("Testing RSA Signing/Verification:");
  496. import std.digest;
  497. auto keypair = new RSA();
  498. scope(exit) keypair.destroy();
  499. ubyte[48] data = [ 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF,
  500. 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF,
  501. 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF ];
  502. ubyte[48] data2 = [ 0x1, 0x2, 0x3, 0x4, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF,
  503. 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF,
  504. 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF ];
  505. ubyte[] sig = keypair.sign(data);
  506. writeln("Signature: ", toHexString!(LetterCase.lower)(sig));
  507. assert(keypair.verify(data, sig));
  508. assert(!keypair.verify(data2, sig));
  509. }