Skip to content

Instantly share code, notes, and snippets.

@tracker1
Created September 1, 2020 20:53
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tracker1/87bbebbf235e697588fc9d9b8ca4f0a2 to your computer and use it in GitHub Desktop.
Save tracker1/87bbebbf235e697588fc9d9b8ca4f0a2 to your computer and use it in GitHub Desktop.
Passphrase Hashing
import util from 'util';
import crypto from 'crypto';
const HASH_ALGO = 'sha512';
// nist recommends 10000, 10x nist, approx 83ms on i7-8650U
const HASH_ITERATIONS = 100000;
const HASH_SALT_BYTES = 16; // Exceeds NIST Minimum 32/8 (4 bytes)
const HASH_BYTES = 64; // Match Hash Algorithm lengh / bits (512 / 8);
const atob = input => Buffer.from(input, 'base64');
const btoa = input => input.toString('base64').replace(/\=+$/, '');
const h = util.promisify(crypto.pbkdf2);
const pbkdf2 = (input, salt) => h(input, salt, HASH_ITERATIONS, HASH_BYTES, HASH_ALGO);
const gs = util.promisify(crypto.randomBytes);
const generateSalt = () => gs(HASH_SALT_BYTES);
const normalize = str => Buffer.from(String(str).normalize('NFKC'));
export const hash = async str => {
const salt = await generateSalt();
const result = await pbkdf2(normalize(str), salt);
return `${btoa(salt)}.${btoa(result)}`;
};
export const verify = async (hash, str) => {
if (typeof hash !== 'string') return false; // invalid input
if (typeof str !== 'string') return false; // invalid input
if (hash.length > 120) return false; // hash is impossibly long
const [salt, result] = hash.split('.');
const result2 = await pbkdf2(normalize(str), atob(salt));
return result === btoa(result2);
};
export default { hash, verify };
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment