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.util;
028:
029: import java.io.IOException;
030:
031: /**
032: * This class provides interface to card file system.
033: */
034: abstract public class FileSystemAbstract {
035:
036: /** Expected maximum path depth. */
037: static final int MAX_PATH_DEPTH = 10;
038: /** APDU INS byte. */
039: protected static final byte INS_SELECT = (byte) 0xa4;
040: /** APDU INS byte. */
041: protected static final byte INS_READ = (byte) 0xb0;
042: /** APDU INS byte. */
043: protected static final byte INS_UPDATE = (byte) 0xd6;
044: /** Root path for this application. */
045: short[] root;
046: /** The length of path to current DF. */
047: int pathLength;
048: /**
049: * Path to the current DF. Element at index pathLength contains EF
050: * identifier if EF is selected.
051: */
052: short[] currentPath;
053: /** Connection used by this file system. */
054: protected Connection apdu;
055: /** Size of currently selected EF or -1 if DF is selected. */
056: protected int currentFileSize;
057: /** True if currently selected file is EF. */
058: protected boolean isEFSelected;
059:
060: /**
061: * Constructs new FileSystem object.
062: * @param apdu connection to be used by this object.
063: */
064: public FileSystemAbstract(Connection apdu) {
065: this .apdu = apdu;
066: }
067:
068: /**
069: * Sets the root directory for this file system.
070: * @param root root directory path
071: */
072: public void setRoot(short[] root) {
073:
074: this .root = root;
075: currentPath = new short[MAX_PATH_DEPTH];
076:
077: for (int j = 0; j < root.length; j++) {
078: currentPath[j] = root[j];
079: }
080: pathLength = root.length;
081: }
082:
083: /**
084: * Sends the select DF command to the card.
085: * @param root short[] directory path
086: * @throws IOException if IO error occurs
087: */
088: public void selectRoot(short[] root) throws IOException {
089: setRoot(root);
090: for (int i = 0; i < root.length; i++) {
091: byte[] data = apdu.resetCommand().putShort(root[i])
092: .sendCommand(INS_SELECT, 0x0100);
093: }
094: }
095:
096: /**
097: * If necessary converts path into completely specified path.
098: * @param t TLV object that contains octet string representing path
099: * @return completely specified path
100: * @throws IOException if path is incorrect
101: */
102: public short[] makePath(TLV t) throws IOException {
103:
104: short[] path;
105: short s = Utils.getShort(t.data, t.valueOffset);
106: if (root == null) { // only file IDs are used
107: if (t.length == 2) {
108: path = new short[1];
109: path[0] = s;
110: return path;
111: }
112: throw new IOException("Invalid path - ID expected");
113: }
114:
115: // absolute path
116: if (s == 0x3f00) {
117: path = new short[t.length / 2];
118: for (int i = 0; i < path.length; i++) {
119: path[i] = Utils.getShort(t.data, t.valueOffset + i * 2);
120: }
121: return path;
122: }
123:
124: // it must be relative path
125: int len = root.length + t.length / 2;
126: int offset = t.valueOffset;
127:
128: if (s == 0x3fff) {
129: len--;
130: offset += 2;
131: } else {
132: if (t.length != 2) {
133: throw new IOException(
134: "Invalid path - file ID must be used");
135: }
136: }
137:
138: path = new short[len];
139: System.arraycopy(root, 0, path, 0, root.length);
140: for (int i = root.length; i < path.length; i++) {
141: path[i] = Utils.getShort(t.data, offset);
142: offset += 2;
143: }
144: return path;
145: }
146:
147: /**
148: * Selects file using only SELECT APDU command with file identifier.
149: * According to WIM specification support of this selection method
150: * is mandatory.
151: * @param path file path
152: * @throws IOException if IO error occurs
153: */
154: public void select(short[] path) throws IOException {
155:
156: if (root == null) {
157: if (path.length == 1) {
158: select(path[0]);
159: return;
160: }
161: throw new IOException("Invalid path - file ID must be used");
162: }
163:
164: int c = 0; // index of first path element that differs
165: while (c < pathLength && c < path.length) {
166: if (currentPath[c] != path[c]) {
167: break;
168: }
169: c++;
170: }
171:
172: if (c == 0) {
173: throw new IOException("Invalid path - wrong root directory");
174: }
175:
176: if (c == pathLength && pathLength == (path.length - 1)
177: && isEFSelected
178: && currentPath[pathLength] == path[pathLength]) {
179: return;
180: }
181:
182: while (c < pathLength) {
183: pathLength--;
184: select(currentPath[pathLength]);
185: }
186:
187: while (pathLength < path.length) {
188: select(path[pathLength]);
189: currentPath[pathLength] = path[pathLength];
190: if (isEFSelected) {
191: if (pathLength == path.length - 1) {
192: break;
193: }
194: throw new IOException("Invalid path - EF in path");
195: }
196: pathLength++;
197: }
198: }
199:
200: /**
201: * Selects file by ID.
202: * @param id file ID
203: * @throws IOException if IOError occurs
204: */
205: abstract public void select(short id) throws IOException;
206:
207: /**
208: * Reads the current EF.
209: * @return array that contains EF body.
210: * @throws IOException if IO error occurs
211: */
212: public byte[] readFile() throws IOException {
213: byte[] data = readData(0, currentFileSize, 0);
214: return data;
215: }
216:
217: /**
218: * Reads part of selected file.
219: * @param offset the offset into data buffer where data should be
220: * placed
221: * @param length data length
222: * @param fileOffset file data offset
223: * @throws IOException if IO error occurs
224: * @return data byte array of the data
225: */
226: abstract public byte[] readData(int offset, int length,
227: int fileOffset) throws IOException;
228:
229: /**
230: * Loads and parses one DER encoded object.
231: * @param l location of the object.
232: * @return the object.
233: * @throws IOException if I/O error occurs
234: * @throws TLVException if parsing error occurs
235: */
236: public TLV loadObject(Location l) throws IOException, TLVException {
237: select(l.path);
238: byte[] tmp = readData(0, l.length, l.offset);
239: return new TLV(tmp, 0);
240: }
241:
242: /**
243: * Converts PKCS#15 path into location.
244: * @param path TLV structure that represents path
245: * @return Location object
246: */
247: public Location pathToLocation(TLV path) throws IOException {
248:
249: try {
250: if (path.type == TLV.SEQUENCE_TYPE) {
251: path = path.child;
252: }
253:
254: short[] p = makePath(path);
255: int offset = 0;
256: int length = -1;
257:
258: if (path.next != null) {
259: offset = path.next.getInteger();
260: length = path.next.next.getInteger();
261: }
262: return new Location(p, offset, length);
263: } catch (IOException e) {
264: throw new IOException("Invalid path structure");
265: } catch (NullPointerException npe) {
266: throw new IOException("Invalid path structure");
267: } catch (IndexOutOfBoundsException iobe) {
268: throw new IOException("Invalid path structure");
269: }
270: }
271:
272: /**
273: * Returns the size of currently selected EF or -1 if DF is
274: * selected.
275: * @return file size
276: */
277: public int getCurrrentFileSize() {
278: return currentFileSize;
279: }
280: }
|