001/* 002 * jPOS Project [http://jpos.org] 003 * Copyright (C) 2000-2026 jPOS Software SRL 004 * 005 * This program is free software: you can redistribute it and/or modify 006 * it under the terms of the GNU Affero General Public License as 007 * published by the Free Software Foundation, either version 3 of the 008 * License, or (at your option) any later version. 009 * 010 * This program is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 013 * GNU Affero General Public License for more details. 014 * 015 * You should have received a copy of the GNU Affero General Public License 016 * along with this program. If not, see <http://www.gnu.org/licenses/>. 017 */ 018 019package org.jpos.emv; 020 021import java.io.PrintStream; 022import java.util.Objects; 023 024import org.jpos.iso.ISOUtil; 025import org.jpos.util.Loggeable; 026 027/** 028 * CVR parser based on A.19 CVR, M/Chip 4 Issuer Guide to Debit and Credit Parameter Management, December 2004 029 */ 030public class CVRMastercard implements Loggeable { 031 032 private final byte[] cvr; 033 034 /** 035 * Creates a CVRMastercard from raw bytes. 036 * @param cvr byte array containing the CVR value (must be 6 bytes) 037 */ 038 public CVRMastercard(byte[] cvr) { 039 Objects.requireNonNull("cvr", "CVR cannot be null."); 040 if (cvr.length != 6) 041 throw new IllegalArgumentException( 042 String.format("Invalid length. Expected = 6, actual = %s.", cvr.length)); 043 this.cvr = cvr; 044 } 045 046 /** 047 * Creates a CVRMastercard from a hex string. 048 * @param cvr hexadecimal string representation (must be 12 chars) 049 */ 050 public CVRMastercard(String cvr) { 051 Objects.requireNonNull("cvr", "CVR cannot be null."); 052 cvr = cvr.trim(); 053 if (cvr.length() != 12) 054 throw new IllegalArgumentException( 055 String.format("Invalid length. Expected = 12, actual = %s.", cvr.length())); 056 this.cvr = ISOUtil.hex2byte(cvr); 057 } 058 059 /** 060 * Returns true if aac returned in second generate a c. 061 * @return true if condition applies 062 */ 063 public boolean aacReturnedInSecondGenerateAC() { 064 return !isBitOn(cvr[0], 8) && !isBitOn(cvr[0], 7); 065 } 066 067 /** 068 * Returns true if aac returned in first generate a c. 069 * @return true if condition applies 070 */ 071 public boolean aacReturnedInFirstGenerateAC() { 072 return !isBitOn(cvr[0], 6) && !isBitOn(cvr[0], 5); 073 } 074 075 /** 076 * Returns true if tc returned in second generate a c. 077 * @return true if condition applies 078 */ 079 public boolean tcReturnedInSecondGenerateAC() { 080 return !isBitOn(cvr[0], 8) && isBitOn(cvr[0], 7); 081 } 082 083 /** 084 * Returns true if arqc returned in first generate a c. 085 * @return true if condition applies 086 */ 087 public boolean arqcReturnedInFirstGenerateAC() { 088 return isBitOn(cvr[0], 6) && !isBitOn(cvr[0], 5); 089 } 090 091 /** 092 * Returns true if tc returned in first generate a c. 093 * @return true if condition applies 094 */ 095 public boolean tcReturnedInFirstGenerateAC() { 096 return !isBitOn(cvr[0], 6) && isBitOn(cvr[0], 5); 097 } 098 099 /** 100 * Returns true if offline p i n verification performed. 101 * @return true if condition applies 102 */ 103 public boolean offlinePINVerificationPerformed() { 104 return isBitOn(cvr[0], 3); 105 } 106 107 /** 108 * Returns true if offline p i n verification not performed. 109 * @return true if condition applies 110 */ 111 public boolean offlinePINVerificationNotPerformed() { 112 return isBitOn(cvr[3], 6); 113 } 114 115 /** 116 * Returns true if dda returned. 117 * @return true if condition applies 118 */ 119 public boolean ddaReturned() { 120 return isBitOn(cvr[1], 8); 121 } 122 123 /** 124 * Returns true if combined d d a a c generation returned in first generate a c. 125 * @return true if condition applies 126 */ 127 public boolean combinedDDAACGenerationReturnedInFirstGenerateAC() { 128 return isBitOn(cvr[1], 7); 129 } 130 131 /** 132 * Returns true if combined d d a a c generation returned in second generate a c. 133 * @return true if condition applies 134 */ 135 public boolean combinedDDAACGenerationReturnedInSecondGenerateAC() { 136 return isBitOn(cvr[1], 6); 137 } 138 139 /** 140 * Returns true if issuer authentication failed. 141 * @return true if condition applies 142 */ 143 public boolean issuerAuthenticationFailed() { 144 return isBitOn(cvr[4], 3); 145 } 146 147 /** 148 * Returns true if script received. 149 * @return true if condition applies 150 */ 151 public boolean scriptReceived() { 152 return isBitOn(cvr[4], 2); 153 } 154 155 /** 156 * Returns true if script failed. 157 * @return true if condition applies 158 */ 159 public boolean scriptFailed() { 160 return isBitOn(cvr[4], 1); 161 } 162 163 /** 164 * Returns true if ciac default skipped on c a t3. 165 * @return true if condition applies 166 */ 167 public boolean ciacDefaultSkippedOnCAT3() { 168 return isBitOn(cvr[1], 4); 169 } 170 171 /** 172 * Returns true if match found in additional check table. 173 * @return true if condition applies 174 */ 175 public boolean matchFoundInAdditionalCheckTable() { 176 return isBitOn(cvr[5], 2); 177 } 178 179 /** 180 * Returns true if no match found in additional check table. 181 * @return true if condition applies 182 */ 183 public boolean noMatchFoundInAdditionalCheckTable() { 184 return isBitOn(cvr[5], 1); 185 } 186 187 /** 188 * Returns the right nibble of script counter. 189 * @return the value 190 */ 191 public int rightNibbleOfScriptCounter() { 192 StringBuilder sb = new StringBuilder(); 193 sb.append(isBitOn(cvr[2], 8) ? "1" : "0"); 194 sb.append(isBitOn(cvr[2], 7) ? "1" : "0"); 195 sb.append(isBitOn(cvr[2], 6) ? "1" : "0"); 196 sb.append(isBitOn(cvr[2], 5) ? "1" : "0"); 197 return Integer.parseInt(sb.toString(), 2); 198 } 199 200 /** 201 * Returns the right nibble of p i n try counter. 202 * @return the value 203 */ 204 public int rightNibbleOfPINTryCounter() { 205 StringBuilder sb = new StringBuilder(); 206 sb.append(isBitOn(cvr[2], 4) ? "1" : "0"); 207 sb.append(isBitOn(cvr[2], 3) ? "1" : "0"); 208 sb.append(isBitOn(cvr[2], 2) ? "1" : "0"); 209 sb.append(isBitOn(cvr[2], 1) ? "1" : "0"); 210 return Integer.parseInt(sb.toString(), 2); 211 } 212 213 /** 214 * Returns true if offline p i n verification failed. 215 * @return true if condition applies 216 */ 217 public boolean offlinePINVerificationFailed() { 218 return isBitOn(cvr[0], 2); 219 } 220 221 /** 222 * Returns true if ptl exceeded. 223 * @return true if condition applies 224 */ 225 public boolean ptlExceeded() { 226 return isBitOn(cvr[0], 2); 227 } 228 229 /** 230 * Returns true if international transaction. 231 * @return true if condition applies 232 */ 233 public boolean internationalTransaction() { 234 return isBitOn(cvr[3], 3); 235 } 236 237 /** 238 * Returns true if domestic transaction. 239 * @return true if condition applies 240 */ 241 public boolean domesticTransaction() { 242 return isBitOn(cvr[3], 2); 243 } 244 245 /** 246 * Returns true if terminal erroneously considers offline p i n o k. 247 * @return true if condition applies 248 */ 249 public boolean terminalErroneouslyConsidersOfflinePINOK() { 250 return isBitOn(cvr[3], 1); 251 } 252 253 /** 254 * Returns true if lower consecutive offline limit exceeded. 255 * @return true if condition applies 256 */ 257 public boolean lowerConsecutiveOfflineLimitExceeded() { 258 return isBitOn(cvr[4], 8); 259 } 260 261 /** 262 * Returns true if upper consecutive offline limit exceeded. 263 * @return true if condition applies 264 */ 265 public boolean upperConsecutiveOfflineLimitExceeded() { 266 return isBitOn(cvr[4], 7); 267 } 268 269 /** 270 * Returns true if lower cumulative offline limit exceeded. 271 * @return true if condition applies 272 */ 273 public boolean lowerCumulativeOfflineLimitExceeded() { 274 return isBitOn(cvr[4], 6); 275 } 276 277 /** 278 * Returns true if upper cumulative offline limit exceeded. 279 * @return true if condition applies 280 */ 281 public boolean upperCumulativeOfflineLimitExceeded() { 282 return isBitOn(cvr[4], 5); 283 } 284 285 /** 286 * Returns true if go online on next transaction set. 287 * @return true if condition applies 288 */ 289 public boolean goOnlineOnNextTransactionSet() { 290 return isBitOn(cvr[4], 4); 291 } 292 293 /** 294 * Returns true if unable to go online. 295 * @return true if condition applies 296 */ 297 public boolean unableToGoOnline() { 298 return isBitOn(cvr[3], 7); 299 } 300 301 /** 302 * Returns true if second generate a c not requested. 303 * @return true if condition applies 304 */ 305 public boolean secondGenerateACNotRequested() { 306 return isBitOn(cvr[0], 8) && !isBitOn(cvr[0], 7); 307 } 308 309 /** 310 * Returns true if issuer authentication performed. 311 * @return true if condition applies 312 */ 313 public boolean issuerAuthenticationPerformed() { 314 return isBitOn(cvr[1], 5); 315 } 316 317 /** 318 * Returns true if offline encrypted p i n verification performed. 319 * @return true if condition applies 320 */ 321 public boolean offlineEncryptedPINVerificationPerformed() { 322 return isBitOn(cvr[0], 2); 323 } 324 325 private boolean isBitOn(byte value, int position) { 326 return ((value >> (position - 1)) & 1) == 1; 327 } 328 329 @Override 330 public void dump(PrintStream p, String indent) { 331 String inner = indent + " "; 332 String inner2 = inner + " "; 333 StringBuilder sb = new StringBuilder(); 334 p.printf("%s<cvr-mastercard value='%s'>%s%n", indent, ISOUtil.hexString(cvr), sb.toString()); 335 336 p.printf("%sBYTE 1:%n", inner); 337 if (aacReturnedInSecondGenerateAC()) 338 p.printf("%sACC RETURNED IN SECOND GENERATE AC%n", inner2); 339 if (tcReturnedInSecondGenerateAC()) 340 p.printf("%sTC RETURNED IN SECOND GENERATE AC%n", inner2); 341 if (secondGenerateACNotRequested()) 342 p.printf("%sSECOND GENERATE AC NOT REQUESTED%n", inner2); 343 if (aacReturnedInFirstGenerateAC()) 344 p.printf("%sACC RETURNED IN FIRST GENERATE AC%n", inner2); 345 if (tcReturnedInFirstGenerateAC()) 346 p.printf("%sTC RETURNED IN FIRST GENERATE AC%n", inner2); 347 if (arqcReturnedInFirstGenerateAC()) 348 p.printf("%sARQC RETURNED IN FIRST GENERATE AC%n", inner2); 349 if (offlinePINVerificationPerformed()) 350 p.printf("%sOFFLINE PIN VERIFICATION PERFORMED%n", inner2); 351 if (offlineEncryptedPINVerificationPerformed()) 352 p.printf("%sOFFLINE ENCRYPTED PIN VERIFICATION PERFORMED%n", inner2); 353 354 p.printf("%n%sBYTE 2:%n", inner); 355 if (ddaReturned()) 356 p.printf("%sDDA RETURNED%n", inner2); 357 if (combinedDDAACGenerationReturnedInFirstGenerateAC()) 358 p.printf("%sCOMBINED DDA/AC GENERATION RETURNED IN FIRST GENERATE AC%n", inner2); 359 if (combinedDDAACGenerationReturnedInSecondGenerateAC()) 360 p.printf("%sCOMBINED DDA/AC GENERATION RETURNED IN SECOND GENERATE AC%n", inner2); 361 if (issuerAuthenticationPerformed()) 362 p.printf("%sISSUER AUTHENTICATION PEFORMED%n", inner2); 363 if (ciacDefaultSkippedOnCAT3()) 364 p.printf("%sCIAC-DEFAULT SKIPPED ON CAT3%n", inner2); 365 366 p.printf("%n%sBYTE 3:%n", inner); 367 p.printf("%sRIGHT NIBBLE OF SCRIPT COUNTER = %s%n", inner2, rightNibbleOfScriptCounter()); 368 p.printf("%sRIGHT NIBBLE OF PIN TRY COUNTER = %s%n", inner2, rightNibbleOfPINTryCounter()); 369 370 p.printf("%n%sBYTE 4:%n", inner); 371 if (unableToGoOnline()) 372 p.printf("%sUNABLE TO GO ONLINE INDICATED%n", inner2); 373 if (offlinePINVerificationNotPerformed()) 374 p.printf("%sOFFLINE PIN VERIFICATION NOT PERFORMED%n", inner2); 375 if (offlinePINVerificationFailed()) 376 p.printf("%sOFFLINE PIN VERIFICATION FAILED%n", inner2); 377 if (ptlExceeded()) 378 p.printf("%sPTL EXCEEDED%n", inner2); 379 if (internationalTransaction()) 380 p.printf("%sINTERNATIONAL TRANSACTION%n", inner2); 381 if (domesticTransaction()) 382 p.printf("%sDOMESTIC TRANSACTION%n", inner2); 383 if (terminalErroneouslyConsidersOfflinePINOK()) 384 p.printf("%sTERMINAL ERRONEOUSLY CONSIDERS OFFLINE PIN OK%n", inner2); 385 386 p.printf("%n%sBYTE 5:%n", inner); 387 if (lowerConsecutiveOfflineLimitExceeded()) 388 p.printf("%sLOWER CONSECUTIVE OFFLINE LIMIT EXCEEDED%n", inner2); 389 if (upperConsecutiveOfflineLimitExceeded()) 390 p.printf("%sUPPER CONSECUTIVE OFFLINE LIMIT EXCEEDED%n", inner2); 391 if (lowerCumulativeOfflineLimitExceeded()) 392 p.printf("%sLOWER CUMULATIVE OFFLINE LIMIT EXCEEDED%n", inner2); 393 if (upperCumulativeOfflineLimitExceeded()) 394 p.printf("%sUPPER CUMULATIVE OFFLINE LIMIT EXCEEDED%n", inner2); 395 if (goOnlineOnNextTransactionSet()) 396 p.printf("%sGO ONLINE ON NEXT TRANSACTION WAS SET%n", inner2); 397 if (issuerAuthenticationFailed()) 398 p.printf("%sISSUER AUTHENTICATION FAILED%n", inner2); 399 if (scriptReceived()) 400 p.printf("%sSCRIPT RECEIVED%n", inner2); 401 if (scriptFailed()) 402 p.printf("%sSCRIPT FAILED%n", inner2); 403 404 p.printf("%n%sBYTE 6:%n", inner); 405 if (matchFoundInAdditionalCheckTable()) 406 p.printf("%sMATCH FOUND IN ADDITIONAL CHECK TABLE%n", inner2); 407 if (noMatchFoundInAdditionalCheckTable()) 408 p.printf("%sNO MATCH FOUND IN ADDITIONAL CHECK TABLE%n", inner2); 409 410 p.printf("%s</cvr-mastercard>%n", indent); 411 } 412}