diff --git a/lib/crypto.js b/lib/crypto.js index cb4ebbd509..5a35603207 100644 --- a/lib/crypto.js +++ b/lib/crypto.js @@ -31,6 +31,8 @@ try { var Verify = binding.Verify; var DiffieHellman = binding.DiffieHellman; var PBKDF2 = binding.PBKDF2; + var randomBytes = binding.randomBytes; + var pseudoRandomBytes = binding.pseudoRandomBytes; var crypto = true; } catch (e) { @@ -163,3 +165,9 @@ exports.createDiffieHellman = function(size_or_key, enc) { } exports.pbkdf2 = PBKDF2; + +exports.randomBytes = randomBytes; +exports.pseudoRandomBytes = pseudoRandomBytes; + +exports.rng = randomBytes; +exports.prng = pseudoRandomBytes; diff --git a/src/node.h b/src/node.h index b147bbfc12..5748598916 100644 --- a/src/node.h +++ b/src/node.h @@ -45,6 +45,18 @@ #include +#ifndef offset_of +// g++ in strict mode complains loudly about the system offsetof() macro +// because it uses NULL as the base address. +#define offset_of(type, member) \ + ((intptr_t) ((char *) &(((type *) 8)->member) - 8)) +#endif + +#ifndef container_of +#define container_of(ptr, type, member) \ + ((type *) ((char *) (ptr) - offset_of(type, member))) +#endif + #ifndef ARRAY_SIZE #define ARRAY_SIZE(a) (sizeof((a)) / sizeof((a)[0])) #endif diff --git a/src/node_crypto.cc b/src/node_crypto.cc index cd1e538bf8..6e3e365d63 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -4013,6 +4013,139 @@ PBKDF2(const Arguments& args) { return Undefined(); } + +typedef int (*RandomBytesGenerator)(unsigned char* buf, int size); + +struct RandomBytesRequest { + ~RandomBytesRequest(); + Persistent callback_; + unsigned long error_; // openssl error code or zero + uv_work_t work_req_; + size_t size_; + char* data_; +}; + + +RandomBytesRequest::~RandomBytesRequest() { + if (!callback_.IsEmpty()) { + callback_.Dispose(); + callback_.Clear(); + } +} + + +void RandomBytesFree(char* data, void* hint) { + delete[] data; +} + + +template +void RandomBytesWork(uv_work_t* work_req) { + RandomBytesRequest* req = + container_of(work_req, RandomBytesRequest, work_req_); + + int r = generator(reinterpret_cast(req->data_), req->size_); + + switch (r) { + case 0: + // RAND_bytes() returns 0 on error, RAND_pseudo_bytes() returns 0 + // when the result is not cryptographically strong - the latter + // sucks but is not an error + if (generator == RAND_bytes) + req->error_ = ERR_get_error(); + break; + + case -1: + // not supported - can this actually happen? + req->error_ = (unsigned long) -1; + break; + } +} + + +void RandomBytesCheck(RandomBytesRequest* req, Handle argv[2]) { + HandleScope scope; + + if (req->error_) { + char errmsg[256] = "Operation not supported"; + + if (req->error_ != (unsigned long) -1) + ERR_error_string_n(req->error_, errmsg, sizeof errmsg); + + argv[0] = Exception::Error(String::New(errmsg)); + argv[1] = Null(); + } + else { + // avoids the malloc + memcpy + Buffer* buffer = Buffer::New(req->data_, req->size_, RandomBytesFree, NULL); + argv[0] = Null(); + argv[1] = buffer->handle_; + } +} + + +template +void RandomBytesAfter(uv_work_t* work_req) { + RandomBytesRequest* req = + container_of(work_req, RandomBytesRequest, work_req_); + + HandleScope scope; + Handle argv[2]; + RandomBytesCheck(req, argv); + + TryCatch tc; + req->callback_->Call(Context::GetCurrent()->Global(), 2, argv); + + if (tc.HasCaught()) + FatalException(tc); + + delete req; +} + + +template +Handle RandomBytes(const Arguments& args) { + HandleScope scope; + + // maybe allow a buffer to write to? cuts down on object creation + // when generating random data in a loop + if (!args[0]->IsUint32()) { + Local s = String::New("Argument #1 must be number > 0"); + return ThrowException(Exception::TypeError(s)); + } + + const size_t size = args[0]->Uint32Value(); + + RandomBytesRequest* req = new RandomBytesRequest(); + req->error_ = 0; + req->data_ = new char[size]; + req->size_ = size; + + if (args[1]->IsFunction()) { + Local callback_v = Local(Function::Cast(*args[1])); + req->callback_ = Persistent::New(callback_v); + + uv_queue_work(uv_default_loop(), + &req->work_req_, + RandomBytesWork, + RandomBytesAfter); + + return Undefined(); + } + else { + Handle argv[2]; + RandomBytesWork(&req->work_req_); + RandomBytesCheck(req, argv); + delete req; + + if (!argv[0]->IsNull()) + return ThrowException(argv[0]); + else + return argv[1]; + } +} + + void InitCrypto(Handle target) { HandleScope scope; @@ -4046,6 +4179,8 @@ void InitCrypto(Handle target) { Verify::Initialize(target); NODE_SET_METHOD(target, "PBKDF2", PBKDF2); + NODE_SET_METHOD(target, "randomBytes", RandomBytes); + NODE_SET_METHOD(target, "pseudoRandomBytes", RandomBytes); subject_symbol = NODE_PSYMBOL("subject"); issuer_symbol = NODE_PSYMBOL("issuer"); diff --git a/src/node_crypto.h b/src/node_crypto.h index 0d05279d38..d28602eb57 100644 --- a/src/node_crypto.h +++ b/src/node_crypto.h @@ -34,6 +34,7 @@ #include #include #include +#include #ifdef OPENSSL_NPN_NEGOTIATED #include