-
Notifications
You must be signed in to change notification settings - Fork 13
/
Copy pathcommon.js
89 lines (78 loc) · 2.24 KB
/
common.js
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
'use strict';
const crypto = require('node:crypto');
const SCRYPT_PARAMS = { N: 32768, r: 8, p: 1, maxmem: 64 * 1024 * 1024 };
const SCRYPT_PREFIX = '$scrypt$N=32768,r=8,p=1,maxmem=67108864$';
const serializeHash = (hash, salt) => {
const saltString = salt.toString('base64').split('=')[0];
const hashString = hash.toString('base64').split('=')[0];
return `${SCRYPT_PREFIX}${saltString}$${hashString}`;
};
const parseOptions = (options) => {
const values = [];
const items = options.split(',');
for (const item of items) {
const [key, val] = item.split('=');
values.push([key, Number(val)]);
}
return Object.fromEntries(values);
};
const deserializeHash = (phcString) => {
const [, name, options, salt64, hash64] = phcString.split('$');
if (name !== 'scrypt') {
throw new Error('Node.js crypto module only supports scrypt');
}
const params = parseOptions(options);
const salt = Buffer.from(salt64, 'base64');
const hash = Buffer.from(hash64, 'base64');
return { params, salt, hash };
};
const SALT_LEN = 32;
const KEY_LEN = 64;
const hashPassword = (password) =>
new Promise((resolve, reject) => {
crypto.randomBytes(SALT_LEN, (err, salt) => {
if (err) {
reject(err);
return;
}
crypto.scrypt(password, salt, KEY_LEN, SCRYPT_PARAMS, (err, hash) => {
if (err) {
reject(err);
return;
}
resolve(serializeHash(hash, salt));
});
});
});
const validatePassword = (password, serHash) => {
const { params, salt, hash } = deserializeHash(serHash);
return new Promise((resolve, reject) => {
const callback = (err, hashedPassword) => {
if (err) {
reject(err);
return;
}
resolve(crypto.timingSafeEqual(hashedPassword, hash));
};
crypto.scrypt(password, salt, hash.length, params, callback);
});
};
const jsonParse = (buffer) => {
if (buffer.length === 0) return null;
try {
return JSON.parse(buffer);
} catch {
return null;
}
};
const receiveBody = async (req) => {
const buffers = [];
for await (const chunk of req) buffers.push(chunk);
return Buffer.concat(buffers).toString();
};
module.exports = Object.freeze({
hashPassword,
validatePassword,
jsonParse,
receiveBody,
});