Web worker: unit tests for random buffer and entropy estimation
This commit is contained in:
parent
c9910929df
commit
a777371418
|
@ -30,11 +30,11 @@ var crypto = require('../crypto'),
|
||||||
type_keyid = require('../type/keyid.js'),
|
type_keyid = require('../type/keyid.js'),
|
||||||
enums = require('../enums.js');
|
enums = require('../enums.js');
|
||||||
|
|
||||||
var INITIAL_SEED = 4096, // bytes seeded to worker
|
var INITIAL_SEED = 4096, // random bytes seeded to worker
|
||||||
SEED_REQUEST = 4096, // bytes seeded after worker request
|
SEED_REQUEST = 4096, // random bytes seeded after worker request
|
||||||
RSA_FACTOR = 2,
|
RSA_FACTOR = 2, // estimated rounds required to find BigInt for p + rounds required to find BigInt for q
|
||||||
DSA_FACTOR = 2,
|
DSA_FACTOR = 2 // estimated rounds required in random.getRandomBigIntegerInRange(2, q-1)
|
||||||
ELG_FACTOR = 2;
|
ELG_FACTOR = 2; // estimated rounds required in random.getRandomBigIntegerInRange(2, p-2)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes a new proxy and loads the web worker
|
* 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
|
var requ = 0; // required entropy in bytes
|
||||||
switch (op) {
|
switch (op) {
|
||||||
case 'enc':
|
case 'enc':
|
||||||
|
if (!publicKeys) throw new Error('publicKeys required for operation enc');
|
||||||
requ += 32; // max. size of session key
|
requ += 32; // max. size of session key
|
||||||
requ += 16; // max. size CFB prefix random
|
requ += 16; // max. size CFB prefix random
|
||||||
publicKeys && publicKeys.forEach(function(key) {
|
publicKeys.forEach(function(key) {
|
||||||
var subKeyPackets = key.getSubkeyPackets();
|
var subKeyPackets = key.getSubkeyPackets();
|
||||||
for (var i = 0; i < subKeyPackets.length; i++) {
|
for (var i = 0; i < subKeyPackets.length; i++) {
|
||||||
if (enums.write(enums.publicKey, subKeyPackets[i].algorithm) == enums.publicKey.elgamal) {
|
if (enums.write(enums.publicKey, subKeyPackets[i].algorithm) == enums.publicKey.elgamal) {
|
||||||
|
@ -120,13 +121,15 @@ AsyncProxy.prototype.entropyEstimation = function(op, publicKeys, privateKeys, o
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case 'sig':
|
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) {
|
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;
|
break;
|
||||||
case 'gen':
|
case 'gen':
|
||||||
|
if (!options.numBits) throw new Error('options.numBits required for operation gen');
|
||||||
requ += 8; // salt for S2K;
|
requ += 8; // salt for S2K;
|
||||||
requ += 16; // CFB initialization vector
|
requ += 16; // CFB initialization vector
|
||||||
requ += (Math.ceil(options.numBits / 8) + 1) * RSA_FACTOR;
|
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
|
* @param {Function} callback receives encrypted ASCII armored message
|
||||||
*/
|
*/
|
||||||
AsyncProxy.prototype.signAndEncryptMessage = function(publicKeys, privateKey, text, callback) {
|
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]);
|
this.entropyEstimation('sig', null, [privateKey]);
|
||||||
publicKeys = publicKeys.map(function(key) {
|
publicKeys = publicKeys.map(function(key) {
|
||||||
return key.toPacketlist();
|
return key.toPacketlist();
|
||||||
|
|
|
@ -5,11 +5,6 @@ var openpgp = typeof window != 'undefined' && window.openpgp ? window.openpgp :
|
||||||
var chai = require('chai'),
|
var chai = require('chai'),
|
||||||
expect = chai.expect;
|
expect = chai.expect;
|
||||||
|
|
||||||
describe('High level API', function() {
|
|
||||||
|
|
||||||
var proxy;
|
|
||||||
|
|
||||||
this.timeout(0);
|
|
||||||
|
|
||||||
var pub_key_rsa =
|
var pub_key_rsa =
|
||||||
['-----BEGIN PGP PUBLIC KEY BLOCK-----',
|
['-----BEGIN PGP PUBLIC KEY BLOCK-----',
|
||||||
|
@ -180,6 +175,12 @@ var priv_key_de =
|
||||||
expect(privKeyDE).to.exist;
|
expect(privKeyDE).to.exist;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
describe('High level API', function() {
|
||||||
|
|
||||||
|
var proxy;
|
||||||
|
|
||||||
|
this.timeout(0);
|
||||||
|
|
||||||
before(function() {
|
before(function() {
|
||||||
proxy = new openpgp.AsyncProxy('../dist/openpgp.worker.js');
|
proxy = new openpgp.AsyncProxy('../dist/openpgp.worker.js');
|
||||||
expect(proxy).to.exist;
|
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() {
|
describe('Decrypt and Verify', function() {
|
||||||
|
|
||||||
var msgRSA, msgDE;
|
var msgRSA, msgDE;
|
||||||
|
@ -286,10 +294,7 @@ var priv_key_de =
|
||||||
proxy.decryptAndVerifyMessage(privKeyRSA, [pubKeyRSA], msgRSA, function(err, data) {
|
proxy.decryptAndVerifyMessage(privKeyRSA, [pubKeyRSA], msgRSA, function(err, data) {
|
||||||
expect(err).to.not.exist;
|
expect(err).to.not.exist;
|
||||||
expect(data).to.exist;
|
expect(data).to.exist;
|
||||||
expect(data.text).to.equal(plaintext);
|
verifySignature(data, privKeyRSA);
|
||||||
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;
|
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -298,10 +303,28 @@ var priv_key_de =
|
||||||
proxy.decryptAndVerifyMessage(privKeyDE, [pubKeyDE], msgDE, function(err, data) {
|
proxy.decryptAndVerifyMessage(privKeyDE, [pubKeyDE], msgDE, function(err, data) {
|
||||||
expect(err).to.not.exist;
|
expect(err).to.not.exist;
|
||||||
expect(data).to.exist;
|
expect(data).to.exist;
|
||||||
expect(data.text).to.equal(plaintext);
|
verifySignature(data, privKeyDE);
|
||||||
expect(data.signatures).to.have.length(1);
|
done();
|
||||||
expect(data.signatures[0].valid).to.be.true;
|
});
|
||||||
expect(data.signatures[0].keyid.equals(privKeyDE.getSigningKeyPacket().getKeyId())).to.be.true;
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
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();
|
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() {
|
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);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
Loading…
Reference in New Issue
Block a user