From 2a30d328fa97eccef80256436f13651adadae4e4 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Thu, 26 Jul 2012 01:45:04 +0200 Subject: [PATCH] crypto: add sync interface to crypto.pbkdf2() Fixes #3766. --- src/node_crypto.cc | 128 +++++++++++++++++++------------------ test/simple/test-crypto.js | 67 ++++++++----------- 2 files changed, 95 insertions(+), 100 deletions(-) diff --git a/src/node_crypto.cc b/src/node_crypto.cc index 5b21e55a8c..d1dd84dab9 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -4192,7 +4192,9 @@ class DiffieHellman : public ObjectWrap { DH* dh; }; + struct pbkdf2_req { + uv_work_t work_req; int err; char* pass; size_t passlen; @@ -4204,60 +4206,65 @@ struct pbkdf2_req { Persistent callback; }; -void -EIO_PBKDF2(uv_work_t* 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); + +void EIO_PBKDF2(pbkdf2_req* req) { + req->err = PKCS5_PBKDF2_HMAC_SHA1( + req->pass, + req->passlen, + (unsigned char*)req->salt, + req->saltlen, + req->iter, + req->keylen, + (unsigned char*)req->key); + memset(req->pass, 0, req->passlen); + memset(req->salt, 0, req->saltlen); } -void -EIO_PBKDF2After(uv_work_t* req) { - HandleScope scope; - pbkdf2_req* request = (pbkdf2_req*)req->data; - delete req; +void EIO_PBKDF2(uv_work_t* work_req) { + pbkdf2_req* req = container_of(work_req, pbkdf2_req, work_req); + EIO_PBKDF2(req); +} - Local argv[2]; - if (request->err) { + +void EIO_PBKDF2After(pbkdf2_req* req, Local argv[2]) { + if (req->err) { argv[0] = Local::New(Undefined()); - argv[1] = Encode(request->key, request->keylen, BINARY); - memset(request->key, 0, request->keylen); + argv[1] = Encode(req->key, req->keylen, BINARY); + memset(req->key, 0, req->keylen); } else { argv[0] = Exception::Error(String::New("PBKDF2 error")); argv[1] = Local::New(Undefined()); } - // XXX There should be an object connected to this that - // we can attach a domain onto. - MakeCallback(Context::GetCurrent()->Global(), - request->callback, - ARRAY_SIZE(argv), argv); + delete[] req->pass; + delete[] req->salt; + delete[] req->key; + delete req; +} + - delete[] request->pass; - delete[] request->salt; - delete[] request->key; - request->callback.Dispose(); +void EIO_PBKDF2After(uv_work_t* work_req) { + pbkdf2_req* req = container_of(work_req, pbkdf2_req, work_req); - delete request; + HandleScope scope; + Local argv[2]; + Persistent cb = req->callback; + EIO_PBKDF2After(req, argv); + + // XXX There should be an object connected to this that + // we can attach a domain onto. + MakeCallback(Context::GetCurrent()->Global(), cb, ARRAY_SIZE(argv), argv); + cb.Dispose(); } -Handle -PBKDF2(const Arguments& args) { + +Handle PBKDF2(const Arguments& args) { HandleScope scope; const char* type_error = NULL; char* pass = NULL; char* salt = NULL; - char* key = NULL; ssize_t passlen = -1; ssize_t saltlen = -1; ssize_t keylen = -1; @@ -4265,10 +4272,9 @@ PBKDF2(const Arguments& args) { ssize_t salt_written = -1; ssize_t iter = -1; Local callback; - pbkdf2_req* request = NULL; - uv_work_t* req = NULL; + pbkdf2_req* req = NULL; - if (args.Length() != 5) { + if (args.Length() != 4 && args.Length() != 5) { type_error = "Bad parameter"; goto err; } @@ -4317,33 +4323,33 @@ PBKDF2(const Arguments& args) { goto err; } - key = new char[keylen]; + req = new pbkdf2_req; + req->err = 0; + req->pass = pass; + req->passlen = passlen; + req->salt = salt; + req->saltlen = saltlen; + req->iter = iter; + req->key = new char[keylen]; + req->keylen = keylen; - if (!args[4]->IsFunction()) { - type_error = "Callback not a function"; - goto err; + if (args[4]->IsFunction()) { + callback = Local::Cast(args[4]); + req->callback = Persistent::New(callback); + uv_queue_work(uv_default_loop(), + &req->work_req, + EIO_PBKDF2, + EIO_PBKDF2After); + return Undefined(); + } else { + Local argv[2]; + EIO_PBKDF2(req); + EIO_PBKDF2After(req, argv); + if (argv[0]->IsObject()) return ThrowException(argv[0]); + return scope.Close(argv[1]); } - callback = Local::Cast(args[4]); - - 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::New(callback); - - req = new uv_work_t(); - req->data = request; - uv_queue_work(uv_default_loop(), req, EIO_PBKDF2, EIO_PBKDF2After); - return Undefined(); - err: - delete[] key; delete[] salt; delete[] pass; return ThrowException(Exception::TypeError(String::New(type_error))); diff --git a/test/simple/test-crypto.js b/test/simple/test-crypto.js index de8c1a9d63..abd83247d9 100644 --- a/test/simple/test-crypto.js +++ b/test/simple/test-crypto.js @@ -618,46 +618,35 @@ assert.strictEqual(rsaVerify.verify(rsaPubPem, rsaSignature, 'hex'), true); // // 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'); -}); +function testPBKDF2(password, salt, iterations, keylen, expected) { + var actual = crypto.pbkdf2(password, salt, iterations, keylen); + assert.equal(actual, expected); -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, iterations, keylen, function(err, actual) { + assert.equal(actual, expected); + }); +} -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'); -}); +testPBKDF2('password', 'salt', 1, 20, + '\x0c\x60\xc8\x0f\x96\x1f\x0e\x71\xf3\xa9\xb5\x24' + + '\xaf\x60\x12\x06\x2f\xe0\x37\xa6'); -// Error path should not leak memory (check with valgrind). -assert.throws(function() { - crypto.pbkdf2('password', 'salt', 1, 20, null); -}); +testPBKDF2('password', 'salt', 2, 20, + '\xea\x6c\x01\x4d\xc7\x2d\x6f\x8c\xcd\x1e\xd9\x2a' + + '\xce\x1d\x41\xf0\xd8\xde\x89\x57'); + +testPBKDF2('password', 'salt', 4096, 20, + '\x4b\x00\x79\x01\xb7\x65\x48\x9a\xbe\xad\x49\xd9\x26' + + '\xf7\x21\xd0\x65\xa4\x29\xc1'); + +testPBKDF2('passwordPASSWORDpassword', + 'saltSALTsaltSALTsaltSALTsaltSALTsalt', + 4096, + 25, + '\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'); + +testPBKDF2('pass\0word', 'sa\0lt', 4096, 16, + '\x56\xfa\x6a\xa7\x55\x48\x09\x9d\xcc\x37\xd7\xf0\x34' + + '\x25\xe0\xc3');