From 890c3c0d321c3b59ec1224d4630b3f2401ef94e7 Mon Sep 17 00:00:00 2001 From: tkellner Date: Tue, 7 Jan 2014 17:28:54 +0000 Subject: Add custom PC/SC library for MacOS X from OpeneCard project git-svn-id: https://joinup.ec.europa.eu/svn/mocca/trunk@1276 8a26b1a7-26f0-462f-b9ef-d0e30c41f5a4 --- .../main/java/org/openecard/scio/osx/CardImpl.java | 289 +++++++++++++++++++++ 1 file changed, 289 insertions(+) create mode 100644 smcc/src/main/java/org/openecard/scio/osx/CardImpl.java (limited to 'smcc/src/main/java/org/openecard/scio/osx/CardImpl.java') 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(); + } + } + +} -- cgit v1.2.3