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