Created
October 13, 2022 11:54
-
-
Save ddjerqq/4dd7d8927dc3e900f9ad96d712912f59 to your computer and use it in GitHub Desktop.
Password hasher. used to prepare the passwords for super secure storage.
This file contains hidden or 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
| const crypto = require("crypto") | |
| /** | |
| * Password hasher | |
| * used to prepare the passwords for super secure storage. | |
| * | |
| * this system is so secure (unless the SALTS and PEPPERS are *stolen*) that its safe to have weak passwords like password123 iloveu and so on. | |
| * as 2 same passwords will (most *likely*) have a different hash | |
| * | |
| * # Notes: | |
| * | |
| * * if the SALTS and PEPPERS are stolen, along this file which has the whole method inside it, it will render the security useless. | |
| * so keep this file on the server, and let users send plain text to the server. use TLS (transport layer security) and HTTP__S__ | |
| * | |
| * * 2 identical plain text passwords will have different hashes most likely because of the _SAUCES. but there still is a chance that two users will have a same password. | |
| * that is okay tho, and it is not a big deal, just something to be aware of. | |
| * | |
| * # Payload structure | |
| * `[dice] is plain_text_password split in 2 parts, with the second part reversed`; | |
| * | |
| * one of the hashes is `[salt][dice][pepper][random_sauce]`; | |
| * | |
| * @example | |
| * let hashes = []; | |
| * for each sauce in Password.SAUCES { | |
| * hashes.push(sha256([salt][dice][pepper][sauce])) | |
| * } | |
| * return random choice from the hashes | |
| * | |
| * @example | |
| * const password = "password"; | |
| * const hash = await Password.hash(password); | |
| * console.assert(Password.check(password, hash)); | |
| */ | |
| class Password { | |
| #_SALT = "SECRET"; | |
| #_PEPPER = "SECRET"; | |
| #_SAUCES = "SECRET"; | |
| static async #_hash(payload) { | |
| const msgBuffer = new TextEncoder().encode(payload); | |
| const hashBuffer = await crypto.subtle.digest("SHA-256", msgBuffer); | |
| const hashArray = Array.from(new Uint8Array(hashBuffer)); | |
| return hashArray.map(b => b.toString(16).padStart(2, "0")).join(""); | |
| } | |
| static async #_prepare(password) { | |
| // salt and pepper the password | |
| const salted = Password.#_SALT + password + Password.#_PEPPER; | |
| // dice the password | |
| const mid = Math.floor(salted.length / 2); | |
| // split it in two and reverse the second half | |
| const diced = salted.slice(0, mid) + salted.slice(mid).split("").reverse(); | |
| // for each kind of sauce, sauce the password and add it to the output list | |
| let parts = []; | |
| for (const sauce of this.#_SAUCES) { | |
| let hash = await Password.#_hash(diced + sauce); | |
| parts.push(hash); | |
| } | |
| return parts; | |
| } | |
| /** | |
| * hash a plain text password and return one of the saucy sha256 hashes. | |
| * @param {String} plain_text_password the password which you want to hash. | |
| * @returns {Promise<String>} one of the sauced password hashes. | |
| * @example | |
| * let one_of_the_hashes = await Password.hash("$up3rSecure"); | |
| */ | |
| static async hash(plain_text_password) { | |
| const sauces = await this.#_prepare(plain_text_password); | |
| const idx = Math.floor(Math.random() * sauces.length); | |
| return sauces[idx]; | |
| } | |
| /** | |
| * check if the hash belongs to the password. | |
| * @param {String} plain_text_password the password which you want to check. | |
| * @param {String} hash the hash of the password, usually from the database. | |
| * @returns {Promise<boolean>} returns true if the hash belongs to the password. | |
| * @example | |
| * const isPasswordValid = await check("$up3rSecure", "8bb32691631f72e8c39b0834b1f8c956ffbb666df6facef5a951ab9f1b549b17"); | |
| * console.assert(isPasswordValid) | |
| */ | |
| static async check(plain_text_password, hash) { | |
| const password = new Password(plain_text_password); | |
| const hashes = await password.#_prepare(); | |
| return hashes.includes(hash) | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment