hash pasword types fix

This commit is contained in:
RevIQ
2026-01-09 05:44:56 -05:00
parent 0e6a028ef0
commit 60bcbeffb3
3 changed files with 67 additions and 5 deletions

View File

@@ -13,8 +13,8 @@ describe("hashPassword", () => {
expect(parts[1]).toBe("pbkdf2-sha256"); expect(parts[1]).toBe("pbkdf2-sha256");
expect(parts[2]).toBe("100000"); expect(parts[2]).toBe("100000");
// Salt and hash should be non-empty base64 strings // Salt and hash should be non-empty base64 strings
expect(parts[3].length).toBeGreaterThan(0); expect(parts[3]?.length).toBeGreaterThan(0);
expect(parts[4].length).toBeGreaterThan(0); expect(parts[4]?.length).toBeGreaterThan(0);
}); });
it("should generate different hashes for the same password", async () => { it("should generate different hashes for the same password", async () => {
@@ -98,4 +98,66 @@ describe("verifyPassword", () => {
const wrongResult = await verifyPassword("password", hash); const wrongResult = await verifyPassword("password", hash);
expect(wrongResult).toBe(false); expect(wrongResult).toBe(false);
}); });
it("should verify known hash for 'password'", async () => {
// This hash was generated for the password "password"
const storedHash =
"$pbkdf2-sha256$100000$iUaDbbVm+Mf0HG7RcCCOzw==$IQfBN4chRU3wqCEoC9XOusIVYkyW24dbJd/ksm0VBJk=";
const password = "password";
const result = await verifyPassword(password, storedHash);
expect(result).toBe(true);
});
it("should derive consistent hash for known salt and password", async () => {
// Manually verify the derivation produces the expected hash
const password = "password";
const saltB64 = "iUaDbbVm+Mf0HG7RcCCOzw==";
const expectedHashB64 = "IQfBN4chRU3wqCEoC9XOusIVYkyW24dbJd/ksm0VBJk=";
const iterations = 100000;
// Decode salt
const saltBinary = atob(saltB64);
const salt = new Uint8Array(saltBinary.length);
for (let i = 0; i < saltBinary.length; i++) {
salt[i] = saltBinary.charCodeAt(i);
}
// Derive key
const encoder = new TextEncoder();
const passwordBuffer = encoder.encode(password);
const keyMaterial = await crypto.subtle.importKey(
"raw",
passwordBuffer,
"PBKDF2",
false,
["deriveBits"],
);
const derivedBits = await crypto.subtle.deriveBits(
{
name: "PBKDF2",
salt,
iterations,
hash: "SHA-256",
},
keyMaterial,
32 * 8,
);
// Encode result to base64
const derivedArray = new Uint8Array(derivedBits);
let binary = "";
for (const byte of derivedArray) {
binary += String.fromCharCode(byte);
}
const derivedB64 = btoa(binary);
console.log("Expected hash:", expectedHashB64);
console.log("Derived hash: ", derivedB64);
console.log("Match:", derivedB64 === expectedHashB64);
expect(derivedB64).toBe(expectedHashB64);
});
}); });

View File

@@ -107,6 +107,7 @@ export const verifyPassword = async (
const derivedBits = await crypto.subtle.deriveBits( const derivedBits = await crypto.subtle.deriveBits(
{ {
name: "PBKDF2", name: "PBKDF2",
// @ts-expect-error - salt is a Uint8Array
salt, salt,
iterations, iterations,
hash: "SHA-256", hash: "SHA-256",

View File

@@ -1,7 +1,6 @@
{ {
"extends": "@macalinao/tsconfig/tsconfig.base.json", "extends": "@macalinao/tsconfig/tsconfig.base.json",
"compilerOptions": { "compilerOptions": {
"types": ["@cloudflare/workers-types"] "types": ["bun"]
}, }
"exclude": ["**/*.test.ts"]
} }