1 /*
2  * Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package sun.security.smartcardio;
27 
28 import java.nio.*;
29 import java.security.AccessController;
30 import java.security.PrivilegedAction;
31 
32 import javax.smartcardio.*;
33 
34 import static sun.security.smartcardio.PCSC.*;
35 
36 /**
37  * CardChannel implementation.
38  *
39  * @since   1.6
40  * @author  Andreas Sterbenz
41  */
42 final class ChannelImpl extends CardChannel {
43 
44     // the card this channel is associated with
45     private final CardImpl card;
46 
47     // the channel number, 0 for the basic logical channel
48     private final int channel;
49 
50     // whether this channel has been closed. only logical channels can be closed
51     private volatile boolean isClosed;
52 
ChannelImpl(CardImpl card, int channel)53     ChannelImpl(CardImpl card, int channel) {
54         this.card = card;
55         this.channel = channel;
56     }
57 
checkClosed()58     void checkClosed() {
59         card.checkState();
60         if (isClosed) {
61             throw new IllegalStateException("Logical channel has been closed");
62         }
63     }
64 
getCard()65     public Card getCard() {
66         return card;
67     }
68 
getChannelNumber()69     public int getChannelNumber() {
70         checkClosed();
71         return channel;
72     }
73 
checkManageChannel(byte[] b)74     private static void checkManageChannel(byte[] b) {
75         if (b.length < 4) {
76             throw new IllegalArgumentException
77                 ("Command APDU must be at least 4 bytes long");
78         }
79         if ((b[0] >= 0) && (b[1] == 0x70)) {
80             throw new IllegalArgumentException
81                 ("Manage channel command not allowed, use openLogicalChannel()");
82         }
83     }
84 
transmit(CommandAPDU command)85     public ResponseAPDU transmit(CommandAPDU command) throws CardException {
86         checkClosed();
87         card.checkExclusive();
88         byte[] commandBytes = command.getBytes();
89         byte[] responseBytes = doTransmit(commandBytes);
90         return new ResponseAPDU(responseBytes);
91     }
92 
transmit(ByteBuffer command, ByteBuffer response)93     public int transmit(ByteBuffer command, ByteBuffer response) throws CardException {
94         checkClosed();
95         card.checkExclusive();
96         if ((command == null) || (response == null)) {
97             throw new NullPointerException();
98         }
99         if (response.isReadOnly()) {
100             throw new ReadOnlyBufferException();
101         }
102         if (command == response) {
103             throw new IllegalArgumentException
104                     ("command and response must not be the same object");
105         }
106         if (response.remaining() < 258) {
107             throw new IllegalArgumentException
108                     ("Insufficient space in response buffer");
109         }
110         byte[] commandBytes = new byte[command.remaining()];
111         command.get(commandBytes);
112         byte[] responseBytes = doTransmit(commandBytes);
113         response.put(responseBytes);
114         return responseBytes.length;
115     }
116 
117     private final static boolean t0GetResponse =
118         getBooleanProperty("sun.security.smartcardio.t0GetResponse", true);
119 
120     private final static boolean t1GetResponse =
121         getBooleanProperty("sun.security.smartcardio.t1GetResponse", true);
122 
123     private final static boolean t1StripLe =
124         getBooleanProperty("sun.security.smartcardio.t1StripLe", false);
125 
getBooleanProperty(String name, boolean def)126     private static boolean getBooleanProperty(String name, boolean def) {
127         String val = AccessController.doPrivileged(
128             (PrivilegedAction<String>) () -> System.getProperty(name));
129         if (val == null) {
130             return def;
131         }
132         if (val.equalsIgnoreCase("true")) {
133             return true;
134         } else if (val.equalsIgnoreCase("false")) {
135             return false;
136         } else {
137             throw new IllegalArgumentException
138                 (name + " must be either 'true' or 'false'");
139         }
140     }
141 
concat(byte[] b1, byte[] b2, int n2)142     private byte[] concat(byte[] b1, byte[] b2, int n2) {
143         int n1 = b1.length;
144         if ((n1 == 0) && (n2 == b2.length)) {
145             return b2;
146         }
147         byte[] res = new byte[n1 + n2];
148         System.arraycopy(b1, 0, res, 0, n1);
149         System.arraycopy(b2, 0, res, n1, n2);
150         return res;
151     }
152 
153     private final static byte[] B0 = new byte[0];
154 
doTransmit(byte[] command)155     private byte[] doTransmit(byte[] command) throws CardException {
156         // note that we modify the 'command' array in some cases, so it must
157         // be a copy of the application provided data.
158         try {
159             checkManageChannel(command);
160             setChannel(command);
161             int n = command.length;
162             boolean t0 = card.protocol == SCARD_PROTOCOL_T0;
163             boolean t1 = card.protocol == SCARD_PROTOCOL_T1;
164             if (t0 && (n >= 7) && (command[4] == 0)) {
165                 throw new CardException
166                         ("Extended length forms not supported for T=0");
167             }
168             if ((t0 || (t1 && t1StripLe)) && (n >= 7)) {
169                 int lc = command[4] & 0xff;
170                 if (lc != 0) {
171                     if (n == lc + 6) {
172                         n--;
173                     }
174                 } else {
175                     lc = ((command[5] & 0xff) << 8) | (command[6] & 0xff);
176                     if (n == lc + 9) {
177                         n -= 2;
178                     }
179                 }
180             }
181             boolean getresponse = (t0 && t0GetResponse) || (t1 && t1GetResponse);
182             int k = 0;
183             byte[] result = B0;
184             while (true) {
185                 if (++k >= 32) {
186                     throw new CardException("Could not obtain response");
187                 }
188                 byte[] response = SCardTransmit
189                     (card.cardId, card.protocol, command, 0, n);
190                 int rn = response.length;
191                 if (getresponse && (rn >= 2) && (n >= 1)) {
192                     // see ISO 7816/2005, 5.1.3
193                     if ((rn == 2) && (response[0] == 0x6c)) {
194                         // Resend command using SW2 as short Le field
195                         command[n - 1] = response[1];
196                         continue;
197                     }
198                     if (response[rn - 2] == 0x61) {
199                         // Issue a GET RESPONSE command with the same CLA
200                         // using SW2 as short Le field
201                         if (rn > 2) {
202                             result = concat(result, response, rn - 2);
203                         }
204                         if (command.length < 5) {
205                             byte cla = command[0];
206                             command = new byte[5];
207                             command[0] = cla;
208                         }
209                         command[1] = (byte)0xC0;
210                         command[2] = 0;
211                         command[3] = 0;
212                         command[4] = response[rn - 1];
213                         n = 5;
214                         continue;
215                     }
216                 }
217                 result = concat(result, response, rn);
218                 break;
219             }
220             return result;
221         } catch (PCSCException e) {
222             card.handleError(e);
223             throw new CardException(e);
224         }
225     }
226 
getSW(byte[] res)227     private static int getSW(byte[] res) throws CardException {
228         if (res.length < 2) {
229             throw new CardException("Invalid response length: " + res.length);
230         }
231         int sw1 = res[res.length - 2] & 0xff;
232         int sw2 = res[res.length - 1] & 0xff;
233         return (sw1 << 8) | sw2;
234     }
235 
isOK(byte[] res)236     private static boolean isOK(byte[] res) throws CardException {
237         return (res.length == 2) && (getSW(res) == 0x9000);
238     }
239 
setChannel(byte[] com)240     private void setChannel(byte[] com) {
241         int cla = com[0];
242         if (cla < 0) {
243             // proprietary class format, cannot set or check logical channel
244             // for now, just return
245             return;
246         }
247         // classes 001x xxxx is reserved for future use in ISO, ignore
248         if ((cla & 0xe0) == 0x20) {
249             return;
250         }
251         // see ISO 7816/2005, table 2 and 3
252         if (channel <= 3) {
253             // mask of bits 7, 1, 0 (channel number)
254             // 0xbc == 1011 1100
255             com[0] &= 0xbc;
256             com[0] |= channel;
257         } else if (channel <= 19) {
258             // mask of bits 7, 3, 2, 1, 0 (channel number)
259             // 0xbc == 1011 0000
260             com[0] &= 0xb0;
261             com[0] |= 0x40;
262             com[0] |= (channel - 4);
263         } else {
264             throw new RuntimeException("Unsupported channel number: " + channel);
265         }
266     }
267 
close()268     public void close() throws CardException {
269         if (getChannelNumber() == 0) {
270             throw new IllegalStateException("Cannot close basic logical channel");
271         }
272         if (isClosed) {
273             return;
274         }
275         card.checkExclusive();
276         try {
277             byte[] com = new byte[] {0x00, 0x70, (byte)0x80, 0};
278             com[3] = (byte)getChannelNumber();
279             setChannel(com);
280             byte[] res = SCardTransmit(card.cardId, card.protocol, com, 0, com.length);
281             if (isOK(res) == false) {
282                 throw new CardException("close() failed: " + PCSC.toString(res));
283             }
284         } catch (PCSCException e) {
285             card.handleError(e);
286             throw new CardException("Could not close channel", e);
287         } finally {
288             isClosed = true;
289         }
290     }
291 
toString()292     public String toString() {
293         return "PC/SC channel " + channel;
294     }
295 
296 }
297