
Implement optional constant-time decryption flow to hinder Bleichenbacher-like attacks against RSA- and ElGamal public-key encrypted session keys. Changes: - Add `config.constantTimePKCS1Decryption` to enable the constant-time processing (defaults to `false`). The constant-time option is off by default since it has measurable performance impact on message decryption, and it is only helpful in specific application scenarios (more info below). - Add `config.constantTimePKCS1DecryptionSupportedSymmetricAlgorithms` (defaults to the AES algorithms). The set of supported ciphers is restricted by default since the number of algorithms negatively affects performance. Bleichenbacher-like attacks are of concern for applications where both of the following conditions are met: 1. new/incoming messages are automatically decrypted (without user interaction); 2. an attacker can determine how long it takes to decrypt each message (e.g. due to decryption errors being logged remotely).
181 lines
6.3 KiB
JavaScript
181 lines
6.3 KiB
JavaScript
const util = require('../../src/util');
|
|
|
|
const chai = require('chai');
|
|
|
|
const { expect } = chai;
|
|
|
|
module.exports = () => describe('Util unit tests', function() {
|
|
|
|
describe('isString', function() {
|
|
it('should return true for type "string"', function() {
|
|
const data = 'foo';
|
|
expect(util.isString(data)).to.be.true;
|
|
});
|
|
it('should return true for type String', function() {
|
|
const data = String('foo');
|
|
expect(util.isString(data)).to.be.true;
|
|
});
|
|
it('should return true for inherited type of String', function() {
|
|
function MyString() {}
|
|
MyString.prototype = Object.create(String.prototype);
|
|
const data = new MyString();
|
|
expect(util.isString(data)).to.be.true;
|
|
});
|
|
it('should return true for empty string', function() {
|
|
const data = '';
|
|
expect(util.isString(data)).to.be.true;
|
|
});
|
|
it('should return false for undefined', function() {
|
|
let data;
|
|
expect(util.isString(data)).to.be.false;
|
|
});
|
|
it('should return false for Object', function() {
|
|
const data = {};
|
|
expect(util.isString(data)).to.be.false;
|
|
});
|
|
});
|
|
|
|
describe('isArray', function() {
|
|
it('should return true for []', function() {
|
|
const data = [];
|
|
expect(util.isArray(data)).to.be.true;
|
|
});
|
|
it('should return true for type Array', function() {
|
|
const data = Array(); // eslint-disable-line no-array-constructor
|
|
expect(util.isArray(data)).to.be.true;
|
|
});
|
|
it('should return true for inherited type of Array', function() {
|
|
function MyArray() {}
|
|
MyArray.prototype = Object.create(Array.prototype);
|
|
const data = new MyArray();
|
|
expect(util.isArray(data)).to.be.true;
|
|
});
|
|
it('should return false for undefined', function() {
|
|
let data;
|
|
expect(util.isArray(data)).to.be.false;
|
|
});
|
|
it('should return false for Object', function() {
|
|
const data = {};
|
|
expect(util.isArray(data)).to.be.false;
|
|
});
|
|
});
|
|
|
|
describe('isUint8Array', function() {
|
|
it('should return true for type Uint8Array', function() {
|
|
const data = new Uint8Array();
|
|
expect(util.isUint8Array(data)).to.be.true;
|
|
});
|
|
it('should return true for inherited type of Uint8Array', function() {
|
|
function MyUint8Array() {}
|
|
MyUint8Array.prototype = new Uint8Array();
|
|
const data = new MyUint8Array();
|
|
expect(util.isUint8Array(data)).to.be.true;
|
|
});
|
|
it('should return false for undefined', function() {
|
|
let data;
|
|
expect(util.isUint8Array(data)).to.be.false;
|
|
});
|
|
it('should return false for Object', function() {
|
|
const data = {};
|
|
expect(util.isUint8Array(data)).to.be.false;
|
|
});
|
|
});
|
|
|
|
describe('leftPad', function() {
|
|
it('should not change the input if the length is correct', function() {
|
|
const bytes = new Uint8Array([2, 1]);
|
|
const padded = util.leftPad(bytes, 2);
|
|
expect(padded).to.deep.equal(bytes);
|
|
});
|
|
it('should add leading zeros to input array', function() {
|
|
const bytes = new Uint8Array([1, 2]);
|
|
const padded = util.leftPad(bytes, 5);
|
|
expect(padded).to.deep.equal(new Uint8Array([0, 0, 0, 1, 2]));
|
|
});
|
|
});
|
|
|
|
describe('uint8ArrayToMPI', function() {
|
|
it('should strip leading zeros', function() {
|
|
const bytes = new Uint8Array([0, 0, 1, 2]);
|
|
const mpi = util.uint8ArrayToMPI(bytes);
|
|
expect(mpi).to.deep.equal(new Uint8Array([0, 9, 1, 2]));
|
|
});
|
|
it('should throw on array of all zeros', function() {
|
|
const bytes = new Uint8Array([0, 0]);
|
|
expect(() => util.uint8ArrayToMPI(bytes)).to.throw('Zero MPI');
|
|
});
|
|
});
|
|
|
|
describe('isEmailAddress', function() {
|
|
it('should return true for valid email address', function() {
|
|
const data = 'test@example.com';
|
|
expect(util.isEmailAddress(data)).to.be.true;
|
|
});
|
|
it('should return true for valid email address', function() {
|
|
const data = 'test@xn--wgv.xn--q9jyb4c';
|
|
expect(util.isEmailAddress(data)).to.be.true;
|
|
});
|
|
it('should return false for invalid email address', function() {
|
|
const data = 'Test User <test@example.com>';
|
|
expect(util.isEmailAddress(data)).to.be.false;
|
|
});
|
|
it('should return false for invalid email address', function() {
|
|
const data = 'test@examplecom';
|
|
expect(util.isEmailAddress(data)).to.be.false;
|
|
});
|
|
it('should return false for invalid email address', function() {
|
|
const data = 'testexamplecom';
|
|
expect(util.isEmailAddress(data)).to.be.false;
|
|
});
|
|
it('should return false for empty string', function() {
|
|
const data = '';
|
|
expect(util.isEmailAddress(data)).to.be.false;
|
|
});
|
|
it('should return false for undefined', function() {
|
|
let data;
|
|
expect(util.isEmailAddress(data)).to.be.false;
|
|
});
|
|
it('should return false for Object', function() {
|
|
const data = {};
|
|
expect(util.isEmailAddress(data)).to.be.false;
|
|
});
|
|
});
|
|
|
|
describe('constant time select', function() {
|
|
it('selectUint8Array should work for arrays of equal length', function () {
|
|
const size = 10;
|
|
const a = new Uint8Array(size).fill(1);
|
|
const b = new Uint8Array(size).fill(2);
|
|
expect(util.selectUint8Array(true, a, b)).to.deep.equal(a);
|
|
expect(util.selectUint8Array(false, a, b)).to.deep.equal(b);
|
|
});
|
|
|
|
it('selectUint8Array should work for arrays of different length', function () {
|
|
const size = 10;
|
|
const a = new Uint8Array(size).fill(1);
|
|
const b = new Uint8Array(2 * size).fill(2);
|
|
expect(util.selectUint8Array(true, a, b)).to.deep.equal(a);
|
|
expect(util.selectUint8Array(false, a, b)).to.deep.equal(b);
|
|
expect(util.selectUint8Array(true, b, a)).to.deep.equal(b);
|
|
expect(util.selectUint8Array(false, b, a)).to.deep.equal(a);
|
|
});
|
|
|
|
it('selectUint8 should return the expected value based on condition', function () {
|
|
const a = 1;
|
|
const b = 2;
|
|
expect(util.selectUint8(true, a, b)).to.equal(a);
|
|
expect(util.selectUint8(false, a, b)).to.equal(b);
|
|
});
|
|
});
|
|
|
|
describe('Misc.', function() {
|
|
it('util.readNumber should not overflow until full range of uint32', function () {
|
|
const ints = [2 ** 20, 2 ** 25, 2 ** 30, 2 ** 32 - 1];
|
|
for (let i = 0; i < ints.length; i++) {
|
|
expect(util.readNumber(util.writeNumber(ints[i], 4))).to.equal(ints[i]);
|
|
}
|
|
});
|
|
});
|
|
|
|
});
|