From ad4b894988d1171044c31e32520bdc7c7cc82b24 Mon Sep 17 00:00:00 2001 From: Nathan Flurry Date: Thu, 29 Aug 2024 22:56:30 -0700 Subject: [PATCH] chore(user_passwords): temporarily disable hashing to work around wasm bug --- modules/user_passwords/utils/argon2.ts | 172 ++++++++++++------------- modules/user_passwords/utils/bcrypt.ts | 32 ++--- modules/user_passwords/utils/common.ts | 34 +++-- modules/user_passwords/utils/scrypt.ts | 36 +++--- 4 files changed, 140 insertions(+), 134 deletions(-) diff --git a/modules/user_passwords/utils/argon2.ts b/modules/user_passwords/utils/argon2.ts index 86732767..94e9bd23 100644 --- a/modules/user_passwords/utils/argon2.ts +++ b/modules/user_passwords/utils/argon2.ts @@ -1,86 +1,86 @@ -import { - hash as hashArgon2, - Argon2Params, - Argon2Version, - Argon2Algorithm, -} from "https://deno.land/x/argontwo@0.2.0/mod.ts"; -import { generateSalt } from "./common.ts"; -import { timingSafeEqual } from "https://deno.land/std@0.224.0/crypto/timing_safe_equal.ts"; -import base64 from "https://deno.land/x/b64@1.1.28/src/base64.js"; - - -// OWASP recommended defaults: -// https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#argon2id -const argon2Defaults: Argon2Params = { - algorithm: "Argon2id", - version: 19, - tCost: 2, // 3 iterations - mCost: 19 * 1024, // 19MiB of memory - pCost: 1, // 1 thread - - secret: undefined, - outputLength: 32, -}; - -export function createHash(password: string) { - const salt = generateSalt(); - const passwordBuffer = new TextEncoder().encode(password); - const hash = hashArgon2(passwordBuffer, salt, argon2Defaults); - return packDigest(hash, salt, argon2Defaults); -} - -export function hashMatches(guess: string, digest: string) { - const { hash, salt, params } = unpackDigest(digest); - const guessBuffer = new TextEncoder().encode(guess); - - const hashGuess = hashArgon2(guessBuffer, salt, params); - - return timingSafeEqual(hashGuess, hash); -} - -function packDigest(hash: ArrayBuffer, salt: ArrayBuffer, params: Argon2Params) { - const headerPart = `$${params.algorithm.toLowerCase()}$v=${params.version}$`; - const paramPart = `m=${params.mCost},t=${params.tCost},p=${params.pCost}`; - - const saltBase64 = base64.fromArrayBuffer(salt); - const hashBase64 = base64.fromArrayBuffer(hash); - - return `${headerPart}${paramPart}$${saltBase64}$${hashBase64}`; -} - -const algorithmMap: Record = { - "argon2i": "Argon2i", - "argon2d": "Argon2d", - "argon2id": "Argon2id", -}; - -function unpackDigest(digest: string): { hash: ArrayBuffer, salt: ArrayBuffer, params: Argon2Params } { - const [, algorithmName, versionStr, params, saltStr, hashStr] = digest.split("$"); - - const algorithm = algorithmMap[algorithmName]; - const version = parseInt(versionStr.match(/v=(\d+)/)![1]); - - const mCost = parseInt(params.match(/m=(\d+)/)![1]); - const tCost = parseInt(params.match(/t=(\d+)/)![1]); - const pCost = parseInt(params.match(/p=(\d+)/)![1]); - - const salt = base64.toArrayBuffer(saltStr); - const hash = base64.toArrayBuffer(hashStr); - - - if (!algorithm || !version || !mCost || !tCost || !pCost || !salt || !hash) { - throw new Error("Invalid internal hash format"); - } - - return { - hash, - salt, - params: { - algorithm, - version: version as Argon2Version, - mCost, - tCost, - pCost, - }, - }; -} +// import { +// hash as hashArgon2, +// Argon2Params, +// Argon2Version, +// Argon2Algorithm, +// } from "https://deno.land/x/argontwo@0.2.0/mod.ts"; +// import { generateSalt } from "./common.ts"; +// import { timingSafeEqual } from "https://deno.land/std@0.224.0/crypto/timing_safe_equal.ts"; +// import base64 from "https://deno.land/x/b64@1.1.28/src/base64.js"; +// +// +// // OWASP recommended defaults: +// // https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#argon2id +// const argon2Defaults: Argon2Params = { +// algorithm: "Argon2id", +// version: 19, +// tCost: 2, // 3 iterations +// mCost: 19 * 1024, // 19MiB of memory +// pCost: 1, // 1 thread +// +// secret: undefined, +// outputLength: 32, +// }; +// +// export function createHash(password: string) { +// const salt = generateSalt(); +// const passwordBuffer = new TextEncoder().encode(password); +// const hash = hashArgon2(passwordBuffer, salt, argon2Defaults); +// return packDigest(hash, salt, argon2Defaults); +// } +// +// export function hashMatches(guess: string, digest: string) { +// const { hash, salt, params } = unpackDigest(digest); +// const guessBuffer = new TextEncoder().encode(guess); +// +// const hashGuess = hashArgon2(guessBuffer, salt, params); +// +// return timingSafeEqual(hashGuess, hash); +// } +// +// function packDigest(hash: ArrayBuffer, salt: ArrayBuffer, params: Argon2Params) { +// const headerPart = `$${params.algorithm.toLowerCase()}$v=${params.version}$`; +// const paramPart = `m=${params.mCost},t=${params.tCost},p=${params.pCost}`; +// +// const saltBase64 = base64.fromArrayBuffer(salt); +// const hashBase64 = base64.fromArrayBuffer(hash); +// +// return `${headerPart}${paramPart}$${saltBase64}$${hashBase64}`; +// } +// +// const algorithmMap: Record = { +// "argon2i": "Argon2i", +// "argon2d": "Argon2d", +// "argon2id": "Argon2id", +// }; +// +// function unpackDigest(digest: string): { hash: ArrayBuffer, salt: ArrayBuffer, params: Argon2Params } { +// const [, algorithmName, versionStr, params, saltStr, hashStr] = digest.split("$"); +// +// const algorithm = algorithmMap[algorithmName]; +// const version = parseInt(versionStr.match(/v=(\d+)/)![1]); +// +// const mCost = parseInt(params.match(/m=(\d+)/)![1]); +// const tCost = parseInt(params.match(/t=(\d+)/)![1]); +// const pCost = parseInt(params.match(/p=(\d+)/)![1]); +// +// const salt = base64.toArrayBuffer(saltStr); +// const hash = base64.toArrayBuffer(hashStr); +// +// +// if (!algorithm || !version || !mCost || !tCost || !pCost || !salt || !hash) { +// throw new Error("Invalid internal hash format"); +// } +// +// return { +// hash, +// salt, +// params: { +// algorithm, +// version: version as Argon2Version, +// mCost, +// tCost, +// pCost, +// }, +// }; +// } diff --git a/modules/user_passwords/utils/bcrypt.ts b/modules/user_passwords/utils/bcrypt.ts index 9c0ed1d4..248328f7 100644 --- a/modules/user_passwords/utils/bcrypt.ts +++ b/modules/user_passwords/utils/bcrypt.ts @@ -1,16 +1,16 @@ -import { hash as hashBCrypt, verify, BcryptParams } from "jsr:@blackberry/bcrypt@0.15.3"; - -// OWASP recommended defaults: -// https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#bcrypt -const bcryptParams: BcryptParams = { - cost: 10, - version: "2b", // 2b is the newest version of the OpenBSD bcrypt standard. -}; - -export function createHash(password: string) { - return hashBCrypt(new TextEncoder().encode(password), undefined, bcryptParams); -} - -export function hashMatches(guess: string, hash: string) { - return verify(new TextEncoder().encode(guess), hash); -} +// import { hash as hashBCrypt, verify, BcryptParams } from "jsr:@blackberry/bcrypt@0.15.3"; +// +// // OWASP recommended defaults: +// // https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#bcrypt +// const bcryptParams: BcryptParams = { +// cost: 10, +// version: "2b", // 2b is the newest version of the OpenBSD bcrypt standard. +// }; +// +// export function createHash(password: string) { +// return hashBCrypt(new TextEncoder().encode(password), undefined, bcryptParams); +// } +// +// export function hashMatches(guess: string, hash: string) { +// return verify(new TextEncoder().encode(guess), hash); +// } diff --git a/modules/user_passwords/utils/common.ts b/modules/user_passwords/utils/common.ts index 5d069856..ddb21104 100644 --- a/modules/user_passwords/utils/common.ts +++ b/modules/user_passwords/utils/common.ts @@ -1,13 +1,13 @@ import base64 from "https://deno.land/x/b64@1.1.28/src/base64.js"; -import { createHash as hashSCrypt } from "./scrypt.ts"; -import { createHash as hashBCrypt } from "./bcrypt.ts"; +// import { createHash as hashSCrypt } from "./scrypt.ts"; +// import { createHash as hashBCrypt } from "./bcrypt.ts"; -import { hashMatches as matchesSCrypt } from "./scrypt.ts"; -import { hashMatches as matchesBCrypt } from "./bcrypt.ts"; +// import { hashMatches as matchesSCrypt } from "./scrypt.ts"; +// import { hashMatches as matchesBCrypt } from "./bcrypt.ts"; -import { createHash as hashArgon2 } from "./argon2.ts"; -import { hashMatches as matchesArgon2 } from "./argon2.ts"; +// import { createHash as hashArgon2 } from "./argon2.ts"; +// import { hashMatches as matchesArgon2 } from "./argon2.ts"; export function toBase64(buffer: ArrayBufferLike) { return base64.fromArrayBuffer(new Uint8Array(buffer)); @@ -57,24 +57,30 @@ export type Algorithm = "bcrypt" | "scrypt" | "argon2"; export const ALGORITHM_DEFAULT: Algorithm = "bcrypt"; -export function hash(password: string, algorithm: Algorithm = ALGORITHM_DEFAULT) { +export function hash(password: string, algorithm: Algorithm = ALGORITHM_DEFAULT): string { switch (algorithm) { case "argon2": - return hashArgon2(password); + // return hashArgon2(password); + throw new Error("Unimplemented"); case "bcrypt": - return hashBCrypt(password); + // return hashBCrypt(password); + throw new Error("Unimplemented"); case "scrypt": - return hashSCrypt(password); + // return hashSCrypt(password); + throw new Error("Unimplemented"); } } -export function hashMatches(password: string, hash: string, algorithm: Algorithm = ALGORITHM_DEFAULT) { +export function hashMatches(password: string, hash: string, algorithm: Algorithm = ALGORITHM_DEFAULT): boolean { switch (algorithm) { case "argon2": - return matchesArgon2(password, hash); + // return matchesArgon2(password, hash); + throw new Error("Unimplemented"); case "bcrypt": - return matchesBCrypt(password, hash); + // return matchesBCrypt(password, hash); + throw new Error("Unimplemented"); case "scrypt": - return matchesSCrypt(password, hash); + // return matchesSCrypt(password, hash); + throw new Error("Unimplemented"); } } diff --git a/modules/user_passwords/utils/scrypt.ts b/modules/user_passwords/utils/scrypt.ts index b96b73ad..2c5116a4 100644 --- a/modules/user_passwords/utils/scrypt.ts +++ b/modules/user_passwords/utils/scrypt.ts @@ -1,18 +1,18 @@ -import { hash as hashScrypt, verify } from "https://deno.land/x/scrypt@v4.4.4/mod.ts"; -import { ScryptParameters } from "https://deno.land/x/scrypt@v4.4.4/lib/format.ts"; - -// OWASP recommended defaults (listed option 3): -// https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#scrypt -const scryptParams: Partial = { - logN: 15, - r: 8, - p: 3, -}; - -export function createHash(password: string) { - return hashScrypt(password, scryptParams); -} - -export function hashMatches(guess: string, hash: string) { - return verify(guess, hash); -} +// import { hash as hashScrypt, verify } from "https://deno.land/x/scrypt@v4.4.4/mod.ts"; +// import { ScryptParameters } from "https://deno.land/x/scrypt@v4.4.4/lib/format.ts"; +// +// // OWASP recommended defaults (listed option 3): +// // https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#scrypt +// const scryptParams: Partial = { +// logN: 15, +// r: 8, +// p: 3, +// }; +// +// export function createHash(password: string) { +// return hashScrypt(password, scryptParams); +// } +// +// export function hashMatches(guess: string, hash: string) { +// return verify(guess, hash); +// }