001: /*
002: *
003: *
004: * Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: */
026:
027: package com.sun.io.j2me.apdu;
028:
029: import java.io.IOException;
030: import java.io.InterruptedIOException;
031: import javax.microedition.io.ConnectionNotFoundException;
032: import com.sun.cardreader.*;
033: import com.sun.satsa.acl.*;
034:
035: /**
036: * The Cad class maintains the context for the client (terminal) side
037: * of the terminal CAD connection. This class is responsible for sending
038: * APDUs to the card, receiving back the responses and automatically
039: * receiving a response if status '61 XX' is received from the
040: * card.
041: */
042: class Cad {
043:
044: /**
045: * Since at any time once channel always has to be open with the card
046: * and that channel is generally the basic channel. If the basic
047: * channel is not in use, the APDU manager can select the next
048: * application on that channel.
049: * This variable shows if basic channel is in use currently or not.
050: */
051: private boolean basicChannelInUse;
052:
053: /**
054: * Indicates if slot is the SAT slot and a SIM is installed on it.
055: * This flag is set in Cad.selectApplication.
056: * It is cleared when Cad.isAlive detects the death of connection or
057: * Cad.getATR is not able to reset the card.
058: */
059: boolean SIMPresent;
060:
061: /** APDU for logical channel allocation. */
062: private byte[] getChannelAPDU;
063:
064: /** APDU for closing a logical channel. */
065: private byte[] closeChannelAPDU;
066:
067: /** APDU for GET RESPONSE command. */
068: private byte[] getResponseAPDU;
069:
070: /** APDU for ISO7816-4 'case 2' command. Length is 5 bytes. */
071: private byte[] case2APDU;
072:
073: /** FCI information received from selected applications. */
074: private byte[] FCI;
075:
076: /** CardSlot created for Cad. */
077: private CardSlot cardSlot;
078:
079: /** Stuff buffer for received data. */
080: private TempByteArray resp_buffer = new TempByteArray();
081:
082: /** Stuff buffer for output data. */
083: private TempByteArray output_buffer = new TempByteArray();
084:
085: /** Slot number */
086: private int slotN;
087:
088: /**
089: * Constructor for CADClient.
090: * @param slot Global slot number
091: * @throws IOException if there any problems in opening the socket or
092: * streams or powering up the card.
093: */
094: Cad(int slot) throws IOException {
095:
096: try {
097: cardSlot = SlotFactory.getCardSlot(slot);
098: if (cardSlot == null) {
099: throw new IOException(
100: "Slot factory could not create a slot");
101: }
102: } catch (CardDeviceException e) {
103: throw new IOException("Config error: " + e.getMessage());
104: }
105: slotN = slot;
106: getChannelAPDU = new byte[] { 0, 0x70, 0, 0, 1 };
107: closeChannelAPDU = new byte[] { 0, 0x70, (byte) 0x80, 0 };
108: getResponseAPDU = new byte[] { 0, (byte) 0xC0, 0, 0, 0 };
109: case2APDU = new byte[5];
110: }
111:
112: /**
113: * Exchange an Apdu with a CAD.
114: * @param h Handle of the connection.
115: * @param commandAPDU APDU data in byte array form.
116: * @return response APDU data in byte array form.
117: * @exception InterruptedIOException if connection was closed in the
118: * other thread.
119: * @exception IOException if a communication error happens while
120: * communicating with the CAD.
121: */
122: byte[] exchangeApdu(Handle h, byte[] commandAPDU)
123: throws IOException {
124: byte[] result;
125: int result_length = 0;
126:
127: int Lc, Le;
128:
129: Lc = 0;
130: Le = commandAPDU.length == 4 ? -1 : commandAPDU[4] & 0xFF;
131:
132: if (commandAPDU.length > 5) {
133:
134: Lc = Le;
135:
136: if (5 + Lc > commandAPDU.length) {
137: throw new IllegalArgumentException("Malformed APDU");
138: }
139:
140: Le = 5 + Lc == commandAPDU.length ? -1
141: : commandAPDU[5 + Lc] & 0xFF;
142: }
143:
144: if (Le == 0) {
145: Le = 256;
146: }
147: if (Le == -1) {
148: Le = 0;
149: }
150:
151: int cla = commandAPDU[0] & 0xf0;
152: int channel = cla != 0 && (cla < 0x80 || cla > 0xA0) ? 0
153: : commandAPDU[0] & 3;
154:
155: cardSlot.lockSlot();
156: if (Lc == 0 && commandAPDU.length > 5) { // (case 4 & Lc==0) ==> case 2
157: System.arraycopy(commandAPDU, 0, case2APDU, 0, 4);
158: case2APDU[4] = commandAPDU[5];
159: commandAPDU = case2APDU;
160: }
161: while (true) {
162: int bytes_read;
163: int sw1, sw2;
164:
165: bytes_read = cardSlot.xferData(commandAPDU, resp_buffer
166: .ensure(Le + 2));
167: if (bytes_read < 2) {
168: cardSlot.unlockSlot();
169: throw new IOException("Bad response");
170: }
171: if (h != null && !h.opened) {
172: cardSlot.unlockSlot();
173: throw new InterruptedIOException("Connection closed");
174: }
175: sw1 = resp_buffer.val(bytes_read - 2) & 0xFF;
176: sw2 = resp_buffer.val(bytes_read - 1) & 0xFF;
177:
178: // Correct wrong Le
179: /* IMPL_NOTE: We shell make sure that Le!=sw2 but that is out of spec */
180: if (bytes_read == 2 && sw1 == 0x6C && sw2 != 0x00
181: && Le != 0) {
182:
183: Le = sw2;
184: if (commandAPDU.length == 4) { // case 1
185: System.arraycopy(commandAPDU, 0, case2APDU, 0,
186: commandAPDU.length);
187: case2APDU[4] = (byte) (Le & 0xFF);
188: commandAPDU = case2APDU;
189: } else if (commandAPDU.length == 5) { // case 2
190: if (commandAPDU != case2APDU) {
191: System.arraycopy(commandAPDU, 0, case2APDU, 0,
192: 4);
193: commandAPDU = case2APDU;
194: }
195: case2APDU[4] = (byte) (Le & 0xFF);
196: } else {
197: if (commandAPDU.length == 5 + Lc) { // case 3
198: byte[] temp = new byte[5 + Lc + 1];
199: System.arraycopy(commandAPDU, 0, temp, 0,
200: 5 + Lc);
201: commandAPDU = temp;
202: }
203: commandAPDU[5 + Lc] = (byte) (Le & 0xFF);
204: }
205: result_length = 0;
206: continue;
207: }
208: if (result_length >= 2) {
209: result_length -= 2; // Delete previous status bytes
210: }
211: System.arraycopy(resp_buffer.data(), 0, output_buffer
212: .expand(result_length + bytes_read), result_length,
213: bytes_read);
214: result_length += bytes_read;
215:
216: if (Le == 0
217: || (sw1 != 0x61 && (channel != 0 || !SIMPresent || (sw1 != 0x62
218: && sw1 != 0x63 && sw1 != 0x9E && sw1 != 0x9F)))) {
219: break;
220: }
221:
222: Le = sw1 == 0x62 || sw1 == 0x63 ? 0 : sw2;
223:
224: commandAPDU = getResponseAPDU;
225: commandAPDU[0] = (byte) channel;
226: commandAPDU[4] = (byte) Le;
227: if (Le == 0) {
228: Le = 256;
229: }
230: }
231: cardSlot.unlockSlot();
232: result = new byte[result_length];
233: System.arraycopy(output_buffer.data(), 0, result, 0,
234: result_length);
235: return result;
236: }
237:
238: /**
239: * This method returns the ATR received from the card that this
240: * CadClient object is used to communicate with.
241: * @return ATR information received from the card at startup or
242: * reset. In case of I/O troubles returns null.
243: */
244: byte[] getATR() {
245: byte[] result;
246:
247: if (!isAlive()) {
248: result = null;
249: } else {
250: result = cardSlot.getATR();
251: }
252: if (result == null) {
253: SIMPresent = false;
254: }
255: return result;
256: }
257:
258: /**
259: * This method returns the FCI received from the card.
260: * @return FCI information received from the card at
261: * selectApplication.
262: */
263: byte[] getFCI() {
264: return FCI;
265: }
266:
267: /**
268: * Returns the card session identifier.
269: * This number is different for different card sessions.
270: * @return the card session identifier
271: */
272: public int getCardSessionId() {
273: return cardSlot.getCardSessionId();
274: }
275:
276: /**
277: * This method is called when there is a connection creation is
278: * in progress
279: * and specifically card application selection is required.
280: * This method also does the channel management part. If basic channel
281: * is available for use, this method tries to select the target
282: * application
283: * on the basic channel. Otherwise, it requests the card for the channel
284: * and attempts to select the application on that particular channel.
285: *
286: * If the selection is
287: * successful, this method returns the logical channel reserved for
288: * communicating with the selected card application.
289: * @param forSAT true if selection is made for SAT connection
290: * @param selectAPDU byte encoded selection APDU
291: * @exception ConnectionNotFoundException when selection is not successful
292: * @exception IOException if no channel is available for communication
293: * establishment or there are communication problems.
294: * @exception IllegalArgumentException if bad APDU provided.
295: * @return response apdu from the select application request
296: */
297: int selectApplication(boolean forSAT, byte[] selectAPDU)
298: throws IOException {
299:
300: int channel;
301:
302: // Test if 'POWER UP' is needed or a card was changed
303: if (!isAlive()) {
304: try {
305: if (cardSlot.isSAT()) {
306: throw new ConnectionNotFoundException(
307: "SIM not found");
308: } else {
309: throw new ConnectionNotFoundException(
310: "SmartCard not found");
311: }
312: } catch (IOException e) {
313: throw new ConnectionNotFoundException(
314: "SmartCard not found");
315: }
316: }
317:
318: if (cardSlot.initConnection()) {
319: clean();
320: }
321:
322: if (!forSAT) {
323: if (cardSlot.isSAT()) {
324: SIMPresent = true;
325: } else {
326: SIMPresent = false;
327: }
328: if (basicChannelInUse || SIMPresent) {
329: // get another channel for communication.
330: byte[] response = exchangeApdu(null, getChannelAPDU);
331: if (response.length == 2) {
332: // just got back the status word
333: throw new IOException(
334: "No logical channel available");
335: }
336: // new channel number is in the first byte of response
337: channel = response[0];
338: } else {
339: basicChannelInUse = true;
340: channel = 0;
341: }
342: } else {
343: basicChannelInUse = true;
344: channel = 0;
345: SIMPresent = true;
346: }
347:
348: selectAPDU[0] = (byte) ((selectAPDU[0] & 0xFC) | channel);
349:
350: byte[] result = exchangeApdu(null, selectAPDU);
351:
352: int sw1 = result[result.length - 2] & 0xFF;
353: int sw2 = result[result.length - 1] & 0xFF;
354: if ((sw1 << 8) + sw2 != 0x9000) {
355: closeChannel(channel);
356: throw new ConnectionNotFoundException(
357: "Card application selection failed");
358: }
359: FCI = result;
360: return channel;
361: }
362:
363: /**
364: * This method is used to close connection with the card. If the
365: * channel number passed to this method is for basic channel then
366: * the basic channel is marked as available and nothing is
367: * communicated with the card.
368: * If the channel is not the basic channel, a request to close the
369: * channel is sent to the card.
370: * @param channel channel number
371: * @exception IOException if a communication error happens
372: */
373: void closeChannel(int channel) throws IOException {
374:
375: if (channel == 0) {
376: basicChannelInUse = false;
377: return;
378: }
379:
380: try {
381: closeChannelAPDU[3] = (byte) channel;
382: exchangeApdu(null, closeChannelAPDU);
383: } catch (IOException ioException) {
384: throw new IOException("Error closing connection");
385: }
386: }
387:
388: /**
389: * Checks if the the connection is still live or not.
390: *
391: * @return <code>true</code> if the connection is alive
392: */
393: boolean isAlive() {
394: if (cardSlot.isAlive()) {
395: return true;
396: } else {
397: SIMPresent = false;
398: return false;
399: }
400: }
401:
402: /**
403: * Initializes ACL for the slot.
404: */
405: void initACL() {
406: cardSlot.initACL();
407: }
408:
409: /**
410: * Frees up the resource that was used by this slot.
411: * This method is used only in case of error.
412: * IOException is ignored.
413: */
414: void freeSystemResources() {
415: try {
416: cardSlot.closeSlot();
417: } catch (IOException e) {
418: }
419:
420: }
421:
422: /**
423: * Does cleaning of the object.
424: */
425: void clean() {
426: basicChannelInUse = false;
427: SIMPresent = false;
428: FCI = null;
429: }
430:
431: /**
432: * The TempByteArray class contains temporary byte array for
433: * output buffer during APDUExchange.
434: */
435: class TempByteArray {
436: /**
437: * The byte array.
438: */
439: private byte[] value;
440:
441: /**
442: * The constructor.
443: * @param size Initial size.
444: */
445: TempByteArray(int size) {
446: value = new byte[size];
447: }
448:
449: /**
450: * Default constructor.
451: */
452: TempByteArray() {
453: value = new byte[256 + 2];
454: }
455:
456: /**
457: * Gets value of byte by index.
458: * @param i Index.
459: * @return Value for i.
460: */
461: byte val(int i) {
462: return value[i];
463: }
464:
465: /**
466: * Gets all data as byte array.
467: * @return Buffer as byte array.
468: */
469: byte[] data() {
470: return value;
471: }
472:
473: /**
474: * Expands the buffer when it too small to hold newsize bytes.
475: * If buffer contains data they can be lost.
476: * @param newsize Minimum size of buffer.
477: * @return Buffer as byte array.
478: */
479: byte[] ensure(int newsize) {
480: if (value.length < newsize) {
481: value = new byte[newsize];
482: }
483: return value;
484: }
485:
486: /**
487: * Expands the buffer when it too small to hold newsize bytes.
488: * @param newsize Minimum size of buffer.
489: * @return Buffer as byte array.
490: */
491: byte[] expand(int newsize) {
492: if (value.length < newsize) {
493: byte[] temp = new byte[newsize];
494: System.arraycopy(value, 0, temp, 0, value.length);
495: value = temp;
496: }
497: return value;
498: }
499: }
500: }
|