simplify random.js

This commit is contained in:
Bart Butler 2018-03-05 12:18:04 -08:00
parent 3df1d849b3
commit b088f005da
6 changed files with 38 additions and 67 deletions

View File

@ -54,7 +54,7 @@ function getPkcs1Padding(length) {
let result = ''; let result = '';
let randomByte; let randomByte;
while (result.length < length) { while (result.length < length) {
randomByte = random.getSecureRandomOctet(); randomByte = random.getRandomBytes(1)[0];
if (randomByte !== 0) { if (randomByte !== 0) {
result += String.fromCharCode(randomByte); result += String.fromCharCode(randomByte);
} }

View File

@ -38,49 +38,7 @@ export default {
* @return {Uint8Array} Random byte array * @return {Uint8Array} Random byte array
*/ */
getRandomBytes: function(length) { getRandomBytes: function(length) {
const result = new Uint8Array(length); const buf = 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');
}
if (typeof window !== 'undefined' && window.crypto && window.crypto.getRandomValues) { if (typeof window !== 'undefined' && window.crypto && window.crypto.getRandomValues) {
window.crypto.getRandomValues(buf); window.crypto.getRandomValues(buf);
} else if (typeof window !== 'undefined' && typeof window.msCrypto === 'object' && typeof window.msCrypto.getRandomValues === 'function') { } else if (typeof window !== 'undefined' && typeof window.msCrypto === 'object' && typeof window.msCrypto.getRandomValues === 'function') {
@ -126,15 +84,17 @@ export default {
function RandomBuffer() { function RandomBuffer() {
this.buffer = null; this.buffer = null;
this.size = null; this.size = null;
this.callback = null;
} }
/** /**
* Initialize buffer * Initialize buffer
* @param {Integer} size size of buffer * @param {Integer} size size of buffer
*/ */
RandomBuffer.prototype.init = function(size) { RandomBuffer.prototype.init = function(size, callback) {
this.buffer = new Uint8Array(size); this.buffer = new Uint8Array(size);
this.size = 0; this.size = 0;
this.callback = callback;
}; };
/** /**

View File

@ -79,7 +79,7 @@ SymEncryptedAEADProtected.prototype.decrypt = function (sessionKeyAlgorithm, key
* @return {Promise<undefined>} Nothing is returned * @return {Promise<undefined>} Nothing is returned
*/ */
SymEncryptedAEADProtected.prototype.encrypt = function (sessionKeyAlgorithm, key) { 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 => { return crypto.gcm.encrypt(sessionKeyAlgorithm, this.packets.write(), key, this.iv).then(encrypted => {
this.encrypted = encrypted; this.encrypted = encrypted;
}); });

View File

@ -20,7 +20,6 @@ import crypto from '../crypto';
import packet from '../packet'; import packet from '../packet';
const INITIAL_RANDOM_SEED = 50000; // random bytes seeded to worker 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 * Initializes a new proxy and loads the web worker
@ -75,7 +74,7 @@ AsyncProxy.prototype.onMessage = function(event) {
delete this.tasks[msg.id]; delete this.tasks[msg.id];
break; break;
case 'request-seed': case 'request-seed':
this.seedRandom(RANDOM_SEED_REQUEST); this.seedRandom(msg.amount);
break; break;
default: default:
throw new Error('Unknown Worker Event.'); throw new Error('Unknown Worker Event.');
@ -87,24 +86,10 @@ AsyncProxy.prototype.onMessage = function(event) {
* @param {Integer} size Number of bytes to send * @param {Integer} size Number of bytes to send
*/ */
AsyncProxy.prototype.seedRandom = function(size) { 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)); 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 * Terminates the worker
*/ */

View File

@ -24,10 +24,29 @@ self.window = {}; // to make UMD bundles work
importScripts('openpgp.js'); importScripts('openpgp.js');
var openpgp = window.openpgp; var openpgp = window.openpgp;
var randomQueue = [];
var randomRequested = false;
var MIN_SIZE_RANDOM_BUFFER = 40000; var MIN_SIZE_RANDOM_BUFFER = 40000;
var MAX_SIZE_RANDOM_BUFFER = 60000; 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<Object>} 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. * Handle messages from the main window.
@ -43,6 +62,13 @@ self.onmessage = function(event) {
case 'seed-random': case 'seed-random':
seedRandom(msg.buf); seedRandom(msg.buf);
var queueCopy = randomQueue;
randomQueue = [];
for (var i = 0; i < queueCopy.length; i++) {
queueCopy[i]();
}
break; break;
default: default:
@ -99,8 +125,8 @@ function delegate(id, method, options) {
* @param {Object} event Contains event type and data * @param {Object} event Contains event type and data
*/ */
function response(event) { function response(event) {
if (openpgp.crypto.random.randomBuffer.size < MIN_SIZE_RANDOM_BUFFER) { if (!randomRequested && openpgp.crypto.random.randomBuffer.size < MIN_SIZE_RANDOM_BUFFER) {
self.postMessage({ event: 'request-seed' }); self.postMessage({ event: 'request-seed', amount: MIN_SIZE_RANDOM_REQUEST });
} }
self.postMessage(event, openpgp.util.getTransferables(event.data)); self.postMessage(event, openpgp.util.getTransferables(event.data));
} }

View File

@ -309,7 +309,7 @@ describe('API functional testing', function() {
if(algo.substr(0,3) === 'aes') { if(algo.substr(0,3) === 'aes') {
it(algo, function() { it(algo, function() {
const key = crypto.generateSessionKey(algo); 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( return crypto.gcm.encrypt(
algo, util.str_to_Uint8Array(plaintext), key, iv algo, util.str_to_Uint8Array(plaintext), key, iv