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
JavaScript
;
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;
};