UNPKG

ut-codec

Version:
235 lines (218 loc) 9.86 kB
var bitsyntax = require('ut-bitsyntax'); var merge = require('lodash.merge'); var defaultFormat = require('./payshield.messages.json'); const ERRORCODES = { '00': 'No error', '01': 'Verification failure or warning of imported key parity error', '02': 'Key inappropriate length for algorithm', '04': 'Invalid key type code', '05': 'Invalid key length flag', '10': 'Source key parity error', '11': 'Destination key parity error or key all zeros', '12': 'Contents of user storage not available. Reset, power-down or overwrite', '13': 'Invalid LMK Identifier', '14': 'PIN encrypted under LMK pair 02-03 is invalid', '15': 'Invalid input data (invalid format, invalid characters, or not enough data provided)', '16': 'Console or printer not ready or not connected', '17': 'HSM not in the Authorised state, or not enabled for clear PIN output, or both', '18': 'Document format definition not loaded', '19': 'Specified Diebold Table is invalid', '20': 'PIN block does not contain valid values', '21': 'Invalid index value, or index/block count would cause an overflow condition', '22': 'Invalid account number', '23': 'Invalid PIN block format code', '24': 'PIN is fewer than 4 or more than 12 digits in length', '25': 'Decimalisation Table error', '26': 'Invalid key scheme', '27': 'Incompatible key length', '28': 'Invalid key type', '29': 'Key function not permitted', '30': 'Invalid reference number', '31': 'Insufficient solicitation entries for batch', '33': 'LMK key change storage is corrupted', '39': 'Fraud detection', '40': 'Invalid checksum', '41': 'Internal hardware/software error: bad RAM, invalid error codes, etc.', '42': 'DES failure', '47': 'Algorithm not licensed', '49': 'Private key error, report to supervisor', '51': 'Invalid message header', '65': 'Transaction Key Scheme set to None', '67': 'Command not licensed', '68': 'Command has been disabled', '69': 'PIN block format has been disabled', '74': 'Invalid digest info syntax (no hash mode only)', '75': 'Single length key masquerading as double or triple length key', '76': 'Public key length error', '77': 'Clear data block error', '78': 'Private key length error', '79': 'Hash algorithm object identifier error', '80': 'Data length error. The amount of MAC data (or other data) is greater than or less than the expected amount.', '81': 'Invalid certificate header', '82': 'Invalid check value length', '83': 'Key block format error', '84': 'Key block check value error', '85': 'Invalid OAEP Mask Generation Function', '86': 'Invalid OAEP MGF Hash Function', '87': 'OAEP Parameter Error', '90': 'Data parity error in the request message received by the HSM', '91': 'Longitudinal Redundancy Check (LRC) character does not match the value computed over the input data (when the HSM has received a transparent async packet)', '92': 'The Count value (for the Command/Data field) is not between limits, or is not correct (when the HSM has received a transparent async packet)', 'A1': 'Incompatible LMK schemes', 'A2': 'Incompatible LMK identifiers', 'A3': 'Incompatible keyblock LMK identifiers', 'A4': 'Key block authentication failure', 'A5': 'Incompatible key length', 'A6': 'Invalid key usage', 'A7': 'Invalid algorithm', 'A8': 'Invalid mode of use', 'A9': 'Invalid key version number', 'AA': 'Invalid export field', 'AB': 'Invalid number of optional blocks', 'AC': 'Optional header block error', 'AD': 'Key status optional block error', 'AE': 'Invalid start date/time', 'AF': 'Invalid end date/time', 'B0': 'Invalid encryption mode', 'B1': 'Invalid authentication mode', 'B2': 'Miscellaneous keyblock error', 'B3': 'Invalid number of optional blocks', 'B4': 'Optional block data error', 'B5': 'Incompatible components', 'B6': 'Incompatible key status optional blocks', 'B7': 'Invalid change field', 'B8': 'Invalid old value', 'B9': 'Invalid new value', 'BA': 'No key status block in the keyblock', 'BB': 'Invalid wrapping key', 'BC': 'Repeated optional block', 'BD': 'Incompatible key types' }; function PayshieldCodec(config, val, log) { this.logFactory = log; this.log = {}; this.val = val; this.commands = {}; this.headerPattern = null; this.commandNames = {}; this.init(config); } PayshieldCodec.prototype.init = function(config) { this.logFactory && (this.log = this.logFactory.createLog(config.logLevel, { name: config.id, context: 'PayShield codec' })); if (this.log.info) { this.log.info('Initializing Payshield parser! headerFormat: ' + config.headerFormat + ',messageFormat:' + config.messageFormat); } this.headerPattern = bitsyntax.parse('headerNo:' + config.headerFormat + ', code:2/string, body/binary'); this.headerMatcher = bitsyntax.matcher('headerNo:' + config.headerFormat + ', code:2/string, body/binary'); this.errorMatcher = bitsyntax.matcher('errorCode:2/string, rest/binary'); var commandsObj = merge({}, defaultFormat, config.messageFormat); if (this.headerPattern === false) { throw new Error('Cant parse header pattern!'); } for (var property in commandsObj) { if (commandsObj.hasOwnProperty(property)) { if (commandsObj[property].requestPattern) { var requestPattern = bitsyntax.parse(commandsObj[property].requestPattern); if (!requestPattern) { throw new Error('Cant parse request pattern for command:' + property + '!'); } this.commands[property + ':request'] = { pattern: requestPattern, matcher: bitsyntax.matcher(commandsObj[property].requestPattern), code: commandsObj[property].requestCode, method: property, mtid: 'request' }; this.commandNames[commandsObj[property].requestCode] = property + ':request'; } if (commandsObj[property].responsePattern) { var responsePattern = bitsyntax.parse(commandsObj[property].responsePattern); if (!responsePattern) { throw new Error('Cant parse response pattern for command:' + property + '!'); } this.commands[property + ':response'] = { pattern: responsePattern, matcher: bitsyntax.matcher(commandsObj[property].responsePattern), errorMatcher: commandsObj[property].errorPattern && bitsyntax.matcher(commandsObj[property].errorPattern), code: commandsObj[property].responseCode, method: property, mtid: 'response' }; this.commandNames[commandsObj[property].responseCode] = property + ':response'; } } } }; PayshieldCodec.prototype.decode = function(buff, $meta) { if (this.log.debug) { this.log.debug('PayshieldParser.decode buffer:' + buff.toString()); } var headObj = this.headerMatcher(buff); if (!headObj) { throw new Error('Unable to match header to header pattern!'); } var commandName = this.commandNames[headObj.code]; if (!commandName) { throw new Error('Unknown response code:' + headObj.code); } var cmd = this.commands[commandName]; if (!cmd) { throw new Error('Not implemented opcode:' + commandName + '!'); } var bodyObj = this.errorMatcher(headObj.body); if (!bodyObj) { throw new Error('Unable to match response errorCode!'); } // 00 = No error // 02 = Key inappropriate length for algorithm (in some cases is warning) if (['00', '02'].includes(bodyObj.errorCode)) { bodyObj = cmd.matcher(headObj.body); if (!bodyObj) { throw new Error('Unable to match pattern for opcode:' + commandName + '!'); } $meta.trace = headObj.headerNo; $meta.mtid = cmd.mtid; $meta.method = cmd.method; } else { $meta.trace = headObj.headerNo; $meta.mtid = 'error'; $meta.method = cmd.method; if (cmd.errorMatcher) { // try to match errorPattern if it exists bodyObj = cmd.errorMatcher(headObj.body) || bodyObj; } bodyObj.type = 'payshield.' + bodyObj.errorCode; bodyObj.errorCode = 'payshield.' + bodyObj.errorCode; bodyObj.errorMessage = cmd.method + ':' + (ERRORCODES[bodyObj.errorCode] || bodyObj.errorCode); } return bodyObj; }; PayshieldCodec.prototype.encode = function(data, $meta, context) { // TODO: add validation this.log.debug && this.log.debug('PayshieldParser.encode data:' + data); var commandName = $meta.method.split('.').pop() + ':' + $meta.mtid; if (this.commands[commandName] === undefined) { throw new Error('Not implemented opcode:' + commandName + '!'); } var headerNo = $meta.trace; if (headerNo === undefined || headerNo === null) { headerNo = $meta.trace = ('000000' + context.trace).substr(-6); context.trace += 1; if (context.trace > 999999) { context.trace = 0; } } var bodyBuff = bitsyntax.build(this.commands[commandName].pattern, data); if (!bodyBuff) { throw new Error('Unable to build body of opcode:' + commandName + '!'); } var cmdCode = this.commands[commandName].code; return bitsyntax.build(this.headerPattern, { headerNo: headerNo, code: cmdCode, body: bodyBuff }); }; module.exports = PayshieldCodec;