Web worker: simplify random data supply
This commit is contained in:
parent
abf0545208
commit
040ccbaf2c
|
@ -30,11 +30,8 @@ var crypto = require('../crypto'),
|
|||
type_keyid = require('../type/keyid.js'),
|
||||
enums = require('../enums.js');
|
||||
|
||||
var INITIAL_SEED = 4096, // random bytes seeded to worker
|
||||
SEED_REQUEST = 4096, // random bytes seeded after worker request
|
||||
RSA_FACTOR = 2, // estimated rounds required to find BigInt for p + rounds required to find BigInt for q
|
||||
DSA_FACTOR = 2 // estimated rounds required in random.getRandomBigIntegerInRange(2, q-1)
|
||||
ELG_FACTOR = 2; // estimated rounds required in random.getRandomBigIntegerInRange(2, p-2)
|
||||
var INITIAL_RANDOM_SEED = 50000, // random bytes seeded to worker
|
||||
RANDOM_SEED_REQUEST = 20000; // random bytes seeded after worker request
|
||||
|
||||
/**
|
||||
* Initializes a new proxy and loads the web worker
|
||||
|
@ -44,7 +41,7 @@ var INITIAL_SEED = 4096, // random bytes seeded to worker
|
|||
function AsyncProxy(path) {
|
||||
this.worker = new Worker(path || 'openpgp.worker.js');
|
||||
this.worker.onmessage = this.onMessage.bind(this);
|
||||
this.seedRandom(INITIAL_SEED);
|
||||
this.seedRandom(INITIAL_RANDOM_SEED);
|
||||
// FIFO
|
||||
this.tasks = [];
|
||||
}
|
||||
|
@ -59,7 +56,7 @@ AsyncProxy.prototype.onMessage = function(event) {
|
|||
this.tasks.shift()(msg.err ? new Error(msg.err) : null, msg.data);
|
||||
break;
|
||||
case 'request-seed':
|
||||
this.seedRandom(SEED_REQUEST);
|
||||
this.seedRandom(RANDOM_SEED_REQUEST);
|
||||
break;
|
||||
default:
|
||||
throw new Error('Unknown Worker Event.');
|
||||
|
@ -94,53 +91,6 @@ AsyncProxy.prototype.terminate = function() {
|
|||
this.worker.terminate();
|
||||
};
|
||||
|
||||
/**
|
||||
* Estimation on how much random bytes are required to process the operation
|
||||
* @param {String} op 'enc', 'sig' or 'gen'
|
||||
* @param {Array<module:key~Key>} publicKeys
|
||||
* @param {Array<module:key~Key>} privateKeys
|
||||
* @param {Object} options
|
||||
* @return {Integer} number of bytes required
|
||||
*/
|
||||
AsyncProxy.prototype.entropyEstimation = function(op, publicKeys, privateKeys, options) {
|
||||
var requ = 0; // required entropy in bytes
|
||||
switch (op) {
|
||||
case 'enc':
|
||||
if (!publicKeys) throw new Error('publicKeys required for operation enc');
|
||||
requ += 32; // max. size of session key
|
||||
requ += 16; // max. size CFB prefix random
|
||||
publicKeys.forEach(function(key) {
|
||||
var subKeyPackets = key.getSubkeyPackets();
|
||||
for (var i = 0; i < subKeyPackets.length; i++) {
|
||||
if (enums.write(enums.publicKey, subKeyPackets[i].algorithm) == enums.publicKey.elgamal) {
|
||||
var keyByteSize = subKeyPackets[i].mpi[0].byteLength();
|
||||
requ += keyByteSize * ELG_FACTOR; // key byte size for ElGamal keys
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
break;
|
||||
case 'sig':
|
||||
if (!privateKeys) throw new Error('privateKeys required for operation sig');
|
||||
privateKeys.forEach(function(key) {
|
||||
if (enums.write(enums.publicKey, key.primaryKey.algorithm) == enums.publicKey.dsa) {
|
||||
requ += 32 * DSA_FACTOR; // 32 bytes for DSA N value
|
||||
}
|
||||
});
|
||||
break;
|
||||
case 'gen':
|
||||
if (!options.numBits) throw new Error('options.numBits required for operation gen');
|
||||
requ += 8; // salt for S2K;
|
||||
requ += 16; // CFB initialization vector
|
||||
requ += (Math.ceil(options.numBits / 8) + 1) * RSA_FACTOR;
|
||||
requ = requ * 2; // * number of key packets
|
||||
break;
|
||||
default:
|
||||
throw new Error('Unknown operation.');
|
||||
}
|
||||
return requ;
|
||||
};
|
||||
|
||||
/**
|
||||
* Encrypts message text with keys
|
||||
* @param {Array<module:key~Key>} keys array of keys, used to encrypt the message
|
||||
|
@ -148,15 +98,13 @@ AsyncProxy.prototype.entropyEstimation = function(op, publicKeys, privateKeys, o
|
|||
* @param {Function} callback receives encrypted ASCII armored message
|
||||
*/
|
||||
AsyncProxy.prototype.encryptMessage = function(keys, text, callback) {
|
||||
var estimation = this.entropyEstimation('enc', keys);
|
||||
keys = keys.map(function(key) {
|
||||
return key.toPacketlist();
|
||||
});
|
||||
this.worker.postMessage({
|
||||
event: 'encrypt-message',
|
||||
keys: keys,
|
||||
text: text,
|
||||
seed: this.getRandomBuffer(estimation)
|
||||
text: text
|
||||
});
|
||||
this.tasks.push(callback);
|
||||
};
|
||||
|
@ -169,8 +117,6 @@ AsyncProxy.prototype.encryptMessage = function(keys, text, callback) {
|
|||
* @param {Function} callback receives encrypted ASCII armored message
|
||||
*/
|
||||
AsyncProxy.prototype.signAndEncryptMessage = function(publicKeys, privateKey, text, callback) {
|
||||
var estimation = this.entropyEstimation('enc', publicKeys) +
|
||||
this.entropyEstimation('sig', null, [privateKey]);
|
||||
publicKeys = publicKeys.map(function(key) {
|
||||
return key.toPacketlist();
|
||||
});
|
||||
|
@ -179,8 +125,7 @@ AsyncProxy.prototype.signAndEncryptMessage = function(publicKeys, privateKey, te
|
|||
event: 'sign-and-encrypt-message',
|
||||
publicKeys: publicKeys,
|
||||
privateKey: privateKey,
|
||||
text: text,
|
||||
seed: this.getRandomBuffer(estimation)
|
||||
text: text
|
||||
});
|
||||
this.tasks.push(callback);
|
||||
};
|
||||
|
@ -239,15 +184,13 @@ AsyncProxy.prototype.decryptAndVerifyMessage = function(privateKey, publicKeys,
|
|||
* @param {Function} callback receives ASCII armored message
|
||||
*/
|
||||
AsyncProxy.prototype.signClearMessage = function(privateKeys, text, callback) {
|
||||
var estimation = this.entropyEstimation('sig', null, privateKeys);
|
||||
privateKeys = privateKeys.map(function(key) {
|
||||
return key.toPacketlist();
|
||||
});
|
||||
this.worker.postMessage({
|
||||
event: 'sign-clear-message',
|
||||
privateKeys: privateKeys,
|
||||
text: text,
|
||||
seed: this.getRandomBuffer(estimation)
|
||||
text: text
|
||||
});
|
||||
this.tasks.push(callback);
|
||||
};
|
||||
|
@ -294,8 +237,7 @@ AsyncProxy.prototype.generateKeyPair = function(keyType, numBits, userId, passph
|
|||
keyType: keyType,
|
||||
numBits: numBits,
|
||||
userId: userId,
|
||||
passphrase: passphrase,
|
||||
seed: this.getRandomBuffer(this.entropyEstimation('gen', null, null, {numBits: numBits}))
|
||||
passphrase: passphrase
|
||||
});
|
||||
this.tasks.push(function(err, data) {
|
||||
if (data) {
|
||||
|
|
|
@ -19,8 +19,8 @@ window = {}; // to make UMD bundles work
|
|||
|
||||
importScripts('openpgp.js');
|
||||
|
||||
var MIN_SIZE_RANDOM_BUFFER = 8192;
|
||||
var MAX_SIZE_RANDOM_BUFFER = 16384;
|
||||
var MIN_SIZE_RANDOM_BUFFER = 40000;
|
||||
var MAX_SIZE_RANDOM_BUFFER = 60000;
|
||||
|
||||
window.openpgp.crypto.random.randomBuffer.init(MAX_SIZE_RANDOM_BUFFER);
|
||||
|
||||
|
@ -29,9 +29,6 @@ onmessage = function (event) {
|
|||
err = null,
|
||||
msg = event.data,
|
||||
correct = false;
|
||||
if (msg.seed) {
|
||||
window.openpgp.crypto.random.randomBuffer.set(msg.seed);
|
||||
}
|
||||
switch (msg.event) {
|
||||
case 'seed-random':
|
||||
window.openpgp.crypto.random.randomBuffer.set(msg.buf);
|
||||
|
|
|
@ -386,6 +386,19 @@ describe('High level API', function() {
|
|||
});
|
||||
});
|
||||
|
||||
it('Depleted random buffer in worker gives error', function (done) {
|
||||
var wProxy = new openpgp.AsyncProxy('../dist/openpgp.worker.js');
|
||||
wProxy.worker = new Worker('../dist/openpgp.worker.js');
|
||||
wProxy.worker.onmessage = wProxy.onMessage.bind(wProxy);
|
||||
wProxy.seedRandom(10);
|
||||
wProxy.encryptMessage([pubKeyRSA], plaintext, function(err, data) {
|
||||
expect(data).to.not.exist;
|
||||
expect(err).to.exist;
|
||||
expect(err).to.eql(new Error('Random number buffer depleted.'));
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('Key generation', function() {
|
||||
|
@ -539,56 +552,3 @@ describe('Random Buffer', function() {
|
|||
});
|
||||
|
||||
});
|
||||
|
||||
describe('Entropy Estimation', function() {
|
||||
|
||||
var proxy;
|
||||
|
||||
before(function() {
|
||||
proxy = new openpgp.AsyncProxy('../dist/openpgp.worker.js');
|
||||
expect(proxy).to.exist;
|
||||
initKeys();
|
||||
});
|
||||
|
||||
it('RSA encrypt', function () {
|
||||
var oneRSA = proxy.entropyEstimation('enc', [pubKeyRSA]);
|
||||
// 32 byte session key + 16 byte CFB prefix
|
||||
expect(oneRSA).to.equal(48);
|
||||
var twoRSA = proxy.entropyEstimation('enc', [pubKeyRSA, pubKeyRSA]);
|
||||
// only number of symmetrically encrypted packets relevant (we expect 1)
|
||||
expect(twoRSA).to.equal(oneRSA);
|
||||
});
|
||||
|
||||
it('RSA sign', function () {
|
||||
var oneRSA = proxy.entropyEstimation('sig', null, [privKeyRSA]);
|
||||
// no entropy required for RSA signing
|
||||
expect(oneRSA).to.equal(0);
|
||||
});
|
||||
|
||||
it('RSA generate', function () {
|
||||
var oneRSA = proxy.entropyEstimation('gen', null, null, {numBits: 2048});
|
||||
// 8 salt for S2K
|
||||
// 16 CFB initialization vector
|
||||
// 256 byte size of key
|
||||
// 1 ?
|
||||
// 2 at least one round required for p and one for and q required
|
||||
// 2 number of key packets
|
||||
expect(oneRSA).to.equal((8 + 16 + (256 + 1) * 2) * 2);
|
||||
});
|
||||
|
||||
it('ELG encrypt', function () {
|
||||
var oneELG = proxy.entropyEstimation('enc', [pubKeyDE]);
|
||||
// 32 byte session key + 16 byte CFB prefix
|
||||
// 2 estimated rounds required in random.getRandomBigIntegerInRange(2, p-2)
|
||||
expect(oneELG).to.equal(48 + (pubKeyDE.subKeys[0].subKey.getBitSize() / 8) * 2);
|
||||
|
||||
});
|
||||
|
||||
it('DSA sign', function () {
|
||||
var oneDSA = proxy.entropyEstimation('sig', null, [privKeyDE]);
|
||||
// 32 bytes for DSA N value
|
||||
// 2 estimated rounds required in random.getRandomBigIntegerInRange(2, q-1)
|
||||
expect(oneDSA).to.equal(32 * 2);
|
||||
});
|
||||
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue
Block a user