Skip to content

Commit 6262fa4

Browse files
bnoordhuistargos
authored andcommitted
crypto: refactor pbkdf2() and pbkdf2Sync() methods
Use the scrypt() infrastructure to reimplement pbkdf2() in a simpler manner. PR-URL: #20816 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Tobias Nießen <[email protected]>
1 parent c9b4592 commit 6262fa4

File tree

6 files changed

+121
-180
lines changed

6 files changed

+121
-180
lines changed

doc/api/errors.md

+6
Original file line numberDiff line numberDiff line change
@@ -739,6 +739,12 @@ An invalid [crypto digest algorithm][] was specified.
739739
A crypto method was used on an object that was in an invalid state. For
740740
instance, calling [`cipher.getAuthTag()`][] before calling `cipher.final()`.
741741

742+
<a id="ERR_CRYPTO_PBKDF2_ERROR"></a>
743+
### ERR_CRYPTO_PBKDF2_ERROR
744+
745+
The PBKDF2 algorithm failed for unspecified reasons. OpenSSL does not provide
746+
more details and therefore neither does Node.js.
747+
742748
<a id="ERR_CRYPTO_SCRYPT_INVALID_PARAMETER"></a>
743749
### ERR_CRYPTO_SCRYPT_INVALID_PARAMETER
744750

lib/internal/crypto/pbkdf2.js

+41-32
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,61 @@
11
'use strict';
22

3+
const { AsyncWrap, Providers } = process.binding('async_wrap');
4+
const { Buffer } = require('buffer');
5+
const { pbkdf2: _pbkdf2 } = process.binding('crypto');
36
const {
7+
ERR_CRYPTO_INVALID_DIGEST,
8+
ERR_CRYPTO_PBKDF2_ERROR,
49
ERR_INVALID_ARG_TYPE,
510
ERR_INVALID_CALLBACK,
6-
ERR_CRYPTO_INVALID_DIGEST,
711
} = require('internal/errors').codes;
812
const {
913
checkIsArrayBufferView,
1014
checkIsUint,
1115
getDefaultEncoding,
1216
} = require('internal/crypto/util');
13-
const {
14-
PBKDF2
15-
} = process.binding('crypto');
1617

1718
function pbkdf2(password, salt, iterations, keylen, digest, callback) {
1819
if (typeof digest === 'function') {
1920
callback = digest;
2021
digest = undefined;
2122
}
2223

24+
({ password, salt, iterations, keylen, digest } =
25+
check(password, salt, iterations, keylen, digest, callback));
26+
2327
if (typeof callback !== 'function')
2428
throw new ERR_INVALID_CALLBACK();
2529

26-
return _pbkdf2(password, salt, iterations, keylen, digest, callback);
30+
const encoding = getDefaultEncoding();
31+
const keybuf = Buffer.alloc(keylen);
32+
33+
const wrap = new AsyncWrap(Providers.PBKDF2REQUEST);
34+
wrap.ondone = (ok) => { // Retains keybuf while request is in flight.
35+
if (!ok) return callback.call(wrap, new ERR_CRYPTO_PBKDF2_ERROR());
36+
if (encoding === 'buffer') return callback.call(wrap, null, keybuf);
37+
callback.call(wrap, null, keybuf.toString(encoding));
38+
};
39+
40+
handleError(keybuf, password, salt, iterations, digest, wrap);
2741
}
2842

2943
function pbkdf2Sync(password, salt, iterations, keylen, digest) {
30-
return _pbkdf2(password, salt, iterations, keylen, digest);
44+
({ password, salt, iterations, keylen, digest } =
45+
check(password, salt, iterations, keylen, digest, pbkdf2Sync));
46+
const keybuf = Buffer.alloc(keylen);
47+
handleError(keybuf, password, salt, iterations, digest);
48+
const encoding = getDefaultEncoding();
49+
if (encoding === 'buffer') return keybuf;
50+
return keybuf.toString(encoding);
3151
}
3252

33-
function _pbkdf2(password, salt, iterations, keylen, digest, callback) {
34-
35-
if (digest !== null && typeof digest !== 'string')
36-
throw new ERR_INVALID_ARG_TYPE('digest', ['string', 'null'], digest);
53+
function check(password, salt, iterations, keylen, digest, callback) {
54+
if (typeof digest !== 'string') {
55+
if (digest !== null)
56+
throw new ERR_INVALID_ARG_TYPE('digest', ['string', 'null'], digest);
57+
digest = 'sha1';
58+
}
3759

3860
password = checkIsArrayBufferView('password', password);
3961
salt = checkIsArrayBufferView('salt', salt);
@@ -42,30 +64,17 @@ function _pbkdf2(password, salt, iterations, keylen, digest, callback) {
4264
iterations = checkIsUint('iterations', iterations, 'a non-negative number');
4365
keylen = checkIsUint('keylen', keylen);
4466

45-
const encoding = getDefaultEncoding();
67+
return { password, salt, iterations, keylen, digest };
68+
}
4669

47-
if (encoding === 'buffer') {
48-
const ret = PBKDF2(password, salt, iterations, keylen, digest, callback);
49-
if (ret === -1)
50-
throw new ERR_CRYPTO_INVALID_DIGEST(digest);
51-
return ret;
52-
}
70+
function handleError(keybuf, password, salt, iterations, digest, wrap) {
71+
const rc = _pbkdf2(keybuf, password, salt, iterations, digest, wrap);
5372

54-
// at this point, we need to handle encodings.
55-
if (callback) {
56-
function next(er, ret) {
57-
if (ret)
58-
ret = ret.toString(encoding);
59-
callback(er, ret);
60-
}
61-
if (PBKDF2(password, salt, iterations, keylen, digest, next) === -1)
62-
throw new ERR_CRYPTO_INVALID_DIGEST(digest);
63-
} else {
64-
const ret = PBKDF2(password, salt, iterations, keylen, digest);
65-
if (ret === -1)
66-
throw new ERR_CRYPTO_INVALID_DIGEST(digest);
67-
return ret.toString(encoding);
68-
}
73+
if (rc === -1)
74+
throw new ERR_CRYPTO_INVALID_DIGEST(digest);
75+
76+
if (rc === false)
77+
throw new ERR_CRYPTO_PBKDF2_ERROR();
6978
}
7079

7180
module.exports = {

lib/internal/errors.js

+2
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,8 @@ E('ERR_CRYPTO_HASH_FINALIZED', 'Digest already called', Error);
503503
E('ERR_CRYPTO_HASH_UPDATE_FAILED', 'Hash update failed', Error);
504504
E('ERR_CRYPTO_INVALID_DIGEST', 'Invalid digest: %s', TypeError);
505505
E('ERR_CRYPTO_INVALID_STATE', 'Invalid state for operation %s', Error);
506+
// TODO(bnoordhuis) Decapitalize: s/PBKDF2 Error/PBKDF2 error/
507+
E('ERR_CRYPTO_PBKDF2_ERROR', 'PBKDF2 Error', Error);
506508
E('ERR_CRYPTO_SCRYPT_INVALID_PARAMETER', 'Invalid scrypt parameter', Error);
507509
E('ERR_CRYPTO_SCRYPT_NOT_SUPPORTED', 'Scrypt algorithm not supported', Error);
508510
// Switch to TypeError. The current implementation does not seem right.

src/env.h

-2
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,6 @@ struct PackageConfig {
244244
V(password_string, "password") \
245245
V(path_string, "path") \
246246
V(pending_handle_string, "pendingHandle") \
247-
V(pbkdf2_error_string, "PBKDF2 Error") \
248247
V(pid_string, "pid") \
249248
V(pipe_string, "pipe") \
250249
V(pipe_target_string, "pipeTarget") \
@@ -337,7 +336,6 @@ struct PackageConfig {
337336
V(inspector_console_api_object, v8::Object) \
338337
V(message_port, v8::Object) \
339338
V(message_port_constructor_template, v8::FunctionTemplate) \
340-
V(pbkdf2_constructor_template, v8::ObjectTemplate) \
341339
V(pipe_constructor_template, v8::FunctionTemplate) \
342340
V(performance_entry_callback, v8::Function) \
343341
V(performance_entry_template, v8::Function) \

0 commit comments

Comments
 (0)