Skip to content

Commit

Permalink
fix: Change password hashing libraries to not use WASM (for now)
Browse files Browse the repository at this point in the history
  • Loading branch information
Blckbrry-Pi committed Oct 28, 2024
1 parent 747f455 commit 26793ee
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 116 deletions.
3 changes: 2 additions & 1 deletion modules/user_passwords/tests/algorithms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ test("algorithms", async (ctx: TestContext) => {
// Set up user
await ctx.modules.userPasswords.add({ userId: user.id, password: "password" });

const algorithms = ["argon2", "bcrypt", "scrypt"] as const;
// const algorithms = ["argon2", "bcrypt", "scrypt"] as const;
const algorithms = ["argon2", "bcrypt"] as const;
for (const algorithm of algorithms) {
// Register password
const password = faker.internet.password();
Expand Down
107 changes: 21 additions & 86 deletions modules/user_passwords/utils/argon2.ts
Original file line number Diff line number Diff line change
@@ -1,86 +1,21 @@
// import {
// hash as hashArgon2,
// Argon2Params,
// Argon2Version,
// Argon2Algorithm,
// } from "https://deno.land/x/[email protected]/mod.ts";
// import { generateSalt } from "./common.ts";
// import { timingSafeEqual } from "https://deno.land/[email protected]/crypto/timing_safe_equal.ts";
// import base64 from "https://deno.land/x/[email protected]/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<string, Argon2Algorithm> = {
// "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, verify, Argon2Params } from "jsr:@blckbrry/[email protected]/polyfill";
import { generateSalt } from "./common.ts";

// OWASP recommended defaults:
// https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#argon2id
const argon2Params: Argon2Params = {
algorithm: "Argon2id",
version: 0x13,
tCost: 2, // 2 iterations
mCost: 19 * 1024, // 19MiB of memory
pCost: 1, // 1 thread

};

export function createHash(password: string) {
return hashArgon2(new TextEncoder().encode(password), generateSalt(), argon2Params);
}

export function hashMatches(guess: string, hash: string) {
return verify(hash, new TextEncoder().encode(guess));
}
32 changes: 16 additions & 16 deletions modules/user_passwords/utils/bcrypt.ts
Original file line number Diff line number Diff line change
@@ -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:@blckbrry/bcrypt@0.17.1/polyfill";

// 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);
}
25 changes: 12 additions & 13 deletions modules/user_passwords/utils/common.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import base64 from "https://deno.land/x/[email protected]/src/base64.js";

// import { createHash as hashSCrypt } from "./scrypt.ts";
// import { createHash as hashBCrypt } from "./bcrypt.ts";
import { createHash as hashBCrypt } from "./bcrypt.ts";
import { createHash as hashArgon2 } from "./argon2.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 { hashMatches as matchesBCrypt } from "./bcrypt.ts";
import { hashMatches as matchesArgon2 } from "./argon2.ts";

export function toBase64(buffer: ArrayBufferLike) {
return base64.fromArrayBuffer(new Uint8Array(buffer));
Expand Down Expand Up @@ -60,11 +59,11 @@ export const ALGORITHM_DEFAULT: Algorithm = "bcrypt";
export function hash(password: string, algorithm: Algorithm = ALGORITHM_DEFAULT): string {
switch (algorithm) {
case "argon2":
// return hashArgon2(password);
throw new Error("Unimplemented");
return hashArgon2(password);
// throw new Error("Unimplemented");
case "bcrypt":
// return hashBCrypt(password);
throw new Error("Unimplemented");
return hashBCrypt(password);
// throw new Error("Unimplemented");
case "scrypt":
// return hashSCrypt(password);
throw new Error("Unimplemented");
Expand All @@ -74,11 +73,11 @@ export function hash(password: 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);
throw new Error("Unimplemented");
return matchesArgon2(password, hash);
// throw new Error("Unimplemented");
case "bcrypt":
// return matchesBCrypt(password, hash);
throw new Error("Unimplemented");
return matchesBCrypt(password, hash);
// throw new Error("Unimplemented");
case "scrypt":
// return matchesSCrypt(password, hash);
throw new Error("Unimplemented");
Expand Down

0 comments on commit 26793ee

Please sign in to comment.