1 module secured.kdf; 2 3 import std.typecons; 4 import std.format; 5 6 import deimos.openssl.evp; 7 import secured.openssl; 8 9 import secured.hash; 10 import secured.random; 11 import secured.util; 12 13 public struct KdfResult { 14 public ubyte[] salt; 15 public ubyte[] key; 16 } 17 18 @safe public KdfResult pbkdf2(string password, uint iterations = 1_000_000) { 19 KdfResult result; 20 result.salt = random(getHashLength(HashAlgorithm.SHA2_384)); 21 result.key = pbkdf2_ex(password, result.salt, HashAlgorithm.SHA2_384, getHashLength(HashAlgorithm.SHA2_384), iterations); 22 return result; 23 } 24 25 @safe public bool pbkdf2_verify(KdfResult test, string password, uint iterations = 1_000_000) { 26 ubyte[] key = pbkdf2_ex(password, test.salt, HashAlgorithm.SHA2_384, getHashLength(HashAlgorithm.SHA2_384), iterations); 27 return constantTimeEquality(test.key, key); 28 } 29 30 @trusted public ubyte[] pbkdf2_ex(string password, const ubyte[] salt, HashAlgorithm func, uint outputLen, uint iterations) 31 { 32 if (salt.length != getHashLength(func)) { 33 throw new CryptographicException(format("The PBKDF2 salt must be %s bytes in length.", getHashLength(func))); 34 } 35 if (outputLen > getHashLength(func)) { 36 throw new CryptographicException(format("The PBKDF2 output length must be less than or equal to %s bytes in length.", getHashLength(func))); 37 } 38 39 ubyte[] output = new ubyte[outputLen]; 40 if(PKCS5_PBKDF2_HMAC(password.ptr, cast(int)password.length, salt.ptr, cast(int)salt.length, iterations, getOpenSSLHashAlgorithm(func), outputLen, output.ptr) == 0) { 41 throw new CryptographicException("Unable to execute PBKDF2 hash function."); 42 } 43 return output; 44 } 45 46 @safe public bool pbkdf2_verify_ex(const ubyte[] test, string password, const ubyte[] salt, HashAlgorithm func, uint outputLen, uint iterations) { 47 ubyte[] key = pbkdf2_ex(password, salt, func, outputLen, iterations); 48 return constantTimeEquality(test, key); 49 } 50 51 unittest 52 { 53 import std.datetime.stopwatch; 54 import std.digest; 55 import std.stdio; 56 57 writeln("Testing PBKDF2 Basic Methods:"); 58 59 //Test basic methods 60 auto sw = StopWatch(AutoStart.no); 61 sw.start(); 62 auto result = pbkdf2("password"); 63 sw.stop(); 64 writefln("PBKDF2 took %sms for 1,000,000 iterations", sw.peek.total!"msecs"); 65 66 assert(result.key.length == 48); 67 assert(pbkdf2_verify(result, "password")); 68 writeln(toHexString!(LetterCase.lower)(result.key)); 69 70 //Test extended methods 71 ubyte[64] salt = [ 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 72 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 73 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 74 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF ]; 75 76 ubyte[] key = pbkdf2_ex("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", salt, HashAlgorithm.SHA2_512, 64, 100000); 77 assert(pbkdf2_verify_ex(key, "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", salt, HashAlgorithm.SHA2_512, 64, 100000)); 78 writeln(toHexString!(LetterCase.lower)(key)); 79 } 80 81 unittest 82 { 83 import std.digest; 84 import std.stdio; 85 86 writeln("Testing PBKDF2 Extended with Defaults:"); 87 88 ubyte[48] key = [ 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 89 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 90 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF ]; 91 92 ubyte[] vec1 = pbkdf2_ex("", key, HashAlgorithm.SHA2_384, 48, 25000); 93 ubyte[] vec2 = pbkdf2_ex("abc", key, HashAlgorithm.SHA2_384, 48, 25000); 94 ubyte[] vec3 = pbkdf2_ex("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", key, HashAlgorithm.SHA2_384, 48, 25000); 95 96 writeln(toHexString!(LetterCase.lower)(vec1)); 97 writeln(toHexString!(LetterCase.lower)(vec2)); 98 writeln(toHexString!(LetterCase.lower)(vec3)); 99 100 assert(toHexString!(LetterCase.lower)(vec1) == "b0ddf56b90903d638ec8d07a4205ba2bcfa944955d553e1ef3f91cba84e8e3bde9db7c8ccf14df26f8305fc8634572f9"); 101 assert(toHexString!(LetterCase.lower)(vec2) == "b0a5e09a38bee3eb2b84d477d5259ef7bebf0e48d9512178f7e26cc330278ff45417d47d84db06a12b8ea49377a7c7cb"); 102 assert(toHexString!(LetterCase.lower)(vec3) == "d1aacafea3a9fdf3ee6236b1b45527974ea01539b4a7cc493bba56e15e14d520b2834d7bf22b83bb5c21c4bccb423be2"); 103 } 104 105 unittest 106 { 107 import std.digest; 108 import std.stdio; 109 110 writeln("Testing PBKDF2 Extended with Custom Iterations:"); 111 112 ubyte[48] key = [0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 113 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 114 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF ]; 115 116 ubyte[] vec1 = pbkdf2_ex("", key, HashAlgorithm.SHA2_384, 48, 150000); 117 ubyte[] vec2 = pbkdf2_ex("abc", key, HashAlgorithm.SHA2_384, 48, 150000); 118 ubyte[] vec3 = pbkdf2_ex("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", key, HashAlgorithm.SHA2_384, 48, 150000); 119 120 writeln(toHexString!(LetterCase.lower)(vec1)); 121 writeln(toHexString!(LetterCase.lower)(vec2)); 122 writeln(toHexString!(LetterCase.lower)(vec3)); 123 124 assert(toHexString!(LetterCase.lower)(vec1) == "babdcbbf4ff89367ed223d2edd06ef5473ac9cdc827783ed0b4b5eafd9e4097beb2ef66d6fc92d24dbf4b86aa51b4a0f"); 125 assert(toHexString!(LetterCase.lower)(vec2) == "8894348ccea06d79f80382ae7d4434c0f2ef41f871d936604f426518ab23bde4410fddce6dad943c95de75dbece9b54a"); 126 assert(toHexString!(LetterCase.lower)(vec3) == "fba55e91818c35b1e4cc753fbd01a6cd138c49da472b58b2d7c4860ba39a3dd9032f8f641aadcd74a819361ed27c9a0f"); 127 } 128 129 unittest 130 { 131 import std.digest; 132 import std.stdio; 133 134 writeln("Testing PBKDF2 Extended with Custom Output Length:"); 135 136 ubyte[48] key = [ 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 137 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 138 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF ]; 139 140 ubyte[] vec1 = pbkdf2_ex("", key, HashAlgorithm.SHA2_384, 32, 25000); 141 ubyte[] vec2 = pbkdf2_ex("abc", key, HashAlgorithm.SHA2_384, 32, 25000); 142 ubyte[] vec3 = pbkdf2_ex("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", key, HashAlgorithm.SHA2_384, 32, 25000); 143 144 writeln(toHexString!(LetterCase.lower)(vec1)); 145 writeln(toHexString!(LetterCase.lower)(vec2)); 146 writeln(toHexString!(LetterCase.lower)(vec3)); 147 148 assert(toHexString!(LetterCase.lower)(vec1) == "b0ddf56b90903d638ec8d07a4205ba2bcfa944955d553e1ef3f91cba84e8e3bd"); 149 assert(toHexString!(LetterCase.lower)(vec2) == "b0a5e09a38bee3eb2b84d477d5259ef7bebf0e48d9512178f7e26cc330278ff4"); 150 assert(toHexString!(LetterCase.lower)(vec3) == "d1aacafea3a9fdf3ee6236b1b45527974ea01539b4a7cc493bba56e15e14d520"); 151 } 152 153 @safe public KdfResult hkdf(const ubyte[] key, ulong outputLen) { 154 KdfResult result; 155 result.salt = random(getHashLength(HashAlgorithm.SHA2_384)); 156 result.key = hkdf_ex(key, result.salt, string.init, outputLen, HashAlgorithm.SHA2_384); 157 return result; 158 } 159 160 @trusted public ubyte[] hkdf_ex(const ubyte[] key, const ubyte[] salt, string info, ulong outputLen, HashAlgorithm func) { 161 if (key.length == 0) { 162 throw new CryptographicException("HKDF key cannot be an empty array."); 163 } 164 165 EVP_PKEY_CTX* pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, null); 166 scope(exit) { 167 if(pctx !is null) { 168 EVP_PKEY_CTX_free(pctx); 169 } 170 } 171 172 if (EVP_PKEY_derive_init(pctx) <= 0) { 173 throw new CryptographicException("Unable to create HKDF function."); 174 } 175 176 if (EVP_PKEY_CTX_set_hkdf_md(pctx, getOpenSSLHashAlgorithm(func)) <= 0) { 177 throw new CryptographicException("Unable to create HKDF hash function."); 178 } 179 180 if (salt.length != 0 && EVP_PKEY_CTX_set1_hkdf_salt(pctx, salt) <= 0) { 181 throw new CryptographicException("Unable to set HKDF salt."); 182 } 183 184 if (info.length != 0 && EVP_PKEY_CTX_add1_hkdf_info(pctx, info) <= 0) { 185 throw new CryptographicException("Unable to set HKDF info."); 186 } 187 188 if (EVP_PKEY_CTX_set1_hkdf_key(pctx, key) <= 0) { 189 throw new CryptographicException("Unable to set HKDF key."); 190 } 191 192 ubyte[] keyMaterial = new ubyte[outputLen]; 193 if (EVP_PKEY_derive(pctx, keyMaterial.ptr, &outputLen) <= 0) { 194 throw new CryptographicException("Unable to generate the requested key material."); 195 } 196 197 return keyMaterial; 198 } 199 200 unittest 201 { 202 import std.digest; 203 import std.stdio; 204 205 writeln("Testing HKDF Extended with Defaults:"); 206 207 ubyte[48] salt = [ 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 208 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 209 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF ]; 210 211 ubyte[] vec2 = hkdf_ex(cast(ubyte[])"abc", salt, "", 64, HashAlgorithm.SHA2_224); 212 ubyte[] vec3 = hkdf_ex(cast(ubyte[])"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", salt, "test", 64, HashAlgorithm.SHA2_224); 213 214 writeln(toHexString!(LetterCase.lower)(vec2)); 215 writeln(toHexString!(LetterCase.lower)(vec3)); 216 217 assert(toHexString!(LetterCase.lower)(vec2) == "0fcc4d227bb180f4a2631da9bf158203ced36a73752d1f6fb05be764cbd13460556e6ddd69b0d3b2cdf08457a18253811e38d8059177e5dc22b5b52a6b1cb30a"); 218 assert(toHexString!(LetterCase.lower)(vec3) == "d05e4ba15e07095b8b6dc3abbdde3f790fb4c1d6146e93e12312fbf54b5a1aff4c9c9108046fc390f2bef5fbcbf44d57ac05732525ccbf0a856821fe178f47c2"); 219 } 220 221 @safe public KdfResult scrypt(string password) { 222 KdfResult result; 223 result.salt = random(32); 224 result.key = scrypt_ex(password, result.salt, 1_048_576, 8, 1, 1_074_790_400, 64); 225 return result; 226 } 227 228 @safe public KdfResult scrypt(const ubyte[] password) { 229 KdfResult result; 230 result.salt = random(32); 231 result.key = scrypt_ex(password, result.salt, 1_048_576, 8, 1, 1_074_790_400, 64); 232 return result; 233 } 234 235 @trusted public ubyte[] scrypt_ex(string password, const ubyte[] salt, ulong n, ulong r, ulong p, ulong maxMemory, ulong length) { 236 import std.string; 237 return scrypt_ex(cast(ubyte[])password.representation, salt, n, r, p, maxMemory, length); 238 } 239 240 @trusted public ubyte[] scrypt_ex(const ubyte[] password, const ubyte[] salt, ulong n, ulong r, ulong p, ulong maxMemory, ulong length) { 241 ubyte[] hash = new ubyte[length]; 242 243 if (EVP_PBE_scrypt((cast(char[])password).ptr, password.length, salt.ptr, salt.length, n, r, p, maxMemory, hash.ptr, length) <= 0) { 244 throw new CryptographicException("Unable to calculate SCrypt hash."); 245 } 246 247 return hash; 248 } 249 250 unittest 251 { 252 import std.digest; 253 import std.stdio; 254 255 writeln("Testing SCrypt Extended with Defaults:"); 256 257 ubyte[48] salt = [ 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 258 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 259 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF ]; 260 261 ubyte[] vec2 = scrypt_ex("abc", salt, 1_048_576, 8, 1, 1_074_790_400, 64); 262 ubyte[] vec3 = scrypt_ex("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", salt, 1_048_576, 8, 1, 1_074_790_400, 64); 263 264 writeln(toHexString!(LetterCase.lower)(vec2)); 265 writeln(toHexString!(LetterCase.lower)(vec3)); 266 267 assert(toHexString!(LetterCase.lower)(vec2) == "134fca5087e04c2a79e0ea2c793660f19d466db74a069e1f2e4da2b177d51402501bd39ffc592b9419ec0280cc17dca7af8df54f836179d69a4b9e9f6b9467fd"); 268 assert(toHexString!(LetterCase.lower)(vec3) == "45397ec370eb31f3155ad162d83ec165ff8e363bc4e03c1c61c5a31ad17d0dac51d9e8911f32e9b588adf284a9de24561483dbaf0ea519b6a29ecae77eab5b90"); 269 }