Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | 1x 1x 1x 1x 1x 15x 15x 15x 15x 15x 15x 15x 15x 15x 15x 15x 15x 15x 15x 15x 15x 15x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 15x 15x 15x 15x 15x 12x 15x 11x 11x 11x 11x 11x 11x 11x 11x 11x 15x 1x 1x 2x 8x 8x 8x 2x 2x 1x | import {randomBytes, randomInt, scrypt, timingSafeEqual} from 'crypto'
/**
* Passwords for band members. Admins generate them; nobody picks a weak one
* unless they change it themselves (minimum length enforced).
*
* Hash format: scrypt$<N>$<r>$<p>$<salt b64>$<hash b64>
*/
const N = 16384
const R = 8
const P = 1
const KEYLEN = 32
function scryptAsync(
password: string,
salt: Buffer,
n: number,
r: number,
p: number,
) {
return new Promise<Buffer>((resolve, reject) =>
scrypt(
password,
salt,
KEYLEN,
{N: n, r, p, maxmem: 64 * 1024 * 1024},
(err, key) => (err ? reject(err) : resolve(key)),
),
)
}
export async function hashPassword(password: string) {
const salt = randomBytes(16)
const hash = await scryptAsync(password.normalize('NFKC'), salt, N, R, P)
return [
'scrypt',
N,
R,
P,
salt.toString('base64'),
hash.toString('base64'),
].join('$')
}
export async function verifyPassword(
password: string,
stored: string | null | undefined,
) {
if (!stored) return false
const [alg, n, r, p, salt, hash] = stored.split('$')
if (alg !== 'scrypt' || !salt || !hash) return false
const expected = Buffer.from(hash, 'base64')
const actual = await scryptAsync(
password.normalize('NFKC'),
Buffer.from(salt, 'base64'),
Number(n),
Number(r),
Number(p),
)
return actual.length === expected.length && timingSafeEqual(actual, expected)
}
// No 0/o, 1/l/i: these get read aloud and typed on phones
const ALPHABET = 'abcdefghjkmnpqrstuvwxyz23456789'
/** e.g. "k7mq-x2vd-9rta-hp3e": ~79 bits, easy to type on a phone. */
export function generatePassword(groups = 4, size = 4) {
return Array.from({length: groups}, () =>
Array.from({length: size}, () => ALPHABET[randomInt(ALPHABET.length)]).join(
'',
),
).join('-')
}
export const MIN_PASSWORD_LENGTH = 10
|