summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/util/SmartCardIO.java9
-rw-r--r--smcc/src/main/java/org/openecard/scio/osx/CardImpl.java289
-rw-r--r--smcc/src/main/java/org/openecard/scio/osx/ChannelImpl.java298
-rw-r--r--smcc/src/main/java/org/openecard/scio/osx/PCSC.java173
-rw-r--r--smcc/src/main/java/org/openecard/scio/osx/PCSCException.java178
-rw-r--r--smcc/src/main/java/org/openecard/scio/osx/PCSCTerminals.java290
-rw-r--r--smcc/src/main/java/org/openecard/scio/osx/PlatformPCSC.java125
-rw-r--r--smcc/src/main/java/org/openecard/scio/osx/SunOSXPCSC.java89
-rw-r--r--smcc/src/main/java/org/openecard/scio/osx/TerminalImpl.java154
-rw-r--r--smcc/src/main/java/org/openecard/scio/osx/package-info.java29
-rw-r--r--smcc/src/main/resources/at/gv/egiz/smcc/osx-pcsc-jni/jre6.libosxj2pcsc.dylibbin0 -> 39484 bytes
-rw-r--r--smcc/src/main/resources/at/gv/egiz/smcc/osx-pcsc-jni/jre7.libosxj2pcsc.dylibbin0 -> 14908 bytes
12 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;
diff --git a/smcc/src/main/resources/at/gv/egiz/smcc/osx-pcsc-jni/jre6.libosxj2pcsc.dylib b/smcc/src/main/resources/at/gv/egiz/smcc/osx-pcsc-jni/jre6.libosxj2pcsc.dylib
new file mode 100644
index 00000000..2bc454e3
--- /dev/null
+++ b/smcc/src/main/resources/at/gv/egiz/smcc/osx-pcsc-jni/jre6.libosxj2pcsc.dylib
Binary files differ
diff --git a/smcc/src/main/resources/at/gv/egiz/smcc/osx-pcsc-jni/jre7.libosxj2pcsc.dylib b/smcc/src/main/resources/at/gv/egiz/smcc/osx-pcsc-jni/jre7.libosxj2pcsc.dylib
new file mode 100644
index 00000000..6228a60c
--- /dev/null
+++ b/smcc/src/main/resources/at/gv/egiz/smcc/osx-pcsc-jni/jre7.libosxj2pcsc.dylib
Binary files differ