Merge pull request #21 from eCollect/feat/drop-bn.js

Closes #17 and #13
This commit is contained in:
Dimitar Nanov 2019-11-07 10:52:01 +02:00 committed by GitHub
commit 926d1bca18
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 3922 additions and 31 deletions

26
.gitignore vendored
View File

@ -1,13 +1,19 @@
npm-debug.log # mac shit
node_modules
.DS_Store .DS_Store
*.local.json5
yarn.lock # nyc test coverage
/project.sublime-workspace .nyc_output
/public/css/style.css.map
/.idea # Dependency directories
node_modules/
# Optional npm cache directory
.npm
npm-debug.log
# Optional eslint cache
.eslintcache
# vscode
/.vscode /.vscode
*.pid *.pid
/coverage
package-lock.json
*.html

View File

@ -5,7 +5,6 @@ const fs = require('fs');
const handlebars = require('handlebars'); const handlebars = require('handlebars');
const Crypto = require('./crypto/Crypto'); const Crypto = require('./crypto/Crypto');
const { date } = require('./utils.js'); const { date } = require('./utils.js');
// const BN = require('bn.js');
const registerHelpers = () => { const registerHelpers = () => {
handlebars.registerHelper('today', () => date.toISODate(Date.now(), false)); handlebars.registerHelper('today', () => date.toISODate(Date.now(), false));

View File

@ -1,9 +1,8 @@
'use strict'; 'use strict';
const packageJson = require('../package.json'); const { version } = require('../package.json');
const name = 'Node Ebics Client'; const name = 'Node Ebics Client';
const { version } = packageJson;
const orderOperations = { const orderOperations = {
ini: 'INI', ini: 'INI',
upload: 'UPLOAD', upload: 'UPLOAD',

66
lib/crypto/BigNumber.js Normal file
View File

@ -0,0 +1,66 @@
'use strict';
const { jsbn: { BigInteger } } = require('node-forge');
class BigNumber {
constructor(value, radix = 10) {
if (value === null || value === undefined)
throw new Error('value is missing.');
this._n = new BigInteger(null);
if (value instanceof BigNumber)
this._n = value._n;
else if (value instanceof BigInteger)
this._n = value;
else if (typeof value === 'number')
this._n.fromInt(value);
else if (typeof value === 'string')
this._n.fromString(value, radix);
else if (Buffer.isBuffer(value))
this._n.fromString(value.toString('hex'), 16);
else if (Array.isArray(value))
this._n.fromString(Buffer.from(value).toString('hex'), 16);
else
throw new TypeError('Unsupported value type.');
}
toBEBuffer(length) {
const arr = this._n.toByteArray();
if (length === undefined)
return Buffer.from(arr);
if (arr.length > length)
throw new Error('Number out of range.');
while (arr.length < length)
arr.unshift(0);
return Buffer.from(arr);
}
toString(radix = 10) {
const result = this._n.toString(radix);
if (radix === 16)
return result.padStart(2, '0');
return result;
}
and(num) {
return new BigNumber(this._n.and(new BigNumber(num)._n));
}
mul(num) {
return new BigNumber(this._n.multiply(new BigNumber(num)._n));
}
mod(num) {
return new BigNumber(this._n.mod(new BigNumber(num)._n));
}
shrn(num) {
return new BigNumber(this._n.shiftRight(new BigNumber(num)._n));
}
}
module.exports = BigNumber;

View File

@ -2,15 +2,14 @@
const crypto = require('crypto'); const crypto = require('crypto');
const BN = require('bn.js'); const BigNumber = require('./BigNumber.js');
const mgf1 = require('./MGF1'); const mgf1 = require('./MGF1');
const modPow = (base, power, mod) => { const modPow = (base, power, mod) => {
let result = new BN(1); let result = new BigNumber(1);
while (power > 0) { while (power > 0) {
result = power.and(new BN(1)) == 1 ? (result.mul(base)).mod(mod) : result; // eslint-disable-line result = power.and(new BigNumber(1)) == 1 ? (result.mul(base)).mod(mod) : result; // eslint-disable-line
base = (base.mul(base)).mod(mod); base = (base.mul(base)).mod(mod);
power = power.shrn(1); power = power.shrn(1);
} }
@ -28,10 +27,13 @@ const emsaPSS = (msg, salt) => {
const dbMask = mgf1.generate(mTickHash, db.length); const dbMask = mgf1.generate(mTickHash, db.length);
const maskedDb = mgf1.xor(db, dbMask); const maskedDb = mgf1.xor(db, dbMask);
let maskedDbMsb = mgf1.rjust(new BN(maskedDb.slice(0, 1), 2).toString(2), 8, '0'); let maskedDbMsb = mgf1.rjust(new BigNumber(maskedDb.slice(0, 1)).toString(2), 8, '0');
maskedDbMsb = `0${maskedDbMsb.substr(1)}`; maskedDbMsb = `0${maskedDbMsb.substr(1)}`;
maskedDb[0] = (new BN(maskedDbMsb, 2).toBuffer())[0]; // eslint-disable-line // console.log((new BN(maskedDbMsb, 2).toBuffer())[0], new BigNumber(maskedDbMsb, 2).toBuffer()[0]);
// maskedDb[0] = (new BN(maskedDbMsb, 2).toBuffer())[0]; // eslint-disable-line
maskedDb[0] = new BigNumber(maskedDbMsb, 2).toBEBuffer()[0]; // eslint-disable-line
return Buffer.concat([maskedDb, mTickHash, Buffer.from('BC', 'hex')]); return Buffer.concat([maskedDb, mTickHash, Buffer.from('BC', 'hex')]);
}; };
@ -65,11 +67,12 @@ module.exports = class Crypto {
} }
static sign(key, msg, salt = crypto.randomBytes(32)) { static sign(key, msg, salt = crypto.randomBytes(32)) {
const base = new BN(emsaPSS(msg, salt)); // console.log(key.d());
const power = new BN(key.d()); const base = new BigNumber(emsaPSS(msg, salt));
const mod = new BN(key.n()); const power = new BigNumber(key.d());
const mod = new BigNumber(key.n());
return (modPow(base, power, mod)).toBuffer().toString('base64'); return (modPow(base, power, mod)).toBEBuffer().toString('base64');
} }
static pad(d) { static pad(d) {

View File

@ -1,13 +1,13 @@
'use strict'; 'use strict';
const crypto = require('crypto'); const crypto = require('crypto');
const BN = require('bn.js');
const BigNumber = require('./BigNumber.js');
const MFG_LEN = 32; const MFG_LEN = 32;
const divceil = (a, b) => ~~(((a + b) - 1) / b); // eslint-disable-line no-bitwise const divceil = (a, b) => ~~(((a + b) - 1) / b); // eslint-disable-line no-bitwise
const rjust = (string, width, padding) => { const rjust = (string, width, padding = ' ') => {
padding = padding || ' ';
padding = padding.substr(0, 1); padding = padding.substr(0, 1);
if (string.length < width) if (string.length < width)
return padding.repeat(width - string.length) + string; return padding.repeat(width - string.length) + string;
@ -26,7 +26,7 @@ const i2osp = (x, len) => {
if (x >= 256 ** len) if (x >= 256 ** len)
throw new Error('Integer too large'); throw new Error('Integer too large');
return Buffer.from(rjust((Buffer.from((new BN(x)).toArray('be', 4)).toString()).replace(/\x00/gi, ''), len, '\x00')); // eslint-disable-line no-control-regex return Buffer.from(rjust(new BigNumber(x).toBEBuffer(4).toString().replace(/\x00/gi, ''), len, '\x00')); // eslint-disable-line no-control-regex
}; };
module.exports = { module.exports = {

3761
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -5,7 +5,7 @@
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"lint": "eslint .", "lint": "eslint .",
"test": "mocha test/**/*.js" "test": "nyc mocha test/**/*.js"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@ -19,6 +19,16 @@
], ],
"author": "eCollect Sofia Tech Team", "author": "eCollect Sofia Tech Team",
"contributors": [ "contributors": [
{
"name": "Dimitar Nanov",
"url": "https://nanov.io",
"email": "dimitar@nanov.io"
},
{
"name": "Vladislav Hristov",
"url": "https://github.com/vladhristov",
"email": "vlad.s.ch@gmail.com"
},
{ {
"name": "Vasyl Stashuk", "name": "Vasyl Stashuk",
"url": "https://github.com/vasyas" "url": "https://github.com/vasyas"
@ -28,9 +38,8 @@
"url": "https://github.com/yagop" "url": "https://github.com/yagop"
} }
], ],
"license": "GPLv3", "license": "GPL-3.0-only",
"dependencies": { "dependencies": {
"bn.js": "^5.0.0",
"handlebars": "^4.4.3", "handlebars": "^4.4.3",
"js2xmlparser": "^4.0.0", "js2xmlparser": "^4.0.0",
"node-forge": "^0.9.1", "node-forge": "^0.9.1",
@ -46,6 +55,7 @@
"eslint-config-ecollect-base": "^0.1.2", "eslint-config-ecollect-base": "^0.1.2",
"eslint-plugin-import": "^2.18.2", "eslint-plugin-import": "^2.18.2",
"libxmljs": "^0.19.7", "libxmljs": "^0.19.7",
"mocha": "^6.2.2" "mocha": "^6.2.2",
"nyc": "^14.1.1"
} }
} }

47
test/unit/BigNumber.js Normal file
View File

@ -0,0 +1,47 @@
'use strict';
/* eslint-env node, mocha */
const { assert } = require('chai');
const { jsbn: { BigInteger } } = require('node-forge');
const BigNumber = require('../../lib/crypto/BigNumber');
const types = [
{ name: 'BigNumber', value: new BigNumber(11) },
{ name: 'BigInteger', value: new BigInteger('11') },
{ name: 'string with default radix', value: '11' },
{ name: 'string with radix 16', value: '0b', radix: 16 },
{ name: 'Buffer', value: Buffer.from('0b', 'hex') },
{ name: 'Array', value: [11] },
];
describe('BigNumber', () => {
describe('creating an instance', () => {
it('should throw with no value given', () => assert.throws(() => new BigNumber()));
it('should throw wrong value type', () => assert.throws(() => new BigNumber({})));
for (const { name, value, radix } of types) {
let instance;
describe(`out of ${name}`, () => {
it('create instance', () => assert.doesNotThrow(() => {
instance = new BigNumber(value, radix);
}));
it('toString with radix 10', () => assert.equal(instance.toString(), '11'));
it('toString with radix 16', () => assert.equal(instance.toString(16), '0b'));
it('toBEBuffer without length', () => assert.equal(instance.toBEBuffer().toString('hex'), '0b'));
it('toBEBuffer with length', () => assert.equal(instance.toBEBuffer(4).toString('hex'), '0000000b'));
});
}
});
describe('exports', () => {
it('toBEBuffer with too short length should throw', () => assert.throw(() => new BigNumber(837462187362).toBEBuffer(1)));
});
describe('operators', () => {
const num = new BigNumber(1);
it('and', () => assert.equal(num.and(1), 1));
it('mul', () => assert.equal(num.mul(2), 2));
it('mod', () => assert.equal(num.mod(1), 0));
it('shrn', () => assert.equal(num.shrn(1), 0));
});
});