Path: blob/master/src/java.smartcardio/share/classes/sun/security/smartcardio/ChannelImpl.java
41159 views
/*1* Copyright (c) 2005, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.security.smartcardio;2627import java.nio.*;28import java.security.AccessController;29import java.security.PrivilegedAction;3031import javax.smartcardio.*;3233import static sun.security.smartcardio.PCSC.*;3435/**36* CardChannel implementation.37*38* @since 1.639* @author Andreas Sterbenz40*/41final class ChannelImpl extends CardChannel {4243// the card this channel is associated with44private final CardImpl card;4546// the channel number, 0 for the basic logical channel47private final int channel;4849// whether this channel has been closed. only logical channels can be closed50private volatile boolean isClosed;5152ChannelImpl(CardImpl card, int channel) {53this.card = card;54this.channel = channel;55}5657void checkClosed() {58card.checkState();59if (isClosed) {60throw new IllegalStateException("Logical channel has been closed");61}62}6364public Card getCard() {65return card;66}6768public int getChannelNumber() {69checkClosed();70return channel;71}7273private static void checkManageChannel(byte[] b) {74if (b.length < 4) {75throw new IllegalArgumentException76("Command APDU must be at least 4 bytes long");77}78if ((b[0] >= 0) && (b[1] == 0x70)) {79throw new IllegalArgumentException80("Manage channel command not allowed, use openLogicalChannel()");81}82}8384public ResponseAPDU transmit(CommandAPDU command) throws CardException {85checkClosed();86card.checkExclusive();87byte[] commandBytes = command.getBytes();88byte[] responseBytes = doTransmit(commandBytes);89return new ResponseAPDU(responseBytes);90}9192public int transmit(ByteBuffer command, ByteBuffer response) throws CardException {93checkClosed();94card.checkExclusive();95if ((command == null) || (response == null)) {96throw new NullPointerException();97}98if (response.isReadOnly()) {99throw new ReadOnlyBufferException();100}101if (command == response) {102throw new IllegalArgumentException103("command and response must not be the same object");104}105if (response.remaining() < 258) {106throw new IllegalArgumentException107("Insufficient space in response buffer");108}109byte[] commandBytes = new byte[command.remaining()];110command.get(commandBytes);111byte[] responseBytes = doTransmit(commandBytes);112response.put(responseBytes);113return responseBytes.length;114}115116private final static boolean t0GetResponse =117getBooleanProperty("sun.security.smartcardio.t0GetResponse", true);118119private final static boolean t1GetResponse =120getBooleanProperty("sun.security.smartcardio.t1GetResponse", true);121122private final static boolean t1StripLe =123getBooleanProperty("sun.security.smartcardio.t1StripLe", false);124125private static boolean getBooleanProperty(String name, boolean def) {126@SuppressWarnings("removal")127String val = AccessController.doPrivileged(128(PrivilegedAction<String>) () -> System.getProperty(name));129if (val == null) {130return def;131}132if (val.equalsIgnoreCase("true")) {133return true;134} else if (val.equalsIgnoreCase("false")) {135return false;136} else {137throw new IllegalArgumentException138(name + " must be either 'true' or 'false'");139}140}141142private byte[] concat(byte[] b1, byte[] b2, int n2) {143int n1 = b1.length;144if ((n1 == 0) && (n2 == b2.length)) {145return b2;146}147byte[] res = new byte[n1 + n2];148System.arraycopy(b1, 0, res, 0, n1);149System.arraycopy(b2, 0, res, n1, n2);150return res;151}152153private final static int RESPONSE_ITERATIONS = 256;154private final static byte[] B0 = new byte[0];155156private byte[] doTransmit(byte[] command) throws CardException {157// note that we modify the 'command' array in some cases, so it must158// be a copy of the application provided data.159try {160checkManageChannel(command);161setChannel(command);162int n = command.length;163boolean t0 = card.protocol == SCARD_PROTOCOL_T0;164boolean t1 = card.protocol == SCARD_PROTOCOL_T1;165if (t0 && (n >= 7) && (command[4] == 0)) {166throw new CardException167("Extended length forms not supported for T=0");168}169if ((t0 || (t1 && t1StripLe)) && (n >= 7)) {170int lc = command[4] & 0xff;171if (lc != 0) {172if (n == lc + 6) {173n--;174}175} else {176lc = ((command[5] & 0xff) << 8) | (command[6] & 0xff);177if (n == lc + 9) {178n -= 2;179}180}181}182boolean getresponse = (t0 && t0GetResponse) || (t1 && t1GetResponse);183int k = 0;184byte[] result = B0;185while (true) {186if (++k > RESPONSE_ITERATIONS) {187throw new CardException("Number of response iterations" +188" exceeded maximum " + RESPONSE_ITERATIONS);189}190byte[] response = SCardTransmit191(card.cardId, card.protocol, command, 0, n);192int rn = response.length;193if (getresponse && (rn >= 2) && (n >= 1)) {194// see ISO 7816/2005, 5.1.3195if ((rn == 2) && (response[0] == 0x6c)) {196// Resend command using SW2 as short Le field197command[n - 1] = response[1];198continue;199}200if (response[rn - 2] == 0x61) {201// Issue a GET RESPONSE command with the same CLA202// using SW2 as short Le field203if (rn > 2) {204result = concat(result, response, rn - 2);205}206if (command.length < 5) {207byte cla = command[0];208command = new byte[5];209command[0] = cla;210}211command[1] = (byte)0xC0;212command[2] = 0;213command[3] = 0;214command[4] = response[rn - 1];215n = 5;216continue;217}218}219result = concat(result, response, rn);220break;221}222return result;223} catch (PCSCException e) {224card.handleError(e);225throw new CardException(e);226}227}228229private static int getSW(byte[] res) throws CardException {230if (res.length < 2) {231throw new CardException("Invalid response length: " + res.length);232}233int sw1 = res[res.length - 2] & 0xff;234int sw2 = res[res.length - 1] & 0xff;235return (sw1 << 8) | sw2;236}237238private static boolean isOK(byte[] res) throws CardException {239return (res.length == 2) && (getSW(res) == 0x9000);240}241242private void setChannel(byte[] com) {243int cla = com[0];244if (cla < 0) {245// proprietary class format, cannot set or check logical channel246// for now, just return247return;248}249// classes 001x xxxx is reserved for future use in ISO, ignore250if ((cla & 0xe0) == 0x20) {251return;252}253// see ISO 7816/2005, table 2 and 3254if (channel <= 3) {255// mask of bits 7, 1, 0 (channel number)256// 0xbc == 1011 1100257com[0] &= 0xbc;258com[0] |= channel;259} else if (channel <= 19) {260// mask of bits 7, 3, 2, 1, 0 (channel number)261// 0xbc == 1011 0000262com[0] &= 0xb0;263com[0] |= 0x40;264com[0] |= (channel - 4);265} else {266throw new RuntimeException("Unsupported channel number: " + channel);267}268}269270public void close() throws CardException {271if (getChannelNumber() == 0) {272throw new IllegalStateException("Cannot close basic logical channel");273}274if (isClosed) {275return;276}277card.checkExclusive();278try {279byte[] com = new byte[] {0x00, 0x70, (byte)0x80, 0};280com[3] = (byte)getChannelNumber();281setChannel(com);282byte[] res = SCardTransmit(card.cardId, card.protocol, com, 0, com.length);283if (isOK(res) == false) {284throw new CardException("close() failed: " + PCSC.toString(res));285}286} catch (PCSCException e) {287card.handleError(e);288throw new CardException("Could not close channel", e);289} finally {290isClosed = true;291}292}293294public String toString() {295return "PC/SC channel " + channel;296}297298}299300301