1 /++ 2 My minimal interface to https://github.com/p-h-c/phc-winner-argon2 3 4 You must compile and install the C library separately. 5 +/ 6 module arsd.argon2; 7 8 // a password length limitation might legit make sense here cuz of the hashing function can get slow 9 10 // it is conceivably useful to hash the password with a secret key before passing to this function, 11 // but I'm not going to do that automatically here just to keep this thin and simple. 12 13 import core.stdc.stdint; 14 15 pragma(lib, "argon2"); 16 17 extern(C) 18 int argon2id_hash_encoded( 19 const uint32_t t_cost, 20 const uint32_t m_cost, 21 const uint32_t parallelism, 22 const void *pwd, const size_t pwdlen, 23 const void *salt, const size_t saltlen, 24 const size_t hashlen, char *encoded, 25 const size_t encodedlen); 26 27 extern(C) 28 int argon2id_verify(const char *encoded, const void *pwd, 29 const size_t pwdlen); 30 31 enum ARGON2_OK = 0; 32 33 /// Parameters to the argon2 function. Bigger numbers make it harder to 34 /// crack, but also take more resources for legitimate users too 35 /// (e.g. making logins and signups slower and more memory-intensive). Some 36 /// examples are provided. HighSecurity is about 3/4 second on my computer, 37 /// MediumSecurity about 1/3 second, LowSecurity about 1/10 second. 38 struct SecurityParameters { 39 uint cpuCost; 40 uint memoryCost; /// in KiB fyi 41 uint parallelism; 42 } 43 44 /// ditto 45 enum HighSecurity = SecurityParameters(8, 512_000, 8); 46 /// ditto 47 enum MediumSecurity = SecurityParameters(4, 256_000, 4); 48 /// ditto 49 enum LowSecurity = SecurityParameters(2, 128_000, 4); 50 51 /// Check's a user's provided password against the saved password, and returns true if they matched. Neither string can be empty. 52 bool verify(string savedPassword, string providedPassword) { 53 return argon2id_verify((savedPassword[$-1] == 0 ? savedPassword : (savedPassword ~ '\0')).ptr, providedPassword.ptr, providedPassword.length) == ARGON2_OK; 54 } 55 56 /// encode a password for secure storage. verify later with [verify] 57 string encode(string password, SecurityParameters params = MediumSecurity) { 58 char[256] buffer; 59 enum HASHLEN = 80; 60 61 import core.stdc.string; 62 63 ubyte[32] salt = void; 64 65 version(linux) {{ 66 import core.sys.posix.unistd; 67 import core.sys.posix.fcntl; 68 int fd = open("/dev/urandom", O_RDONLY); 69 auto ret = read(fd, salt.ptr, salt.length); 70 assert(ret == salt.length); 71 close(fd); 72 }} else version(Windows) {{ 73 // https://docs.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom 74 static assert(0); 75 }} else { 76 import std.random; 77 foreach(ref s; salt) 78 s = cast(ubyte) uniform(0, 256); 79 80 static assert(0, "csrng not implemented"); 81 } 82 83 auto ret = argon2id_hash_encoded( 84 params.cpuCost, 85 params.memoryCost, 86 params.parallelism, 87 password.ptr, password.length, 88 salt.ptr, salt.length, 89 HASHLEN, // desired size of hash. I think this is fine being arbitrary 90 buffer.ptr, 91 buffer.length 92 ); 93 94 if(ret != ARGON2_OK) 95 throw new Exception("wtf"); 96 97 return buffer[0 .. strlen(buffer.ptr) + 1].idup; 98 }