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 org.jpos.iso.ISOUtil; 022import org.jpos.util.Loggeable; 023 024import java.io.PrintStream; 025import java.util.Objects; 026 027/** 028 * Terminal verification results (TVR) parser. 029 */ 030public final class TerminalVerificationResults implements Loggeable { 031 032 /** Raw 5-byte TVR value. */ 033 private final byte[] tvr; 034 035 /** 036 * Constructs a TerminalVerificationResults from a 5-byte TVR array. 037 * 038 * @param tvr the 5-byte Terminal Verification Results value 039 * @throws IllegalArgumentException if the array length is not 5 040 */ 041 public TerminalVerificationResults(byte[] tvr) { 042 Objects.requireNonNull(tvr); 043 if (tvr.length != 5) 044 throw new IllegalArgumentException("TVR length must be 5."); 045 this.tvr = tvr; 046 } 047 048 /** 049 * Constructs a TerminalVerificationResults from a hex-encoded TVR string. 050 * 051 * @param hexTVR 10-character hexadecimal string representing the 5-byte TVR 052 */ 053 public TerminalVerificationResults(String hexTVR) { 054 this(ISOUtil.hex2byte(hexTVR)); 055 } 056 057 /** 058 * Returns {@code true} if offline data authentication was not performed. 059 * 060 * @return {@code true} if byte 1 bit 8 is set 061 */ 062 public boolean offlineDataProcNotPerformed() { 063 return isBitOn(tvr[0], 8); 064 } 065 066 /** 067 * Returns {@code true} if Static Data Authentication (SDA) failed. 068 * 069 * @return {@code true} if byte 1 bit 7 is set 070 */ 071 public boolean sdaFailed() { 072 return isBitOn(tvr[0], 7); 073 } 074 075 /** 076 * Returns {@code true} if ICC data is missing. 077 * 078 * @return {@code true} if byte 1 bit 6 is set 079 */ 080 public boolean iccDataMissing() { 081 return isBitOn(tvr[0], 6); 082 } 083 084 /** 085 * Returns {@code true} if the PAN appears in the hotlist. 086 * 087 * @return {@code true} if byte 1 bit 5 is set 088 */ 089 public boolean panInHotlist() { 090 return isBitOn(tvr[0], 5); 091 } 092 093 /** 094 * Returns {@code true} if Dynamic Data Authentication (DDA) failed. 095 * 096 * @return {@code true} if byte 1 bit 4 is set 097 */ 098 public boolean ddaFailed() { 099 return isBitOn(tvr[0], 4); 100 } 101 102 /** 103 * Returns {@code true} if Combined DDA/Application Cryptogram Generation (CDA) failed. 104 * 105 * @return {@code true} if byte 1 bit 3 is set 106 */ 107 public boolean cdaFailed() { 108 return isBitOn(tvr[0], 3); 109 } 110 111 /** 112 * Returns {@code true} if SDA was selected. 113 * 114 * @return {@code true} if byte 1 bit 2 is set 115 */ 116 public boolean sdaSelected() { 117 return isBitOn(tvr[0], 2); 118 } 119 120 /** 121 * Returns {@code true} if any reserved-for-future-use (RFU) bit is unset as expected. 122 * 123 * @return {@code true} if none of the RFU bits are unexpectedly set 124 */ 125 public boolean rfu() { 126 return !isBitOn(tvr[0], 1) || !isBitOn(tvr[1], 1) || 127 !isBitOn(tvr[1], 2) || !isBitOn(tvr[1], 3) || 128 !isBitOn(tvr[2], 2) || !isBitOn(tvr[2], 1) || 129 !isBitOn(tvr[3], 3) || !isBitOn(tvr[3], 2) || 130 !isBitOn(tvr[3], 1) || 131 (isBitOn(tvr[4], 2) && isBitOn(tvr[4], 1)); 132 } 133 134 /** 135 * Returns {@code true} if the card and terminal have different application versions. 136 * 137 * @return {@code true} if byte 2 bit 8 is set 138 */ 139 public boolean cardAndTerminalDiffApps() { 140 return isBitOn(tvr[1], 8); 141 } 142 143 /** 144 * Returns {@code true} if the application has expired. 145 * 146 * @return {@code true} if byte 2 bit 7 is set 147 */ 148 public boolean expiredApplication() { 149 return isBitOn(tvr[1], 7); 150 } 151 152 /** 153 * Returns {@code true} if the application is not yet effective. 154 * 155 * @return {@code true} if byte 2 bit 6 is set 156 */ 157 public boolean applicationNotEffective() { 158 return isBitOn(tvr[1], 6); 159 } 160 161 /** 162 * Returns {@code true} if the service is not allowed for the card product. 163 * 164 * @return {@code true} if byte 2 bit 5 is set 165 */ 166 public boolean serviceNotAllowedForCardProduct() { 167 return isBitOn(tvr[1], 5); 168 } 169 170 /** 171 * Returns {@code true} if this is a new card (first transaction). 172 * 173 * @return {@code true} if byte 2 bit 4 is set 174 */ 175 public boolean newCard() { 176 return isBitOn(tvr[1], 4); 177 } 178 179 /** 180 * Returns {@code true} if cardholder verification was not successful. 181 * 182 * @return {@code true} if byte 3 bit 8 is set 183 */ 184 public boolean cardholderVerificationNotSuccessful() { 185 return isBitOn(tvr[2], 8); 186 } 187 188 /** 189 * Returns {@code true} if an unrecognised CVM (Cardholder Verification Method) was encountered. 190 * 191 * @return {@code true} if byte 3 bit 7 is set 192 */ 193 public boolean unrecognisedCVM() { 194 return isBitOn(tvr[2], 7); 195 } 196 197 /** 198 * Returns {@code true} if the PIN try limit has been exceeded. 199 * 200 * @return {@code true} if byte 3 bit 6 is set 201 */ 202 public boolean pinTryLimitExceeded() { 203 return isBitOn(tvr[2], 6); 204 } 205 206 /** 207 * Returns {@code true} if a PIN is required but no PIN pad is present. 208 * 209 * @return {@code true} if byte 3 bit 5 is set 210 */ 211 public boolean pinRequiredButNoPinPadPresent() { 212 return isBitOn(tvr[2], 5); 213 } 214 215 /** 216 * Returns {@code true} if a PIN is required but was not entered. 217 * 218 * @return {@code true} if byte 3 bit 4 is set 219 */ 220 public boolean pinRequiredButNotEntered() { 221 return isBitOn(tvr[2], 4); 222 } 223 224 /** 225 * Returns {@code true} if an online PIN was entered. 226 * 227 * @return {@code true} if byte 3 bit 3 is set 228 */ 229 public boolean onlinePINEntered() { 230 return isBitOn(tvr[2], 3); 231 } 232 233 /** 234 * Returns {@code true} if the transaction amount exceeds the floor limit. 235 * 236 * @return {@code true} if byte 4 bit 8 is set 237 */ 238 public boolean transactionExceedsFloorLimit() { 239 return isBitOn(tvr[3], 8); 240 } 241 242 /** 243 * Returns {@code true} if the lower consecutive offline limit was exceeded. 244 * 245 * @return {@code true} if byte 4 bit 7 is set 246 */ 247 public boolean lowerConsecutiveOfflineLimitExceeded() { 248 return isBitOn(tvr[3], 7); 249 } 250 251 /** 252 * Returns {@code true} if the upper consecutive offline limit was exceeded. 253 * 254 * @return {@code true} if byte 4 bit 6 is set 255 */ 256 public boolean upperConsecutiveOfflineLimitExceeded() { 257 return isBitOn(tvr[3], 6); 258 } 259 260 /** 261 * Returns {@code true} if the transaction was selected randomly for online processing. 262 * 263 * @return {@code true} if byte 4 bit 5 is set 264 */ 265 public boolean transactionSelectedRandomlyOnlineProcessing() { 266 return isBitOn(tvr[3], 5); 267 } 268 269 /** 270 * Returns {@code true} if the merchant forced the transaction online. 271 * 272 * @return {@code true} if byte 4 bit 4 is set 273 */ 274 public boolean merchantForcedTransactionOnline() { 275 return isBitOn(tvr[3], 4); 276 } 277 278 /** 279 * Returns {@code true} if the default TDOL (Transaction Data Object List) was used. 280 * 281 * @return {@code true} if byte 5 bit 8 is set 282 */ 283 public boolean defaultTDOLUsed() { 284 return isBitOn(tvr[4], 8); 285 } 286 287 /** 288 * Returns {@code true} if issuer authentication failed. 289 * 290 * @return {@code true} if byte 5 bit 7 is set 291 */ 292 public boolean issuerAuthenticationFailed() { 293 return isBitOn(tvr[4], 7); 294 } 295 296 /** 297 * Returns {@code true} if an issuer script failed before the final Generate AC command. 298 * 299 * @return {@code true} if byte 5 bit 6 is set 300 */ 301 public boolean scriptFailedBeforeFinalGenerateAC() { 302 return isBitOn(tvr[4], 6); 303 } 304 305 /** 306 * Returns {@code true} if an issuer script failed after the final Generate AC command. 307 * 308 * @return {@code true} if byte 5 bit 5 is set 309 */ 310 public boolean scriptFailedAfterFinalGenerateAC() { 311 return isBitOn(tvr[4], 5); 312 } 313 314 /** 315 * Returns {@code true} if the relay resistance threshold was exceeded. 316 * 317 * @return {@code true} if byte 5 bit 4 is set 318 */ 319 public boolean relayResistanceThresholdExceeded() { 320 return isBitOn(tvr[4], 4); 321 } 322 323 /** 324 * Returns {@code true} if the relay resistance time limits were exceeded. 325 * 326 * @return {@code true} if byte 5 bit 3 is set 327 */ 328 public boolean relayResistanceTimeLimitsExceeded() { 329 return isBitOn(tvr[4], 3); 330 } 331 332 /** 333 * Returns {@code true} if the relay resistance protocol is not supported. 334 * 335 * @return {@code true} if byte 5 bits 2 and 1 are both clear 336 */ 337 public boolean relayResistanceProtocolNotSupported() { 338 return !isBitOn(tvr[4], 2) && !isBitOn(tvr[4], 1); 339 } 340 341 /** 342 * Returns {@code true} if the relay resistance protocol was not performed. 343 * 344 * @return {@code true} if byte 5 bit 2 is clear and bit 1 is set 345 */ 346 public boolean relayResistanceProtocolNotPerformed() { 347 return !isBitOn(tvr[4], 2) && isBitOn(tvr[4], 1); 348 } 349 350 /** 351 * Returns {@code true} if the relay resistance protocol was performed. 352 * 353 * @return {@code true} if byte 5 bit 2 is set and bit 1 is clear 354 */ 355 public boolean relayResistanceProtocolPerformed() { 356 return isBitOn(tvr[4], 2) && !isBitOn(tvr[4], 1); 357 } 358 359 /** 360 * Tests whether a specific bit position is set in the given byte. 361 * 362 * @param value the byte to test 363 * @param position bit position (1 = least significant) 364 * @return {@code true} if the bit at the given position is 1 365 */ 366 private boolean isBitOn(byte value, int position) { 367 return ((value >> (position - 1)) & 1) == 1; 368 } 369 370 /** 371 * Dumps a human-readable binary representation of the TVR to the given stream. 372 * 373 * @param p the output stream 374 * @param indent indentation prefix string 375 */ 376 @Override 377 public void dump(PrintStream p, String indent) { 378 String inner = indent + " "; 379 p.printf("%s<terminal-verification-results value='%s'>%n", indent, ISOUtil.hexString(tvr)); 380 p.printf("%sByte 1: %s%n", inner, String.format("%8s", Integer.toBinaryString(tvr[0] & 0xFF)).replace(' ', '0')); 381 p.printf("%sByte 2: %s%n", inner, String.format("%8s", Integer.toBinaryString(tvr[1] & 0xFF)).replace(' ', '0')); 382 p.printf("%sByte 3: %s%n", inner, String.format("%8s", Integer.toBinaryString(tvr[2] & 0xFF)).replace(' ', '0')); 383 p.printf("%sByte 4: %s%n", inner, String.format("%8s", Integer.toBinaryString(tvr[3] & 0xFF)).replace(' ', '0')); 384 p.printf("%sByte 5: %s%n", inner, String.format("%8s", Integer.toBinaryString(tvr[4] & 0xFF)).replace(' ', '0')); 385 p.printf("%s</terminal-verification-results>%n", indent); 386 } 387}