Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React Native Synchronous Secure Random Number Generation

I'm trying to generate keypairs in a React Native project. The key pair generation tool relies on the crypto module's random byte generation, which produces a buffer of a specified lengths with random byte values.

In order to use the crypto module within React Native, it has to be browserified, and the browserified random number generator looks like this:

https://github.com/crypto-browserify/randombytes/blob/master/browser.js

Here's the key component:

var crypto = global.crypto || global.msCrypto

if (crypto && crypto.getRandomValues) {
  module.exports = randomBytes
} else {
  module.exports = oldBrowser
}

Indeed, when debugging the application with Chrome, everything works fine, but when running it on iOS's JavaScriptCore engine, the oldBrowser method gets called instead, throwing the following error:

secure random number generation not supported by this browser use chrome, FireFox or Internet Explorer 11

Thus I'm trying to find a replacement for the random bytes generation. One module I found is this one:

https://www.npmjs.com/package/react-native-randombytes

It uses the device's native libraries to generate a random number, and exposes it to React Native through their Obj-C/JS interface. It should be noted that this method only works on iOS, and the library's author doesn't have an Android solution yet, but that's an issue for another time.

This method works, in that it can generate random bytes, but it has one major drawback. React only supports asynchronous interfacing between Objective-C and JavaScript, which means that this method returns its results asynchronously. The original randomBytes method is synchronous, and pretty much every SDK out there that relies on it uses it synchronously. So if we were to go with the async version, all the SDKs would have to be rewritten for it, including all dependencies that rely on methods that used to be synchronous and now would no longer be.

Thus I'm trying to find a way to make the asynchronous native random number generator work synchronously. There are several node packages that do that, the most prominent one of them being deasync, but deasync relies on some core Node modules that cannot be browserified, so the synchronous version doesn't work.

Alternatively, I've tried wrapping it in a method that would set a semaphore, call the async generator, and wait in a while loop for the semaphore's value to change. That attempt failed because the while loop was blocking the callback from ever executing. Here's an approximation of my attempts, where the call to the async method has been replaced with a setTimeout, and the random number to be returned is a four, as determined by a fair dice roll.

function testSynchronicity() {
  var isDone = false;
  setTimeout(function() {
    isDone = true;
  }, 1000); // set isDone to true after a second

  while (!isDone) {
    // do nothing
  }
  return 4;
};

As this wasn't working, I figured I would try a completely different random number generator entirely, without the native-code-relying react-native-randombytes module and went with this one for JavaScript:

https://github.com/skeeto/rng-js

It worked fine within Node itself, but after browserifying it and trying to run the first example within React Native, it threw an error saying that the main object was not a constructor. Here's what the example looks like:

var RNG = require('./rng_react'); // rng_react is rng-js browserified
var rng = new RNG();
var randomValue = rng.random(0, 255, false);

So at this point, I'm at a bit of a loss, and would appreciate any help. Thanks!

EDIT: If all else fails, there's this, but I think it would pretty much beat the purpose of the question. https://github.com/bitpay/bitcore-lib/blob/master/lib/crypto/random.js#L37

like image 254
arik Avatar asked Jan 11 '16 22:01

arik


2 Answers

Your solution does answer the question but seems a bit complex. In particular, why not use only SJCL?

In my case I've ended up using react-native-securerandom, which is just a thin wrapper over Android and iOS native calls. Then I've done this to initialise SJCL's RNG:

const { generateSecureRandom } = require('react-native-securerandom');
const sjcl = require('lib/vendor/sjcl');

const randomBytes = await generateSecureRandom(1024/8);

let temp = [];
for (let n in randomBytes) {
    if (!randomBytes.hasOwnProperty(n)) continue;
    temp.push(randomBytes[n].toString(16));
}

const hexSeed = sjcl.codec.hex.toBits(temp.join(''));
sjcl.random.addEntropy(hexSeed, 1024, 'generateSecureRandom');
like image 51
laurent Avatar answered Oct 20 '22 19:10

laurent


I have found an answer that usually works. However, it is imperfect, because it works only if the randomBytes method is not required during app launch.

My solution does involve using the react-native-randombytes library. It relies on iOS's built-in CSPRNG to generate a random buffer, and then returns it asynchronously. In order to support synchronous responses, I expanded the moduel's randomBytes to not throw an error when no callback method is provided, but rather to use Stanford's JavaScript Crypto Library to generate random "words," as they're called, convert those to a buffer and then trim it accordingly:

var sjcl = require('sjcl');
var sjclRandom = new sjcl.prng(10);

var RNRandomBytes = require('react-native').NativeModules.RNRandomBytes;

module.exports.randomBytes = function(length, cb) {

  if (!cb) {
    var size = length;
    var wordCount = Math.ceil(size * 0.25);
    var randomBytes = sjclRandom.randomWords(wordCount, 10);
    var hexString = sjcl.codec.hex.fromBits(randomBytes);
    hexString = hexString.substr(0, size * 2);

    return new Buffer(hexString, 'hex');
  }

  RNRandomBytes.randomBytes(length, function(err, base64String) {
    if (err) {
      cb(err);
    } else {
      cb(null, new Buffer(base64String, 'base64'));
    }
  });

};

The crux is, in order for the SJCL library to have sufficient entropy, it needs to have been seeded properly. So, on startup, we use the asynchronous CSPRNG functionality to seed the SJCL random number generator:

module.exports.randomBytes(4096, function(err, buffer) {
  var hexString = buffer.toString('hex');
  // we need to convert the hex string to bytes, or else SJCL assumes low entropy
  var stanfordSeed = sjcl.codec.hex.toBits(hexString);
  sjclRandom.addEntropy(stanfordSeed, 10, 'csprng');
});

Thus, we have a synchronous randomBytes method within React Native, provided we have had the opportunity to call it asynchronously at least once before we need its synchronous functionality.

like image 41
arik Avatar answered Oct 20 '22 18:10

arik