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 private final byte[] tvr; 033 034 public TerminalVerificationResults(byte[] tvr) { 035 Objects.requireNonNull(tvr); 036 if (tvr.length != 5) 037 throw new IllegalArgumentException("TVR length must be 5."); 038 this.tvr = tvr; 039 } 040 041 public TerminalVerificationResults(String hexTVR) { 042 this(ISOUtil.hex2byte(hexTVR)); 043 } 044 045 public boolean offlineDataProcNotPerformed() { 046 return isBitOn(tvr[0], 8); 047 } 048 049 public boolean sdaFailed() { 050 return isBitOn(tvr[0], 7); 051 } 052 053 public boolean iccDataMissing() { 054 return isBitOn(tvr[0], 6); 055 } 056 057 public boolean panInHotlist() { 058 return isBitOn(tvr[0], 5); 059 } 060 061 public boolean ddaFailed() { 062 return isBitOn(tvr[0], 4); 063 } 064 065 public boolean cdaFailed() { 066 return isBitOn(tvr[0], 3); 067 } 068 069 public boolean sdaSelected() { 070 return isBitOn(tvr[0], 2); 071 } 072 073 public boolean rfu() { 074 return !isBitOn(tvr[0], 1) || !isBitOn(tvr[1], 1) || 075 !isBitOn(tvr[1], 2) || !isBitOn(tvr[1], 3) || 076 !isBitOn(tvr[2], 2) || !isBitOn(tvr[2], 1) || 077 !isBitOn(tvr[3], 3) || !isBitOn(tvr[3], 2) || 078 !isBitOn(tvr[3], 1) || 079 (isBitOn(tvr[4], 2) && isBitOn(tvr[4], 1)); 080 } 081 082 public boolean cardAndTerminalDiffApps() { 083 return isBitOn(tvr[1], 8); 084 } 085 086 public boolean expiredApplication() { 087 return isBitOn(tvr[1], 7); 088 } 089 090 public boolean applicationNotEffective() { 091 return isBitOn(tvr[1], 6); 092 } 093 094 public boolean serviceNotAllowedForCardProduct() { 095 return isBitOn(tvr[1], 5); 096 } 097 098 public boolean newCard() { 099 return isBitOn(tvr[1], 4); 100 } 101 102 public boolean cardholderVerificationNotSuccessful() { 103 return isBitOn(tvr[2], 8); 104 } 105 106 public boolean unrecognisedCVM() { 107 return isBitOn(tvr[2], 7); 108 } 109 110 public boolean pinTryLimitExceeded() { 111 return isBitOn(tvr[2], 6); 112 } 113 114 public boolean pinRequiredButNoPinPadPresent() { 115 return isBitOn(tvr[2], 5); 116 } 117 118 public boolean pinRequiredButNotEntered() { 119 return isBitOn(tvr[2], 4); 120 } 121 122 public boolean onlinePINEntered() { 123 return isBitOn(tvr[2], 3); 124 } 125 126 public boolean transactionExceedsFloorLimit() { 127 return isBitOn(tvr[3], 8); 128 } 129 130 public boolean lowerConsecutiveOfflineLimitExceeded() { 131 return isBitOn(tvr[3], 7); 132 } 133 134 public boolean upperConsecutiveOfflineLimitExceeded() { 135 return isBitOn(tvr[3], 6); 136 } 137 138 public boolean transactionSelectedRandomlyOnlineProcessing() { 139 return isBitOn(tvr[3], 5); 140 } 141 142 public boolean merchantForcedTransactionOnline() { 143 return isBitOn(tvr[3], 4); 144 } 145 146 public boolean defaultTDOLUsed() { 147 return isBitOn(tvr[4], 8); 148 } 149 150 public boolean issuerAuthenticationFailed() { 151 return isBitOn(tvr[4], 7); 152 } 153 154 public boolean scriptFailedBeforeFinalGenerateAC() { 155 return isBitOn(tvr[4], 6); 156 } 157 158 public boolean scriptFailedAfterFinalGenerateAC() { 159 return isBitOn(tvr[4], 5); 160 } 161 162 public boolean relayResistanceThresholdExceeded() { 163 return isBitOn(tvr[4], 4); 164 } 165 166 public boolean relayResistanceTimeLimitsExceeded() { 167 return isBitOn(tvr[4], 3); 168 } 169 170 public boolean relayResistanceProtocolNotSupported() { 171 return !isBitOn(tvr[4], 2) && !isBitOn(tvr[4], 1); 172 } 173 174 public boolean relayResistanceProtocolNotPerformed() { 175 return !isBitOn(tvr[4], 2) && isBitOn(tvr[4], 1); 176 } 177 178 public boolean relayResistanceProtocolPerformed() { 179 return isBitOn(tvr[4], 2) && !isBitOn(tvr[4], 1); 180 } 181 182 private boolean isBitOn(byte value, int position) { 183 return ((value >> (position - 1)) & 1) == 1; 184 } 185 186 @Override 187 public void dump(PrintStream p, String indent) { 188 String inner = indent + " "; 189 p.printf("%s<terminal-verification-results value='%s'>%n", indent, ISOUtil.hexString(tvr)); 190 p.printf("%sByte 1: %s%n", inner, String.format("%8s", Integer.toBinaryString(tvr[0] & 0xFF)).replace(' ', '0')); 191 p.printf("%sByte 2: %s%n", inner, String.format("%8s", Integer.toBinaryString(tvr[1] & 0xFF)).replace(' ', '0')); 192 p.printf("%sByte 3: %s%n", inner, String.format("%8s", Integer.toBinaryString(tvr[2] & 0xFF)).replace(' ', '0')); 193 p.printf("%sByte 4: %s%n", inner, String.format("%8s", Integer.toBinaryString(tvr[3] & 0xFF)).replace(' ', '0')); 194 p.printf("%sByte 5: %s%n", inner, String.format("%8s", Integer.toBinaryString(tvr[4] & 0xFF)).replace(' ', '0')); 195 p.printf("%s</terminal-verification-results>%n", indent); 196 } 197}