import { timingSafeEqual } from "./crypto.js";
//#region src/password.ts
/**
* `@platform-modules/util/password` — PBKDF2-SHA-512 password hashing.
*
* Stored format: `pbkdf2sha512$<iter>$<saltB64>$<hashB64>` (salt and hash are standard base64).
*/
const ALGO = "SHA-512";
const ITERATIONS = 1e5;
const KEY_BYTES = 64;
const SALT_BYTES = 16;
function asBufferSource(bytes) {
	return bytes;
}
function toB64(bytes) {
	const arr = bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes);
	return btoa(String.fromCharCode(...arr));
}
function fromB64(b64) {
	return new Uint8Array(atob(b64).split("").map((c) => c.charCodeAt(0)));
}
async function pbkdf2Derive(plain, salt, iterations, hash, keyBytes) {
	const keyMaterial = await crypto.subtle.importKey("raw", new TextEncoder().encode(plain), "PBKDF2", false, ["deriveBits"]);
	return crypto.subtle.deriveBits({
		name: "PBKDF2",
		salt: asBufferSource(salt),
		iterations,
		hash
	}, keyMaterial, keyBytes * 8);
}
/** Returns `pbkdf2sha512$<iter>$<saltB64>$<hashB64>`. */
async function hashPassword(plain, opts) {
	const derive = opts?.deriveBits ?? pbkdf2Derive;
	const salt = crypto.getRandomValues(new Uint8Array(SALT_BYTES));
	const bits = await derive(plain, salt, ITERATIONS, ALGO, KEY_BYTES);
	return `pbkdf2sha512$${ITERATIONS}$${toB64(salt)}$${toB64(bits)}`;
}
/** Parses iter and salt from `stored` — never from a module constant. */
async function verifyPassword(plain, stored, opts) {
	const derive = opts?.deriveBits ?? pbkdf2Derive;
	const parts = stored.split("$");
	if (parts.length !== 4 || parts[0] !== "pbkdf2sha512") return false;
	const [, iterStr, saltB64, hashB64] = parts;
	const iterations = Number(iterStr);
	if (!Number.isInteger(iterations) || iterations <= 0) return false;
	let salt;
	try {
		salt = fromB64(saltB64);
	} catch {
		return false;
	}
	const bits = await derive(plain, salt, iterations, ALGO, KEY_BYTES);
	return timingSafeEqual(toB64(bits), hashB64);
}
//#endregion
export { hashPassword, verifyPassword };
