/* * 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 sun.security.smartcardio; import java.nio.ByteBuffer; import javax.smartcardio.*; import static sun.security.smartcardio.PCSC.*; /** * 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(); } } }
/*
* 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 sun.security.smartcardio;
import java.nio.ByteBuffer;
import javax.smartcardio.*;
import static sun.security.smartcardio.PCSC.*;
/**
* 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();
}
}
}