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.satsa.aclapplet;
028:
029: import javacard.framework.*;
030: import javacard.security.*;
031: import javacardx.crypto.Cipher;
032:
033: /**
034: * Card side application for ACL implementation.
035: */
036: public class ACLApplet extends Applet {
037:
038: /** Constant that is used to avoid '(short) x' notation. */
039: static final byte x0 = 0;
040: /** Constant that is used to avoid '(short) x' notation. */
041: static final byte x1 = 1;
042: /** Constant that is used to avoid '(short) x' notation. */
043: static final byte x2 = 2;
044: /** Constant that is used to avoid '(short) x' notation. */
045: static final byte x3 = 3;
046: /** Constant that is used to avoid '(short) x' notation. */
047: static final byte x4 = 4;
048: /** Constant that is used to avoid '(short) x' notation. */
049: static final byte x5 = 5;
050: /** Constant that is used to avoid '(short) x' notation. */
051: static final byte x6 = 6;
052: /** Constant that is used to avoid '(short) x' notation. */
053: static final byte x8 = 8;
054:
055: /** INS byte for command APDU. */
056: static final byte INS_SELECT = (byte) 0xa4;
057: /** INS byte for command APDU. */
058: static final byte INS_READ = (byte) 0xb0;
059: /** INS byte for command APDU. */
060:
061: /** Root DF for entire file structure. */
062: DFile top;
063: /**
064: * Root DF for WIM application. All relative paths start from
065: * here. */
066: DFile base;
067: /** Currently selected file. */
068: File current;
069:
070: /** Constructor. */
071: ACLApplet() {
072: register();
073: }
074:
075: /**
076: * To create an instance of the Applet subclass, the JCRE will call
077: * this static method first.
078: * @param bArray the array containing installation parameters
079: * @param bOffset the starting offset in bArray
080: * @param bLength the length in bytes of the parameter data in bArray
081: */
082: public static void install(byte[] bArray, short bOffset,
083: byte bLength) {
084: new ACLApplet();
085: }
086:
087: /**
088: * Called by the JCRE to inform this applet that it has been
089: * selected. When invoked first time initialises the file system.
090: * @return true
091: */
092: public boolean select() {
093:
094: if (top == null) {
095: init();
096: }
097: current = base;
098: return true;
099: }
100:
101: /**
102: * Initialises the WIM data structures.
103: */
104: void init() {
105:
106: Parser.init(Data.Files);
107: top = (DFile) readFile(null);
108:
109: if (base == null) {
110: ISOException.throwIt((short) 0x9001);
111: }
112:
113: }
114:
115: /**
116: * Creates new file object.
117: * @param parent parent DF for this file
118: * @return the new file object
119: */
120: File readFile(DFile parent) {
121:
122: short id = Parser.getShort();
123: short type = Parser.getByte();
124: short length = Parser.getShort();
125:
126: if ((type & File.DIR) == 0) {
127:
128: EFile f;
129: if ((type & File.EMPTY) == 0) {
130: f = new EFile(parent, id, type, Parser.offset, length,
131: Data.Files);
132: Parser.skip(length);
133: } else {
134: type &= ~File.EMPTY;
135: byte[] data = new byte[length];
136: short dlen = Parser.getShort();
137: Util.arrayCopyNonAtomic(Data.Files, Parser.offset,
138: data, (short) 0, dlen);
139: f = new EFile(parent, id, type, (short) 0, length, data);
140: Parser.skip(dlen);
141: }
142: return f;
143: }
144:
145: DFile f = new DFile(parent, id, type);
146:
147: File[] files = new File[length];
148: for (short i = 0; i < length; i++) {
149: files[i] = readFile(f);
150: }
151:
152: f.files = files;
153:
154: if (type == File.WIM) {
155: base = f;
156: }
157:
158: return f;
159: }
160:
161: /**
162: * Main entry point.
163: * @param apdu command APDU
164: */
165: public void process(APDU apdu) {
166:
167: byte[] data = apdu.getBuffer();
168: byte CLA = (byte) (data[ISO7816.OFFSET_CLA] & 0xF0);
169: byte INS = data[ISO7816.OFFSET_INS];
170:
171: if (CLA == 0 && INS == (byte) (0xA4)
172: && data[ISO7816.OFFSET_P1] == 4) {
173: return;
174: }
175:
176: if (CLA != (byte) 0x00) {
177: ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED);
178: }
179:
180: switch (INS) {
181:
182: case INS_SELECT:
183: selectFile(apdu);
184: return;
185:
186: case INS_READ:
187: read(apdu);
188: return;
189:
190: }
191: ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
192: }
193:
194: /**
195: * Handles SELECT FILE APDU.
196: * @param apdu command APDU
197: */
198: void selectFile(APDU apdu) {
199:
200: byte[] data = apdu.getBuffer();
201:
202: /* if (Util.getShort(data, x2) != 0) {
203: ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2);
204: }
205: */
206: checkDataSize(x2, apdu);
207:
208: File f = select(Util.getShort(data, x5));
209:
210: if (f == null) {
211: ISOException.throwIt(ISO7816.SW_FILE_NOT_FOUND);
212: }
213:
214: current = f;
215:
216: if (current.isDF()) {
217:
218: Util.setShort(data, x0, (short) 0x6f00);
219: apdu.setOutgoingAndSend(x0, x2);
220: } else {
221:
222: Util.setShort(data, x0, (short) 0x6f04);
223: Util.setShort(data, x2, (short) 0x8002);
224: Util.setShort(data, x4, ((EFile) current).length);
225: apdu.setOutgoingAndSend(x0, x6);
226: }
227: }
228:
229: /**
230: * Selects the file specified by file identifier.
231: * @param id file identifier
232: * @return selected file
233: */
234: File select(short id) {
235:
236: DFile f;
237: if (current.isDF()) {
238: f = (DFile) current;
239: } else {
240: f = current.parent;
241: }
242:
243: File x = f.getFile(id);
244: if (x != null) {
245: return x;
246: }
247:
248: f = f.parent;
249:
250: if (f == null) {
251: return null;
252: }
253:
254: return f.getFile(id);
255: }
256:
257: /**
258: * Handles READ BINARY APDU.
259: * @param apdu command APDU
260: */
261: void read(APDU apdu) {
262:
263: if (current.isDF()) {
264: ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED);
265: }
266:
267: EFile f = (EFile) current;
268:
269: byte[] data = apdu.getBuffer();
270:
271: short offset = Util.getShort(data, x2);
272: if (offset < 0 || offset > f.length) {
273: ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2);
274: }
275:
276: short len = (short) (data[x4] & 0xff);
277: if ((short) (offset + len) > f.length) {
278: //ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
279: len = (short) (f.length - offset);
280: if (len < 0) {
281: ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
282: }
283: }
284:
285: apdu.setOutgoing();
286: apdu.setOutgoingLength(len);
287: apdu.sendBytesLong(f.data, (short) (f.offset + offset), len);
288: }
289:
290: /**
291: * Returns the size of DER object for given value size.
292: * @param i the value size
293: * @return the size of DER object
294: */
295: static short getDERSize(short i) {
296:
297: if (i < 128) {
298: return (short) (i + 2);
299: }
300: return (short) (i + 3);
301: }
302:
303: /**
304: * Places encoded length of DER object into the buffer.
305: * @param data the buffer
306: * @param offset offset in the buffer where the length must be
307: * placed
308: * @param length the length to be placed
309: * @return the new offset
310: */
311: static short putLength(byte[] data, short offset, short length) {
312: if (length >= 128) {
313: data[offset++] = (byte) 0x81;
314: }
315: data[offset++] = (byte) length;
316: return offset;
317: }
318:
319: /**
320: * Returns file object specified by path in the buffer.
321: * @param data the buffer
322: * @param index path offset
323: * @param l path length
324: * @return file object or null if not found
325: */
326: File getFile(byte[] data, short index, short l) {
327:
328: // path must contain even number of bytes
329: if (l < 2 || l % 2 != 0) {
330: return null;
331: }
332: l = (short) (l / 2);
333:
334: short id = Util.getShort(data, index);
335:
336: File x;
337:
338: if (l == 1) {
339: x = base;
340: } else {
341:
342: if (id == top.id) {
343: x = top;
344: } else {
345: if (id == (short) 0x3fff) {
346: x = base;
347: } else {
348: return null;
349: }
350: }
351: index += 2;
352: l--;
353: }
354:
355: while (l != 0) {
356:
357: if (!x.isDF()) {
358: return null;
359: }
360: File f = ((DFile) x).getFile(Util.getShort(data, index));
361: if (f == null || f.parent != x) {
362: return null;
363: }
364: x = f;
365: index += 2;
366: l--;
367: }
368:
369: return x;
370: }
371:
372: /**
373: * Verifies that APDU contains correct number of data bytes.
374: * @param expectedSize expected data size
375: * @param apdu APDU object
376: */
377: private void checkDataSize(short expectedSize, APDU apdu) {
378: if (expectedSize != apdu.setIncomingAndReceive()) {
379: ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
380: }
381: }
382:
383: }
|