// MUSCLE SmartCard Development
// Authors: Tommaso Cucinotta <cucinotta@sssup.it>
// David Corcoran <corcoran@linuxnet.com>
// Ludovic Rousseau <ludovic.rousseau@free.fr>
// Jamie Nicolson <nicolson@netscape.com>
// Package: CardEdgeApplet
// Description: CardEdge implementation with JavaCard
// Protocol Authors: Tommaso Cucinotta <cucinotta@sssup.it>
// David Corcoran <corcoran@linuxnet.com>
// Modified:
// Eirik Herskedal <ehersked@cs.purdue.edu>
//
// BEGIN LICENSE BLOCK
// Copyright (C) 1999-2002 David Corcoran <corcoran@linuxnet.com>
// Copyright (C) 2006 Red Hat, Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// 3. The name of the author may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// Changes to this license can be made only by the copyright author with
// explicit written consent.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Alternatively, the contents of this file may be used under the terms of
// the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
// case the provisions of the LGPL are applicable instead of those above. If
// you wish to allow use of your version of this file only under the terms
// of the LGPL, and not to allow others to use your version of this file
// under the terms of the BSD license, indicate your decision by deleting
// the provisions above and replace them with the notice and other
// provisions required by the LGPL. If you do not delete the provisions
// above, a recipient may use your version of this file under the terms of
// either the BSD license or the LGPL.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
// END LICENSE_BLOCK
package com.redhat.ckey.applet;
import javacard.framework.ISOException;
import javacard.framework.Util;
// Referenced classes of package com.redhat.ckey.applet:
// MemoryManager
/**
* Object Manager Class
*
* <p>Objects are linked in a list in the dynamic memory. No smart search
* is done at the moment.</p>
*
* <p>TODO - Could we definitively avoid a map enforcing the ID (equal to
* the memory address, i.e.) - security implications ?</p>
*
* Object fields:
* <pre>
* short next
* short obj_class
* short obj_id
* short obj_size
* byte[] data
* </pre>
*
* @author Tommaso Cucinotta
* @author David Corcoran
* @author Ludovic Rousseau
* @version 0.9.9
*
*/
public class ObjectManager
{
public static final byte OBJ_ACL_SIZE = 6;
private static final byte OBJ_HEADER_SIZE = 14;
private static final byte OBJ_H_NEXT = 0;
private static final byte OBJ_H_CLASS = 2;
private static final byte OBJ_H_ID = 4;
private static final byte OBJ_H_ACL = 6;
private static final short OBJ_ACL_READ = 6;
private static final short OBJ_ACL_WRITE = 8;
private static final short OBJ_ACL_DELETE = 10;
private static final byte OBJ_H_SIZE = 12;
private static final byte OBJ_H_DATA = 14;
/**
* There have been memory problems on the card
*/
public static final short SW_NO_MEMORY_LEFT = (short)0x9C01;
public static final short SW_OBJECT_NOT_FOUND = (short)0x9C07;
public static final short SW_OBJECT_EXISTS = (short)0x9C08;
/**
* Size of an Object Record filled by getFirstRecord() or
* getNextRecord(): ID, Size, ACL
*/
public static final short RECORD_SIZE = 14;
/**
* Iterator on objects.
*/
private short it;
/**
* The Memory Manager object
*/
private MemoryManager mem;
/**
* Head of the objects' list
*/
private short obj_list_head;
/**
* Constructor for the ObjectManager class.
*
* @param mem_ref The MemoryManager object to be used to allocate
* objects' memory.
*/
public ObjectManager(MemoryManager mem_ref)
{
mem = null;
obj_list_head = -1;
mem = mem_ref;
obj_list_head = -1;
}
/**
* Check if logged in identities satisfy requirements for an
* operation
*
* @param required_ids The required identities as from an ACL short
* @param logged_ids The current logged in identities as stored in
* CardEdge.logged_ids
*/
private boolean authorizeOp(short base, short logged_ids, short offset)
{
short required_ids = mem.getShort((short)(base - OBJ_H_DATA), offset);
return (required_ids & logged_ids) != 0;
}
/**
* Allow or unallow delete on object given the logged identities
*/
public boolean authorizeDeleteFromAddress(short base, short logged_ids)
{
return authorizeOp(base, logged_ids, OBJ_ACL_DELETE);
}
/**
* Allow or unallow read on object given the logged identities
*
* @param base The object base address as returned from
* getBaseAddress()
* @param logged_ids The current logged in identities as stored in
* CardEdge.logged_ids
*/
public boolean authorizeReadFromAddress(short base, short logged_ids)
{
return authorizeOp(base, logged_ids, OBJ_ACL_READ);
}
/**
* Allow or unallow write on object given the logged identities
*
* @param base The object base address as returned from
* getBaseAddress()
* @param logged_ids The current logged in identities as stored in
* CardEdge.logged_ids
*/
public boolean authorizeWriteFromAddress(short base, short logged_ids)
{
return authorizeOp(base, logged_ids, OBJ_ACL_WRITE);
}
/**
* Clamps an object freeing the unused memory
*
* @throws SW_NO_MEMORY_LEFT exception if cannot allocate the
* memory. Does not check if object exists.
*
* @param type Object Type
* @param id Object ID (Type and ID form a generic 4 bytes
* identifier)
* @param new_size The new object size (must be less than current
* size)
*
* @return True if clamp was possible, false otherwise
*/
public boolean clampObject(short type, short id, short new_size)
{
short base = getEntry(type, id);
if(base == -1)
ISOException.throwIt((short)SW_OBJECT_NOT_FOUND);
if(mem.realloc(base, (short)(new_size + RECORD_SIZE)))
{
mem.setShort(base, (short)OBJ_H_SIZE, new_size);
return true;
} else
{
return false;
}
}
/**
* Compare an object's ACL with the provided ACL.
*
* @param base The object base address, as returned from
* getBaseAddress()
* @param acl The buffer containing the ACL
*
* @return True if the ACLs are equal
*/
public boolean compareACLFromAddress(short base, byte acl[])
{
return Util.arrayCompare(mem.getBuffer(),
(short)((base - OBJ_HEADER_SIZE) + OBJ_H_ACL), acl,
(short)0, (short)OBJ_ACL_SIZE) == 0;
}
/**
* Creates an object with specified parameters.
*
* @throws SW_NO_MEMORY_LEFT exception if cannot allocate the
* memory. Does not check if object exists.
*
* @param type Object Type
* @param id Object ID (Type and ID form a generic 4 bytes
* identifier)
* @param acl_buf Java byte array containing the ACL for the new object
* @param acl_offset Offset at which the ACL starts in acl_buf[]
*
* @return The memory base address for the object. It can be used in
* successive calls to xxxFromAddress() methods.
*
*/
public short createObject(short type, short id, short size,
byte acl_buf[], short acl_offset)
{
if (exists(type, id))
ISOException.throwIt(SW_OBJECT_EXISTS);
short base = mem.alloc((short)(size + OBJ_HEADER_SIZE));
if(base == -1)
ISOException.throwIt((short)SW_NO_MEMORY_LEFT);
mem.setShort(base, (short)OBJ_H_NEXT, obj_list_head);
mem.setShort(base, (short)OBJ_H_CLASS, type);
mem.setShort(base, (short)OBJ_H_ID, id);
mem.setShort(base, (short)OBJ_H_SIZE, size);
mem.setBytes(base, (short)OBJ_H_ACL, acl_buf, acl_offset,
(short)OBJ_ACL_SIZE);
obj_list_head = base;
return (short)(base + OBJ_H_DATA);
}
/**
* Creates an object with the maximum available size
*/
public short createObjectMax(short type, short id,
byte acl_buf[], short acl_offset)
{
short obj_size = mem.getMaxSize();
if(obj_size == 0)
ISOException.throwIt((short)SW_NO_MEMORY_LEFT);
return createObject(type, id, (short)(obj_size - OBJ_H_DATA),
acl_buf, acl_offset);
}
/**
* Destroy the specified object
*
* @param type Object Type
* @param id Object ID (Type and ID form a generic 4 bytes
* identifier)
* @param secure If true, object memory is zeroed before being
* released.
*/
public void destroyObject(short type, short id, boolean secure)
{
boolean found;
do {
short curr = obj_list_head;
short prev = -1;
for (found = false; !found && curr != -1; ) {
if(mem.getShort(curr, (short)OBJ_H_CLASS) == type &&
mem.getShort(curr, (short)OBJ_H_ID) == id) {
found = true;
} else {
prev = curr;
curr = mem.getShort(curr, (short)0);
}
}
if(found) {
if(prev != -1)
mem.setShort(prev, (short)0, mem.getShort(curr, (short)0));
else
obj_list_head = mem.getShort(curr, (short)0);
if(secure) {
Util.arrayFillNonAtomic(mem.getBuffer(),
(short)(curr + OBJ_H_DATA),
mem.getShort(curr, (short)OBJ_H_SIZE),
(byte)0);
}
mem.free(curr);
}
} while (found);
}
/**
* Checks if an object exists
*
* @param type The object type
* @param id The object ID
*
* @return true if object exists
*/
public boolean exists(short type, short id)
{
short base = getEntry(type, id);
return base != -1;
}
/**
* Returns the data base address (offset) for an object.
*
* <p>The base address can be used for further calls to
* xxxFromAddress() methods</p>
*
* <p>This function should only be used if performance issue arise.
* setObjectData() and getObjectData() should be used, instead.</p>
*
* @param type Object Type
* @param id Object ID (Type and ID form a generic 4 bytes
* identifier)
*
* @return The starting offset of the object. At this location
*/
public short getBaseAddress(short type, short id)
{
short base = getEntry(type, id);
if(base == -1)
return -1;
else
return (short)(base + OBJ_H_DATA);
}
/**
* Returns the header base address (offset) for the specified
* object.
*
* <p>Object header is found at the returned offset, while object
* data starts right after the header.</p>
*
* <p>This performs a linear search, so performance issues could
* arise as the number of objects grows If object is not found,
* then returns NULL_OFFSET.</p>
*
* @param type Object Type
* @param id Object ID (Type and ID form a generic 4 bytes
* identifier)
*
* @return The starting offset of the object or NULL_OFFSET if the
* object is not found.
*/
private short getEntry(short type, short id)
{
for(short base = obj_list_head; base != -1;
base = mem.getShort(base, (short)0)) {
if(mem.getShort(base, (short)OBJ_H_CLASS) == type &&
mem.getShort(base, (short)OBJ_H_ID) == id)
return base;
}
return -1;
}
/**
* Resets the objects iterator and retrieves the information record
* of the first object, if any.
*
* @param buffer The byte array into which the record will be copied
* @param offset The offset in buffer[] at which the record will be
* copied
*
* @return True if an object was found. False if there are no
* objects.
*
* @see #getNextRecord(byte[], short)
*/
public boolean getFirstRecord(byte buffer[], short offset)
{
it = obj_list_head;
return getNextRecord(buffer, offset);
}
/**
* Retrieves the information record of the next object, if any.
*
* @param buffer The byte array into which the record will be copied
* @param offset The offset in buffer[] at which the record will be
* copied
*
* @return True if an object was found. False if there are no more
* objects to inspect.
*
* @see #getFirstRecord(byte[], short)
*/
public boolean getNextRecord(byte buffer[], short offset)
{
if(it == -1)
{
return false;
} else
{
Util.setShort(buffer, offset, mem.getShort(it, (short)2));
Util.setShort(buffer, (short)(offset + 2),
mem.getShort(it, (short)OBJ_H_ID));
Util.setShort(buffer, (short)(offset + 4), (short)0);
Util.setShort(buffer, (short)(offset + 6),
mem.getShort(it, (short)OBJ_H_SIZE));
Util.arrayCopyNonAtomic(mem.getBuffer(),
(short)(it + OBJ_H_ACL), buffer,
(short)(offset + 8),
(short)OBJ_ACL_SIZE);
it = mem.getShort(it, (short)0);
return true;
}
}
/**
* Returns object size from the base address
*/
public short getSizeFromAddress(short base)
{
return mem.getShort((short)((base - OBJ_H_DATA) + OBJ_H_SIZE));
}
/**
* Set the object's ACL.
*/
private void setACL(short type, short id, byte acl_buf[], short acl_offset)
{
short base = getEntry(type, id);
mem.setBytes(base, (short)OBJ_H_ACL, acl_buf, acl_offset, (short)OBJ_ACL_SIZE);
}
}