Browse Source

crypto: PBKDF2 function from OpenSSL

v0.7.4-release
Glen Low 14 years ago
committed by Ben Noordhuis
parent
commit
04122ad2d3
  1. 5
      doc/api/crypto.markdown
  2. 3
      lib/crypto.js
  3. 119
      src/node_crypto.cc
  4. 22
      test/simple/test-crypto.js

5
doc/api/crypto.markdown

@ -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
lib/crypto.js

@ -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) {
@ -160,3 +161,5 @@ exports.createDiffieHellman = function(size_or_key, enc) {
}
}
exports.pbkdf2 = PBKDF2;

119
src/node_crypto.cc

@ -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;
@ -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");

22
test/simple/test-crypto.js

@ -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');
});

Loading…
Cancel
Save