-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathmosquitto_pbkdf2.js
193 lines (184 loc) · 8.51 KB
/
mosquitto_pbkdf2.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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
/**
* ================================================================
* This module provide PBKDF2 passwords functionality to be used
* toguether with mosquitto-auth-plug, that uses this password
* format but using its specific algorithm configuration.
* License: MIT
* @fileOverview PBKDF2 to be used with mosquitto-auth-plug.
* @author enZina Technologies <[email protected]>
* @version 1.0
* ================================================================
*/
var pbkdf2 = require('pbkdf2');
var crypto = require('crypto');
// All events are managed by this event manager.
var EventEmitter = require('events').EventEmitter;
var localEventEmitter = new EventEmitter();
localEventEmitter.on('passwordCreationStarted', createNewHash);
localEventEmitter.on('newPasswordGenerated', onNewPasswordCreated);
localEventEmitter.on('passwordRecreationStarted', recreateExistingHash);
localEventEmitter.on('passwordVerificationFinished', onPasswordVerificationFinished);
// Parameters configuration. Modify as needed.
var separator = '$';
var tag = 'PBKDF2';
var algorithm = 'sha256';
var iterations = 901;
var keyLen = 24;
var saltLen = 12;
/**
* ================================================================
* This function start the creation of a new PBKDF2 password to be
* stored on MySQL for user authentication. It runs asynchronously
* and uses the format required by mosquitto-auth-plug.
* @description Starts the creation of a new PBKDF2 asynchronously
* following the format required by mosquitto-auth-plug.
* @param plainPassword The plain password for the specified
* username.
* @param onNewPassword Callback function to be called once the new
* password generation has finished. It is passed in to the next
* callback until it is necessary.
* @author Manuel Domínguez-Dorado <[email protected]>
* @since 1.0
* ================================================================
*/
function createPasswordAsync(plainPassword, onNewPassword) {
localEventEmitter.emit('passwordCreationStarted', plainPassword, onNewPassword);
}
/**
* ================================================================
* This function is a callback that is launched when a
* 'passwordCreationStarted' event is triggered. Creates a new
* PBKDF2 password using the values specified as parameters. It
* runs asynchronously and uses the format required by
* mosquitto-auth-plug. On success, it emits a
* 'newPasswordGenerated' event to notify other functions.
* @description Creates a new PBKDF2 asynchronously following the
* format required by mosquitto-auth-plug.
* @param plainPassword The plain password for the specified
* username.
* @param onNewPassword Callback function to be called once the new
* password generation has finished. It is passed in to the next
* callback until it is necessary.
* @author Manuel Domínguez-Dorado <[email protected]>
* @since 1.0
* ================================================================
*/
function createNewHash(plainPassword, onNewPassword) {
setImmediate(function () {
var pw = plainPassword;
var newSalt;
var newPasswordForMySQL;
var newHash;
try {
newSalt = crypto.randomBytes(saltLen).toString('base64');
newHash = pbkdf2.pbkdf2Sync(pw, newSalt, iterations, keyLen, algorithm).toString('base64');
newPasswordForMySQL = tag + separator + algorithm + separator + iterations + separator + newSalt + separator + newHash;
} catch (err) {
console.log('Not enough entropy to generate random salt');
newSalt = 'NewDefaultSalt16';
newHash = pbkdf2.pbkdf2Sync(pw, newSalt, iterations, keyLen, algorithm).toString('base64');
newPasswordForMySQL = tag + separator + algorithm + separator + iterations + separator + newSalt + separator + newHash;
} finally {
localEventEmitter.emit('newPasswordGenerated', newPasswordForMySQL, onNewPassword);
}
});
}
/**
* ================================================================
* This function is a callback that is launched when a
* 'newPasswordGenerated' event is triggered. It calls the callback
* function that will receive the created PBKDF2 password.
* @description Calls the callback function that will receive the
* created PBKDF2 password.
* @param pbkdf2Password The generated PBKDF2 that correspond to
* the plainPassword that was specified at the begining of the
* process.
* @param onNewPassword Callback function that will receive the
* created PBKDF2 password.
* @author Manuel Domínguez-Dorado <[email protected]>
* @since 1.0
* ================================================================
*/
function onNewPasswordCreated(pbkdf2Password, onNewPassword) {
setImmediate(onNewPassword, pbkdf2Password);
}
/**
* ================================================================
* This function start the process of verifying if a given plain
* password match a PBKDF2 hash (in mosquitto-auth-plug format),
* and returns the result of such verification.
* @description Verify if plain password match the supplied PBKDF2
* hash.
* @param plainPassword The plain password to be checked.
* @param pbkdf2Password A PBKDF2 hash (in mosquitto-auth-plug
* format) to be compared with the plain password supplied.
* @param onVerificationFinished Callback function to be called
* once the verificaation process has finished. It is passed in to
* the next callback until it is necessary.
* @author Manuel Domínguez-Dorado <[email protected]>
* @since 1.0
* ================================================================
*/
function verifyCredentials(plainPassword, pbkdf2Password, onVerificationFinished) {
localEventEmitter.emit('passwordRecreationStarted', plainPassword, pbkdf2Password, onVerificationFinished);
}
/**
* ================================================================
* This function is a callback that is launched when a
* 'passwordRecreationStarted' event is triggered. Creates a new
* PBKDF2 password using the values specified as parameters and
* those oncluded in a previous PBKDF2 hash. It runs asynchronously
* and uses the format required by mosquitto-auth-plug. On success,
* it emits a 'passwordVerificationFinished' event to notify other
* functions.
* @description Recreates a PBKDF2 hash from a plain password and
* checks whether it matches the previous PBKDF2 hash.
* @param plainPassword The plain password to be checked.
* @param pbkdf2Password A PBKDF2 hash (in mosquitto-auth-plug
* format) to be compared with the plain password supplied.
* @param onVerificationFinished Callback function to be called
* once the verificaation process has finished. It is passed in to
* the next callback until it is necessary.
* @author Manuel Domínguez-Dorado <[email protected]>
* @since 1.0
* ================================================================
*/
function recreateExistingHash(plainPassword, pbkdf2Password, onVerificationFinished) {
setImmediate(function () {
var pw = plainPassword;
var fields = [];
fields = pbkdf2Password.split(separator);
var storedIterations = Number(fields[2]);
var storedSalt = fields[3];
var recreatedPasswordForMySQL;
var recreatedHash;
recreatedHash = pbkdf2.pbkdf2Sync(pw, storedSalt, storedIterations, keyLen, algorithm).toString('base64');
recreatedPasswordForMySQL = tag + separator + algorithm + separator + iterations + separator + storedSalt + separator + recreatedHash;
if (pbkdf2Password === recreatedPasswordForMySQL) {
localEventEmitter.emit('passwordVerificationFinished', true, onVerificationFinished);
} else {
localEventEmitter.emit('passwordVerificationFinished', false, onVerificationFinished);
}
});
}
/**
* ================================================================
* This function is a callback that is launched when a
* 'passwordVerificationFinished' event is triggered. It calls the
* callback function that will receive the result of the password
* verification.
* @description Calls the callback function that will receive the
* password verification result.
* @param validPassword The result value, 'true' if password
* verification is successful or 'false' on the contrary.
* @param onVerificationFinished Callback function that will
* receive the result of the password verification.
* @author Manuel Domínguez-Dorado <[email protected]>
* @since 1.0
* ================================================================
*/
function onPasswordVerificationFinished(validPassword, onVerificationFinished) {
setImmediate(onVerificationFinished, validPassword);
}
exports.createPasswordAsync = createPasswordAsync;
exports.verifyCredentials = verifyCredentials;