Skip to content

Commit 594428a

Browse files
committedSep 19, 2015
Use constant time string compare, fixes dcodeIO#29
1 parent dd6eaf7 commit 594428a

File tree

7 files changed

+86
-44
lines changed

7 files changed

+86
-44
lines changed
 

‎bower.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "bcryptjs",
33
"description": "Optimized bcrypt in plain JavaScript with zero dependencies.",
4-
"version": "2.2.1",
4+
"version": "2.2.2",
55
"main": "dist/bcrypt-isaac.js",
66
"license": "New-BSD",
77
"homepage": "http://dcode.io/",

‎dist/bcrypt.js

+32-11
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,28 @@
194194
nextTick(callback.bind(this, Error("Illegal arguments: "+(typeof s)+', '+(typeof salt))));
195195
};
196196

197+
/**
198+
* Compares two strings of the same length in constant time.
199+
* @param {string} known Must be of the correct length
200+
* @param {string} unknown Must be the same length as `known`
201+
* @returns {boolean}
202+
* @inner
203+
*/
204+
function safeStringCompare(known, unknown) {
205+
var right = 0,
206+
wrong = 0;
207+
for (var i=0, k=known.length; i<k; ++i) {
208+
if (known.charCodeAt(i) === unknown.charCodeAt(i))
209+
++right;
210+
else
211+
++wrong;
212+
}
213+
// Prevent removal of unused variables (never true, actually)
214+
if (right < 0)
215+
return false;
216+
return wrong === 0;
217+
}
218+
197219
/**
198220
* Synchronously tests a string against a hash.
199221
* @param {string} s String to compare
@@ -207,15 +229,7 @@
207229
throw Error("Illegal arguments: "+(typeof s)+', '+(typeof hash));
208230
if (hash.length !== 60)
209231
return false;
210-
var comp = bcrypt.hashSync(s, hash.substr(0, hash.length-31)),
211-
same = comp.length === hash.length,
212-
max_length = (comp.length < hash.length) ? comp.length : hash.length;
213-
// to prevent timing attacks, should check entire string
214-
// don't exit after found to be false
215-
for (var i = 0; i < max_length; ++i)
216-
if (comp.length >= i && hash.length >= i && comp[i] != hash[i])
217-
same = false;
218-
return same;
232+
return safeStringCompare(bcrypt.hashSync(s, hash.substr(0, hash.length-31)), hash);
219233
};
220234

221235
/**
@@ -235,8 +249,15 @@
235249
nextTick(callback.bind(this, Error("Illegal arguments: "+(typeof s)+', '+(typeof hash))));
236250
return;
237251
}
252+
if (hash.length !== 60) {
253+
nextTick(callback.bind(this, null, false));
254+
return;
255+
}
238256
bcrypt.hash(s, hash.substr(0, 29), function(err, comp) {
239-
callback(err, hash === comp);
257+
if (err)
258+
callback(err);
259+
else
260+
callback(null, safeStringCompare(comp, hash));
240261
}, progressCallback);
241262
};
242263

@@ -1032,7 +1053,7 @@
10321053
*/
10331054
function next() {
10341055
if (progressCallback)
1035-
progressCallback(i/rounds);
1056+
progressCallback(i / rounds);
10361057
if (i < rounds) {
10371058
var start = Date.now();
10381059
for (; i < rounds;) {

‎dist/bcrypt.min.js

+18-18
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎dist/bcrypt.min.map

+2-2
Large diffs are not rendered by default.

‎package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "bcryptjs",
33
"description": "Optimized bcrypt in plain JavaScript with zero dependencies. Compatible to 'bcrypt'.",
4-
"version": "2.2.1",
4+
"version": "2.2.2",
55
"author": "Daniel Wirtz <dcode@dcode.io>",
66
"contributors": [
77
"Shane Girish <shaneGirish@gmail.com> (https://github.com/shaneGirish)",

‎src/bcrypt.js

+31-10
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,28 @@ bcrypt.hash = function(s, salt, callback, progressCallback) {
154154
nextTick(callback.bind(this, Error("Illegal arguments: "+(typeof s)+', '+(typeof salt))));
155155
};
156156

157+
/**
158+
* Compares two strings of the same length in constant time.
159+
* @param {string} known Must be of the correct length
160+
* @param {string} unknown Must be the same length as `known`
161+
* @returns {boolean}
162+
* @inner
163+
*/
164+
function safeStringCompare(known, unknown) {
165+
var right = 0,
166+
wrong = 0;
167+
for (var i=0, k=known.length; i<k; ++i) {
168+
if (known.charCodeAt(i) === unknown.charCodeAt(i))
169+
++right;
170+
else
171+
++wrong;
172+
}
173+
// Prevent removal of unused variables (never true, actually)
174+
if (right < 0)
175+
return false;
176+
return wrong === 0;
177+
}
178+
157179
/**
158180
* Synchronously tests a string against a hash.
159181
* @param {string} s String to compare
@@ -167,15 +189,7 @@ bcrypt.compareSync = function(s, hash) {
167189
throw Error("Illegal arguments: "+(typeof s)+', '+(typeof hash));
168190
if (hash.length !== 60)
169191
return false;
170-
var comp = bcrypt.hashSync(s, hash.substr(0, hash.length-31)),
171-
same = comp.length === hash.length,
172-
max_length = (comp.length < hash.length) ? comp.length : hash.length;
173-
// to prevent timing attacks, should check entire string
174-
// don't exit after found to be false
175-
for (var i = 0; i < max_length; ++i)
176-
if (comp.length >= i && hash.length >= i && comp[i] != hash[i])
177-
same = false;
178-
return same;
192+
return safeStringCompare(bcrypt.hashSync(s, hash.substr(0, hash.length-31)), hash);
179193
};
180194

181195
/**
@@ -195,8 +209,15 @@ bcrypt.compare = function(s, hash, callback, progressCallback) {
195209
nextTick(callback.bind(this, Error("Illegal arguments: "+(typeof s)+', '+(typeof hash))));
196210
return;
197211
}
212+
if (hash.length !== 60) {
213+
nextTick(callback.bind(this, null, false));
214+
return;
215+
}
198216
bcrypt.hash(s, hash.substr(0, 29), function(err, comp) {
199-
callback(err, hash === comp);
217+
if (err)
218+
callback(err);
219+
else
220+
callback(null, safeStringCompare(comp, hash));
200221
}, progressCallback);
201222
};
202223

‎src/bcrypt/impl.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -419,7 +419,7 @@ function _crypt(b, salt, rounds, callback, progressCallback) {
419419
*/
420420
function next() {
421421
if (progressCallback)
422-
progressCallback(i/rounds);
422+
progressCallback(i / rounds);
423423
if (i < rounds) {
424424
var start = Date.now();
425425
for (; i < rounds;) {

0 commit comments

Comments
 (0)
Please sign in to comment.