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