public class SystemPropertiesServiceLookup extends XfsServiceLookup { private static final Logger LOG = LoggerFactory.getLogger(SystemPropertiesServiceLookup.class); private List<ServiceEntry> lookup() { final String method = "lookup()"; final List<ServiceEntry> services = new ArrayList<ServiceEntry>(); for (Map.Entry<Object, Object> entry : System.getProperties().entrySet()) { if (LOG.isDebugEnabled()) { LOG.debug(method, entry.getKey() + "=" + entry.getValue()); } XfsServiceClass serviceClass = XfsServiceClass.getForServiceClassName(String.valueOf(entry.getKey())); if (serviceClass != null) { String logicalName = String.valueOf(entry.getValue()); if (LOG.isInfoEnabled()) { LOG.info(method, serviceClass.name() + ": " + logicalName); } services.add(new ServiceEntry(logicalName, getServicClass(serviceClass))); } } return services; } @Override public Iterator<ServiceEntry> iterator() { return lookup().iterator(); } }
final class VelocityChecking { private static final Logger LOG = LoggerFactory.getLogger(VelocityChecking.class); private final EMVTransaction transaction; VelocityChecking(EMVTransaction transaction) { this.transaction = transaction; } void doVelocityChecking() { final String method = "doVelocityChecking()"; try { int lowerLimit = BinaryNumber.toInt(transaction.getData(EMVTag.LOWER_CONSECUTIVE_OFFLINE_LIMIT)); int upperLimit = BinaryNumber.toInt(transaction.getData(EMVTag.UPPER_CONSECUTIVE_OFFLINE_LIMIT)); int lastOnlineATC = readLastOnlineATC(); if (lastOnlineATC == 0) { indicateNewCard(); } int atc = readATC(); if (LOG.isInfoEnabled()) { LOG.info(method, "ATC: " + atc + ", Last Online ATC: " + lastOnlineATC); } int consecutiveOffline = atc - lastOnlineATC; TVR tvr = transaction.getTVR(); if (consecutiveOffline > lowerLimit) { tvr.getByte4().lowerConsecutiveOfflineLimitExceeded(); } if (consecutiveOffline > upperLimit) { tvr.getByte4().upperConsecutiveOfflineLimitExceeded(); } } catch (IOException e) { if (LOG.isErrorEnabled()) { LOG.error(method, "Error reading data from ICC", e); } } catch (ProcessingStateException e) { if (LOG.isErrorEnabled()) { LOG.error(method, "Error reading data from ICC", e); } TVR tvr = transaction.getTVR(); tvr.getByte4().lowerConsecutiveOfflineLimitExceeded(); tvr.getByte4().upperConsecutiveOfflineLimitExceeded(); } catch (DataNotFoundException e) { if (LOG.isDebugEnabled()) { LOG.debug(method, "Data not found: " + e.getEMVTag()); } } } private int readATC() throws ProcessingStateException, IOException { final String method = "readATC()"; try { return BinaryNumber.toInt(read(EMVTag.APPLICATION_TRANSACTION_COUNTER)); } catch (ProcessingStateException e) { if (LOG.isInfoEnabled()) { LOG.info(method, "ATC is missing"); } TVR tvr = transaction.getTVR(); tvr.getByte1().iccDataMissing(); throw e; } } private int readLastOnlineATC() throws ProcessingStateException, IOException { final String method = "readLastOnlineATC()"; try { return BinaryNumber.toInt(read(EMVTag.LAST_ONLINE_ATC_REGISTER)); } catch (ProcessingStateException e) { if (LOG.isInfoEnabled()) { LOG.info(method, "Last Online ATC Register is missing"); } throw e; } } private byte[] read(EMVTag emvTag) throws IOException, ProcessingStateException { GetDataCommand getDataCommand = GetDataCommand.create(emvTag.getTag()); RAPDU response = transaction.getICReader().transmit(getDataCommand); new ProcessingState(response.getSW()).assertSuccessful(); return TLV.parse(response.getData()).getValue(); } private void indicateNewCard() { final String method = "indicateNewCard()"; if (LOG.isInfoEnabled()) { LOG.info(method, "New card"); } TVR tvr = transaction.getTVR(); tvr.getByte2().newCard(); } }
final class RandomTransactionSelection { private static final Logger LOG = LoggerFactory.getLogger(RandomTransactionSelection.class); private final EMVTransaction transaction; /** Amount, Authorised */ private BigInteger amount = null; /** Terminal Floor Limit */ private BigInteger floorLimit = null; /** Target Percentage to be Used for Random Selection */ private int targetPercentage = -1; /** Maximum Target Percentage to be used for Biased Random Selection */ private int maxTargetPercentage = -1; /** Threshold Value for Biased Random Selection */ private BigInteger thresholdValue = null; private int randomNumber = -1; RandomTransactionSelection(EMVTransaction transaction) { this.transaction = transaction; } void perform() { final String method = "perform()"; try { prerequisites(); if (amount.compareTo(thresholdValue) < 0) { randomSelection(); } else { biasedRandomSelection(); } } catch (DataNotFoundException e) { if (LOG.isErrorEnabled()) { LOG.error(method, "Data not found: " + e.getEMVTag(), e); } } } private void prerequisites() throws DataNotFoundException { final String method = "prerequisites()"; amount = BinaryNumber.toBigInteger(transaction.getData(EMVTag.AMOUNT_AUTHORISED_BINARY)); floorLimit = BinaryNumber.toBigInteger(transaction.getData(EMVTag.TERMINAL_FLOOR_LIMIT)); RandomTransactionSelectionData selectionData = transaction.getApplication().getRandomTransactionSelectionData(); targetPercentage = selectionData.getTargetPercentage(); maxTargetPercentage = selectionData.getMaxTargetPercentage(); thresholdValue = BinaryNumber.toBigInteger(selectionData.getThresholdValue()); if (thresholdValue.compareTo(floorLimit) >= 0) { if (LOG.isWarnEnabled()) { LOG.warn( method, "Threshold Value (" + thresholdValue + ") is greater or equals to floor limit (" + floorLimit + ")"); } thresholdValue = BigInteger.ZERO; } randomNumber = generateRandomNumber(); if (LOG.isDebugEnabled()) { LOG.debug( method, "Amount: " + amount + ", Floor Limit: " + floorLimit + ", Target Percentage: " + targetPercentage + ", Max Target Percentage: " + maxTargetPercentage + ", Threshold Value: " + thresholdValue + ", Random Number: " + randomNumber); } } private void biasedRandomSelection() { double factor = interpolationFactor(); int transactionTargetPercent = (int) (((maxTargetPercentage - targetPercentage) * factor) + targetPercentage); if (randomNumber <= transactionTargetPercent) { selectForOnlineProcessing(); } } private double interpolationFactor() { double dividend = amount.subtract(thresholdValue).doubleValue(); double divisor = floorLimit.subtract(thresholdValue).doubleValue(); return dividend / divisor; } private void randomSelection() { final String method = "randomSelection()"; if (LOG.isDebugEnabled()) { LOG.debug( method, "Target Percentage: " + targetPercentage + ", Random number: " + randomNumber); } if (randomNumber <= targetPercentage) { selectForOnlineProcessing(); } } private void selectForOnlineProcessing() { TVR tvr = transaction.getTVR(); tvr.getByte4().transactionSelectedRandomlyForOnlineProcessing(); } private int generateRandomNumber() { return new Random().nextInt(99) + 1; } }
/** @author Andreas Fagschlunger */ public class O2XfsConf { static { System.loadLibrary("at.o2xfs.win32"); System.loadLibrary("at.o2xfs.xfs.conf"); } private static final Logger LOG = LoggerFactory.getLogger(O2XfsConf.class); private class ValuePair implements Map.Entry<String, String> { private String name = null; private String data = null; private ValuePair(final String name, final String data) { this.name = name; this.data = data; } @Override public String getKey() { return name; } @Override public String getValue() { return data; } @Override public String setValue(String value) { throw new UnsupportedOperationException(); } } private static final int SIZE_LIMIT = 256; public static final HKEY WFS_CFG_HKEY_XFS_ROOT = new HKEY(1L); /** @since 3.0 */ public static final HKEY WFS_CFG_HKEY_MACHINE_XFS_ROOT = new HKEY(2L); /** @since 3.0 */ public static final HKEY WFS_CFG_HKEY_USER_DEFAULT_XFS_ROOT = new HKEY(3L); private List<HKEY> openKeys = null; private static O2XfsConf instance = null; private O2XfsConf() { openKeys = new ArrayList<HKEY>(); } public static O2XfsConf getInstance() { if (instance == null) { synchronized (O2XfsConf.class) { if (instance == null) { instance = new O2XfsConf(); } } } return instance; } /** * Closes the specified key. * * @param hKey Handle to the currently open key that is to be closed. * @throws XfsException */ public void wfmCloseKey(final HKEY hKey) throws XfsException { final int errorCode = wfmCloseKey0(hKey); XfsException.throwFor(errorCode); synchronized (openKeys) { openKeys.remove(hKey); } } private native int wfmCloseKey0(Type hKey); /** Opens the specified key. */ public HKEY wfmOpenKey(final HKEY hKey, final String subKey) throws XfsException { HKEY hkResult = new HKEY(); final int errorCode = wfmOpenKey0(hKey, (subKey == null ? null : new ZSTR(subKey)), hkResult); XfsException.throwFor(errorCode); synchronized (openKeys) { openKeys.add(hkResult); } return hkResult; } private native int wfmOpenKey0(Type hKey, Type lpszSubKey, Type phkResult); /** Retrieves the data for the value with the specified name, within the specified open key. */ public String wfmQueryValue(final HKEY hKey, final String valueName) throws XfsException { final ZSTR data = new ZSTR(SIZE_LIMIT, true); final DWORD cchData = new DWORD(0L); final int errorCode = wfmQueryValue0(hKey, new ZSTR(valueName), data, cchData); XfsException.throwFor(errorCode); return data.toString(); } private native int wfmQueryValue0(Type hKey, Type lpszValueName, Type lpszData, Type lpcchData); /** * Enumerates the subkeys of the specified open key. Retrieves information about one subkey each * time it is called. * * @param hKey Handle to a currently open key, or the predefined handle value: {@link * #WFS_CFG_HKEY_XFS_ROOT} * @return the name of the subkey */ public String wfmEnumKey(final HKEY key, final DWORD iSubKey) throws XfsException { final ZSTR name = new ZSTR(SIZE_LIMIT, true); DWORD cchName = new DWORD(name.length); FILETIME lastWrite = new FILETIME(); final int errorCode = wfmEnumKey0(key, iSubKey, name, cchName, lastWrite); XfsException.throwFor(errorCode); return name.toString(); } private native int wfmEnumKey0( Type hKey, Type iSubKey, Type lpszName, Type lpcchName, Type lpftLastWrite); /** * Enumerates the values of the specified open key. Retrieves the name and data for one value each * time it is called. */ public Map.Entry<String, String> wfmEnumValue(final HKEY hKey, final DWORD iValue) throws XfsException { ZSTR value = new ZSTR(SIZE_LIMIT, true); ZSTR data = new ZSTR(SIZE_LIMIT, true); final int errorCode = wfmEnumValue0(hKey, iValue, value, data); XfsException.throwFor(errorCode); return new ValuePair(value.toString(), data.toString()); } private native int wfmEnumValue0(Type hKey, Type iValue, Type lpszValue, Type lpszData); @Override protected void finalize() throws Throwable { super.finalize(); final String method = "finalize()"; synchronized (openKeys) { if (openKeys.size() > 0) { if (LOG.isWarnEnabled()) { LOG.warn(method, "Closing " + openKeys.size() + " open key(s)"); } } while (openKeys.size() > 0) { HKEY hKey = openKeys.get(0); try { wfmCloseKey(hKey); } catch (XfsException e) { if (LOG.isErrorEnabled()) { LOG.error(method, "Error closing HKEY: " + hKey, e); } } } } } }