/* * * * Copyright 1990-2009 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.aclapplet; import javacard.framework.*; import javacard.security.*; import javacardx.crypto.Cipher; /** * Card side application for ACL implementation. */ public class ACLApplet extends Applet { /** Constant that is used to avoid '(short) x' notation. */ static final byte x0 = 0; /** Constant that is used to avoid '(short) x' notation. */ static final byte x1 = 1; /** Constant that is used to avoid '(short) x' notation. */ static final byte x2 = 2; /** Constant that is used to avoid '(short) x' notation. */ static final byte x3 = 3; /** Constant that is used to avoid '(short) x' notation. */ static final byte x4 = 4; /** Constant that is used to avoid '(short) x' notation. */ static final byte x5 = 5; /** Constant that is used to avoid '(short) x' notation. */ static final byte x6 = 6; /** Constant that is used to avoid '(short) x' notation. */ static final byte x8 = 8; /** INS byte for command APDU. */ static final byte INS_SELECT = (byte) 0xa4; /** INS byte for command APDU. */ static final byte INS_READ = (byte) 0xb0; /** INS byte for command APDU. */ /** Root DF for entire file structure. */ DFile top; /** * Root DF for WIM application. All relative paths start from * here. */ DFile base; /** Currently selected file. */ File current; /** Constructor. */ ACLApplet() { register(); } /** * To create an instance of the Applet subclass, the JCRE will call * this static method first. * @param bArray the array containing installation parameters * @param bOffset the starting offset in bArray * @param bLength the length in bytes of the parameter data in bArray */ public static void install(byte[] bArray, short bOffset, byte bLength) { new ACLApplet(); } /** * Called by the JCRE to inform this applet that it has been * selected. When invoked first time initialises the file system. * @return true */ public boolean select() { if (top == null) { init(); } current = base; return true; } /** * Initialises the WIM data structures. */ void init() { Parser.init(Data.Files); top = (DFile) readFile(null); if (base == null) { ISOException.throwIt((short) 0x9001); } } /** * Creates new file object. * @param parent parent DF for this file * @return the new file object */ File readFile(DFile parent) { short id = Parser.getShort(); short type = Parser.getByte(); short length = Parser.getShort(); if ((type & File.DIR) == 0) { EFile f; if ((type & File.EMPTY) == 0) { f = new EFile(parent, id, type, Parser.offset, length, Data.Files); Parser.skip(length); } else { type &= ~File.EMPTY; byte[] data = new byte[length]; short dlen = Parser.getShort(); Util.arrayCopyNonAtomic(Data.Files, Parser.offset, data, (short) 0, dlen); f = new EFile(parent, id, type, (short) 0, length, data); Parser.skip(dlen); } return f; } DFile f = new DFile(parent, id, type); File[] files = new File[length]; for (short i = 0; i < length; i++) { files[i] = readFile(f); } f.files = files; if (type == File.WIM) { base = f; } return f; } /** * Main entry point. * @param apdu command APDU */ public void process(APDU apdu) { byte[] data = apdu.getBuffer(); byte CLA = (byte) (data[ISO7816.OFFSET_CLA] & 0xF0); byte INS = data[ISO7816.OFFSET_INS]; if (CLA == 0 && INS == (byte)(0xA4) && data[ISO7816.OFFSET_P1] == 4) { return; } if (CLA != (byte) 0x00) { ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED); } switch (INS) { case INS_SELECT: selectFile(apdu); return; case INS_READ: read(apdu); return; } ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); } /** * Handles SELECT FILE APDU. * @param apdu command APDU */ void selectFile(APDU apdu) { byte[] data = apdu.getBuffer(); /* if (Util.getShort(data, x2) != 0) { ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2); } */ checkDataSize(x2, apdu); File f = select(Util.getShort(data, x5)); if (f == null) { ISOException.throwIt(ISO7816.SW_FILE_NOT_FOUND); } current = f; if (current.isDF()) { Util.setShort(data, x0, (short) 0x6f00); apdu.setOutgoingAndSend(x0, x2); } else { Util.setShort(data, x0, (short) 0x6f04); Util.setShort(data, x2, (short) 0x8002); Util.setShort(data, x4, ((EFile) current).length); apdu.setOutgoingAndSend(x0, x6); } } /** * Selects the file specified by file identifier. * @param id file identifier * @return selected file */ File select(short id) { DFile f; if (current.isDF()) { f = (DFile) current; } else { f = current.parent; } File x = f.getFile(id); if (x != null) { return x; } f = f.parent; if (f == null) { return null; } return f.getFile(id); } /** * Handles READ BINARY APDU. * @param apdu command APDU */ void read(APDU apdu) { if (current.isDF()) { ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED); } EFile f = (EFile) current; byte[] data = apdu.getBuffer(); short offset = Util.getShort(data, x2); if (offset < 0 || offset > f.length) { ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2); } short len = (short) (data[x4] & 0xff); if ((short)(offset + len) > f.length) { //ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); len = (short)(f.length - offset); if (len < 0) { ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } } apdu.setOutgoing(); apdu.setOutgoingLength(len); apdu.sendBytesLong(f.data, (short) (f.offset + offset), len); } /** * Returns the size of DER object for given value size. * @param i the value size * @return the size of DER object */ static short getDERSize(short i) { if (i < 128) { return (short) (i + 2); } return (short) (i + 3); } /** * Places encoded length of DER object into the buffer. * @param data the buffer * @param offset offset in the buffer where the length must be * placed * @param length the length to be placed * @return the new offset */ static short putLength(byte[] data, short offset, short length) { if (length >= 128) { data[offset++] = (byte) 0x81; } data[offset++] = (byte) length; return offset; } /** * Returns file object specified by path in the buffer. * @param data the buffer * @param index path offset * @param l path length * @return file object or null if not found */ File getFile(byte[] data, short index, short l) { // path must contain even number of bytes if (l < 2 || l % 2 != 0) { return null; } l = (short) (l / 2); short id = Util.getShort(data, index); File x; if (l == 1) { x = base; } else { if (id == top.id) { x = top; } else { if (id == (short) 0x3fff) { x = base; } else { return null; } } index += 2; l--; } while (l != 0) { if (! x.isDF()) { return null; } File f = ((DFile) x).getFile(Util.getShort(data, index)); if (f == null || f.parent != x) { return null; } x = f; index += 2; l--; } return x; } /** * Verifies that APDU contains correct number of data bytes. * @param expectedSize expected data size * @param apdu APDU object */ private void checkDataSize(short expectedSize, APDU apdu) { if (expectedSize != apdu.setIncomingAndReceive()) { ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } } }