// 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>
// Robert Relyea <rrelyea@redhat.com>
// Nelson Bolyard <nelsonb@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.*;
import javacard.security.*;
import javacardx.crypto.Cipher;
import visa.openplatform.ProviderSecurityDomain;
import visa.openplatform.OPSystem;
// Referenced classes of package com.redhat.ckey.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 = 24;
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 = 4;
private static final short BUILDID_MAJOR = (short) 0x5261;
private static final short BUILDID_MINOR = (short) 0x7c4e;
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;
private static final byte INS_UNBLOCK_PIN = (byte)0x46;
private static final byte INS_GET_CHALLENGE = (byte)0x62;
private static final byte INS_CAC_EXT_AUTH = (byte)0x38;
private static final byte INS_LOGOUT_ALL = (byte)0x60;
/* public */
private static final byte INS_VERIFY_PIN = (byte)0x42;
private static final byte INS_LIST_OBJECTS = (byte)0x58;
private static final byte INS_LIST_PINS = (byte)0x48;
private static final byte INS_LIST_KEYS = (byte)0x3A;
private static final byte INS_GET_STATUS = (byte)0x3C;
private static final byte INS_GET_LIFECYCLE = (byte)0xF2;
private static final byte INS_GET_ISSUER_INFO = (byte)0xF6;
private static final byte INS_GET_BUILDID = (byte)0x70;
private static final byte INS_NOP = (byte)0x71;
private static final byte INS_GET_RANDOM = (byte)0x72;
private static final byte INS_SEED_RANDOM = (byte)0x73;
private static final byte INS_GET_BUILTIN_ACL = (byte)0xFA;
/* nonce validated only */
private static final byte INS_LOGOUT = (byte)0x61;
/* nonce validated & Secure Channel */
private static final byte INS_IMPORT_KEY = (byte)0x32;
private static final byte INS_COMPUTE_CRYPT = (byte)0x36;
private static final byte INS_CREATE_PIN = (byte)0x40;
private static final byte INS_CHANGE_PIN = (byte)0x44;
private static final byte INS_CREATE_OBJ = (byte)0x5A;
private static final byte INS_DELETE_OBJ = (byte)0x52;
private static final byte INS_READ_OBJ = (byte)0x56;
private static final byte INS_WRITE_OBJ = (byte)0x54;
/* Secure channel only */
private static final byte INS_INIT_UPDATE = (byte)0x50;
private static final byte INS_SEC_EXT_AUTH = (byte)0x82;
private static final byte INS_SEC_SET_LIFECYCLE = (byte)0xF0;
private static final byte INS_SEC_SET_ISSUER_INFO = (byte)0xF4;
private static final byte INS_SEC_SET_BUILTIN_ACL = (byte)0xF8;
private static final byte INS_SEC_SET_PIN = (byte)0x04;
private static final byte INS_SEC_READ_IOBUF = (byte)0x08;
private static final byte INS_SEC_IMPORT_KEY_ENCRYPTED = (byte)0x0A;
private static final byte INS_SEC_START_ENROLLMENT = (byte)0x0C;
// * There have been memory problems on the card
private static final short SW_NO_MEMORY_LEFT = (short)0x9C01;
// * Entered PIN is not correct
private static final short SW_AUTH_FAILED = (short)0x9C02;
// * Required operation is not allowed in actual circumstances
private static final short SW_OPERATION_NOT_ALLOWED = (short)0x9C03;
// * Required feature is not (yet) supported
private static final short SW_UNSUPPORTED_FEATURE = (short)0x9C05;
// * Required operation was not authorized because of a lack of privileges
private static final short SW_UNAUTHORIZED = (short)0x9C06;
// * Required object is missing
private static final short SW_OBJECT_NOT_FOUND = (short)0x9C07;
// * New object ID already in use
private static final short SW_OBJECT_EXISTS = (short)0x9C08;
// * Algorithm specified is not correct
private static final short SW_INCORRECT_ALG = (short)0x9C09;
// * Verify operation detected an invalid signature
private static final short SW_SIGNATURE_INVALID = (short)0x9C0B;
// * Operation has been blocked for security reason
private static final short SW_IDENTITY_BLOCKED = (short)0x9C0C;
// * Unspecified Applet error
private static final short SW_UNSPECIFIED_ERROR = (short)0x9C0D;
// * Invalid input parameter to command
private static final short SW_INVALID_PARAMETER = (short)0x9C0F;
// * Incorrect P1 parameter
private static final short SW_INCORRECT_P1 = (short)0x9C10;
// * Incorrect P2 parameter
private static final short SW_INCORRECT_P2 = (short)0x9C11;
// * No more data available
private static final short SW_SEQUENCE_END = (short)0x9C12;
// * Cipher Direction given is not supported for this Operation
private static final short SW_DIRECTION_UNSUPPORTED = (short)0x9C13;
// * Cipher Direction invalid, unrecognized
private static final short SW_DIRECTION_INVALID = (short)0x9C14;
// * Data Location given is not supported for this Operation
private static final short SW_LOCATION_UNSUPPORTED = (short)0x9C15;
// * Data Location invalid, unrecognized
private static final short SW_LOCATION_INVALID = (short)0x9C16;
// * Key Type given is not supported for this Operation and Direction
private static final short SW_KEY_TYPE_UNSUPPORTED = (short)0x9C17;
// * Key Type invalid, unrecognized
private static final short SW_KEY_TYPE_INVALID = (short)0x9C18;
// * Data Chunk Size Invalid
private static final short SW_DATA_CHUNK_SIZE_ERROR = (short)0x9C19;
// * Key Size Invalid
private static final short SW_KEY_SIZE_ERROR = (short)0x9C1A;
// * Cipher Mode given is not supported for this Operation and Direction
private static final short SW_CIPH_MODE_UNSUPPORTED = (short)0x9C1B;
// * Cipher Mode invalid, unrecognized
private static final short SW_CIPH_MODE_INVALID = (short)0x9C1C;
// * Output space insufficient to hold result.
private static final short SW_OUT_BUF_TOO_SMALL = (short)0x9C1D;
// * Key slots already assigned with different pairing
private static final short SW_INCONSTANT_KEYPAIRING = (short)0x9C1E;
// * Input space insufficient to decode result.
private static final short SW_IN_BUF_TOO_SMALL = (short)0x9C1F;
// * Wrapped Key failed verify
private static final short SW_BAD_WRAPPED_KEY = (short)0x9C20;
// * Wrapped Key failed verify
private static final short SW_BAD_ALGID_FOR_KEY = (short)0x9C21;
// * Wrapped Key failed verify
private static final short SW_BAD_WRAPPED_PRIV_KEY = (short)0x9C22;
// * For debugging purposes
private static final short SW_INTERNAL_ERROR = (short)0x9CFF;
private static final byte ALG_RSA = 0;
private static final byte ALG_RSA_CRT = 1;
private static final byte ALG_DSA = 2;
private static final byte ALG_DES = 3;
private static final byte ALG_3DES = 4;
private static final byte ALG_3DES3 = 5;
private static final byte KEY_RSA_PUBLIC = 1;
private static final byte KEY_RSA_PRIVATE = 2;
private static final byte KEY_RSA_PRIVATE_CRT = 3;
private static final byte KEY_DSA_PUBLIC = 4;
private static final byte KEY_DSA_PRIVATE = 5;
private static final byte KEY_DES = 6;
private static final byte KEY_3DES = 7;
private static final byte KEY_3DES3 = 8;
private static final byte KEY_RSA_PKCS8_PAIR = 9;
private static final byte BLOB_ENC_PLAIN = 0;
private static final byte OP_INIT = 1;
private static final byte OP_PROCESS = 2;
private static final byte OP_FINALIZE = 3;
private static final byte OP_ONE_STEP = 4;
private static final byte CD_SIGN = 1;
private static final byte CD_VERIFY = 2;
private static final byte CD_ENCRYPT = 3;
private static final byte CD_DECRYPT = 4;
private static final byte CM_RSA_NOPAD = 0;
private static final byte CM_RSA_PAD_PKCS1 = 1;
private static final byte CM_DSA_SHA = 16;
private static final byte CM_DES_CBC_NOPAD = 32;
private static final byte CM_DES_ECB_NOPAD = 33;
private static final byte DL_APDU = 1;
private static final byte DL_OBJECT = 2;
/**
* List option
*/
private static final byte LIST_OPT_RESET = 0;
private static final byte LIST_OPT_NEXT = 1;
private static final byte OPT_DEFAULT = 0;
private static final byte OPT_RSA_PUB_EXP = 1;
private static final byte OPT_DSA_GPQ = 2;
private static final short OFFSET_GENKEY_ALG = 5;
private static final short OFFSET_GENKEY_SIZE = 6;
private static final short OFFSET_GENKEY_PRV_ACL = 8;
private static final short OFFSET_GENKEY_PUB_ACL = 14;
private static final short OFFSET_GENKEY_OPTIONS = 20;
private static final short OFFSET_GENKEY_RSA_PUB_EXP_LENGTH = 21;
private static final short OFFSET_GENKEY_RSA_PUB_EXP_VALUE = 23;
private static final short OFFSET_GENKEY_DSA_GPQ = 21;
private static final short KEYBLOB_OFFSET_ENCODING = 0;
private static final short KEYBLOB_OFFSET_KEY_TYPE = 1;
private static final short KEYBLOB_OFFSET_KEY_SIZE = 2;
private static final short KEYBLOB_OFFSET_KEY_DATA = 4;
private static final short WRAPKEY_OFFSET_TYPE = 4;
private static final short WRAPKEY_OFFSET_SIZE = 5;
private static final short WRAPKEY_OFFSET_DATA = 6;
private static final short OFFSET_IMP_KEY_ENC_WRAP_KEY = 5;
private static final short MAX_RSA_MOD_BITS = 2048;
private static final short MAX_RSA_MOD_BYTES = 256;
// 554 = 2 bytes for explicit length,
// 512 bytes for data
// 40 bytes for two sha digest buffers.
//private static final short IOBUF_ALLOC = 554;
private static final short IOBUF_ALLOC = 900;
// offsets in iobuf used by CryptProcessFinal()
private static final short VFY_OFF = 450;
private static final short VFY_MD_0 = 714;
private static final short VFY_MD_1 = 734;
// how many ms to delay when a bad password is detected
private static final short BAD_PASSWD_DELAY = 1000;
// PKCS #8 RSA oid.
private static final byte pkcs8_RSA_oid[] = {
0x06, 0x09, // OID tag (9 bytes)
0x2a, (byte)0x86, 0x48, (byte)0x86, (byte)0xf7, 0x0d, 0x01, 0x01, 0x01
};
private static final short pkcs8_RSA_oid_size = 11;
// PKCS #1 SHA1 encoding header (DER).
private static final byte sha1encode[] = {
// SEQUENCE 33 bytes
0x30, 0x21,
// alogirthm ID (Sequence, 9 bytes)
0x30, 0x09,
// OID tag (5 bytes)
0x06, 0x05,
// sha1 oid 1.3.14.3.2.26
0x2b, 0x0e, 0x03, 0x02, 0x1a,
// paremeter = NULL
0x05, 0x00,
// the actual hash (OCTECT, 20 bytes)
0x04, 0x14
// Hash goes here
};
private static final short sha1encodeLen = 15;
/**
* Instance variable primitive declarations ALL PERSISTENT MEMORY
*/
private byte pinEnabled;
private byte isWritable;
private short create_object_ACL;
private short create_key_ACL;
private short create_pin_ACL;
private byte enable_ACL_change;
private MessageDigest shaDigest;
private boolean transientInit;
private RandomData randomGenerator;
private Cipher des;
private ASN1 asn1;
/* these values candidates for Transient objects */
private short authenticated_id; /* high */
private short nonce_ids; /* high */
private short iobuf_size; /* medium */
private byte key_it; /* low */
private byte channelID; /* low */
/**
* Instance variable objects and array declarations - PERSISTENT
* Allocated by "new" calls below.
*/
private MemoryManager mem;
private ObjectManager om;
private Cipher[] ciphers; // persistent
private KeyPair[] keyPairs; // persistent
private Key[] keys; // persistent
private byte[] keyMate; // persistent
private OwnerPIN[] pins; // persistent
private Signature[] signatures; // persistent
private byte[] default_nonce; // persistent
private byte[] keyACLs; // persistent
private byte[] keyTries; // persistent
private byte[] issuerInfo; // persistent
/**
* Instance variable array declarations - TRANSIENT
* Allocated by JCSystem.makeTransientXxxxxArray calls below.
*/
private boolean[] cardResetProcessed; // transient 1-entry array
private byte[] ciph_dirs; // transient
private byte[] iobuf; // transient
private byte[] nonce; // transient
private short[] loginCount; // transient
private CardEdge(byte bArray[], short bOffset, byte bLength)
{
//
// In the future, we may want to allow the RA to change these
// values on the fly?
//
// initialize the parameters to their default values
//Save offset of the instance aid length.
byte remainingLength = bLength;
short mem_size = (short)5000;
create_object_ACL = RA_ACL;
create_key_ACL = RA_ACL;
create_pin_ACL = RA_ACL;
enable_ACL_change = 0; // can't change ACLs by default
pins = new OwnerPIN [MAX_NUM_PINS];
keys = new Key [MAX_NUM_KEYS];
keyMate = new byte [MAX_NUM_KEYS];
keyACLs = new byte [MAX_NUM_KEYS * KEY_ACL_SIZE];
keyTries = new byte [MAX_NUM_KEYS];
keyPairs = new KeyPair [MAX_NUM_KEYS];
ciphers = new Cipher [MAX_NUM_KEYS];
signatures = new Signature [MAX_NUM_KEYS];
default_nonce = new byte [NONCE_SIZE];
issuerInfo = new byte [ISSUER_INFO_SIZE];
for (byte i = 0; i < MAX_NUM_KEYS; i++) {
keyTries[i] = MAX_KEY_TRIES;
keyMate[i] = -1;
}
Util.arrayFillNonAtomic(default_nonce, ZEROS, NONCE_SIZE, ZEROB);
Util.arrayFillNonAtomic(issuerInfo, ZEROS,ISSUER_INFO_SIZE, ZEROB);
byte appDataLen = 0;
byte issuerLen = 0;
//Attempt to parse app specific initialization data
//Format
// n bytes- System header data, skip past to get to app data
// App specific data
// 1 byte - Issuer Info Len
// n bytes- Issuer Info Value, string of characters
// 1 byte - Custem memory size Len, agree upon 2
// 2 bytes- Custom memory size value
// 1 byte - Applet bit mask Len, now only handle 1, allow for more in future
// n bytes- Applet bit mask value. Process 1 now.
//Skip past the header data provided by the system
//Get instance AID length
byte len = bArray[bOffset];
short customMemSize = 0;
do {
if(remainingLength <= 0) {
break;
}
if( len > remainingLength) {
break;
}
//Skip past the instance AID
//
bOffset+= len + 1;
remainingLength-= (len + 1);
if(remainingLength <= 0) {
break;
}
//Get app privileges length
len = bArray[bOffset];
if(len > remainingLength) {
break;
}
//Skip past the app privileges
bOffset+= len + 1;
remainingLength-=(len + 1);
if(remainingLength <= 0) {
break;
}
//Get length of the entire application specific data block
appDataLen = bArray[bOffset];
if( appDataLen > remainingLength)
{
break;
}
bOffset +=1;
remainingLength=appDataLen;
if(remainingLength <= 0) {
break;
}
//Get the issuer info length
issuerLen = bArray[bOffset];
if(issuerLen > remainingLength) {
break;
}
bOffset++;
remainingLength--;
if(remainingLength <= 0) {
break;
}
//Actually copy our issuer info data
if(issuerLen != 0) {
if((short) issuerLen < ISSUER_INFO_SIZE) {
Util.arrayCopyNonAtomic(bArray,bOffset,issuerInfo,ZEROB,issuerLen);
} else {
break;
}
}
//skip past the issuer data if any
bOffset+= issuerLen ;
remainingLength-= issuerLen;
if(remainingLength <= 0) {
break;
}
//Get memory size length
byte memSizeLen= bArray[bOffset];
if(memSizeLen > remainingLength) {
break;
}
bOffset+=1;
remainingLength-=1;
if(remainingLength <= 0) {
break;
}
//allow configuration of the mem_size, since it can't be changed
//on the fly.
//Get memory size from next block,assume 2 bytes
if(memSizeLen == 2) {
customMemSize = Util.makeShort(bArray[bOffset],bArray[(short) (bOffset + 1)]);
bOffset += 2;
remainingLength-=2;
}
//Sanity check the mem size
if(customMemSize > 0) {
mem_size = (short) customMemSize ;
}
if(remainingLength <= 0) {
break;
}
//obtain the applet bit mask to alter the behavior as needed
//only pay attention to first byte now.
byte appletBitMask = 0;
byte bitMaskLen = 0;
bitMaskLen = bArray[bOffset];
if(bitMaskLen > remainingLength) {
break;
}
if(bitMaskLen > 0) {
bOffset += 1;
remainingLength-=1;
appletBitMask = bArray[bOffset];
}
if(remainingLength <= 0) {
break;
}
//The first thing in the bitmask we support is allowing the change of ACL's
if(appletBitMask != 0) {
enable_ACL_change = (byte)(appletBitMask & 0x1);
}
} while(false);
//Memory Management data instantiation
mem = new MemoryManager(mem_size);
om = new ObjectManager(mem);
authenticated_id = 0;
nonce_ids = 0;
pinEnabled = 0;
isWritable = 0;
randomGenerator = null;
shaDigest = null;
des = null;
transientInit = false;
}
private void delay(short delayTime) {
short i;
if (randomGenerator == null) {
randomGenerator =
RandomData.getInstance(RandomData.ALG_SECURE_RANDOM);
}
delayTime = (short)(delayTime >> 2);
for (i=0; i < delayTime; i++) {
/* take about 4 ms */
randomGenerator.generateData(iobuf, VFY_MD_1, (short)4);
}
}
private void ChangePIN(APDU apdu, byte buffer[])
{
byte pin_nb = buffer[ISO7816.OFFSET_P1];
if (pin_nb < 0 || pin_nb >= MAX_NUM_PINS)
ISOException.throwIt(SW_INCORRECT_P1);
OwnerPIN pin = pins[pin_nb];
if (pin == null)
ISOException.throwIt(SW_INCORRECT_P1);
if (buffer[ISO7816.OFFSET_P2] != 0)
ISOException.throwIt(SW_INCORRECT_P2);
short avail = Util.makeShort(ZEROB, buffer[ISO7816.OFFSET_LC]);
if (apdu.setIncomingAndReceive() != avail)
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
if (avail < 4)
ISOException.throwIt(SW_INVALID_PARAMETER);
byte pin_size = buffer[ISO7816.OFFSET_CDATA];
if (avail < (short)(1 + pin_size + 1))
ISOException.throwIt(SW_INVALID_PARAMETER);
if (!CheckPINPolicy(buffer, (short)6, pin_size))
ISOException.throwIt(SW_INVALID_PARAMETER);
byte new_pin_size = buffer[(short)(6 + pin_size)];
if (avail < (short)(1 + pin_size + 1 + new_pin_size))
ISOException.throwIt(SW_INVALID_PARAMETER);
if (!CheckPINPolicy(buffer, (short)(6 + pin_size + 1), new_pin_size))
ISOException.throwIt(SW_INVALID_PARAMETER);
if (pin.getTriesRemaining() == 0)
ISOException.throwIt(SW_IDENTITY_BLOCKED);
if (!pin.check(buffer, (short)6, pin_size))
{
LogoutAllIdentity(pin_nb);
delay(BAD_PASSWD_DELAY);
ISOException.throwIt(SW_AUTH_FAILED);
}
pin.update(buffer, (short)(6 + pin_size + 1), new_pin_size);
LogoutAllIdentity(pin_nb);
}
/**
* Checks if PIN policies are satisfied for a PIN code
*/
private boolean CheckPINPolicy(byte pin_buffer[], short pin_offset,
byte pin_size)
{
return pin_size >= pinMinSize && pin_size <= pinMaxSize;
}
/***********************************************************************/
private void doDigest(byte[] inBuf, short inOff, short inLen,
byte[] outBuf, short outOff)
{
if (shaDigest == null) {
shaDigest = MessageDigest.getInstance(MessageDigest.ALG_SHA, false);
}
shaDigest.reset();
shaDigest.doFinal(inBuf, inOff, inLen, outBuf, outOff);
}
/***********************************************************************
* Subroutines for ComputeCrypt APDU handler
*/
private void CryptInit(APDU apdu, byte buffer[], short bytesLeft)
{
if (bytesLeft < 3) {
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
return;
}
byte key_nb = buffer[ISO7816.OFFSET_P1];
byte op = buffer[ISO7816.OFFSET_P2];
byte ciph_mode = buffer[ISO7816.OFFSET_CDATA];
byte ciph_dir = buffer[ISO7816.OFFSET_CDATA+1];
byte data_location = buffer[ISO7816.OFFSET_CDATA+2];
byte[] src_buf;
short src_base;
short src_avail;
switch(data_location) {
case DL_APDU:
src_buf = buffer;
src_base = ISO7816.OFFSET_CDATA + 3;
src_avail = (short)(bytesLeft - 3);
break;
case DL_OBJECT:
src_buf = iobuf;
src_base = 0;
src_avail = iobuf_size;
break;
default:
ISOException.throwIt(SW_LOCATION_INVALID);
return;
}
if (src_avail < 2) {
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
return;
}
short size = Util.getShort(src_buf, src_base);
if (src_avail < (short)(2 + size)) {
ISOException.throwIt(SW_DATA_CHUNK_SIZE_ERROR);
return;
}
Key key = keys[key_nb];
ciph_dirs[key_nb] = ciph_dir;
switch(ciph_dir) {
case CD_SIGN:
case CD_VERIFY:
ISOException.throwIt(SW_DIRECTION_UNSUPPORTED);
return;
case CD_ENCRYPT:
case CD_DECRYPT:
{
byte ciph_alg_id;
byte keyType = key.getType();
boolean ignoreSize = false;
switch (keyType) {
case KeyBuilder.TYPE_RSA_PUBLIC:
case KeyBuilder.TYPE_RSA_PRIVATE:
case KeyBuilder.TYPE_RSA_CRT_PRIVATE:
if (op == OP_ONE_STEP) {
size = 0; // ignore input buffer
} else if (size != 0) {
ISOException.throwIt(SW_DATA_CHUNK_SIZE_ERROR);
return;
}
if (key.getSize() > MAX_RSA_MOD_BITS) {
ISOException.throwIt(SW_KEY_SIZE_ERROR);
return;
}
if (ciph_mode == CM_RSA_NOPAD) {
ciph_alg_id = Cipher.ALG_RSA_NOPAD;
} else if (ciph_mode == CM_RSA_PAD_PKCS1) {
// ciph_alg_id = Cipher.ALG_RSA_PKCS1;
ISOException.throwIt(SW_CIPH_MODE_UNSUPPORTED);
return;
} else {
ISOException.throwIt(SW_CIPH_MODE_INVALID);
return;
}
break;
case KeyBuilder.TYPE_DES:
// XXX Check the validity of "size" here.
// if (ciph_mode == CM_DES_CBC_NOPAD) {
// ciph_alg_id = Cipher.ALG_DES_CBC_NOPAD;
// } else if (ciph_mode == CM_DES_ECB_NOPAD) {
// ciph_alg_id = Cipher.ALG_DES_ECB_NOPAD;
// } else {
// ISOException.throwIt(SW_CIPH_MODE_INVALID);
// return;
// }
// break;
case KeyBuilder.TYPE_DSA_PUBLIC:
case KeyBuilder.TYPE_DSA_PRIVATE:
ISOException.throwIt(SW_KEY_TYPE_UNSUPPORTED);
return;
default:
ISOException.throwIt(SW_KEY_TYPE_INVALID);
return;
}
Cipher ciph = getCipher(key_nb, ciph_alg_id);
if (size == 0)
ciph.init(key, (byte)(ciph_dir == CD_ENCRYPT
? Cipher.MODE_ENCRYPT : Cipher.MODE_DECRYPT));
else
ciph.init(key, (byte)(ciph_dir == CD_ENCRYPT
? Cipher.MODE_ENCRYPT : Cipher.MODE_DECRYPT),
src_buf, (short)(src_base + 2), size);
}
break;
default:
ISOException.throwIt(SW_DIRECTION_INVALID);
break;
}
}
private boolean EnDeCryptProcessFinal(APDU apdu, byte buffer[],
short bytesLeft)
{
byte key_nb = buffer[ISO7816.OFFSET_P1];
byte op = buffer[ISO7816.OFFSET_P2];
byte ciph_dir = ciph_dirs[key_nb];
Key key = keys[key_nb];
Cipher ciph = ciphers[key_nb];
byte data_location;
byte[] src_buf;
byte[] dst_buf;
short src_base = ISO7816.OFFSET_CDATA;
short dst_base = 2;
short src_avail;
short dst_len;
short dst_avail;
boolean doubleCheck = false;
if (ciph == null) {
ISOException.throwIt(SW_INCORRECT_P1);
return false;
}
if (op == OP_ONE_STEP) {
src_base += 2;
bytesLeft -= 2;
}
// Did the APDU include enough data for the data_location?
if (bytesLeft < 1) {
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
return false;
}
data_location = buffer[ src_base ];
switch(data_location) {
case DL_APDU: // write directy to the APDU
src_buf = buffer;
dst_buf = buffer;
src_base += 1; // starts right after data_location
src_avail = (short)(bytesLeft - 1);
dst_avail = 255; // usable bytes in APDU
break;
case DL_OBJECT: // use heap object
src_buf = iobuf;
dst_buf = iobuf;
src_base = 0;
src_avail = iobuf_size;
dst_avail = 258; // 2 byte length + 256 byte data
break;
default:
ISOException.throwIt(SW_LOCATION_INVALID);
return false;
}
if (src_avail < 2) {
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
return false;
}
short size = Util.getShort(src_buf, src_base);
src_base += 2;
if (src_avail < (short)(2 + size) || size < 0) {
ISOException.throwIt(SW_DATA_CHUNK_SIZE_ERROR);
return false;
}
// Now, check size against key length.
byte keyType = key.getType();
switch (keyType) {
case KeyBuilder.TYPE_RSA_CRT_PRIVATE:
if (ciph_dir == CD_ENCRYPT)
doubleCheck = true;
// fall thru
case KeyBuilder.TYPE_RSA_PUBLIC:
case KeyBuilder.TYPE_RSA_PRIVATE:
{
if (op == OP_PROCESS) {
ISOException.throwIt(SW_KEY_TYPE_UNSUPPORTED); // ambiguous?
return false;
}
short keyBits = key.getSize();
if (keyBits > MAX_RSA_MOD_BITS) {
ISOException.throwIt(SW_KEY_SIZE_ERROR);
return false;
}
dst_len = (short)((short)(keyBits + 7) / 8) ;
byte ciph_alg_id = ciph.getAlgorithm();
// If we're not using padding, or if this is a decryption,
// then the input buffer must be the same size as the modulus.
if (ciph_alg_id == Cipher.ALG_RSA_NOPAD ||
ciph_dir == CD_DECRYPT) {
if (size != dst_len) {
ISOException.throwIt(SW_DATA_CHUNK_SIZE_ERROR);
return false;
}
}
if (ciph_alg_id == Cipher.ALG_RSA_PKCS1) {
if (ciph_dir == CD_ENCRYPT) {
// For encryption with PKCS#1 padding, the input buffer
// mustbe at least 11 bytes shorter than the modulus.
if (size > (short)(dst_len - 11)) {
ISOException.throwIt(SW_DATA_CHUNK_SIZE_ERROR);
return false;
}
} else {
// For decryption with PKCS#1 padding, the output
// will be at least 11 bytes shorter than input.
dst_len -= 11;
}
}
// make sure there's enough room for the output.
if ((short)(2 + dst_len) > dst_avail) {
ISOException.throwIt(SW_OUT_BUF_TOO_SMALL);
return false;
}
}
break;
case KeyBuilder.TYPE_DSA_PUBLIC:
case KeyBuilder.TYPE_DSA_PRIVATE:
ISOException.throwIt(SW_KEY_TYPE_UNSUPPORTED);
return false;
default:
ISOException.throwIt(SW_KEY_TYPE_INVALID);
return false;
}
if (op == OP_PROCESS) {
dst_len = ciph.update(src_buf, src_base, size,
dst_buf, dst_base);
} else {
if (doubleCheck) {
doDigest(src_buf, src_base, size, iobuf, VFY_MD_0);
}
dst_len = ciph.doFinal(src_buf, src_base, size,
dst_buf, dst_base);
if (doubleCheck) {
// Use the public key to decrypt
Key pubkey = keys[(short)keyMate[key_nb]];
// setup cipher object for decrypt
ciph.init(pubkey, Cipher.MODE_DECRYPT);
// do a decrypt to the original source buffer
short newsize = ciph.doFinal(dst_buf, dst_base, dst_len,
iobuf, VFY_OFF);
// should get back result of same length as original input
if (size != newsize) {
ISOException.throwIt(SW_INTERNAL_ERROR); // unambiguous
return false;
}
doDigest(iobuf, VFY_OFF, size, iobuf, VFY_MD_1);
// compare checksums of original and decrypted sources
if (0 != Util.arrayCompare(iobuf, VFY_MD_0,
iobuf, VFY_MD_1, (short)20 )) {
ISOException.throwIt(SW_SIGNATURE_INVALID); // unambiguous
return false;
}
}
}
Util.setShort(dst_buf, ZEROS, dst_len);
dst_len += 2;
if (data_location == DL_OBJECT) {
iobuf_size = dst_len;
} else {
if( dst_len > dst_avail ) {
ISOException.throwIt(SW_OUT_BUF_TOO_SMALL);
return false;
}
apdu.setOutgoingAndSend(ZEROS, dst_len);
}
return false;
}
private boolean CryptProcessFinal(APDU apdu, byte buffer[], short bytesLeft)
{
byte key_nb = buffer[ISO7816.OFFSET_P1];
byte ciph_dir = ciph_dirs[key_nb];
switch(ciph_dir) {
case CD_SIGN:
case CD_VERIFY:
ISOException.throwIt(SW_DIRECTION_UNSUPPORTED);
break;
case CD_ENCRYPT:
case CD_DECRYPT:
return EnDeCryptProcessFinal(apdu, buffer, bytesLeft);
default:
ISOException.throwIt(SW_DIRECTION_INVALID);
break;
}
return false;
}
/***********************************************************************
* APDU handlers
*/
private void ComputeCrypt(APDU apdu, byte buffer[])
{
short bytesLeft = (short)(0xff & buffer[ISO7816.OFFSET_LC]);
byte key_nb = buffer[ISO7816.OFFSET_P1];
if (key_nb < 0 || key_nb >= MAX_NUM_KEYS || keys[key_nb] == null) {
ISOException.throwIt(SW_INCORRECT_P1);
}
if (!authorizeKeyUse(key_nb)) {
ISOException.throwIt(SW_UNAUTHORIZED);
}
byte op = buffer[ISO7816.OFFSET_P2];
boolean repeat = false;
switch(op) {
case OP_INIT:
CryptInit(apdu, buffer, bytesLeft);
break;
case OP_PROCESS:
case OP_FINALIZE:
do {
repeat = CryptProcessFinal(apdu, buffer, bytesLeft);
} while (repeat);
break;
case OP_ONE_STEP:
CryptInit(apdu, buffer, bytesLeft);
repeat = CryptProcessFinal(apdu, buffer, bytesLeft);
break;
default:
ISOException.throwIt(SW_INCORRECT_P2);
break;
}
}
private void CreateObject(APDU apdu, byte buffer[])
{
short bytesLeft = Util.makeShort(ZEROB, buffer[ISO7816.OFFSET_LC]);
boolean forceCreate = ((authenticated_id & RA_ACL) == RA_ACL);
if ((authenticated_id & create_object_ACL) == 0)
ISOException.throwIt(SW_UNAUTHORIZED);
if (bytesLeft != 14)
ISOException.throwIt(SW_INVALID_PARAMETER);
if (buffer[ISO7816.OFFSET_P1] != 0)
ISOException.throwIt(SW_INCORRECT_P1);
short obj_class = Util.getShort(buffer, ISO7816.OFFSET_CDATA);
short obj_id = Util.getShort(buffer, (short)(ISO7816.OFFSET_CDATA+2));
if (Util.getShort(buffer, (short)(ISO7816.OFFSET_CDATA+4)) != 0 ||
buffer[ISO7816.OFFSET_CDATA+6] < 0)
ISOException.throwIt(SW_NO_MEMORY_LEFT);
short objlen = Util.getShort(buffer, (short)(ISO7816.OFFSET_CDATA+6));
if( objlen <= 0 )
ISOException.throwIt(SW_INVALID_PARAMETER);
if( obj_class == (short)0xffff && (obj_id == (short)0xffff ||
obj_id == (short)0xfffe ) ) {
// I/O buffer
if( objlen > IOBUF_ALLOC )
ISOException.throwIt(SW_NO_MEMORY_LEFT);
iobuf_size = objlen;
} else {
if (om.exists(obj_class, obj_id)) {
if( forceCreate ) {
om.destroyObject(obj_class, obj_id, true);
} else {
ISOException.throwIt(SW_OBJECT_EXISTS);
}
}
short size = Util.getShort(buffer, (short)(ISO7816.OFFSET_CDATA+6));
om.createObject(obj_class, obj_id, size,
buffer, (short)(ISO7816.OFFSET_CDATA+8));
}
}
private void CreatePIN(APDU apdu, byte buffer[])
{
byte pin_nb = buffer[ISO7816.OFFSET_P1];
byte num_tries = buffer[ISO7816.OFFSET_P2];
if ((authenticated_id & create_pin_ACL) == 0 )
ISOException.throwIt(SW_UNAUTHORIZED);
if (pin_nb < 0 || pin_nb >= MAX_NUM_PINS || pins[pin_nb] != null)
ISOException.throwIt(SW_INCORRECT_P1);
byte avail = buffer[ISO7816.OFFSET_LC];
if (avail < 1)
ISOException.throwIt(SW_INVALID_PARAMETER);
if (!CheckPINPolicy(buffer, ISO7816.OFFSET_CDATA, avail))
ISOException.throwIt(SW_INVALID_PARAMETER);
pins[pin_nb] = new OwnerPIN(num_tries, pinMaxSize);
pins[pin_nb].update(buffer, ISO7816.OFFSET_CDATA, avail);
pinEnabled = 1;
}
private void DeleteObject(APDU apdu, byte buffer[])
{
if (buffer[ISO7816.OFFSET_P1] != 0)
ISOException.throwIt(SW_INCORRECT_P1);
if (buffer[ISO7816.OFFSET_P2] != 0 && buffer[ISO7816.OFFSET_P2] != 1)
ISOException.throwIt(SW_INCORRECT_P2);
short bytesLeft = Util.makeShort(ZEROB, buffer[ISO7816.OFFSET_LC]);
if (bytesLeft != 4)
ISOException.throwIt(SW_INVALID_PARAMETER);
short obj_class = Util.getShort(buffer, ISO7816.OFFSET_CDATA);
short obj_id = Util.getShort(buffer, (short)(ISO7816.OFFSET_CDATA+2));
if( obj_class == -1 && (obj_id == -1 || obj_id == -2) ) {
// I/O Buffer
iobuf_size = 0;
} else {
short base = om.getBaseAddress(obj_class, obj_id);
if (base == -1)
ISOException.throwIt(SW_OBJECT_NOT_FOUND);
if (!om.authorizeDeleteFromAddress(base, authenticated_id))
ISOException.throwIt(SW_UNAUTHORIZED);
om.destroyObject(obj_class, obj_id, buffer[ISO7816.OFFSET_P2] == 1);
}
}
// Get the applet available memory
private void GetMemory(APDU apdu, byte buffer[])
{
// short pos = 0;
//
// // Sigh, these functions are appearantly not in the Axalto runtime
//
// // Total object memory
// // Persistant
// Util.setShort(buffer, pos, ZEROS);
// pos += 2;
// Util.setShort(buffer, pos, ZEROS);
// //Util.setShort(buffer, pos,
// //JCSystem.getAvailableMemory(JCSystem.MEMORY_TYPE_PERSISTENT));
// pos += 2;
//
// // Transient clear on reset
// Util.setShort(buffer, pos, ZEROS);
// pos += 2;
// Util.setShort(buffer, pos, ZEROS);
// //Util.setShort(buffer, pos,
// //JCSystem.getAvailableMemory(JCSystem.MEMORY_TYPE_TRANSIENT_RESET));
// pos += 2;
//
// // Transient clear on deselect
// Util.setShort(buffer, pos, ZEROS);
// pos += 2;
// Util.setShort(buffer, pos, ZEROS);
// //Util.setShort(buffer, pos,
// //JCSystem.getAvailableMemory(JCSystem.MEMORY_TYPE_TRANSIENT_DESELECT));
// pos += 2;
//
// // Send it...
// apdu.setOutgoingAndSend(ZEROS, pos);
ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
}
private void GetStatus(APDU apdu, byte buffer[])
{
if (buffer[ISO7816.OFFSET_P2] != 0)
ISOException.throwIt(SW_INCORRECT_P2);
if (buffer[ISO7816.OFFSET_P1] == 1) {
GetMemory(apdu, buffer);
return;
}
if (buffer[ISO7816.OFFSET_P1] != 0)
ISOException.throwIt(SW_INCORRECT_P1);
short pos = 0;
buffer[pos++] = VERSION_PROTOCOL_MAJOR;
buffer[pos++] = VERSION_PROTOCOL_MINOR;
buffer[pos++] = VERSION_APPLET_MAJOR;
buffer[pos++] = VERSION_APPLET_MINOR;
// Total object memory
Util.setShort(buffer, pos, ZEROS);
pos += 2;
Util.setShort(buffer, pos, (short)mem.getBuffer().length);
pos += 2;
Util.setShort(buffer, pos, ZEROS);
pos += 2;
Util.setShort(buffer, pos, mem.freemem());
pos += 2;
// transient
// Number of PINs used
byte cnt = 0;
for(short i = 0; i < pins.length; i++)
if (pins[i] != null)
cnt++;
buffer[pos++] = cnt;
// Number of keys used
cnt = 0;
for(short i = 0; i < keys.length; i++)
if (keys[i] != null)
cnt++;
buffer[pos++] = cnt;
// Logged identities
Util.setShort(buffer, pos, nonce_ids);
pos += 2;
apdu.setOutgoingAndSend(ZEROS, pos);
}
private void importKeyBlob(byte key_nb, byte mate_nb,
byte[] buf, short offset, short avail)
{
offset++;
avail--;
byte key_type = buf[offset];
offset++;
avail--;
short key_size = Util.getShort(buf, offset);
offset += 2;
avail -= 2;
switch(key_type)
{
case KEY_RSA_PUBLIC:
{
RSAPublicKey rsa_pub_key =
(RSAPublicKey)getKey(key_nb, key_type, key_size);
if (avail < 2)
ISOException.throwIt(SW_INVALID_PARAMETER);
short size = Util.getShort(buf, offset);
offset += 2;
avail -= 2;
if (avail < (short)(size + 2))
ISOException.throwIt(SW_INVALID_PARAMETER);
rsa_pub_key.setModulus(buf, offset, size);
offset += size;
avail -= size;
size = Util.getShort(buf, offset);
offset += 2;
avail -= 2;
if (avail < size)
ISOException.throwIt(SW_INVALID_PARAMETER);
rsa_pub_key.setExponent(buf, offset, size);
offset += size;
avail -= size;
break;
}
case KEY_RSA_PRIVATE:
{
ISOException.throwIt(SW_KEY_TYPE_UNSUPPORTED);
// fall through
// #ifdef notdef
// RSAPrivateKey rsa_prv_key =
// (RSAPrivateKey)getKey(key_nb, key_type, key_size);
// if (avail < 2)
// ISOException.throwIt(SW_INVALID_PARAMETER);
//
// short size = Util.getShort(buf, offset);
// offset += 2;
// avail -= 2;
// if (avail < (short)(size + 2))
// ISOException.throwIt(SW_INVALID_PARAMETER);
//
// rsa_prv_key.setModulus(buf, offset, size);
// offset += size;
// avail -= size;
// size = Util.getShort(buf, offset);
// offset += 2;
// avail -= 2;
// if (avail < size)
// ISOException.throwIt(SW_INVALID_PARAMETER);
//
// rsa_prv_key.setExponent(buf, offset, size);
//
// offset += size;
// avail -= size;
//
// break;
// #endif
}
case KEY_RSA_PRIVATE_CRT:
{
RSAPrivateCrtKey rsa_prv_key_crt =
(RSAPrivateCrtKey)getKey(key_nb, key_type, key_size);
if (avail < 2)
ISOException.throwIt(SW_INVALID_PARAMETER);
short size = Util.getShort(buf, offset);
offset += 2;
avail -= 2;
if (avail < (short)(size + 2))
ISOException.throwIt(SW_INVALID_PARAMETER);
rsa_prv_key_crt.setP(buf, offset, size);
offset += size;
avail -= size;
size = Util.getShort(buf, offset);
offset += 2;
avail -= 2;
if (avail < (short)(size + 2))
ISOException.throwIt(SW_INVALID_PARAMETER);
rsa_prv_key_crt.setQ(buf, offset, size);
offset += size;
avail -= size;
size = Util.getShort(buf, offset);
offset += 2;
avail -= 2;
if (avail < (short)(size + 2))
ISOException.throwIt(SW_INVALID_PARAMETER);
rsa_prv_key_crt.setPQ(buf, offset, size);
offset += size;
avail -= size;
size = Util.getShort(buf, offset);
offset += 2;
avail -= 2;
if (avail < (short)(size + 2))
ISOException.throwIt(SW_INVALID_PARAMETER);
rsa_prv_key_crt.setDP1(buf, offset, size);
offset += size;
avail -= size;
size = Util.getShort(buf, offset);
offset += 2;
avail -= 2;
if (avail < size)
ISOException.throwIt(SW_IN_BUF_TOO_SMALL);
rsa_prv_key_crt.setDQ1(buf, offset, size);
offset += size;
avail -= size;
break;
}
case KEY_RSA_PKCS8_PAIR:
{
RSAPrivateCrtKey rsa_prv_key_crt = (RSAPrivateCrtKey)
getKey(key_nb, KEY_RSA_PRIVATE_CRT, key_size);
RSAPublicKey rsa_pub_key =
(RSAPublicKey)getKey(mate_nb, KEY_RSA_PUBLIC, key_size);
short size, end;
short base = offset;
if (asn1 == null) {
asn1 = new ASN1();
}
avail += offset; /* convert avail from a size to and an end of
* buffer offset */
// strip off the sequence
offset = asn1.Unwrap(buf,offset,avail, (short)0);
avail = asn1.GetEnd();
// skip the version
offset = asn1.Skip(buf,offset,avail, (short)1);
// fetch and check the oid
offset = asn1.Unwrap(buf,offset,avail, (short)2);
if (Util.arrayCompare(buf, offset, pkcs8_RSA_oid, ZEROS,
pkcs8_RSA_oid_size) != 0) {
ISOException.throwIt(SW_BAD_ALGID_FOR_KEY);
}
offset = asn1.GetNext();
// fetch the key
offset = asn1.Unwrap(buf,offset,avail, (short)3);
avail = asn1.GetEnd();
//offset = ASN1UnwrapBitString(buf,offset,avail, (short)4);
// unwrap the SEQUENCE
offset = asn1.Unwrap(buf, offset, avail, (short)5);
avail = asn1.GetEnd();
// skip the version
offset = asn1.Skip(buf, offset, avail, (short)6);
// fetch the modulus
offset = asn1.Unwrap(buf, offset, avail, (short)7);
end = asn1.GetEnd();
offset= asn1.Signed2Unsigned(buf, offset, end, (short)8);
size = asn1.GetSize();
rsa_pub_key.setModulus(buf, offset, size);
offset = asn1.GetNext();
// fetch the public exponent
offset = asn1.Unwrap(buf, offset, avail, (short)9);
end = asn1.GetEnd();
offset= asn1.Signed2Unsigned(buf, offset, end, (short)10);
size = asn1.GetSize();
rsa_pub_key.setExponent(buf, offset, size);
offset = asn1.GetNext();
// skip the private exponent
offset = asn1.Skip(buf, offset, avail, (short)11);
// fetch Prime 1
offset = asn1.Unwrap(buf, offset, avail, (short)12);
end = asn1.GetEnd();
offset= asn1.Signed2Unsigned(buf, offset, end, (short)13);
size = asn1.GetSize();
rsa_prv_key_crt.setP(buf, offset, size);
offset = asn1.GetNext();
// fetch Prime 2
offset = asn1.Unwrap(buf, offset, avail, (short)14);
end = asn1.GetEnd();
offset= asn1.Signed2Unsigned(buf, offset, end, (short)15);
size = asn1.GetSize();
rsa_prv_key_crt.setQ(buf, offset, size);
offset = asn1.GetNext();
// fetch exponent1
offset = asn1.Unwrap(buf,offset,avail, (short)16);
end = asn1.GetEnd();
offset= asn1.Signed2Unsigned(buf, offset, end, (short)17);
size = asn1.GetSize();
rsa_prv_key_crt.setDP1(buf, offset, size);
offset = asn1.GetNext();
// fetch exponent2
offset = asn1.Unwrap(buf,offset,avail, (short)18);
end = asn1.GetEnd();
offset= asn1.Signed2Unsigned(buf, offset, end, (short)19);
size = asn1.GetSize();
rsa_prv_key_crt.setDQ1(buf, offset, size);
offset = asn1.GetNext();
// fetch coefficent
offset = asn1.Unwrap(buf,offset,avail, (short)20);
end = asn1.GetEnd();
offset= asn1.Signed2Unsigned(buf, offset, end, (short)21);
size = asn1.GetSize();
rsa_prv_key_crt.setPQ(buf, offset, size);
offset = asn1.GetNext();
break;
}
case KEY_DES:
case KEY_3DES:
case KEY_3DES3:
// {
// DESKey des_key = (DESKey)getKey(key_nb, key_type, key_size);
// if (avail < 2)
// ISOException.throwIt(SW_INVALID_PARAMETER);
// short size = Util.getShort(buf, offset);
// offset += 2;
// avail -= 2;
// if (avail < size)
// ISOException.throwIt(SW_INVALID_PARAMETER);
// des_key.setKey(buf, offset);
// offset += size;
// avail -= size;
// break;
// }
case KEY_DSA_PUBLIC:
case KEY_DSA_PRIVATE:
{
ISOException.throwIt(SW_KEY_TYPE_UNSUPPORTED);
// fall through
}
default:
{
ISOException.throwIt(SW_KEY_TYPE_INVALID);
break;
}
}
}
private void ImportKey(APDU apdu, byte buffer[])
{
short bytesLeft = Util.makeShort(ZEROB, buffer[ISO7816.OFFSET_LC]);
byte key_nb = buffer[ISO7816.OFFSET_P1];
byte mate_nb = buffer[ISO7816.OFFSET_P2];
short obj_class = Util.getShort(buffer, ISO7816.OFFSET_CDATA);
short obj_id = Util.getShort(buffer, (short)(ISO7816.OFFSET_CDATA+2));
byte keybuf[];
short keybuf_size;
short base;
if (key_nb < 0 || key_nb >= MAX_NUM_KEYS)
ISOException.throwIt(SW_INCORRECT_P1);
if (keys[key_nb] != null && keys[key_nb].isInitialized() &&
!authorizeKeyWrite(key_nb))
ISOException.throwIt(SW_UNAUTHORIZED);
if( obj_class == (short)0xffff &&
(obj_id == (short)0xffff || obj_id == (short)0xfffe ) ) {
// I/O Object
base = ZEROS;
keybuf = iobuf;
keybuf_size = iobuf_size;
} else {
base = om.getBaseAddress(obj_class, obj_id);
keybuf = mem.getBuffer();
if (base == -1)
ISOException.throwIt(SW_OBJECT_NOT_FOUND);
if (!om.authorizeReadFromAddress(base, authenticated_id))
ISOException.throwIt(SW_UNAUTHORIZED);
keybuf_size = om.getSizeFromAddress(base);
}
if (keybuf_size < 4)
ISOException.throwIt(SW_INVALID_PARAMETER);
if (keybuf[base] != 0)
ISOException.throwIt(SW_UNSUPPORTED_FEATURE);
byte key_type = keybuf[(short)(base+KEYBLOB_OFFSET_KEY_TYPE)];
if (key_type == KEY_RSA_PKCS8_PAIR) {
if (keys[mate_nb] != null && keys[mate_nb].isInitialized() &&
!authorizeKeyWrite(mate_nb))
ISOException.throwIt(SW_UNAUTHORIZED);
//
// once we've paired up keys, make sure those keys are always
// mated keys. This may be restrictive, but we already require
// that once we create a key, that key must always have the same
// key type, even if we overwrite it, so also requiring mated
// keys to remain consistant is a reasonable restriction.
//
if ( ((keyMate[mate_nb] != -1)
&& (keyMate[mate_nb] != key_nb))
|| ((keyMate[key_nb] != -1)
&& (keyMate[key_nb] != mate_nb)) ) {
ISOException.throwIt(SW_INCONSTANT_KEYPAIRING);
}
keyMate[mate_nb] = key_nb;
keyMate[key_nb] = mate_nb;
}
importKeyBlob(key_nb, mate_nb, keybuf, base, keybuf_size);
// set the ACL value
Util.arrayCopy(buffer, (short)(ISO7816.OFFSET_CDATA+4), keyACLs,
(short)(key_nb * KEY_ACL_SIZE), (short)KEY_ACL_SIZE);
if (key_type == KEY_RSA_PKCS8_PAIR) {
// set ACL value on public key
Util.arrayCopy(buffer, (short)(ISO7816.OFFSET_CDATA+4+KEY_ACL_SIZE),
keyACLs, (short)(mate_nb * KEY_ACL_SIZE), (short)KEY_ACL_SIZE);
}
Util.arrayFillNonAtomic(keybuf, base, keybuf_size, ZEROB);
}
private void ListKeys(APDU apdu, byte buffer[])
{
if (buffer[ISO7816.OFFSET_P2] != 0)
ISOException.throwIt(SW_INCORRECT_P2);
short expectedBytes = Util.makeShort(ZEROB, buffer[ISO7816.OFFSET_LC]);
if (expectedBytes != 11)
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
if (buffer[ISO7816.OFFSET_P1] == 0)
key_it = 0;
else
if (buffer[ISO7816.OFFSET_P1] != 1)
ISOException.throwIt(SW_INCORRECT_P1);
for(; key_it < MAX_NUM_KEYS && (keys[key_it] == null ||
!keys[key_it].isInitialized()); key_it++);
if (key_it < MAX_NUM_KEYS)
{
Key key = keys[key_it];
buffer[0] = key_it;
buffer[1] = getKeyType(key);
buffer[2] = keyMate[key_it];
Util.setShort(buffer, (short)3, key.getSize());
Util.arrayCopyNonAtomic(keyACLs, (short)(key_it * KEY_ACL_SIZE),
buffer, ISO7816.OFFSET_CDATA, (short)KEY_ACL_SIZE);
key_it++;
apdu.setOutgoingAndSend((short)0, (short)11);
}
}
private void ListObjects(APDU apdu, byte buffer[])
{
if (buffer[ISO7816.OFFSET_P2] != 0)
ISOException.throwIt(SW_INCORRECT_P2);
byte expectedBytes = buffer[ISO7816.OFFSET_LC];
if (expectedBytes < 14)
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
boolean found = false;
if (buffer[ISO7816.OFFSET_P1] == 0)
found = om.getFirstRecord(buffer, ZEROS);
else
if (buffer[ISO7816.OFFSET_P1] != 1)
ISOException.throwIt(SW_INCORRECT_P1);
else
found = om.getNextRecord(buffer, ZEROS);
if (found)
apdu.setOutgoingAndSend((short)0, (short)14);
else
ISOException.throwIt(SW_SEQUENCE_END);
}
private void ListPINs(APDU apdu, byte buffer[])
{
if (buffer[ISO7816.OFFSET_P1] != 0)
ISOException.throwIt(SW_INCORRECT_P1);
if (buffer[ISO7816.OFFSET_P2] != 0)
ISOException.throwIt(SW_INCORRECT_P2);
byte expectedBytes = buffer[ISO7816.OFFSET_LC];
if (expectedBytes != 2)
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
short mask = 0;
for(short b = 0; b < MAX_NUM_PINS; b++)
if (pins[b] != null)
mask |= (short)(1 << b);
Util.setShort(buffer, (short)0, mask);
apdu.setOutgoingAndSend((short)0, (short)2);
}
private void ReadObject(APDU apdu, byte buffer[])
{
if (buffer[ISO7816.OFFSET_P1] != 0)
ISOException.throwIt(SW_INCORRECT_P1);
if (buffer[ISO7816.OFFSET_P2] != 0)
ISOException.throwIt(SW_INCORRECT_P2);
short bytesLeft = Util.makeShort(ZEROB, buffer[ISO7816.OFFSET_LC]);
if (bytesLeft != 9)
ISOException.throwIt(SW_INVALID_PARAMETER);
short obj_class = Util.getShort(buffer, ISO7816.OFFSET_CDATA);
short obj_id = Util.getShort(buffer, (short)7);
short offset = Util.getShort(buffer, (short)11);
short size = Util.makeShort(ZEROB, buffer[13]);
byte[] buf;
short base;
if( offset < 0 || size < 0 )
ISOException.throwIt(SW_INVALID_PARAMETER);
if( obj_class == (short)0xffff &&
(obj_id == (short)0xffff || obj_id == (short)0xfffe ) ) {
// I/O Buffer
buf = iobuf;
base = 0;
if( offset > iobuf_size || (short)(offset + size) > iobuf_size )
ISOException.throwIt(SW_INVALID_PARAMETER);
} else {
buf = mem.getBuffer();
base = om.getBaseAddress(obj_class, obj_id);
if (base == -1)
ISOException.throwIt(SW_OBJECT_NOT_FOUND);
if (!om.authorizeReadFromAddress(base, authenticated_id))
ISOException.throwIt(SW_UNAUTHORIZED);
if ((short)(offset + size) > om.getSizeFromAddress(base))
ISOException.throwIt(SW_INVALID_PARAMETER);
}
sendData(apdu, buf, (short)(base + offset), size);
}
/**
* Deletes and zeros the IO objects and throws the passed in
* exception
*/
private void ThrowDeleteObjects(short exception)
{
Util.arrayFillNonAtomic(iobuf, ZEROS, iobuf_size, ZEROB);
ISOException.throwIt(exception);
}
private void VerifyPIN(APDU apdu, byte buffer[])
{
byte pin_nb = buffer[ISO7816.OFFSET_P1];
if (pin_nb < 0 || pin_nb >= MAX_NUM_PINS)
ISOException.throwIt(SW_INCORRECT_P1);
OwnerPIN pin = pins[pin_nb];
if (pin == null)
ISOException.throwIt(SW_INCORRECT_P1);
if (buffer[ISO7816.OFFSET_P2] != 0)
ISOException.throwIt(SW_INCORRECT_P2);
short numBytes = Util.makeShort(ZEROB, buffer[ISO7816.OFFSET_LC]);
if (numBytes != apdu.setIncomingAndReceive())
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
// Attempt to turn off blocking, just use the verify timeout
//if (pin.getTriesRemaining() == 0)
// ISOException.throwIt(SW_IDENTITY_BLOCKED);
if (!CheckPINPolicy(buffer,ISO7816.OFFSET_CDATA,(byte)numBytes)
|| !pin.check(buffer, ISO7816.OFFSET_CDATA, (byte)numBytes))
{
LogoutAllIdentity(pin_nb);
delay(BAD_PASSWD_DELAY);
ISOException.throwIt(SW_AUTH_FAILED);
}
LoginIdentity(pin_nb);
sendData(apdu, nonce, ZEROS, NONCE_SIZE);
}
private void WriteObject(APDU apdu, byte buffer[])
{
if (buffer[ISO7816.OFFSET_P1] != 0)
ISOException.throwIt(SW_INCORRECT_P1);
if (buffer[ISO7816.OFFSET_P2] != 0)
ISOException.throwIt(SW_INCORRECT_P2);
short obj_class = Util.getShort(buffer, ISO7816.OFFSET_CDATA);
short obj_id = Util.getShort(buffer, (short)(ISO7816.OFFSET_CDATA+2));
short offset = Util.getShort(buffer, (short)(ISO7816.OFFSET_CDATA+6));
short size = Util.makeShort(ZEROB, buffer[ISO7816.OFFSET_CDATA+8]);
short obj_size;
short base;
byte[] buf;
if( offset < 0 || size < 0 )
ISOException.throwIt(SW_INVALID_PARAMETER);
if( obj_class == (short)0xffff &&
(obj_id == (short)0xffff || obj_id == (short)0xfffe ) ) {
// I/O Object
base = 0;
buf = iobuf;
iobuf_size = (short) ( size + offset);
obj_size = iobuf_size;
} else {
base = om.getBaseAddress(obj_class, obj_id);
buf = mem.getBuffer();
if (base == -1)
ISOException.throwIt(SW_OBJECT_NOT_FOUND);
if (!om.authorizeWriteFromAddress(base, authenticated_id))
ISOException.throwIt(SW_UNAUTHORIZED);
obj_size = om.getSizeFromAddress(base);
}
if ( offset > obj_size || size > obj_size ||
(short) (offset + size) > obj_size)
ISOException.throwIt(SW_INVALID_PARAMETER);
Util.arrayCopyNonAtomic(buffer, (short)14, buf,
(short)(base+offset), size);
}
private void Logout(APDU apdu, byte[] buffer)
{
//Disable exceptions below to appease Gemalto 64K USB key
//byte lc = buffer[ISO7816.OFFSET_LC];
//if( lc != 0 )
// ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
byte logoutID = buffer[ISO7816.OFFSET_P1];
//if ((authenticated_id & (short)(1 << logoutID)) == 0) {
// ISOException.throwIt(SW_UNAUTHORIZED);
//}
LogoutOneIdentity(logoutID);
}
private void initializeUpdate(APDU apdu, byte[] buffer)
{
short ins = buffer[ISO7816.OFFSET_INS];
if( ins != INS_INIT_UPDATE ) {
ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
}
apdu.setIncomingAndReceive();
ProviderSecurityDomain domain = OPSystem.getSecurityDomain();
channelID = domain.openSecureChannel(apdu);
short len = (short)(buffer[ISO7816.OFFSET_LC]&0xff);
apdu.setOutgoing();
apdu.setOutgoingLength(len);
apdu.sendBytes(ISO7816.OFFSET_CDATA, len);
}
private void externalAuthenticate(APDU apdu, byte[] buffer)
{
apdu.setIncomingAndReceive();
ProviderSecurityDomain domain = OPSystem.getSecurityDomain();
domain.verifyExternalAuthenticate(channelID, apdu);
// According to the Global Platform programming guidelines,
// we might need to verify the security level ourselves.
// Secrity level 0: No secure messaging
// Security level 1: Mac only
// Security level 3: Encrypt and Mac
//
// While the security level appears to be a bit mask, only these
// three levels are defined in the 2.01 Open Platform spec.
// Global Platform 2.1 defines addional levels which mantains
// this bit mask:
//
// Security level 16: Response Mac only
// Security level 17: Response and Card Mac
// Security level 19: Response and Card Mac, Encrypt
//
// Also, security levels 48, 49, and 51 are RFU. Since the applet
// cannot do response Macs, we check the security level explicitly
// rather than checking for the MACing bit.
//
if (( buffer[ISO7816.OFFSET_P1] != (byte) 0x01 ) &&
( buffer[ISO7816.OFFSET_P1] != (byte) 0x03 )) {
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
}
}
private void verifySecureChannel(APDU apdu, byte[] buffer) {
apdu.setIncomingAndReceive();
ProviderSecurityDomain domain = OPSystem.getSecurityDomain();
domain.unwrap(channelID, apdu);
AuthenticateIdentity(RA_IDENTITY);
}
private void verifySecureNonce(APDU apdu, byte[] buffer) {
short bytes = Util.makeShort(ZEROB,buffer[ISO7816.OFFSET_LC]);
if (apdu.setIncomingAndReceive() != bytes)
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
if (bytes < NONCE_SIZE) {
ISOException.throwIt(SW_UNAUTHORIZED);
}
short offset = (short) (ISO7816.OFFSET_CDATA + bytes - NONCE_SIZE);
if (Util.arrayCompare(buffer, offset, default_nonce, ZEROS,
NONCE_SIZE) == 0) {
AuthenticateIdentity(DEFAULT_IDENTITY);
} else if (Util.arrayCompare(buffer, offset, nonce, ZEROS,
NONCE_SIZE) == 0) {
// in the future, we would have one nonce per identity. for
// now once an app logs in as one identity, it can be any identity
authenticated_id = nonce_ids;
} else {
ISOException.throwIt(SW_UNAUTHORIZED);
//AuthenticateIdentity(DEFAULT_IDENTITY);
}
buffer[ISO7816.OFFSET_LC] = (byte) (bytes - NONCE_SIZE);
}
private void resetPIN(APDU apdu, byte[] buffer) {
byte pin_nb = buffer[ISO7816.OFFSET_P1] ;
if (pin_nb < 0 || pin_nb >= MAX_NUM_PINS)
ISOException.throwIt(SW_INCORRECT_P1);
if( buffer[ISO7816.OFFSET_P2] != ZEROB )
ISOException.throwIt(SW_INCORRECT_P2);
byte pinLen = buffer[ISO7816.OFFSET_LC];
if( pinLen < ZEROB ) {
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
}
OwnerPIN pin = pins[pin_nb];
if (pin == null)
ISOException.throwIt(SW_INCORRECT_P1);
if( !CheckPINPolicy(buffer, ISO7816.OFFSET_CDATA, pinLen))
ISOException.throwIt(SW_INVALID_PARAMETER);
pin.resetAndUnblock();
pin.update(buffer, ISO7816.OFFSET_CDATA, pinLen);
LogoutAllIdentity(pin_nb);
}
private short outputRSAPublicKey(short key_nb, byte[] buf, short offset, short key_size) {
buf[offset] = ZEROB; // plaintext
offset++;
buf[offset] = (byte) 1; // RSA public key
offset++;
Util.setShort(buf, offset, (short)(key_size)); // Key Size.
offset+=2;
RSAPublicKey key = (RSAPublicKey) keys[key_nb];
short modsize = key.getModulus(buf, (short)(offset+2));
Util.setShort(buf, offset, modsize);
offset += 2 + modsize;
short expsize = key.getExponent(buf, (short)(offset + 2));
Util.setShort(buf, offset, expsize);
return (short) (8 + modsize + expsize);
}
private void startEnrollment(APDU apdu, byte[] buffer) {
byte prv_key_nb = (byte) (buffer[ISO7816.OFFSET_P1] & 0xf);
byte pub_key_nb = (byte) (buffer[ISO7816.OFFSET_P2] & 0xf);
byte owner = (byte) ((buffer[ISO7816.OFFSET_P1] >> 4) & 0xf) ;
byte usage = (byte) ((buffer[ISO7816.OFFSET_P2] >> 4) & 0xf);
short acl = 0;
short key_size = Util.getShort(buffer, (short)(ISO7816.OFFSET_CDATA+1));
if ((buffer[ISO7816.OFFSET_P1] == 0)
&& (buffer[ISO7816.OFFSET_P2] == 0)) {
// old style. set up old values.
prv_key_nb = 0;
pub_key_nb = 1;
owner = 0xf;
usage = 1;
}
if (owner == 0xf) {
acl = ANY_ONE_ACL;
} else if (owner < 0 || owner > MAX_NUM_PINS) {
ISOException.throwIt(SW_INCORRECT_P1);
} else {
acl = (short) (1 << owner);
}
if (prv_key_nb < 0 || prv_key_nb >= MAX_NUM_KEYS)
ISOException.throwIt(SW_INCORRECT_P1);
if (keys[prv_key_nb] != null &&
keys[prv_key_nb].isInitialized() &&
!authorizeKeyWrite(prv_key_nb))
ISOException.throwIt(SW_UNAUTHORIZED);
if (pub_key_nb < 0 || pub_key_nb >= MAX_NUM_KEYS)
ISOException.throwIt(SW_INCORRECT_P2);
if (keys[pub_key_nb] != null &&
keys[pub_key_nb].isInitialized() &&
!authorizeKeyWrite(pub_key_nb))
ISOException.throwIt(SW_UNAUTHORIZED);
if( buffer[ISO7816.OFFSET_LC] != (byte) 23 ) {
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
}
ProviderSecurityDomain domain = OPSystem.getSecurityDomain();
boolean verified = false;
verified = domain.decryptVerifyKey(channelID, apdu, (short) 9);
if (!verified) {
ISOException.throwIt(SW_BAD_WRAPPED_KEY);
}
GenerateKeyPairRSA(apdu, buffer, prv_key_nb, pub_key_nb, acl);
// copy public key to output object
short pubkeysize = outputRSAPublicKey(pub_key_nb, iobuf, (short)2, (short) key_size);
short modsize = (short) ((short)key_size / (short) 8);
Util.setShort(iobuf, ZEROS, pubkeysize);
// Compute digest over public key and decrypted challenge.
// Write the digest into the iobuf.
Util.arrayCopyNonAtomic(buffer, (short)11, iobuf,
(short)(2 + pubkeysize), (short)16);
doDigest(iobuf, (short)2, (short)(16+pubkeysize),
iobuf, (short)(2+pubkeysize+modsize) );
// Sign the digest, writing the signature over the digest in the iobuf
short sigsize = handSign(prv_key_nb, iobuf, (short) (2+pubkeysize+modsize),
(short)shaDigest.getLength(), iobuf, (short)(2+pubkeysize+2), modsize);
Util.setShort(iobuf, (short)(2 + pubkeysize), sigsize);
iobuf_size = (short) (2 + pubkeysize + 2 + sigsize);
Util.setShort(buffer, ZEROS, iobuf_size);
apdu.setOutgoingAndSend(ZEROS, (short)2);
}
//
// HandSign hard codes SHA1.
//
private short handSign(byte key_nb, byte inbuf[], short inOffset,
short len, byte outbuf[], short outOffset, short modsize)
{
short index;
//
// build the signed data
//
// Hard coded for SHA1
index = (short)(outOffset+modsize-(short)20);
Util.arrayCopyNonAtomic(inbuf, inOffset, outbuf, index, (short)20);
index = (short) (index - sha1encodeLen);
Util.arrayCopyNonAtomic(sha1encode,ZEROS,outbuf,index,sha1encodeLen);
index = (short) (index -1 );
outbuf[index] = 0;
Util.arrayFillNonAtomic(outbuf,(short)(outOffset+2),
(short)(index-outOffset-2), (byte)0xff);
outbuf[(short)(outOffset+1)] = 1;
outbuf[outOffset] = 0;
Cipher ciph = getCipher(key_nb, Cipher.ALG_RSA_NOPAD);
ciph.init(keys[key_nb], (byte) Cipher.MODE_ENCRYPT);
return ciph.doFinal(outbuf, outOffset, modsize,
outbuf, outOffset);
}
private void GenerateKeyPairRSA(APDU apdu, byte buffer[],
byte prv_key_nb, byte pub_key_nb, short prv_acl)
{
byte alg_id = buffer[ISO7816.OFFSET_CDATA];
short key_size = Util.getShort(buffer, (short)(ISO7816.OFFSET_CDATA+1));
byte options = buffer[ISO7816.OFFSET_CDATA+3];
//
// once we've paired up keys, make sure those keys are always
// mated keys. This may be restrictive, but we already require
// that once we create a key, that key must always have the same
// key type, even if we overwrite it, so also requiring mated
// keys to remain consistant is a reasonable restriction.
//
if ( ((keyMate[pub_key_nb] != -1)
&& (keyMate[pub_key_nb] != prv_key_nb))
|| ((keyMate[prv_key_nb] != -1)
&& (keyMate[prv_key_nb] != pub_key_nb)) ) {
ISOException.throwIt(SW_INCONSTANT_KEYPAIRING);
}
RSAPublicKey pub_key =
(RSAPublicKey)getKey(pub_key_nb, KEY_RSA_PUBLIC, key_size);
PrivateKey prv_key =
(PrivateKey)getKey(prv_key_nb, KEY_RSA_PRIVATE_CRT, key_size);
keyMate[pub_key_nb] = prv_key_nb;
keyMate[prv_key_nb] = pub_key_nb;
// set private key ACLs
short index = (short) (prv_key_nb * KEY_ACL_SIZE);
Util.setShort(keyACLs, index, (short) NO_ONE_ACL);
index += 2;
// only RA may write
Util.setShort(keyACLs, index, (short) RA_ACL);
index += 2;
Util.setShort(keyACLs, index, (short) prv_acl);
// set public key ACLs
index = (short) (pub_key_nb * KEY_ACL_SIZE);
Util.setShort(keyACLs, index, (short) ANY_ONE_ACL);
index += 2;
// only RA may write
Util.setShort(keyACLs, index, (short) RA_ACL);
index += 2;
Util.setShort(keyACLs, index, (short) ANY_ONE_ACL);
if (pub_key.isInitialized())
pub_key.clearKey();
if (keyPairs[pub_key_nb] == null && keyPairs[prv_key_nb] == null)
{
keyPairs[pub_key_nb] = new KeyPair(pub_key, prv_key);
keyPairs[prv_key_nb] = keyPairs[pub_key_nb];
} else if (keyPairs[pub_key_nb] != keyPairs[prv_key_nb])
ISOException.throwIt(SW_OPERATION_NOT_ALLOWED);
KeyPair kp = keyPairs[pub_key_nb];
if (kp.getPublic() != pub_key || kp.getPrivate() != prv_key)
ISOException.throwIt(SW_INTERNAL_ERROR);
kp.genKeyPair();
}
private void importKeyEncrypted(APDU apdu, byte[] buffer)
{
byte prv_key_nb = (byte) (buffer[ISO7816.OFFSET_P1] & 0xf);
byte pub_key_nb = (byte) (buffer[ISO7816.OFFSET_P2] & 0xf);
byte owner = (byte) ((buffer[ISO7816.OFFSET_P1] >> 4) & 0xf) ;
byte usage = (byte) ((buffer[ISO7816.OFFSET_P2] >> 4) & 0xf);
short acl = 0;
short obj_class = Util.getShort(buffer, ISO7816.OFFSET_CDATA);
short obj_id = Util.getShort(buffer, (short)(ISO7816.OFFSET_CDATA+2));
byte keybuf[];
short keybuf_size;
short base;
if (owner == 0xf) {
acl = ANY_ONE_ACL;
} else if (owner < 0 || owner > MAX_NUM_PINS) {
ISOException.throwIt(SW_INCORRECT_P1);
} else {
acl = (short) (1 << owner);
}
if (prv_key_nb < 0 || prv_key_nb >= MAX_NUM_KEYS)
ISOException.throwIt(SW_INCORRECT_P1);
if (keys[prv_key_nb] != null &&
keys[prv_key_nb].isInitialized() &&
!authorizeKeyWrite(prv_key_nb))
ISOException.throwIt(SW_UNAUTHORIZED);
if (pub_key_nb < 0 || pub_key_nb >= MAX_NUM_KEYS)
ISOException.throwIt(SW_INCORRECT_P2);
if (keys[pub_key_nb] != null &&
keys[pub_key_nb].isInitialized() &&
!authorizeKeyWrite(pub_key_nb))
ISOException.throwIt(SW_UNAUTHORIZED);
short available = Util.makeShort(ZEROB, buffer[ISO7816.OFFSET_LC]);
if ( available <= WRAPKEY_OFFSET_DATA ) {
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
}
// length of DES3 key
short desLength = Util.makeShort(ZEROB, buffer[ISO7816.OFFSET_CDATA+
WRAPKEY_OFFSET_SIZE]);
// Sigh, the token on supports DES2 keys.
if ( desLength != (short) 16 ) {
ISOException.throwIt(SW_KEY_SIZE_ERROR);
}
// length of Check
// 1 byte length
// n-byte check value
short checkOffset = (short)(WRAPKEY_OFFSET_DATA+
(int)desLength);
if ( available <= checkOffset ) {
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
}
short checkLength = Util.makeShort(ZEROB,
buffer[(short)(ISO7816.OFFSET_CDATA+checkOffset)]);
//iv
short ivOffset = (short)(checkOffset + 1 + (int)checkLength);
if ( available <= ivOffset ) {
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
}
short ivLength = Util.makeShort(ZEROB,
buffer[(short)(ISO7816.OFFSET_CDATA+ivOffset)]);
if ( available < (short)(ivOffset+1+ivLength) ) {
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
}
if ( ivLength != 8) {
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); // wrong error code
}
ivOffset += ISO7816.OFFSET_CDATA;
ProviderSecurityDomain domain = OPSystem.getSecurityDomain();
boolean verified = false;
verified = domain.decryptVerifyKey(channelID, apdu,
(short)(ISO7816.OFFSET_CDATA+4));
if (!verified) {
ISOException.throwIt(SW_BAD_WRAPPED_KEY);
}
if( obj_class == (short)0xffff &&
(obj_id == (short)0xffff || obj_id == (short)0xfffe ) ) {
// I/O Object
base = ZEROS;
keybuf = iobuf;
keybuf_size = iobuf_size;
} else {
base = om.getBaseAddress(obj_class, obj_id);
keybuf = mem.getBuffer();
if (base == -1)
ISOException.throwIt(SW_OBJECT_NOT_FOUND);
if (!om.authorizeReadFromAddress(base, authenticated_id))
ISOException.throwIt(SW_UNAUTHORIZED);
// we clear the buffer at the end, so we also need write
// privellege
if (!om.authorizeWriteFromAddress(base, authenticated_id))
ISOException.throwIt(SW_UNAUTHORIZED);
keybuf_size = om.getSizeFromAddress(base);
}
// name the key type (it's not encrypted, so we can grab it early */
byte key_type = keybuf[(short)(base+KEYBLOB_OFFSET_KEY_TYPE)];
// get the des key to decrypt the private key
if (keybuf[base] == 0x01) { // BLOB_ENC_ENCRYPTED
DESKey des3 = (DESKey) KeyBuilder.buildKey(
KeyBuilder.TYPE_DES, KeyBuilder.LENGTH_DES3_2KEY, false);
des3.setKey(buffer,(short)(ISO7816.OFFSET_CDATA+WRAPKEY_OFFSET_DATA));
if (des == null) {
des = Cipher.getInstance(Cipher.ALG_DES_CBC_NOPAD, false);
//ISOException.throwIt((short)2);
}
// decrypt the private key
des.init(des3, Cipher.MODE_DECRYPT, buffer,
(short)(ivOffset+1), ivLength);
des.doFinal(keybuf, (short)(base+KEYBLOB_OFFSET_KEY_DATA),
(short)(keybuf_size-KEYBLOB_OFFSET_KEY_DATA),
keybuf, (short)(base+KEYBLOB_OFFSET_KEY_DATA));
} else if (iobuf[0] != 0x00) {
ISOException.throwIt(SW_INVALID_PARAMETER);
}
// at this point the key is in the object buffer in the clear.
// if anything goes wrong from here on out, we want to smash the
// data in the buf so the key doesn't get leaked.
try {
if (key_type == KEY_RSA_PRIVATE ||
key_type == KEY_RSA_PRIVATE_CRT ||
key_type == KEY_RSA_PKCS8_PAIR) {
//
// once we've paired up keys, make sure those keys are always
// mated keys. This may be restrictive, but we already require
// that once we create a key, that key must always have the same
// key type, even if we overwrite it, so also requiring mated
// keys to remain consistant is a reasonable restriction.
//
if ( ((keyMate[pub_key_nb] != -1)
&& (keyMate[pub_key_nb] != prv_key_nb))
|| ((keyMate[prv_key_nb] != -1)
&& (keyMate[prv_key_nb] != pub_key_nb)) ) {
ISOException.throwIt(SW_INCONSTANT_KEYPAIRING);
}
keyMate[pub_key_nb] = prv_key_nb;
keyMate[prv_key_nb] = pub_key_nb;
}
// if it doesn't start with a sequence, it's not DER data,
// don't try to decode it.
if ((key_type == KEY_RSA_PKCS8_PAIR) &&
(keybuf[(short)(base+KEYBLOB_OFFSET_KEY_DATA)] != 0x30)) {
ISOException.throwIt(SW_BAD_WRAPPED_PRIV_KEY);
}
importKeyBlob(prv_key_nb, pub_key_nb, keybuf, base, keybuf_size);
} finally {
// we're done with the keybuf, just clear it out now
Util.arrayFillNonAtomic(keybuf, base, keybuf_size, ZEROB);
}
// set private key ACLs
short index = (short) (prv_key_nb * KEY_ACL_SIZE);
Util.setShort(keyACLs, index, (short) NO_ONE_ACL);
index += 2;
// only RA may write
Util.setShort(keyACLs, index, (short) RA_ACL);
index += 2;
Util.setShort(keyACLs, index, (short) acl);
if (key_type == KEY_RSA_PKCS8_PAIR) {
// set public key ACLs
index = (short) (pub_key_nb * KEY_ACL_SIZE);
Util.setShort(keyACLs, index, (short) ANY_ONE_ACL);
index += 2;
// only RA may write
Util.setShort(keyACLs, index, (short) RA_ACL);
index += 2;
Util.setShort(keyACLs, index, (short) ANY_ONE_ACL);
}
}
private void readIOBuf(APDU apdu, byte buffer[]) {
//
// byte p1 = buffer[ISO7816.OFFSET_P1];
// byte p2 = buffer[ISO7816.OFFSET_P2];
// byte lc = buffer[ISO7816.OFFSET_LC];
//
// if( p2 != ZEROB )
// ISOException.throwIt(SW_INCORRECT_P2);
//
// if( lc != (byte) 2 )
// ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
//
// short offset = Util.getShort(buffer, ISO7816.OFFSET_CDATA);
// short len = (short) (p1 & 0xff);
//
// if( offset < (short)0 || len > iobuf_size || offset > iobuf_size ||
// (short)(len+offset) > iobuf_size )
// ISOException.throwIt(SW_INVALID_PARAMETER);
//
// Util.arrayCopyNonAtomic(iobuf, offset, buffer, ZEROS, len);
// apdu.setOutgoingAndSend(ZEROS, len);
ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
}
private void getLifeCycle(APDU apdu, byte[] buffer) {
byte lc = buffer[ISO7816.OFFSET_LC];
buffer[0] = OPSystem.getCardContentState();
if (lc == 1) {
// compatibility
apdu.setOutgoingAndSend(ZEROS, (short)1);
} else {
buffer[1] = (byte)(pinEnabled + isWritable);
buffer[2] = VERSION_PROTOCOL_MAJOR;
buffer[3] = VERSION_PROTOCOL_MINOR;
apdu.setOutgoingAndSend(ZEROS, (short)4);
}
}
private void setLifeCycle(APDU apdu, byte[] buffer) {
byte lc = buffer[ISO7816.OFFSET_LC];
if( lc != 0 )
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
boolean result =
OPSystem.setCardContentState(buffer[ISO7816.OFFSET_P1]);
if( result == false )
ISOException.throwIt(SW_INVALID_PARAMETER);
}
private void getIssuerInfo(APDU apdu, byte[] buffer) {
short size = Util.makeShort(ZEROB,buffer[ISO7816.OFFSET_LC]);
if (size != ISSUER_INFO_SIZE) {
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
}
Util.arrayCopyNonAtomic(issuerInfo, ZEROS, buffer, ZEROS,
ISSUER_INFO_SIZE);
apdu.setOutgoingAndSend(ZEROS, ISSUER_INFO_SIZE);
}
private void setIssuerInfo(APDU apdu, byte[] buffer) {
short size = Util.makeShort(ZEROB,buffer[ISO7816.OFFSET_LC]);
if (size != ISSUER_INFO_SIZE) {
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
}
Util.arrayCopyNonAtomic(buffer, ISO7816.OFFSET_CDATA,
issuerInfo, ZEROS, ISSUER_INFO_SIZE);
}
private void getRandom(APDU apdu, byte[] buffer) {
short len = Util.makeShort(ZEROB,buffer[ISO7816.OFFSET_LC]);
if (randomGenerator == null) {
randomGenerator =
RandomData.getInstance(RandomData.ALG_SECURE_RANDOM);
}
randomGenerator.generateData(buffer, ZEROS, len);
apdu.setOutgoingAndSend(ZEROS, len);
}
private void seedRandom(APDU apdu, byte[] buffer) {
short len = Util.makeShort(ZEROB,buffer[ISO7816.OFFSET_LC]);
if (randomGenerator == null) {
randomGenerator =
RandomData.getInstance(RandomData.ALG_SECURE_RANDOM);
}
randomGenerator.setSeed(buffer, ISO7816.OFFSET_CDATA, len);
}
private void getBuildID(APDU apdu, byte[] buffer) {
short size = Util.makeShort(ZEROB,buffer[ISO7816.OFFSET_LC]);
if (size < 4) {
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
}
Util.setShort(buffer, ZEROS, BUILDID_MAJOR);
Util.setShort(buffer, (short)2, BUILDID_MINOR);
apdu.setOutgoingAndSend(ZEROS, (short)4);
}
private void getBuiltInACL(APDU apdu, byte[] buffer) {
if (buffer[ISO7816.OFFSET_LC] < (byte)7) {
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
}
Util.setShort(buffer, ZEROS, create_object_ACL);
Util.setShort(buffer, (short)2, create_key_ACL);
Util.setShort(buffer, (short)4, create_pin_ACL);
buffer[6] = enable_ACL_change;
apdu.setOutgoingAndSend(ZEROS, (short)7);
}
private void setBuiltInACL(APDU apdu, byte[] buffer) {
if (buffer[ISO7816.OFFSET_LC] != (byte)7) {
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
}
// my default, the applet is completely locked down.
if (enable_ACL_change == 0) {
ISOException.throwIt(SW_OPERATION_NOT_ALLOWED);
}
create_object_ACL = Util.getShort(buffer, ISO7816.OFFSET_CDATA);
create_key_ACL = Util.getShort(buffer,(short)(ISO7816.OFFSET_CDATA+2));
create_pin_ACL = Util.getShort(buffer,(short)(ISO7816.OFFSET_CDATA+4));
enable_ACL_change = buffer[ISO7816.OFFSET_CDATA+6];
isWritable = 0;
if (((create_object_ACL & 0xff) != 0) &&
((create_key_ACL & 0xff) != 0)) {
isWritable = 2;
}
}
/**
* UTILITY FUNCTIONS
*/
private void sendData(APDU apdu, byte data[], short offset, short size)
{
if (size > 255)
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
Util.arrayCopyNonAtomic(data, offset, apdu.getBuffer(), ZEROS, size);
apdu.setOutgoingAndSend(ZEROS, size);
}
private void LoginIdentity(byte id_nb)
{
if (Util.arrayCompare(nonce, ZEROS, default_nonce, ZEROS,
NONCE_SIZE) == 0) {
if (randomGenerator == null) {
randomGenerator =
RandomData.getInstance(RandomData.ALG_SECURE_RANDOM);
}
randomGenerator.generateData(nonce, ZEROS, NONCE_SIZE);
}
nonce_ids |= (short)(1 << id_nb);
loginCount[id_nb] += 1;
}
private void AuthenticateIdentity(byte id_nb)
{
authenticated_id |= (short)(1 << id_nb);
}
private void LogoutOneIdentity(byte id_nb)
{
// log out one app from this identity. when all identities have
// been logged out, clean the nonce
if (loginCount[id_nb] != 0) {
// never decrement past '0'
loginCount[id_nb] -= 1;
}
if (loginCount[id_nb] != 0) {
return;
}
nonce_ids &= ~(short)(1 << id_nb);
pins[id_nb].reset();
if (nonce_ids == 0) {
Util.arrayFillNonAtomic(nonce, ZEROS, NONCE_SIZE, ZEROB);
}
}
private void LogoutAllIdentity(byte id_nb)
{
// log everyone off this identity
loginCount[id_nb] = 0;
LogoutOneIdentity(id_nb);
}
private void LogoutAll()
{
for(byte i = 0; i < MAX_NUM_PINS; i++)
if (pins[i] != null) {
LogoutAllIdentity(i);
}
}
/**
* Check from ACL if a key can be read
*/
private boolean authorizeKeyRead(byte key_nb)
{
short acl_offset = (short)(key_nb * KEY_ACL_SIZE);
short required_ids = Util.getShort(keyACLs, acl_offset);
return (required_ids & authenticated_id) != 0;
}
/**
* Check from ACL if a key can be used
*/
private boolean authorizeKeyUse(byte key_nb)
{
short acl_offset = (short)(key_nb * KEY_ACL_SIZE + 4);
short required_ids = Util.getShort(keyACLs, acl_offset);
return (required_ids & authenticated_id) != 0;
}
/**
* Check from ACL if a key can be overwritten
*/
private boolean authorizeKeyWrite(byte key_nb)
{
short acl_offset = (short)(key_nb * KEY_ACL_SIZE + 2);
short required_ids = Util.getShort(keyACLs, acl_offset);
return (required_ids & authenticated_id) != 0;
}
private Cipher getCipher(byte key_nb, byte alg_id)
{
if (ciphers[key_nb] == null)
ciphers[key_nb] = Cipher.getInstance(alg_id, false);
else
if (ciphers[key_nb].getAlgorithm() != alg_id)
ISOException.throwIt(SW_OPERATION_NOT_ALLOWED);
return ciphers[key_nb];
}
/**
* Retrieves the Key object to be used w/ the specified key number,
* key type (KEY_XX) and size.
*
* <p>If exists, check it has the proper key type * If not, creates
* it.</p>
*
* @return Retrieved Key object
* @throws SW_UNATUTHORIZED
* @throws SW_OPERATION_NOT_ALLOWED
*/
private Key getKey(byte key_nb, byte key_type, short key_size)
{
byte jc_key_type = keyType2JCType(key_type);
if (keys[key_nb] == null)
{
if (0 == (authenticated_id & create_key_ACL))
ISOException.throwIt(SW_UNAUTHORIZED);
keys[key_nb] = KeyBuilder.buildKey(jc_key_type, key_size, false);
} else
if (keys[key_nb].getSize() != key_size
|| keys[key_nb].getType() != jc_key_type)
ISOException.throwIt(SW_OPERATION_NOT_ALLOWED);
return keys[key_nb];
}
private byte getKeyType(Key key)
{
switch(key.getType())
{
case KeyBuilder.TYPE_RSA_PUBLIC:
return KEY_RSA_PUBLIC;
case KeyBuilder.TYPE_RSA_PRIVATE:
return KEY_RSA_PRIVATE;
case KeyBuilder.TYPE_RSA_CRT_PRIVATE:
return KEY_RSA_PRIVATE_CRT;
case ALG_DES:
if (key.getSize() == KeyBuilder.LENGTH_DES)
return KEY_DES;
if (key.getSize() == KeyBuilder.LENGTH_DES3_2KEY)
return KEY_3DES;
if (key.getSize() == KeyBuilder.LENGTH_DES3_3KEY)
return KEY_3DES3;
break;
}
ISOException.throwIt(SW_KEY_TYPE_INVALID);
return 0;
}
private Signature getSignature(byte key_nb, byte alg_id)
{
if (signatures[key_nb] == null)
signatures[key_nb] = Signature.getInstance(alg_id, false);
else
if (signatures[key_nb].getAlgorithm() != alg_id)
ISOException.throwIt(SW_OPERATION_NOT_ALLOWED);
return signatures[key_nb];
}
public static void install(byte bArray[], short bOffset, byte bLength)
{
CardEdge wal = new CardEdge(bArray, bOffset, bLength);
wal.register();
}
private byte keyType2JCType(byte key_type)
{
switch(key_type)
{
case KEY_RSA_PUBLIC:
return KeyBuilder.TYPE_RSA_PUBLIC;
case KEY_RSA_PRIVATE:
return KeyBuilder.TYPE_RSA_PRIVATE;
case KEY_RSA_PRIVATE_CRT:
return KeyBuilder.TYPE_RSA_CRT_PRIVATE;
case KEY_DSA_PUBLIC:
ISOException.throwIt(SW_UNSUPPORTED_FEATURE);
// fall through
case KEY_DSA_PRIVATE: // '\005'
ISOException.throwIt(SW_UNSUPPORTED_FEATURE);
// fall through
case KEY_DES:
return KeyBuilder.TYPE_DES;
case KEY_3DES:
case KEY_3DES3:
return KeyBuilder.TYPE_DES;
default:
ISOException.throwIt(SW_INVALID_PARAMETER);
break;
}
return 0;
}
private void processCardReset() {
LogoutAll();
// This flag is CLEAR_ON_RESET, so it will be set to false when
// the card is reset or removed.
cardResetProcessed[0] = true;
}
private boolean requireAuth(byte ins)
{
boolean ret = false;
switch (ins) {
case INS_IMPORT_KEY:
case INS_COMPUTE_CRYPT:
case INS_CREATE_PIN:
case INS_CREATE_OBJ:
case INS_DELETE_OBJ:
case INS_READ_OBJ:
case INS_WRITE_OBJ:
// case INS_LOGOUT:
ret = true;
}
return ret;
}
private void initTransient()
{
iobuf = JCSystem.makeTransientByteArray(IOBUF_ALLOC,
JCSystem.CLEAR_ON_DESELECT);
ciph_dirs = JCSystem.makeTransientByteArray(MAX_NUM_KEYS,
JCSystem.CLEAR_ON_DESELECT);
//
// before release we need to make sure that the nonce is
// an array for each pin. (again memory size, but in this case
// we are taking about 8->64 bytes, not trying to find another 1k
// bytes.
//
nonce = JCSystem.makeTransientByteArray(NONCE_SIZE,
JCSystem.CLEAR_ON_RESET);
loginCount = JCSystem.makeTransientShortArray(MAX_NUM_PINS,
JCSystem.CLEAR_ON_RESET);
cardResetProcessed = JCSystem.makeTransientBooleanArray((short)1,
JCSystem.CLEAR_ON_RESET);
transientInit = true;
}
//
// handle non-secure standard commands. Called from process.
//
private void processCardEdgeAPDU(APDU apdu, byte buffer[])
{
byte ins = buffer[ISO7816.OFFSET_INS];
if (requireAuth(ins)) {
verifySecureNonce(apdu, buffer);
}
switch(ins)
{
case INS_IMPORT_KEY:
ImportKey(apdu, buffer);
break;
case INS_COMPUTE_CRYPT:
ComputeCrypt(apdu, buffer);
break;
case INS_VERIFY_PIN:
VerifyPIN(apdu, buffer);
break;
case INS_CREATE_PIN:
CreatePIN(apdu, buffer);
break;
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);
}
}
}