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.
This commit is contained in:
parent
ccec14b568
commit
66d908f09a
@ -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;
|
||||||
|
|
||||||
|
@ -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…
x
Reference in New Issue
Block a user