Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

new approach to randomness

  • Loading branch information...
commit eee08ac2b8aae44cd9cd37bb885992bd28899e9c 1 parent 40064d1
@benadida benadida authored
View
10 lib/jwcrypto.js
@@ -9,9 +9,13 @@
var algs = require("./algs/index"),
utils = require("./utils"),
delay = utils.delay,
+ rng = require("./rng"),
libs = require("../libs/minimal");
-var RNG = new libs.SecureRandom();
+var RNG = new rng.RNG();
+
+// start autoseeding now
+RNG.autoseed();
function NoSuchAlgorithmException(message) {
this.message = message;
@@ -137,9 +141,9 @@ exports.decrypt = function(encryptedPayload, encryptionAndMACKeys, cb) {
};
-// rng
+// entropy here is a string that is expected to be relatively high entropy
exports.addEntropy = function(entropy) {
- // do something! FIXME XXX
+ RNG.addEntropy(entropy);
};
exports.assertion = require("./assertion");
View
80 lib/rng.js
@@ -0,0 +1,80 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * abstract out RNG depending on client or server.
+ *
+ * auto-seeding has to be requested.
+ * (the seed is automatic, not the decision to auto-seed.)
+ *
+ * nextBytes takes a byteArray as input and populates it,
+ * because that's how the cool kids do it and so we will not bikeshed.
+ */
+
+var utils = require("./utils"),
+ delay = utils.delay,
+ libs = require("../libs/minimal"),
+ sjcl = libs.sjcl;
+
+// detect if we have native crypto support
+var crypto = null;
+try {
+ crypto = require("crypto");
+} catch(e) {}
+
+// proper boolean for whether we have native support
+var IS_NATIVE = !!crypto;
+
+function NativeRNG() {
+}
+
+NativeRNG.prototype = {
+ addEntropy: function(seed_in) {
+ // do nothing, natively we don't care
+ },
+ autoseed: function(cb) {
+ // yay, don't need to do anything
+ if (cb)
+ delay(cb)();
+ },
+ nextBytes: function(byteArray) {
+ var randomBytes = crypto.randomBytes(byteArray.length);
+ for (var i=0; i<byteArray.length; i++)
+ byteArray[i] = randomBytes[i];
+ }
+};
+
+function BrowserRNG() {
+}
+
+BrowserRNG.prototype = {
+ // WARNING: assumes that there's enough entropy in here to say it's 256
+ addEntropy: function(seed_in) {
+ sjcl.random.addEntropy(seed_in, 256);
+ },
+ autoseed: function(cb) {
+ // see if we have window.crypto.getRandomValues
+ if (window.crypto.getRandomValues) {

mightn't this throw if window.crypto is undefined? that may be the case on some browsers.

cha-ching indeed. Fixing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ // then sjcl already seeded itself
+ if (cb)
+ delay(cb)();
+ } else {
+ sjcl.random.addEventListener('seeded', function(blarg) {
+ // no passing of arguments to the callback
+ if (cb)
+ cb();
+ });
+
+ // tell sjcl to start collecting some randomness
+ sjcl.random.startCollectors();
+ }
+ },
+ nextBytes: function(byteArray) {
+ var randomBytes = sjcl.random.randomWords(byteArray.length);
+ for (var i=0; i<byteArray.length; i++)
+ byteArray[i] = randomBytes[i];
+ }
+};
+
+exports.RNG = IS_NATIVE ? NativeRNG : BrowserRNG;

no newline makes hulk sad

i didn't know hulk was so sensitive.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
View
1  libs/exports.js
@@ -6,7 +6,6 @@
exports.RSAKey = RSAKey;
exports.BigInteger = BigInteger;
-exports.SecureRandom = SecureRandom;
exports.sjcl = sjcl;
exports.hex2b64 = hex2b64;
exports.b64tohex = b64tohex;
View
1  libs/exports_minimal.js
@@ -5,7 +5,6 @@
//
exports.BigInteger = BigInteger;
-exports.SecureRandom = SecureRandom;
exports.sjcl = sjcl;
exports.hex2b64 = hex2b64;
exports.b64tohex = b64tohex;
View
2  libs/minimal_package.txt
@@ -6,6 +6,4 @@ external/base64.js
external/jsbn-optimized.js
external/jsbn2-optimized.js
jsbn-patch.js
-external/prng4.js
-external/rng.js
exports_minimal.js
View
14 test.html
@@ -9,9 +9,20 @@
function runkeygen() {
var jwcrypto = require("./lib/jwcrypto");
var start = new Date();
+ jwcrypto.addEntropy("12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
jwcrypto.generateKeypair({algorithm: "DS", keysize: 256}, function(err, kp) {
var end = new Date();
- alert(end-start);
+ alert(end-start + "ms");
+ });
+ }
+
+ function runrandom() {
+ var rng = require("./lib/rng");
+ var the_rng = new rng.RNG();
+ the_rng.autoseed(function() {
+ var bytes = new Array(20);
+ the_rng.nextBytes(bytes);
+ alert(bytes);
});
}
@@ -19,6 +30,7 @@
</head>
<body>
<button onclick="runkeygen();">keygen-DS256</button>
+<button onclick="runrandom();">run-random</button>
<!--
<button onclick="runtests();">run tests</button>
<button onclick="runperftests();">run perf tests</button>
View
12 test/jwcrypto-test.js
@@ -15,6 +15,18 @@ var suite = vows.describe('API tests');
// disable vows (often flakey?) async error behavior
suite.options.error = false;
+suite.addBatch({
+ "adding entropy": {
+ topic: function() {
+ jwcrypto.addEntropy("foobarbaz");
+ return null;
+ },
+ "works": function() {
+ assert.ok(true);
+ }
+ }
+});
+
testUtils.addBatches(suite, function(alg, keysize) {
var keypair;
var obj = {foo: "bar"};
View
59 test/rng-test.js
@@ -0,0 +1,59 @@
+#!/usr/bin/env node
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const
+vows = require('vows'),
+assert = require('assert'),
+rng = require('../lib/rng');
+
+var suite = vows.describe('RNG tests');
+
+suite.addBatch({
+ "create rng": {
+ topic: function() {
+ return new rng.RNG();
+ },
+ "looks good": function(rng) {
+ assert.isObject(rng);
+ assert.isFunction(rng.addEntropy);
+ assert.isFunction(rng.autoseed);
+ assert.isFunction(rng.nextBytes);
+ },
+ "and when we seed": {
+ topic: function(rng) {
+ rng.addEntropy("foobar");
+ return null;
+ },
+ "all is well": function() {
+ assert.ok(true);
+ }
+ },
+ "and when we autoseed": {
+ topic: function(rng) {
+ rng.autoseed(this.callback);
+ },
+ "eventually returns": function() {
+ assert.ok(true);
+ },
+ "and when we get random bytes": {
+ topic: function(rng) {
+ var bytes = [0,0,0,0,0,0,0,0,0,0];
+ rng.nextBytes(bytes);
+ return bytes;
+ },
+ "contains stuff": function(bytes) {
+ assert.isArray(bytes);
+ },
+ "and that stuff is random'ish": function(bytes) {
+ // this test is unlikely to fail unless no randomness is getting out
+ assert.ok(!(bytes[0] == 0 && bytes[1] == 0 && bytes[2] == 0));
+ }
+ }
+ },
+ }
+});
+// run or export the suite.
+if (process.argv[1] === __filename) suite.run();
+else suite.export(module);

2 comments on commit eee08ac

@michaelrhanson

So, will addEntropy() be called in a production environment? If so how?

@benadida

yes, addEntropy() will be called to mix in some server-side entropy to ensure that clients that have too little entropy can be upgraded.

Please sign in to comment.
Something went wrong with that request. Please try again.