Web worker: unit tests for random buffer and entropy estimation

This commit is contained in:
Thomas Oberndörfer 2014-01-16 13:34:31 +01:00
parent c9910929df
commit a777371418
2 changed files with 329 additions and 171 deletions

View File

@ -30,11 +30,11 @@ var crypto = require('../crypto'),
type_keyid = require('../type/keyid.js'),
enums = require('../enums.js');
var INITIAL_SEED = 4096, // bytes seeded to worker
SEED_REQUEST = 4096, // bytes seeded after worker request
RSA_FACTOR = 2,
DSA_FACTOR = 2,
ELG_FACTOR = 2;
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)
/**
* Initializes a new proxy and loads the web worker
@ -106,9 +106,10 @@ AsyncProxy.prototype.entropyEstimation = function(op, publicKeys, privateKeys, o
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 && publicKeys.forEach(function(key) {
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) {
@ -120,13 +121,15 @@ AsyncProxy.prototype.entropyEstimation = function(op, publicKeys, privateKeys, o
});
break;
case 'sig':
privateKeys && privateKeys.forEach(function(key) {
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 keys
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;
@ -166,7 +169,7 @@ 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', publikKeys) +
var estimation = this.entropyEstimation('enc', publicKeys) +
this.entropyEstimation('sig', null, [privateKey]);
publicKeys = publicKeys.map(function(key) {
return key.toPacketlist();

View File

@ -5,11 +5,6 @@ var openpgp = typeof window != 'undefined' && window.openpgp ? window.openpgp :
var chai = require('chai'),
expect = chai.expect;
describe('High level API', function() {
var proxy;
this.timeout(0);
var pub_key_rsa =
['-----BEGIN PGP PUBLIC KEY BLOCK-----',
@ -180,6 +175,12 @@ var priv_key_de =
expect(privKeyDE).to.exist;
}
describe('High level API', function() {
var proxy;
this.timeout(0);
before(function() {
proxy = new openpgp.AsyncProxy('../dist/openpgp.worker.js');
expect(proxy).to.exist;
@ -271,6 +272,13 @@ var priv_key_de =
});
function verifySignature(data, privKey) {
expect(data.text).to.equal(plaintext);
expect(data.signatures).to.have.length(1);
expect(data.signatures[0].valid).to.be.true;
expect(data.signatures[0].keyid.equals(privKey.getSigningKeyPacket().getKeyId())).to.be.true;
}
describe('Decrypt and Verify', function() {
var msgRSA, msgDE;
@ -286,10 +294,7 @@ var priv_key_de =
proxy.decryptAndVerifyMessage(privKeyRSA, [pubKeyRSA], msgRSA, function(err, data) {
expect(err).to.not.exist;
expect(data).to.exist;
expect(data.text).to.equal(plaintext);
expect(data.signatures).to.have.length(1);
expect(data.signatures[0].valid).to.be.true;
expect(data.signatures[0].keyid.equals(privKeyRSA.getSigningKeyPacket().getKeyId())).to.be.true;
verifySignature(data, privKeyRSA);
done();
});
});
@ -298,10 +303,28 @@ var priv_key_de =
proxy.decryptAndVerifyMessage(privKeyDE, [pubKeyDE], msgDE, function(err, data) {
expect(err).to.not.exist;
expect(data).to.exist;
expect(data.text).to.equal(plaintext);
expect(data.signatures).to.have.length(1);
expect(data.signatures[0].valid).to.be.true;
expect(data.signatures[0].keyid.equals(privKeyDE.getSigningKeyPacket().getKeyId())).to.be.true;
verifySignature(data, privKeyDE);
done();
});
});
});
describe('Sign and Encrypt', function() {
before(function() {
privKeyRSA.decrypt('hello world');
});
it('RSA: signAndEncryptMessage async', function (done) {
proxy.signAndEncryptMessage([pubKeyRSA], privKeyRSA, plaintext, function(err, data) {
expect(err).to.not.exist;
expect(data).to.exist;
expect(data).to.match(/^-----BEGIN PGP MESSAGE/);
var msg = openpgp.message.readArmored(data);
expect(msg).to.be.an.instanceof(openpgp.message.Message);
var decrypted = openpgp.decryptAndVerifyMessage(privKeyRSA, [pubKeyRSA], msg);
verifySignature(decrypted, privKeyRSA);
done();
});
});
@ -337,6 +360,17 @@ var priv_key_de =
});
});
it('RSA: verifyClearSignedMessage async', function (done) {
var signed = openpgp.signClearMessage([privKeyRSA], plaintext);
signed = openpgp.cleartext.readArmored(signed);
proxy.verifyClearSignedMessage([pubKeyRSA], signed, function(err, data) {
expect(err).to.not.exist;
expect(data).to.exist;
verifySignature(data, privKeyRSA);
done();
});
});
});
describe('Error handling', function() {
@ -379,4 +413,125 @@ var priv_key_de =
});
describe('Random Buffer', function() {
var randomBuffer;
before(function() {
randomBuffer = new openpgp.crypto.random.randomBuffer.constructor();
expect(randomBuffer).to.exist;
});
it('Throw error if not initialized', function () {
expect(randomBuffer.set).to.throw('RandomBuffer is not initialized');
expect(randomBuffer.get).to.throw('RandomBuffer is not initialized');
});
it('Initialization', function () {
randomBuffer.init(5);
expect(randomBuffer.buffer).to.exist;
expect(randomBuffer.buffer).to.have.length(5);
expect(randomBuffer.size).to.equal(0);
});
function equal(buf, arr) {
for (var i = 0; i < buf.length; i++) {
if (buf[i] !== arr[i]) return false;
}
return true;
}
it('Set Method', function () {
randomBuffer.init(5);
var buf = new Uint32Array(2);
buf[0] = 1; buf[1] = 2;
randomBuffer.set(buf);
expect(equal(randomBuffer.buffer, [1,2,0,0,0])).to.be.true;
expect(randomBuffer.size).to.equal(2);
randomBuffer.set(buf);
expect(equal(randomBuffer.buffer, [1,2,1,2,0])).to.be.true;
expect(randomBuffer.size).to.equal(4);
randomBuffer.set(buf);
expect(equal(randomBuffer.buffer, [1,2,1,2,1])).to.be.true;
expect(randomBuffer.size).to.equal(5);
randomBuffer.init(1);
var buf = new Uint32Array(2);
buf[0] = 1; buf[1] = 2;
randomBuffer.set(buf);
expect(buf).to.to.have.property('0', 1);
expect(randomBuffer.size).to.equal(1);
});
it('Get Method', function () {
randomBuffer.init(5);
var buf = new Uint32Array(5);
buf[0] = 1; buf[1] = 2; buf[2] = 5; buf[3] = 7; buf[4] = 8;
randomBuffer.set(buf);
var buf = new Uint32Array(2);
randomBuffer.get(buf);
expect(equal(randomBuffer.buffer, [1,2,5,7,8])).to.be.true;
expect(randomBuffer.size).to.equal(3);
expect(buf).to.to.have.property('0', 8);
expect(buf).to.to.have.property('1', 7);
expect(equal(randomBuffer.buffer, [1,2,5,7,8])).to.be.true;
randomBuffer.get(buf);
expect(buf).to.to.have.property('0', 5);
expect(buf).to.to.have.property('1', 2);
expect(randomBuffer.size).to.equal(1);
expect(function() { randomBuffer.get(buf) }).to.throw('Random number buffer depleted.');
});
});
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);
});
});