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 * 036 * @param cvr Byte array containing the CVR value. 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 * 048 * @param cvr Hexadecimal string representation of the CVR value. 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 public boolean aacReturnedInSecondGenerateAC() { 060 return !isBitOn(cvr[0], 8) && !isBitOn(cvr[0], 7); 061 } 062 063 public boolean aacReturnedInFirstGenerateAC() { 064 return !isBitOn(cvr[0], 6) && !isBitOn(cvr[0], 5); 065 } 066 067 public boolean tcReturnedInSecondGenerateAC() { 068 return !isBitOn(cvr[0], 8) && isBitOn(cvr[0], 7); 069 } 070 071 public boolean arqcReturnedInFirstGenerateAC() { 072 return isBitOn(cvr[0], 6) && !isBitOn(cvr[0], 5); 073 } 074 075 public boolean tcReturnedInFirstGenerateAC() { 076 return !isBitOn(cvr[0], 6) && isBitOn(cvr[0], 5); 077 } 078 079 public boolean offlinePINVerificationPerformed() { 080 return isBitOn(cvr[0], 3); 081 } 082 083 public boolean offlinePINVerificationNotPerformed() { 084 return isBitOn(cvr[3], 6); 085 } 086 087 public boolean ddaReturned() { 088 return isBitOn(cvr[1], 8); 089 } 090 091 public boolean combinedDDAACGenerationReturnedInFirstGenerateAC() { 092 return isBitOn(cvr[1], 7); 093 } 094 095 public boolean combinedDDAACGenerationReturnedInSecondGenerateAC() { 096 return isBitOn(cvr[1], 6); 097 } 098 099 public boolean issuerAuthenticationFailed() { 100 return isBitOn(cvr[4], 3); 101 } 102 103 public boolean scriptReceived() { 104 return isBitOn(cvr[4], 2); 105 } 106 107 public boolean scriptFailed() { 108 return isBitOn(cvr[4], 1); 109 } 110 111 public boolean ciacDefaultSkippedOnCAT3() { 112 return isBitOn(cvr[1], 4); 113 } 114 115 public boolean matchFoundInAdditionalCheckTable() { 116 return isBitOn(cvr[5], 2); 117 } 118 119 public boolean noMatchFoundInAdditionalCheckTable() { 120 return isBitOn(cvr[5], 1); 121 } 122 123 public int rightNibbleOfScriptCounter() { 124 StringBuilder sb = new StringBuilder(); 125 sb.append(isBitOn(cvr[2], 8) ? "1" : "0"); 126 sb.append(isBitOn(cvr[2], 7) ? "1" : "0"); 127 sb.append(isBitOn(cvr[2], 6) ? "1" : "0"); 128 sb.append(isBitOn(cvr[2], 5) ? "1" : "0"); 129 return Integer.parseInt(sb.toString(), 2); 130 } 131 132 public int rightNibbleOfPINTryCounter() { 133 StringBuilder sb = new StringBuilder(); 134 sb.append(isBitOn(cvr[2], 4) ? "1" : "0"); 135 sb.append(isBitOn(cvr[2], 3) ? "1" : "0"); 136 sb.append(isBitOn(cvr[2], 2) ? "1" : "0"); 137 sb.append(isBitOn(cvr[2], 1) ? "1" : "0"); 138 return Integer.parseInt(sb.toString(), 2); 139 } 140 141 public boolean offlinePINVerificationFailed() { 142 return isBitOn(cvr[0], 2); 143 } 144 145 public boolean ptlExceeded() { 146 return isBitOn(cvr[0], 2); 147 } 148 149 public boolean internationalTransaction() { 150 return isBitOn(cvr[3], 3); 151 } 152 153 public boolean domesticTransaction() { 154 return isBitOn(cvr[3], 2); 155 } 156 157 public boolean terminalErroneouslyConsidersOfflinePINOK() { 158 return isBitOn(cvr[3], 1); 159 } 160 161 public boolean lowerConsecutiveOfflineLimitExceeded() { 162 return isBitOn(cvr[4], 8); 163 } 164 165 public boolean upperConsecutiveOfflineLimitExceeded() { 166 return isBitOn(cvr[4], 7); 167 } 168 169 public boolean lowerCumulativeOfflineLimitExceeded() { 170 return isBitOn(cvr[4], 6); 171 } 172 173 public boolean upperCumulativeOfflineLimitExceeded() { 174 return isBitOn(cvr[4], 5); 175 } 176 177 public boolean goOnlineOnNextTransactionSet() { 178 return isBitOn(cvr[4], 4); 179 } 180 181 public boolean unableToGoOnline() { 182 return isBitOn(cvr[3], 7); 183 } 184 185 public boolean secondGenerateACNotRequested() { 186 return isBitOn(cvr[0], 8) && !isBitOn(cvr[0], 7); 187 } 188 189 public boolean issuerAuthenticationPerformed() { 190 return isBitOn(cvr[1], 5); 191 } 192 193 public boolean offlineEncryptedPINVerificationPerformed() { 194 return isBitOn(cvr[0], 2); 195 } 196 197 private boolean isBitOn(byte value, int position) { 198 return ((value >> (position - 1)) & 1) == 1; 199 } 200 201 @Override 202 public void dump(PrintStream p, String indent) { 203 String inner = indent + " "; 204 String inner2 = inner + " "; 205 StringBuilder sb = new StringBuilder(); 206 p.printf("%s<cvr-mastercard value='%s'>%s%n", indent, ISOUtil.hexString(cvr), sb.toString()); 207 208 p.printf("%sBYTE 1:%n", inner); 209 if (aacReturnedInSecondGenerateAC()) 210 p.printf("%sACC RETURNED IN SECOND GENERATE AC%n", inner2); 211 if (tcReturnedInSecondGenerateAC()) 212 p.printf("%sTC RETURNED IN SECOND GENERATE AC%n", inner2); 213 if (secondGenerateACNotRequested()) 214 p.printf("%sSECOND GENERATE AC NOT REQUESTED%n", inner2); 215 if (aacReturnedInFirstGenerateAC()) 216 p.printf("%sACC RETURNED IN FIRST GENERATE AC%n", inner2); 217 if (tcReturnedInFirstGenerateAC()) 218 p.printf("%sTC RETURNED IN FIRST GENERATE AC%n", inner2); 219 if (arqcReturnedInFirstGenerateAC()) 220 p.printf("%sARQC RETURNED IN FIRST GENERATE AC%n", inner2); 221 if (offlinePINVerificationPerformed()) 222 p.printf("%sOFFLINE PIN VERIFICATION PERFORMED%n", inner2); 223 if (offlineEncryptedPINVerificationPerformed()) 224 p.printf("%sOFFLINE ENCRYPTED PIN VERIFICATION PERFORMED%n", inner2); 225 226 p.printf("%n%sBYTE 2:%n", inner); 227 if (ddaReturned()) 228 p.printf("%sDDA RETURNED%n", inner2); 229 if (combinedDDAACGenerationReturnedInFirstGenerateAC()) 230 p.printf("%sCOMBINED DDA/AC GENERATION RETURNED IN FIRST GENERATE AC%n", inner2); 231 if (combinedDDAACGenerationReturnedInSecondGenerateAC()) 232 p.printf("%sCOMBINED DDA/AC GENERATION RETURNED IN SECOND GENERATE AC%n", inner2); 233 if (issuerAuthenticationPerformed()) 234 p.printf("%sISSUER AUTHENTICATION PEFORMED%n", inner2); 235 if (ciacDefaultSkippedOnCAT3()) 236 p.printf("%sCIAC-DEFAULT SKIPPED ON CAT3%n", inner2); 237 238 p.printf("%n%sBYTE 3:%n", inner); 239 p.printf("%sRIGHT NIBBLE OF SCRIPT COUNTER = %s%n", inner2, rightNibbleOfScriptCounter()); 240 p.printf("%sRIGHT NIBBLE OF PIN TRY COUNTER = %s%n", inner2, rightNibbleOfPINTryCounter()); 241 242 p.printf("%n%sBYTE 4:%n", inner); 243 if (unableToGoOnline()) 244 p.printf("%sUNABLE TO GO ONLINE INDICATED%n", inner2); 245 if (offlinePINVerificationNotPerformed()) 246 p.printf("%sOFFLINE PIN VERIFICATION NOT PERFORMED%n", inner2); 247 if (offlinePINVerificationFailed()) 248 p.printf("%sOFFLINE PIN VERIFICATION FAILED%n", inner2); 249 if (ptlExceeded()) 250 p.printf("%sPTL EXCEEDED%n", inner2); 251 if (internationalTransaction()) 252 p.printf("%sINTERNATIONAL TRANSACTION%n", inner2); 253 if (domesticTransaction()) 254 p.printf("%sDOMESTIC TRANSACTION%n", inner2); 255 if (terminalErroneouslyConsidersOfflinePINOK()) 256 p.printf("%sTERMINAL ERRONEOUSLY CONSIDERS OFFLINE PIN OK%n", inner2); 257 258 p.printf("%n%sBYTE 5:%n", inner); 259 if (lowerConsecutiveOfflineLimitExceeded()) 260 p.printf("%sLOWER CONSECUTIVE OFFLINE LIMIT EXCEEDED%n", inner2); 261 if (upperConsecutiveOfflineLimitExceeded()) 262 p.printf("%sUPPER CONSECUTIVE OFFLINE LIMIT EXCEEDED%n", inner2); 263 if (lowerCumulativeOfflineLimitExceeded()) 264 p.printf("%sLOWER CUMULATIVE OFFLINE LIMIT EXCEEDED%n", inner2); 265 if (upperCumulativeOfflineLimitExceeded()) 266 p.printf("%sUPPER CUMULATIVE OFFLINE LIMIT EXCEEDED%n", inner2); 267 if (goOnlineOnNextTransactionSet()) 268 p.printf("%sGO ONLINE ON NEXT TRANSACTION WAS SET%n", inner2); 269 if (issuerAuthenticationFailed()) 270 p.printf("%sISSUER AUTHENTICATION FAILED%n", inner2); 271 if (scriptReceived()) 272 p.printf("%sSCRIPT RECEIVED%n", inner2); 273 if (scriptFailed()) 274 p.printf("%sSCRIPT FAILED%n", inner2); 275 276 p.printf("%n%sBYTE 6:%n", inner); 277 if (matchFoundInAdditionalCheckTable()) 278 p.printf("%sMATCH FOUND IN ADDITIONAL CHECK TABLE%n", inner2); 279 if (noMatchFoundInAdditionalCheckTable()) 280 p.printf("%sNO MATCH FOUND IN ADDITIONAL CHECK TABLE%n", inner2); 281 282 p.printf("%s</cvr-mastercard>%n", indent); 283 } 284}