/*
*
*
* Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 only, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License version 2 for more details (a copy is
* included at /legal/license.txt).
*
* You should have received a copy of the GNU General Public License
* version 2 along with this work; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 or visit www.sun.com if you need additional
* information or have any questions.
*/
package com.sun.satsa.util;
import java.io.IOException;
/**
* This class provides interface to card file system.
*/
abstract public class FileSystemAbstract {
/** Expected maximum path depth. */
static final int MAX_PATH_DEPTH = 10;
/** APDU INS byte. */
protected static final byte INS_SELECT = (byte) 0xa4;
/** APDU INS byte. */
protected static final byte INS_READ = (byte) 0xb0;
/** APDU INS byte. */
protected static final byte INS_UPDATE = (byte) 0xd6;
/** Root path for this application. */
short[] root;
/** The length of path to current DF. */
int pathLength;
/**
* Path to the current DF. Element at index pathLength contains EF
* identifier if EF is selected.
*/
short[] currentPath;
/** Connection used by this file system. */
protected Connection apdu;
/** Size of currently selected EF or -1 if DF is selected. */
protected int currentFileSize;
/** True if currently selected file is EF. */
protected boolean isEFSelected;
/**
* Constructs new FileSystem object.
* @param apdu connection to be used by this object.
*/
public FileSystemAbstract(Connection apdu) {
this.apdu = apdu;
}
/**
* Sets the root directory for this file system.
* @param root root directory path
*/
public void setRoot(short[] root) {
this.root = root;
currentPath = new short[MAX_PATH_DEPTH];
for (int j = 0; j < root.length; j++) {
currentPath[j] = root[j];
}
pathLength = root.length;
}
/**
* Sends the select DF command to the card.
* @param root short[] directory path
* @throws IOException if IO error occurs
*/
public void selectRoot(short[] root) throws IOException {
setRoot(root);
for (int i = 0; i < root.length; i++) {
byte[] data = apdu.resetCommand().
putShort(root[i]).
sendCommand(INS_SELECT, 0x0100);
}
}
/**
* If necessary converts path into completely specified path.
* @param t TLV object that contains octet string representing path
* @return completely specified path
* @throws IOException if path is incorrect
*/
public short[] makePath(TLV t) throws IOException {
short[] path;
short s = Utils.getShort(t.data, t.valueOffset);
if (root == null) { // only file IDs are used
if (t.length == 2) {
path = new short[1];
path[0] = s;
return path;
}
throw new IOException("Invalid path - ID expected");
}
// absolute path
if (s == 0x3f00) {
path = new short[t.length / 2];
for (int i = 0; i < path.length; i++) {
path[i] = Utils.getShort(t.data, t.valueOffset + i * 2);
}
return path;
}
// it must be relative path
int len = root.length + t.length / 2;
int offset = t.valueOffset;
if (s == 0x3fff) {
len--;
offset += 2;
} else {
if (t.length != 2) {
throw new IOException(
"Invalid path - file ID must be used");
}
}
path = new short[len];
System.arraycopy(root, 0, path, 0, root.length);
for (int i = root.length; i < path.length; i++) {
path[i] = Utils.getShort(t.data, offset);
offset += 2;
}
return path;
}
/**
* Selects file using only SELECT APDU command with file identifier.
* According to WIM specification support of this selection method
* is mandatory.
* @param path file path
* @throws IOException if IO error occurs
*/
public void select(short[] path) throws IOException {
if (root == null) {
if (path.length == 1) {
select(path[0]);
return;
}
throw new IOException(
"Invalid path - file ID must be used");
}
int c = 0; // index of first path element that differs
while (c < pathLength && c < path.length) {
if (currentPath[c] != path[c]) {
break;
}
c++;
}
if (c == 0) {
throw new IOException("Invalid path - wrong root directory");
}
if (c == pathLength &&
pathLength == (path.length - 1) &&
isEFSelected &&
currentPath[pathLength] == path[pathLength]) {
return;
}
while (c < pathLength) {
pathLength--;
select(currentPath[pathLength]);
}
while (pathLength < path.length) {
select(path[pathLength]);
currentPath[pathLength] = path[pathLength];
if (isEFSelected) {
if (pathLength == path.length - 1) {
break;
}
throw new IOException("Invalid path - EF in path");
}
pathLength++;
}
}
/**
* Selects file by ID.
* @param id file ID
* @throws IOException if IOError occurs
*/
abstract public void select(short id) throws IOException;
/**
* Reads the current EF.
* @return array that contains EF body.
* @throws IOException if IO error occurs
*/
public byte[] readFile() throws IOException {
byte[] data = readData(0, currentFileSize, 0);
return data;
}
/**
* Reads part of selected file.
* @param offset the offset into data buffer where data should be
* placed
* @param length data length
* @param fileOffset file data offset
* @throws IOException if IO error occurs
* @return data byte array of the data
*/
abstract public byte[] readData(int offset, int length, int fileOffset)
throws IOException;
/**
* Loads and parses one DER encoded object.
* @param l location of the object.
* @return the object.
* @throws IOException if I/O error occurs
* @throws TLVException if parsing error occurs
*/
public TLV loadObject(Location l) throws IOException, TLVException {
select(l.path);
byte[] tmp = readData(0, l.length, l.offset);
return new TLV(tmp, 0);
}
/**
* Converts PKCS#15 path into location.
* @param path TLV structure that represents path
* @return Location object
*/
public Location pathToLocation(TLV path) throws IOException {
try {
if (path.type == TLV.SEQUENCE_TYPE) {
path = path.child;
}
short[] p = makePath(path);
int offset = 0;
int length = -1;
if (path.next != null) {
offset = path.next.getInteger();
length = path.next.next.getInteger();
}
return new Location(p, offset, length);
} catch (IOException e) {
throw new IOException("Invalid path structure");
} catch (NullPointerException npe) {
throw new IOException("Invalid path structure");
} catch (IndexOutOfBoundsException iobe) {
throw new IOException("Invalid path structure");
}
}
/**
* Returns the size of currently selected EF or -1 if DF is
* selected.
* @return file size
*/
public int getCurrrentFileSize() {
return currentFileSize;
}
}
|