UNPKG

asp-identity-pw

Version:

Password hash and validation library that is compatible with the default ASP.NET Core Identity framework.

149 lines (148 loc) 6.99 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.verifyPassword = exports.hashIdentityPasswordV3 = exports.hashIdentityPasswordV2 = void 0; const crypto = __importStar(require("crypto")); const key_derivation_prf_1 = require("./key_derivation_prf"); /** * PBKDF2 with HMAC-SHA1, 128-bit salt, 256-bit subkey, 1000 iterations. * Format: { 0x00, salt, subkey } * (See also: SDL crypto guidelines v5.1, Part III) * @param password Hashed password as base64 encoded string */ const hashIdentityPasswordV2 = (password) => { const options = { salt: crypto.randomBytes(128 / 8), prf: key_derivation_prf_1.KeyDerivationPrf.HMAC_SHA1, iter: 1000, keylen: 256 / 8, }; return hashPassword(password, options.salt, options.prf, options.iter, options.keylen); }; exports.hashIdentityPasswordV2 = hashIdentityPasswordV2; /** * PBKDF2 with HMAC-SHA256, 128-bit salt, 256-bit subkey, 10000 iterations. * Format: { 0x01, prf (UInt32), iter count (UInt32), salt length (UInt32), salt, subkey } * (All UInt32s are stored big-endian.) * @param password The password that should be hashed. * @returns Hashed password as base64 encoded string */ const hashIdentityPasswordV3 = (password) => { const options = { salt: crypto.randomBytes(128 / 8), prf: key_derivation_prf_1.KeyDerivationPrf.HMAC_SHA256, iter: 10000, keylen: 256 / 8, }; return hashPassword(password, options.salt, options.prf, options.iter, options.keylen); }; exports.hashIdentityPasswordV3 = hashIdentityPasswordV3; /** * Verifies a password that was hashed using the @see hashPassword function. * @param password The password to verify. * @param hash The hash to verify the password against (in base64 encoding). * @returns True if the password matches the hash, false otherwise. */ const verifyPassword = (password, hash) => { const hashBuffer = Buffer.from(hash, 'base64'); const usedPrf = getPrfMethodFromHash(hashBuffer); const digestString = key_derivation_prf_1.KdPrfDigestMap[usedPrf]; switch (usedPrf) { case key_derivation_prf_1.KeyDerivationPrf.HMAC_SHA1: { const iterationCount = 1000; const saltLength = 128 / 8; const salt = Buffer.alloc(saltLength); hashBuffer.copy(salt, 0, 1, 1 + saltLength); const derivedKeyLength = hashBuffer.length - 1 - saltLength; const derivedKey = Buffer.alloc(derivedKeyLength); hashBuffer.copy(derivedKey, 0, 1 + saltLength, 1 + saltLength + derivedKeyLength); const result = crypto.pbkdf2Sync(password, salt, iterationCount, derivedKeyLength, digestString); return result.equals(derivedKey); } case key_derivation_prf_1.KeyDerivationPrf.HMAC_SHA256: { const iterationCount = readNetworkByteOrder(hashBuffer, 5); const saltLength = readNetworkByteOrder(hashBuffer, 9); const salt = Buffer.alloc(saltLength); hashBuffer.copy(salt, 0, 13, 13 + saltLength); const derivedKeyLength = hashBuffer.length - 13 - saltLength; const derivedKey = Buffer.alloc(derivedKeyLength); hashBuffer.copy(derivedKey, 0, 13 + saltLength, 13 + saltLength + derivedKeyLength); const result = crypto.pbkdf2Sync(password, salt, iterationCount, derivedKeyLength, digestString); return result.equals(derivedKey); } default: throw new Error(`Unknown key derivation prf: ${usedPrf}`); } }; exports.verifyPassword = verifyPassword; const hashPassword = (password, salt, prf, iterations, keyLength) => { const digestString = key_derivation_prf_1.KdPrfDigestMap[prf]; const saltBuffer = Buffer.isBuffer(salt) ? salt : Buffer.from(salt, 'binary'); if (!digestString) throw new Error(`Unknown key derivation prf: ${prf}`); const derivedKey = crypto.pbkdf2Sync(password, saltBuffer, iterations, keyLength, digestString); switch (prf) { case key_derivation_prf_1.KeyDerivationPrf.HMAC_SHA1: { const outputBytes = Buffer.alloc(1 + saltBuffer.length + derivedKey.length); outputBytes[0] = PrfBitMap[prf]; saltBuffer.copy(outputBytes, 1); derivedKey.copy(outputBytes, 1 + saltBuffer.length); return outputBytes.toString('base64'); } case key_derivation_prf_1.KeyDerivationPrf.HMAC_SHA256: { const outputBytes = Buffer.alloc(13 + salt.length + derivedKey.length); outputBytes[0] = PrfBitMap[prf]; writeNetworkByteOrder(outputBytes, 1, 1); writeNetworkByteOrder(outputBytes, 5, iterations); writeNetworkByteOrder(outputBytes, 9, salt.length); saltBuffer.copy(outputBytes, 13); derivedKey.copy(outputBytes, 13 + salt.length); return outputBytes.toString('base64'); } default: throw new Error(`Unknown key derivation prf: ${prf}`); } }; const getPrfMethodFromHash = (hash) => { const hashBytes = Buffer.isBuffer(hash) ? hash : Buffer.from(hash, 'base64'); return BitPrfMap[hashBytes[0]]; }; const PrfBitMap = { [key_derivation_prf_1.KeyDerivationPrf.HMAC_SHA1]: 0x00, [key_derivation_prf_1.KeyDerivationPrf.HMAC_SHA256]: 0x01, }; const BitPrfMap = { [0x00]: key_derivation_prf_1.KeyDerivationPrf.HMAC_SHA1, [0x01]: key_derivation_prf_1.KeyDerivationPrf.HMAC_SHA256, }; const readNetworkByteOrder = (buffer, offset) => { return ((buffer[offset + 0] << 24) | (buffer[offset + 1] << 16) | (buffer[offset + 2] << 8) | buffer[offset + 3]); }; const writeNetworkByteOrder = (buffer, offset, value) => { // Magic numbers beste numbers buffer[offset + 0] = (value >> 24) & 0xff; buffer[offset + 1] = (value >> 16) & 0xff; buffer[offset + 2] = (value >> 8) & 0xff; buffer[offset + 3] = (value >> 0) & 0xff; };