You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
74 lines
2.0 KiB
74 lines
2.0 KiB
/** |
|
* Проверка хеша в формате Werkzeug 3 (scrypt:… / pbkdf2:…). |
|
* @see https://github.com/pallets/werkzeug/blob/main/src/werkzeug/security.py |
|
*/ |
|
import crypto from 'crypto'; |
|
|
|
/** |
|
* @param {string} pwhash |
|
* @param {string} password |
|
* @returns {boolean} |
|
*/ |
|
function hashInternal(method, salt, password) { |
|
const methodParts = method.split(':'); |
|
const kind = methodParts[0]; |
|
const saltBytes = Buffer.from(salt, 'utf8'); |
|
const passwordBytes = Buffer.from(password, 'utf8'); |
|
|
|
if (kind === 'scrypt') { |
|
const n = methodParts[1] ? parseInt(methodParts[1], 10) : 2 ** 15; |
|
const r = methodParts[2] ? parseInt(methodParts[2], 10) : 8; |
|
const p = methodParts[3] ? parseInt(methodParts[3], 10) : 1; |
|
const maxmem = 132 * n * r * p; |
|
return crypto |
|
.scryptSync(passwordBytes, saltBytes, 64, { N: n, r, p, maxmem }) |
|
.toString('hex'); |
|
} |
|
|
|
if (kind === 'pbkdf2') { |
|
const hashName = methodParts[1] || 'sha256'; |
|
const iterStr = methodParts[2]; |
|
if (!iterStr) { |
|
throw new Error('pbkdf2: missing iterations'); |
|
} |
|
const iterations = parseInt(iterStr, 10); |
|
return crypto |
|
.pbkdf2Sync(passwordBytes, saltBytes, iterations, 32, hashName) |
|
.toString('hex'); |
|
} |
|
|
|
throw new Error(`Invalid hash method: ${kind}`); |
|
} |
|
|
|
/** |
|
* @param {string} pwhash |
|
* @param {string} password |
|
* @returns {boolean} |
|
*/ |
|
export function checkWerkzeugPassword(pwhash, password) { |
|
if (!pwhash || pwhash.length < 3) { |
|
return false; |
|
} |
|
const parts = pwhash.split('$'); |
|
if (parts.length < 3) { |
|
return false; |
|
} |
|
const hashval = parts.pop(); |
|
const salt = parts.pop(); |
|
const method = parts.join('$'); |
|
if (!method || !salt || !hashval) { |
|
return false; |
|
} |
|
let computed; |
|
try { |
|
computed = hashInternal(method, salt, password); |
|
} catch { |
|
return false; |
|
} |
|
const a = Buffer.from(computed, 'hex'); |
|
const b = Buffer.from(hashval, 'hex'); |
|
if (a.length !== b.length) { |
|
return false; |
|
} |
|
return crypto.timingSafeEqual(a, b); |
|
}
|
|
|