openpgpjs/test/general/biginteger.js
larabr d49d92e5cb Update to Mocha v10 in tests, declare lib as module and add exports to package.json
Mocha v10 requires the lib to be esm compliant.
ESM mandates the use of file extensions in imports, so to minimize the
changes (for now), we rely on the flag `experimental-specifier-resolution=node`
and on `ts-node` (needed only for Node 20).

Breaking changes:
downstream bundlers might be affected by the package.json changes depending on
how they load the library.
NB: legacy package.json entrypoints are still available.
2023-10-25 12:53:10 +02:00

162 lines
7.1 KiB
JavaScript

import { use as chaiUse, expect } from 'chai';
import chaiAsPromised from 'chai-as-promised';
chaiUse(chaiAsPromised);
import BN from 'bn.js';
import * as random from '../../src/crypto/random.js';
import util from '../../src/util.js';
let BigInteger;
async function getRandomBN(min, max) {
if (max.cmp(min) <= 0) {
throw new Error('Illegal parameter value: max <= min');
}
const modulus = max.sub(min);
const bytes = modulus.byteLength();
const r = new BN(random.getRandomBytes(bytes + 8));
return r.mod(modulus).add(min);
}
export default () => describe('BigInteger interface', function() {
before(async () => {
BigInteger = await util.getBigInteger();
});
it('constructor throws on undefined input', function() {
expect(() => new BigInteger()).to.throw('Invalid BigInteger input');
});
it('constructor supports strings', function() {
const input = '417653931840771530406225971293556769925351769207235721650257629558293828796031115397206059067934284452829611906818956352854418342467914729341523414945427019410284762464062112274326172407819051167058569790660930309496043254270888417520676082271432948852231332576271876251597199882908964994070268531832274431027';
const got = new BigInteger(input);
const expected = new BN(input);
expect(got.toString()).to.equal(expected.toString());
});
it('constructor supports Uint8Arrays', function() {
const expected = new BN('417653931840771530406225971293556769925351769207235721650257629558293828796031115397206059067934284452829611906818956352854418342467914729341523414945427019410284762464062112274326172407819051167058569790660930309496043254270888417520676082271432948852231332576271876251597199882908964994070268531832274431027');
const input = expected.toArrayLike(Uint8Array);
const got = new BigInteger(input);
expect(got.toString()).to.equal(expected.toString());
});
it('conditional operators are correct', function() {
const a = new BigInteger(12);
const b = new BigInteger(34);
expect(a.equal(a)).to.be.true;
expect(a.equal(b)).to.be.false;
expect(a.gt(a) === a.lt(a)).to.be.true;
expect(a.gt(b) === a.lt(b)).to.be.false;
expect(a.gte(a) === a.lte(a)).to.be.true;
const zero = new BigInteger(0);
const one = new BigInteger(1);
expect(zero.isZero()).to.be.true;
expect(one.isZero()).to.be.false;
expect(one.isOne()).to.be.true;
expect(zero.isOne()).to.be.false;
expect(zero.isEven()).to.be.true;
expect(one.isEven()).to.be.false;
expect(zero.isNegative()).to.be.false;
expect(zero.dec().isNegative()).to.be.true;
});
it('bitLength is correct', function() {
const n = new BigInteger(127);
let expected = 7;
expect(n.bitLength() === expected).to.be.true;
expect(n.inc().bitLength() === (++expected)).to.be.true;
});
it('byteLength is correct', function() {
const n = new BigInteger(65535);
let expected = 2;
expect(n.byteLength() === expected).to.be.true;
expect(n.inc().byteLength() === (++expected)).to.be.true;
});
it('toUint8Array is correct', function() {
const nString = '417653931840771530406225971293556769925351769207235721650257629558293828796031115397206059067934284452829611906818956352854418342467914729341523414945427019410284762464062112274326172407819051167058569790660930309496043254270888417520676082271432948852231332576271876251597199882908964994070268531832274431027';
const n = new BigInteger(nString);
const paddedSize = Number(n.byteLength()) + 1;
// big endian, unpadded
let expected = new BN(nString).toArrayLike(Uint8Array);
expect(n.toUint8Array()).to.deep.equal(expected);
// big endian, padded
expected = new BN(nString).toArrayLike(Uint8Array, 'be', paddedSize);
expect(n.toUint8Array('be', paddedSize)).to.deep.equal(expected);
// little endian, unpadded
expected = new BN(nString).toArrayLike(Uint8Array, 'le');
expect(n.toUint8Array('le')).to.deep.equal(expected);
//little endian, padded
expected = new BN(nString).toArrayLike(Uint8Array, 'le', paddedSize);
expect(n.toUint8Array('le', paddedSize)).to.deep.equal(expected);
});
it('binary operators are consistent', function() {
const a = new BigInteger(12);
const b = new BigInteger(34);
const ops = ['add', 'sub', 'mul', 'mod', 'leftShift', 'rightShift'];
ops.forEach(op => {
const iop = `i${op}`;
expect(a[op](b).equal(a[iop](b))).to.be.true;
});
});
it('unary operators are consistent', function() {
const a = new BigInteger(12);
const one = new BigInteger(1);
expect(a.sub(one).equal(a.dec())).to.be.true;
expect(a.add(one).equal(a.inc())).to.be.true;
});
it('modExp is correct (large values)', function() {
const stringX = '417653931840771530406225971293556769925351769207235721650257629558293828796031115397206059067934284452829611906818956352854418342467914729341523414945427019410284762464062112274326172407819051167058569790660930309496043254270888417520676082271432948852231332576271876251597199882908964994070268531832274431027';
const stringE = '21139356010872569239159922781526379521587348169074209285187910481667533072168468011617194695181255483288792585413365359733692097084373249198758148704369207793873998901870577262254971784191473102265830193058813215898765238784670469696574407580179153118937858890572095234316482449291777882525949871374961971753';
const stringN = '129189808515414783602892982235788912674846062846614219472827821758734760420002631653235573915244294540972376140705505703576175711417114803419704967903726436285518767606681184247119430411311152556442947708732584954518890222684529678365388350886907287414896703685680210648760841628375425909680236584021041565183';
const x = new BigInteger(stringX);
const e = new BigInteger(stringE);
const n = new BigInteger(stringN);
const got = x.modExp(e, n);
const expected = new BN(stringX).toRed(BN.red(new BN(stringN))).redPow(new BN(stringE));
// different formats, it's easier to compare strings
expect(got.toString() === expected.toString()).to.be.true;
});
it('gcd is correct', async function() {
const aBN = await getRandomBN(new BN(2), new BN(200));
const bBN = await getRandomBN(new BN(2), new BN(200));
if (aBN.isEven()) aBN.iaddn(1);
const a = new BigInteger(aBN.toString());
const b = new BigInteger(bBN.toString());
const expected = aBN.gcd(bBN);
expect(a.gcd(b).toString()).to.equal(expected.toString());
});
it('modular inversion is correct', async function() {
const moduloBN = new BN(229); // this is a prime
const baseBN = await getRandomBN(new BN(2), moduloBN);
const a = new BigInteger(baseBN.toString());
const n = new BigInteger(moduloBN.toString());
const expected = baseBN.invm(moduloBN);
expect(a.modInv(n).toString()).to.equal(expected.toString());
expect(() => a.mul(n).modInv(n)).to.throw('Inverse does not exist');
});
it('getBit is correct', async function() {
const i = 5;
const nBN = await getRandomBN(new BN(2), new BN(200));
const n = new BigInteger(nBN.toString());
const expected = nBN.testn(5) ? 1 : 0;
expect(n.getBit(i) === expected).to.be.true;
});
});