-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathpayshield.js
234 lines (218 loc) · 9.63 KB
/
payshield.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
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;