From 8492d940542f61b17aa3a2da7de23f6539ffaad5 Mon Sep 17 00:00:00 2001 From: AngelPashov Date: Fri, 1 Jun 2018 16:16:43 +0300 Subject: [PATCH] various modular fixes --- .eslintrc | 2 +- index.js | 3 +- lib/BankLetter.js | 89 ++++++-------- lib/Client.js | 202 +++++++++++++++++-------------- lib/Key.js | 85 +++++++------ lib/MGF1.js | 93 ++++++-------- lib/Response.js | 38 +++--- lib/Signer.js | 31 +++-- lib/consts.js | 10 +- lib/middleware/ParseResponse.js | 6 +- lib/middleware/XMLSign.js | 6 +- lib/orders/C52.js | 54 +++++---- lib/orders/GenericOrder.js | 167 ++++++++++++------------- lib/orders/GenericUploadOrder.js | 70 +++++------ lib/orders/HAA.js | 32 ++--- lib/orders/HAC.js | 46 +++---- lib/orders/HIA.js | 66 +++++----- lib/orders/HKD.js | 32 ++--- lib/orders/HPB.js | 24 ++-- lib/orders/HTD.js | 32 ++--- lib/orders/INI.js | 60 ++++----- package.json | 69 ++++++----- {lib => templates}/ini.hbs | 0 23 files changed, 605 insertions(+), 612 deletions(-) rename {lib => templates}/ini.hbs (100%) diff --git a/.eslintrc b/.eslintrc index 72b2042..6a9f7a9 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,5 +1,5 @@ { - "extends": "airbnb", + "extends": "airbnb-base", "env": { "node": true }, diff --git a/index.js b/index.js index 395b4e4..a5be567 100644 --- a/index.js +++ b/index.js @@ -1,4 +1,5 @@ 'use strict'; const Client = require('./lib/Client'); -module.exports = Client; \ No newline at end of file + +module.exports = Client; diff --git a/lib/BankLetter.js b/lib/BankLetter.js index aa3ae9f..31d5a98 100644 --- a/lib/BankLetter.js +++ b/lib/BankLetter.js @@ -1,66 +1,55 @@ 'use strict'; -const fs = require('fs'); - -const moment = require('moment'); +const moment = require('moment'); const handlebars = require('handlebars'); -const BN = require("bn.js"); +const BN = require('bn.js'); +const registerHelpers = () => { + handlebars.registerHelper('today', () => moment().format('DD.MM.YYYY')); + + handlebars.registerHelper('now', () => moment().format('HH:mm:ss')); + + handlebars.registerHelper('keyExponentBits', k => Buffer.byteLength(new BN(k.key.keyPair.e).toBuffer()) * 8); + + handlebars.registerHelper('keyModulusBits', k => k.key.getKeySize()); + // return Buffer.byteLength(new BN(k.key.keyPair.e).toBuffer()) * 8; + + handlebars.registerHelper('keyExponent', k => k.e()); + + handlebars.registerHelper('keyModulus', k => k.n().toUpperCase().match(/.{1,2}/g).join(' ')); + + handlebars.registerHelper('sha256', (k) => { + const digest = Buffer.from(k.publicDigest(), 'base64').toString('HEX'); + + return digest.toUpperCase().match(/.{1,2}/g).join(' '); + }); +}; module.exports = class BankLetter { - constructor(client, bankName) { - this.client = client; - this.bankName = bankName; - this.pathToTemplate = './app/ebics/ini.hbs'; - }; + constructor({ + client, + bankName, + template, + }) { + this.client = client; + this.bankName = bankName; + this.template = template; + } - _registerHelpers() { - handlebars.registerHelper("today", () => { - return moment().format('DD.MM.YYYY'); - }); - - handlebars.registerHelper("now", () => { - return moment().format('HH:mm:ss'); - }); - - handlebars.registerHelper("keyExponentBits", (k) => { - return Buffer.byteLength(new BN(k.key.keyPair.e).toBuffer()) * 8; - }); - - handlebars.registerHelper("keyModulusBits", (k) => { - return k.key.getKeySize(); - // return Buffer.byteLength(new BN(k.key.keyPair.e).toBuffer()) * 8; - }); - - handlebars.registerHelper("keyExponent", (k) => { - return k.e(); - }); - - handlebars.registerHelper("keyModulus", (k) => { - return k.n().toUpperCase().match(/.{1,2}/g).join(' '); - }); - - handlebars.registerHelper("sha256", (k) => { - const digest = Buffer.from(k.publicDigest(), 'base64').toString('HEX'); - - return digest.toUpperCase().match(/.{1,2}/g).join(' '); - }); - }; generate() { - this._registerHelpers(); + registerHelpers(); - const str = fs.readFileSync(this.pathToTemplate).toString(); - const templ = handlebars.compile(str); + const templ = handlebars.compile(this.template); const data = { - bankName : this.bankName, - userId : this.client.userId, + bankName: this.bankName, + userId: this.client.userId, partnerId: this.client.partnerId, - A006 : this.client.a(), - X002 : this.client.x(), - E002 : this.client.e(), + A006: this.client.a(), + X002: this.client.x(), + E002: this.client.e(), }; return templ(data); } -} +}; diff --git a/lib/Client.js b/lib/Client.js index da3e76c..7f1a2e4 100644 --- a/lib/Client.js +++ b/lib/Client.js @@ -1,26 +1,30 @@ 'use strict'; -const fs = require("fs"); -const crypto = require("crypto"); -const $request = require("request"); +const fs = require('fs'); +const path = require('path'); -const BN = require('bn.js'); -const xpath = require("xpath"); -const NodeRSA = require("node-rsa"); -const DOMParser = require("xmldom").DOMParser; +const crypto = require('crypto'); +const $request = require('request'); -const Key = require('./Key'); -const XMLSign = require('./middleware/XMLSign'); +const BN = require('bn.js'); +const xpath = require('xpath'); +const NodeRSA = require('node-rsa'); +const { DOMParser } = require('xmldom'); + +const Key = require('./Key'); +const XMLSign = require('./middleware/XMLSign'); const ParseResponse = require('./middleware/ParseResponse'); -const BankLetter = require('./BankLetter'); -const EBICSINI = require('./orders/INI'); -const EBICSHIA = require('./orders/HIA'); -const EBICSHPB = require('./orders/HPB'); -const EBICSHKD = require('./orders/HKD'); -const EBICSHAA = require('./orders/HAA'); -const EBICSHAC = require('./orders/HAC'); -const EBICSHTD = require('./orders/HTD'); -const EBICSC52 = require('./orders/C52'); +const BankLetter = require('./BankLetter'); +const EBICSINI = require('./orders/INI'); +const EBICSHIA = require('./orders/HIA'); +const EBICSHPB = require('./orders/HPB'); +const EBICSHKD = require('./orders/HKD'); +const EBICSHAA = require('./orders/HAA'); +const EBICSHAC = require('./orders/HAC'); +const EBICSHTD = require('./orders/HTD'); +const EBICSC52 = require('./orders/C52'); + +const defaultIniTemplatePath = path.join(__dirname, '../templates/ini.hbs'); const utils = { exponent: { @@ -31,31 +35,31 @@ const utils = { // str = AQAB => 65537 fromBase64(str) { return new BN(Buffer.from(str, 'base64'), 2).toNumber(); - } - } -} + }, + }, +}; module.exports = class Client { - constructor(keysContent, passphrase, url, hostId, userId, partnerId) { - this.keysContent = keysContent; - this.passphrase = passphrase; - this.url = url; - this.hostId = hostId; - this.userId = userId; - this.partnerId = partnerId; - this.encryptAlgorithm = 'aes-256-cbc'; - this.keys = keysContent ? this.extractKeys() : {}; - }; + constructor(keysContent, passphrase, url, hostId, userId, partnerId) { + this.keysContent = keysContent; + this.passphrase = passphrase; + this.url = url; + this.hostId = hostId; + this.userId = userId; + this.partnerId = partnerId; + this.encryptAlgorithm = 'aes-256-cbc'; + this.keys = keysContent ? this.extractKeys() : {}; + } - a() { - return this.keys["A006"]; - }; + a() { + return this.keys.A006; + } - e() { - return this.keys["E002"]; - }; + e() { + return this.keys.E002; + } - x() { - return this.keys["X002"]; + x() { + return this.keys.X002; } bankX() { @@ -67,62 +71,74 @@ module.exports = class Client { } encrypt(data) { - const cipher = crypto.createCipher(this.encryptAlgorithm, this.passphrase); + const cipher = crypto.createCipher(this.encryptAlgorithm, this.passphrase); const encrypted = cipher.update(data, 'utf8', 'hex') + cipher.final('hex'); return Buffer.from(encrypted).toString('base64'); - }; + } decrypt(data) { - data = (new Buffer(data, 'base64')).toString(); + data = (new Buffer(data, 'base64')).toString(); // eslint-disable-line no-buffer-constructor - const decipher = crypto.createDecipher(this.encryptAlgorithm, this.passphrase); + const decipher = crypto.createDecipher(this.encryptAlgorithm, this.passphrase); const decrypted = decipher.update(data, 'hex', 'utf8') + decipher.final('utf8'); return decrypted; - }; + } - static setup(passphrase, url, hostId, userId, partnerId, keysize = 2048) { + static setup(passphrase, url, hostId, userId, partnerId, keysize = 2048) { const client = new Client(null, passphrase, url, hostId, userId, partnerId); - for (let key in {A006: '', X002: '', E002: ''}) { - client.keys[key] = new Key(new NodeRSA({ b: keysize })); - } + Object.keys({ A006: '', X002: '', E002: '' }).forEach((key) => { + client.keys[key] = new Key(new NodeRSA({ b: keysize })); + }); + // for (const key in Object.keys({ A006: '', X002: '', E002: '' })) + // client.keys[key] = new Key(new NodeRSA({ b: keysize })); + return client; - }; + } - saveIniLetter(bankName, path) { - const letter = new BankLetter(this, bankName); + saveIniLetter(bankName, target, template) { + const letter = new BankLetter({ + client: this, + bankName, + template: template || fs.readFileSync(defaultIniTemplatePath, { encoding: 'utf8' }), + }); try { - fs.writeFileSync(path, letter.generate()); + fs.writeFileSync(target, letter.generate()); } catch (error) { throw error; } - }; + } - saveKeys(path) { - const data = {}; + saveKeys(target) { + const data = {}; - for (let key in this.keys) { - data[key] = this.encrypt(this.keys[key].toPem()); - }; + Object.keys(this.keys).forEach((key) => { + data[key] = this.encrypt(this.keys[key].toPem()); + }); + // for (const key in this.keys) + // data[key] = this.encrypt(this.keys[key].toPem()); try { - fs.writeFileSync(path, JSON.stringify(data)); - } catch(error) { + fs.writeFileSync(target, JSON.stringify(data)); + } catch (error) { throw error; } - }; + } extractKeys() { - const keys = {}; + const keys = {}; const jsonData = JSON.parse(this.keysContent); - for (let key in jsonData) { + Object.keys(jsonData).forEach((key) => { keys[key] = new Key(this.decrypt(jsonData[key])); - } + }); + // for (const key in jsonData) + // keys[key] = new Key(this.decrypt(jsonData[key])); + return keys; } @@ -130,73 +146,71 @@ module.exports = class Client { async download(order) { const res = await this.ebicsRequest(order.toXML()); - const ttt = res.toXML(); // keep this for debugging purposes + const ttt = res.toXML(); // TODO: keep this for debugging purposes order.transactionId = res.transactionId(); if (res.isSegmented() && res.isLastSegment()) { const receipt = await this.ebicsRequest(order.toReceiptXML()); - const receiptXML = order.toReceiptXML(); // keep this for debugging purposes - const rX = receipt.toXML(); // keep this for debugging purposes + const receiptXML = order.toReceiptXML(); // TODO: keep this for debugging purposes + const rX = receipt.toXML(); // TODO: keep this for debugging purposes } return res.orderData(); - }; + } async upload(order) { let res = await this.ebicsRequest(order.toXML()); order.transactionId = res.transactionId(); - const orderId = res.orderId(); + // const orderId = res.orderId(); res = await this.ebicsRequest(order.toTransferXML()); return res.transactionId(); } - async downloadAndUnzip(order) { + async downloadAndUnzip(order) { // eslint-disable-line } ebicsRequest(order) { return new Promise((resolve, reject) => { $request.post({ - url : this.url, - body : XMLSign.go(this, order), - headers: { 'content-type': 'text/xml;charset=UTF-8' } - }, (err, res, data) => { - return err ? reject(err): resolve(ParseResponse.go(this, data)); - }); + url: this.url, + body: XMLSign.go(this, order), + headers: { 'content-type': 'text/xml;charset=UTF-8' }, + }, (err, res, data) => (err ? reject(err) : resolve(ParseResponse.go(this, data)))); }); - }; + } - async INI() { + async INI() { return this.ebicsRequest((new EBICSINI(this)).toXML()); - }; + } - async HIA() { + async HIA() { return this.ebicsRequest((new EBICSHIA(this)).toXML()); - }; + } async HPB() { const data = await this.download(new EBICSHPB(this)); - const doc = new DOMParser().parseFromString(data, 'text/xml'); - const sel = xpath.useNamespaces({'xmlns': "urn:org:ebics:H004"}); - const keyNodes = sel("//xmlns:PubKeyValue", doc); + const doc = new DOMParser().parseFromString(data, 'text/xml'); + const sel = xpath.useNamespaces({ xmlns: 'urn:org:ebics:H004' }); + const keyNodes = sel('//xmlns:PubKeyValue', doc); // console.log(keyNodes); - function xmlLastChild (node) { + function xmlLastChild(node) { let y = node.lastChild; - while (y.nodeType != 1) y = y.previousSibling; + while (y.nodeType !== 1) y = y.previousSibling; return y; - }; + } for (let i = 0; i < keyNodes.length; i++) { - const type = xmlLastChild(keyNodes[i].parentNode).textContent; - const modulus = xpath.select("//*[local-name(.)='Modulus']", keyNodes[i])[0].textContent; + const type = xmlLastChild(keyNodes[i].parentNode).textContent; + const modulus = xpath.select("//*[local-name(.)='Modulus']", keyNodes[i])[0].textContent; const exponent = xpath.select("//*[local-name(.)='Exponent']", keyNodes[i])[0].textContent; const mod = new BN(Buffer.from(modulus, 'base64'), 2).toBuffer(); @@ -210,23 +224,23 @@ module.exports = class Client { } return [this.bankX(), this.bankE()]; - }; + } HKD() { return this.download(new EBICSHKD(this)); - }; + } HAA() { return this.download(new EBICSHAA(this)); - }; + } HTD() { return this.download(new EBICSHTD(this)); - }; + } HAC(from = null, to = null) { return this.download(new EBICSHAC(this, from, to)); - }; + } C52(from, to) { return this.downloadAndUnzip(new EBICSC52(this, from, to)); diff --git a/lib/Key.js b/lib/Key.js index 7f44565..78c6648 100644 --- a/lib/Key.js +++ b/lib/Key.js @@ -2,78 +2,75 @@ const crypto = require('crypto'); -const BN = require('bn.js'); -const NodeRSA = require("node-rsa"); +const BN = require('bn.js'); +const NodeRSA = require('node-rsa'); -const MGF1 = require('./MGF1'); +const mgf1 = require('./MGF1'); module.exports = class Key { - constructor(encodedKey, passphrase = null) { - if (encodedKey instanceof NodeRSA) { - this.key = encodedKey - } else { - this.key = new NodeRSA(encodedKey); - } - }; - - publicDigest() { - const str = [this.e().replace(/^(0+)/g, ''), this.n().replace(/^(0+)/g, '')].map((x) => x.toLowerCase()).join(' '); - - return crypto.createHash('sha256').update(str).digest('base64').trim(); - }; - - publicEncrypt(buf) { - return crypto.publicEncrypt({ 'key': this.toPem(), padding: crypto.constants.RSA_PKCS1_PADDING }, buf); + constructor(encodedKey/* , passphrase = null */) { + this.key = (encodedKey instanceof NodeRSA) ? encodedKey : new NodeRSA(encodedKey); } - n() { - return this.key.exportKey("components-public").n.toString("hex", 1); - }; + publicDigest() { + const str = [this.e().replace(/^(0+)/g, ''), this.n().replace(/^(0+)/g, '')].map(x => x.toLowerCase()).join(' '); + + return crypto.createHash('sha256').update(str).digest('base64').trim(); + } + + publicEncrypt(buf) { + return crypto.publicEncrypt({ + key: this.toPem(), + padding: crypto.constants.RSA_PKCS1_PADDING, + }, buf); + } + + n() { + return this.key.exportKey('components-public').n.toString('hex', 1); + } e() { - return new BN(this.key.exportKey("components-public").e).toBuffer().toString('hex'); - }; + return new BN(this.key.exportKey('components-public').e).toBuffer().toString('hex'); + } toPem() { - return this.key.isPrivate() ? this.key.exportKey("pkcs1-private-pem") : this.key.exportKey("pkcs8-public-pem"); + return this.key.isPrivate() ? this.key.exportKey('pkcs1-private-pem') : this.key.exportKey('pkcs8-public-pem'); } sign(msg, salt = crypto.randomBytes(32)) { - const base = new BN(this._emsaPSS(msg, salt)); + const base = new BN(Key._emsaPSS(msg, salt)); const power = new BN(this.key.keyPair.d.toBuffer()); - const mod = new BN(this.key.keyPair.n.toBuffer()); + const mod = new BN(this.key.keyPair.n.toBuffer()); - return (this._modPow(base, power, mod)).toBuffer().toString('base64'); + return (Key._modPow(base, power, mod)).toBuffer().toString('base64'); } - _emsaPSS(msg, salt) { - const eightNullBytes = Buffer.from("\x00".repeat(8)); - const digestedMsg = crypto.createHash('sha256').update(msg).digest(); - const mTickHash = crypto.createHash('sha256').update(Buffer.concat([eightNullBytes, digestedMsg, salt]), 'binary').digest(); + static _emsaPSS(msg, salt) { + const eightNullBytes = Buffer.from('\x00'.repeat(8)); + const digestedMsg = crypto.createHash('sha256').update(msg).digest(); + const mTickHash = crypto.createHash('sha256').update(Buffer.concat([eightNullBytes, digestedMsg, salt]), 'binary').digest(); - const ps = Buffer.from("\x00".repeat(190)); - const db = Buffer.concat([ps, Buffer.from("\x01"), salt]); + const ps = Buffer.from('\x00'.repeat(190)); + const db = Buffer.concat([ps, Buffer.from('\x01'), salt]); - const dbMask = MGF1.generate(mTickHash, db.length); - const maskedDb = MGF1.xor(db, dbMask); // so far so good + const dbMask = mgf1.generate(mTickHash, db.length); + const maskedDb = mgf1.xor(db, dbMask); // so far so good - let maskedDbMsb = (new MGF1)._rjust(new BN(maskedDb.slice(0, 1), 2).toString(2), 8, "0"); - maskedDbMsb = "0" + maskedDbMsb.substr(1); + let maskedDbMsb = mgf1.rjust(new BN(maskedDb.slice(0, 1), 2).toString(2), 8, '0'); - maskedDb[0] = (new BN(maskedDbMsb, 2).toBuffer())[0]; + maskedDbMsb = `0${maskedDbMsb.substr(1)}`; + maskedDb[0] = (new BN(maskedDbMsb, 2).toBuffer())[0]; // eslint-disable-line return Buffer.concat([maskedDb, mTickHash, Buffer.from('BC', 'hex')]); } - _modPow(base, power, mod) { + static _modPow(base, power, mod) { let result = new BN(1); - - while( power > 0 ) { - result = power.and(new BN(1)) == 1 ? (result.mul(base)).mod(mod) : result; + while (power > 0) { + result = power.and(new BN(1)) === 1 ? (result.mul(base)).mod(mod) : result; base = (base.mul(base)).mod(mod); power = power.shrn(1); } - return result; } }; diff --git a/lib/MGF1.js b/lib/MGF1.js index e968b36..d68a691 100644 --- a/lib/MGF1.js +++ b/lib/MGF1.js @@ -1,67 +1,48 @@ -'use strict' +'use strict'; -const crypto = require("crypto"); -const BN = require('bn.js'); +const crypto = require('crypto'); +const BN = require('bn.js'); -module.exports = class MGF1 { +const MFG_LEN = 32; - constructor() { - this._len = 32; - } +const divceil = (a, b) => ~~(((a + b) - 1) / b); // eslint-disable-line no-bitwise +const rjust = (string, width, padding) => { + padding = padding || ' '; + padding = padding.substr(0, 1); + if (string.length < width) + return padding.repeat(width - string.length) + string; + return string; +}; +const xor = (a, b) => { + if (a.length !== b.length) + throw new Error('Different length for a and b'); - /** - * - * @param {Buffer} seed - * @param {Number} masklen - * - * @returns Buffer - */ - static generate(seed, masklen) { - const mgf1 = new MGF1(); + for (let i = 0; i < a.length; i++) + a[i] ^= b[i]; // eslint-disable-line no-bitwise - if ( masklen > 4294967296 * this._len) { + return a; +}; +const i2osp = (x, len) => { + if (x >= 256 ** len) + 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 +}; + +module.exports = { + generate(seed, masklen) { + if (masklen > 4294967296 * MFG_LEN) throw new Error('Mask too long'); - } + const b = []; - for (let i = 0; i < mgf1._divceil(masklen, mgf1._len); i++) { - b[i] = crypto.createHash('sha256').update(Buffer.concat([seed, mgf1._i2osp(i, 4)])).digest(); - } + for (let i = 0; i < divceil(masklen, MFG_LEN); i++) + b[i] = crypto.createHash('sha256').update(Buffer.concat([seed, i2osp(i, 4)])).digest(); + return (Buffer.concat(b)).slice(0, masklen); - } - - static xor(a, b) { - if ( a.length != b.length ) { - throw new Error('Different length for a and b'); - } - - for ( let i = 0; i < a.length; i++ ) { - a[i] ^= b[i]; - } - - return a; - } - - _divceil(a, b) { - return ~~((a + b - 1) / b); - } - - _i2osp(x, len) { - if ( x >= 256 ** len ) { - throw new Error('Integer too large'); - } - - return Buffer.from(this._rjust( (Buffer.from((new BN(x)).toArray('be', 4)).toString()).replace(/\x00/gi, ''), len, "\x00" )); - } - - _rjust( string, width, padding ) { - padding = padding || " "; - padding = padding.substr( 0, 1 ); - if ( string.length < width ) - return padding.repeat( width - string.length ) + string; - else - return string; - } -} + }, + xor, + rjust, +}; diff --git a/lib/Response.js b/lib/Response.js index 325c304..dfbe08d 100644 --- a/lib/Response.js +++ b/lib/Response.js @@ -1,52 +1,52 @@ 'use strict'; -const zlib = require('zlib'); -const crypto = require("crypto"); +const zlib = require('zlib'); +const crypto = require('crypto'); -const DOMParser = require("xmldom").DOMParser; -const XMLSerializer = require("xmldom").XMLSerializer; -const xpath = require("xpath"); +const { DOMParser, XMLSerializer } = require('xmldom'); +const xpath = require('xpath'); +const DEFAULT_IV = Buffer.from(Array(16).fill(0, 0, 15)); module.exports = class Response { constructor(client, data) { this.client = client; - this.doc = new DOMParser().parseFromString(data, 'text/xml'); - }; + this.doc = new DOMParser().parseFromString(data, 'text/xml'); + } isSegmented() { - const select = xpath.useNamespaces({'xmlns': "urn:org:ebics:H004"}); - const node = select("//xmlns:header/xmlns:mutable/xmlns:SegmentNumber", this.doc); + const select = xpath.useNamespaces({ xmlns: 'urn:org:ebics:H004' }); + const node = select('//xmlns:header/xmlns:mutable/xmlns:SegmentNumber', this.doc); - return node.length ? true: false; + return !!node.length; } isLastSegment() { - const select = xpath.useNamespaces({'xmlns': "urn:org:ebics:H004"}); + const select = xpath.useNamespaces({ xmlns: 'urn:org:ebics:H004' }); const node = select("//xmlns:header/xmlns:mutable/*[@lastSegment='true']", this.doc); - return node.length ? true: false; + return !!node.length; } orderData() { - const orderData = this.doc.getElementsByTagNameNS("urn:org:ebics:H004", "OrderData")[0].textContent; - const decipher = crypto.createDecipheriv('aes-128-cbc', this.transactionKey(), Buffer.from([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,])).setAutoPadding(false); - const data = Buffer.from(decipher.update(orderData, 'base64', 'binary') + decipher.final('binary'), 'binary'); + const orderData = this.doc.getElementsByTagNameNS('urn:org:ebics:H004', 'OrderData')[0].textContent; + const decipher = crypto.createDecipheriv('aes-128-cbc', this.transactionKey(), DEFAULT_IV).setAutoPadding(false); + const data = Buffer.from(decipher.update(orderData, 'base64', 'binary') + decipher.final('binary'), 'binary'); return zlib.inflateSync(data).toString(); } transactionKey() { - const keyNodeText = this.doc.getElementsByTagNameNS("urn:org:ebics:H004", "TransactionKey")[0].textContent; + const keyNodeText = this.doc.getElementsByTagNameNS('urn:org:ebics:H004', 'TransactionKey')[0].textContent; const tkEncrypted = Buffer.from(keyNodeText, 'base64'); - this.client.e().key.setOptions({encryptionScheme: 'pkcs1'}); + this.client.e().key.setOptions({ encryptionScheme: 'pkcs1' }); return this.client.e().key.decrypt(tkEncrypted); } transactionId() { - const select = xpath.useNamespaces({'xmlns': "urn:org:ebics:H004"}); - const node = select("//xmlns:header/xmlns:static/xmlns:TransactionID", this.doc); + const select = xpath.useNamespaces({ xmlns: 'urn:org:ebics:H004' }); + const node = select('//xmlns:header/xmlns:static/xmlns:TransactionID', this.doc); return node.length ? node[0].textContent : ''; } diff --git a/lib/Signer.js b/lib/Signer.js index 7cb4ec3..21cccfc 100644 --- a/lib/Signer.js +++ b/lib/Signer.js @@ -1,11 +1,10 @@ 'use strict'; -const crypto = require("crypto"); +const crypto = require('crypto'); -const DOMParser = require("xmldom").DOMParser; -const XMLSerializer = require("xmldom").XMLSerializer; -const select = require("xpath.js"); -const c14n = require('xml-crypto/lib/c14n-canonicalization').C14nCanonicalization; +const { DOMParser, XMLSerializer } = require('xmldom'); +const select = require('xpath.js'); +const C14n = require('xml-crypto/lib/c14n-canonicalization').C14nCanonicalization; module.exports = class Signer { @@ -67,30 +66,29 @@ module.exports = class Signer { digest() { // get the xml node, where the digested value is supposed to be - const nodeDigestValue = this.doc.getElementsByTagName("ds:DigestValue")[0]; + const nodeDigestValue = this.doc.getElementsByTagName('ds:DigestValue')[0]; - const nodes = select(this.doc, "//*[@authenticate='true']"); + // const nodes = select(this.doc, "//*[@authenticate='true']"); // canonicalize the node that has authenticate='true' attribute + // const contentToDigest = select(this.doc, '//*[@authenticate="true"]') const contentToDigest = select(this.doc, "//*[@authenticate='true']") - .map(x => { - const aaaa = x.toString(); - return new c14n().process(x) - }).join(""); + .map(x => new C14n().process(x)).join(''); + console.log('digest', 'contentToDigest', contentToDigest); // fix the canonicalization const fixedContent = contentToDigest.replace(/xmlns="urn:org:ebics:H004"/g, 'xmlns="urn:org:ebics:H004" xmlns:ds="http://www.w3.org/2000/09/xmldsig#"'); - if (nodeDigestValue) { + if (nodeDigestValue) nodeDigestValue.textContent = crypto.createHash('sha256').update(fixedContent).digest('base64').trim(); - } - }; + } sign() { - const nodeSignatureValue = this.doc.getElementsByTagName("ds:SignatureValue")[0]; + const nodeSignatureValue = this.doc.getElementsByTagName('ds:SignatureValue')[0]; + console.log('sign =>'); if (nodeSignatureValue) { - const contentToSign = (new c14n().process(select(this.doc, "//ds:SignedInfo")[0])).replace('xmlns:ds="http://www.w3.org/2000/09/xmldsig#"', 'xmlns="urn:org:ebics:H004" xmlns:ds="http://www.w3.org/2000/09/xmldsig#"'); + const contentToSign = (new C14n().process(select(this.doc, '//ds:SignedInfo')[0])).replace('xmlns:ds="http://www.w3.org/2000/09/xmldsig#"', 'xmlns="urn:org:ebics:H004" xmlns:ds="http://www.w3.org/2000/09/xmldsig#"'); nodeSignatureValue.textContent = this.client.x().key.sign(contentToSign, 'base64'); } @@ -99,5 +97,4 @@ module.exports = class Signer { toXML() { return new XMLSerializer().serializeToString(this.doc); } - }; diff --git a/lib/consts.js b/lib/consts.js index 50fdd0d..e9bb0db 100644 --- a/lib/consts.js +++ b/lib/consts.js @@ -3,10 +3,10 @@ const packageJson = require('../package.json'); const name = 'eCollect Node Ebics Client'; -const version = packageJson.version; +const { version } = packageJson; module.exports = { - name, - version, - productString: `${name} ${version}`, -}; \ No newline at end of file + name, + version, + productString: `${name} ${version}`, +}; diff --git a/lib/middleware/ParseResponse.js b/lib/middleware/ParseResponse.js index fc20336..51494af 100644 --- a/lib/middleware/ParseResponse.js +++ b/lib/middleware/ParseResponse.js @@ -6,10 +6,10 @@ module.exports = class ParseResponse { constructor(client, data) { this.client = client; this.data = data; - }; + } - static go (client, data) { - const parseRensponse = new ParseResponse(client, data); + static go(client, data) { + // const parseRensponse = new ParseResponse(client, data); const response = new Response(client, data); // TODO: diff --git a/lib/middleware/XMLSign.js b/lib/middleware/XMLSign.js index c88abfe..f40955e 100644 --- a/lib/middleware/XMLSign.js +++ b/lib/middleware/XMLSign.js @@ -6,10 +6,10 @@ module.exports = class XMLSign { constructor(client, data) { this.client = client; this.data = data; - }; + } - static go (client, data) { - const xmlSigner = new XMLSign(client, data); + static go(client, data) { + // const xmlSigner = new XMLSign(client, data); const signer = new Signer(client, data); signer.digest(); diff --git a/lib/orders/C52.js b/lib/orders/C52.js index 05ab511..965358f 100644 --- a/lib/orders/C52.js +++ b/lib/orders/C52.js @@ -3,48 +3,58 @@ const GenericOrder = require('./GenericOrder'); module.exports = class C52 extends GenericOrder { - constructor (client, from, to) { + constructor(client, from, to) { super(client); this._from = from; - this._to = to; + this._to = to; this._schema.header = { - "@" : { authenticate: true }, + '@': { + authenticate: true, + }, static: { - HostID : this.hostId, - Nonce : this.nonce(), + HostID: this.hostId, + Nonce: this.nonce(), Timestamp: this.timestamp(), PartnerID: this.partnerId, - UserID : this.userId, - Product : { - "@": { Language: "de" }, - "#": this.productString, + UserID: this.userId, + Product: { + '@': { + Language: 'de', + }, + '#': this.productString, }, OrderDetails: { - OrderType : "C52", - OrderAttribute : "DZHNN", + OrderType: 'C52', + OrderAttribute: 'DZHNN', StandardOrderParams: { DateRange: { Start: this._from, - End : this._to - } + End: this._to, + }, }, }, BankPubKeyDigests: { Authentication: { - "@": { Version: "X002", Algorithm: "http://www.w3.org/2001/04/xmlenc#sha256" }, - "#": this.client.bankX().publicDigest() + '@': { + Version: 'X002', + Algorithm: 'http://www.w3.org/2001/04/xmlenc#sha256', + }, + '#': this.client.bankX().publicDigest(), }, Encryption: { - "@": { Version: "E002", Algorithm: "http://www.w3.org/2001/04/xmlenc#sha256" }, - "#": this.client.bankE().publicDigest() - } + '@': { + Version: 'E002', + Algorithm: 'http://www.w3.org/2001/04/xmlenc#sha256', + }, + '#': this.client.bankE().publicDigest(), + }, }, - SecurityMedium: "0000" + SecurityMedium: '0000', }, mutable: { - TransactionPhase: "Initialisation" - } + TransactionPhase: 'Initialisation', + }, }; - }; + } }; diff --git a/lib/orders/GenericOrder.js b/lib/orders/GenericOrder.js index b108102..e34cb76 100644 --- a/lib/orders/GenericOrder.js +++ b/lib/orders/GenericOrder.js @@ -1,10 +1,47 @@ 'use strict'; // const randHex = require('../../lib/utils').randHex; -const crypto = require("crypto"); +const crypto = require('crypto'); const js2xmlparser = require('js2xmlparser'); -const consts = require('../consts'); +const consts = require('../consts'); +const authSignature = () => { + return { + 'ds:SignedInfo': { + 'ds:CanonicalizationMethod': { + '@': { + Algorithm: + 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315', + }, + }, + 'ds:SignatureMethod': { + '@': { + Algorithm: + 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256', + }, + }, + 'ds:Reference': { + '@': { URI: "#xpointer(//*[@authenticate='true'])" }, + 'ds:Transforms': { + 'ds:Transform': { + '@': { + Algorithm: + 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315', + }, + }, + }, + 'ds:DigestMethod': { + '@': { + Algorithm: + 'http://www.w3.org/2001/04/xmlenc#sha256', + }, + }, + 'ds:DigestValue': {}, + }, + }, + 'ds:SignatureValue': {}, + }; +}; module.exports = class GenericOrder { constructor(client) { this.client = client; @@ -18,67 +55,29 @@ module.exports = class GenericOrder { this.xmlOptions = { declaration: { include: true, - encoding: "utf-8" + encoding: 'utf-8', }, format: { doubleQuotes: true, indent: '', newline: '', - pretty: true - } + pretty: true, + }, }; this._schema = { - "@": { - "xmlns:ds": "http://www.w3.org/2000/09/xmldsig#", - xmlns: "urn:org:ebics:H004", - Version: "H004", - Revision: "1" + '@': { + 'xmlns:ds': 'http://www.w3.org/2000/09/xmldsig#', + xmlns: 'urn:org:ebics:H004', + Version: 'H004', + Revision: '1', }, header: {}, - AuthSignature: this.authSignature(), + AuthSignature: authSignature(), - body: {} - }; - } - - authSignature() { - return { - "ds:SignedInfo": { - "ds:CanonicalizationMethod": { - "@": { - Algorithm: - "http://www.w3.org/TR/2001/REC-xml-c14n-20010315" - } - }, - "ds:SignatureMethod": { - "@": { - Algorithm: - "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256" - } - }, - "ds:Reference": { - "@": { URI: "#xpointer(//*[@authenticate='true'])" }, - "ds:Transforms": { - "ds:Transform": { - "@": { - Algorithm: - "http://www.w3.org/TR/2001/REC-xml-c14n-20010315" - } - } - }, - "ds:DigestMethod": { - "@": { - Algorithm: - "http://www.w3.org/2001/04/xmlenc#sha256" - } - }, - "ds:DigestValue": {} - } - }, - "ds:SignatureValue": {} + body: {}, }; } @@ -86,92 +85,94 @@ module.exports = class GenericOrder { return this._schema; } - get productString() { + static get productString() { return consts.productString; } - nonce() { + static nonce() { return crypto.randomBytes(16).toString('hex'); } - timestamp() { + // TODO: remove eslint-disable-line + timestamp() { // eslint-disable-line return new Date().toISOString(); } - root() { - return "ebicsRequest"; + root() { // eslint-disable-line class-methods-use-this + return 'ebicsRequest'; } toReceiptXML() { const xmlObj = { - "@": { - "xmlns:ds": "http://www.w3.org/2000/09/xmldsig#", - xmlns: "urn:org:ebics:H004", - Version: "H004", - Revision: "1" + '@': { + 'xmlns:ds': 'http://www.w3.org/2000/09/xmldsig#', + xmlns: 'urn:org:ebics:H004', + Version: 'H004', + Revision: '1', }, header: { - "@": { authenticate: true }, + '@': { authenticate: true }, static: { HostID: this.hostId, - TransactionID: this.transactionId + TransactionID: this.transactionId, }, mutable: { TransactionPhase: 'Receipt', - } + }, }, AuthSignature: this.authSignature(), body: { TransferReceipt: { - "@": { authenticate: true }, - ReceiptCode: 0 - } - } + '@': { authenticate: true }, + ReceiptCode: 0, + }, + }, }; return js2xmlparser.parse(this.root(), xmlObj, this.xmlOptions); } - toTransferXML(){ + toTransferXML() { const xmlObj = { - "@": { - "xmlns:ds": "http://www.w3.org/2000/09/xmldsig#", - xmlns: "urn:org:ebics:H004", - Version: "H004", - Revision: "1" + '@': { + 'xmlns:ds': 'http://www.w3.org/2000/09/xmldsig#', + xmlns: 'urn:org:ebics:H004', + Version: 'H004', + Revision: '1', }, header: { - "@": { authenticate: true }, + '@': { authenticate: true }, static: { HostID: this.hostId, - TransactionID: this.transactionId + TransactionID: this.transactionId, }, mutable: { TransactionPhase: 'Transfer', SegmentNumber: { - "@": { lastSegment: true }, - "#": 1 - } - } + '@': { lastSegment: true }, + '#': 1, + }, + }, }, AuthSignature: this.authSignature(), body: { DataTransfer: { - OrderData: this.encryptedOrderData() - } - } + OrderData: this.encryptedOrderData(), + }, + }, }; return js2xmlparser.parse(this.root(), xmlObj, this.xmlOptions); } - encryptedOrderData() { + encryptedOrderData() { // eslint-disable-line class-methods-use-this + return null; } toXML() { diff --git a/lib/orders/GenericUploadOrder.js b/lib/orders/GenericUploadOrder.js index 353bbfa..eb55f64 100644 --- a/lib/orders/GenericUploadOrder.js +++ b/lib/orders/GenericUploadOrder.js @@ -1,79 +1,79 @@ 'use strict'; const zlib = require('zlib'); -const crypto = require("crypto"); +const crypto = require('crypto'); const js2xmlparser = require('js2xmlparser'); const GenericOrder = require('./GenericOrder'); +const pad = (d) => { + const dLen = d.length; + const len = 16 * (Math.trunc(dLen / 16) + 1); + + return Buffer.concat([d, Buffer.from(Buffer.from([0]).toString().repeat(len - dLen - 1)), Buffer.from([len - dLen])]); +}; + module.exports = class GenericUploadOrder extends GenericOrder { constructor(client, document) { super(client); - this._document = document; - this._key = crypto.randomBytes(16); + this._document = document; + this._key = crypto.randomBytes(16); this._schema.body = { DataTransfer: { DataEncryptionInfo: { - "@": { authenticate: true }, + '@': { authenticate: true }, EncryptionPubKeyDigest: { - "@": { Version: "E002", Algorithm: "http://www.w3.org/2001/04/xmlenc#sha256" }, - "#": this.client.bankE().publicDigest() + '@': { Version: 'E002', Algorithm: 'http://www.w3.org/2001/04/xmlenc#sha256' }, + '#': this.client.bankE().publicDigest(), }, TransactionKey: this.client.bankE().publicEncrypt(this._key).toString('base64'), }, SignatureData: { - "@": { authenticate: true }, - "#": this.encryptedOrderSignature() - } - } + '@': { authenticate: true }, + '#': this.encryptedOrderSignature(), + }, + }, }; - }; + } orderSignature() { const xmlObj = { - "@": { - xmlns: "http://www.ebics.org/S001", - "xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance", - "xsi:schemaLocation": "http://www.ebics.org/S001 http://www.ebics.org/S001/ebics_signature.xsd" + '@': { + xmlns: 'http://www.ebics.org/S001', + 'xmlns:xsi': 'http://www.w3.org/2001/XMLSchema-instance', + 'xsi:schemaLocation': 'http://www.ebics.org/S001 http://www.ebics.org/S001/ebics_signature.xsd', }, OrderSignatureData: { - SignatureVersion: "A006", + SignatureVersion: 'A006', SignatureValue: this.signatureValue(), PartnerID: this.partnerId, - UserID: this.userId - } + UserID: this.userId, + }, }; return js2xmlparser.parse('UserSignatureData', xmlObj, this.xmlOptions); - }; + } signatureValue() { - const digested = crypto.createHash('sha256').update(this._document.replace(/\n|\r/g, "")).digest(); + const digested = crypto.createHash('sha256').update(this._document.replace(/\n|\r/g, '')).digest(); return this.client.a().sign(digested); - }; + } encryptedOrderData() { - const dst = zlib.deflateSync(this._document.replace(/\r|\n/g, "")); - const cipher = crypto.createCipheriv('aes-128-cbc', this._key, Buffer.from([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,])).setAutoPadding(false); + const dst = zlib.deflateSync(this._document.replace(/\r|\n/g, '')); + const cipher = crypto.createCipheriv('aes-128-cbc', this._key, Buffer.from([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])).setAutoPadding(false); - return Buffer.concat([cipher.update(this._pad(dst)), cipher.final()]).toString('base64'); + return Buffer.concat([cipher.update(pad(dst)), cipher.final()]).toString('base64'); } - encryptedOrderSignature() { - const dst = zlib.deflateSync(this.orderSignature()); - const cipher = crypto.createCipheriv('aes-128-cbc', this._key, Buffer.from([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,])).setAutoPadding(false); + encryptedOrderSignature() { + const dst = zlib.deflateSync(this.orderSignature()); + const cipher = crypto.createCipheriv('aes-128-cbc', this._key, Buffer.from([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])).setAutoPadding(false); - return Buffer.concat([cipher.update(this._pad(dst)), cipher.final()]).toString('base64'); - }; - - _pad(d) { - const dLen = d.length; - const len = 16 * ( Math.trunc(dLen / 16) + 1 ); - - return Buffer.concat([d, Buffer.from(Buffer.from([0]).toString().repeat(len - dLen - 1)), Buffer.from([len-dLen])]); + return Buffer.concat([cipher.update(pad(dst)), cipher.final()]).toString('base64'); } }; diff --git a/lib/orders/HAA.js b/lib/orders/HAA.js index cb101b3..58ae7a6 100644 --- a/lib/orders/HAA.js +++ b/lib/orders/HAA.js @@ -3,11 +3,11 @@ const GenericOrder = require('./GenericOrder'); module.exports = class HAA extends GenericOrder { - constructor (client) { + constructor(client) { super(client); this._schema.header = { - "@": { authenticate: true }, + '@': { authenticate: true }, static: { HostID: this.hostId, Nonce: this.nonce(), @@ -15,29 +15,29 @@ module.exports = class HAA extends GenericOrder { PartnerID: this.partnerId, UserID: this.userId, Product: { - "@": { Language: "de" }, - "#": this.productString + '@': { Language: 'de' }, + '#': this.productString, }, OrderDetails: { - OrderType: "HAA", - OrderAttribute: "DZHNN", - StandardOrderParams: "" + OrderType: 'HAA', + OrderAttribute: 'DZHNN', + StandardOrderParams: '', }, BankPubKeyDigests: { Authentication: { - "@": { Version: "X002", Algorithm: "http://www.w3.org/2001/04/xmlenc#sha256" }, - "#": this.client.bankX().publicDigest() + '@': { Version: 'X002', Algorithm: 'http://www.w3.org/2001/04/xmlenc#sha256' }, + '#': this.client.bankX().publicDigest(), }, Encryption: { - "@": { Version: "E002", Algorithm: "http://www.w3.org/2001/04/xmlenc#sha256" }, - "#": this.client.bankE().publicDigest() - } + '@': { Version: 'E002', Algorithm: 'http://www.w3.org/2001/04/xmlenc#sha256' }, + '#': this.client.bankE().publicDigest(), + }, }, - SecurityMedium: "0000" + SecurityMedium: '0000', }, mutable: { - TransactionPhase: "Initialisation" - } + TransactionPhase: 'Initialisation', + }, }; - }; + } }; diff --git a/lib/orders/HAC.js b/lib/orders/HAC.js index 68f2e57..4b5770d 100644 --- a/lib/orders/HAC.js +++ b/lib/orders/HAC.js @@ -3,50 +3,50 @@ const GenericOrder = require('./GenericOrder'); module.exports = class HAC extends GenericOrder { - constructor (client, from = null, to = null) { + constructor(client, from = null, to = null) { super(client); this._from = from; - this._to = to; + this._to = to; this._schema.header = { - "@" : { authenticate: true }, + '@': { authenticate: true }, static: { - HostID : this.hostId, - Nonce : this.nonce(), + HostID: this.hostId, + Nonce: this.nonce(), Timestamp: this.timestamp(), PartnerID: this.partnerId, - UserID : this.userId, - Product : { - "@": { Language: "de" }, - "#": this.productString, + UserID: this.userId, + Product: { + '@': { Language: 'de' }, + '#': this.productString, }, OrderDetails: { - OrderType : "HAC", - OrderAttribute : "DZHNN", + OrderType: 'HAC', + OrderAttribute: 'DZHNN', StandardOrderParams: this._hasDateRange() ? { DateRange: { Start: this._from, - End : this._to - } - } : "" + End: this._to, + }, + } : '', }, BankPubKeyDigests: { Authentication: { - "@": { Version: "X002", Algorithm: "http://www.w3.org/2001/04/xmlenc#sha256" }, - "#": this.client.bankX().publicDigest() + '@': { Version: 'X002', Algorithm: 'http://www.w3.org/2001/04/xmlenc#sha256' }, + '#': this.client.bankX().publicDigest(), }, Encryption: { - "@": { Version: "E002", Algorithm: "http://www.w3.org/2001/04/xmlenc#sha256" }, - "#": this.client.bankE().publicDigest() - } + '@': { Version: 'E002', Algorithm: 'http://www.w3.org/2001/04/xmlenc#sha256' }, + '#': this.client.bankE().publicDigest(), + }, }, - SecurityMedium: "0000" + SecurityMedium: '0000', }, mutable: { - TransactionPhase: "Initialisation" - } + TransactionPhase: 'Initialisation', + }, }; - }; + } _hasDateRange() { return this._from && this._to; diff --git a/lib/orders/HIA.js b/lib/orders/HIA.js index b3866fb..dfe9d26 100644 --- a/lib/orders/HIA.js +++ b/lib/orders/HIA.js @@ -10,76 +10,76 @@ module.exports = class HIA extends GenericOrder { super(client); this._schema = { - "@": { - "xmlns:ds": "http://www.w3.org/2000/09/xmldsig#", - xmlns: "urn:org:ebics:H004", - Version: "H004", - Revision: "1" + '@': { + 'xmlns:ds': 'http://www.w3.org/2000/09/xmldsig#', + xmlns: 'urn:org:ebics:H004', + Version: 'H004', + Revision: '1', }, header: { - "@": { authenticate: true }, + '@': { authenticate: true }, static: { HostID: this.hostId, PartnerID: this.partnerId, UserID: this.userId, Product: { - "@": { Language: "de" }, - "#": this.productString, + '@': { Language: 'de' }, + '#': this.productString, }, OrderDetails: { - OrderType: "HIA", - OrderAttribute: "DZNNN" + OrderType: 'HIA', + OrderAttribute: 'DZNNN', }, - SecurityMedium: "0000" + SecurityMedium: '0000', }, - mutable: {} + mutable: {}, }, body: { DataTransfer: { - OrderData: Buffer.from(zlib.deflateSync(this.orderData())).toString('base64') - } - } + OrderData: Buffer.from(zlib.deflateSync(this.orderData())).toString('base64'), + }, + }, }; } - root() { - return "ebicsUnsecuredRequest"; - }; + root() { // eslint-disable-line class-methods-use-this + return 'ebicsUnsecuredRequest'; + } orderData() { const xmlOrderData = { - "@": { - "xmlns:ds": "http://www.w3.org/2000/09/xmldsig#", - xmlns: "urn:org:ebics:H004" + '@': { + 'xmlns:ds': 'http://www.w3.org/2000/09/xmldsig#', + xmlns: 'urn:org:ebics:H004', }, AuthenticationPubKeyInfo: { PubKeyValue: { - "ds:RSAKeyValue": { - "ds:Modulus": Buffer.from(this.client.x().n(), 'HEX').toString('base64'), - "ds:Exponent": "AQAB" + 'ds:RSAKeyValue': { + 'ds:Modulus': Buffer.from(this.client.x().n(), 'HEX').toString('base64'), + 'ds:Exponent': 'AQAB', }, }, - AuthenticationVersion: "X002" + AuthenticationVersion: 'X002', }, EncryptionPubKeyInfo: { PubKeyValue: { - "ds:RSAKeyValue": { - "ds:Modulus": Buffer.from(this.client.e().n(), 'HEX').toString('base64'), - "ds:Exponent": "AQAB" + 'ds:RSAKeyValue': { + 'ds:Modulus': Buffer.from(this.client.e().n(), 'HEX').toString('base64'), + 'ds:Exponent': 'AQAB', }, }, - EncryptionVersion: "E002" + EncryptionVersion: 'E002', }, PartnerID: this.partnerId, - UserID: this.userId + UserID: this.userId, }; - return js2xmlparser.parse("HIARequestOrderData", xmlOrderData, this.xmlOptions); - }; + return js2xmlparser.parse('HIARequestOrderData', xmlOrderData, this.xmlOptions); + } toXML() { return js2xmlparser.parse(this.root(), this._schema, this.xmlOptions); - }; + } }; diff --git a/lib/orders/HKD.js b/lib/orders/HKD.js index d33acab..f03d5d0 100644 --- a/lib/orders/HKD.js +++ b/lib/orders/HKD.js @@ -3,11 +3,11 @@ const GenericOrder = require('./GenericOrder'); module.exports = class HKD extends GenericOrder { - constructor (client) { + constructor(client) { super(client); this._schema.header = { - "@": { authenticate: true }, + '@': { authenticate: true }, static: { HostID: this.hostId, Nonce: this.nonce(), @@ -15,29 +15,29 @@ module.exports = class HKD extends GenericOrder { PartnerID: this.partnerId, UserID: this.userId, Product: { - "@": { Language: "de" }, - "#": this.productString, + '@': { Language: 'de' }, + '#': this.productString, }, OrderDetails: { - OrderType: "HKD", - OrderAttribute: "DZHNN", - StandardOrderParams: "" + OrderType: 'HKD', + OrderAttribute: 'DZHNN', + StandardOrderParams: '', }, BankPubKeyDigests: { Authentication: { - "@": { Version: "X002", Algorithm: "http://www.w3.org/2001/04/xmlenc#sha256" }, - "#": this.client.bankX().publicDigest() + '@': { Version: 'X002', Algorithm: 'http://www.w3.org/2001/04/xmlenc#sha256' }, + '#': this.client.bankX().publicDigest(), }, Encryption: { - "@": { Version: "E002", Algorithm: "http://www.w3.org/2001/04/xmlenc#sha256" }, - "#": this.client.bankE().publicDigest() - } + '@': { Version: 'E002', Algorithm: 'http://www.w3.org/2001/04/xmlenc#sha256' }, + '#': this.client.bankE().publicDigest(), + }, }, - SecurityMedium: "0000" + SecurityMedium: '0000', }, mutable: { - TransactionPhase: "Initialisation" - } + TransactionPhase: 'Initialisation', + }, }; - }; + } }; diff --git a/lib/orders/HPB.js b/lib/orders/HPB.js index b0f8bc7..207d2aa 100644 --- a/lib/orders/HPB.js +++ b/lib/orders/HPB.js @@ -3,11 +3,11 @@ const GenericOrder = require('./GenericOrder'); module.exports = class HPB extends GenericOrder { - constructor (client) { + constructor(client) { super(client); this._schema.header = { - "@": { authenticate: true }, + '@': { authenticate: true }, static: { HostID: this.hostId, Nonce: this.nonce(), @@ -15,20 +15,20 @@ module.exports = class HPB extends GenericOrder { PartnerID: this.partnerId, UserID: this.userId, Product: { - "@": { Language: "de" }, - "#": this.productString, + '@': { Language: 'de' }, + '#': this.productString, }, OrderDetails: { - OrderType: "HPB", - OrderAttribute: "DZHNN" + OrderType: 'HPB', + OrderAttribute: 'DZHNN', }, - SecurityMedium: "0000" + SecurityMedium: '0000', }, - mutable: {} + mutable: {}, }; - }; + } - root() { - return "ebicsNoPubKeyDigestsRequest"; - }; + root() { // eslint-disable-line class-methods-use-this + return 'ebicsNoPubKeyDigestsRequest'; + } }; diff --git a/lib/orders/HTD.js b/lib/orders/HTD.js index 68a1510..071637c 100644 --- a/lib/orders/HTD.js +++ b/lib/orders/HTD.js @@ -3,11 +3,11 @@ const GenericOrder = require('./GenericOrder'); module.exports = class HTD extends GenericOrder { - constructor (client) { + constructor(client) { super(client); this._schema.header = { - "@": { authenticate: true }, + '@': { authenticate: true }, static: { HostID: this.hostId, Nonce: this.nonce(), @@ -15,29 +15,29 @@ module.exports = class HTD extends GenericOrder { PartnerID: this.partnerId, UserID: this.userId, Product: { - "@": { Language: "de" }, - "#": this.productString, + '@': { Language: 'de' }, + '#': this.productString, }, OrderDetails: { - OrderType: "HTD", - OrderAttribute: "DZHNN", - StandardOrderParams: "" + OrderType: 'HTD', + OrderAttribute: 'DZHNN', + StandardOrderParams: '', }, BankPubKeyDigests: { Authentication: { - "@": { Version: "X002", Algorithm: "http://www.w3.org/2001/04/xmlenc#sha256" }, - "#": this.client.bankX().publicDigest() + '@': { Version: 'X002', Algorithm: 'http://www.w3.org/2001/04/xmlenc#sha256' }, + '#': this.client.bankX().publicDigest(), }, Encryption: { - "@": { Version: "E002", Algorithm: "http://www.w3.org/2001/04/xmlenc#sha256" }, - "#": this.client.bankE().publicDigest() - } + '@': { Version: 'E002', Algorithm: 'http://www.w3.org/2001/04/xmlenc#sha256' }, + '#': this.client.bankE().publicDigest(), + }, }, - SecurityMedium: "0000" + SecurityMedium: '0000', }, mutable: { - TransactionPhase: "Initialisation" - } + TransactionPhase: 'Initialisation', + }, }; - }; + } }; diff --git a/lib/orders/INI.js b/lib/orders/INI.js index 8d9d434..631f0eb 100644 --- a/lib/orders/INI.js +++ b/lib/orders/INI.js @@ -6,70 +6,70 @@ const js2xmlparser = require('js2xmlparser'); const GenericOrder = require('./GenericOrder'); module.exports = class INI extends GenericOrder { - constructor (client) { + constructor(client) { super(client); this._schema = { - "@": { - "xmlns:ds": "http://www.w3.org/2000/09/xmldsig#", - xmlns: "urn:org:ebics:H004", - Version: "H004", - Revision: "1" + '@': { + 'xmlns:ds': 'http://www.w3.org/2000/09/xmldsig#', + xmlns: 'urn:org:ebics:H004', + Version: 'H004', + Revision: '1', }, header: { - "@": { authenticate: true }, + '@': { authenticate: true }, static: { HostID: this.hostId, PartnerID: this.partnerId, UserID: this.userId, Product: { - "@": { Language: "de" }, - "#": this.productString, + '@': { Language: 'de' }, + '#': this.productString, }, OrderDetails: { - OrderType: "INI", - OrderAttribute: "DZNNN" + OrderType: 'INI', + OrderAttribute: 'DZNNN', }, - SecurityMedium: "0000" + SecurityMedium: '0000', }, - mutable: {} + mutable: {}, }, body: { DataTransfer: { - OrderData: Buffer.from(zlib.deflateSync(this.keySignature())).toString('base64') - } - } + OrderData: Buffer.from(zlib.deflateSync(this.keySignature())).toString('base64'), + }, + }, }; } - root() { - return "ebicsUnsecuredRequest"; - }; + root() { // eslint-disable-line class-methods-use-this + return 'ebicsUnsecuredRequest'; + } keySignature() { const xmlOrderData = { - "@": { - "xmlns:ds": "http://www.w3.org/2000/09/xmldsig#", - xmlns: "http://www.ebics.org/S001" + '@': { + 'xmlns:ds': 'http://www.w3.org/2000/09/xmldsig#', + xmlns: 'http://www.ebics.org/S001', }, SignaturePubKeyInfo: { PubKeyValue: { - "ds:RSAKeyValue": { - "ds:Modulus": Buffer.from(this.client.a().n(), 'HEX').toString('base64'), - "ds:Exponent": "AQAB" + 'ds:RSAKeyValue': { + 'ds:Modulus': Buffer.from(this.client.a().n(), 'HEX').toString('base64'), + 'ds:Exponent': 'AQAB', }, - TimeStamp: this.timestamp() + TimeStamp: this.timestamp(), }, - SignatureVersion: "A006" + SignatureVersion: 'A006', }, PartnerID: this.partnerId, - UserID: this.userId + UserID: this.userId, }; - return js2xmlparser.parse("SignaturePubKeyOrderData", xmlOrderData, this.xmlOptions); - }; + return js2xmlparser.parse('SignaturePubKeyOrderData', xmlOrderData, this.xmlOptions); + } toXML() { return js2xmlparser.parse(this.root(), this._schema, this.xmlOptions); diff --git a/package.json b/package.json index e86e10c..927344a 100644 --- a/package.json +++ b/package.json @@ -1,36 +1,39 @@ { - "name": "node-ebics-client", - "version": "0.0.35", - "description": "Node.js ISO 20022 Compliant EBICS Client", - "main": "index.js", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, - "repository": { - "type": "git", - "url": "https://github.com/eCollect/node-ebics-client" - }, - "keywords": [ - "EBICS", - "ISO20022", - "nodejs", - "api" - ], - "author": "eCollect Sofia Tech Team", - "license": "MIT", - "dependencies": { - "bn.js": "^4.11.8", - "handlebars": "^4.0.11", - "js2xmlparser": "^3.0.0", - "moment": "^2.22.1", - "node-rsa": "^0.4.2", - "xml-c14n": "0.0.6", - "xmldom": "^0.1.27", - "xpath": "0.0.27" - }, - "devDependencies": { - "eslint": "^4.19.1", - "eslint-config-airbnb-base": "^12.1.0", - "eslint-plugin-import": "^2.12.0" + "name": "ebics-client", + "version": "0.0.36", + "description": "Node.js ISO 20022 Compliant EBICS Client", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "https://github.com/eCollect/node-ebics-client" + }, + "keywords": [ + "EBICS", + "ISO20022", + "nodejs", + "api" + ], + "author": "eCollect Sofia Tech Team", + "license": "MIT", + "dependencies": { + "bn.js": "^4.11.8", + "handlebars": "^4.0.11", + "js2xmlparser": "^3.0.0", + "moment": "^2.22.1", + "node-rsa": "^0.4.2", + "request": "^2.87.0", + "xml-c14n": "0.0.6", + "xml-crypto": "^0.10.1", + "xmldom": "^0.1.27", + "xpath": "0.0.27", + "xpath.js": "^1.1.0" + }, + "devDependencies": { + "eslint": "^4.19.1", + "eslint-config-airbnb-base": "^12.1.0", + "eslint-plugin-import": "^2.12.0" } } diff --git a/lib/ini.hbs b/templates/ini.hbs similarity index 100% rename from lib/ini.hbs rename to templates/ini.hbs