From 187636019c290d757aca77d4c14fb4f2519acd38 Mon Sep 17 00:00:00 2001 From: Vladislav Hristov Date: Fri, 15 Jun 2018 09:33:41 +0300 Subject: [PATCH] code optimization --- index.js | 12 +- lib/Client.js | 35 +++-- lib/ISO20022OrderBuilder.js | 101 ++++++++++++ lib/OrderBuilder.js | 24 +-- lib/keymanagers/FsKeyStorage.js | 40 ----- lib/keymanagers/KeysManager.js | 92 ++++------- lib/keymanagers/fsKeysStorage.js | 26 ++++ lib/middleware/ParseResponse.js | 11 -- lib/middleware/XMLSign.js | 15 -- lib/middleware/response.js | 11 ++ lib/middleware/serializer.js | 13 ++ lib/middleware/signer.js | 11 ++ .../Response.js => orders/H004/response.js} | 30 ++-- lib/orders/H004/serializer.js | 19 +++ lib/orders/H004/serializers/download.js | 62 ++++++++ lib/orders/H004/serializers/generic.js | 133 ++++++++++++++++ lib/orders/H004/serializers/ini.js | 144 ++++++++++++++++++ lib/orders/H004/serializers/upload.js | 88 +++++++++++ lib/orders/H004/signer.js | 50 ++++++ lib/orders/orders.js | 35 +++++ lib/versions/H004/OrderSerializer.js | 15 -- lib/versions/H004/Signer.js | 67 -------- .../H004/serializers/GenericSerializer.js | 87 ----------- .../serializers/InitializationSerializer.js | 128 ---------------- .../H004/serializers/PaymentSerializer.js | 130 ---------------- .../H004/serializers/StatusSerializer.js | 66 -------- 26 files changed, 776 insertions(+), 669 deletions(-) create mode 100644 lib/ISO20022OrderBuilder.js delete mode 100644 lib/keymanagers/FsKeyStorage.js create mode 100644 lib/keymanagers/fsKeysStorage.js delete mode 100644 lib/middleware/ParseResponse.js delete mode 100644 lib/middleware/XMLSign.js create mode 100644 lib/middleware/response.js create mode 100644 lib/middleware/serializer.js create mode 100644 lib/middleware/signer.js rename lib/{versions/H004/Response.js => orders/H004/response.js} (94%) create mode 100644 lib/orders/H004/serializer.js create mode 100644 lib/orders/H004/serializers/download.js create mode 100644 lib/orders/H004/serializers/generic.js create mode 100644 lib/orders/H004/serializers/ini.js create mode 100644 lib/orders/H004/serializers/upload.js create mode 100644 lib/orders/H004/signer.js create mode 100644 lib/orders/orders.js delete mode 100644 lib/versions/H004/OrderSerializer.js delete mode 100644 lib/versions/H004/Signer.js delete mode 100644 lib/versions/H004/serializers/GenericSerializer.js delete mode 100644 lib/versions/H004/serializers/InitializationSerializer.js delete mode 100644 lib/versions/H004/serializers/PaymentSerializer.js delete mode 100644 lib/versions/H004/serializers/StatusSerializer.js diff --git a/index.js b/index.js index a5be567..63bbdcd 100644 --- a/index.js +++ b/index.js @@ -1,5 +1,15 @@ 'use strict'; const Client = require('./lib/Client'); +const OrderBuilder = require('./lib/OrderBuilder'); +const ISO20022Builder = require('./lib/ISO20022OrderBuilder'); +const keysManager = require('./lib/keymanagers/keysManager'); +const fsKeysStorage = require('./lib/keymanagers/fsKeysStorage'); -module.exports = Client; +module.exports = { + Client, + OrderBuilder, + ISO20022Builder, + keysManager, + fsKeysStorage, +}; diff --git a/lib/Client.js b/lib/Client.js index daff0ee..ae69e3d 100644 --- a/lib/Client.js +++ b/lib/Client.js @@ -2,11 +2,12 @@ const $request = require('request'); -const XMLSign = require('./middleware/XMLSign'); -const ParseResponse = require('./middleware/ParseResponse'); +const signer = require('./middleware/signer'); +const serializer = require('./middleware/serializer'); +const response = require('./middleware/response'); module.exports = class Client { - constructor({ url }) { + constructor(url) { this.url = url; } @@ -52,25 +53,31 @@ module.exports = class Client { return [transactionId, orderId]; } - async downloadAndUnzip(order) { // eslint-disable-line - - } - ebicsRequest(order) { return new Promise((resolve, reject) => { + const { version, keys } = order; + // const s = signer.version(version).use(serializer.use(order).toXML(), keys).digest().sign().toXML(); // new (signer.version(version))(serializer.use(order).toXML(), keys).digest().sign().toXML(); $request.post({ url: this.url, - body: XMLSign.sign(order), + body: signer.version(version).sign(serializer.use(order).toXML(), keys), // s, // new (signer.version(version))(serializer.use(order).toXML(), keys).digest().sign().toXML(), headers: { 'content-type': 'text/xml;charset=UTF-8' }, - }, (err, res, data) => (err ? reject(err) : resolve(ParseResponse.parse(data, order.keys, order.version)))); + }, (err, res, data) => (err ? reject(err) : resolve(response.version(version)(data, keys)))); }); } - request(order) { - if (order.type.toLowerCase() === 'ini') return this.initialization(order); - if (order.type.toLowerCase() === 'payment') return this.upload(order); - if (order.type.toLowerCase() === 'status') return this.download(order); + ini(order) { + return this.initialization(order); + } - throw Error('Invalid order type'); + payment(order) { + return this.upload(order); + } + + statement(order) { + return this.download(order); + } + + status(order) { + return this.download(order); } }; diff --git a/lib/ISO20022OrderBuilder.js b/lib/ISO20022OrderBuilder.js new file mode 100644 index 0000000..02e637f --- /dev/null +++ b/lib/ISO20022OrderBuilder.js @@ -0,0 +1,101 @@ +'use strict'; + +const OrderBuilder = require('./OrderBuilder'); + +module.exports = class ISO20022OrderBuilder extends OrderBuilder { + get document() { return this._document; } + get ebicsData() { return this._ebicsData; } + + use(dataToUse) { + if (Object.prototype.hasOwnProperty.call(dataToUse, 'ebicsData')) this._ebicsData = dataToUse.ebicsData; + if (Object.prototype.hasOwnProperty.call(dataToUse, 'document')) this._document = dataToUse.document; + + return this; + } + + static h004() { + const builder = new ISO20022OrderBuilder(); + + builder._version = 'H004'; + + return builder; + } + + INI() { + return this.details({ + ebicsData: this.ebicsData, + orderDetails: { OrderType: 'INI', OrderAttribute: 'DZNNN' }, + }); + } + + HIA() { + return this.details({ + ebicsData: this.ebicsData, + orderDetails: { OrderType: 'HIA', OrderAttribute: 'DZNNN' }, + }); + } + + HPB() { + return this.details({ + ebicsData: this.ebicsData, + orderDetails: { OrderType: 'HPB', OrderAttribute: 'DZHNN' }, + }); + } + + HKD() { + return this.details({ + ebicsData: this.ebicsData, + orderDetails: { OrderType: 'HKD', OrderAttribute: 'DZHNN', StandardOrderParams: {} }, + }); + } + + HPD() { + return this.details({ + ebicsData: this.ebicsData, + orderDetails: { OrderType: 'HPD', OrderAttribute: 'DZHNN', StandardOrderParams: {} }, + }); + } + + HTD() { + return this.details({ + ebicsData: this.ebicsData, + orderDetails: { OrderType: 'HTD', OrderAttribute: 'DZHNN', StandardOrderParams: {} }, + }); + } + + HAA() { + return this.details({ + ebicsData: this.ebicsData, + orderDetails: { OrderType: 'HAA', OrderAttribute: 'DZHNN', StandardOrderParams: {} }, + }); + } + + HAC(start = null, end = null) { + const params = start && end + ? { DateRange: { Start: start, End: end } } + : {}; + + return this.details({ + ebicsData: this.ebicsData, + orderDetails: { OrderType: 'HAC', OrderAttribute: 'DZHNN', StandardOrderParams: params }, + }); + } + + PTK(start = null, end = null) { + const params = start && end + ? { DateRange: { Start: start, End: end } } + : {}; + + return this.details({ + ebicsData: this.ebicsData, + orderDetails: { OrderType: 'PTK', OrderAttribute: 'DZHNN', StandardOrderParams: params }, + }); + } + + Z52() { + return this.details({ + ebicsData: this.ebicsData, + orderDetails: { OrderType: 'Z52', OrderAttribute: 'DZHNN', StandardOrderParams: {} }, + }); + } +}; diff --git a/lib/OrderBuilder.js b/lib/OrderBuilder.js index 5d23e62..4b0bc29 100644 --- a/lib/OrderBuilder.js +++ b/lib/OrderBuilder.js @@ -2,10 +2,11 @@ const crypto = require('crypto'); -// const orderTypes = ['ini', 'download', 'upload', 'zip']; +const constants = require('./consts'); module.exports = class OrderBuilder { constructor() { + this._productString = constants.productString; this._transactionKey = crypto.randomBytes(16); } @@ -16,24 +17,6 @@ module.exports = class OrderBuilder { return this; } - payment() { - this._type = 'payment'; - - return this; - } - - status() { - this._type = 'status'; - - return this; - } - - ini() { - this._type = 'ini'; - - return this; - } - static h004() { const builder = new OrderBuilder(); @@ -45,7 +28,6 @@ module.exports = class OrderBuilder { /** * Getters */ - get type() { return this._type; } get data() { return this._data; } get orderDetails() { return this._data.orderDetails; } get transactionId() { return this._data.transactionId; } @@ -57,6 +39,8 @@ module.exports = class OrderBuilder { get userId() { return this._data.ebicsData.userId; } get keys() { return this._data.ebicsData.keysManager.keys(); } get version() { return this._version; } + get productString() { return this._productString; } + get orderType() { return this.orderDetails.OrderType; } set transactionId(tid) { this._data.transactionId = tid === '' ? null : tid; diff --git a/lib/keymanagers/FsKeyStorage.js b/lib/keymanagers/FsKeyStorage.js deleted file mode 100644 index e6b6911..0000000 --- a/lib/keymanagers/FsKeyStorage.js +++ /dev/null @@ -1,40 +0,0 @@ -'use strict'; - -const fs = require('fs'); -/* const extractKeys = (keysObject, encryptAlgorithm, passphrase) => Object.entries(keysObject).reduce((keys, [key, data]) => { - keys[key] = decrypt(data, encryptAlgorithm, passphrase); - return keys; -}, {}); */ - -module.exports = class FsKeyStorage { - /** - * @param {String} path - destingiton file to save the keys - */ - constructor({ path }) { - if (!path) - throw new Error('Invalid path provided'); - - this._path = path; - } - - get path() { - return this._path; - } - - read() { - return fs.readFileSync(this._path, { encoding: 'utf8' }); - // return extractKeys(JSON.parse(fs.readFileSync(this._path, { encoding: 'utf8' })), this.algorithm, this.passphrase); - } - - save(data) { - fs.writeFileSync(this._path, data, { encoding: 'utf8' }); - // fs.writeFileSync(this._path, encrypt(JSON.stringify(data), this.algorithm, this.passphrase), { encoding: 'utf8' }); - } - - hasData() { - if (fs.existsSync(this._path)) - return this.read() !== ''; - - return false; - } -}; diff --git a/lib/keymanagers/KeysManager.js b/lib/keymanagers/KeysManager.js index 6981f6c..b9488ae 100644 --- a/lib/keymanagers/KeysManager.js +++ b/lib/keymanagers/KeysManager.js @@ -19,76 +19,50 @@ const decrypt = (data, algorithm, passphrase) => { return decrypted; }; -module.exports = class KeysManager { - constructor(keysStorage, passphrase, algorithm = 'aes-256-cbc', createIfNone = true) { - this._storage = keysStorage; - this._passphrase = passphrase; - this._algorithm = algorithm; +module.exports = (keysStorage, passphrase, algorithm = 'aes-256-cbc') => { + const storage = keysStorage; + const pass = passphrase; + const algo = algorithm; + // const createIfNone = createIfNone; - if (createIfNone && !this._storage.hasData()) - this.generate(); - } + return { + generate(save = true) { + const keys = Keys.generate(); - /** - * Generates the keys to work with. Then either - * saves them to the storage or returnes the keys generated - * - * @param {Boolean} save - * @default true - * - * @returns void | Keys object - */ - generate(save = true) { - const keys = Keys.generate(); + if (save) { + this.write(keys); - if (save) this.write(keys); + return this; + } - return keys; - } + return keys; + }, - /** - * Writes the keys to the storage - * - * @param {Keys} keysObject - * - * @returns void - */ - write(keysObject) { - keysObject = keysObject.keys; + write(keysObject) { + keysObject = keysObject.keys; - Object.keys(keysObject).map((key) => { - keysObject[key] = keysObject[key] === null ? null : keysObject[key].toPem(); + Object.keys(keysObject).map((key) => { + keysObject[key] = keysObject[key] === null ? null : keysObject[key].toPem(); - return key; - }); + return key; + }); - this._storage.save(encrypt(JSON.stringify(keysObject), this._algorithm, this._passphrase)); - } + storage.save(encrypt(JSON.stringify(keysObject), algo, pass)); - setBankKeys(bankKeys) { - const keys = this.keys(); + return this; + }, - keys.setBankKeys(bankKeys); - this.write(keys); - } + setBankKeys(bankKeys) { + const keys = this.keys(); - /** - * Gets the keys - * - * @returns Keys object - */ - keys() { - return this._read(); - } + keys.setBankKeys(bankKeys); + this.write(keys); + }, - /** - * Reads the keys from the storage - * - * @returns Keys object - */ - _read() { - const keysString = this._storage.read(); + keys() { + const keysString = storage.read(); - return new Keys(JSON.parse(decrypt(keysString, this._algorithm, this._passphrase))); - } + return new Keys(JSON.parse(decrypt(keysString, algo, pass))); + }, + }; }; diff --git a/lib/keymanagers/fsKeysStorage.js b/lib/keymanagers/fsKeysStorage.js new file mode 100644 index 0000000..09bfccc --- /dev/null +++ b/lib/keymanagers/fsKeysStorage.js @@ -0,0 +1,26 @@ +'use strict'; + +const fs = require('fs'); + +module.exports = (pathToFile) => { + const path = pathToFile; + + return { + read() { + return fs.readFileSync(path, { encoding: 'utf8' }); + }, + + save(data) { + fs.writeFileSync(path, data, { encoding: 'utf8' }); + + return this; + }, + + hasData() { + if (fs.existsSync(path)) + return this.read() !== ''; + + return false; + }, + }; +}; diff --git a/lib/middleware/ParseResponse.js b/lib/middleware/ParseResponse.js deleted file mode 100644 index 8521377..0000000 --- a/lib/middleware/ParseResponse.js +++ /dev/null @@ -1,11 +0,0 @@ -'use strict'; - -const H004Response = require('../versions/H004/Response'); - -module.exports = class ParseResponse { - static parse(data, keys, version) { - if (version.toUpperCase() === 'H004') return new H004Response(data, keys); - - throw Error('Unknow EBICS response version'); - } -}; diff --git a/lib/middleware/XMLSign.js b/lib/middleware/XMLSign.js deleted file mode 100644 index 7d0195e..0000000 --- a/lib/middleware/XMLSign.js +++ /dev/null @@ -1,15 +0,0 @@ -'use strict'; - -const H004Signer = require('../versions/H004/Signer'); - -const H004Serializer = require('../versions/H004/OrderSerializer'); - -module.exports = class XMLSign { - static sign(order) { - const { keys } = order; - - if (order.version.toUpperCase() === 'H004') return new H004Signer(H004Serializer.serialize(order).toXML(), keys).digest().sign().toXML(); - - throw Error('Error from XMLSign class: Invalid version number'); - } -}; diff --git a/lib/middleware/response.js b/lib/middleware/response.js new file mode 100644 index 0000000..81eaead --- /dev/null +++ b/lib/middleware/response.js @@ -0,0 +1,11 @@ +'use strict'; + +const H004Response = require('../orders/H004/response'); + +module.exports = { + version(v) { + if (v.toUpperCase() === 'H004') return H004Response; + + throw Error('Error from middleware/response.js: Invalid version number'); + }, +}; diff --git a/lib/middleware/serializer.js b/lib/middleware/serializer.js new file mode 100644 index 0000000..e778323 --- /dev/null +++ b/lib/middleware/serializer.js @@ -0,0 +1,13 @@ +'use strict'; + +const H004Serializer = require('../orders/H004/serializer'); + +module.exports = { + use(order) { + const { version } = order; + + if (version.toUpperCase() === 'H004') return H004Serializer.use(order); + + throw Error('Error middleware/serializer.js: Invalid version number'); + }, +}; diff --git a/lib/middleware/signer.js b/lib/middleware/signer.js new file mode 100644 index 0000000..8c67038 --- /dev/null +++ b/lib/middleware/signer.js @@ -0,0 +1,11 @@ +'use strict'; + +const H004Signer = require('../orders/H004/signer'); + +module.exports = { + version(v) { + if (v.toUpperCase() === 'H004') return H004Signer; + + throw Error('Error from middleware/signer.js: Invalid version number'); + }, +}; diff --git a/lib/versions/H004/Response.js b/lib/orders/H004/response.js similarity index 94% rename from lib/versions/H004/Response.js rename to lib/orders/H004/response.js index 506d892..c8051a6 100644 --- a/lib/versions/H004/Response.js +++ b/lib/orders/H004/response.js @@ -19,25 +19,23 @@ const lastChild = (node) => { return y; }; -module.exports = class Response { - constructor(data, keys) { - this.keys = keys; - this.doc = new DOMParser().parseFromString(data, 'text/xml'); - } +module.exports = (xml, keys) => ({ + keys, + doc: new DOMParser().parseFromString(xml, 'text/xml'), isSegmented() { const select = xpath.useNamespaces({ xmlns: 'urn:org:ebics:H004' }); const node = select('//xmlns:header/xmlns:mutable/xmlns:SegmentNumber', this.doc); return !!node.length; - } + }, isLastSegment() { const select = xpath.useNamespaces({ xmlns: 'urn:org:ebics:H004' }); const node = select("//xmlns:header/xmlns:mutable/*[@lastSegment='true']", this.doc); return !!node.length; - } + }, orderData() { const orderDataNode = this.doc.getElementsByTagNameNS('urn:org:ebics:H004', 'OrderData'); @@ -49,41 +47,41 @@ module.exports = class Response { 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; return Crypto.privateDecrypt(this.keys.e(), Buffer.from(keyNodeText, 'base64')); - } + }, transactionId() { 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 : ''; - } + }, orderId() { const select = xpath.useNamespaces({ xmlns: 'urn:org:ebics:H004' }); const node = select('//xmlns:header/xmlns:mutable/xmlns:OrderID', this.doc); return node.length ? node[0].textContent : ''; - } + }, returnCode() { const select = xpath.useNamespaces({ xmlns: 'urn:org:ebics:H004' }); const node = select('//xmlns:header/xmlns:mutable/xmlns:ReturnCode', this.doc); return node.length ? node[0].textContent : ''; - } + }, reportText() { const select = xpath.useNamespaces({ xmlns: 'urn:org:ebics:H004' }); const node = select('//xmlns:header/xmlns:mutable/xmlns:ReportText', this.doc); return node.length ? node[0].textContent : ''; - } + }, bankKeys() { const orderData = this.orderData(); @@ -108,9 +106,9 @@ module.exports = class Response { } return bankKeys; - } + }, toXML() { return new XMLSerializer().serializeToString(this.doc); - } -}; + }, +}); diff --git a/lib/orders/H004/serializer.js b/lib/orders/H004/serializer.js new file mode 100644 index 0000000..999b322 --- /dev/null +++ b/lib/orders/H004/serializer.js @@ -0,0 +1,19 @@ +'use strict'; + +const orders = require('../orders'); + +const iniSerializer = require('./serializers/ini'); +const downloadSerializer = require('./serializers/download'); +const uploadSerializer = require('./serializers/upload'); + +module.exports = { + use(order) { + const { version, orderType } = order; + + if (orders.version(version).isIni(orderType)) return iniSerializer.use(order); + if (orders.version(version).isDownload(orderType)) return downloadSerializer.use(order); + if (orders.version(version).isUpload(orderType)) return uploadSerializer.use(order); + + throw Error('Error from orders/orders.js: Wrong order version/type.'); + }, +}; diff --git a/lib/orders/H004/serializers/download.js b/lib/orders/H004/serializers/download.js new file mode 100644 index 0000000..969a764 --- /dev/null +++ b/lib/orders/H004/serializers/download.js @@ -0,0 +1,62 @@ +'use strict'; + +const js2xmlparser = require('js2xmlparser'); + +const Crypto = require('../../../crypto/Crypto'); + +const genericSerializer = require('./generic'); + +module.exports = { + use(orderBuilder) { + const { + ebicsData, orderDetails, keys, productString, transactionId, + } = orderBuilder; + const { + rootName, xmlOptions, xmlSchema, receipt, transfer, + } = genericSerializer(orderBuilder); + + this.rootName = rootName; + this.xmlOptions = xmlOptions; + this.xmlSchema = xmlSchema; + this.receipt = receipt; + this.transfer = transfer; + + if (transactionId) return this.receipt(); + + this.xmlSchema.header = { + '@': { authenticate: true }, + static: { + HostID: ebicsData.hostId, + Nonce: Crypto.nonce(), + Timestamp: Crypto.timestamp(), + PartnerID: ebicsData.partnerId, + UserID: ebicsData.userId, + Product: { + '@': { Language: 'en' }, + '#': productString, + }, + OrderDetails: orderDetails, + BankPubKeyDigests: { + Authentication: { + '@': { Version: 'X002', Algorithm: 'http://www.w3.org/2001/04/xmlenc#sha256' }, + '#': Crypto.digestPublicKey(keys.bankX()), + }, + Encryption: { + '@': { Version: 'E002', Algorithm: 'http://www.w3.org/2001/04/xmlenc#sha256' }, + '#': Crypto.digestPublicKey(keys.bankE()), + }, + }, + SecurityMedium: '0000', + }, + mutable: { + TransactionPhase: 'Initialisation', + }, + }; + + return this; + }, + + toXML() { + return js2xmlparser.parse(this.rootName, this.xmlSchema, this.xmlOptions); + }, +}; diff --git a/lib/orders/H004/serializers/generic.js b/lib/orders/H004/serializers/generic.js new file mode 100644 index 0000000..1cc028b --- /dev/null +++ b/lib/orders/H004/serializers/generic.js @@ -0,0 +1,133 @@ +'use strict'; + +const rootName = 'ebicsRequest'; +const rootAttributes = { + 'xmlns:ds': 'http://www.w3.org/2000/09/xmldsig#', + xmlns: 'urn:org:ebics:H004', + Version: 'H004', + Revision: '1', +}; +const header = {}; +const authSignature = ({ + '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': {}, +}); +const body = {}; + +const xmlOptions = { + declaration: { + include: true, + encoding: 'utf-8', + }, + format: { + doubleQuotes: true, + indent: '', + newline: '', + pretty: true, + }, +}; + +module.exports = (orderBuilder) => { + const { ebicsData, transactionId } = orderBuilder; + + return { + rootName, + xmlOptions, + xmlSchema: { + '@': rootAttributes, + header, + AuthSignature: authSignature, + body, + }, + + receipt() { + this.xmlSchema = { + '@': rootAttributes, + + header: { + '@': { authenticate: true }, + static: { + HostID: ebicsData.hostId, + TransactionID: transactionId, + }, + mutable: { + TransactionPhase: 'Receipt', + }, + }, + + AuthSignature: authSignature, + + body: { + TransferReceipt: { + '@': { authenticate: true }, + ReceiptCode: 0, + }, + }, + }; + + return this; + }, + + transfer(encryptedOrderData) { + this.xmlSchema = { + '@': rootAttributes, + + header: { + '@': { authenticate: true }, + static: { + HostID: ebicsData.hostId, + TransactionID: transactionId, + }, + mutable: { + TransactionPhase: 'Transfer', + SegmentNumber: { + '@': { lastSegment: true }, + '#': 1, + }, + }, + }, + + AuthSignature: authSignature, + + body: { + DataTransfer: { + OrderData: encryptedOrderData, + }, + }, + }; + + return this; + }, + }; +}; diff --git a/lib/orders/H004/serializers/ini.js b/lib/orders/H004/serializers/ini.js new file mode 100644 index 0000000..33093cb --- /dev/null +++ b/lib/orders/H004/serializers/ini.js @@ -0,0 +1,144 @@ +'use strict'; + +const zlib = require('zlib'); + +const js2xmlparser = require('js2xmlparser'); + +const Crypto = require('../../../crypto/Crypto'); + +const genericSerializer = require('./generic'); + +const keySignature = (ebicsData, key, xmlOptions) => { + const xmlOrderData = { + '@': { + 'xmlns:ds': 'http://www.w3.org/2000/09/xmldsig#', + xmlns: 'http://www.ebics.org/S001', + }, + SignaturePubKeyInfo: { + PubKeyValue: { + 'ds:RSAKeyValue': { + 'ds:Modulus': key.n().toString('base64'), + 'ds:Exponent': key.e().toString('base64'), + }, + TimeStamp: Crypto.timestamp(), + }, + SignatureVersion: 'A006', + }, + PartnerID: ebicsData.partnerId, + UserID: ebicsData.userId, + }; + + return js2xmlparser.parse('SignaturePubKeyOrderData', xmlOrderData, xmlOptions); +}; +const orderData = (ebicsData, keys, xmlOptions) => { + const xmlOrderData = { + '@': { + 'xmlns:ds': 'http://www.w3.org/2000/09/xmldsig#', + xmlns: 'urn:org:ebics:H004', + }, + AuthenticationPubKeyInfo: { + PubKeyValue: { + 'ds:RSAKeyValue': { + 'ds:Modulus': keys.x().n().toString('base64'), + 'ds:Exponent': keys.x().e().toString('base64'), + }, + }, + AuthenticationVersion: 'X002', + }, + EncryptionPubKeyInfo: { + PubKeyValue: { + 'ds:RSAKeyValue': { + 'ds:Modulus': keys.e().n().toString('base64'), + 'ds:Exponent': keys.e().e().toString('base64'), + }, + }, + EncryptionVersion: 'E002', + }, + PartnerID: ebicsData.partnerId, + UserID: ebicsData.userId, + }; + + return js2xmlparser.parse('HIARequestOrderData', xmlOrderData, xmlOptions); +}; +const commonHeader = (ebicsData, orderDetails, productString) => ({ + '@': { authenticate: true }, + static: { + HostID: ebicsData.hostId, + Nonce: Crypto.nonce(), + Timestamp: Crypto.timestamp(), + PartnerID: ebicsData.partnerId, + UserID: ebicsData.userId, + Product: { + '@': { Language: 'en' }, + '#': productString, + }, + OrderDetails: orderDetails, + SecurityMedium: '0000', + }, + mutable: {}, +}); +const process = { + INI: { + rootName: 'ebicsUnsecuredRequest', + header: (ebicsData, orderDetails, productString) => { + const ch = commonHeader(ebicsData, orderDetails, productString); + + delete ch.static.Nonce; + delete ch.static.Timestamp; + + return ch; + }, + body: (ebicsData, keys, xmlOptions) => ({ + DataTransfer: { + OrderData: Buffer.from(zlib.deflateSync(keySignature(ebicsData, keys.a(), xmlOptions))).toString('base64'), + }, + }), + }, + HIA: { + rootName: 'ebicsUnsecuredRequest', + header: (ebicsData, orderDetails, productString) => { + const ch = commonHeader(ebicsData, orderDetails, productString); + + delete ch.static.Nonce; + delete ch.static.Timestamp; + + return ch; + }, + body: (ebicsData, keys, xmlOptions) => ({ + DataTransfer: { + OrderData: Buffer.from(zlib.deflateSync(orderData(ebicsData, keys, xmlOptions))).toString('base64'), + }, + }), + }, + HPB: { + rootName: 'ebicsNoPubKeyDigestsRequest', + header: (ebicsData, orderDetails, productString) => commonHeader(ebicsData, orderDetails, productString), + body: () => ({}), + }, +}; + +module.exports = { + use(orderBuilder) { + const { xmlOptions, xmlSchema } = genericSerializer(orderBuilder); + const { + ebicsData, orderDetails, keys, productString, + } = orderBuilder; + const orderType = orderDetails.OrderType.toUpperCase(); + + this.rootName = process[orderType].rootName; + this.xmlOptions = xmlOptions; + this.xmlSchema = xmlSchema; + + this.xmlSchema.header = process[orderType].header(ebicsData, orderDetails, productString); + this.xmlSchema.body = process[orderType].body(ebicsData, keys, this.xmlOptions); + + if (orderType !== 'HPB' && Object.prototype.hasOwnProperty.call(this.xmlSchema, 'AuthSignature')) + delete this.xmlSchema.AuthSignature; + + return this; + }, + + toXML() { + return js2xmlparser.parse(this.rootName, this.xmlSchema, this.xmlOptions); + }, +}; diff --git a/lib/orders/H004/serializers/upload.js b/lib/orders/H004/serializers/upload.js new file mode 100644 index 0000000..a062454 --- /dev/null +++ b/lib/orders/H004/serializers/upload.js @@ -0,0 +1,88 @@ +'use strict'; + +const zlib = require('zlib'); +const crypto = require('crypto'); + +const js2xmlparser = require('js2xmlparser'); + +const Crypto = require('../../../crypto/Crypto'); + +const downloadSerializer = require('./download'); + +const signatureValue = (document, key) => { + const digested = Crypto.digestWithHash(document.replace(/\n|\r/g, '')); + + return Crypto.sign(key, digested); +}; +const orderSignature = (ebicsData, document, key, xmlOptions) => { + 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', + }, + OrderSignatureData: { + SignatureVersion: 'A006', + SignatureValue: signatureValue(document, key), + PartnerID: ebicsData.partnerId, + UserID: ebicsData.userId, + }, + }; + + return js2xmlparser.parse('UserSignatureData', xmlObj, xmlOptions); +}; +const encryptedOrderSignature = (ebicsData, document, transactionKey, key, xmlOptions) => { + const dst = zlib.deflateSync(orderSignature(ebicsData, document, key, xmlOptions)); + const cipher = crypto.createCipheriv('aes-128-cbc', transactionKey, 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(Crypto.pad(dst)), cipher.final()]).toString('base64'); +}; +const encryptedOrderData = (document, transactionKey) => { + const dst = zlib.deflateSync(document.replace(/\n|\r/g, '')); + const cipher = crypto.createCipheriv('aes-128-cbc', transactionKey, 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(Crypto.pad(dst)), cipher.final()]).toString('base64'); +}; + +module.exports = { + use(orderBuilder) { + const { + ebicsData, keys, transactionId, transactionKey, document, + } = orderBuilder; + const { + rootName, xmlOptions, xmlSchema, transfer, + } = downloadSerializer.use(orderBuilder); + + this.rootName = rootName; + this.xmlOptions = xmlOptions; + this.xmlSchema = xmlSchema; + this.transfer = transfer; + + if (transactionId) return this.transfer(encryptedOrderData(document, transactionKey)); + + this.xmlSchema.header.static.NumSegments = 1; + + this.xmlSchema.body = { + DataTransfer: { + DataEncryptionInfo: { + '@': { authenticate: true }, + EncryptionPubKeyDigest: { + '@': { Version: 'E002', Algorithm: 'http://www.w3.org/2001/04/xmlenc#sha256' }, + '#': Crypto.digestPublicKey(keys.bankE()), + }, + TransactionKey: Crypto.publicEncrypt(keys.bankE(), transactionKey).toString('base64'), + }, + SignatureData: { + '@': { authenticate: true }, + '#': encryptedOrderSignature(ebicsData, document, transactionKey, keys.a(), this.xmlOptions), + }, + }, + }; + + return this; + }, + + toXML() { + return js2xmlparser.parse(this.rootName, this.xmlSchema, this.xmlOptions); + }, +}; diff --git a/lib/orders/H004/signer.js b/lib/orders/H004/signer.js new file mode 100644 index 0000000..598e259 --- /dev/null +++ b/lib/orders/H004/signer.js @@ -0,0 +1,50 @@ + +'use strict'; + +// const crypto = require('crypto'); +const Crypto = require('../../crypto/Crypto'); + +const { DOMParser, XMLSerializer } = require('xmldom'); +const xpath = require('xpath'); +const C14n = require('xml-crypto/lib/c14n-canonicalization').C14nCanonicalization; + +const digest = (doc) => { + // get the xml node, where the digested value is supposed to be + const nodeDigestValue = doc.getElementsByTagName('ds:DigestValue')[0]; + + // canonicalize the node that has authenticate='true' attribute + const contentToDigest = xpath.select("//*[@authenticate='true']", doc) + .map(x => new C14n().process(x)).join(''); + + // 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) + nodeDigestValue.textContent = Crypto.digestWithHash(fixedContent).toString('base64').trim(); + + return doc; +}; + +const sign = (doc, key) => { + const nodeSignatureValue = doc.getElementsByTagName('ds:SignatureValue')[0]; + + if (nodeSignatureValue) { + const select = xpath.useNamespaces({ ds: 'http://www.w3.org/2000/09/xmldsig#' }); + const contentToSign = (new C14n().process(select('//ds:SignedInfo', doc)[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 = Crypto.privateSign(key, contentToSign); // this.keys.x().key.sign(contentToSign, 'base64'); + } + + return doc; +}; + +const toXML = doc => new XMLSerializer().serializeToString(doc); + +module.exports = { + sign(data, keys) { + const keyX = keys.x(); + const doc = new DOMParser().parseFromString(data, 'text/xml'); + + return toXML(sign(digest(doc), keyX)); + }, +}; diff --git a/lib/orders/orders.js b/lib/orders/orders.js new file mode 100644 index 0000000..58248ed --- /dev/null +++ b/lib/orders/orders.js @@ -0,0 +1,35 @@ +'use strict'; + +const orders = { + H004: { + ini: ['INI', 'HIA', 'HPB'], + download: ['HAA', 'HTD', 'XTD', 'HPD', 'HKD', 'PTK', 'HAC', 'STA', 'VMK', 'C52', 'C53', 'C54', 'Z01'], + upload: ['AZV', 'CD1', 'CDB', 'CDD', 'CDS', 'CCT', 'CCS', 'XE3'], + }, +}; + +module.exports = { + version(v) { + this.orders = orders[v.toUpperCase()]; + + return this; + }, + + isIni(orderType) { + const { ini } = this.orders; + + return ini.includes(orderType.toUpperCase()); + }, + + isDownload(orderType) { + const { download } = this.orders; + + return download.includes(orderType.toUpperCase()); + }, + + isUpload(orderType) { + const { upload } = this.orders; + + return upload.includes(orderType.toUpperCase()); + }, +}; diff --git a/lib/versions/H004/OrderSerializer.js b/lib/versions/H004/OrderSerializer.js deleted file mode 100644 index 6cdaf0f..0000000 --- a/lib/versions/H004/OrderSerializer.js +++ /dev/null @@ -1,15 +0,0 @@ -'use strict'; - -const InitializationSerializer = require('./serializers/InitializationSerializer'); -const StatusSerializer = require('./serializers/StatusSerializer'); -const PaymentSerializer = require('./serializers/PaymentSerializer'); - -module.exports = class OrderSerializer { - static serialize(order) { - if (order.type === 'ini') return new InitializationSerializer(order); - if (order.type === 'payment') return new PaymentSerializer(order); - if (order.type === 'status') return new StatusSerializer(order); - - throw Error('Incorect order type. Available types: ini, status, payment, statement'); - } -}; diff --git a/lib/versions/H004/Signer.js b/lib/versions/H004/Signer.js deleted file mode 100644 index d535a03..0000000 --- a/lib/versions/H004/Signer.js +++ /dev/null @@ -1,67 +0,0 @@ -'use strict'; - -// const crypto = require('crypto'); -const Crypto = require('../../crypto/Crypto'); - -const { DOMParser, XMLSerializer } = require('xmldom'); -const xpath = require('xpath'); -const C14n = require('xml-crypto/lib/c14n-canonicalization').C14nCanonicalization; - - -module.exports = class Signer { - /** - * Contructor. - * - * @param {Keys} keys - * @param {String} data - */ - constructor(data, keys) { - /** - * Keys to operate with - * - * @type {Keys} - */ - this.keys = keys; - - /** - * Request data - generated xml - * - * @type {String} - */ - this.doc = new DOMParser().parseFromString(data, 'text/xml'); - } - - digest() { - // get the xml node, where the digested value is supposed to be - const nodeDigestValue = this.doc.getElementsByTagName('ds:DigestValue')[0]; - - // canonicalize the node that has authenticate='true' attribute - const contentToDigest = xpath.select("//*[@authenticate='true']", this.doc) - .map(x => new C14n().process(x)).join(''); - - // 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) - nodeDigestValue.textContent = Crypto.digestWithHash(fixedContent).toString('base64').trim(); - - return this; - } - - sign() { - const nodeSignatureValue = this.doc.getElementsByTagName('ds:SignatureValue')[0]; - - if (nodeSignatureValue) { - const select = xpath.useNamespaces({ ds: 'http://www.w3.org/2000/09/xmldsig#' }); - const contentToSign = (new C14n().process(select('//ds:SignedInfo', this.doc)[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 = Crypto.privateSign(this.keys.x(), contentToSign); // this.keys.x().key.sign(contentToSign, 'base64'); - } - - return this; - } - - toXML() { - return new XMLSerializer().serializeToString(this.doc); - } -}; diff --git a/lib/versions/H004/serializers/GenericSerializer.js b/lib/versions/H004/serializers/GenericSerializer.js deleted file mode 100644 index 39bc0e5..0000000 --- a/lib/versions/H004/serializers/GenericSerializer.js +++ /dev/null @@ -1,87 +0,0 @@ -'use strict'; - -const js2xmlparser = require('js2xmlparser'); - -const consts = require('../../../consts'); -const xmlOptions = { - declaration: { - include: true, - encoding: 'utf-8', - }, - format: { - doubleQuotes: true, - indent: '', - newline: '', - pretty: true, - }, -}; - -const authSignature = ({ - '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 GenericSerializer { - constructor(orderBuilder) { - this._order = orderBuilder; - this._rootName = 'ebicsRequest'; - this._rootAttributes = { - 'xmlns:ds': 'http://www.w3.org/2000/09/xmldsig#', - xmlns: 'urn:org:ebics:H004', - Version: 'H004', - Revision: '1', - }; - this._orderDetails = orderBuilder.orderDetails; - this._hostId = orderBuilder.hostId; - this._partnerId = orderBuilder.partnerId; - this._userId = orderBuilder.userId; - this._keys = orderBuilder.keys; - this._transactionId = orderBuilder.transactionId; - this._xmlOptions = xmlOptions; - this._xml = {}; - this._productString = consts.productString; - } - - static authSignature() { - return authSignature; - } - - get keys() { - return this._keys; - } - - toXML() { - return js2xmlparser.parse(this._rootName, this._xml, this._xmlOptions); - } -}; diff --git a/lib/versions/H004/serializers/InitializationSerializer.js b/lib/versions/H004/serializers/InitializationSerializer.js deleted file mode 100644 index e09d573..0000000 --- a/lib/versions/H004/serializers/InitializationSerializer.js +++ /dev/null @@ -1,128 +0,0 @@ -'use strict'; - -const zlib = require('zlib'); -const js2xmlparser = require('js2xmlparser'); - -const Crypto = require('../../../crypto/Crypto'); - -const GenericSerializer = require('./GenericSerializer'); - -module.exports = class InitializationSerializer extends GenericSerializer { - constructor(order) { - super(order); - - this._xml = { - '@': this._rootAttributes, - header: { - '@': { authenticate: true }, - static: { - HostID: this._hostId, - Nonce: Crypto.nonce(), - Timestamp: Crypto.timestamp(), - PartnerID: this._partnerId, - UserID: this._userId, - Product: { - '@': { Language: 'en' }, - '#': this._productString, - }, - OrderDetails: this._orderDetails, - SecurityMedium: '0000', - }, - mutable: {}, - }, - }; - - if (this._isINI() || this._isHIA()) { - delete this._xml.header.static.Nonce; - delete this._xml.header.static.Timestamp; - - this._rootName = 'ebicsUnsecuredRequest'; - this._xml.body = { - DataTransfer: { - OrderData: this.orderData(), - }, - }; - } else { - this._rootName = 'ebicsNoPubKeyDigestsRequest'; - this._xml.AuthSignature = GenericSerializer.authSignature(); - this._xml.body = {}; - } - } - - orderData() { - if (this._isINI()) return this._iniKeySignature(); - if (this._isHIA()) return this._hiaOrderData(); - - return ''; - } - - _iniKeySignature() { - const xmlOrderData = { - '@': { - 'xmlns:ds': 'http://www.w3.org/2000/09/xmldsig#', - xmlns: 'http://www.ebics.org/S001', - }, - SignaturePubKeyInfo: { - PubKeyValue: { - 'ds:RSAKeyValue': { - 'ds:Modulus': Buffer.from(this._keys.a().n(), 'HEX').toString('base64'), - 'ds:Exponent': this._keys.a().e().toString('base64'), - }, - TimeStamp: Crypto.timestamp(), - }, - SignatureVersion: 'A006', - }, - PartnerID: this._partnerId, - UserID: this._userId, - }; - - const signature = js2xmlparser.parse('SignaturePubKeyOrderData', xmlOrderData, this._xmlOptions); - - return Buffer.from(zlib.deflateSync(signature)).toString('base64'); - } - - _hiaOrderData() { - const xmlOrderData = { - '@': { - 'xmlns:ds': 'http://www.w3.org/2000/09/xmldsig#', - xmlns: 'urn:org:ebics:H004', - }, - AuthenticationPubKeyInfo: { - PubKeyValue: { - 'ds:RSAKeyValue': { - 'ds:Modulus': Buffer.from(this._keys.x().n(), 'HEX').toString('base64'), - 'ds:Exponent': this._keys.x().e().toString('base64'), - }, - }, - AuthenticationVersion: 'X002', - }, - EncryptionPubKeyInfo: { - PubKeyValue: { - 'ds:RSAKeyValue': { - 'ds:Modulus': Buffer.from(this.keys.e().n(), 'HEX').toString('base64'), - 'ds:Exponent': this._keys.e().e().toString('base64'), - }, - }, - EncryptionVersion: 'E002', - }, - PartnerID: this._partnerId, - UserID: this._userId, - }; - - const order = js2xmlparser.parse('HIARequestOrderData', xmlOrderData, this._xmlOptions); - - return Buffer.from(zlib.deflateSync(order)).toString('base64'); - } - - _isINI() { - return this._orderDetails.OrderType.toUpperCase() === 'INI'; - } - - _isHIA() { - return this._orderDetails.OrderType.toUpperCase() === 'HIA'; - } - - _isHPB() { - return this._orderDetails.OrderType.toUpperCase() === 'HPB'; - } -}; diff --git a/lib/versions/H004/serializers/PaymentSerializer.js b/lib/versions/H004/serializers/PaymentSerializer.js deleted file mode 100644 index c143439..0000000 --- a/lib/versions/H004/serializers/PaymentSerializer.js +++ /dev/null @@ -1,130 +0,0 @@ -'use strict'; - -const zlib = require('zlib'); -const crypto = require('crypto'); - -const js2xmlparser = require('js2xmlparser'); - -const Crypto = require('../../../crypto/Crypto'); - -const GenericSerializer = require('./GenericSerializer'); - -module.exports = class PaymentSerializer extends GenericSerializer { - constructor(order) { - super(order); - - this._transactionKey = order.transactionKey; - - this._xml = { - '@': this._rootAttributes, - header: { - '@': { authenticate: true }, - static: { - HostID: this._hostId, - Nonce: Crypto.nonce(), - Timestamp: Crypto.timestamp(), - PartnerID: this._partnerId, - UserID: this._userId, - Product: { - '@': { Language: 'en' }, - '#': this._productString, - }, - OrderDetails: this._orderDetails, - BankPubKeyDigests: { - Authentication: { - '@': { Version: 'X002', Algorithm: 'http://www.w3.org/2001/04/xmlenc#sha256' }, - '#': Crypto.digestPublicKey(this._keys.bankX()), - }, - Encryption: { - '@': { Version: 'E002', Algorithm: 'http://www.w3.org/2001/04/xmlenc#sha256' }, - '#': Crypto.digestPublicKey(this._keys.bankE()), - }, - }, - SecurityMedium: '0000', - NumSegments: 1, - }, - mutable: { - TransactionPhase: 'Initialisation', - }, - }, - AuthSignature: GenericSerializer.authSignature(), - body: { - DataTransfer: { - DataEncryptionInfo: { - '@': { authenticate: true }, - EncryptionPubKeyDigest: { - '@': { Version: 'E002', Algorithm: 'http://www.w3.org/2001/04/xmlenc#sha256' }, - '#': Crypto.digestPublicKey(this._keys.bankE()), - }, - TransactionKey: Crypto.publicEncrypt(this._keys.bankE(), this._transactionKey).toString('base64'), - }, - SignatureData: { - '@': { authenticate: true }, - '#': this.encryptedOrderSignature(), - }, - }, - }, - }; - - if (order.hasTransactionId()) { - this._xml.header = { - '@': { authenticate: true }, - static: { - HostID: this._hostId, - TransactionID: this._transactionId, - }, - mutable: { - TransactionPhase: 'Transfer', - SegmentNumber: { - '@': { lastSegment: true }, - '#': 1, - }, - }, - }; - - this._xml.body = { - DataTransfer: { - OrderData: this.encryptedOrderData(), - }, - }; - } - } - - 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', - }, - OrderSignatureData: { - SignatureVersion: 'A006', - SignatureValue: this.signatureValue(), - PartnerID: this._partnerId, - UserID: this._userId, - }, - }; - - return js2xmlparser.parse('UserSignatureData', xmlObj, this._xmlOptions); - } - - signatureValue() { - const digested = Crypto.digestWithHash(this._order.document.replace(/\n|\r/g, '')); - - return Crypto.sign(this._keys.a(), digested); - } - - encryptedOrderData() { - const dst = zlib.deflateSync(this._order.document.replace(/\n|\r/g, '')); - const cipher = crypto.createCipheriv('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); - - return Buffer.concat([cipher.update(Crypto.pad(dst)), cipher.final()]).toString('base64'); - } - - encryptedOrderSignature() { - const dst = zlib.deflateSync(this.orderSignature()); - const cipher = crypto.createCipheriv('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); - - return Buffer.concat([cipher.update(Crypto.pad(dst)), cipher.final()]).toString('base64'); - } -}; diff --git a/lib/versions/H004/serializers/StatusSerializer.js b/lib/versions/H004/serializers/StatusSerializer.js deleted file mode 100644 index 580f036..0000000 --- a/lib/versions/H004/serializers/StatusSerializer.js +++ /dev/null @@ -1,66 +0,0 @@ -'use strict'; - -const Crypto = require('../../../crypto/Crypto'); - -const GenericSerializer = require('./GenericSerializer'); - -module.exports = class StatusSerializer extends GenericSerializer { - constructor(order) { - super(order); - - this._xml = { - '@': this._rootAttributes, - header: { - '@': { authenticate: true }, - static: { - HostID: this._hostId, - Nonce: Crypto.nonce(), - Timestamp: Crypto.timestamp(), - PartnerID: this._partnerId, - UserID: this._userId, - Product: { - '@': { Language: 'en' }, - '#': this._productString, - }, - OrderDetails: this._orderDetails, - BankPubKeyDigests: { - Authentication: { - '@': { Version: 'X002', Algorithm: 'http://www.w3.org/2001/04/xmlenc#sha256' }, - '#': Crypto.digestPublicKey(this._keys.bankX()), - }, - Encryption: { - '@': { Version: 'E002', Algorithm: 'http://www.w3.org/2001/04/xmlenc#sha256' }, - '#': Crypto.digestPublicKey(this._keys.bankE()), - }, - }, - SecurityMedium: '0000', - }, - mutable: { - TransactionPhase: 'Initialisation', - }, - }, - AuthSignature: GenericSerializer.authSignature(), - body: {}, - }; - - if (order.hasTransactionId()) { - this._xml.header = { - '@': { authenticate: true }, - static: { - HostID: this._hostId, - TransactionID: this._transactionId, - }, - mutable: { - TransactionPhase: 'Receipt', - }, - }; - - this._xml.body = { - TransferReceipt: { - '@': { authenticate: true }, - ReceiptCode: 0, - }, - }; - } - } -};