PCSC4Java  0.2
Library PCSC for Java language.
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
D:/Affaire/Perso/SmartCardToolBox/pcsc4java-framework-0.2/src/fr/redbilled/security/pcscforjava/ChannelImpl.java
Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved.
00003  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
00004  *
00005  * This code is free software; you can redistribute it and/or modify it
00006  * under the terms of the GNU General Public License version 2 only, as
00007  * published by the Free Software Foundation.  Oracle designates this
00008  * particular file as subject to the "Classpath" exception as provided
00009  * by Oracle in the LICENSE file that accompanied this code.
00010  *
00011  * This code is distributed in the hope that it will be useful, but WITHOUT
00012  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00013  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
00014  * version 2 for more details (a copy is included in the LICENSE file that
00015  * accompanied this code).
00016  *
00017  * You should have received a copy of the GNU General Public License version
00018  * 2 along with this work; if not, write to the Free Software Foundation,
00019  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
00020  *
00021  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
00022  * or visit www.oracle.com if you need additional information or have any
00023  * questions.
00024  */
00025 
00026 package fr.redbilled.security.pcscforjava;
00027 
00028 import java.nio.*;
00029 import java.security.AccessController;
00030 
00031 import fr.redbilled.pcscforjava.*;
00032 
00033 import static fr.redbilled.security.pcscforjava.PCSC.*;
00034 
00035 import sun.security.action.GetPropertyAction;
00036 
00043 final class ChannelImpl extends CardChannel {
00044 
00045     // the card this channel is associated with
00046     private final CardImpl card;
00047 
00048     // the channel number, 0 for the basic logical channel
00049     private final int channel;
00050 
00051     // whether this channel has been closed. only logical channels can be closed
00052     private volatile boolean isClosed;
00053 
00054     ChannelImpl(CardImpl card, int channel) {
00055         this.card = card;
00056         this.channel = channel;
00057     }
00058 
00059     void checkClosed() {
00060         card.checkState();
00061         if (isClosed) {
00062             throw new IllegalStateException("Logical channel has been closed");
00063         }
00064     }
00065 
00066     public Card getCard() {
00067         return card;
00068     }
00069 
00070     public int getChannelNumber() {
00071         checkClosed();
00072         return channel;
00073     }
00074 
00075     private static void checkManageChannel(byte[] b) {
00076         if (b.length < 4) {
00077             throw new IllegalArgumentException
00078                 ("Command APDU must be at least 4 bytes long");
00079         }
00080         if ((b[0] >= 0) && (b[1] == 0x70)) {
00081             throw new IllegalArgumentException
00082                 ("Manage channel command not allowed, use openLogicalChannel()");
00083         }
00084     }
00085 
00086     public ResponseAPDU transmit(CommandAPDU command, boolean bAutoGetResp,
00087             boolean bAutoReissue) throws CardException {
00088         checkClosed();
00089         card.checkExclusive();
00090         byte[] commandBytes = command.getBytes();
00091         byte[] responseBytes = doTransmit(commandBytes, bAutoGetResp,
00092                 bAutoReissue);
00093         return new ResponseAPDU(responseBytes);
00094     }
00095 
00096     public int transmit(ByteBuffer command, boolean bAutoGetResp,
00097             boolean bAutoReissue, ByteBuffer response) throws CardException {
00098         checkClosed();
00099         card.checkExclusive();
00100         if ((command == null) || (response == null)) {
00101             throw new NullPointerException();
00102         }
00103         if (response.isReadOnly()) {
00104             throw new ReadOnlyBufferException();
00105         }
00106         if (command == response) {
00107             throw new IllegalArgumentException
00108                     ("command and response must not be the same object");
00109         }
00110         if (response.remaining() < 258) {
00111             throw new IllegalArgumentException
00112                     ("Insufficient space in response buffer");
00113         }
00114         byte[] commandBytes = new byte[command.remaining()];
00115         command.get(commandBytes);
00116         byte[] responseBytes = doTransmit(commandBytes, bAutoGetResp,
00117                 bAutoReissue);
00118         response.put(responseBytes);
00119         return responseBytes.length;
00120     }
00121 
00122     private final static boolean t0GetResponse =
00123         getBooleanProperty("fr.redbilled.security.pcscforjava.t0GetResponse",
00124         true);
00125 
00126     private final static boolean t1GetResponse =
00127         getBooleanProperty("fr.redbilled.security.pcscforjava.t1GetResponse",
00128         true);
00129 
00130     private final static boolean t1StripLe =
00131         getBooleanProperty("fr.redbilled.security.pcscforjava.t1StripLe",
00132         false);
00133 
00134     private static boolean getBooleanProperty(String name, boolean def) {
00135         String val = AccessController.doPrivileged(new GetPropertyAction(name));
00136         if (val == null) {
00137             return def;
00138         }
00139         if (val.equalsIgnoreCase("true")) {
00140             return true;
00141         } else if (val.equalsIgnoreCase("false")) {
00142             return false;
00143         } else {
00144             throw new IllegalArgumentException
00145                 (name + " must be either 'true' or 'false'");
00146         }
00147     }
00148 
00149     private byte[] concat(byte[] b1, byte[] b2, int n2) {
00150         int n1 = b1.length;
00151         if ((n1 == 0) && (n2 == b2.length)) {
00152             return b2;
00153         }
00154         byte[] res = new byte[n1 + n2];
00155         System.arraycopy(b1, 0, res, 0, n1);
00156         System.arraycopy(b2, 0, res, n1, n2);
00157         return res;
00158     }
00159 
00160     private final static byte[] B0 = new byte[0];
00161 
00162     private synchronized byte[] doTransmit(byte[] command, boolean bAutoGetResp,
00163             boolean bAutoReissue) throws CardException {
00164         // note that we modify the 'command' array in some cases, so it must
00165         // be a copy of the application provided data.
00166         try {
00167             checkManageChannel(command);
00168             setChannel(command);
00169             int n = command.length;
00170             boolean t0 = card.protocol == SCARD_PROTOCOL_T0;
00171             boolean t1 = card.protocol == SCARD_PROTOCOL_T1;
00172             if (t0 && (n >= 7) && (command[4] == 0)) {
00173                 throw new CardException
00174                         ("Extended length forms not supported for T=0");
00175             }
00176             if ((t0 || (t1 && t1StripLe)) && (n >= 7)) {
00177                 int lc = command[4] & 0xff;
00178                 if (lc != 0) {
00179                     if (n == lc + 6) {
00180                         n--;
00181                     }
00182                 } else {
00183                     lc = ((command[5] & 0xff) << 8) | (command[6] & 0xff);
00184                     if (n == lc + 9) {
00185                         n -= 2;
00186                     }
00187                 }
00188             }
00189             boolean getresponse = (t0 && t0GetResponse) || (t1 && t1GetResponse);
00190             int k = 0;
00191             byte[] result = B0;
00192             while (true) {
00193                 if (++k >= 32) {
00194                     throw new CardException("Could not obtain response");
00195                 }
00196                 byte[] response = SCardTransmit
00197                     (card.cardId, card.protocol, command, 0, n);
00198                 int rn = response.length;
00199                 if (getresponse && (rn >= 2)) {
00200                     if(bAutoReissue) {
00201                         // see ISO 7816/2005, 5.1.3
00202                         if ((rn == 2) && (response[0] == 0x6c)) {
00203                             // Resend command using SW2 as short Le field
00204                             command[n - 1] = response[1];
00205                             continue;
00206                         }
00207                     }
00208 
00209                     if(bAutoGetResp) {
00210                         if (response[rn - 2] == 0x61) {
00211                             // Issue a GET RESPONSE command 
00212                             // using SW2 as short Le field
00213                             if (rn > 2) {
00214                                 result = concat(result, response, rn - 2);
00215                             }
00216                             
00217                             command[0] = 0x00;
00218                             command[1] = (byte)0xC0;
00219                             command[2] = 0;
00220                             command[3] = 0;
00221                             command[4] = response[rn - 1];
00222                             n = 5;
00223                             continue;
00224                         }
00225                     }
00226                 }
00227                 result = concat(result, response, rn);
00228                 break;
00229             }
00230             return result;
00231         } catch (PCSCException e) {
00232             card.handleError(e);
00233             throw new CardException(e);
00234         }
00235     }
00236 
00237     private static int getSW(byte[] res) throws CardException {
00238         if (res.length < 2) {
00239             throw new CardException("Invalid response length: " + res.length);
00240         }
00241         int sw1 = res[res.length - 2] & 0xff;
00242         int sw2 = res[res.length - 1] & 0xff;
00243         return (sw1 << 8) | sw2;
00244     }
00245 
00246     private static boolean isOK(byte[] res) throws CardException {
00247         return (res.length == 2) && (getSW(res) == 0x9000);
00248     }
00249 
00250     private void setChannel(byte[] com) {
00251         int cla = com[0];
00252         if (cla < 0) {
00253             // proprietary class format, cannot set or check logical channel
00254             // for now, just return
00255             return;
00256         }
00257         // classes 001x xxxx is reserved for future use in ISO, ignore
00258         /*if ((cla & 0xe0) == 0x20) {
00259             return;
00260         }*/
00261         // see ISO 7816/2005, table 2 and 3
00262         if (channel <= 3) {
00263             // mask of bits 7, 1, 0 (channel number)
00264             // 0xbc == 1011 1100
00265             com[0] &= 0xbc;
00266             com[0] |= channel;
00267         } else if (channel <= 19) {
00268             // mask of bits 7, 3, 2, 1, 0 (channel number)
00269             // 0xbc == 1011 0000
00270             com[0] &= 0xb0;
00271             com[0] |= 0x40;
00272             com[0] |= (channel - 4);
00273         } else {
00274             throw new RuntimeException("Unsupported channel number: " + channel);
00275         }
00276     }
00277 
00278     public void close() throws CardException {
00279         if (getChannelNumber() == 0) {
00280             throw new IllegalStateException("Cannot close basic logical channel");
00281         }
00282         if (isClosed) {
00283             return;
00284         }
00285         card.checkExclusive();
00286         try {
00287             byte[] com = new byte[] {0x00, 0x70, (byte)0x80, 0};
00288             com[3] = (byte)getChannelNumber();
00289             setChannel(com);
00290             byte[] res = SCardTransmit(card.cardId, card.protocol, com, 0, com.length);
00291             if (isOK(res) == false) {
00292                 throw new CardException("close() failed: " + PCSC.toString(res));
00293             }
00294         } catch (PCSCException e) {
00295             card.handleError(e);
00296             throw new CardException("Could not close channel", e);
00297         } finally {
00298             isClosed = true;
00299         }
00300     }
00301 
00302     public String toString() {
00303         return "PC/SC channel " + channel;
00304     }
00305 
00306 }