From b088f005daa10ef19a6535eb4514f7bd86cdc7c2 Mon Sep 17 00:00:00 2001 From: Bart Butler Date: Mon, 5 Mar 2018 12:18:04 -0800 Subject: [PATCH] simplify random.js --- src/crypto/pkcs1.js | 2 +- src/crypto/random.js | 48 ++-------------------- src/packet/sym_encrypted_aead_protected.js | 2 +- src/worker/async_proxy.js | 19 +-------- src/worker/worker.js | 32 +++++++++++++-- test/crypto/crypto.js | 2 +- 6 files changed, 38 insertions(+), 67 deletions(-) diff --git a/src/crypto/pkcs1.js b/src/crypto/pkcs1.js index 38dc0671..71719651 100644 --- a/src/crypto/pkcs1.js +++ b/src/crypto/pkcs1.js @@ -54,7 +54,7 @@ function getPkcs1Padding(length) { let result = ''; let randomByte; while (result.length < length) { - randomByte = random.getSecureRandomOctet(); + randomByte = random.getRandomBytes(1)[0]; if (randomByte !== 0) { result += String.fromCharCode(randomByte); } diff --git a/src/crypto/random.js b/src/crypto/random.js index 320c70dd..e00af509 100644 --- a/src/crypto/random.js +++ b/src/crypto/random.js @@ -38,49 +38,7 @@ export default { * @return {Uint8Array} Random byte array */ getRandomBytes: function(length) { - const result = new Uint8Array(length); - for (let i = 0; i < length; i++) { - result[i] = this.getSecureRandomOctet(); - } - return result; - }, - - /** - * Return a secure random number in the specified range - * @param {Integer} from Min of the random number - * @param {Integer} to Max of the random number (max 32bit) - * @return {Integer} A secure random number - */ - getSecureRandom: function(from, to) { - let randUint = this.getSecureRandomUint(); - const bits = ((to - from)).toString(2).length; - while ((randUint & ((2 ** bits) - 1)) > (to - from)) { - randUint = this.getSecureRandomUint(); - } - return from + (Math.abs(randUint & ((2 ** bits) - 1))); - }, - - getSecureRandomOctet: function() { - const buf = new Uint8Array(1); - this.getRandomValues(buf); - return buf[0]; - }, - - getSecureRandomUint: function() { - const buf = new Uint8Array(4); - const dv = new DataView(buf.buffer); - this.getRandomValues(buf); - return dv.getUint32(0); - }, - - /** - * Helper routine which calls platform specific crypto random generator - * @param {Uint8Array} buf - */ - getRandomValues: function(buf) { - if (!(buf instanceof Uint8Array)) { - throw new Error('Invalid type: buf not an Uint8Array'); - } + const buf = new Uint8Array(length); if (typeof window !== 'undefined' && window.crypto && window.crypto.getRandomValues) { window.crypto.getRandomValues(buf); } else if (typeof window !== 'undefined' && typeof window.msCrypto === 'object' && typeof window.msCrypto.getRandomValues === 'function') { @@ -126,15 +84,17 @@ export default { function RandomBuffer() { this.buffer = null; this.size = null; + this.callback = null; } /** * Initialize buffer * @param {Integer} size size of buffer */ -RandomBuffer.prototype.init = function(size) { +RandomBuffer.prototype.init = function(size, callback) { this.buffer = new Uint8Array(size); this.size = 0; + this.callback = callback; }; /** diff --git a/src/packet/sym_encrypted_aead_protected.js b/src/packet/sym_encrypted_aead_protected.js index 631ea56a..0557883c 100644 --- a/src/packet/sym_encrypted_aead_protected.js +++ b/src/packet/sym_encrypted_aead_protected.js @@ -79,7 +79,7 @@ SymEncryptedAEADProtected.prototype.decrypt = function (sessionKeyAlgorithm, key * @return {Promise} Nothing is returned */ SymEncryptedAEADProtected.prototype.encrypt = function (sessionKeyAlgorithm, key) { - this.iv = crypto.random.getRandomValues(new Uint8Array(IV_LEN)); // generate new random IV + this.iv = crypto.random.getRandomBytes(IV_LEN); // generate new random IV return crypto.gcm.encrypt(sessionKeyAlgorithm, this.packets.write(), key, this.iv).then(encrypted => { this.encrypted = encrypted; }); diff --git a/src/worker/async_proxy.js b/src/worker/async_proxy.js index 4e68e25c..3e42cd42 100644 --- a/src/worker/async_proxy.js +++ b/src/worker/async_proxy.js @@ -20,7 +20,6 @@ import crypto from '../crypto'; import packet from '../packet'; const INITIAL_RANDOM_SEED = 50000; // random bytes seeded to worker -const RANDOM_SEED_REQUEST = 20000; // random bytes seeded after worker request /** * Initializes a new proxy and loads the web worker @@ -75,7 +74,7 @@ AsyncProxy.prototype.onMessage = function(event) { delete this.tasks[msg.id]; break; case 'request-seed': - this.seedRandom(RANDOM_SEED_REQUEST); + this.seedRandom(msg.amount); break; default: throw new Error('Unknown Worker Event.'); @@ -87,24 +86,10 @@ AsyncProxy.prototype.onMessage = function(event) { * @param {Integer} size Number of bytes to send */ AsyncProxy.prototype.seedRandom = function(size) { - const buf = this.getRandomBuffer(size); + const buf = crypto.random.getRandomBytes(size); this.worker.postMessage({ event:'seed-random', buf }, util.getTransferables(buf)); }; -/** - * Get Uint8Array with random numbers - * @param {Integer} size Length of buffer - * @return {Uint8Array} - */ -AsyncProxy.prototype.getRandomBuffer = function(size) { - if (!size) { - return null; - } - const buf = new Uint8Array(size); - crypto.random.getRandomValues(buf); - return buf; -}; - /** * Terminates the worker */ diff --git a/src/worker/worker.js b/src/worker/worker.js index b0354aac..7b934376 100644 --- a/src/worker/worker.js +++ b/src/worker/worker.js @@ -24,10 +24,29 @@ self.window = {}; // to make UMD bundles work importScripts('openpgp.js'); var openpgp = window.openpgp; +var randomQueue = []; +var randomRequested = false; var MIN_SIZE_RANDOM_BUFFER = 40000; var MAX_SIZE_RANDOM_BUFFER = 60000; +var MIN_SIZE_RANDOM_REQUEST = 20000; -openpgp.crypto.random.randomBuffer.init(MAX_SIZE_RANDOM_BUFFER); +/** + * Handle random buffer exhaustion by requesting more random bytes from the main window + * @return {Promise} Empty promise whose resolution indicates that the buffer has been refilled + */ +function randomCallback() { + + if (!randomRequested) { + self.postMessage({ event: 'request-seed', amount: MAX_SIZE_RANDOM_BUFFER }); + } + randomRequested = true; + + return new Promise(function(resolve, reject) { + randomQueue.push(resolve); + }); +} + +openpgp.crypto.random.randomBuffer.init(MAX_SIZE_RANDOM_BUFFER, randomCallback); /** * Handle messages from the main window. @@ -43,6 +62,13 @@ self.onmessage = function(event) { case 'seed-random': seedRandom(msg.buf); + + var queueCopy = randomQueue; + randomQueue = []; + for (var i = 0; i < queueCopy.length; i++) { + queueCopy[i](); + } + break; default: @@ -99,8 +125,8 @@ function delegate(id, method, options) { * @param {Object} event Contains event type and data */ function response(event) { - if (openpgp.crypto.random.randomBuffer.size < MIN_SIZE_RANDOM_BUFFER) { - self.postMessage({ event: 'request-seed' }); + if (!randomRequested && openpgp.crypto.random.randomBuffer.size < MIN_SIZE_RANDOM_BUFFER) { + self.postMessage({ event: 'request-seed', amount: MIN_SIZE_RANDOM_REQUEST }); } self.postMessage(event, openpgp.util.getTransferables(event.data)); } diff --git a/test/crypto/crypto.js b/test/crypto/crypto.js index 0a59504d..fde07520 100644 --- a/test/crypto/crypto.js +++ b/test/crypto/crypto.js @@ -309,7 +309,7 @@ describe('API functional testing', function() { if(algo.substr(0,3) === 'aes') { it(algo, function() { const key = crypto.generateSessionKey(algo); - const iv = crypto.random.getRandomValues(new Uint8Array(crypto.gcm.ivLength)); + const iv = crypto.random.getRandomBytes(crypto.gcm.ivLength); return crypto.gcm.encrypt( algo, util.str_to_Uint8Array(plaintext), key, iv