diff options
Diffstat (limited to 'smcc/src/main/java')
10 files changed, 1633 insertions, 1 deletions
| diff --git a/smcc/src/main/java/at/gv/egiz/smcc/util/SmartCardIO.java b/smcc/src/main/java/at/gv/egiz/smcc/util/SmartCardIO.java index 08a1ea39..b646fb0a 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/util/SmartCardIO.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/util/SmartCardIO.java @@ -37,9 +37,11 @@ import javax.smartcardio.CardTerminals;  import javax.smartcardio.TerminalFactory;  import javax.smartcardio.CardTerminals.State; +import org.openecard.scio.osx.SunOSXPCSC;  import org.slf4j.Logger;  import org.slf4j.LoggerFactory; +  /**   *   * @author mcentner @@ -65,7 +67,12 @@ public class SmartCardIO {    private void updateTerminalFactory() {      TerminalFactory terminalFactory;      try { -      terminalFactory = TerminalFactory.getInstance("PC/SC", null); +      String osName = System.getProperty("os.name"); +      if (osName.contains("OS X")) { +        terminalFactory = TerminalFactory.getInstance("PC/SC", null, new SunOSXPCSC()); +      } else { +        terminalFactory = TerminalFactory.getInstance("PC/SC", null); +      }      } catch (NoSuchAlgorithmException e) {        log.info("Failed to get TerminalFactory of type 'PC/SC'.", e);        terminalFactory = TerminalFactory.getDefault(); diff --git a/smcc/src/main/java/org/openecard/scio/osx/CardImpl.java b/smcc/src/main/java/org/openecard/scio/osx/CardImpl.java new file mode 100644 index 00000000..de798817 --- /dev/null +++ b/smcc/src/main/java/org/openecard/scio/osx/CardImpl.java @@ -0,0 +1,289 @@ +/* + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation.  Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package org.openecard.scio.osx; + +import static org.openecard.scio.osx.PCSC.SCARD_LEAVE_CARD; +import static org.openecard.scio.osx.PCSC.SCARD_RESET_CARD; +import static org.openecard.scio.osx.PCSC.SCARD_SHARE_DIRECT; +import static org.openecard.scio.osx.PCSC.SCARD_SHARE_SHARED; +import static org.openecard.scio.osx.PCSC.SCARD_W_REMOVED_CARD; +import static org.openecard.scio.osx.PCSC.SCardBeginTransaction; +import static org.openecard.scio.osx.PCSC.SCardConnect; +import static org.openecard.scio.osx.PCSC.SCardControl; +import static org.openecard.scio.osx.PCSC.SCardDisconnect; +import static org.openecard.scio.osx.PCSC.SCardEndTransaction; +import static org.openecard.scio.osx.PCSC.SCardStatus; +import static org.openecard.scio.osx.PCSC.SCardTransmit; +import static org.openecard.scio.osx.PlatformPCSC.SCARD_PROTOCOL_T0; +import static org.openecard.scio.osx.PlatformPCSC.SCARD_PROTOCOL_T1; + +import javax.smartcardio.ATR; +import javax.smartcardio.Card; +import javax.smartcardio.CardChannel; +import javax.smartcardio.CardException; +import javax.smartcardio.CardPermission; + +/** + * Card implementation. + *  + * @since 1.6 + * @author Andreas Sterbenz + */ +final class CardImpl extends Card { + +  private static enum State { +    OK, REMOVED, DISCONNECTED +  }; + +  // the terminal that created this card +  private final TerminalImpl terminal; + +  // the native SCARDHANDLE +  final long cardId; + +  // atr of this card +  private final ATR atr; + +  // protocol in use, one of SCARD_PROTOCOL_T0 and SCARD_PROTOCOL_T1 +  final int protocol; + +  // the basic logical channel (channel 0) +  private final ChannelImpl basicChannel; + +  // state of this card connection +  private volatile State state; + +  // thread holding exclusive access to the card, or null +  private volatile Thread exclusiveThread; + +  CardImpl(TerminalImpl terminal, String protocol) throws PCSCException { +    this.terminal = terminal; +    int sharingMode = SCARD_SHARE_SHARED; +    int connectProtocol; +    if (protocol.equals("*")) { +      connectProtocol = SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1; +    } else if (protocol.equalsIgnoreCase("T=0")) { +      connectProtocol = SCARD_PROTOCOL_T0; +    } else if (protocol.equalsIgnoreCase("T=1")) { +      connectProtocol = SCARD_PROTOCOL_T1; +    } else if (protocol.equalsIgnoreCase("direct")) { +      // testing +      connectProtocol = 0; +      sharingMode = SCARD_SHARE_DIRECT; +    } else { +      throw new IllegalArgumentException("Unsupported protocol " + protocol); +    } +    cardId = SCardConnect(terminal.contextId, terminal.name, sharingMode, +        connectProtocol); +    byte[] status = new byte[2]; +    byte[] atrBytes = SCardStatus(cardId, status); +    atr = new ATR(atrBytes); +    this.protocol = status[1] & 0xff; +    basicChannel = new ChannelImpl(this, 0); +    state = State.OK; +  } + +  void checkState() { +    State s = state; +    if (s == State.DISCONNECTED) { +      throw new IllegalStateException("Card has been disconnected"); +    } else if (s == State.REMOVED) { +      throw new IllegalStateException("Card has been removed"); +    } +  } + +  boolean isValid() { +    if (state != State.OK) { +      return false; +    } +    // ping card via SCardStatus +    try { +      SCardStatus(cardId, new byte[2]); +      return true; +    } catch (PCSCException e) { +      state = State.REMOVED; +      return false; +    } +  } + +  private void checkSecurity(String action) { +    SecurityManager sm = System.getSecurityManager(); +    if (sm != null) { +      sm.checkPermission(new CardPermission(terminal.name, action)); +    } +  } + +  void handleError(PCSCException e) { +    if (e.code == SCARD_W_REMOVED_CARD) { +      state = State.REMOVED; +    } +  } + +  public ATR getATR() { +    return atr; +  } + +  public String getProtocol() { +    switch (protocol) { +    case SCARD_PROTOCOL_T0: +      return "T=0"; +    case SCARD_PROTOCOL_T1: +      return "T=1"; +    default: +      // should never occur +      return "Unknown protocol " + protocol; +    } +  } + +  public CardChannel getBasicChannel() { +    checkSecurity("getBasicChannel"); +    checkState(); +    return basicChannel; +  } + +  private static int getSW(byte[] b) { +    if (b.length < 2) { +      return -1; +    } +    int sw1 = b[b.length - 2] & 0xff; +    int sw2 = b[b.length - 1] & 0xff; +    return (sw1 << 8) | sw2; +  } + +  private static byte[] commandOpenChannel = new byte[] { 0, 0x70, 0, 0, 1 }; + +  public CardChannel openLogicalChannel() throws CardException { +    checkSecurity("openLogicalChannel"); +    checkState(); +    checkExclusive(); +    try { +      byte[] response = SCardTransmit(cardId, protocol, commandOpenChannel, 0, +          commandOpenChannel.length); +      if ((response.length != 3) || (getSW(response) != 0x9000)) { +        throw new CardException("openLogicalChannel() failed, card response: " +            + PCSC.toString(response)); +      } +      return new ChannelImpl(this, response[0]); +    } catch (PCSCException e) { +      handleError(e); +      throw new CardException("openLogicalChannel() failed", e); +    } +  } + +  void checkExclusive() throws CardException { +    Thread t = exclusiveThread; +    if (t == null) { +      return; +    } +    if (t != Thread.currentThread()) { +      throw new CardException("Exclusive access established by another Thread"); +    } +  } + +  public synchronized void beginExclusive() throws CardException { +    checkSecurity("exclusive"); +    checkState(); +    if (exclusiveThread != null) { +      throw new CardException( +          "Exclusive access has already been assigned to Thread " +              + exclusiveThread.getName()); +    } +    try { +      SCardBeginTransaction(cardId); +    } catch (PCSCException e) { +      handleError(e); +      throw new CardException("beginExclusive() failed", e); +    } +    exclusiveThread = Thread.currentThread(); +  } + +  public synchronized void endExclusive() throws CardException { +    checkState(); +    if (exclusiveThread != Thread.currentThread()) { +      throw new IllegalStateException( +          "Exclusive access not assigned to current Thread"); +    } +    try { +      SCardEndTransaction(cardId, SCARD_LEAVE_CARD); +    } catch (PCSCException e) { +      handleError(e); +      throw new CardException("endExclusive() failed", e); +    } finally { +      exclusiveThread = null; +    } +  } + +  public byte[] transmitControlCommand(int controlCode, byte[] command) +      throws CardException { +    checkSecurity("transmitControl"); +    checkState(); +    checkExclusive(); +    if (command == null) { +      throw new NullPointerException(); +    } +    try { +      byte[] r = SCardControl(cardId, controlCode, command); +      return r; +    } catch (PCSCException e) { +      handleError(e); +      throw new CardException("transmitControlCommand() failed", e); +    } +  } + +  public void disconnect(boolean reset) throws CardException { +    if (reset) { +      checkSecurity("reset"); +    } +    if (state != State.OK) { +      return; +    } +    checkExclusive(); +    try { +      SCardDisconnect(cardId, (reset ? SCARD_LEAVE_CARD : SCARD_RESET_CARD)); +    } catch (PCSCException e) { +      throw new CardException("disconnect() failed", e); +    } finally { +      state = State.DISCONNECTED; +      exclusiveThread = null; +    } +  } + +  public String toString() { +    return "PC/SC card in " + terminal.getName() + ", protocol " +        + getProtocol() + ", state " + state; +  } + +  protected void finalize() throws Throwable { +    try { +      if (state == State.OK) { +        SCardDisconnect(cardId, SCARD_LEAVE_CARD); +      } +    } finally { +      super.finalize(); +    } +  } + +} diff --git a/smcc/src/main/java/org/openecard/scio/osx/ChannelImpl.java b/smcc/src/main/java/org/openecard/scio/osx/ChannelImpl.java new file mode 100644 index 00000000..1094fb9d --- /dev/null +++ b/smcc/src/main/java/org/openecard/scio/osx/ChannelImpl.java @@ -0,0 +1,298 @@ +/* + * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation.  Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package org.openecard.scio.osx; + +import static org.openecard.scio.osx.PCSC.SCardTransmit; +import static org.openecard.scio.osx.PlatformPCSC.SCARD_PROTOCOL_T0; +import static org.openecard.scio.osx.PlatformPCSC.SCARD_PROTOCOL_T1; + +import java.nio.ByteBuffer; +import java.nio.ReadOnlyBufferException; +import java.security.AccessController; +import javax.smartcardio.Card; +import javax.smartcardio.CardChannel; +import javax.smartcardio.CardException; +import javax.smartcardio.CommandAPDU; +import javax.smartcardio.ResponseAPDU; + +import sun.security.action.GetPropertyAction; + +/** + * CardChannel implementation. + *  + * @since 1.6 + * @author Andreas Sterbenz + */ +final class ChannelImpl extends CardChannel { + +  // the card this channel is associated with +  private final CardImpl card; + +  // the channel number, 0 for the basic logical channel +  private final int channel; + +  // whether this channel has been closed. only logical channels can be closed +  private volatile boolean isClosed; + +  ChannelImpl(CardImpl card, int channel) { +    this.card = card; +    this.channel = channel; +  } + +  void checkClosed() { +    card.checkState(); +    if (isClosed) { +      throw new IllegalStateException("Logical channel has been closed"); +    } +  } + +  public Card getCard() { +    return card; +  } + +  public int getChannelNumber() { +    checkClosed(); +    return channel; +  } + +  private static void checkManageChannel(byte[] b) { +    if (b.length < 4) { +      throw new IllegalArgumentException( +          "Command APDU must be at least 4 bytes long"); +    } +    if ((b[0] >= 0) && (b[1] == 0x70)) { +      throw new IllegalArgumentException( +          "Manage channel command not allowed, use openLogicalChannel()"); +    } +  } + +  public ResponseAPDU transmit(CommandAPDU command) throws CardException { +    checkClosed(); +    card.checkExclusive(); +    byte[] commandBytes = command.getBytes(); +    byte[] responseBytes = doTransmit(commandBytes); +    return new ResponseAPDU(responseBytes); +  } + +  public int transmit(ByteBuffer command, ByteBuffer response) +      throws CardException { +    checkClosed(); +    card.checkExclusive(); +    if ((command == null) || (response == null)) { +      throw new NullPointerException(); +    } +    if (response.isReadOnly()) { +      throw new ReadOnlyBufferException(); +    } +    if (command == response) { +      throw new IllegalArgumentException( +          "command and response must not be the same object"); +    } +    if (response.remaining() < 258) { +      throw new IllegalArgumentException( +          "Insufficient space in response buffer"); +    } +    byte[] commandBytes = new byte[command.remaining()]; +    command.get(commandBytes); +    byte[] responseBytes = doTransmit(commandBytes); +    response.put(responseBytes); +    return responseBytes.length; +  } + +  private final static boolean t0GetResponse = getBooleanProperty( +      "sun.security.smartcardio.t0GetResponse", true); + +  private final static boolean t1GetResponse = getBooleanProperty( +      "sun.security.smartcardio.t1GetResponse", true); + +  private final static boolean t1StripLe = getBooleanProperty( +      "sun.security.smartcardio.t1StripLe", false); + +  private static boolean getBooleanProperty(String name, boolean def) { +    String val = AccessController.doPrivileged(new GetPropertyAction(name)); +    if (val == null) { +      return def; +    } +    if (val.equalsIgnoreCase("true")) { +      return true; +    } else if (val.equalsIgnoreCase("false")) { +      return false; +    } else { +      throw new IllegalArgumentException(name +          + " must be either 'true' or 'false'"); +    } +  } + +  private byte[] concat(byte[] b1, byte[] b2, int n2) { +    int n1 = b1.length; +    if ((n1 == 0) && (n2 == b2.length)) { +      return b2; +    } +    byte[] res = new byte[n1 + n2]; +    System.arraycopy(b1, 0, res, 0, n1); +    System.arraycopy(b2, 0, res, n1, n2); +    return res; +  } + +  private final static byte[] B0 = new byte[0]; + +  private byte[] doTransmit(byte[] command) throws CardException { +    // note that we modify the 'command' array in some cases, so it must +    // be a copy of the application provided data. +    try { +      checkManageChannel(command); +      setChannel(command); +      int n = command.length; +      boolean t0 = card.protocol == SCARD_PROTOCOL_T0; +      boolean t1 = card.protocol == SCARD_PROTOCOL_T1; +      if (t0 && (n >= 7) && (command[4] == 0)) { +        throw new CardException("Extended length forms not supported for T=0"); +      } +      if ((t0 || (t1 && t1StripLe)) && (n >= 7)) { +        int lc = command[4] & 0xff; +        if (lc != 0) { +          if (n == lc + 6) { +            n--; +          } +        } else { +          lc = ((command[5] & 0xff) << 8) | (command[6] & 0xff); +          if (n == lc + 9) { +            n -= 2; +          } +        } +      } +      boolean getresponse = (t0 && t0GetResponse) || (t1 && t1GetResponse); +      int k = 0; +      byte[] result = B0; +      while (true) { +        if (++k >= 32) { +          throw new CardException("Could not obtain response"); +        } +        byte[] response = SCardTransmit(card.cardId, card.protocol, command, 0, +            n); +        int rn = response.length; +        if (getresponse && (rn >= 2)) { +          // see ISO 7816/2005, 5.1.3 +          if ((rn == 2) && (response[0] == 0x6c)) { +            // Resend command using SW2 as short Le field +            command[n - 1] = response[1]; +            continue; +          } +          if (response[rn - 2] == 0x61) { +            // Issue a GET RESPONSE command with the same CLA +            // using SW2 as short Le field +            if (rn > 2) { +              result = concat(result, response, rn - 2); +            } +            command[1] = (byte) 0xC0; +            command[2] = 0; +            command[3] = 0; +            command[4] = response[rn - 1]; +            n = 5; +            continue; +          } + +        } +        result = concat(result, response, rn); +        break; +      } +      return result; +    } catch (PCSCException e) { +      card.handleError(e); +      throw new CardException(e); +    } +  } + +  private static int getSW(byte[] res) throws CardException { +    if (res.length < 2) { +      throw new CardException("Invalid response length: " + res.length); +    } +    int sw1 = res[res.length - 2] & 0xff; +    int sw2 = res[res.length - 1] & 0xff; +    return (sw1 << 8) | sw2; +  } + +  private static boolean isOK(byte[] res) throws CardException { +    return (res.length == 2) && (getSW(res) == 0x9000); +  } + +  private void setChannel(byte[] com) { +    int cla = com[0]; +    if (cla < 0) { +      // proprietary class format, cannot set or check logical channel +      // for now, just return +      return; +    } +    // classes 001x xxxx is reserved for future use in ISO, ignore +    if ((cla & 0xe0) == 0x20) { +      return; +    } +    // see ISO 7816/2005, table 2 and 3 +    if (channel <= 3) { +      // mask of bits 7, 1, 0 (channel number) +      // 0xbc == 1011 1100 +      com[0] &= 0xbc; +      com[0] |= channel; +    } else if (channel <= 19) { +      // mask of bits 7, 3, 2, 1, 0 (channel number) +      // 0xbc == 1011 0000 +      com[0] &= 0xb0; +      com[0] |= 0x40; +      com[0] |= (channel - 4); +    } else { +      throw new RuntimeException("Unsupported channel number: " + channel); +    } +  } + +  public void close() throws CardException { +    if (getChannelNumber() == 0) { +      throw new IllegalStateException("Cannot close basic logical channel"); +    } +    if (isClosed) { +      return; +    } +    card.checkExclusive(); +    try { +      byte[] com = new byte[] { 0x00, 0x70, (byte) 0x80, 0 }; +      com[3] = (byte) getChannelNumber(); +      setChannel(com); +      byte[] res = SCardTransmit(card.cardId, card.protocol, com, 0, com.length); +      if (isOK(res) == false) { +        throw new CardException("close() failed: " + PCSC.toString(res)); +      } +    } catch (PCSCException e) { +      card.handleError(e); +      throw new CardException("Could not close channel", e); +    } finally { +      isClosed = true; +    } +  } + +  public String toString() { +    return "PC/SC channel " + channel; +  } + +} diff --git a/smcc/src/main/java/org/openecard/scio/osx/PCSC.java b/smcc/src/main/java/org/openecard/scio/osx/PCSC.java new file mode 100644 index 00000000..1cdba441 --- /dev/null +++ b/smcc/src/main/java/org/openecard/scio/osx/PCSC.java @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation.  Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package org.openecard.scio.osx; + +/** + * Access to native PC/SC functions and definition of PC/SC constants. + * Initialization and platform specific PC/SC constants are handled in the + * platform specific superclass. + *  + * @since 1.6 + * @author Andreas Sterbenz + */ +final class PCSC extends PlatformPCSC { + +  private PCSC() { +    // no instantiation +  } + +  static void checkAvailable() throws RuntimeException { +    if (INIT_EXCEPTION != null) { +      throw new UnsupportedOperationException( +          "PC/SC not available on this platform", INIT_EXCEPTION); +    } +  } + +  // returns SCARDCONTEXT (contextId) +  static native long SCardEstablishContext(int scope) throws PCSCException; + +  static native String[] SCardListReaders(long contextId) throws PCSCException; + +  // returns SCARDHANDLE (cardId) +  static native long SCardConnect(long contextId, String readerName, +      int shareMode, int preferredProtocols) throws PCSCException; + +  static native byte[] SCardTransmit(long cardId, int protocol, byte[] buf, +      int ofs, int len) throws PCSCException; + +  // returns the ATR of the card, updates status[] with reader state and +  // protocol +  static native byte[] SCardStatus(long cardId, byte[] status) +      throws PCSCException; + +  static native void SCardDisconnect(long cardId, int disposition) +      throws PCSCException; + +  // returns dwEventState[] of the same size and order as readerNames[] +  static native int[] SCardGetStatusChange(long contextId, long timeout, +      int[] currentState, String[] readerNames) throws PCSCException; + +  static native void SCardBeginTransaction(long cardId) throws PCSCException; + +  static native void SCardEndTransaction(long cardId, int disposition) +      throws PCSCException; + +  static native byte[] SCardControl(long cardId, int controlCode, +      byte[] sendBuffer) throws PCSCException; + +  // PCSC success/error/failure/warning codes +  final static int SCARD_S_SUCCESS = 0x00000000; +  final static int SCARD_E_CANCELLED = 0x80100002; +  final static int SCARD_E_CANT_DISPOSE = 0x8010000E; +  final static int SCARD_E_INSUFFICIENT_BUFFER = 0x80100008; +  final static int SCARD_E_INVALID_ATR = 0x80100015; +  final static int SCARD_E_INVALID_HANDLE = 0x80100003; +  final static int SCARD_E_INVALID_PARAMETER = 0x80100004; +  final static int SCARD_E_INVALID_TARGET = 0x80100005; +  final static int SCARD_E_INVALID_VALUE = 0x80100011; +  final static int SCARD_E_NO_MEMORY = 0x80100006; +  final static int SCARD_F_COMM_ERROR = 0x80100013; +  final static int SCARD_F_INTERNAL_ERROR = 0x80100001; +  final static int SCARD_F_UNKNOWN_ERROR = 0x80100014; +  final static int SCARD_F_WAITED_TOO_LONG = 0x80100007; +  final static int SCARD_E_UNKNOWN_READER = 0x80100009; +  final static int SCARD_E_TIMEOUT = 0x8010000A; +  final static int SCARD_E_SHARING_VIOLATION = 0x8010000B; +  final static int SCARD_E_NO_SMARTCARD = 0x8010000C; +  final static int SCARD_E_UNKNOWN_CARD = 0x8010000D; +  final static int SCARD_E_PROTO_MISMATCH = 0x8010000F; +  final static int SCARD_E_NOT_READY = 0x80100010; +  final static int SCARD_E_SYSTEM_CANCELLED = 0x80100012; +  final static int SCARD_E_NOT_TRANSACTED = 0x80100016; +  final static int SCARD_E_READER_UNAVAILABLE = 0x80100017; + +  final static int SCARD_W_UNSUPPORTED_CARD = 0x80100065; +  final static int SCARD_W_UNRESPONSIVE_CARD = 0x80100066; +  final static int SCARD_W_UNPOWERED_CARD = 0x80100067; +  final static int SCARD_W_RESET_CARD = 0x80100068; +  final static int SCARD_W_REMOVED_CARD = 0x80100069; +  final static int SCARD_W_INSERTED_CARD = 0x8010006A; + +  final static int SCARD_E_UNSUPPORTED_FEATURE = 0x8010001F; +  final static int SCARD_E_PCI_TOO_SMALL = 0x80100019; +  final static int SCARD_E_READER_UNSUPPORTED = 0x8010001A; +  final static int SCARD_E_DUPLICATE_READER = 0x8010001B; +  final static int SCARD_E_CARD_UNSUPPORTED = 0x8010001C; +  final static int SCARD_E_NO_SERVICE = 0x8010001D; +  final static int SCARD_E_SERVICE_STOPPED = 0x8010001E; + +  // MS undocumented +  final static int SCARD_E_NO_READERS_AVAILABLE = 0x8010002E; +  // std. Windows invalid handle return code, used instead of SCARD code +  final static int WINDOWS_ERROR_INVALID_HANDLE = 6; +  final static int WINDOWS_ERROR_INVALID_PARAMETER = 87; + +  // +  final static int SCARD_SCOPE_USER = 0x0000; +  final static int SCARD_SCOPE_TERMINAL = 0x0001; +  final static int SCARD_SCOPE_SYSTEM = 0x0002; +  final static int SCARD_SCOPE_GLOBAL = 0x0003; + +  final static int SCARD_SHARE_EXCLUSIVE = 0x0001; +  final static int SCARD_SHARE_SHARED = 0x0002; +  final static int SCARD_SHARE_DIRECT = 0x0003; + +  final static int SCARD_LEAVE_CARD = 0x0000; +  final static int SCARD_RESET_CARD = 0x0001; +  final static int SCARD_UNPOWER_CARD = 0x0002; +  final static int SCARD_EJECT_CARD = 0x0003; + +  final static int SCARD_STATE_UNAWARE = 0x0000; +  final static int SCARD_STATE_IGNORE = 0x0001; +  final static int SCARD_STATE_CHANGED = 0x0002; +  final static int SCARD_STATE_UNKNOWN = 0x0004; +  final static int SCARD_STATE_UNAVAILABLE = 0x0008; +  final static int SCARD_STATE_EMPTY = 0x0010; +  final static int SCARD_STATE_PRESENT = 0x0020; +  final static int SCARD_STATE_ATRMATCH = 0x0040; +  final static int SCARD_STATE_EXCLUSIVE = 0x0080; +  final static int SCARD_STATE_INUSE = 0x0100; +  final static int SCARD_STATE_MUTE = 0x0200; +  final static int SCARD_STATE_UNPOWERED = 0x0400; + +  final static int TIMEOUT_INFINITE = 0xffffffff; + +  private final static char[] hexDigits = "0123456789abcdef".toCharArray(); + +  public static String toString(byte[] b) { +    StringBuffer sb = new StringBuffer(b.length * 3); +    for (int i = 0; i < b.length; i++) { +      int k = b[i] & 0xff; +      if (i != 0) { +        sb.append(':'); +      } +      sb.append(hexDigits[k >>> 4]); +      sb.append(hexDigits[k & 0xf]); +    } +    return sb.toString(); +  } + +} diff --git a/smcc/src/main/java/org/openecard/scio/osx/PCSCException.java b/smcc/src/main/java/org/openecard/scio/osx/PCSCException.java new file mode 100644 index 00000000..fc25be1d --- /dev/null +++ b/smcc/src/main/java/org/openecard/scio/osx/PCSCException.java @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation.  Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package org.openecard.scio.osx; + +import static org.openecard.scio.osx.PCSC.SCARD_E_CANCELLED; +import static org.openecard.scio.osx.PCSC.SCARD_E_CANT_DISPOSE; +import static org.openecard.scio.osx.PCSC.SCARD_E_CARD_UNSUPPORTED; +import static org.openecard.scio.osx.PCSC.SCARD_E_DUPLICATE_READER; +import static org.openecard.scio.osx.PCSC.SCARD_E_INSUFFICIENT_BUFFER; +import static org.openecard.scio.osx.PCSC.SCARD_E_INVALID_ATR; +import static org.openecard.scio.osx.PCSC.SCARD_E_INVALID_HANDLE; +import static org.openecard.scio.osx.PCSC.SCARD_E_INVALID_PARAMETER; +import static org.openecard.scio.osx.PCSC.SCARD_E_INVALID_TARGET; +import static org.openecard.scio.osx.PCSC.SCARD_E_INVALID_VALUE; +import static org.openecard.scio.osx.PCSC.SCARD_E_NOT_READY; +import static org.openecard.scio.osx.PCSC.SCARD_E_NOT_TRANSACTED; +import static org.openecard.scio.osx.PCSC.SCARD_E_NO_MEMORY; +import static org.openecard.scio.osx.PCSC.SCARD_E_NO_READERS_AVAILABLE; +import static org.openecard.scio.osx.PCSC.SCARD_E_NO_SERVICE; +import static org.openecard.scio.osx.PCSC.SCARD_E_NO_SMARTCARD; +import static org.openecard.scio.osx.PCSC.SCARD_E_PCI_TOO_SMALL; +import static org.openecard.scio.osx.PCSC.SCARD_E_PROTO_MISMATCH; +import static org.openecard.scio.osx.PCSC.SCARD_E_READER_UNAVAILABLE; +import static org.openecard.scio.osx.PCSC.SCARD_E_READER_UNSUPPORTED; +import static org.openecard.scio.osx.PCSC.SCARD_E_SERVICE_STOPPED; +import static org.openecard.scio.osx.PCSC.SCARD_E_SHARING_VIOLATION; +import static org.openecard.scio.osx.PCSC.SCARD_E_SYSTEM_CANCELLED; +import static org.openecard.scio.osx.PCSC.SCARD_E_TIMEOUT; +import static org.openecard.scio.osx.PCSC.SCARD_E_UNKNOWN_CARD; +import static org.openecard.scio.osx.PCSC.SCARD_E_UNKNOWN_READER; +import static org.openecard.scio.osx.PCSC.SCARD_E_UNSUPPORTED_FEATURE; +import static org.openecard.scio.osx.PCSC.SCARD_F_COMM_ERROR; +import static org.openecard.scio.osx.PCSC.SCARD_F_INTERNAL_ERROR; +import static org.openecard.scio.osx.PCSC.SCARD_F_UNKNOWN_ERROR; +import static org.openecard.scio.osx.PCSC.SCARD_F_WAITED_TOO_LONG; +import static org.openecard.scio.osx.PCSC.SCARD_S_SUCCESS; +import static org.openecard.scio.osx.PCSC.SCARD_W_INSERTED_CARD; +import static org.openecard.scio.osx.PCSC.SCARD_W_REMOVED_CARD; +import static org.openecard.scio.osx.PCSC.SCARD_W_RESET_CARD; +import static org.openecard.scio.osx.PCSC.SCARD_W_UNPOWERED_CARD; +import static org.openecard.scio.osx.PCSC.SCARD_W_UNRESPONSIVE_CARD; +import static org.openecard.scio.osx.PCSC.SCARD_W_UNSUPPORTED_CARD; +import static org.openecard.scio.osx.PCSC.WINDOWS_ERROR_INVALID_HANDLE; +import static org.openecard.scio.osx.PCSC.WINDOWS_ERROR_INVALID_PARAMETER; + +/** + * Exception for PC/SC errors. The native code portion checks the return value + * of the SCard* functions. If it indicates an error, the native code constructs + * an instance of this exception, throws it, and returns to Java. + *  + * @since 1.6 + * @author Andreas Sterbenz + */ +final class PCSCException extends Exception { + +  private static final long serialVersionUID = -3703197031255879618L; + +  final int code; + +  PCSCException(int code) { +    super(toErrorString(code)); +    this.code = code; +  } + +  private static String toErrorString(int code) { +    switch (code) { +    case SCARD_S_SUCCESS: +      return "SCARD_S_SUCCESS"; +    case SCARD_E_CANCELLED: +      return "SCARD_E_CANCELLED"; +    case SCARD_E_CANT_DISPOSE: +      return "SCARD_E_CANT_DISPOSE"; +    case SCARD_E_INSUFFICIENT_BUFFER: +      return "SCARD_E_INSUFFICIENT_BUFFER"; +    case SCARD_E_INVALID_ATR: +      return "SCARD_E_INVALID_ATR"; +    case SCARD_E_INVALID_HANDLE: +      return "SCARD_E_INVALID_HANDLE"; +    case SCARD_E_INVALID_PARAMETER: +      return "SCARD_E_INVALID_PARAMETER"; +    case SCARD_E_INVALID_TARGET: +      return "SCARD_E_INVALID_TARGET"; +    case SCARD_E_INVALID_VALUE: +      return "SCARD_E_INVALID_VALUE"; +    case SCARD_E_NO_MEMORY: +      return "SCARD_E_NO_MEMORY"; +    case SCARD_F_COMM_ERROR: +      return "SCARD_F_COMM_ERROR"; +    case SCARD_F_INTERNAL_ERROR: +      return "SCARD_F_INTERNAL_ERROR"; +    case SCARD_F_UNKNOWN_ERROR: +      return "SCARD_F_UNKNOWN_ERROR"; +    case SCARD_F_WAITED_TOO_LONG: +      return "SCARD_F_WAITED_TOO_LONG"; +    case SCARD_E_UNKNOWN_READER: +      return "SCARD_E_UNKNOWN_READER"; +    case SCARD_E_TIMEOUT: +      return "SCARD_E_TIMEOUT"; +    case SCARD_E_SHARING_VIOLATION: +      return "SCARD_E_SHARING_VIOLATION"; +    case SCARD_E_NO_SMARTCARD: +      return "SCARD_E_NO_SMARTCARD"; +    case SCARD_E_UNKNOWN_CARD: +      return "SCARD_E_UNKNOWN_CARD"; +    case SCARD_E_PROTO_MISMATCH: +      return "SCARD_E_PROTO_MISMATCH"; +    case SCARD_E_NOT_READY: +      return "SCARD_E_NOT_READY"; +    case SCARD_E_SYSTEM_CANCELLED: +      return "SCARD_E_SYSTEM_CANCELLED"; +    case SCARD_E_NOT_TRANSACTED: +      return "SCARD_E_NOT_TRANSACTED"; +    case SCARD_E_READER_UNAVAILABLE: +      return "SCARD_E_READER_UNAVAILABLE"; + +    case SCARD_W_UNSUPPORTED_CARD: +      return "SCARD_W_UNSUPPORTED_CARD"; +    case SCARD_W_UNRESPONSIVE_CARD: +      return "SCARD_W_UNRESPONSIVE_CARD"; +    case SCARD_W_UNPOWERED_CARD: +      return "SCARD_W_UNPOWERED_CARD"; +    case SCARD_W_RESET_CARD: +      return "SCARD_W_RESET_CARD"; +    case SCARD_W_REMOVED_CARD: +      return "SCARD_W_REMOVED_CARD"; +    case SCARD_W_INSERTED_CARD: +      return "SCARD_W_INSERTED_CARD"; + +    case SCARD_E_UNSUPPORTED_FEATURE: +      return "SCARD_E_UNSUPPORTED_FEATURE"; +    case SCARD_E_PCI_TOO_SMALL: +      return "SCARD_E_PCI_TOO_SMALL"; +    case SCARD_E_READER_UNSUPPORTED: +      return "SCARD_E_READER_UNSUPPORTED"; +    case SCARD_E_DUPLICATE_READER: +      return "SCARD_E_DUPLICATE_READER"; +    case SCARD_E_CARD_UNSUPPORTED: +      return "SCARD_E_CARD_UNSUPPORTED"; +    case SCARD_E_NO_SERVICE: +      return "SCARD_E_NO_SERVICE"; +    case SCARD_E_SERVICE_STOPPED: +      return "SCARD_E_SERVICE_STOPPED"; + +    case SCARD_E_NO_READERS_AVAILABLE: +      return "SCARD_E_NO_READERS_AVAILABLE"; +    case WINDOWS_ERROR_INVALID_HANDLE: +      return "WINDOWS_ERROR_INVALID_HANDLE"; +    case WINDOWS_ERROR_INVALID_PARAMETER: +      return "WINDOWS_ERROR_INVALID_PARAMETER"; + +    default: +      return "Unknown error 0x" + Integer.toHexString(code); +    } +  } +} diff --git a/smcc/src/main/java/org/openecard/scio/osx/PCSCTerminals.java b/smcc/src/main/java/org/openecard/scio/osx/PCSCTerminals.java new file mode 100644 index 00000000..495d0679 --- /dev/null +++ b/smcc/src/main/java/org/openecard/scio/osx/PCSCTerminals.java @@ -0,0 +1,290 @@ +/* + * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation.  Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package org.openecard.scio.osx; + +import static javax.smartcardio.CardTerminals.State.CARD_ABSENT; +import static javax.smartcardio.CardTerminals.State.CARD_INSERTION; +import static javax.smartcardio.CardTerminals.State.CARD_PRESENT; +import static javax.smartcardio.CardTerminals.State.CARD_REMOVAL; +import static org.openecard.scio.osx.PCSC.SCARD_E_TIMEOUT; +import static org.openecard.scio.osx.PCSC.SCARD_SCOPE_USER; +import static org.openecard.scio.osx.PCSC.SCARD_STATE_PRESENT; +import static org.openecard.scio.osx.PCSC.SCARD_STATE_UNAWARE; +import static org.openecard.scio.osx.PCSC.SCardEstablishContext; +import static org.openecard.scio.osx.PCSC.SCardGetStatusChange; +import static org.openecard.scio.osx.PCSC.SCardListReaders; +import static org.openecard.scio.osx.PCSC.TIMEOUT_INFINITE; + +import java.lang.ref.Reference; +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.smartcardio.CardException; +import javax.smartcardio.CardTerminal; +import javax.smartcardio.CardTerminals; + +/** + * TerminalFactorySpi implementation class. + *  + * @since 1.6 + * @author Andreas Sterbenz + */ +final class PCSCTerminals extends CardTerminals { + +  // SCARDCONTEXT, currently shared between all threads/terminals +  private static long contextId; + +  // terminal state used by waitForCard() +  private Map<String, ReaderState> stateMap; + +  PCSCTerminals() { +    // empty +  } + +  static synchronized void initContext() throws PCSCException { +    if (contextId == 0) { +      contextId = SCardEstablishContext(SCARD_SCOPE_USER); +    } +  } + +  private static final Map<String, Reference<TerminalImpl>> terminals = new HashMap<String, Reference<TerminalImpl>>(); + +  private static synchronized TerminalImpl implGetTerminal(String name) { +    Reference<TerminalImpl> ref = terminals.get(name); +    TerminalImpl terminal = (ref != null) ? ref.get() : null; +    if (terminal != null) { +      return terminal; +    } +    terminal = new TerminalImpl(contextId, name); +    terminals.put(name, new WeakReference<TerminalImpl>(terminal)); +    return terminal; + +  } + +  public synchronized List<CardTerminal> list(State state) throws CardException { +    if (state == null) { +      throw new NullPointerException(); +    } +    try { +      String[] readerNames = SCardListReaders(contextId); +      List<CardTerminal> list = new ArrayList<CardTerminal>(readerNames.length); +      if (stateMap == null) { +        // If waitForChange() has never been called, treat event +        // queries as status queries. +        if (state == CARD_INSERTION) { +          state = CARD_PRESENT; +        } else if (state == CARD_REMOVAL) { +          state = CARD_ABSENT; +        } +      } +      for (String readerName : readerNames) { +        CardTerminal terminal = implGetTerminal(readerName); +        ReaderState readerState; +        switch (state) { +        case ALL: +          list.add(terminal); +          break; +        case CARD_PRESENT: +          if (terminal.isCardPresent()) { +            list.add(terminal); +          } +          break; +        case CARD_ABSENT: +          if (terminal.isCardPresent() == false) { +            list.add(terminal); +          } +          break; +        case CARD_INSERTION: +          readerState = stateMap.get(readerName); +          if ((readerState != null) && readerState.isInsertion()) { +            list.add(terminal); +          } +          break; +        case CARD_REMOVAL: +          readerState = stateMap.get(readerName); +          if ((readerState != null) && readerState.isRemoval()) { +            list.add(terminal); +          } +          break; +        default: +          throw new CardException("Unknown state: " + state); +        } +      } +      return Collections.unmodifiableList(list); +    } catch (PCSCException e) { +      throw new CardException("list() failed", e); +    } +  } + +  private static class ReaderState { +    private int current, previous; + +    ReaderState() { +      current = SCARD_STATE_UNAWARE; +      previous = SCARD_STATE_UNAWARE; +    } + +    int get() { +      return current; +    } + +    void update(int newState) { +      previous = current; +      current = newState; +    } + +    boolean isInsertion() { +      return !present(previous) && present(current); +    } + +    boolean isRemoval() { +      return present(previous) && !present(current); +    } + +    static boolean present(int state) { +      return (state & SCARD_STATE_PRESENT) != 0; +    } +  } + +  public synchronized boolean waitForChange(long timeout) throws CardException { +    if (timeout < 0) { +      throw new IllegalArgumentException("Timeout must not be negative: " +          + timeout); +    } +    if (stateMap == null) { +      // We need to initialize the state database. +      // Do that with a recursive call, which will return immediately +      // because we pass SCARD_STATE_UNAWARE. +      // After that, proceed with the real call. +      stateMap = new HashMap<String, ReaderState>(); +      waitForChange(0); +    } +    if (timeout == 0) { +      timeout = TIMEOUT_INFINITE; +    } +    try { +      String[] readerNames = SCardListReaders(contextId); +      int n = readerNames.length; +      if (n == 0) { +        throw new IllegalStateException("No terminals available"); +      } +      int[] status = new int[n]; +      ReaderState[] readerStates = new ReaderState[n]; +      for (int i = 0; i < readerNames.length; i++) { +        String name = readerNames[i]; +        ReaderState state = stateMap.get(name); +        if (state == null) { +          state = new ReaderState(); +        } +        readerStates[i] = state; +        status[i] = state.get(); +      } +      status = SCardGetStatusChange(contextId, timeout, status, readerNames); +      stateMap.clear(); // remove any readers that are no longer available +      for (int i = 0; i < n; i++) { +        ReaderState state = readerStates[i]; +        state.update(status[i]); +        stateMap.put(readerNames[i], state); +      } +      return true; +    } catch (PCSCException e) { +      if (e.code == SCARD_E_TIMEOUT) { +        return false; +      } else { +        throw new CardException("waitForChange() failed", e); +      } +    } +  } + +  static List<CardTerminal> waitForCards( +      List<? extends CardTerminal> terminals, long timeout, boolean wantPresent) +      throws CardException { +    // the argument sanity checks are performed in +    // javax.smartcardio.TerminalFactory or TerminalImpl + +    long thisTimeout; +    if (timeout == 0) { +      timeout = TIMEOUT_INFINITE; +      thisTimeout = TIMEOUT_INFINITE; +    } else { +      // if timeout is not infinite, do the initial call that retrieves +      // the status with a 0 timeout. Otherwise, we might get incorrect +      // timeout exceptions (seen on Solaris with PC/SC shim) +      thisTimeout = 0; +    } + +    String[] names = new String[terminals.size()]; +    int i = 0; +    for (CardTerminal terminal : terminals) { +      if (terminal instanceof TerminalImpl == false) { +        throw new IllegalArgumentException("Invalid terminal type: " +            + terminal.getClass().getName()); +      } +      TerminalImpl impl = (TerminalImpl) terminal; +      names[i++] = impl.name; +    } + +    int[] status = new int[names.length]; +    Arrays.fill(status, SCARD_STATE_UNAWARE); + +    try { +      while (true) { +        // note that we pass "timeout" on each native PC/SC call +        // that means that if we end up making multiple (more than 2) +        // calls, we might wait too long. +        // for now assume that is unlikely and not a problem. +        status = SCardGetStatusChange(contextId, thisTimeout, status, names); +        thisTimeout = timeout; + +        List<CardTerminal> results = null; +        for (i = 0; i < names.length; i++) { +          boolean nowPresent = (status[i] & SCARD_STATE_PRESENT) != 0; +          if (nowPresent == wantPresent) { +            if (results == null) { +              results = new ArrayList<CardTerminal>(); +            } +            results.add(implGetTerminal(names[i])); +          } +        } + +        if (results != null) { +          return Collections.unmodifiableList(results); +        } +      } +    } catch (PCSCException e) { +      if (e.code == SCARD_E_TIMEOUT) { +        return Collections.emptyList(); +      } else { +        throw new CardException("waitForCard() failed", e); +      } +    } +  } + +} diff --git a/smcc/src/main/java/org/openecard/scio/osx/PlatformPCSC.java b/smcc/src/main/java/org/openecard/scio/osx/PlatformPCSC.java new file mode 100644 index 00000000..bbc48bc0 --- /dev/null +++ b/smcc/src/main/java/org/openecard/scio/osx/PlatformPCSC.java @@ -0,0 +1,125 @@ +/**************************************************************************** + * Copyright (C) 2012-2013 ecsec GmbH. + * All rights reserved. + * Contact: ecsec GmbH (info@ecsec.de) + * + * This file is part of the Open eCard App. + * + * GNU General Public License Usage + * This file may be used under the terms of the GNU General Public + * License version 3.0 as published by the Free Software Foundation + * and appearing in the file LICENSE.GPL included in the packaging of + * this file. Please review the following information to ensure the + * GNU General Public License version 3.0 requirements will be met: + * http://www.gnu.org/copyleft/gpl.html. + * + * Other Usage + * Alternatively, this file may be used in accordance with the terms + * and conditions contained in a signed written agreement between + * you and ecsec GmbH. + * + ***************************************************************************/ + +package org.openecard.scio.osx; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.AccessController; +import java.security.PrivilegedAction; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * OS X specific PlatformPCSC. For more information see {@link package-info}. + *  + * @author Benedikt Biallowons <benedikt.biallowons@ecsec.de> + */ +public class PlatformPCSC { + +  private static final String PCSC_JNI_LIBRARY_PATH = "/at/gv/egiz/smcc/osx-pcsc-jni/"; +  private static final String PCSC_JNI_LIBRARY_NAME = "libosxj2pcsc.dylib"; +  private static final String PCSC_FRAMEWORK = "/System/Library/Frameworks/PCSC.framework/Versions/Current/PCSC"; + +  private final static Logger log = LoggerFactory.getLogger(PlatformPCSC.class); + +  public static final int SCARD_PROTOCOL_T0 = 0x0001; +  public static final int SCARD_PROTOCOL_T1 = 0x0002; +  public static final int SCARD_PROTOCOL_RAW = 0x0004; +  public static final int SCARD_UNKNOWN = 0x0001; +  public static final int SCARD_ABSENT = 0x0002; +  public static final int SCARD_PRESENT = 0x0004; +  public static final int SCARD_SWALLOWED = 0x0008; +  public static final int SCARD_POWERED = 0x0010; +  public static final int SCARD_NEGOTIABLE = 0x0020; +  public static final int SCARD_SPECIFIC = 0x0040; + +  public static final Exception INIT_EXCEPTION; + +  static { +    INIT_EXCEPTION = AccessController +        .doPrivileged(new PrivilegedAction<Exception>() { +          public Exception run() { +            try { +              String[] parts = PCSC_JNI_LIBRARY_NAME.split("\\."); + +              File tempFile = File.createTempFile(parts[0], parts[1]); +              tempFile.deleteOnExit(); + +              log.debug("Copying " + PCSC_JNI_LIBRARY_PATH + getLibrary() + " to " + tempFile); +              InputStream is = PlatformPCSC.class +                  .getResourceAsStream(PCSC_JNI_LIBRARY_PATH + getLibrary()); + +              if (is == null) { +                return new FileNotFoundException(getLibrary() + " not found."); +              } + +              OutputStream os = new FileOutputStream(tempFile); + +              try { +                byte[] buffer = new byte[1024]; +                int readBytes; + +                while ((readBytes = is.read(buffer)) != -1) { +                  os.write(buffer, 0, readBytes); +                } + +                log.debug("loading " + tempFile.getAbsolutePath()); +                System.load(tempFile.getAbsolutePath()); +                initialize(PCSC_FRAMEWORK); +              } finally { +                os.close(); +                is.close(); +              } + +              return null; +            } catch (Exception e) { +              return e; +            } +          } +        }); +  } + +  /** +   * Chooses a suitable JNI library depending the current JRE. +   *  +   * @return library filename +   */ +  private static String getLibrary() { +    String javaVersion = System.getProperty("java.version"); + +    if (javaVersion.startsWith("1.6")) { +      return "jre6." + PCSC_JNI_LIBRARY_NAME; +    } else if (javaVersion.startsWith("1.7")) { +      return "jre7." + PCSC_JNI_LIBRARY_NAME; +    } + +    return null; +  } + +  private static native void initialize(String libraryName); + +} diff --git a/smcc/src/main/java/org/openecard/scio/osx/SunOSXPCSC.java b/smcc/src/main/java/org/openecard/scio/osx/SunOSXPCSC.java new file mode 100644 index 00000000..f05d0373 --- /dev/null +++ b/smcc/src/main/java/org/openecard/scio/osx/SunOSXPCSC.java @@ -0,0 +1,89 @@ +/**************************************************************************** + * Copyright (C) 2012-2013 ecsec GmbH. + * All rights reserved. + * Contact: ecsec GmbH (info@ecsec.de) + * + * This file is part of the Open eCard App. + * + * GNU General Public License Usage + * This file may be used under the terms of the GNU General Public + * License version 3.0 as published by the Free Software Foundation + * and appearing in the file LICENSE.GPL included in the packaging of + * this file. Please review the following information to ensure the + * GNU General Public License version 3.0 requirements will be met: + * http://www.gnu.org/copyleft/gpl.html. + * + * Other Usage + * Alternatively, this file may be used in accordance with the terms + * and conditions contained in a signed written agreement between + * you and ecsec GmbH. + * + ***************************************************************************/ + +package org.openecard.scio.osx; + +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.security.Provider; +import javax.smartcardio.CardTerminals; +import javax.smartcardio.TerminalFactorySpi; + +/** + * OS X PC/SC provider. For more information see {@link package-info}. + *  + * @author Benedikt Biallowons <benedikt.biallowons@ecsec.de> + */ +public final class SunOSXPCSC extends Provider { + +  private static final long serialVersionUID = -8720099822090870310L; + +  /** +   * Default constructor registering this provider. +   */ +  public SunOSXPCSC() { +    super("SunOSXPCSC", 1.0d, "Sun OS X PC/SC provider"); +    AccessController.doPrivileged(new PrivilegedAction<Void>() { +      public Void run() { +        put("TerminalFactory.PC/SC", +            "org.openecard.scio.osx.SunOSXPCSC$Factory"); +        return null; +      } +    }); +  } + +  /** +   * Internal provider factory. For more information see +   * {@link TerminalFactorySpi} +   *  +   * @author Benedikt Biallowons <benedikt.biallowons@ecsec.de> +   */ +  public static final class Factory extends TerminalFactorySpi { +    /** +     * Constructor checking availability & initializing context. +     *  +     * @param obj +     *          provider parameters +     * @throws PCSCException +     *           if problems occur. +     */ +    public Factory(Object obj) throws PCSCException { +      if (obj != null) { +        throw new IllegalArgumentException( +            "SunOSXPCSC factory doesn't take parameters"); +      } +      PCSC.checkAvailable(); +      PCSCTerminals.initContext(); +    } + +    /** +     * Returns the available readers. This must be a new object for each call. +     *  +     * @return CardTerminals card terminals wrapper object +     */ +    @Override +    protected CardTerminals engineTerminals() { +      return new PCSCTerminals(); +    } +  } + +} diff --git a/smcc/src/main/java/org/openecard/scio/osx/TerminalImpl.java b/smcc/src/main/java/org/openecard/scio/osx/TerminalImpl.java new file mode 100644 index 00000000..993fe67a --- /dev/null +++ b/smcc/src/main/java/org/openecard/scio/osx/TerminalImpl.java @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation.  Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package org.openecard.scio.osx; + +import static org.openecard.scio.osx.PCSC.SCARD_E_TIMEOUT; +import static org.openecard.scio.osx.PCSC.SCARD_STATE_PRESENT; +import static org.openecard.scio.osx.PCSC.SCARD_STATE_UNAWARE; +import static org.openecard.scio.osx.PCSC.SCARD_W_REMOVED_CARD; +import static org.openecard.scio.osx.PCSC.SCardGetStatusChange; +import static org.openecard.scio.osx.PCSC.TIMEOUT_INFINITE; + +import javax.smartcardio.Card; +import javax.smartcardio.CardException; +import javax.smartcardio.CardNotPresentException; +import javax.smartcardio.CardPermission; +import javax.smartcardio.CardTerminal; + +/** + * CardTerminal implementation. + *  + * @since 1.6 + * @author Andreas Sterbenz + */ +final class TerminalImpl extends CardTerminal { + +  // native SCARDCONTEXT +  final long contextId; + +  // the name of this terminal (native PC/SC name) +  final String name; + +  private CardImpl card; + +  TerminalImpl(long contextId, String name) { +    this.contextId = contextId; +    this.name = name; +  } + +  public String getName() { +    return name; +  } + +  public synchronized Card connect(String protocol) throws CardException { +    SecurityManager sm = System.getSecurityManager(); +    if (sm != null) { +      sm.checkPermission(new CardPermission(name, "connect")); +    } +    if (card != null) { +      if (card.isValid()) { +        String cardProto = card.getProtocol(); +        if (protocol.equals("*") || protocol.equalsIgnoreCase(cardProto)) { +          return card; +        } else { +          throw new CardException("Cannot connect using " + protocol +              + ", connection already established using " + cardProto); +        } +      } else { +        card = null; +      } +    } +    try { +      card = new CardImpl(this, protocol); +      return card; +    } catch (PCSCException e) { +      if (e.code == SCARD_W_REMOVED_CARD) { +        throw new CardNotPresentException("No card present", e); +      } else { +        throw new CardException("connect() failed", e); +      } +    } +  } + +  public boolean isCardPresent() throws CardException { +    try { +      int[] status = SCardGetStatusChange(contextId, 0, +          new int[] { SCARD_STATE_UNAWARE }, new String[] { name }); +      return (status[0] & SCARD_STATE_PRESENT) != 0; +    } catch (PCSCException e) { +      throw new CardException("isCardPresent() failed", e); +    } +  } + +  private boolean waitForCard(boolean wantPresent, long timeout) +      throws CardException { +    if (timeout < 0) { +      throw new IllegalArgumentException("timeout must not be negative"); +    } +    if (timeout == 0) { +      timeout = TIMEOUT_INFINITE; +    } +    int[] status = new int[] { SCARD_STATE_UNAWARE }; +    String[] readers = new String[] { name }; +    try { +      // check if card status already matches +      status = SCardGetStatusChange(contextId, 0, status, readers); +      boolean present = (status[0] & SCARD_STATE_PRESENT) != 0; +      if (wantPresent == present) { +        return true; +      } +      // no match, wait (until timeout expires) +      long end = System.currentTimeMillis() + timeout; +      while (wantPresent != present && timeout != 0) { +        // set remaining timeout +        if (timeout != TIMEOUT_INFINITE) { +          timeout = Math.max(end - System.currentTimeMillis(), 0l); +        } +        status = SCardGetStatusChange(contextId, timeout, status, readers); +        present = (status[0] & SCARD_STATE_PRESENT) != 0; +      } +      return wantPresent == present; +    } catch (PCSCException e) { +      if (e.code == SCARD_E_TIMEOUT) { +        return false; +      } else { +        throw new CardException("waitForCard() failed", e); +      } +    } +  } + +  public boolean waitForCardPresent(long timeout) throws CardException { +    return waitForCard(true, timeout); +  } + +  public boolean waitForCardAbsent(long timeout) throws CardException { +    return waitForCard(false, timeout); +  } + +  public String toString() { +    return "PC/SC terminal " + name; +  } +} diff --git a/smcc/src/main/java/org/openecard/scio/osx/package-info.java b/smcc/src/main/java/org/openecard/scio/osx/package-info.java new file mode 100644 index 00000000..5d066a40 --- /dev/null +++ b/smcc/src/main/java/org/openecard/scio/osx/package-info.java @@ -0,0 +1,29 @@ +/**************************************************************************** + * Copyright (C) 2012-2013 ecsec GmbH. + * All rights reserved. + * Contact: ecsec GmbH (info@ecsec.de) + * + * This file is part of the Open eCard App. + * + * GNU General Public License Usage + * This file may be used under the terms of the GNU General Public + * License version 3.0 as published by the Free Software Foundation + * and appearing in the file LICENSE.GPL included in the packaging of + * this file. Please review the following information to ensure the + * GNU General Public License version 3.0 requirements will be met: + * http://www.gnu.org/copyleft/gpl.html. + * + * Other Usage + * Alternatively, this file may be used in accordance with the terms + * and conditions contained in a signed written agreement between + * you and ecsec GmbH. + * + ***************************************************************************/ + +/** + * This package contains a OS X specific SmartcardIO implementation. We've to do + * this, because the current PC/SC JNI libraries on OS X (jre6 & jre7) are too buggy + * to be useful and the design of the sun.security.smartcardio package doesn't allow + * to specify another JNI library path. + */ +package org.openecard.scio.osx; | 
