1 module scrypt.password; 2 3 /* 4 * Copyright (C) 2013 Isak Andersson (BitPuffin@lavabit.com) 5 * 6 * Distributed under the terms of the zlib/libpng license 7 * See LICENSE.txt in project root for more info 8 */ 9 10 import scrypt.crypto_scrypt; 11 import std.string : indexOf; 12 import std.exception : enforce; 13 import std.digest.digest : toHexString; 14 import std.uuid : randomUUID; 15 import std.algorithm : splitter; 16 import std.array: array; 17 import std.conv: to; 18 19 enum SCRYPT_N_DEFAULT = 16384; 20 enum SCRYPT_R_DEFAULT = 8; 21 enum SCRYPT_P_DEFAULT = 1; 22 enum SCRYPT_OUTPUTLEN_DEFAULT = 90; 23 private enum TERMINATOR = '$'; 24 25 /// Takes no parameters, returns a random UUID in string form 26 string genRandomSalt() { 27 return randomUUID().toString(); 28 } 29 30 /** 31 * Some info regarding the params 32 * password: The password you want to hash, for example "my password" 33 * salt: The salt you want to use when hashing your password, for example generateRandomSalt(); 34 * scrypt_outputlen: the length of the output string containing your hashed password from scrypt. Reccomended value is 90. Note, the actual output won't be 90 since it's a sha1 digest 35 * N: General work factor, iteration count. Must be power of two. Recommended value for passwords: 2^14 and 2^20 for sensitive stuff 36 * r: Blocksize for underlying hash. Reccommended value is 8 37 * p: parallelization factor. Reccomended value is 1 38 * If you want to you can use SCRYPT_N_DEFAULT, SCRYPT_R_DEFAULT, SCRYPT_P_DEFAULT, SCRYPT_OUTPUTLEN_DEFAULT as default params 39 */ 40 string genScryptPasswordHash(string password, string salt, size_t scrypt_outputlen, ulong N, uint r, uint p) { 41 ubyte[] outpw = new ubyte[scrypt_outputlen]; 42 crypto_scrypt(cast(ubyte*)password.ptr, password.length, cast(ubyte*)salt.ptr, salt.length, N, r, p, outpw.ptr, outpw.length); 43 44 return toHexString(outpw).idup ~ TERMINATOR ~ salt ~ TERMINATOR ~ to!string(scrypt_outputlen) ~ TERMINATOR ~ to!string(N) ~ TERMINATOR ~ to!string(r) ~ TERMINATOR ~ to!string(p); 45 } 46 47 /** 48 * Some info regarding the params 49 * hash: An already hashed version of your password, for example fetched from a database 50 * password: The password you wish to see if it matches 51 */ 52 bool checkScryptPasswordHash(string hash, string password) { 53 auto params = hash.splitter(TERMINATOR).array[1 .. $]; 54 enforce(params.length == 5, "invalid hash string, does not meet requirements"); 55 return genScryptPasswordHash(password, params[0], to!size_t(params[1]), to!ulong(params[2]), to!uint(params[3]), to!uint(params[4])) == hash; 56 } 57