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 }