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/CardImpl.java
Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2005, 2010, 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 
00029 import fr.redbilled.pcscforjava.*;
00030 
00031 import static fr.redbilled.security.pcscforjava.PCSC.*;
00032 import static fr.redbilled.security.pcscforjava.PCSCDefines.*;
00033 import java.util.logging.Level;
00034 import java.util.logging.Logger;
00035 
00042 final class CardImpl extends Card {
00043 
00044     private static enum State { OK, REMOVED, DISCONNECTED };
00045 
00046     // the terminal that created this card
00047     private final TerminalImpl terminal;
00048 
00049     // the native SCARDHANDLE
00050     final long cardId;
00051 
00052     // atr of this card
00053     private ATR atr;
00054 
00055     // protocol in use, one of SCARD_PROTOCOL_T0 and SCARD_PROTOCOL_T1
00056     final int protocol;
00057     
00058     // sharing mode in use, one of SCARD_SHARE_SHARED and SCARD_SHARE_DIRECT and
00059     // SCARD_SHARE_EXCLUSIVE
00060     final int sharingMode;
00061 
00062     // the basic logical channel (channel 0)
00063     private final ChannelImpl basicChannel;
00064 
00065     // state of this card connection
00066     private volatile State state;
00067 
00068     // thread holding exclusive access to the card, or null
00069     private volatile Thread exclusiveThread;
00070 
00071     CardImpl(TerminalImpl terminal, String protocol) throws PCSCException {
00072         this.terminal = terminal;
00073         int _iSharingMode = SCARD_SHARE_SHARED;
00074         int connectProtocol;
00075         
00076         if(protocol.equalsIgnoreCase("direct")) 
00077         {
00078             connectProtocol = 0;
00079             _iSharingMode = SCARD_SHARE_DIRECT;
00080         } 
00081         else
00082         {
00083             if(protocol.contains("*")) 
00084                 connectProtocol = SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1;
00085             else if(protocol.toLowerCase().contains("t=0")) 
00086                 connectProtocol = SCARD_PROTOCOL_T0;
00087             else if(protocol.toLowerCase().contains("t=1")) 
00088                 connectProtocol = SCARD_PROTOCOL_T1;
00089             else 
00090                 throw new IllegalArgumentException("Unsupported protocol " +
00091                         protocol);
00092             
00093             if(protocol.toLowerCase().contains("exclusive_"))
00094                 _iSharingMode = SCARD_SHARE_EXCLUSIVE;
00095         }
00096         
00097         cardId = SCardConnect(terminal.contextId, terminal.name,
00098                     _iSharingMode, connectProtocol);
00099 
00100         if(_iSharingMode != SCARD_SHARE_DIRECT)
00101         {
00102             byte[] status = new byte[2];
00103             byte[] atrBytes = SCardStatus(cardId, status);
00104             atr = new ATR(atrBytes, getFrequency());
00105             
00106             // I do not know why but some times SCardStatus does not return
00107             // the correct ATR !!
00108             while((atrBytes[0] != 0x3B) && (atrBytes[0] != 0x3F))
00109             {
00110                 atrBytes = SCardStatus(cardId, status);
00111                 atr = new ATR(atrBytes, getFrequency());
00112             }    
00113             
00114             this.protocol = status[1] & 0xff;
00115         }
00116         else
00117         {
00118             byte[] atrBytes = new byte[17];
00119             atrBytes[0] = 0x3B;
00120             atrBytes[1] = 0x0F;
00121             atrBytes[2] = 0x44;
00122             atrBytes[3] = 0x49;
00123             atrBytes[4] = 0x52;
00124             atrBytes[5] = 0x45;
00125             atrBytes[6] = 0x43;
00126             atrBytes[7] = 0x54;
00127             atrBytes[8] = 0x5F;
00128             atrBytes[9] = 0x41;
00129             atrBytes[10] = 0x54;
00130             atrBytes[11] = 0x52;
00131             atrBytes[12] = 0x5F;
00132             atrBytes[13] = 0x46;
00133             atrBytes[14] = 0x41;
00134             atrBytes[15] = 0x4B;
00135             atrBytes[16] = 0x45;
00136             
00137             atr = new ATR(atrBytes, 4000000);
00138             this.protocol = 0;
00139         }
00140 
00141         this.sharingMode = _iSharingMode;
00142         
00143         basicChannel = new ChannelImpl(this, 0);
00144         state = State.OK;
00145     }
00146 
00147     void checkState()  {
00148         State s = state;
00149         if (s == State.DISCONNECTED) {
00150             throw new IllegalStateException("Card has been disconnected");
00151         } else if (s == State.REMOVED) {
00152             throw new IllegalStateException("Card has been removed");
00153         }
00154     }
00155 
00156     boolean isValid() {
00157         if (state != State.OK) {
00158             return false;
00159         }
00160         // ping card via SCardStatus
00161         try {
00162             SCardStatus(cardId, new byte[2]);
00163             return true;
00164         } catch (PCSCException e) {
00165             state = State.REMOVED;
00166             return false;
00167         }
00168     }
00169 
00170     private void checkSecurity(String action) {
00171         SecurityManager sm = System.getSecurityManager();
00172         if (sm != null) {
00173             sm.checkPermission(new CardPermission(terminal.name, action));
00174         }
00175     }
00176 
00177     void handleError(PCSCException e) {
00178         if (e.code == SCARD_W_REMOVED_CARD) {
00179             state = State.REMOVED;
00180         }
00181     }
00182 
00183     public ATR getATR() {
00184         return atr;
00185     }
00186 
00187     public String getProtocol() {
00188         switch (protocol) {
00189         case SCARD_PROTOCOL_T0:
00190             return "T=0";
00191         case SCARD_PROTOCOL_T1:
00192             return "T=1";
00193         default:
00194             // should never occur
00195             return "Unknown protocol " + protocol;
00196         }
00197     }
00198     
00199     public int getSharingMode() {
00200         return this.sharingMode;
00201     }
00202             
00203 
00204     public CardChannel getBasicChannel() {
00205         checkSecurity("getBasicChannel");
00206         checkState();
00207         return basicChannel;
00208     }
00209 
00210     private static int getSW(byte[] b) {
00211         if (b.length < 2) {
00212             return -1;
00213         }
00214         int sw1 = b[b.length - 2] & 0xff;
00215         int sw2 = b[b.length - 1] & 0xff;
00216         return (sw1 << 8) | sw2;
00217     }
00218 
00219     private static byte[] commandOpenChannel = new byte[] {0, 0x70, 0, 0, 1};
00220 
00221     public CardChannel openLogicalChannel() throws CardException {
00222         checkSecurity("openLogicalChannel");
00223         checkState();
00224         checkExclusive();
00225         try {
00226             byte[] response = SCardTransmit
00227                 (cardId, protocol, commandOpenChannel, 0,
00228                 commandOpenChannel.length);
00229             if ((response.length != 3) || (getSW(response) != 0x9000)) {
00230                 throw new CardException
00231                         ("openLogicalChannel() failed, card response: "
00232                         + PCSC.toString(response));
00233             }
00234             return new ChannelImpl(this, response[0]);
00235         } catch (PCSCException e) {
00236             handleError(e);
00237             throw new CardException("openLogicalChannel() failed", e);
00238         }
00239     }
00240 
00241     void checkExclusive() throws CardException {
00242         Thread t = exclusiveThread;
00243         if (t == null) {
00244             return;
00245         }
00246         if (t != Thread.currentThread()) {
00247             throw new CardException("Exclusive access established by another "
00248                     + "Thread");
00249         }
00250     }
00251 
00252     public synchronized void beginExclusive() throws CardException {
00253         checkSecurity("exclusive");
00254         checkState();
00255         if (exclusiveThread != null) {
00256             throw new CardException
00257                     ("Exclusive access has already been assigned to Thread "
00258                     + exclusiveThread.getName());
00259         }
00260         try {
00261             SCardBeginTransaction(cardId);
00262         } catch (PCSCException e) {
00263             handleError(e);
00264             throw new CardException("beginExclusive() failed", e);
00265         }
00266         exclusiveThread = Thread.currentThread();
00267     }
00268 
00269     public synchronized void endExclusive() throws CardException {
00270         checkState();
00271         if (exclusiveThread != Thread.currentThread()) {
00272             throw new IllegalStateException
00273                     ("Exclusive access not assigned to current Thread");
00274         }
00275         try {
00276             SCardEndTransaction(cardId, SCARD_LEAVE_CARD);
00277         } catch (PCSCException e) {
00278             handleError(e);
00279             throw new CardException("endExclusive() failed", e);
00280         } finally {
00281             exclusiveThread = null;
00282         }
00283     }
00284 
00285     @Override
00286     public byte[] transmitControlCommand(int controlCode, byte[] command)
00287             throws CardException {
00288         checkSecurity("transmitControl");
00289         checkState();
00290         checkExclusive();
00291         if (command == null) {
00292             throw new NullPointerException();
00293         }
00294         try {
00295             byte[] r = SCardControl(cardId, controlCode, command);
00296             
00297             if(r == null)
00298                 return new byte[0];
00299 
00300             return r;
00301         } catch (PCSCException e) {
00302             handleError(e);
00303             throw new CardException("transmitControlCommand() failed", e);
00304         }
00305     }
00306 
00307     @Override
00308     public void disconnect(int iDisposition) throws CardException {
00309         checkSecurity("disconnect");
00310 
00311         if (state != State.OK)
00312             return;
00313 
00314         switch(iDisposition)
00315         {
00316             case SCARD_LEAVE_CARD:
00317             case SCARD_RESET_CARD:
00318             case SCARD_UNPOWER_CARD:
00319             case SCARD_EJECT_CARD:
00320                 break;
00321 
00322             default:
00323                 throw new IllegalArgumentException("Unsupported disposition "
00324                         + iDisposition);
00325         }
00326 
00327         checkExclusive();
00328         try {
00329             SCardDisconnect(cardId, iDisposition);
00330         } catch (PCSCException e) {
00331             throw new CardException("disconnect() failed", e);
00332         } finally {
00333             state = State.DISCONNECTED;
00334             exclusiveThread = null;
00335             this.terminal.notifyDisconnection();
00336         }
00337     }
00338 
00339     @Override
00340     public byte[] getAttrib(int iAttribute) throws CardException {
00341         checkSecurity("getAttrib");
00342         checkState();
00343         checkExclusive();
00344 
00345         try {
00346             return SCardGetAttrib(cardId, iAttribute);
00347         } catch (PCSCException ex) {
00348             throw new CardException(ex.toString());
00349         }
00350     }
00351 
00352     @Override
00353     public void setAttrib(int iAttribute, byte[] pBCommand)
00354             throws CardException {
00355         checkSecurity("setAttrib");
00356 
00357         if(iAttribute != SCARD_ATTR_SUPRESS_T1_IFS_REQUEST)
00358         {
00359             throw new IllegalArgumentException("Unsupported attribute "
00360                         + iAttribute);
00361         }
00362 
00363         checkState();
00364         checkExclusive();
00365 
00366         try {
00367             SCardSetAttrib(cardId, iAttribute, pBCommand);
00368         } catch (PCSCException ex) {
00369             throw new CardException(ex.toString());
00370         }
00371     }
00372 
00373     public void reconnect(int iShareMode, int iInitialization)
00374             throws CardException {
00375         byte[] _atr;
00376 
00377         checkSecurity("reconnect");
00378 
00379         if(state != State.OK)
00380             throw new CardException(new PCSCException
00381                     (SCARD_W_UNPOWERED_CARD).toString());
00382 
00383         switch(iShareMode)
00384         {
00385             case SCARD_SHARE_SHARED:
00386             case SCARD_SHARE_EXCLUSIVE:
00387                 break;
00388 
00389             default:
00390                 throw new IllegalArgumentException("Unsupported share mode "
00391                         + iShareMode);
00392         }
00393 
00394         switch(iInitialization)
00395         {
00396             case SCARD_LEAVE_CARD:
00397             case SCARD_RESET_CARD:
00398             case SCARD_UNPOWER_CARD:
00399                 break;
00400 
00401             default:
00402                 throw new IllegalArgumentException("Unsupported initialization "
00403                         + iInitialization);
00404         }
00405 
00406         checkExclusive();
00407 
00408         try {
00409             _atr = SCardReconnect(cardId, iShareMode, protocol, iInitialization);
00410         } catch (PCSCException ex) {
00411             throw new CardException(ex.toString());
00412         }
00413 
00414         if((_atr != null) && (_atr.length > 0))
00415         {
00416             atr = null;
00417             try {
00418                 atr = new ATR(_atr, getFrequency());
00419             } catch (PCSCException ex) {
00420                 throw new CardException(ex.toString());
00421             }
00422         }
00423     }
00424     
00425     public int getFrequency() throws PCSCException
00426     {
00427         byte[]  _clockCard;
00428         int     _iClockCard = 0;
00429         int     _i = 0, _j = 0; 
00430         
00431         // Get the clock card frequency in KHz
00432         _clockCard = SCardGetAttrib(cardId, SCARD_ATTR_CURRENT_CLK);
00433 
00434         _i = 0;
00435 
00436         while(_i < _clockCard.length)
00437         {
00438             int _iTmp = PlatformPCSC.unsignedByteToInt(_clockCard[_i]);
00439             _iClockCard += _iTmp << (_j * 8);
00440             _i++;
00441             _j++;
00442         }
00443         
00444         // Return in Hz
00445         return (_iClockCard * 1000);
00446     }
00447 
00448     @Override
00449     public String toString() {
00450         return "PC/SC card in " + terminal.getName()
00451             + ", protocol " + getProtocol() + ", state " + state;
00452     }
00453 
00454     @Override
00455     protected void finalize() throws Throwable {
00456         try {
00457             if (state == State.OK) {
00458                 SCardDisconnect(cardId, SCARD_LEAVE_CARD);
00459             }
00460         } finally {
00461             super.finalize();
00462         }
00463     }
00464 
00465 }