This repository has been archived by the owner on Sep 17, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore(user_passwords): temporarily disable hashing to work around was…
…m bug
- Loading branch information
1 parent
5067f78
commit ad4b894
Showing
4 changed files
with
140 additions
and
134 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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, | ||
// }, | ||
// }; | ||
// } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
// } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)); | ||
|
@@ -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"); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
// } |