-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
120 lines (100 loc) · 3.32 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
var _defaults = require('lodash.defaults')
var crypto = require('crypto')
var util = require('util')
function PBKDF2 (options) {
var defaults = {
algorithm: 'sha256',
// iTunes uses 10.000 iterations
// see http://css.csail.mit.edu/6.858/2015/readings/ios-security-may12.pdf
iterations: 64000,
// A good rule of thumb is to use a salt that is the same size as the output of the hash function. For example, the output of SHA256 is 256 bits (32 bytes), so the salt should be at least 32 random bytes.
// see https://crackstation.net/hashing-security.htm
salt_length: 32,
key_length: 32
}
this._options = _defaults(options || {}, defaults)
// the input is a hash we want to validate
if (typeof options === 'string') {
this._options = this._parse(options)
}
return this
}
/**
* Creates a password hash containing the algorithm, iterations, salt and derived_key
* @param {[type]} password The password string we want to hash
* @param {Function} callback
*/
PBKDF2.prototype.create = function (password, callback) {
var self = this
// get random bytes
crypto.randomBytes(self._options.salt_length, function (err, bytes) {
if (err) return callback(err)
var salt = self.salt = bytes.toString('base64')
self.createPassword(password, salt, function (err, key) {
if (err) return callback(err)
var hash = self.format(salt, key)
return callback(null, hash)
})
})
}
/**
* Create a password using password string and salt
* @param {string} password The password you want to hash
* @param {string} salt The salt we will be using (base64)
* @param {Function} callback
* @return {[type]} [description]
*/
PBKDF2.prototype.createPassword = function (password, salt, callback) {
var self = this
crypto.pbkdf2(password, self.salt, self._options.iterations, self._options.key_length, 'sha256', function (err, key) {
if (err) return callback(err)
key = self.derived_key = key.toString('hex')
return callback(null, key)
})
}
/**
* Format a password to a DB-friendly string
* @param {string} salt the salt we used for the hmac
* @param {string} key the derived PBKDF2 key
* @return {string} the formatted DB-friendly hash
*/
PBKDF2.prototype.format = function (salt, key) {
return util.format('%s:%s:%s:%s',
this._options.algorithm,
this._options.iterations,
salt,
key)
}
/**
* Parse a formatted hash into the correct options
* @param {string} hash formatted hash
* @return {object} options object
*/
PBKDF2.prototype._parse = function (hash) {
// parse options used for the hash
var args = hash.split(':')
var options = {
algorithm: args[0],
iterations: parseInt(args[1], 10),
salt_length: new Buffer(args[2], 'base64').length,
key_length: new Buffer(args[3], 'hex').length
}
this._options = options
this.salt = args[2]
this.derived_key = args[3]
return options
}
/**
* [validate description]
* @param {string} password The password we want to validate it against
* @param {Function} callback
*/
PBKDF2.prototype.validate = function (password, callback) {
var self = this
var salt = this.salt
var derived_key = this.derived_key
self.createPassword(password, salt, function (err, key) {
return callback(err, key === derived_key)
})
}
module.exports = PBKDF2