Author: rrelyea
Update of /cvs/dirsec/coolkey/applet/src/com/redhat/ckey/applet
In directory cvs-int.fedora.redhat.com:/tmp/cvs-serv14460/src/com/redhat/ckey/applet
Added Files:
ASN1.java CardEdge.java MemoryManager.java ObjectManager.java
Log Message:
Coolkey applet
--- NEW FILE ASN1.java ---
// SmartCard Applet
// Authors: Robert Relyea <rrelyea(a)redhat.com>
// Package: CardEdgeApplet
// Description: CardEdge implementation with JavaCard
//
// BEGIN LICENSE BLOCK
// 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.nkey.applet;
import javacard.framework.ISOException;
import javacard.framework.JCSystem;
import javacard.framework.Util;
/**
* ASN1 parser Class
*
* <p>This Simplistic ASN.1 parser does not interpret tags, it simply finds
* elements based on where their fields are supposed to wind up at. </p>
*
*
* Object fields:
* <pre>
* short[] newSize; // way to get around java's restrictions on pass by ref.
* byte[] data
* </pre>
*
* @author Robert Relyea
* @version 0.0.1
*
*/
public class ASN1
{
public static final short SW_BAD_DER_DATA = (short)0x9cd0;
private final short NEXT = 0;
private final short SIZE = 1;
private final short END = 2;
private short[] params;
public ASN1()
{
params=JCSystem.makeTransientShortArray((short)3,
JCSystem.CLEAR_ON_DESELECT);
}
public short GetEnd()
{
return params[END];
}
public short GetSize()
{
return params[SIZE];
}
public short GetNext()
{
return params[NEXT];
}
public byte GetTag(byte buf[], short offset, short end)
{
if (end <= offset) {
ISOException.throwIt(SW_BAD_DER_DATA);
}
return buf[offset];
}
public short Unwrap(byte buf[], short offset, short end, short dbg)
{
byte tag;
byte len;
short length = 0;
if (end < (short)(offset+2)) {
ISOException.throwIt(SW_BAD_DER_DATA);
}
tag = buf[offset++];
if (tag == 0) {
ISOException.throwIt(SW_BAD_DER_DATA);
}
len = buf[offset++];
length = Util.makeShort((byte)0,len);
if ((len & 0x80) != 0) {
short count = Util.makeShort((byte)0,(byte)(len & 0x7f));
if (end < (short)(offset+count)) {
ISOException.throwIt(SW_BAD_DER_DATA);
}
if (count > 2) {
ISOException.throwIt(SW_BAD_DER_DATA);
}
length = 0;
while (count-- > 0) {
length = (short)((length << 8)
| Util.makeShort((byte)0,buf[offset++]));
}
}
params[SIZE] = length;
params[NEXT] = ((short)(offset+length));
params[END] = ((short)(offset+length));
return offset;
}
public short Skip(byte buf[], short offset, short end, short dbg)
{
Unwrap(buf,offset,end,dbg);
return params[NEXT];
}
public short UnwrapBitString(byte buf[], short offset, short end, short dbg)
{
if (buf[offset] != 0) {
ISOException.throwIt(SW_BAD_DER_DATA);
}
if (end < (short)(offset+1)) {
ISOException.throwIt(SW_BAD_DER_DATA);
}
params[SIZE]--;
return (short)(offset+1);
}
public short Signed2Unsigned(byte buf[], short offset, short end, short dbg)
{
short startOffset = offset;
short startSize=params[SIZE];
for (; offset < end && buf[offset] == 0 ; offset++){
params[SIZE]--;
}
if (offset >= end) {
ISOException.throwIt(SW_BAD_DER_DATA);
}
return offset;
}
}
--- NEW FILE CardEdge.java ---
// MUSCLE SmartCard Development
// Authors: Tommaso Cucinotta <cucinotta(a)sssup.it>
// David Corcoran <corcoran(a)linuxnet.com>
// Ludovic Rousseau <ludovic.rousseau(a)free.fr>
// Jamie Nicolson <nicolson(a)netscape.com>
// Robert Relyea <rrelyea(a)redhat.com>
// Nelson Bolyard <nelsonb(a)netscape.com>
// Package: CardEdgeApplet
// Description: CardEdge implementation with JavaCard
// Protocol Authors: Tommaso Cucinotta <cucinotta(a)sssup.it>
// David Corcoran <corcoran(a)linuxnet.com>
// Modified:
// Eirik Herskedal <ehersked(a)cs.purdue.edu>
//
// BEGIN LICENSE BLOCK
// Copyright (C) 1999-2002 David Corcoran <corcoran(a)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.nkey.applet;
import javacard.framework.*;
import javacard.security.*;
import javacardx.crypto.Cipher;
import visa.openplatform.ProviderSecurityDomain;
import visa.openplatform.OPSystem;
// Referenced classes of package com.redhat.nkey.applet:
// MemoryManager, ObjectManager, ASN1
/**
* Implements MUSCLE's Card Edge Specification.
*
* <p>TODO:
*
* <ul>
* <li>Allows maximum number of keys and PINs and total mem to be specified at the instantiation moment.</li>
*
* <li>How do transactions fit in the methods?</li>
* <li>Where should we issue begin/end transaction?</li>
* <li>Should we ever abort transaction? Where?</li>
* <li>Everytime there is an <tt>"if (avail < )"</tt> check, call <tt>ThrowDeleteObjects()</tt>.</li>
* </ul>
* </p>
*
* <p>NOTES:
*
* <ul>
* <li>C preprocessor flags:
* <ul>
* <li>Encryption algorithms: WITH_RSA, WITH_DSA, WITH_DES, WITH_3DES</li>
* <li>ComputeCrypt directions: WITH_ENCRYPT, WITH_DECRYPT, WITH_SIGN</li>
* <li>Enable/Disable External Authenticate: WITH_EXT_AUTH</li>
* <li>Enable/Disable PIN Policy enforcement: WITH_PIN_POLICY</li>
* </ul>
* </li>
* <li>C preprocessor defines:
* <ul>
* <li>JAVA_PACKAGE: The name of Java package for this Applet</li>
* <li>CardEdge: The name of Java class for the Applet</li>
* </ul>
* </li>
* </ul>
* </p>
*
* @author Tommaso Cucinotta
* @author David Corcoran
* @author Ludovic Rousseau
* @version 0.9.10
*/
public class CardEdge extends Applet
{
private static final byte ZEROB = 0;
private static final byte MAX_NUM_KEYS = 8;
private static final byte MAX_NUM_PINS = 8;
private static final byte VERSION_PROTOCOL_MAJOR = 1;
private static final byte VERSION_PROTOCOL_MINOR = 1;
private static final byte VERSION_APPLET_MAJOR = 1;
private static final byte VERSION_APPLET_MINOR = 3;
private static final short BUILDID_MAJOR = (short) 0x4472;
private static final short BUILDID_MINOR = (short) 0x4aa7;
private static final short ZEROS = 0;
// * Enable pin size check
private static final byte PIN_POLICY_SIZE = 1;
// * Enable pin charset check
private static final byte PIN_POLICY_CHARSET = 2;
// * Enable charset mixing check
private static final byte PIN_POLICY_MIXED = 4;
// * Numbers are allowed
private static final byte PIN_CHARSET_NUMBERS = 1;
// * Upper case letters
private static final byte PIN_CHARSET_UC_LETTERS = 2;
// * Lower case letters
private static final byte PIN_CHARSET_LC_LETTERS = 4;
// * Punctuation symbols: , .
private static final byte PIN_CHARSET_PUNCT = 8;
// * Other binary codes (NUMBERS | OTHERS excludes LETTERS and PUNCT)
private static final byte PIN_CHARSET_OTHERS = (byte)0x80;
// * PIN must contain chars from at least 2 different char sets
private static final byte PIN_MIXED_TWO = 1;
// * PIN must at least contain chars from both upper and lower case
private static final byte PIN_MIXED_CASE = 2;
// * PIN must at least contain 1 char from each char set
private static final byte PIN_MIXED_ALL = 4;
/**
* The User's PIN is pin 0. There is no SO pin.
*/
private static final byte USER_IDENTITY = 0;
private static final byte DEFAULT_IDENTITY = 15; // MUSCLE reserved ID
private static final byte RA_IDENTITY = 14; // MUSCLE reserved ID
private static final short NONCE_SIZE = (short)8;
private static final short ISSUER_INFO_SIZE = (short)0xe0;
private static final short USER_ACL = (short)(1 << USER_IDENTITY);
private static final short DEFAULT_ACL = (short)(1 << DEFAULT_IDENTITY);
private static final short RA_ACL = (short)(1 << RA_IDENTITY);
private static final short ANY_ONE_ACL = (short)0xffff;
private static final short NO_ONE_ACL = (short)0;
private static final byte pinPolicies = 7;
private static final byte pinMinSize = 4;
private static final byte pinMaxSize = 16;
private static final byte MAX_KEY_TRIES = 5;
private static final short IN_OBJECT_CLA = -1;
private static final short IN_OBJECT_ID = -2;
private static final short OUT_OBJECT_CLA = -1;
private static final short OUT_OBJECT_ID = -1;
private static final byte KEY_ACL_SIZE = 6;
private static final byte CardEdge_CLA = (byte)0xB0;
private static final byte CardManager_CLA = (byte)0x80;
private static final byte SECURE_CLA = (byte)0x84;
/**
* Instruction codes
*/
/* Deprecated */
private static final byte INS_SETUP = (byte)0x2A;
private static final byte INS_GEN_KEYPAIR = (byte)0x30;
private static final byte INS_EXPORT_KEY = (byte)0x34;
[...2435 lines suppressed...]
case INS_CHANGE_PIN:
ChangePIN(apdu, buffer);
break;
case INS_CREATE_OBJ:
CreateObject(apdu, buffer);
break;
case INS_DELETE_OBJ:
DeleteObject(apdu, buffer);
break;
case INS_READ_OBJ:
ReadObject(apdu, buffer);
break;
case INS_WRITE_OBJ:
WriteObject(apdu, buffer);
break;
case INS_LOGOUT:
Logout(apdu,buffer);
break;
case INS_LIST_PINS:
ListPINs(apdu, buffer);
break;
case INS_LIST_OBJECTS:
ListObjects(apdu, buffer);
break;
case INS_LIST_KEYS:
ListKeys(apdu, buffer);
break;
case INS_GET_STATUS:
GetStatus(apdu, buffer);
break;
case INS_GET_ISSUER_INFO:
getIssuerInfo(apdu, buffer);
break;
case INS_GET_RANDOM:
getRandom(apdu, buffer);
break;
case INS_SEED_RANDOM:
seedRandom(apdu, buffer);
break;
case INS_GET_LIFECYCLE:
getLifeCycle(apdu, buffer);
break;
case INS_GET_BUILDID:
getBuildID(apdu, buffer);
break;
case INS_GET_BUILTIN_ACL:
getBuiltInACL(apdu, buffer);
break;
case INS_NOP:
break;
// case INS_SETUP:
// case INS_GEN_KEYPAIR:
// case INS_EXPORT_KEY:
// case INS_LOGOUT_ALL:
// case INS_GET_CHALLENGE:
// case INS_CAC_EXT_AUTH:
// case INS_UNBLOCK_PIN:
default:
ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
break;
}
}
//
// handle non-secure standard commands. Called from process.
//
private void processSecureAPDU(APDU apdu, byte buffer[])
{
byte ins = buffer[ISO7816.OFFSET_INS];
if (ins != INS_SEC_EXT_AUTH) {
verifySecureChannel(apdu, buffer);
}
switch (ins) {
case INS_SEC_EXT_AUTH:
externalAuthenticate(apdu, buffer);
break;
case INS_SEC_SET_PIN:
resetPIN(apdu, buffer);
break;
case INS_SEC_START_ENROLLMENT:
startEnrollment(apdu, buffer);
break;
case INS_SEC_IMPORT_KEY_ENCRYPTED:
importKeyEncrypted(apdu, buffer);
break;
case INS_SEC_READ_IOBUF:
readIOBuf(apdu, buffer);
break;
case INS_SEC_SET_LIFECYCLE:
setLifeCycle(apdu, buffer);
break;
case INS_SEC_SET_ISSUER_INFO:
setIssuerInfo(apdu, buffer);
break;
case INS_CREATE_OBJ:
CreateObject(apdu, buffer);
break;
case INS_WRITE_OBJ:
WriteObject(apdu, buffer);
break;
case INS_IMPORT_KEY:
ImportKey(apdu, buffer);
break;
case INS_COMPUTE_CRYPT:
ComputeCrypt(apdu, buffer);
break;
case INS_CREATE_PIN:
CreatePIN(apdu, buffer);
break;
case INS_DELETE_OBJ:
DeleteObject(apdu, buffer);
break;
case INS_READ_OBJ:
ReadObject(apdu, buffer);
break;
case INS_SEC_SET_BUILTIN_ACL:
setBuiltInACL(apdu, buffer);
break;
default:
ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
}
}
//
// **** Most processing starts here!!
//
public void process(APDU apdu)
{
if (selectingApplet())
ISOException.throwIt(ISO7816.SW_NO_ERROR);
if (!transientInit) {
initTransient();
}
if ( !cardResetProcessed[0] ) {
processCardReset();
}
authenticated_id = 0;
byte buffer[] = apdu.getBuffer();
byte cla = buffer[ISO7816.OFFSET_CLA];
switch (cla) {
case ISO7816.CLA_ISO7816:
case ISO7816.INS_SELECT: // right value, but right define?
return;
case CardEdge_CLA:
processCardEdgeAPDU(apdu,buffer);
break;
case CardManager_CLA:
initializeUpdate(apdu, buffer);
break;
case SECURE_CLA:
processSecureAPDU(apdu,buffer);
break;
default:
ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED);
}
}
}
--- NEW FILE MemoryManager.java ---
// MUSCLE SmartCard Development
// Authors: Tommaso Cucinotta <cucinotta(a)sssup.it>
// David Corcoran <corcoran(a)linuxnet.com>
// Ludovic Rousseau <ludovic.rousseau(a)free.fr>
// Jamie Nicolson <nicolson(a)netscape.com>
// Package: CardEdgeApplet
// Description: CardEdge implementation with JavaCard
// Protocol Authors: Tommaso Cucinotta <cucinotta(a)sssup.it>
// David Corcoran <corcoran(a)linuxnet.com>
// Modified:
// Eirik Herskedal <ehersked(a)cs.purdue.edu>
//
// BEGIN LICENSE BLOCK
// Copyright (c) 1999-2002 David Corcoran <corcoran(a)linuxnet.com>
// 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.nkey.applet;
import javacard.framework.Util;
/**
* Memory Manager class.
*
* <p>An instance of this class is capable of handling allocation and
* deallocation of chunks in a large Java byte array that is allocated
* once during the object instantiation.</p>
*
* <p>The Memory Manager allocates or frees memory chunks in the
* preallocated byte array on demand.</p>
*
* <p>No defragmentation is done, actually.</p>
*
* <p>Consecutive freed memory chunks are recompacted.</p>
*
* <p>Every allocation takes 2 more bytes to store the allocated block
* size, just before the allocated offset.</p>
*
* <p>A free memory block starts with a node (NODE_SIZE bytes):</p>
*
* <pre>
* short size;
* short next;
* </pre>
*
* @author Tommaso Cucinotta
* @author David Corcoran
* @author Ludovic Rousseau
* @version 0.9.9
*/
public class MemoryManager
{
/**
* Special offset value used as invalid offset
*/
public static final short NULL_OFFSET = -1;
private static final byte NODE_SIZE = 4;
private byte ptr[];
private short free_head;
/**
* Constructor for the MemoryManager class
*
* @param mem_size Size of the memory are to be allocated
*/
public MemoryManager(short mem_size)
{
ptr = null;
free_head = NULL_OFFSET;
Init(mem_size);
}
private void Init(short mem_size)
{
if(ptr != null)
{
return;
} else
{
ptr = new byte[mem_size];
Util.setShort(ptr, (short)0, mem_size);
Util.setShort(ptr, (short)2, (short)NULL_OFFSET);
free_head = 0;
return;
}
}
/**
* Allocate memory
*
* Each allocation takes actually a 2 bytes overhead.
*
* @param size Size of the memory block
* @return The offset at which allocated memory starts or
* NULL_OFFSET if an error occurred.
* @see #alloc(short)
* @see #freemem()
*/
public short alloc(short size)
{
short offset = free_head;
short prev = NULL_OFFSET;
size += 2;
if(size < NODE_SIZE)
size = NODE_SIZE;
short next_offset;
for(; offset != NULL_OFFSET; offset = next_offset)
{
short free_size = Util.getShort(ptr, offset);
next_offset = Util.getShort(ptr, (short)(offset + 2));
if(free_size >= size)
{
short remain = (short)(free_size - size);
if(remain >= NODE_SIZE)
{
Util.setShort(ptr, offset, remain);
} else
{
size = free_size;
remain = 0;
if(prev == NULL_OFFSET)
free_head = next_offset;
else
Util.setShort(ptr, (short)(prev + 2), next_offset);
}
Util.setShort(ptr, (short)(offset + remain), size);
return (short)(offset + remain + 2);
}
prev = offset;
}
return NULL_OFFSET;
}
/**
* Free a memory block
*
* <p>Consecutive free blocks are recompacted. Recompaction happens on
* free(). 4 cases are considered: don't recompact, recompact with
* next only, with previous only and with both of them.</p>
*
* @param offset The offset at which the memory block starts; it was
* returned from a previous call to {@link #alloc(short)}
*
* @see #alloc(short)
* @see #freemem()
*/
public void free(short offset)
{
offset -= 2;
short size = Util.getShort(ptr, offset);
short prev = NULL_OFFSET;
short base = free_head;
boolean found = false;
short node_next = 0;
for(; base != NULL_OFFSET; base = node_next)
{
node_next = Util.getShort(ptr, (short)(base + 2));
if(offset < base)
{
found = true;
break;
}
prev = base;
}
if(found && (short)(offset + size) == base)
{
size += Util.getShort(ptr, base);
Util.setShort(ptr, offset, size);
if(prev != NULL_OFFSET)
Util.setShort(ptr, (short)(prev + 2), node_next);
else
free_head = node_next;
base = node_next;
}
if(prev != NULL_OFFSET)
{
short prev_size = Util.getShort(ptr, prev);
if((short)(prev + prev_size) == offset)
{
Util.setShort(ptr, prev, (short)(prev_size + size));
} else
{
Util.setShort(ptr, (short)(offset + 2), base);
Util.setShort(ptr, (short)(prev + 2), offset);
}
} else
{
Util.setShort(ptr, (short)(offset + 2), base);
free_head = offset;
}
}
/**
* Get available free memory
*
* @return The total amount of available free memory, equal to the
* sum of all free fragments' sizes.
*
* @see #free(short)
* @see #alloc(short)
*/
public short freemem()
{
short offset = free_head;
short total = 0;
for(; offset != NULL_OFFSET; offset = Util.getShort(ptr, (short)(offset + 2)))
total = (short)((total + Util.getShort(ptr, offset)) - 2);
return total;
}
/**
* Get the size of a memory block
*
* @param offset The offset at which the memory block starts
*/
public short getBlockSize(short offset)
{
return (short)(Util.getShort(ptr, (short)(offset - 2)) - 2);
}
/**
* Retrieve the Java byte array containing all the memory contents.
*
* <p>To optimize, we don't use external buffers, but we directly
* copy from the memory array.</p>
*
* <p><b>Use this function only if really required.</b></p>
*
* @return The Java byte array containing all memory contents
*/
public byte[] getBuffer()
{
return ptr;
}
/**
* Read a byte value from memory
*
* @param base The complete memory location (offset) of the byte to
* read
* @return The byte value
*/
public byte getByte(short base)
{
return ptr[base];
}
/**
* Read a byte value from memory
*
* @param base The base memory location (offset) of the byte to read
* @param offset The offset of the byte (is added to the base
* parameter)
* @return The byte value
*/
public byte getByte(short base, short offset)
{
return ptr[(short)(base + offset)];
}
/**
* Copy a byte sequence from memory
*
* @param dst_bytes[] The destination byte array
* @param dst_offset The offset at which the sequence will be copied
* in dst_bytes[]
* @param src_base The base memory location (offset) of the source
* byte sequence
* @param src_offset The offset of the source byte sequence (is
* added to the src_base parameter)
* @param size The number of bytes to be copied
*/
public void getBytes(byte dst_bytes[], short dst_offset, short src_base,
short src_offset, short size)
{
Util.arrayCopy(ptr, (short)(src_base + src_offset),
dst_bytes, dst_offset, size);
}
/**
* Gets the size of the greatest chunk of available memory
*
* @return The size of the greatest free memory chunk, or zero if
* there is no free mem left
*/
public short getMaxSize()
{
short max_size = 2;
for(short base = free_head; base != NULL_OFFSET;
base = Util.getShort(ptr, (short)(base + 2)))
{
short size = Util.getShort(ptr, base);
if(size > max_size)
max_size = size;
}
return (short)(max_size - 2);
}
/**
* Read a short value from memory
*
* @param base The base memory location (offset) of the short to
* read
* @return The short value
*/
public short getShort(short base)
{
return Util.getShort(ptr, base);
}
/**
* Read a short value from memory
*
* @param base The base memory location (offset) of the short to
* read
* @param offset The offset of the short (is added to the base
* parameter)
* @return The short value
*/
public short getShort(short base, short offset)
{
return Util.getShort(ptr, (short)(base + offset));
}
/**
* Resize (only clamping is supported) a previously allocated memory
* chunk
*
* @param offset Memory offset as returned by alloc()
* @param new_size ew size of the memory block
* @return True if it was possible to realloc(), False otherwise
*
* @see #alloc(short)
* @see #free(short)
* @see #freemem()
*/
public boolean realloc(short offset, short new_size)
{
short actual_size = Util.getShort(ptr, (short)(offset - 2));
new_size += 2;
if(new_size < 3 || (short)(actual_size - new_size) < NODE_SIZE)
{
return false;
} else
{
Util.setShort(ptr, (short)(offset - 2), new_size);
Util.setShort(ptr, (short)((offset + new_size) - 2), (short)(actual_size - new_size));
free((short)(offset + new_size));
return true;
}
}
/**
* Set a byte value into memory
*
* @param base The complete memory location (offset) of the byte to
* set
* @param b The new byte value
*/
public void setByte(short base, byte b)
{
ptr[base] = b;
}
/**
* Set a byte value into memory
*
* @param base The base memory location (offset) of the byte to set
* @param offset The offset of the byte (is added to the base
* parameter)
* @param b The new byte value
*/
public void setByte(short base, short offset, byte b)
{
ptr[(short)(base + offset)] = b;
}
/**
* Copy a byte sequence into memory
*
* @param dst_base The base memory location (offset) of the
* destination byte sequence
* @param dst_offset The offset of the destination byte sequence (is
* added to the dst_base parameter)
* @param src_bytes[] The source byte array
* @param src_offset The offset at which the source sequence starts
* in src_bytes[]
* @param size The number of bytes to be copied
*/
public void setBytes(short dst_base, short dst_offset, byte src_bytes[], short src_offset, short size)
{
Util.arrayCopy(src_bytes, src_offset, ptr, (short)(dst_base + dst_offset), size);
}
/**
* Set a short value into memory
*
* @param base The complete memory location (offset) of the short to
* set
* @param b The short value
*/
public void setShort(short base, short b)
{
Util.setShort(ptr, base, b);
}
/**
* Set a short value into memory
*/
public void setShort(short base, short offset, short b)
{
Util.setShort(ptr, (short)(base + offset), b);
}
}
--- NEW FILE ObjectManager.java ---
// MUSCLE SmartCard Development
// Authors: Tommaso Cucinotta <cucinotta(a)sssup.it>
// David Corcoran <corcoran(a)linuxnet.com>
// Ludovic Rousseau <ludovic.rousseau(a)free.fr>
// Jamie Nicolson <nicolson(a)netscape.com>
// Package: CardEdgeApplet
// Description: CardEdge implementation with JavaCard
// Protocol Authors: Tommaso Cucinotta <cucinotta(a)sssup.it>
// David Corcoran <corcoran(a)linuxnet.com>
// Modified:
// Eirik Herskedal <ehersked(a)cs.purdue.edu>
//
// BEGIN LICENSE BLOCK
// Copyright (C) 1999-2002 David Corcoran <corcoran(a)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.nkey.applet;
import javacard.framework.ISOException;
import javacard.framework.Util;
// Referenced classes of package com.redhat.nkey.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);
}
}