Created
June 22, 2018 16:57
-
-
Save jaydson/cc48390591d43db7b9f9f23b8ad5ddb1 to your computer and use it in GitHub Desktop.
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
var crypto = require('crypto'); | |
// NOTE: slower than 'bcrypt' but is pure JS and has no dependencies. | |
// for server side, consider using 'bcrypt' (C++), has the same API. | |
var bcrypt = require('bcryptjs'); | |
/* Applies SHA256 to the given data, returns encoded as base64 string. | |
* | |
* Parameters: | |
* - data: any value usable by crypto's update() method, i.e: String | |
* or buffer. | |
* | |
* Returns: String encoded as base64 of the sha256 | |
*/ | |
function sha256AsBase64(data) { | |
return crypto.createHash('sha256') | |
.update(data) | |
.digest('base64'); | |
} | |
/* Get bcrypt salt from username. | |
* | |
* Parameters: | |
* - `username`: string with username. | |
* - `rounds` (optional): use bcrypt rounds, or 12 if omitted. | |
* - `prefix` (optional): use bcrypt prefix, or `2a` if omitted. | |
* | |
* Returns: String encoded as bcrypt salt format. Example: | |
* `$2a$12$N9qo8uLOickgx2ZMRZoMye` | |
*/ | |
function bcryptSaltFromUsername(username, rounds, prefix) { | |
var hash = crypto.createHash('sha256').update(username).digest(); | |
return ('$' + (prefix || '2a') + | |
'$' + (rounds || 12) + | |
'$' + bcrypt.encodeBase64(hash, 16)); | |
} | |
/* Hash the given password using the given salt. | |
* | |
* This basically calls bcrypt on the `sha256AsBase64(password)`. | |
* | |
* Parameters: | |
* - `password`: string with clear text password. | |
* - `salt`: salt encoded using bcrypt's standard format. | |
* | |
* Return: 60 characters string encoded with the standard bcrypt | |
* format, ie: | |
* `$2a$12$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy` | |
*/ | |
function bcryptPasswordSaltSync(password, salt) { | |
return bcrypt.hashSync(sha256AsBase64(password), salt); | |
} | |
/* Create a `challenge` using the bcryptPassword and the given salt. | |
* | |
* This basically calls bcrypt on the `bcryptPassword`. | |
* | |
* Parameters: | |
* - `bcryptPassword`: string with hashed password, see | |
* `bcryptUserPasswordSync()`. Must be 60 characters in length. | |
* - `salt`: salt encoded using bcrypt's standard format. | |
* | |
* Return: 60 characters string encoded with the standard bcrypt | |
* format, ie: | |
* `$2a$12$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy` | |
*/ | |
function bcryptChallengePasswordSync(bcryptPassword, salt) { | |
if (bcryptPassword.length !== 60) { | |
throw new Error('invalid bcryptPassword length!'); | |
} | |
return bcrypt.hashSync(bcryptPassword, salt); | |
} | |
/* Given an username and its password, create the bcrypt hash expected | |
* by Elo API. | |
* | |
* The Elo API demands 12 salt-rounds (2^12 operations = 4096 | |
* iterations) using the first 16 bytes of `salt16Bytes = | |
* sha256(username)` encoded as `bcrypt.encodeBase64(salt16Bytes, | |
* 16)`. **NOTE: bcrypt uses a custom base64 alphabet!**. | |
* | |
* Since bcrypt limits its hashed payload to 72 characters and | |
* passwords are of an unknown size we apply `sha256AsBase64(password)` | |
* so we're fixed to 44 characters resulting from the Base64 encode of | |
* the 256 bits from sha256. | |
* | |
* The resulting string is a 60 characters string encoded with the | |
* standard bcrypt format: a prefix, the salt-rounds, the salt and the | |
* actual hash, Example: | |
* | |
* - Prefix: `2a` (bcrypt version) | |
* - Rounds: `12` | |
* - Salt: `N9qo8uLOickgx2ZMRZoMye` | |
* - Hash: `IjZAgcfl7p92ldGxad68LJZdL17lhW` | |
* - Encoded: `$2a$12$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy` | |
* | |
* Parameters: | |
* - `username`: string with username. | |
* - `password`: string with clear text password. | |
* | |
* Return: 60 characters string encoded with the standard bcrypt | |
* format, ie: | |
* `$2a$12$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy` | |
*/ | |
function bcryptUserPasswordSync(username, password) { | |
var salt = bcryptSaltFromUsername(username); | |
return bcryptPasswordSaltSync(password, salt); | |
} | |
/* Hash the given password using the given salt. | |
* | |
* This basically calls bcrypt on the `sha256AsBase64(password)`. | |
* | |
* Parameters: | |
* - `password`: string with clear text password. | |
* - `salt`: salt encoded using bcrypt's standard format. | |
* - `cb`: the callback function that receives 2 parameters: error and | |
* hash. It's called when the computation is done. | |
* | |
* Return: if no `cb` is provided, returns a `Promise`. | |
*/ | |
function bcryptPasswordSalt(password, salt, cb) { | |
return bcrypt.hash(sha256AsBase64(password), salt, cb); | |
} | |
/* Create a `challenge` using the bcryptPassword and the given salt. | |
* | |
* This basically calls bcrypt on the `bcryptPassword`. | |
* | |
* Parameters: | |
* - `bcryptPassword`: string with hashed password, see | |
* `bcryptUserPassword()`. Must be 60 characters in length. | |
* - `salt`: salt encoded using bcrypt's standard format. | |
* - `cb`: the callback function that receives 2 parameters: error and | |
* hash. It's called when the computation is done. | |
* | |
* Return: if no `cb` is provided, returns a `Promise`. | |
*/ | |
function bcryptChallengePassword(bcryptPassword, salt) { | |
if (bcryptPassword.length !== 60) { | |
throw new Error('invalid bcryptPassword length!'); | |
} | |
return bcrypt.hash(bcryptPassword, salt); | |
} | |
/* Given an username and its password, create the bcrypt hash expected | |
* by Elo API. | |
* | |
* The Elo API demands 12 salt-rounds (2^12 operations = 4096 | |
* iterations) using the first 16 bytes of `salt16Bytes = | |
* sha256(username)` encoded as `bcrypt.encodeBase64(salt16Bytes, | |
* 16)`. **NOTE: bcrypt uses a custom base64 alphabet!**. | |
* | |
* Since bcrypt limits its hashed payload to 72 characters and | |
* passwords are of an unknown size we apply `sha256AsBase64(password)` | |
* so we're fixed to 44 characters resulting from the Base64 encode of | |
* the 256 bits from sha256. | |
* | |
* The resulting string is a 60 characters string encoded with the | |
* standard bcrypt format: a prefix, the salt-rounds, the salt and the | |
* actual hash, Example: | |
* | |
* - Prefix: `2a` (bcrypt version) | |
* - Rounds: `12` | |
* - Salt: `N9qo8uLOickgx2ZMRZoMye` | |
* - Hash: `IjZAgcfl7p92ldGxad68LJZdL17lhW` | |
* - Encoded: `$2a$12$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy` | |
* | |
* Parameters: | |
* - `username`: string with username. | |
* - `password`: string with clear text password. | |
* - `cb`: the callback function that receives 2 parameters: error and | |
* hash. It's called when the computation is done. | |
* | |
* Return: if no `cb` is provided, returns a `Promise`. | |
*/ | |
function bcryptUserPassword(username, password, cb) { | |
var salt = bcryptSaltFromUsername(username); | |
return bcryptPasswordSalt(password, salt, cb); | |
} | |
module.exports = { | |
sha256AsBase64: sha256AsBase64, | |
bcryptSaltFromUsername: bcryptSaltFromUsername, | |
bcryptUserPasswordSync: bcryptUserPasswordSync, | |
bcryptPasswordSaltSync: bcryptPasswordSaltSync, | |
bcryptChallengePasswordSync: bcryptChallengePasswordSync, | |
bcryptUserPassword: bcryptUserPassword, | |
bcryptPasswordSalt: bcryptPasswordSalt, | |
bcryptChallengePassword: bcryptChallengePassword, | |
bcrypt: bcrypt, | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment