Skip to content
This repository was archived by the owner on Sep 17, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
172 changes: 86 additions & 86 deletions modules/user_passwords/utils/argon2.ts
Original file line number Diff line number Diff line change
@@ -1,86 +1,86 @@
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,
// 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,
// },
// };
// }
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/[email protected]";

// 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/[email protected]";
//
// // 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);
// }
34 changes: 20 additions & 14 deletions modules/user_passwords/utils/common.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
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 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));
Expand Down Expand Up @@ -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");
}
}
36 changes: 18 additions & 18 deletions modules/user_passwords/utils/scrypt.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import { hash as hashScrypt, verify } from "https://deno.land/x/[email protected]/mod.ts";
import { ScryptParameters } from "https://deno.land/x/[email protected]/lib/format.ts";

// OWASP recommended defaults (listed option 3):
// https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#scrypt
const scryptParams: Partial<ScryptParameters> = {
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/[email protected]/mod.ts";
// import { ScryptParameters } from "https://deno.land/x/[email protected]/lib/format.ts";
//
// // OWASP recommended defaults (listed option 3):
// // https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#scrypt
// const scryptParams: Partial<ScryptParameters> = {
// 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);
// }