Support for Argon2
Describe the feature
Using Argon2 for password hashing in Node.js + worker environments would be awesome. Currently, the only choice in Cloudflare Workers, as far as I'm aware, is PBKDF2 via Web Crypto.
Additional information
- [x] Would you be willing to help implement this feature?
@murisceman @pi0 I've been using the below in cloudflare workers.
A small utility for hashing and verifying text, if this looks good, can I create a PR @pi0 ?
function hexStringToByteArray(hexString) {
return new Uint8Array(
hexString.match(/.{1,2}/g).map((byte) => parseInt(byte, 16))
);
}
function timingSafeEqual(a, b) {
if (a.length !== b.length) {
return false;
}
let result = 0;
for (let i = 0; i < a.length; i++) {
result |= a[i] ^ b[i];
}
return result === 0;
}
export async function hashPassword(password) {
const encoder = new TextEncoder();
const salt = 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 hash = Array.from(new Uint8Array(exportedKey))
.map((b) => b.toString(16).padStart(2, "0"))
.join("");
const encodedSalt = Array.from(salt)
.map((b) => b.toString(16).padStart(2, "0"))
.join("");
return `pbkdf2_sha256$100000$${encodedSalt}$${hash}`;
}
export async function verifyPassword(stored, passwordAttempt) {
const parts = stored.split("$");
const iterations = parseInt(parts[1], 10);
const salt = hexStringToByteArray(parts[2]);
const storedHash = parts[3];
const encoder = new TextEncoder();
const keyMaterial = await crypto.subtle.importKey(
"raw",
encoder.encode(passwordAttempt),
"PBKDF2",
false,
["deriveBits"]
);
const derivedBits = await crypto.subtle.deriveBits(
{
name: "PBKDF2",
salt: salt,
iterations: iterations,
hash: "SHA-256",
},
keyMaterial,
256
);
const attemptedHash = Array.from(new Uint8Array(derivedBits))
.map((b) => b.toString(16).padStart(2, "0"))
.join("");
return timingSafeEqual(
hexStringToByteArray(attemptedHash),
hexStringToByteArray(storedHash)
);
}
Thanks for sharing snippet but is it Argon2?
Thanks for sharing snippet but is it Argon2?
No, this is using PBKDF2. I thought @murisceman was just looking for hashing some text.
This issue is specifically for argon2. Generic encryption utils are in the roadmap already. Thanks anyway.
It seems that someone made bcrypt-edge.
I tested it and it works well on Cloudflare Workers: https://github.com/atinux/nuxt-bcrypt-edge
Demo: https://bcrypt.nuxt.dev
This is awesome! I'd love to see how much time it takes on a Worker with more than 10 rounds, specifically 12 and 13. I believe 14 might be too costly. (https://github.com/atinux/nuxt-bcrypt-edge/pull/1)