File size: 1,965 Bytes
163c686 | 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 | const encoder = new TextEncoder();
const iterations = 310000;
export async function hashPassword(password, salt = crypto.getRandomValues(new Uint8Array(16))) {
const key = await crypto.subtle.importKey(
"raw",
encoder.encode(password),
"PBKDF2",
false,
["deriveBits"]
);
const bits = await crypto.subtle.deriveBits(
{
name: "PBKDF2",
hash: "SHA-256",
salt,
iterations
},
key,
256
);
return `pbkdf2_sha256$${iterations}$${base64url(salt)}$${base64url(new Uint8Array(bits))}`;
}
export async function verifyPassword(password, storedHash) {
const [scheme, storedIterations, saltText, hashText] = String(storedHash || "").split("$");
if (scheme !== "pbkdf2_sha256" || Number(storedIterations) !== iterations) return false;
const candidate = await hashPassword(password, fromBase64url(saltText));
return timingSafeEqual(candidate, storedHash);
}
export async function hashSessionToken(token) {
const digest = await crypto.subtle.digest("SHA-256", encoder.encode(token));
return base64url(new Uint8Array(digest));
}
export function createSessionToken() {
const bytes = crypto.getRandomValues(new Uint8Array(32));
return base64url(bytes);
}
function timingSafeEqual(a, b) {
const left = encoder.encode(a);
const right = encoder.encode(b);
if (left.length !== right.length) return false;
let result = 0;
for (let index = 0; index < left.length; index += 1) {
result |= left[index] ^ right[index];
}
return result === 0;
}
function base64url(bytes) {
const binary = Array.from(bytes, (byte) => String.fromCharCode(byte)).join("");
return btoa(binary).replaceAll("+", "-").replaceAll("/", "_").replaceAll("=", "");
}
function fromBase64url(value) {
const padded = value.replaceAll("-", "+").replaceAll("_", "/").padEnd(Math.ceil(value.length / 4) * 4, "=");
const binary = atob(padded);
return Uint8Array.from(binary, (char) => char.charCodeAt(0));
}
|