unixpass
Version:
Native implementation of Unix compatible DES/MD5/SHA256/SHA512 password hashing.
58 lines (54 loc) • 1.52 kB
JavaScript
'use stict';
const blf = require('blowfish-js');
const bc64 = require('./pb64.js').bc;
function bcrypt(password, salt) {
var r;
try {
let cdata = Buffer.from('OrpheanBeholderScryDoubt');
if (typeof(password) !== 'string') {
throw new TypeError('Password not a string');
}
if (typeof(salt) !== 'string') {
throw new TypeError('Salt not a string');
}
let m = salt.match(/^(\$2([abxy]|)\$(\d\d)\$)([./0-9A-Za-z]{22})/);
if (! m) {
throw new RangeError('Malformed salt');
}
let variant = m[2];
let cost = Number.parseInt(m[3], 10);
if ((cost === undefined) ||
(cost === null) ||
(cost < 4) ||
(cost > 30)) {
throw new RangeError('Invalid cost');
}
let rounds = 1 << cost;
let passbuf = Buffer.from((password + '\0').slice(0, 72), 'utf8');
let salthead = m[1];
let saltbuf = bc64.dec(m[4]);
let cipher = blf.allocState();
cipher = blf.expandState(cipher, passbuf, saltbuf);
for (let i = 0; i < rounds; i++) {
cipher = blf.expandState(cipher, passbuf);
cipher = blf.expandState(cipher, saltbuf);
}
if (cdata.length != 24) {
throw new Error('Unexpected plaintext length');
}
for (let i = 0; i < 64; i++) {
cdata = blf.ecb(cipher, cdata);
}
if (cdata.length != 24) {
throw new Error('Unexpected ciphertext length');
}
cdata = cdata.slice(0, 23);
r = salthead + bc64.enc(saltbuf) + bc64.enc(cdata);
} catch (e) {
console.log(e);
r = undefined;
throw new Error('Password hash error');
}
return r;
}
module.exports = bcrypt;