@lifeworld/lucia
Version:
A simple and flexible authentication library
68 lines (67 loc) • 2.59 kB
JavaScript
import { decodeHex, encodeBase32LowerCaseNoPadding, encodeHexLowerCase } from "@oslojs/encoding";
import { generateRandomString } from "@oslojs/crypto/random";
import { constantTimeEqual } from "@oslojs/crypto/subtle";
import { scrypt } from "./scrypt/index.js";
async function generateScryptKey(data, salt, blockSize = 16) {
const encodedData = new TextEncoder().encode(data);
const encodedSalt = new TextEncoder().encode(salt);
const keyUint8Array = await scrypt(encodedData, encodedSalt, {
N: 16384,
r: blockSize,
p: 1,
dkLen: 64
});
return new Uint8Array(keyUint8Array);
}
const random = {
read(bytes) {
crypto.getRandomValues(bytes);
}
};
export function generateId(length) {
const alphabet = "abcdefghijklmnopqrstuvwxyz0123456789";
return generateRandomString(random, alphabet, length);
}
export function generateIdFromEntropySize(size) {
const buffer = crypto.getRandomValues(new Uint8Array(size));
return encodeBase32LowerCaseNoPadding(buffer);
}
export class Scrypt {
async hash(password) {
const salt = encodeHexLowerCase(crypto.getRandomValues(new Uint8Array(16)));
const key = await generateScryptKey(password.normalize("NFKC"), salt);
return `${salt}:${encodeHexLowerCase(key)}`;
}
async verify(hash, password) {
const parts = hash.split(":");
if (parts.length !== 2)
return false;
const [salt, key] = parts;
const targetKey = await generateScryptKey(password.normalize("NFKC"), salt);
return constantTimeEqual(targetKey, decodeHex(key));
}
}
export class LegacyScrypt {
async hash(password) {
const salt = encodeHexLowerCase(crypto.getRandomValues(new Uint8Array(16)));
const key = await generateScryptKey(password.normalize("NFKC"), salt);
return `s2:${salt}:${encodeHexLowerCase(key)}`;
}
async verify(hash, password) {
const parts = hash.split(":");
if (parts.length === 2) {
const [salt, key] = parts;
const targetKey = await generateScryptKey(password.normalize("NFKC"), salt, 8);
const result = constantTimeEqual(targetKey, decodeHex(key));
return result;
}
if (parts.length !== 3)
return false;
const [version, salt, key] = parts;
if (version === "s2") {
const targetKey = await generateScryptKey(password.normalize("NFKC"), salt);
return constantTimeEqual(targetKey, decodeHex(key));
}
return false;
}
}