Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

WIP

  • Loading branch information...
commit 86482dbdab91f775cbfafc5d22c72c617a350ce6 1 parent db99395
@bnoordhuis authored
View
20 lib/crypto.js
@@ -160,3 +160,23 @@ exports.createDiffieHellman = function(size_or_key, enc) {
}
}
+
+function wrap(fun) {
+ if (crypto) {
+ return function(size, callback) {
+ size = +size;
+ if (size <= 0 || isNaN(size))
+ throw new TypeError('size argument must be a number > 0');
+ if (callback && typeof callback !== 'function')
+ throw new TypeError('callback argument must be a function');
+ return fun(size, callback);
+ };
+ }
+ else {
+ return function() {
+ throw new Error('node.js not compiled with openssl crypto support.');
+ };
+ }
+}
+exports.randomBytes = wrap(binding.randomBytes);
+exports.pseudoRandomBytes = wrap(binding.pseudoRandomBytes);
View
134 src/node_crypto.cc
@@ -3638,6 +3638,132 @@ class DiffieHellman : public ObjectWrap {
};
+typedef int (*random_fun_t)(unsigned char*, int);
+
+template <random_fun_t RAND>
+class RandomBytes {
+public:
+ static Handle<Value> Call(const Arguments& args) {
+ HandleScope scope;
+
+ const int size = args[0]->Int32Value();
+ assert(size > 0);
+
+ if (args[1]->IsFunction()) {
+ RandomBytes* self = new RandomBytes(size, args[1]);
+ eio_custom(RandomBytes::Execute,
+ EIO_PRI_DEFAULT,
+ RandomBytes::Finish,
+ reinterpret_cast<void*>(self));
+ return Undefined();
+ }
+ else {
+ char* bytes = new char[size];
+
+ const int status = RAND_bytes(reinterpret_cast<unsigned char*>(bytes),
+ size);
+ if (status == 1)
+ return CreateBuffer(bytes, size);
+
+ delete bytes;
+ return ThrowException(ExceptionForStatus(status));
+ }
+ }
+
+private:
+ RandomBytes(int size, Handle<Value> callback) {
+ assert(size >= 0);
+ assert(!callback.IsEmpty());
+ assert(callback->IsFunction());
+ callback_ = Persistent<Function>::New(callback.As<Function>());
+ status_ = -1;
+ bytes_ = new char[size];
+ size_ = size;
+ }
+
+ ~RandomBytes() {
+ callback_.Dispose();
+ callback_.Clear();
+ }
+
+ static int Execute(eio_req* req) {
+ reinterpret_cast<RandomBytes*>(req->data)->DoExecute();
+ return 0;
+ }
+
+ static int Finish(eio_req* req) {
+ reinterpret_cast<RandomBytes*>(req->data)->DoFinish();
+ return 0;
+ }
+
+ void DoExecute() {
+ // don't call ERR_get_error() if status != 1, not thread-safe
+ status_ = RAND_bytes(reinterpret_cast<unsigned char*>(bytes_), size_);
+ if (status_ != 1) {
+ delete bytes_;
+ bytes_ = NULL;
+ size_ = 0;
+ }
+ }
+
+ void DoFinish() {
+ HandleScope scope;
+
+ Handle<Value> argv[2] = { Undefined(), Undefined() };
+ if (status_ == 1) {
+ argv[1] = CreateBuffer(bytes_, size_);
+ }
+ else {
+ argv[0] = ExceptionForStatus(status_);
+ }
+
+ TryCatch tc;
+ callback_->Call(Context::GetCurrent()->Global(), 2, argv);
+
+ if (tc.HasCaught()) {
+ FatalException(tc);
+ }
+
+ delete this;
+ }
+
+ // int <-> void* cast helper, gcc optimizes the memset away
+ union Cast {
+ Cast(void* arg) { memset(this, 0, sizeof *this); arg_ = arg; }
+ Cast(int size) { memset(this, 0, sizeof *this); size_ = size; }
+ void* arg_;
+ int size_;
+ };
+
+ static void Release(char* bytes, void* arg) {
+ const int size = Cast(arg).size_;
+ V8::AdjustAmountOfExternalAllocatedMemory(-(size + sizeof(Buffer)));
+ delete bytes;
+ }
+
+ static Handle<Value> CreateBuffer(char* bytes, int size) {
+ void* arg = Cast(size).arg_;
+ V8::AdjustAmountOfExternalAllocatedMemory(size + sizeof(Buffer));
+ return Buffer::New(bytes, size, Release, arg)->handle_;
+ }
+
+ static Handle<Value> ExceptionForStatus(int status) {
+ assert(status == 0 || status == -1);
+ if (status == 0)
+ return Exception::Error(String::New("No random bytes collected."));
+ else if (status == -1)
+ return Exception::Error(String::New("Not supported."));
+ else
+ return Exception::Error(String::New("No error?"));
+ }
+
+ Persistent<Function> callback_;
+ char* bytes_;
+ int status_;
+ int size_;
+};
+
+
void InitCrypto(Handle<Object> target) {
HandleScope scope;
@@ -3670,6 +3796,14 @@ void InitCrypto(Handle<Object> target) {
Sign::Initialize(target);
Verify::Initialize(target);
+ target->Set(String::NewSymbol("randomBytes"),
+ FunctionTemplate::New(
+ RandomBytes<RAND_bytes>::Call)->GetFunction());
+
+ target->Set(String::NewSymbol("pseudoRandomBytes"),
+ FunctionTemplate::New(
+ RandomBytes<RAND_pseudo_bytes>::Call)->GetFunction());
+
subject_symbol = NODE_PSYMBOL("subject");
issuer_symbol = NODE_PSYMBOL("issuer");
valid_from_symbol = NODE_PSYMBOL("valid_from");
View
1  src/node_crypto.h
@@ -33,6 +33,7 @@
#include <openssl/pem.h>
#include <openssl/x509.h>
#include <openssl/hmac.h>
+#include <openssl/rand.h>
#ifdef OPENSSL_NPN_NEGOTIATED
#include <node_buffer.h>
View
18 test/simple/test-crypto.js
@@ -369,3 +369,21 @@ assert.equal(rsaSignature, '5c50e3145c4e2497aadb0eabc83b342d0b0021ece0d4c4a064b7
rsaVerify.update(rsaPubPem);
assert.equal(rsaVerify.verify(rsaPubPem, rsaSignature, 'hex'), 1);
+
+// Test random and pseudo-random byte sequence generator
+[
+ 0,
+ 0.0,
+ -1,
+ -1.0,
+ undefined,
+ null,
+ false,
+ true,
+ '',
+ {},
+ []
+].forEach(function(value) {
+ assert.throws(function() { crypto.randomBytes(value); }, TypeError);
+ assert.throws(function() { crypto.pseudoRandomBytes(value); }, TypeError);
+});
Please sign in to comment.
Something went wrong with that request. Please try again.