1 module scrypt;
2 
3 import std.string : indexOf;
4 import std.exception;
5 import std.digest.digest : toHexString;
6 import std.uuid : randomUUID;
7 import std.algorithm : splitter;
8 import std.array: array;
9 import std.conv: to;
10 
11 import random;
12 
13 uint SCRYPT_RAND_LEN = 32;
14 ulong SCRYPT_N_DEFAULT = 16384;
15 uint SCRYPT_R_DEFAULT = 8;
16 uint SCRYPT_P_DEFAULT = 1;
17 size_t SCRYPT_OUTPUTLEN_DEFAULT = 128;
18 
19 bool SCRYPT_DEBUG = false;
20 
21 public class ScryptException : Exception
22 {
23     this(string message) {super(message);}
24 }
25 
26 public class SodiumChloride {
27     public string na;
28     public ubyte[] cl;
29 
30     this(uint len) {
31         try {
32             ubyte[] randomNum = scryptRNG(len);
33 
34             this.na = randomNum.toHexString();
35             this.cl = cast(ubyte[])this.na;
36         } catch (ScryptRNGException rng) {
37             throw new ScryptException(rng.msg);
38         }
39     }
40 
41     this() {
42         this(SCRYPT_RAND_LEN);
43     }
44 
45     this(string str) {
46         this.na = str.idup;
47         this.cl = cast(ubyte[])this.na;
48     }
49 }
50 
51 ubyte[] hex_to_ubyteArray(string hexnum) {
52     import std.conv: parse;
53     import std.array: array;
54     import std.range: chunks;
55     import std.algorithm: map;
56 
57     ubyte[] bytes = hexnum.chunks(2)
58                 .map!(twoDigits => twoDigits.parse!ubyte(16)).array();
59 
60     return bytes;
61 }
62 
63 string to_hex(ubyte[] bytes) {
64     import std.digest.digest;
65 
66     return bytes.toHexString();
67 }
68 
69 ubyte[] generatePassword(string password) {
70     SodiumChloride salt = new SodiumChloride();
71 
72     return generatePassword(password, salt.cl);
73 }
74 
75 ubyte[] generatePassword(string password, ubyte[] salt) {
76     ubyte[] outpw = new ubyte[SCRYPT_OUTPUTLEN_DEFAULT];
77     libscrypt_scrypt(cast(ubyte*)password.ptr, password.length, cast(ubyte*)salt.ptr, salt.length, SCRYPT_N_DEFAULT, SCRYPT_R_DEFAULT, SCRYPT_P_DEFAULT, outpw.ptr, outpw.length);
78 
79     return outpw ~ salt;
80 }
81 
82 ubyte[] crypto_scrypt(string password, ubyte[] salt, ulong N, uint r, uint p, ulong L) {
83     ubyte[] outpw = new ubyte[L];
84     libscrypt_scrypt(cast(ubyte*)password.ptr,
85 					 password.length,
86 					 cast(ubyte*)salt.ptr,
87 					 salt.length,
88 					 N,
89 					 r,
90 					 p,
91 					 outpw.ptr,
92 					 outpw.length);
93 
94     return outpw ~ salt;
95 }
96 
97 bool checkPassword(ubyte[] hash, string password) {
98 	import std.conv;
99 	import std.stdio;
100 
101 	bool ret = false;
102 
103 	if (SCRYPT_OUTPUTLEN_DEFAULT > hash.length) return ret;
104 
105     auto salt = hash[SCRYPT_OUTPUTLEN_DEFAULT..$];
106 	if(SCRYPT_DEBUG) writeln("---- > found salt : " ~ to!string(salt));
107 	auto checkHash = generatePassword(password, salt);
108 	if(SCRYPT_DEBUG) writeln("---- > hash : " ~ to!string(hash));
109 	if(SCRYPT_DEBUG) writeln("---- > chkh : " ~ to!string(checkHash));
110 	if (checkHash == hash) ret = true;
111 	if(SCRYPT_DEBUG) writeln("---- > returning : " ~ to!string(ret));
112     return ret;
113 }
114 
115 bool checkPassword(ubyte[] hash, string password, ulong N, uint r, uint p, ulong L) {
116 	import std.conv;
117 	import std.stdio;
118 
119 	bool ret = false;
120 
121 	if (L > hash.length) return ret;
122 
123     auto salt = hash[L..$];
124 	if(SCRYPT_DEBUG) writeln("---- > found salt : " ~ to!string(salt) ~ "(" ~ cast(string)salt ~ ")");
125 	auto checkHash = crypto_scrypt(password, salt,N,r,p,L);
126 	if(SCRYPT_DEBUG) writeln("---- > hash : " ~ to!string(hash));
127 	if(SCRYPT_DEBUG) writeln("---- > chkh : " ~ to!string(checkHash));
128 	if (checkHash == hash) ret = true;
129 	if(SCRYPT_DEBUG) writeln("---- > returning : " ~ to!string(ret));
130     return ret;
131 }
132 
133 unittest {
134     import std.stdio;
135 
136 	SCRYPT_DEBUG = true;
137 
138     //
139 	// unit test #1
140 	//
141     writeln("+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=");
142     writeln("+ test #1: generatePassword");
143     writeln("+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=");
144     auto input = "test";
145     auto hash = generatePassword(input);
146     writeln("*** > input: " ~ input);
147     writeln("*** > scrypt: " ~ to!string(hash));
148     writeln("*** > scrypt Hex: " ~ hash.toHexString());
149     assert(hash !is null);
150 
151     writeln("*** test: checkPassword");
152     assert(checkPassword(hash, input));
153     writeln("*** > passed");
154 
155     writeln("*** test: checkPassword (mismatch)");
156     assert(!checkPassword(hash, "not-test"));
157     writeln("*** > passed");
158 
159     //
160     //unit test #2
161     //
162     writeln("\n\n+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=");
163     writeln("+ test #2: convert to hex string and back!");
164     writeln("+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=");
165 
166     string hex = scrypt.to_hex(hash);
167     ubyte[] back_to_hash = scrypt.hex_to_ubyteArray(hex);
168 
169     writeln("*** > orignal hash: " ~ to!string(hash));
170     writeln("*** > to_hex: " ~ hex);
171     writeln("*** > back to hash: " ~ to!string(back_to_hash));
172     assert(hash == back_to_hash);
173     writeln("*** > passed");
174 
175 	//
176 	// unit test #3
177 	//
178     writeln("\n\n+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=");
179     writeln("+ test #3: crypto_scrypt with static salt");
180     writeln("+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=");
181 	SodiumChloride salt = new SodiumChloride("test");
182     auto password = "password";
183     auto L = SCRYPT_OUTPUTLEN_DEFAULT;
184     auto N = SCRYPT_N_DEFAULT;
185     auto r = SCRYPT_R_DEFAULT;
186     auto p = SCRYPT_P_DEFAULT;
187     auto hash2 = crypto_scrypt(password, salt.cl, N, r, p, L);
188 
189     writeln("*** > salt: " ~ salt.na);
190     writeln("*** > password: " ~ input);
191     writeln("*** > L: " ~ to!string(L));
192     writeln("*** > N: " ~ to!string(N));
193     writeln("*** > R: " ~ to!string(r));
194     writeln("*** > P: " ~ to!string(p));
195     writeln("*** > scrypt: " ~ to!string(hash2));
196     writeln("*** > scrypt Hex: " ~ scrypt.to_hex(hash2));
197     assert(hash2 !is null);
198 
199 
200     writeln("*** test: checkPassword");
201     assert(checkPassword(hash2, password,N,r,p,L));
202     writeln("*** > passed");
203 
204     writeln("*** test: checkPassword (mismatch)");
205     assert(!checkPassword(hash2, "aBogusPassword",N,r,p,L));
206     writeln("*** > passed");
207 
208 	//
209 	// unit test #4
210 	//
211     writeln("\n\n+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=");
212     writeln("+ test #4: crypto_scrypt with random salt and Length=512");
213     writeln("+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=");
214 	salt = new SodiumChloride();
215     password = "password";
216     L = 512;
217     N = SCRYPT_N_DEFAULT;
218     r = SCRYPT_R_DEFAULT;
219     p = SCRYPT_P_DEFAULT;
220     hash2 = crypto_scrypt(password, salt.cl, N, r, p, L);
221 
222     writeln("*** > salt: " ~ salt.na);
223     writeln("*** > password: " ~ input);
224     writeln("*** > L: " ~ to!string(L));
225     writeln("*** > N: " ~ to!string(N));
226     writeln("*** > R: " ~ to!string(r));
227     writeln("*** > P: " ~ to!string(p));
228     writeln("*** > scrypt: " ~ to!string(hash2));
229     writeln("*** > scrypt Hex: " ~ to_hex(hash2));
230     assert(hash2 !is null);
231 
232     writeln("*** test: checkPassword");
233     assert(checkPassword(hash2, password,N,r,p,L));
234     writeln("*** > passed");
235 
236     writeln("*** test: checkPassword (mismatch)");
237     assert(!checkPassword(hash2, "aBogusPassword",N,r,p,L));
238     writeln("*** > passed");
239 }
240 
241 
242 private:
243 
244 //import std.c.stdio;
245 import core.stdc.stdio;
246 
247 alias ubyte uint8_t;
248 alias ulong uint64_t;
249 alias uint uint32_t;
250 
251 extern (C):
252 
253 /**
254  * libscrypt_scrypt(passwd, passwdlen, salt, saltlen, N, r, p, buf, buflen):
255  * Compute scrypt(passwd[0 .. passwdlen - 1], salt[0 .. saltlen - 1], N, r,
256  * p, buflen) and write the result into buf.  The parameters r, p, and buflen
257  * must satisfy r * p < 2^30 and buflen <= (2^32 - 1) * 32.  The parameter N
258  * must be a power of 2 greater than 1.
259  *
260  * Return 0 on success; or -1 on error.
261  */
262 int libscrypt_scrypt(const uint8_t *, size_t, const uint8_t *, size_t, uint64_t,
263     uint32_t, uint32_t, uint8_t *, size_t);
264