random.d 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. module secured.random;
  2. import secured.util;
  3. version(CRuntime_Bionic)
  4. version = SecureARC4Random;//ChaCha20
  5. else version(OSX)
  6. version = SecureARC4Random;//AES
  7. else version(OpenBSD)
  8. version = SecureARC4Random;//ChaCha20
  9. else version(NetBSD)
  10. version = SecureARC4Random;//ChaCha20
  11. // Can uncomment following two lines if Solaris versions prior to 11.3 are unsupported:
  12. //else version (Solaris)
  13. // version = SecureARC4Random;
  14. version(SecureARC4Random)
  15. extern(C) @nogc nothrow private @system
  16. {
  17. void arc4random_buf(scope void* buf, size_t nbytes);
  18. }
  19. @trusted public ubyte[] random(uint bytes)
  20. {
  21. if (bytes == 0) {
  22. throw new CryptographicException("The number of requested bytes must be greater than zero.");
  23. }
  24. ubyte[] buffer = new ubyte[bytes];
  25. version(SecureARC4Random)
  26. {
  27. arc4random_buf(buffer.ptr, bytes);
  28. }
  29. else version(Posix)
  30. {
  31. import std.exception;
  32. import std.format;
  33. import std.stdio;
  34. try {
  35. //Initialize the system random file buffer
  36. File urandom = File("/dev/urandom", "rb");
  37. urandom.setvbuf(null, _IONBF);
  38. scope(exit) urandom.close();
  39. //Read into the buffer
  40. try {
  41. buffer = urandom.rawRead(buffer);
  42. }
  43. catch(ErrnoException ex) {
  44. throw new CryptographicException(format("Cannot get the next random bytes. Error ID: %d, Message: %s", ex.errno, ex.msg));
  45. }
  46. catch(Exception ex) {
  47. throw new CryptographicException(format("Cannot get the next random bytes. Message: %s", ex.msg));
  48. }
  49. }
  50. catch(ErrnoException ex) {
  51. throw new CryptographicException(format("Cannot initialize the system RNG. Error ID: %d, Message: %s", ex.errno, ex.msg));
  52. }
  53. catch(Exception ex) {
  54. throw new CryptographicException(format("Cannot initialize the system RNG. Message: %s", ex.msg));
  55. }
  56. }
  57. else version(Windows)
  58. {
  59. import core.sys.windows.windows;
  60. import core.sys.windows.wincrypt;
  61. import std.format;
  62. HCRYPTPROV hCryptProv;
  63. //Get the cryptographic context from Windows
  64. if (!CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
  65. throw new CryptographicException("Unable to acquire Cryptographic Context.");
  66. }
  67. //Release the context when finished
  68. scope(exit) CryptReleaseContext(hCryptProv, 0);
  69. //Generate the random bytes
  70. if (!CryptGenRandom(hCryptProv, cast(DWORD)buffer.length, buffer.ptr)) {
  71. throw new CryptographicException(format("Cannot get the next random bytes. Error ID: %d", GetLastError()));
  72. }
  73. }
  74. else
  75. {
  76. static assert(0, "SecureD does not support this OS.");
  77. }
  78. return buffer;
  79. }
  80. unittest
  81. {
  82. import std.digest;
  83. import std.stdio;
  84. writeln("Testing Random Number Generator with 32/64/512/2048 bytes:");
  85. //Test 32 bytes
  86. ubyte[] rnd1 = random(32);
  87. writeln("32 Bytes:");
  88. writeln(toHexString!(LetterCase.lower)(rnd1));
  89. assert(rnd1.length == 32);
  90. //Test 128 bytes
  91. ubyte[] rnd2 = random(128);
  92. writeln("128 Bytes:");
  93. writeln(toHexString!(LetterCase.lower)(rnd2));
  94. assert(rnd2.length == 128);
  95. //Test 512 bytes
  96. ubyte[] rnd3 = random(512);
  97. writeln("512 Bytes:");
  98. writeln(toHexString!(LetterCase.lower)(rnd3));
  99. assert(rnd3.length == 512);
  100. //Test 2048 bytes
  101. ubyte[] rnd4 = random(2048);
  102. writeln("2048 Bytes:");
  103. writeln(toHexString!(LetterCase.lower)(rnd4));
  104. assert(rnd4.length == 2048);
  105. }
  106. unittest
  107. {
  108. import std.digest;
  109. import std.stdio;
  110. writeln("Testing Random Number Generator for Equality:");
  111. //Test 32 bytes
  112. ubyte[] rnd1 = random(32);
  113. ubyte[] rnd2 = random(32);
  114. writeln("Testing with 32 Bytes");
  115. assert(!constantTimeEquality(rnd1, rnd2));
  116. //Test 128 bytes
  117. rnd1 = random(128);
  118. rnd2 = random(128);
  119. writeln("Testing with 128 Bytes");
  120. assert(!constantTimeEquality(rnd1, rnd2));
  121. //Test 512 bytes
  122. rnd1 = random(512);
  123. rnd2 = random(512);
  124. writeln("Testing with 512 Bytes");
  125. assert(!constantTimeEquality(rnd1, rnd2));
  126. //Test 2048 bytes
  127. rnd1 = random(2048);
  128. rnd2 = random(2048);
  129. writeln("Testing with 2048 Bytes");
  130. assert(!constantTimeEquality(rnd1, rnd2));
  131. }