Browse Source

crypto: convert RandomBytesRequest to a class

Since RandomBytesRequest makes a call to MakeCallback, needed it to be
a class so AsyncWrap could handle any async listeners.

Also added a simple test for an issue had during implementation where
the memory was being released and returned.
Trevor Norris 11 years ago
parent
commit
66d908f09a
  1. 112
      src/node_crypto.cc
  2. 3
      test/simple/test-crypto.js

112
src/node_crypto.cc

@ -3450,23 +3450,64 @@ void PBKDF2(const FunctionCallbackInfo<Value>& args) {
} }
// TODO(bnoordhuis) Turn into proper RAII class. // Only instantiate within a valid HandleScope.
struct RandomBytesRequest { class RandomBytesRequest : public AsyncWrap {
~RandomBytesRequest(); public:
Environment* env_; RandomBytesRequest(Environment* env, Local<Object> object, size_t size)
Persistent<Object> obj_; : AsyncWrap(env, object),
unsigned long error_; // openssl error code or zero error_(0),
size_(size),
data_(static_cast<char*>(malloc(size))) {
if (data() == NULL)
FatalError("node::RandomBytesRequest()", "Out of Memory");
}
~RandomBytesRequest() {
persistent().Dispose();
}
uv_work_t* work_req() {
return &work_req_;
}
inline size_t size() const {
return size_;
}
inline char* data() const {
return data_;
}
inline void release() {
free(data_);
size_ = 0;
}
inline void return_memory(char** d, size_t* len) {
*d = data_;
data_ = NULL;
*len = size_;
size_ = 0;
}
inline unsigned long error() const {
return error_;
}
inline void set_error(unsigned long err) {
error_ = err;
}
// TODO(trevnorris): Make private and make work with container_of macro.
uv_work_t work_req_; uv_work_t work_req_;
private:
unsigned long error_;
size_t size_; size_t size_;
char* data_; char* data_;
}; };
RandomBytesRequest::~RandomBytesRequest() {
obj_.Dispose();
}
template <bool pseudoRandom> template <bool pseudoRandom>
void RandomBytesWork(uv_work_t* work_req) { void RandomBytesWork(uv_work_t* work_req) {
RandomBytesRequest* req = container_of(work_req, RandomBytesRequest* req = container_of(work_req,
@ -3475,38 +3516,40 @@ void RandomBytesWork(uv_work_t* work_req) {
int r; int r;
if (pseudoRandom == true) { if (pseudoRandom == true) {
r = RAND_pseudo_bytes(reinterpret_cast<unsigned char*>(req->data_), r = RAND_pseudo_bytes(reinterpret_cast<unsigned char*>(req->data()),
req->size_); req->size());
} else { } else {
r = RAND_bytes(reinterpret_cast<unsigned char*>(req->data_), req->size_); r = RAND_bytes(reinterpret_cast<unsigned char*>(req->data()), req->size());
} }
// RAND_bytes() returns 0 on error. RAND_pseudo_bytes() returns 0 when the // RAND_bytes() returns 0 on error. RAND_pseudo_bytes() returns 0 when the
// result is not cryptographically strong - but that's not an error. // result is not cryptographically strong - but that's not an error.
if (r == 0 && pseudoRandom == false) { if (r == 0 && pseudoRandom == false) {
req->error_ = ERR_get_error(); req->set_error(ERR_get_error());
} else if (r == -1) { } else if (r == -1) {
req->error_ = static_cast<unsigned long>(-1); req->set_error(static_cast<unsigned long>(-1));
} }
} }
// don't call this function without a valid HandleScope // don't call this function without a valid HandleScope
void RandomBytesCheck(RandomBytesRequest* req, Local<Value> argv[2]) { void RandomBytesCheck(RandomBytesRequest* req, Local<Value> argv[2]) {
if (req->error_) { if (req->error()) {
char errmsg[256] = "Operation not supported"; char errmsg[256] = "Operation not supported";
if (req->error_ != static_cast<unsigned long>(-1)) if (req->error() != static_cast<unsigned long>(-1))
ERR_error_string_n(req->error_, errmsg, sizeof errmsg); ERR_error_string_n(req->error(), errmsg, sizeof errmsg);
argv[0] = Exception::Error(OneByteString(node_isolate, errmsg)); argv[0] = Exception::Error(OneByteString(node_isolate, errmsg));
argv[1] = Null(node_isolate); argv[1] = Null(node_isolate);
req->release();
} else { } else {
char* data = NULL;
size_t size;
req->return_memory(&data, &size);
argv[0] = Null(node_isolate); argv[0] = Null(node_isolate);
argv[1] = Buffer::Use(req->data_, req->size_); argv[1] = Buffer::Use(data, size);
req->data_ = NULL;
} }
free(req->data_);
} }
@ -3515,13 +3558,12 @@ void RandomBytesAfter(uv_work_t* work_req, int status) {
RandomBytesRequest* req = container_of(work_req, RandomBytesRequest* req = container_of(work_req,
RandomBytesRequest, RandomBytesRequest,
work_req_); work_req_);
Environment* env = req->env_; Environment* env = req->env();
Context::Scope context_scope(env->context()); Context::Scope context_scope(env->context());
HandleScope handle_scope(env->isolate()); HandleScope handle_scope(env->isolate());
Local<Value> argv[2]; Local<Value> argv[2];
RandomBytesCheck(req, argv); RandomBytesCheck(req, argv);
Local<Object> obj = PersistentToLocal(node_isolate, req->obj_); req->MakeCallback(env->ondone_string(), ARRAY_SIZE(argv), argv);
MakeCallback(env, obj, env->ondone_string(), ARRAY_SIZE(argv), argv);
delete req; delete req;
} }
@ -3542,34 +3584,24 @@ void RandomBytes(const FunctionCallbackInfo<Value>& args) {
return ThrowTypeError("size > Buffer::kMaxLength"); return ThrowTypeError("size > Buffer::kMaxLength");
} }
RandomBytesRequest* req = new RandomBytesRequest(); Local<Object> obj = Object::New();
req->env_ = env; RandomBytesRequest* req = new RandomBytesRequest(env, obj, size);
req->error_ = 0;
req->size_ = size;
req->data_ = static_cast<char*>(malloc(size));
if (req->data_ == NULL) {
delete req;
V8::LowMemoryNotification();
return ThrowError("Out of memory");
}
if (args[1]->IsFunction()) { if (args[1]->IsFunction()) {
Local<Object> obj = Object::New();
obj->Set(FIXED_ONE_BYTE_STRING(node_isolate, "ondone"), args[1]); obj->Set(FIXED_ONE_BYTE_STRING(node_isolate, "ondone"), args[1]);
// XXX(trevnorris): This will need to go with the rest of domains.
if (env->in_domain()) { if (env->in_domain()) {
obj->Set(env->domain_string(), env->domain_array()->Get(0)); obj->Set(env->domain_string(), env->domain_array()->Get(0));
} }
req->obj_.Reset(node_isolate, obj);
uv_queue_work(env->event_loop(), uv_queue_work(env->event_loop(),
&req->work_req_, req->work_req(),
RandomBytesWork<pseudoRandom>, RandomBytesWork<pseudoRandom>,
RandomBytesAfter); RandomBytesAfter);
args.GetReturnValue().Set(obj); args.GetReturnValue().Set(obj);
} else { } else {
Local<Value> argv[2]; Local<Value> argv[2];
RandomBytesWork<pseudoRandom>(&req->work_req_); RandomBytesWork<pseudoRandom>(req->work_req());
RandomBytesCheck(req, argv); RandomBytesCheck(req, argv);
delete req; delete req;

3
test/simple/test-crypto.js

@ -1005,3 +1005,6 @@ assert.throws(function() {
assert.throws(function() { assert.throws(function() {
crypto.createVerify('RSA-SHA1').update('0', 'hex'); crypto.createVerify('RSA-SHA1').update('0', 'hex');
}, /Bad input string/); }, /Bad input string/);
// Make sure memory isn't released before being returned
console.log(crypto.randomBytes(16));

Loading…
Cancel
Save