/* alice_secret and bob_secret should be the same */
console.log(alice_secret == bob_secret);
-## crypto.pbkdf2(password, salt, iterations, keylen, callback)
+## crypto.pbkdf2(password, salt, iterations, keylen, [digest], callback)
-Asynchronous PBKDF2 applies pseudorandom function HMAC-SHA1 to derive
-a key of given length from the given password, salt and iterations.
-The callback gets two arguments `(err, derivedKey)`.
+Asynchronous PBKDF2 function. Applies the selected HMAC digest function
+(default: SHA1) to derive a key of the requested length from the password,
+salt and number of iterations. The callback gets two arguments:
+`(err, derivedKey)`.
-## crypto.pbkdf2Sync(password, salt, iterations, keylen)
+Example:
+
+ crypto.pbkdf2('secret', 'salt', 4096, 512, 'sha256', function(err, key) {
+ if (err)
+ throw err;
+ console.log(key.toString('hex')); // 'c5e478d...1469e50'
+ });
+
+You can get a list of supported digest functions with
+[crypto.getHashes()](#crypto_crypto_gethashes).
+
+## crypto.pbkdf2Sync(password, salt, iterations, keylen, [digest])
Synchronous PBKDF2 function. Returns derivedKey or throws error.
-exports.pbkdf2 = function(password, salt, iterations, keylen, callback) {
+exports.pbkdf2 = function(password,
+ salt,
+ iterations,
+ keylen,
+ digest,
+ callback) {
+ if (util.isFunction(digest)) {
+ callback = digest;
+ digest = undefined;
+ }
+
if (!util.isFunction(callback))
throw new Error('No callback provided to pbkdf2');
- return pbkdf2(password, salt, iterations, keylen, callback);
+ return pbkdf2(password, salt, iterations, keylen, digest, callback);
};
-exports.pbkdf2Sync = function(password, salt, iterations, keylen) {
- return pbkdf2(password, salt, iterations, keylen);
+exports.pbkdf2Sync = function(password, salt, iterations, keylen, digest) {
+ return pbkdf2(password, salt, iterations, keylen, digest);
};
-function pbkdf2(password, salt, iterations, keylen, callback) {
+function pbkdf2(password, salt, iterations, keylen, digest, callback) {
password = toBuf(password);
salt = toBuf(salt);
if (exports.DEFAULT_ENCODING === 'buffer')
- return binding.PBKDF2(password, salt, iterations, keylen, callback);
+ return binding.PBKDF2(password, salt, iterations, keylen, digest, callback);
// at this point, we need to handle encodings.
var encoding = exports.DEFAULT_ENCODING;
if (callback) {
- binding.PBKDF2(password, salt, iterations, keylen, function(er, ret) {
+ function next(er, ret) {
if (ret)
ret = ret.toString(encoding);
callback(er, ret);
- });
+ }
+ binding.PBKDF2(password, salt, iterations, keylen, digest, next);
} else {
- var ret = binding.PBKDF2(password, salt, iterations, keylen);
+ var ret = binding.PBKDF2(password, salt, iterations, keylen, digest);
return ret.toString(encoding);
}
}
public:
PBKDF2Request(Environment* env,
Local<Object> object,
+ const EVP_MD* digest,
ssize_t passlen,
char* pass,
ssize_t saltlen,
ssize_t iter,
ssize_t keylen)
: AsyncWrap(env, object),
+ digest_(digest),
error_(0),
passlen_(passlen),
pass_(pass),
return &work_req_;
}
+ inline const EVP_MD* digest() const {
+ return digest_;
+ }
+
inline ssize_t passlen() const {
return passlen_;
}
uv_work_t work_req_;
private:
+ const EVP_MD* digest_;
int error_;
ssize_t passlen_;
char* pass_;
void EIO_PBKDF2(PBKDF2Request* req) {
- req->set_error(PKCS5_PBKDF2_HMAC_SHA1(
+ req->set_error(PKCS5_PBKDF2_HMAC(
req->pass(),
req->passlen(),
reinterpret_cast<unsigned char*>(req->salt()),
req->saltlen(),
req->iter(),
+ req->digest(),
req->keylen(),
reinterpret_cast<unsigned char*>(req->key())));
memset(req->pass(), 0, req->passlen());
HandleScope handle_scope(args.GetIsolate());
Environment* env = Environment::GetCurrent(args.GetIsolate());
+ const EVP_MD* digest = NULL;
const char* type_error = NULL;
char* pass = NULL;
char* salt = NULL;
PBKDF2Request* req = NULL;
Local<Object> obj;
- if (args.Length() != 4 && args.Length() != 5) {
+ if (args.Length() != 5 && args.Length() != 6) {
type_error = "Bad parameter";
goto err;
}
goto err;
}
- obj = Object::New();
- req = new PBKDF2Request(env, obj, passlen, pass, saltlen, salt, iter, keylen);
+ if (args[4]->IsString()) {
+ String::Utf8Value digest_name(args[4]);
+ digest = EVP_get_digestbyname(*digest_name);
+ if (digest == NULL) {
+ type_error = "Bad digest name";
+ goto err;
+ }
+ }
+
+ if (digest == NULL) {
+ digest = EVP_sha1();
+ }
- if (args[4]->IsFunction()) {
- obj->Set(env->ondone_string(), args[4]);
+ obj = Object::New();
+ req = new PBKDF2Request(env,
+ obj,
+ digest,
+ passlen,
+ pass,
+ saltlen,
+ salt,
+ iter,
+ keylen);
+
+ if (args[5]->IsFunction()) {
+ obj->Set(env->ondone_string(), args[5]);
// XXX(trevnorris): This will need to go with the rest of domains.
if (env->in_domain())
obj->Set(env->domain_string(), env->domain_array()->Get(0));
'\x56\xfa\x6a\xa7\x55\x48\x09\x9d\xcc\x37\xd7\xf0\x34' +
'\x25\xe0\xc3');
+(function() {
+ var expected =
+ '64c486c55d30d4c5a079b8823b7d7cb37ff0556f537da8410233bcec330ed956';
+ var key = crypto.pbkdf2Sync('password', 'salt', 32, 32, 'sha256');
+ assert.equal(key.toString('hex'), expected);
+
+ crypto.pbkdf2('password', 'salt', 32, 32, 'sha256', common.mustCall(ondone));
+ function ondone(err, key) {
+ if (err) throw err;
+ assert.equal(key.toString('hex'), expected);
+ }
+})();
+
function assertSorted(list) {
assert.deepEqual(list, list.sort());
}