/* * The MIT License * * Copyright 2014, 2015, 2016 Rui Martinho (rmartinho@gmail.com), António Braz (antoniocbraz@gmail.com) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package org.poreid; import org.poreid.pcscforjava.Card; import org.poreid.pcscforjava.CardException; import org.poreid.pcscforjava.ResponseAPDU; import org.poreid.pinpad.ReaderWithPinPad; import org.poreid.pinpad.ReaderWithPinPadData; import java.io.IOException; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import org.poreid.config.POReIDConfig; /** * * @author POReID */ public final class TerminalFeatures { private final Card card; private final String className; private final String readerName; private static final byte FEATURE_VERIFY_PIN_DIRECT = 0x06; private static final byte FEATURE_MODIFY_PIN_DIRECT = 0x07; private static final byte MICROSOFT_DEVICE_TYPE__SMARTCARD = 0x31; // http://research.microsoft.com/en-us/um/redmond/projects/invisible/src/drivers/net/simnic/devioctl.h.htm private static final int SCARD_CTL_BASE_CODE = 0x42000000; // reader.h pcsc - *nix code private static final int GET_FEATURE_REQUEST = 3400; // http://www.pcscworkgroup.com/specifications/files/pcsc10_v2.02.09.pdf private final int CM_IOCTL_GET_FEATURE_REQUEST = SCARD_CTL_CODE(GET_FEATURE_REQUEST); private Integer cachedFeatureVerify; private Integer cachedFeatureModify; private TerminalFeatures(Card card, String readerName) { this.card = card; this.readerName = readerName.replaceAll("((\\(.*\\))( \\d+))|( \\d+)*$", ""); this.className = POReIDConfig.getSmartCardReaderImplementingClassName(this.readerName); } /** * Devolve uma instância da classe * @param card Instância de um cartão (pertence ao smartcardio) * @param readerName Nome do leitor de cartões * @return Instância da classe TerminalFeatures */ public static TerminalFeatures getInstance(Card card, String readerName) { return new TerminalFeatures(card, readerName); } /** * Indica se o leitor disponibiliza a funcionalidade de verificação do pin através de pinpad (caso exista) * @return true se disponibiliza, false se não */ public boolean isVerifyPinThroughPinpadAvailable() { return null != (cachedFeatureVerify = getFeature(FEATURE_VERIFY_PIN_DIRECT)); } /** * Indica se o leitor apesar de dispor de pinpad a funcionalidade de verificação do pin através do pinpad é suportada * @param scClass Nome da classe que implementa o suporte ao cartão * @return true se suporta, false se não. */ public boolean isVerifyPinThroughPinpadSupported(String scClass) { return POReIDConfig.getVerifyPinSupport(readerName, scClass); } /** * Indica se o leitor disponibiliza a funcionalidade de modificação do pin através de pinpad (caso exista) * @return true se disponibiliza, false se não */ public boolean isModifyPinThroughPinpadAvailable() { return null != (cachedFeatureModify = getFeature(FEATURE_MODIFY_PIN_DIRECT)); } /** * Indica se o leitor apesar de dispor de pinpad a funcionalidade de modificação do pin através do pinpad é suportada * @param scClass Nome da classe que implementa o suporte ao cartão * @return true se suporta, false se não. */ public boolean isModifyPinThroughPinpadSupported(String scClass) { return POReIDConfig.getModifyPinSupport(readerName, scClass); } /** * Indica se é possivel utilizar um leitor com pinpad como se fosse um leitor sem pinpad * @return true se for possivel, false se não for. */ public boolean canBypassPinpad(){ return POReIDConfig.getOSInjectPinSupport(readerName); } private Integer getFeature(byte featureTag) { byte[] features; try { features = card.transmitControlCommand(CM_IOCTL_GET_FEATURE_REQUEST, new byte[0]); if (0 == features.length) { return null; } } catch (CardException e) { return null; } return findFeature(featureTag, features); } private Integer findFeature(byte featureTag, byte[] features) { int idx = 0; while (idx < features.length) { if (featureTag == features[idx]) { //http://www.pcscworkgroup.com/specifications/files/pcsc10_v2.02.09.pdf -- 2.6.14 return java.nio.ByteBuffer.wrap(features, idx+2, 4).order(java.nio.ByteOrder.BIG_ENDIAN).getInt(); } idx += 6; } return null; } private int SCARD_CTL_CODE(int code) { int ioctl; if (System.getProperty("os.name").toLowerCase().contains("win")) { ioctl = MICROSOFT_DEVICE_TYPE__SMARTCARD << 16 | (code) << 2; } else { ioctl = SCARD_CTL_BASE_CODE + code; } return ioctl; } /** * Transmite as instruções necessárias à verificação de PIN * @param timeOut Tempo limite * @param minPinSize Tamanho minimo do pin * @param maxPinSize Tamanho máximo do pin * @param apdu Instrução de verificação do pin a enviar para o cartão * @return status word * @throws POReIDException Exceção lançada quando ocorre uma exceção num componente (encapsula a exeção original) */ public byte[] transmitVerifyPinDirect(byte timeOut, byte minPinSize, byte maxPinSize, byte[] apdu) throws POReIDException { try { Constructor<? extends ReaderWithPinPad> ctor = Class.forName(className).asSubclass(ReaderWithPinPad.class).getConstructor(); ReaderWithPinPadData pData = ctor.newInstance().getVerifyPinDirect(timeOut, minPinSize, maxPinSize, apdu); try { if (pData.getIoCtlSmartcardLcdMessages() != 0) { card.transmitControlCommand(pData.getIoCtlSmartcardLcdMessages(), pData.getPinPadString()); } } catch (CardException ignore) { } return card.transmitControlCommand(cachedFeatureVerify, pData.getFeaturePinDirect()); } catch (CardException | InvocationTargetException | IllegalArgumentException | SecurityException | NoSuchMethodException | InstantiationException | IllegalAccessException | ClassNotFoundException | IOException ex) { throw new POReIDException("Não foi possivel efetuar a verificação do PIN através do leitor de cartões", ex); } } /** * Transmite as instruções necessárias à modificação de PIN * @param timeOut Tempo limite * @param minPinSize Tamanho minimo do pin * @param maxPinSize Tamanho máximo do pin * @param modifyApdu Instrução de modificação do pin a enviar para o cartão * @return status word * @throws POReIDException Exceção lançada quando ocorre uma exceção num componente (encapsula a exeção original) */ public byte[] transmitModifyPinDirect(byte timeOut, byte minPinSize, byte maxPinSize, byte[] modifyApdu) throws POReIDException { return transmitModifyPinDirect(timeOut, minPinSize, maxPinSize, null, modifyApdu); } /** * Transmite as instruções necessárias à modificação de PIN * @param timeOut tempo de expiração * @param minPinSize Tamanho minimo do pin * @param maxPinSize Tamanho máximo do pin * @param verifyApdu Instrução de verificação do pin a enviar para o cartão * @param modifyApdu Instrução de modificação do pin a enviar para o cartão * @return status word * @throws POReIDException Exceção lançada quando ocorre uma exceção num componente (encapsula a exeção original) */ public byte[] transmitModifyPinDirect(byte timeOut, byte minPinSize, byte maxPinSize, byte[] verifyApdu, byte[] modifyApdu) throws POReIDException { ResponseAPDU responseApdu; try { if (null != verifyApdu) { responseApdu = new ResponseAPDU(transmitVerifyPinDirect(timeOut, minPinSize, maxPinSize, verifyApdu)); if (0x9000 != responseApdu.getSW()){ return responseApdu.getBytes(); // trata do erro mais acima, poderemos ter de voltar novamente. } } Constructor<? extends ReaderWithPinPad> ctor = Class.forName(className).asSubclass(ReaderWithPinPad.class).getConstructor(); ReaderWithPinPadData pData = ctor.newInstance().getModifyPinDirect((verifyApdu!=null), timeOut, minPinSize, maxPinSize, modifyApdu); try { if (pData.getIoCtlSmartcardLcdMessages() != 0) { card.transmitControlCommand(pData.getIoCtlSmartcardLcdMessages(), pData.getPinPadString()); } } catch (CardException ignore) { } return card.transmitControlCommand(cachedFeatureModify, pData.getFeaturePinDirect()); } catch (CardException | InvocationTargetException | IllegalArgumentException | SecurityException | NoSuchMethodException | InstantiationException | IllegalAccessException | ClassNotFoundException | IOException ex) { throw new POReIDException("Não foi possivel efetuar a alteração do PIN através do leitor de cartões", ex); } } }