1 module viva.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 viva.scrypt.crypto; 11 import std.string : indexOf; 12 import std.exception : enforce; 13 import std.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 = genRandomSalt(), 41 size_t scrypt_outputlen = SCRYPT_OUTPUTLEN_DEFAULT, ulong N = SCRYPT_N_DEFAULT, uint r = SCRYPT_R_DEFAULT, 42 uint p = SCRYPT_P_DEFAULT) @trusted { 43 ubyte[] outpw = new ubyte[scrypt_outputlen]; 44 libscrypt_scrypt(cast(ubyte*)password.ptr, password.length, cast(ubyte*)salt.ptr, salt.length, N, r, p, outpw.ptr, outpw.length); 45 46 return toHexString(outpw).idup ~ TERMINATOR ~ salt ~ TERMINATOR ~ to!string(scrypt_outputlen) ~ TERMINATOR ~ to!string(N) ~ TERMINATOR ~ to!string(r) ~ TERMINATOR ~ to!string(p); 47 } 48 49 /** 50 * Some info regarding the params 51 * hash: An already hashed version of your password, for example fetched from a database 52 * password: The password you wish to see if it matches 53 */ 54 bool checkScryptPasswordHash(string hash, string password) @trusted { 55 auto params = hash.splitter(TERMINATOR).array[1 .. $]; 56 enforce(params.length == 5, "invalid hash string, does not meet requirements"); 57 return genScryptPasswordHash(password, params[0], to!size_t(params[1]), to!ulong(params[2]), to!uint(params[3]), to!uint(params[4])) == hash; 58 }