Skip to content
This repository has been archived by the owner on Apr 22, 2023. It is now read-only.

Commit

Permalink
crypto: PBKDF2 function from OpenSSL
Browse files Browse the repository at this point in the history
  • Loading branch information
pixelglow authored and bnoordhuis committed Aug 12, 2011
1 parent 01b64fc commit 04122ad
Show file tree
Hide file tree
Showing 4 changed files with 149 additions and 0 deletions.
5 changes: 5 additions & 0 deletions doc/api/crypto.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -233,3 +233,8 @@ or `'base64'`.

Sets the Diffie-Hellman private key. Key encoding can be `'binary'`, `'hex'`, or `'base64'`.

### pbkdf2(password, salt, iterations, keylen, 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)`.
3 changes: 3 additions & 0 deletions lib/crypto.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ try {
var Sign = binding.Sign;
var Verify = binding.Verify;
var DiffieHellman = binding.DiffieHellman;
var PBKDF2 = binding.PBKDF2;
var crypto = true;
} catch (e) {

Expand Down Expand Up @@ -160,3 +161,5 @@ exports.createDiffieHellman = function(size_or_key, enc) {
}

}

exports.pbkdf2 = PBKDF2;
119 changes: 119 additions & 0 deletions src/node_crypto.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3752,6 +3752,123 @@ class DiffieHellman : public ObjectWrap {
DH* dh;
};

struct pbkdf2_req {
int err;
char* pass;
size_t passlen;
char* salt;
size_t saltlen;
size_t iter;
char* key;
size_t keylen;
Persistent<Function> callback;
};

void
EIO_PBKDF2(eio_req* req) {
pbkdf2_req* request = (pbkdf2_req*)req->data;
request->err = PKCS5_PBKDF2_HMAC_SHA1(
request->pass,
request->passlen,
(unsigned char*)request->salt,
request->saltlen,
request->iter,
request->keylen,
(unsigned char*)request->key);
memset(request->pass, 0, request->passlen);
memset(request->salt, 0, request->saltlen);
}

int
EIO_PBKDF2After(eio_req* req) {
HandleScope scope;

ev_unref(EV_DEFAULT_UC);

pbkdf2_req* request = (pbkdf2_req*)req->data;
Handle<Value> argv[2];
if (request->err) {
argv[0] = Undefined();
argv[1] = Encode(request->key, request->keylen, BINARY);
memset(request->key, 0, request->keylen);
} else {
argv[0] = Exception::Error(String::New("PBKDF2 error"));
argv[1] = Undefined();
}

TryCatch try_catch;

request->callback->Call(Context::GetCurrent()->Global(), 2, argv);

if (try_catch.HasCaught())
FatalException(try_catch);

delete[] request->pass;
delete[] request->salt;
delete[] request->key;
request->callback.Dispose();

delete request;

return 0;
}

Handle<Value>
PBKDF2(const Arguments& args) {
HandleScope scope;

if (args.Length() != 5)
return ThrowException(Exception::TypeError(String::New("Bad parameter")));

ASSERT_IS_STRING_OR_BUFFER(args[0]);
ssize_t passlen = DecodeBytes(args[0], BINARY);
if (passlen < 0)
return ThrowException(Exception::TypeError(String::New("Bad password")));
char* pass = new char[passlen];
ssize_t pass_written = DecodeWrite(pass, passlen, args[0], BINARY);
assert(pass_written == passlen);

ASSERT_IS_STRING_OR_BUFFER(args[1]);
ssize_t saltlen = DecodeBytes(args[1], BINARY);
if (saltlen < 0)
return ThrowException(Exception::TypeError(String::New("Bad salt")));
char* salt = new char[saltlen];
ssize_t salt_written = DecodeWrite(salt, saltlen, args[1], BINARY);
assert(salt_written == saltlen);

if (!args[2]->IsNumber())
return ThrowException(Exception::TypeError(String::New("Iterations not a number")));
ssize_t iter = args[2]->Int32Value();
if (iter < 0)
return ThrowException(Exception::TypeError(String::New("Bad iterations")));

if (!args[3]->IsNumber())
return ThrowException(Exception::TypeError(String::New("Key length not a number")));
ssize_t keylen = args[3]->Int32Value();
if (keylen < 0)
return ThrowException(Exception::TypeError(String::New("Bad key length")));
char* key = new char[keylen];

if (!args[4]->IsFunction())
return ThrowException(Exception::TypeError(String::New("Callback not a function")));
Local<Function> callback = Local<Function>::Cast(args[4]);

pbkdf2_req* request = new pbkdf2_req;
request->err = 0;
request->pass = pass;
request->passlen = passlen;
request->salt = salt;
request->saltlen = saltlen;
request->iter = iter;
request->key = key;
request->keylen = keylen;
request->callback = Persistent<Function>::New(callback);

eio_custom(EIO_PBKDF2, EIO_PRI_DEFAULT, EIO_PBKDF2After, request);
ev_ref(EV_DEFAULT_UC);

return Undefined();
}

void InitCrypto(Handle<Object> target) {
HandleScope scope;
Expand Down Expand Up @@ -3785,6 +3902,8 @@ void InitCrypto(Handle<Object> target) {
Sign::Initialize(target);
Verify::Initialize(target);

NODE_SET_METHOD(target, "PBKDF2", PBKDF2);

subject_symbol = NODE_PSYMBOL("subject");
issuer_symbol = NODE_PSYMBOL("issuer");
valid_from_symbol = NODE_PSYMBOL("valid_from");
Expand Down
22 changes: 22 additions & 0 deletions test/simple/test-crypto.js
Original file line number Diff line number Diff line change
Expand Up @@ -388,3 +388,25 @@ assert.equal(rsaSignature, '5c50e3145c4e2497aadb0eabc83b342d0b0021ece0d4c4a064b7

rsaVerify.update(rsaPubPem);
assert.equal(rsaVerify.verify(rsaPubPem, rsaSignature, 'hex'), 1);

// Test PBKDF2 with RFC 6070 test vectors (except #4)

crypto.pbkdf2('password', 'salt', 1, 20, function (err, result) {
assert.equal(result, '\x0c\x60\xc8\x0f\x96\x1f\x0e\x71\xf3\xa9\xb5\x24\xaf\x60\x12\x06\x2f\xe0\x37\xa6', 'pbkdf1 test vector 1');
});

crypto.pbkdf2('password', 'salt', 2, 20, function (err, result) {
assert.equal(result, '\xea\x6c\x01\x4d\xc7\x2d\x6f\x8c\xcd\x1e\xd9\x2a\xce\x1d\x41\xf0\xd8\xde\x89\x57', 'pbkdf1 test vector 2');
});

crypto.pbkdf2('password', 'salt', 4096, 20, function (err, result) {
assert.equal(result, '\x4b\x00\x79\x01\xb7\x65\x48\x9a\xbe\xad\x49\xd9\x26\xf7\x21\xd0\x65\xa4\x29\xc1', 'pbkdf1 test vector 3');
});

crypto.pbkdf2('passwordPASSWORDpassword', 'saltSALTsaltSALTsaltSALTsaltSALTsalt', 4096, 25, function (err, result) {
assert.equal(result, '\x3d\x2e\xec\x4f\xe4\x1c\x84\x9b\x80\xc8\xd8\x36\x62\xc0\xe4\x4a\x8b\x29\x1a\x96\x4c\xf2\xf0\x70\x38', 'pbkdf1 test vector 5');
});

crypto.pbkdf2('pass\0word', 'sa\0lt', 4096, 16, function (err, result) {
assert.equal(result, '\x56\xfa\x6a\xa7\x55\x48\x09\x9d\xcc\x37\xd7\xf0\x34\x25\xe0\xc3', 'pbkdf1 test vector 6');
});

0 comments on commit 04122ad

Please sign in to comment.