Skip to content

Commit

Permalink
Expose RAND_poll / RAND_add to user code via crypto module.
Browse files Browse the repository at this point in the history
The purpose of this patch is to allow Node applications to add
additional entropy to OpenSSL's pool. This is useful in environments
where a running Node process can be cloned (e.g. VM snapshotting or
live migration), resulting in a chance of the cloned process sharing
an entropy pool with the original process.

The new AddEntropy function works as follows:

- If no parameters are passed, it calls RAND_poll. Performance was
  evaluated at about 140k ops/sec, but this will vary by OS and
  hardware.

- If one parameter is passed, it is expected to be a buffer, which
  is passed to RAND_add. Performance was evaluated at about
  1.8m ops/sec.

The AddEntropy function is bound to crypto.addEntropy().

Usage:
  var crypto = require('crypto');

  // Add entropy from system-supplied source
  crypto.addEntropy();

  // Add entropy from a user-supplied source
  crypto.addEntropy(new Uint8Array([38, 4, 19, 22]));
  • Loading branch information
scovetta authored and Michael Scovetta committed Apr 2, 2016
1 parent 56efb3d commit dc63a41
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 0 deletions.
3 changes: 3 additions & 0 deletions lib/crypto.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ try {
var getCiphers = binding.getCiphers;
var getHashes = binding.getHashes;
var getCurves = binding.getCurves;
var addEntropy = binding.addEntropy;
} catch (e) {
throw new Error('Node.js is not compiled with openssl crypto support');
}
Expand Down Expand Up @@ -621,6 +622,8 @@ exports.randomBytes = exports.pseudoRandomBytes = randomBytes;

exports.rng = exports.prng = randomBytes;

exports.addEntropy = addEntropy;

exports.getCiphers = function() {
return filterDuplicates(getCiphers());
};
Expand Down
20 changes: 20 additions & 0 deletions src/node_crypto.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5354,6 +5354,25 @@ void GetCurves(const FunctionCallbackInfo<Value>& args) {
}


void AddEntropy(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);

if (args.Length() == 0) {
// Delegate entropy generation to OpenSSL, which will add
// entropy from system sources
RAND_poll();
return;
}
// Make sure we got a buffer from the user and use it to
// seed OpenSSL.
THROW_AND_RETURN_IF_NOT_BUFFER(args[0]);
Local<Object> bufObj = args[0]->ToObject();
const void* buf = Buffer::Data(bufObj);
size_t bufLength = Buffer::Length(bufObj);
RAND_seed(buf, bufLength);
}


void Certificate::Initialize(Environment* env, Local<Object> target) {
HandleScope scope(env->isolate());

Expand Down Expand Up @@ -5648,6 +5667,7 @@ void InitCrypto(Local<Object> target,
env->SetMethod(target, "getCiphers", GetCiphers);
env->SetMethod(target, "getHashes", GetHashes);
env->SetMethod(target, "getCurves", GetCurves);
env->SetMethod(target, "addEntropy", AddEntropy);
env->SetMethod(target, "publicEncrypt",
PublicKeyCipher::Cipher<PublicKeyCipher::kPublic,
EVP_PKEY_encrypt_init,
Expand Down
11 changes: 11 additions & 0 deletions test/parallel/test-crypto-random.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,17 @@ process.setMaxListeners(256);
});
});

// crypto.addEntropy takes nothing or an ArrayBuffer
[-1,
undefined,
null,
false,
true,
{}, [], [1]
].forEach(function(value) {
assert.throws(function() { crypto.addEntropy(value); });
});

// assert that the callback is indeed called
function checkCall(cb, desc) {
var called_ = false;
Expand Down

0 comments on commit dc63a41

Please sign in to comment.