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