UNPKG

11.3 kBJavaScriptView Raw
1(function (root, factory) {
2 if (typeof define === 'function' && define.amd) {
3 define([], factory);
4 } else if (typeof module === 'object' && module.exports) {
5 module.exports = factory();
6 } else {
7 root.argon2 = factory();
8 }
9})(typeof self !== 'undefined' ? self : this, function () {
10 const global = typeof self !== 'undefined' ? self : this;
11
12 /**
13 * @enum
14 */
15 const ArgonType = {
16 Argon2d: 0,
17 Argon2i: 1,
18 Argon2id: 2,
19 };
20
21 function loadModule(mem) {
22 if (loadModule._promise) {
23 return loadModule._promise;
24 }
25 if (loadModule._module) {
26 return Promise.resolve(loadModule._module);
27 }
28 let promise;
29 if (
30 global.process &&
31 global.process.versions &&
32 global.process.versions.node
33 ) {
34 promise = loadWasmModule().then(
35 (Module) =>
36 new Promise((resolve) => {
37 Module.postRun = () => resolve(Module);
38 })
39 );
40 } else {
41 promise = loadWasmBinary().then((wasmBinary) => {
42 const wasmMemory = mem ? createWasmMemory(mem) : undefined;
43 return initWasm(wasmBinary, wasmMemory);
44 });
45 }
46 loadModule._promise = promise;
47 return promise.then((Module) => {
48 loadModule._module = Module;
49 delete loadModule._promise;
50 return Module;
51 });
52 }
53
54 function initWasm(wasmBinary, wasmMemory) {
55 return new Promise((resolve) => {
56 global.Module = {
57 wasmBinary,
58 wasmMemory,
59 postRun() {
60 resolve(Module);
61 },
62 };
63 return loadWasmModule();
64 });
65 }
66
67 function loadWasmModule() {
68 if (global.loadArgon2WasmModule) {
69 return global.loadArgon2WasmModule();
70 }
71 if (typeof require === 'function') {
72 return Promise.resolve(require('../dist/argon2.js'));
73 }
74 return import('../dist/argon2.js');
75 }
76
77 function loadWasmBinary() {
78 if (global.loadArgon2WasmBinary) {
79 return global.loadArgon2WasmBinary();
80 }
81 if (typeof require === 'function') {
82 return Promise.resolve(require('../dist/argon2.wasm')).then(
83 (wasmModule) => {
84 return decodeWasmBinary(wasmModule);
85 }
86 );
87 }
88 const wasmPath =
89 global.argon2WasmPath ||
90 'node_modules/argon2-browser/dist/argon2.wasm';
91 return fetch(wasmPath)
92 .then((response) => response.arrayBuffer())
93 .then((ab) => new Uint8Array(ab));
94 }
95
96 function decodeWasmBinary(base64) {
97 const text = atob(base64);
98 const binary = new Uint8Array(new ArrayBuffer(text.length));
99 for (let i = 0; i < text.length; i++) {
100 binary[i] = text.charCodeAt(i);
101 }
102 return binary;
103 }
104
105 function createWasmMemory(mem) {
106 const KB = 1024;
107 const MB = 1024 * KB;
108 const GB = 1024 * MB;
109 const WASM_PAGE_SIZE = 64 * KB;
110
111 const totalMemory = (2 * GB - 64 * KB) / WASM_PAGE_SIZE;
112 const initialMemory = Math.min(
113 Math.max(Math.ceil((mem * KB) / WASM_PAGE_SIZE), 256) + 256,
114 totalMemory
115 );
116
117 return new WebAssembly.Memory({
118 initial: initialMemory,
119 maximum: totalMemory,
120 });
121 }
122
123 function allocateArray(Module, arr) {
124 return Module.allocate(arr, 'i8', Module.ALLOC_NORMAL);
125 }
126
127 function allocateArrayStr(Module, arr) {
128 const nullTerminatedArray = new Uint8Array([...arr, 0]);
129 return allocateArray(Module, nullTerminatedArray);
130 }
131
132 function encodeUtf8(str) {
133 if (typeof str !== 'string') {
134 return str;
135 }
136 if (typeof TextEncoder === 'function') {
137 return new TextEncoder().encode(str);
138 } else if (typeof Buffer === 'function') {
139 return Buffer.from(str);
140 } else {
141 throw new Error("Don't know how to encode UTF8");
142 }
143 }
144
145 /**
146 * Argon2 hash
147 * @param {string|Uint8Array} params.pass - password string
148 * @param {string|Uint8Array} params.salt - salt string
149 * @param {number} [params.time=1] - the number of iterations
150 * @param {number} [params.mem=1024] - used memory, in KiB
151 * @param {number} [params.hashLen=24] - desired hash length
152 * @param {number} [params.parallelism=1] - desired parallelism
153 * @param {number} [params.type=argon2.ArgonType.Argon2d] - hash type:
154 * argon2.ArgonType.Argon2d
155 * argon2.ArgonType.Argon2i
156 * argon2.ArgonType.Argon2id
157 *
158 * @return Promise
159 *
160 * @example
161 * argon2.hash({ pass: 'password', salt: 'somesalt' })
162 * .then(h => console.log(h.hash, h.hashHex, h.encoded))
163 * .catch(e => console.error(e.message, e.code))
164 */
165 function argon2Hash(params) {
166 const mCost = params.mem || 1024;
167 return loadModule(mCost).then((Module) => {
168 const tCost = params.time || 1;
169 const parallelism = params.parallelism || 1;
170 const pwdEncoded = encodeUtf8(params.pass);
171 const pwd = allocateArrayStr(Module, pwdEncoded);
172 const pwdlen = pwdEncoded.length;
173 const saltEncoded = encodeUtf8(params.salt);
174 const salt = allocateArrayStr(Module, saltEncoded);
175 const saltlen = saltEncoded.length;
176 const argon2Type = params.type || ArgonType.Argon2d;
177 const hash = Module.allocate(
178 new Array(params.hashLen || 24),
179 'i8',
180 Module.ALLOC_NORMAL
181 );
182 const secret = params.secret
183 ? allocateArray(Module, params.secret)
184 : 0;
185 const secretlen = params.secret ? params.secret.byteLength : 0;
186 const ad = params.ad ? allocateArray(Module, params.ad) : 0;
187 const adlen = params.ad ? params.ad.byteLength : 0;
188 const hashlen = params.hashLen || 24;
189 const encodedlen = Module._argon2_encodedlen(
190 tCost,
191 mCost,
192 parallelism,
193 saltlen,
194 hashlen,
195 argon2Type
196 );
197 const encoded = Module.allocate(
198 new Array(encodedlen + 1),
199 'i8',
200 Module.ALLOC_NORMAL
201 );
202 const version = 0x13;
203 let err;
204 let res;
205 try {
206 res = Module._argon2_hash_ext(
207 tCost,
208 mCost,
209 parallelism,
210 pwd,
211 pwdlen,
212 salt,
213 saltlen,
214 hash,
215 hashlen,
216 encoded,
217 encodedlen,
218 argon2Type,
219 secret,
220 secretlen,
221 ad,
222 adlen,
223 version
224 );
225 } catch (e) {
226 err = e;
227 }
228 let result;
229 if (res === 0 && !err) {
230 let hashStr = '';
231 const hashArr = new Uint8Array(hashlen);
232 for (let i = 0; i < hashlen; i++) {
233 const byte = Module.HEAP8[hash + i];
234 hashArr[i] = byte;
235 hashStr += ('0' + (0xff & byte).toString(16)).slice(-2);
236 }
237 const encodedStr = Module.UTF8ToString(encoded);
238 result = {
239 hash: hashArr,
240 hashHex: hashStr,
241 encoded: encodedStr,
242 };
243 } else {
244 try {
245 if (!err) {
246 err = Module.UTF8ToString(
247 Module._argon2_error_message(res)
248 );
249 }
250 } catch (e) {}
251 result = { message: err, code: res };
252 }
253 try {
254 Module._free(pwd);
255 Module._free(salt);
256 Module._free(hash);
257 Module._free(encoded);
258 if (ad) {
259 Module._free(ad);
260 }
261 if (secret) {
262 Module._free(secret);
263 }
264 } catch (e) {}
265 if (err) {
266 throw result;
267 } else {
268 return result;
269 }
270 });
271 }
272
273 /**
274 * Argon2 verify function
275 * @param {string} params.pass - password string
276 * @param {string|Uint8Array} params.encoded - encoded hash
277 * @param {number} [params.type=argon2.ArgonType.Argon2d] - hash type:
278 * argon2.ArgonType.Argon2d
279 * argon2.ArgonType.Argon2i
280 * argon2.ArgonType.Argon2id
281 *
282 * @returns Promise
283 *
284 * @example
285 * argon2.verify({ pass: 'password', encoded: 'encoded-hash' })
286 * .then(() => console.log('OK'))
287 * .catch(e => console.error(e.message, e.code))
288 */
289 function argon2Verify(params) {
290 return loadModule().then((Module) => {
291 const pwdEncoded = encodeUtf8(params.pass);
292 const pwd = allocateArrayStr(Module, pwdEncoded);
293 const pwdlen = pwdEncoded.length;
294 const encEncoded = encodeUtf8(params.encoded);
295 const enc = allocateArrayStr(Module, encEncoded);
296 let argon2Type = params.type;
297 if (argon2Type === undefined) {
298 let typeStr = params.encoded.split('$')[1];
299 if (typeStr) {
300 typeStr = typeStr.replace('a', 'A');
301 argon2Type = ArgonType[typeStr] || ArgonType.Argon2d;
302 }
303 }
304 let err;
305 let res;
306 try {
307 res = Module._argon2_verify(enc, pwd, pwdlen, argon2Type);
308 } catch (e) {
309 err = e;
310 }
311 let result;
312 if (res || err) {
313 try {
314 if (!err) {
315 err = Module.UTF8ToString(
316 Module._argon2_error_message(res)
317 );
318 }
319 } catch (e) {}
320 result = { message: err, code: res };
321 }
322 try {
323 Module._free(pwd);
324 Module._free(enc);
325 } catch (e) {}
326 if (err) {
327 throw result;
328 } else {
329 return result;
330 }
331 });
332 }
333
334 function unloadRuntime() {
335 if (loadModule._module) {
336 loadModule._module.unloadRuntime();
337 delete loadModule._promise;
338 delete loadModule._module;
339 }
340 }
341
342 return {
343 ArgonType,
344 hash: argon2Hash,
345 verify: argon2Verify,
346 unloadRuntime,
347 };
348});