hash.d 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. module secured.hash;
  2. import std.stdio;
  3. import secured.openssl;
  4. import deimos.openssl.evp;
  5. import secured.util;
  6. public enum HashAlgorithm : ubyte {
  7. None,
  8. SHA2_256,
  9. SHA2_384,
  10. SHA2_512,
  11. SHA2_512_224,
  12. SHA2_512_256,
  13. SHA3_224,
  14. SHA3_256,
  15. SHA3_384,
  16. SHA3_512,
  17. Default = SHA2_384,
  18. }
  19. @safe public ubyte[] hash(const ubyte[] data) {
  20. return hash_ex(data, HashAlgorithm.Default);
  21. }
  22. @safe public bool hash_verify(ubyte[] test, ubyte[] data) {
  23. ubyte[] hash = hash_ex(data, HashAlgorithm.Default);
  24. return constantTimeEquality(hash, test);
  25. }
  26. @trusted public ubyte[] hash_ex(const ubyte[] data, HashAlgorithm func)
  27. {
  28. //Create the OpenSSL context
  29. EVP_MD_CTX *mdctx;
  30. if ((mdctx = EVP_MD_CTX_new()) == null) {
  31. throw new CryptographicException("Unable to create OpenSSL context.");
  32. }
  33. scope(exit) {
  34. if(mdctx !is null) {
  35. EVP_MD_CTX_free(mdctx);
  36. }
  37. }
  38. //Initialize the hash algorithm
  39. if (EVP_DigestInit_ex(mdctx, getOpenSSLHashAlgorithm(func), null) < 0) {
  40. throw new CryptographicException("Unable to create hash context.");
  41. }
  42. //Run the provided data through the digest algorithm
  43. if (EVP_DigestUpdate(mdctx, data.ptr, data.length) < 0) {
  44. throw new CryptographicException("Error while updating digest.");
  45. }
  46. //Copy the OpenSSL digest to our D buffer.
  47. uint digestlen;
  48. ubyte[] digest = new ubyte[getHashLength(func)];
  49. if (EVP_DigestFinal_ex(mdctx, digest.ptr, &digestlen) < 0) {
  50. throw new CryptographicException("Error while retrieving the digest.");
  51. }
  52. return digest;
  53. }
  54. @safe public bool hash_verify_ex(const ubyte[] test, const ubyte[] data, HashAlgorithm func) {
  55. ubyte[] hash = hash_ex(data, func);
  56. return constantTimeEquality(hash, test);
  57. }
  58. unittest {
  59. import std.digest;
  60. writeln("Testing SHA2 Byte Array Hash:");
  61. ubyte[] vec1 = hash_ex(cast(ubyte[])"", HashAlgorithm.SHA2_384);
  62. ubyte[] vec2 = hash_ex(cast(ubyte[])"abc", HashAlgorithm.SHA2_384);
  63. ubyte[] vec3 = hash_ex(cast(ubyte[])"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", HashAlgorithm.SHA2_384);
  64. ubyte[] vec4 = hash_ex(cast(ubyte[])"The quick brown fox jumps over the lazy dog.", HashAlgorithm.SHA2_384);
  65. writeln(toHexString!(LetterCase.lower)(vec1));
  66. writeln(toHexString!(LetterCase.lower)(vec2));
  67. writeln(toHexString!(LetterCase.lower)(vec3));
  68. writeln(toHexString!(LetterCase.lower)(vec4));
  69. assert(toHexString!(LetterCase.lower)(vec1) == "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b");
  70. assert(toHexString!(LetterCase.lower)(vec2) == "cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7");
  71. assert(toHexString!(LetterCase.lower)(vec3) == "3391fdddfc8dc7393707a65b1b4709397cf8b1d162af05abfe8f450de5f36bc6b0455a8520bc4e6f5fe95b1fe3c8452b");
  72. assert(toHexString!(LetterCase.lower)(vec4) == "ed892481d8272ca6df370bf706e4d7bc1b5739fa2177aae6c50e946678718fc67a7af2819a021c2fc34e91bdb63409d7");
  73. }
  74. unittest {
  75. import std.digest;
  76. writeln("Testing SHA3 Byte Array Hash:");
  77. ubyte[] vec1 = hash_ex(cast(ubyte[])"", HashAlgorithm.SHA3_384);
  78. ubyte[] vec2 = hash_ex(cast(ubyte[])"abc", HashAlgorithm.SHA3_384);
  79. ubyte[] vec3 = hash_ex(cast(ubyte[])"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", HashAlgorithm.SHA3_384);
  80. ubyte[] vec4 = hash_ex(cast(ubyte[])"The quick brown fox jumps over the lazy dog.", HashAlgorithm.SHA3_384);
  81. writeln(toHexString!(LetterCase.lower)(vec1));
  82. writeln(toHexString!(LetterCase.lower)(vec2));
  83. writeln(toHexString!(LetterCase.lower)(vec3));
  84. writeln(toHexString!(LetterCase.lower)(vec4));
  85. assert(toHexString!(LetterCase.lower)(vec1) == "0c63a75b845e4f7d01107d852e4c2485c51a50aaaa94fc61995e71bbee983a2ac3713831264adb47fb6bd1e058d5f004");
  86. assert(toHexString!(LetterCase.lower)(vec2) == "ec01498288516fc926459f58e2c6ad8df9b473cb0fc08c2596da7cf0e49be4b298d88cea927ac7f539f1edf228376d25");
  87. assert(toHexString!(LetterCase.lower)(vec3) == "991c665755eb3a4b6bbdfb75c78a492e8c56a22c5c4d7e429bfdbc32b9d4ad5aa04a1f076e62fea19eef51acd0657c22");
  88. assert(toHexString!(LetterCase.lower)(vec4) == "1a34d81695b622df178bc74df7124fe12fac0f64ba5250b78b99c1273d4b080168e10652894ecad5f1f4d5b965437fb9");
  89. }
  90. @safe public ubyte[] hash(string path) {
  91. return hash_ex(path, HashAlgorithm.Default);
  92. }
  93. @safe public bool hash_verify(string path, ubyte[] test) {
  94. ubyte[] hash = hash_ex(path, HashAlgorithm.Default);
  95. return constantTimeEquality(hash, test);
  96. }
  97. @trusted public ubyte[] hash_ex(string path, HashAlgorithm func)
  98. {
  99. //Open the file for reading
  100. auto fsfile = File(path, "rb");
  101. scope(exit) {
  102. if(fsfile.isOpen()) {
  103. fsfile.close();
  104. }
  105. }
  106. //Create the OpenSSL context
  107. EVP_MD_CTX *mdctx;
  108. if ((mdctx = EVP_MD_CTX_new()) == null) {
  109. throw new CryptographicException("Unable to create OpenSSL context.");
  110. }
  111. scope(exit) {
  112. if(mdctx !is null) {
  113. EVP_MD_CTX_free(mdctx);
  114. }
  115. }
  116. //Initialize the hash algorithm
  117. if (EVP_DigestInit_ex(mdctx, getOpenSSLHashAlgorithm(func), null) < 0) {
  118. throw new CryptographicException("Unable to create hash context.");
  119. }
  120. //Read the file in chunks and update the Digest
  121. foreach(ubyte[] data; fsfile.byChunk(FILE_BUFFER_SIZE)) {
  122. if (EVP_DigestUpdate(mdctx, data.ptr, data.length) < 0) {
  123. throw new CryptographicException("Error while updating digest.");
  124. }
  125. }
  126. //Copy the OpenSSL digest to our D buffer.
  127. uint digestlen;
  128. ubyte[] digest = new ubyte[getHashLength(func)];
  129. if (EVP_DigestFinal_ex(mdctx, digest.ptr, &digestlen) < 0) {
  130. throw new CryptographicException("Error while retrieving the digest.");
  131. }
  132. return digest;
  133. }
  134. @safe public bool hash_verify_ex(string path, HashAlgorithm func, ubyte[] test) {
  135. ubyte[] hash = hash_ex(path, func);
  136. return constantTimeEquality(hash, test);
  137. }
  138. unittest {
  139. import std.digest;
  140. writeln("Testing File Hash:");
  141. auto f = File("hashtest.txt", "wb");
  142. f.rawWrite("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq");
  143. f.close();
  144. ubyte[] vec = hash_ex("hashtest.txt", HashAlgorithm.SHA2_384);
  145. writeln(toHexString!(LetterCase.lower)(vec));
  146. assert(toHexString!(LetterCase.lower)(vec) == "3391fdddfc8dc7393707a65b1b4709397cf8b1d162af05abfe8f450de5f36bc6b0455a8520bc4e6f5fe95b1fe3c8452b");
  147. remove("hashtest.txt");
  148. }
  149. @trusted package const(EVP_MD)* getOpenSSLHashAlgorithm(HashAlgorithm func) {
  150. import std.conv;
  151. import std.format;
  152. switch (func) {
  153. case HashAlgorithm.SHA2_256: return EVP_sha256();
  154. case HashAlgorithm.SHA2_384: return EVP_sha384();
  155. case HashAlgorithm.SHA2_512: return EVP_sha512();
  156. case HashAlgorithm.SHA2_512_224: return EVP_sha512_224();
  157. case HashAlgorithm.SHA2_512_256: return EVP_sha512_256();
  158. case HashAlgorithm.SHA3_224: return EVP_sha3_224();
  159. case HashAlgorithm.SHA3_256: return EVP_sha3_256();
  160. case HashAlgorithm.SHA3_384: return EVP_sha3_384();
  161. case HashAlgorithm.SHA3_512: return EVP_sha3_512();
  162. default:
  163. throw new CryptographicException(format("Hash Function '%s' is not supported by OpenSSL.", to!string(func)));
  164. }
  165. }
  166. @trusted package string getOpenSSLHashAlgorithmString(HashAlgorithm func) {
  167. import std.conv;
  168. import std.format;
  169. switch (func) {
  170. case HashAlgorithm.SHA2_256: return "sha256";
  171. case HashAlgorithm.SHA2_384: return "sha384";
  172. case HashAlgorithm.SHA2_512: return "sha512";
  173. case HashAlgorithm.SHA2_512_224: return "sha512-224";
  174. case HashAlgorithm.SHA2_512_256: return "sha512-256";
  175. case HashAlgorithm.SHA3_224: return "sha3-224";
  176. case HashAlgorithm.SHA3_256: return "sha3-256";
  177. case HashAlgorithm.SHA3_384: return "sha3-384";
  178. case HashAlgorithm.SHA3_512: return "sha3-512";
  179. default:
  180. throw new CryptographicException(format("Hash Function '%s' is not supported by OpenSSL.", to!string(func)));
  181. }
  182. }
  183. @safe package uint getHashLength(HashAlgorithm func) {
  184. import std.conv;
  185. import std.format;
  186. switch (func) {
  187. case HashAlgorithm.None: return 0;
  188. case HashAlgorithm.SHA2_256: return 32;
  189. case HashAlgorithm.SHA2_384: return 48;
  190. case HashAlgorithm.SHA2_512: return 64;
  191. case HashAlgorithm.SHA2_512_224: return 24;
  192. case HashAlgorithm.SHA2_512_256: return 32;
  193. case HashAlgorithm.SHA3_224: return 24;
  194. case HashAlgorithm.SHA3_256: return 32;
  195. case HashAlgorithm.SHA3_384: return 48;
  196. case HashAlgorithm.SHA3_512: return 64;
  197. default:
  198. throw new CryptographicException(format("Hash Function '%s' is not supported.", to!string(func)));
  199. }
  200. }