Skip to content

Commit

Permalink
crypto: implement randomBytes() and pseudoRandomBytes()
Browse files Browse the repository at this point in the history
  • Loading branch information
bnoordhuis committed Sep 27, 2011
1 parent 67706b8 commit c4eaf7e
Show file tree
Hide file tree
Showing 4 changed files with 156 additions and 0 deletions.
8 changes: 8 additions & 0 deletions lib/crypto.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {

Expand Down Expand Up @@ -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;
12 changes: 12 additions & 0 deletions src/node.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,18 @@

#include <node_object_wrap.h>

#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
Expand Down
135 changes: 135 additions & 0 deletions src/node_crypto.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4013,6 +4013,139 @@ PBKDF2(const Arguments& args) {
return Undefined();
}


typedef int (*RandomBytesGenerator)(unsigned char* buf, int size);

struct RandomBytesRequest {
~RandomBytesRequest();
Persistent<Function> 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 <RandomBytesGenerator generator>
void RandomBytesWork(uv_work_t* work_req) {
RandomBytesRequest* req =
container_of(work_req, RandomBytesRequest, work_req_);

int r = generator(reinterpret_cast<unsigned char*>(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<Value> 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 <RandomBytesGenerator generator>
void RandomBytesAfter(uv_work_t* work_req) {
RandomBytesRequest* req =
container_of(work_req, RandomBytesRequest, work_req_);

HandleScope scope;
Handle<Value> argv[2];
RandomBytesCheck(req, argv);

TryCatch tc;
req->callback_->Call(Context::GetCurrent()->Global(), 2, argv);

if (tc.HasCaught())
FatalException(tc);

delete req;
}


template <RandomBytesGenerator generator>
Handle<Value> 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<String> 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<Function> callback_v = Local<Function>(Function::Cast(*args[1]));
req->callback_ = Persistent<Function>::New(callback_v);

uv_queue_work(uv_default_loop(),
&req->work_req_,
RandomBytesWork<generator>,
RandomBytesAfter<generator>);

return Undefined();
}
else {
Handle<Value> argv[2];
RandomBytesWork<generator>(&req->work_req_);
RandomBytesCheck(req, argv);
delete req;

if (!argv[0]->IsNull())
return ThrowException(argv[0]);
else
return argv[1];
}
}


void InitCrypto(Handle<Object> target) {
HandleScope scope;

Expand Down Expand Up @@ -4046,6 +4179,8 @@ void InitCrypto(Handle<Object> target) {
Verify::Initialize(target);

NODE_SET_METHOD(target, "PBKDF2", PBKDF2);
NODE_SET_METHOD(target, "randomBytes", RandomBytes<RAND_bytes>);
NODE_SET_METHOD(target, "pseudoRandomBytes", RandomBytes<RAND_pseudo_bytes>);

subject_symbol = NODE_PSYMBOL("subject");
issuer_symbol = NODE_PSYMBOL("issuer");
Expand Down
1 change: 1 addition & 0 deletions src/node_crypto.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include <openssl/x509.h>
#include <openssl/x509v3.h>
#include <openssl/hmac.h>
#include <openssl/rand.h>

#ifdef OPENSSL_NPN_NEGOTIATED
#include <node_buffer.h>
Expand Down

0 comments on commit c4eaf7e

Please sign in to comment.