Skip to content

Instantly share code, notes, and snippets.

@nthung2112
Created May 24, 2025 14:37
Show Gist options
  • Save nthung2112/038f508674057aebd48e56f6c9cbf564 to your computer and use it in GitHub Desktop.
Save nthung2112/038f508674057aebd48e56f6c9cbf564 to your computer and use it in GitHub Desktop.
Password encode decode
interface HashPasswordParams {
password: string;
providedSalt?: Uint8Array;
}
async function hashPassword({ password, providedSalt }: HashPasswordParams) {
const encoder = new TextEncoder();
const salt = providedSalt || crypto.getRandomValues(new Uint8Array(16));
const keyMaterial = await crypto.subtle.importKey(
"raw",
encoder.encode(password),
{ name: "PBKDF2" },
false,
["deriveBits", "deriveKey"]
);
const key = await crypto.subtle.deriveKey(
{
name: "PBKDF2",
salt: salt,
iterations: 100000,
hash: "SHA-256",
},
keyMaterial,
{ name: "AES-GCM", length: 256 },
true,
["encrypt", "decrypt"]
);
const exportedKey = await crypto.subtle.exportKey("raw", key);
const hashBuffer = new Uint8Array(exportedKey);
const hashHex = Array.from(hashBuffer)
.map((b: number) => b.toString(16).padStart(2, "0"))
.join("");
const saltHex = Array.from(salt)
.map((b: number) => b.toString(16).padStart(2, "0"))
.join("");
return `${saltHex}:${hashHex}`;
}
interface VerifyPasswordParams {
storedHash: string;
passwordAttempt: string;
}
async function verifyPassword({ storedHash, passwordAttempt }: VerifyPasswordParams) {
const [saltHex, originalHash] = storedHash.split(":");
const salt = new Uint8Array(saltHex.match(/.{1,2}/g)!.map((byte: string) => parseInt(byte, 16)));
const attemptHashWithSalt = await hashPassword({
password: passwordAttempt,
providedSalt: salt
});
const [, attemptHash] = attemptHashWithSalt.split(":");
return attemptHash === originalHash;
}
export { hashPassword, verifyPassword };
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment