React Native 同步安全随机数生成
Posted
技术标签:
【中文标题】React Native 同步安全随机数生成【英文标题】:React Native Synchronous Secure Random Number Generation 【发布时间】:2016-04-16 09:20:19 【问题描述】:我正在尝试在 React Native 项目中生成密钥对。密钥对生成工具依赖于crypto
模块的随机字节生成,它生成具有随机字节值的指定长度的缓冲区。
为了在 React Native 中使用 crypto
模块,它必须经过浏览器化,浏览器化的随机数生成器如下所示:
https://github.com/crypto-browserify/randombytes/blob/master/browser.js
这是关键组件:
var crypto = global.crypto || global.msCrypto
if (crypto && crypto.getRandomValues)
module.exports = randomBytes
else
module.exports = oldBrowser
确实,在使用 Chrome 调试应用程序时,一切正常,但在 ios 的 javascriptCore 引擎上运行时,会调用 oldBrowser
方法,引发以下错误:
此浏览器不支持安全随机数生成 使用 chrome、FireFox 或 Internet Explorer 11
因此,我试图找到随机字节生成的替代品。我找到的一个模块是这个:
https://www.npmjs.com/package/react-native-randombytes
它使用设备的原生库生成一个随机数,并通过它们的 Obj-C/JS 接口将其公开给 React Native。需要注意的是,这种方法只适用于iOS,库的作者还没有android的解决方案,但这是另一个问题。
此方法有效,因为它可以生成随机字节,但它有一个主要缺点。 React 仅支持 Objective-C 和 JavaScript 之间的异步接口,这意味着该方法异步返回其结果。最初的 randomBytes
方法是同步的,几乎所有依赖它的 SDK 都同步使用它。因此,如果我们要使用异步版本,则必须为它重写所有 SDK,包括所有依赖于过去同步的方法的依赖项,现在不再同步。
因此,我试图找到一种方法使异步本机随机数生成器同步工作。有几个 node 包可以做到这一点,其中最突出的是deasync
,但deasync
依赖于一些无法浏览器化的核心 Node 模块,因此同步版本不起作用。
或者,我尝试将它包装在一个方法中,该方法将设置一个信号量,调用异步生成器,并在一个 while 循环中等待信号量的值发生变化。该尝试失败,因为 while 循环阻止了回调的执行。这是我尝试的近似值,其中对 async 方法的调用已替换为 setTimeout
,并且要返回的随机数是 4,由公平掷骰决定。
function testSynchronicity()
var isDone = false;
setTimeout(function()
isDone = true;
, 1000); // set isDone to true after a second
while (!isDone)
// do nothing
return 4;
;
由于这不起作用,我想我会尝试一个完全不同的随机数生成器,没有依赖本机代码的 react-native-randombytes
模块,并为 JavaScript 使用这个:
https://github.com/skeeto/rng-js
它在 Node 本身中运行良好,但是在浏览它并尝试在 React Native 中运行第一个示例之后,它抛出了一个错误,指出主对象不是构造函数。示例如下所示:
var RNG = require('./rng_react'); // rng_react is rng-js browserified
var rng = new RNG();
var randomValue = rng.random(0, 255, false);
所以在这一点上,我有点不知所措,希望能提供任何帮助。谢谢!
编辑:如果一切都失败了,那就是这个,但我认为它几乎会超出问题的目的。 https://github.com/bitpay/bitcore-lib/blob/master/lib/crypto/random.js#L37
【问题讨论】:
【参考方案1】:我找到了一个通常有效的答案。但是,它并不完美,因为它仅在应用启动期间不需要 randomBytes
方法时才有效。
我的解决方案确实涉及使用react-native-randombytes
库。它依赖 iOS 内置的 CSPRNG 生成随机缓冲区,然后异步返回。为了支持同步响应,我扩展了模块的 randomBytes
以在没有提供回调方法时不抛出错误,而是使用斯坦福的 JavaScript 加密库生成随机“单词”,因为它们被称为,将它们转换为一个缓冲区,然后相应地对其进行修剪:
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'));
);
;
关键是,为了让 SJCL 库有足够的熵,它需要被正确地播种。因此,在启动时,我们使用异步 CSPRNG 功能播种 SJCL 随机数生成器:
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');
);
因此,我们在 React Native 中有一个同步的randomBytes
方法,前提是我们有机会在需要它的同步功能之前至少异步调用它一次。
【讨论】:
您重新定义randomBytes
的事实并不意味着使用它的决定 (crypto && crypto.getRandomValues
) 会改变。那么你如何解决这个问题呢?为了通过该测试,我不愿意简单地定义crypto.getRandomValues
,因为我不知道它还会影响什么。这就是我正在努力解决的问题:github.com/EOSIO/eosjs-keygen/issues/24【参考方案2】:
您的解决方案确实回答了这个问题,但似乎有点复杂。特别是,为什么不只使用 SJCL?
就我而言,我最终使用了react-native-securerandom,它只是对 Android 和 iOS 原生调用的一个薄包装。然后我这样做是为了初始化 SJCL 的 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');
【讨论】:
我相信 react-native-securerandom 在 2016 年 1 月还没有出现。它的第一次提交是 2017 年 10 月 :)以上是关于React Native 同步安全随机数生成的主要内容,如果未能解决你的问题,请参考以下文章