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 com.sun.j2me.app.AppPackage;
030: import com.sun.j2me.security.SatsaPermission;
031: import javax.microedition.io.*;
032: import javax.microedition.apdu.*;
033: import com.sun.j2me.io.ConnectionBaseInterface;
034: import com.sun.satsa.acl.ACLPermissions;
035: import com.sun.satsa.acl.AccessControlManager;
036: import com.sun.satsa.acl.APDUPermissions;
037: import com.sun.satsa.util.Utils;
038:
039: import java.io.*;
040:
041: import com.sun.j2me.security.TrustedClass;
042: import com.sun.j2me.security.Token;
043: import com.sun.satsa.security.SecurityInitializer;
044:
045: /**
046: * This is the implementation class for APDUConnection interface and provides
047: * a high-level API to the J2ME applications allowing them to connect and
048: * communicate with the card applications. An instance of this class is
049: * created when Connector.open method is called with 'apdu' as protocol. An
050: * instance of this class is only returned to the calling J2ME application if
051: * the card application selection is successful. If there are any errors that
052: * occur during the card application selection, IOException is thrown.
053: * The application calls <tt>Connector.open</tt> with an APDU URL string and
054: * obtains a {@link javax.microedition.apdu.APDUConnection} object.
055: *
056: */
057: public class Protocol implements APDUConnection,
058: ConnectionBaseInterface, StreamConnection {
059:
060: /*
061: * Inner class to request security token from SecurityTokenInitializer.
062: * SecurityTokenInitializer should be able to check this inner class name.
063: */
064: static private class SecurityTrusted implements TrustedClass {
065: };
066:
067: /** This class has a different security domain than the App suite */
068: private static Token securityToken = SecurityInitializer
069: .requestToken(new SecurityTrusted());
070:
071: /**
072: * This object verifies access rights of the MIDlet.
073: */
074: private APDUPermissions verifier;
075:
076: /**
077: * This flag is to indicate if this APDU connection is to be used for
078: * communicating with SAT.
079: */
080: private boolean openForSAT;
081:
082: /**
083: * Connection handle.
084: */
085: private Handle h;
086:
087: /**
088: * Opens a connection.
089: *
090: * @param name the target of the connection
091: * @param mode indicates whether the caller
092: * intends to write to the connection. Currently,
093: * this parameter is ignored.
094: * @param timeouts indicates whether the caller
095: * wants timeout exceptions. Currently,
096: * this parameter is ignored.
097: * @return this connection
098: * @throws IOException if the connection is closed or unavailable
099: * @throws SecurityException if access is restricted by ACL
100: */
101: public Connection openPrim(String name, int mode, boolean timeouts)
102: throws IOException {
103:
104: // parse the URI for slot number and target
105: int slotIndex = name.indexOf(":");
106: int targetIndex = name.indexOf(";target=");
107:
108: if (targetIndex < 0) {
109: throw new IllegalArgumentException(
110: "Target missing in connection URL");
111: }
112:
113: int slot;
114:
115: if (targetIndex == slotIndex + 1) {
116: slot = 0;
117: } else {
118: try {
119: slot = Integer.parseInt(name.substring(slotIndex + 1,
120: targetIndex), 16);
121: } catch (NumberFormatException e) {
122: throw new IllegalArgumentException(
123: "Invalid slot number");
124: }
125: }
126:
127: String target = name.substring(targetIndex + 8);
128:
129: boolean isSAT = target.equals("SAT");
130:
131: AppPackage appPackage = AppPackage.getInstance();
132: try {
133: if (isSAT) {
134: appPackage
135: .checkForPermission(SatsaPermission.APDU_CHANNEL0_SAT_OPEN);
136: } else {
137: appPackage
138: .checkForPermission(SatsaPermission.APDU_CONNECTION_OPEN);
139: }
140: } catch (InterruptedException ie) {
141: throw new InterruptedIOException(
142: "Interrupted while trying to ask the user permission");
143: }
144:
145: // open connection
146:
147: if (isSAT) {
148: boolean satSlot;
149: try {
150: APDUManager.initACL(slot);
151: satSlot = APDUManager.isSatSlot(slot);
152: } catch (IllegalArgumentException e) {
153: satSlot = false;
154: }
155: if (!satSlot) {
156: throw new ConnectionNotFoundException(
157: "Invalid slot for SIM");
158: }
159: h = APDUManager.openSATConnection(slot);
160: openForSAT = true;
161: } else {
162:
163: APDUManager.checkSlotNumber(slot);
164:
165: byte[] apdu = new byte[32];
166: apdu[1] = (byte) 0xa4;
167: apdu[2] = 4;
168:
169: boolean ok;
170: try {
171: int len = APDUManager.parseDottedBytes(target, apdu, 5);
172: apdu[4] = (byte) len;
173: apdu[5 + len] = 127;
174: ok = len > 4 && len < 17;
175: } catch (NullPointerException npe) {
176: ok = false;
177: } catch (IndexOutOfBoundsException iobe) {
178: ok = false;
179: } catch (NumberFormatException nfe) {
180: ok = false;
181: } catch (IllegalArgumentException iae) {
182: ok = false;
183: }
184:
185: if (!ok) {
186: throw new IllegalArgumentException("Invalid AID");
187: }
188:
189: APDUManager.initACL(slot);
190: verifier = AccessControlManager.getAPDUPermissions(slot,
191: apdu, appPackage.getCA());
192:
193: h = APDUManager.selectApplication(apdu, slot);
194: }
195: return this ;
196: }
197:
198: /**
199: * Closes the connection.
200: * @exception IOException if an I/O error occurs
201: */
202: public void close() throws IOException {
203: /*
204: * IMPL_NOTE: To pass JDTS test
205: * com.sun.satsa.apdu.exchange.interruptedIOClosed next lines
206: * should be uncommented
207: */
208: // try {Thread.sleep(1000); }
209: // catch (InterruptedException ignored) {}
210: if (h != null) {
211: Handle w = h;
212: h = null;
213: APDUManager.closeConnection(w);
214: }
215: }
216:
217: /**
218: * Exchanges an APDU command with a smart card application.
219: * Communication to a smart card device is synchronous.
220: * This method will block until the response has been received
221: * from the smart card application, or is interrupted.
222: * The interruption could be due to the card being removed from
223: * the card access device, the operation may timeout, or the
224: * connection may be closed from another thread accessing this
225: * connection.
226: *
227: * @param commandAPDU a byte encoded command for the smart card
228: * application
229: * @return a byte encoded response to the requested operation
230: * @exception IOException is thrown if the operation was not
231: * successful, or if the connection was already closed
232: * @throws InterruptedIOException if a timeout occurs while
233: * either trying to send the command or if this <code>Connection</code>
234: * object is closed during this exchange operation
235: * @throws NullPointerException if the parameter is null
236: * @throws SecurityException if the application does not
237: * have permission to exchange the message
238: */
239: public byte[] exchangeAPDU(byte[] commandAPDU) throws IOException,
240: InterruptedIOException {
241:
242: checkHandle(h);
243: if (commandAPDU == null || commandAPDU.length < 4) {
244: throw new IllegalArgumentException();
245: }
246:
247: int nibble = commandAPDU[0] & 0xF0;
248: boolean channelEncoded = (nibble == 0 || (nibble >= 0x80 && nibble <= 0xA0));
249:
250: if (channelEncoded) {
251: // mask off the channel information
252: commandAPDU[0] &= 0xFC;
253: }
254:
255: int command = Utils.getInt(commandAPDU, 0);
256:
257: if (openForSAT) {
258: // check if this is an envelope by checking the INS, P1 and
259: // P2 which should have values 0xC2, 0 and 0 respectively.
260: if ((command & 0xffffff) != 0xC20000) {
261: throw new IllegalArgumentException("Non-envelope APDU");
262: }
263: commandAPDU[0] = (byte) 0x80;
264: } else {
265:
266: int cmd = command >> 8;
267: if (cmd == 0xA404 || cmd == 0x7000 || cmd == 0x7080) {
268: throw new IllegalArgumentException(
269: "Selection or channel management APDUs are not allowed");
270: }
271:
272: // if channel is non-zero, valid CLA bytes are only:
273: // 0x0X, 0x8X, 0x9X or 0xAX i.e. the one that can have
274: // the channel information encoded in it
275: if (h.channel != 0 && !channelEncoded) {
276: throw new IllegalArgumentException(
277: "Invalid CLA byte for a non-zero channel");
278: }
279:
280: // check if allowed by the ACL
281: verifier.checkPermission(command);
282:
283: if (channelEncoded) {
284: // set channel bits in two LSB
285: commandAPDU[0] |= h.channel;
286: }
287: }
288: return APDUManager.exchangeAPDU(h, commandAPDU);
289: }
290:
291: /**
292: * Returns the ATR message sent by smart card in response to the
293: * reset operation.
294: * @return the ATR response message, or <code>null</code>
295: * if there is no message available
296: */
297: public byte[] getATR() {
298: byte[] result = null;
299: try {
300: checkHandle(h);
301: result = h.getATR();
302: } catch (IOException e) {
303: result = null;
304: }
305: return result;
306: }
307:
308: /**
309: * This method always throw <code>IllegalArgumentException</code>.
310: * @return An input stream
311: * @exception IOException If an I/O error occurs
312: * @exception IllegalArgumentException is thrown for all requests
313: */
314: public InputStream openInputStream() throws IOException {
315: throw new IllegalArgumentException("Not supported");
316: }
317:
318: /**
319: * This method always throw <code>IllegalArgumentException</code>.
320: * @return An input stream
321: * @exception IOException If an I/O error occurs
322: * @exception IllegalArgumentException is thrown for all requests
323: */
324: public DataInputStream openDataInputStream() throws IOException {
325: throw new IllegalArgumentException("Not supported");
326: }
327:
328: /**
329: * This method always throw
330: * <code>IllegalArgumentException</code>.
331: * @return An output stream
332: * @exception IOException If an I/O error occurs
333: * @exception IllegalArgumentException is thrown for all requests
334: */
335: public OutputStream openOutputStream() throws IOException {
336: throw new IllegalArgumentException("Not supported");
337: }
338:
339: /**
340: * This method always throw
341: * <code>IllegalArgumentException</code>.
342: *
343: * @return An output stream
344: * @exception IOException If an I/O error occurs
345: * @exception IllegalArgumentException is thrown for all requests
346: */
347: public DataOutputStream openDataOutputStream() throws IOException {
348: throw new IllegalArgumentException("Not supported");
349: }
350:
351: /**
352: * A call to enterPin method pops up a UI that requests the PIN
353: * from the user. The pinID field indicates which PIN must be
354: * requested from the user. The user can
355: * either cancel the request
356: * or continue. If the user enters the PIN and chooses to continue the
357: * implementation is responsible
358: * for presenting the PIN value to the card for verification.
359: * @param pinID the type of PIN the implementation is suppose to prompt
360: * the user to enter.
361: * @return result of PIN verification which is the status word
362: * recived from the smart card in the form of a byte array. This method
363: * would return null if the user cancels the request.
364: * @exception IOException is thrown if the PIN could not be communicated
365: * with the card due to IO problems such as if the connection was
366: * closed before the command could be completed successfully.
367: * @exception InterruptedIOException is thrown if the connection object
368: * is closed before a reply from the card is received.
369: * @exception SecurityException is thrown if the J2ME application does
370: * not have appropriate rights to ask for PIN verification.
371: */
372: public byte[] enterPin(int pinID) throws IOException {
373: return doEnterPin(pinID, 0, ACLPermissions.CMD_VERIFY);
374: }
375:
376: /**
377: * A call to <code>changePin</code> method pops up a UI that requests the
378: * the user for an old or existing PIN value and the new PIN value
379: * to change the value of the PIN. The pinID field indicates which PIN is
380: * to be changed. The user can
381: * either cancel the request
382: * or continue. If the user enters the PIN values and chooses to
383: * continue the
384: * implementation is responsible
385: * for presenting the PIN value to the card to the card.
386: * @param pinID the type of PIN the implementation is suppose to prompt
387: * the user to change.
388: * @return result of changing the PIN value which is the status word
389: * recived from the smart card in the form of a byte array. This method
390: * would return null if the user cancels the request.
391: * @exception IOException is thrown if the PIN could not be communicated
392: * with the card due to IO problems such as if the connection was
393: * closed before the command could be completed successfully.
394: * @exception InterruptedIOException is thrown if the connection object
395: * is closed before a reply from the card is received.
396: * @exception SecurityException is thrown if the J2ME application does
397: * not have appropriate rights to ask for changing the PIN value.
398: */
399: public byte[] changePin(int pinID) throws IOException {
400: return doEnterPin(pinID, 0, ACLPermissions.CMD_CHANGE);
401: }
402:
403: /**
404: * A call to <code>disablePin</code> method pops up a UI that requests the
405: * the user to enter the value for the PIN that is to be disabled.
406: * The pinID field
407: * indicates which PIN is to be disabled. The user can
408: * either cancel the request
409: * or continue. If the user enters the PIN and chooses to continue the
410: * implementation is responsible
411: * for presenting the PIN value to the card to disable PIN.
412: * @param pinID the type of PIN the implementation is required to prompt
413: * the user to enter.
414: * @return result of disabling the PIN value which is the status word
415: * recived from the smart card in the form of a byte array. This method
416: * would return null if the user cancels the request.
417: * @exception IOException is thrown if the PIN could not be communicated
418: * with the card due to IO problems such as if the connection was
419: * closed before the command could be completed successfully.
420: * @exception InterruptedIOException is thrown if the connection object
421: * is closed before a reply from the card is received.
422: * @exception SecurityException is thrown if the J2ME application does
423: * not have appropriate rights to ask for disabling the PIN.
424: */
425: public byte[] disablePin(int pinID) throws IOException {
426: return doEnterPin(pinID, 0, ACLPermissions.CMD_DISABLE);
427: }
428:
429: /**
430: * A call to <code>enablePin</code> method pops up a UI that requests the
431: * the user to enter the value for the PIN that is to be enabled.
432: * The pinID field
433: * indicates which PIN is to be enabled. The user can
434: * either cancel the request
435: * or continue. If the user enters the PIN and chooses to continue the
436: * implementation is responsible
437: * for presenting the PIN value to the card for enabling the PIN.
438: * @param pinID the type of PIN the implementation is required to prompt
439: * the user to enter.
440: * @return result of enabling the PIN value which is the status word
441: * recived from the smart card in the form of a byte array. This method
442: * would return null if the user cancels the request.
443: * @exception IOException is thrown if the PIN could not be communicated
444: * with the card due to IO problems such as if the connection was
445: * closed before the command could be completed successfully.
446: * @exception InterruptedIOException is thrown if the connection object
447: * is closed before a reply from the card is received.
448: * @exception SecurityException is thrown if the J2ME application does
449: * not have appropriate rights to ask for enabling the PIN.
450: */
451: public byte[] enablePin(int pinID) throws IOException {
452: return doEnterPin(pinID, 0, ACLPermissions.CMD_ENABLE);
453: }
454:
455: /**
456: * This is a high-level method that lets the J2ME application
457: * ask the user to enter the value for an unblocking PIN,
458: * and the new value for the blocked PIN and send
459: * these to the card.
460: * A call to <code>unblockPin</code> method pops up a UI that requests
461: * the user to enter the value for the unblocking PIN and the
462: * new value for the blocked PIN.
463: * The <code>unblockingPinID</code> field indicates which unblocking
464: * PIN is to be
465: * used to unblock the blocked PIN which is indicated by the field
466: * <code>blockedPinId</code>.
467: * The unblockingPinID field indicates which PIN is to be unblocked.
468: * The user can either cancel the request
469: * or continue. If the user enters the PIN values and chooses to continue,
470: * the implementation is responsible
471: * for presenting the PIN values to the card for unblocking the
472: * blocked PIN.
473: * If padding is required for either of the PIN values, the
474: * implementation is responsible for providing appropriate padding.
475: * @param blockedPinID the Id of PIN that is to be unblocked.
476: * @param unblockingPinId the Id of unblocking PIN.
477: * @return result of unblocking the PIN value which is the status word
478: * received from the smart card in the form of a byte array. This method
479: * would return null if the user cancels the request.
480: * @exception IOException is thrown if the PIN could not be communicated
481: * with the card because the connection was
482: * closed before this method was called or because
483: * of communication problems.
484: * @throws InterruptedIOException can be thrown in any of these situations:
485: * <ul>
486: * <li>if this <code>Connection</code>
487: * object is closed during the exchange
488: * operation</li>
489: * <li>if the card is removed after connection is established and
490: * then reinserted, and attempt is made to unblock PIN
491: * without re-establishing the connection</li>
492: * </ul>
493: * @exception SecurityException is thrown if the J2ME application does
494: * not have appropriate rights to ask for unblocking the PIN.
495: */
496: public byte[] unblockPin(int blockedPinID, int unblockingPinId)
497: throws IOException {
498: return doEnterPin(blockedPinID, unblockingPinId,
499: ACLPermissions.CMD_UNBLOCK);
500: }
501:
502: /**
503: * Performs PIN entry operation.
504: * @param pinID PIN identifier.
505: * @param uPinID unblocking PIN identifier.
506: * @param action PIN operation identifier.
507: * @return result of PIN verification which is the status word
508: * received from the smart card in the form of a byte array. This method
509: * would return null if the user cancels the request.
510: * @exception IOException is thrown if the PIN could not be communicated
511: * with the card due to IO problems such as if the connection was
512: * closed before the command could be completed successfully.
513: * @exception InterruptedIOException is thrown if the connection object
514: * is closed before a reply from the card is received.
515: * @exception SecurityException is thrown if the J2ME application does
516: * not have appropriate rights to ask for PIN verification.
517: */
518: private byte[] doEnterPin(int pinID, int uPinID, int action)
519: throws IOException {
520:
521: checkHandle(h);
522:
523: if (openForSAT) {
524: throw new SecurityException();
525: }
526:
527: int header = verifier.preparePIN(pinID, uPinID, action);
528:
529: Object[] pins = verifier.enterPIN(action, securityToken);
530:
531: if (pins == null) {
532: return null;
533: }
534:
535: byte[] pin1 = (byte[]) pins[0];
536: byte[] pin2 = (pins.length == 2) ? (byte[]) pins[1] : null;
537:
538: int dataSize = pin1.length;
539: if (pin2 != null) {
540: dataSize += pin2.length;
541: }
542:
543: byte[] command = new byte[6 + dataSize];
544:
545: command[0] = (byte) (header >> 24);
546: command[1] = (byte) (header >> 16);
547: command[2] = (byte) (header >> 8);
548: command[3] = (byte) header;
549: command[4] = (byte) dataSize;
550: System.arraycopy(pin1, 0, command, 5, pin1.length);
551:
552: if (pin2 != null) {
553: System.arraycopy(pin2, 0, command, 5 + pin1.length,
554: pin2.length);
555: }
556: command[5 + dataSize] = 0x7f;
557:
558: byte[] result;
559: try {
560: // CLA can be incompatible with this logical channel
561: result = exchangeAPDU(command);
562: } catch (IllegalArgumentException e) {
563: throw new IOException(e.getMessage());
564: }
565: byte[] out = new byte[2];
566: System.arraycopy(result, result.length - 2, out, 0, 2);
567: return out;
568: }
569:
570: /**
571: * Checks if given handle is still valid.
572: * @param h Handle to be checked.
573: * @exception IOException is thrown if connection is closed
574: * @exception InterruptedIOException is thrown if connection
575: * closed and re-established again.
576: */
577: private void checkHandle(Handle h) throws IOException {
578: if (h == null || !h.opened || h.getCardSessionId() == -1) {
579: throw new IOException("Connection closed");
580: }
581: if (h.getCardSessionId() != h.cardSessionId) {
582: throw new InterruptedIOException("Connection closed");
583: }
584: }
585: }
|