From 6f9748fe084e6c5ecc9ece54014df676ea45db8b Mon Sep 17 00:00:00 2001 From: cpathak Date: Mon, 6 Apr 2020 22:45:50 -0700 Subject: [PATCH 1/2] Optimization of the code Implemented transient memory based code. --- .../android/javacard/keymaster/KMArray.java | 92 ++-- .../android/javacard/keymaster/KMBoolTag.java | 64 ++- .../javacard/keymaster/KMByteBlob.java | 110 ++--- .../android/javacard/keymaster/KMByteTag.java | 104 ++--- .../android/javacard/keymaster/KMDecoder.java | 346 +++++++------- .../android/javacard/keymaster/KMEncoder.java | 245 +++++----- .../android/javacard/keymaster/KMEnum.java | 77 ++-- .../javacard/keymaster/KMEnumArrayTag.java | 159 +++---- .../android/javacard/keymaster/KMEnumTag.java | 94 ++-- .../keymaster/KMHardwareAuthToken.java | 137 ++++-- .../keymaster/KMHmacSharingParameters.java | 86 ++-- .../android/javacard/keymaster/KMInteger.java | 148 +++--- .../javacard/keymaster/KMIntegerArrayTag.java | 141 +++--- .../javacard/keymaster/KMIntegerTag.java | 124 ++--- .../keymaster/KMKeyCharacteristics.java | 87 ++-- .../javacard/keymaster/KMKeyParameters.java | 75 +-- .../javacard/keymaster/KMKeymasterApplet.java | 329 +++++++------- .../javacard/keymaster/KMRepository.java | 426 +----------------- .../com/android/javacard/keymaster/KMTag.java | 7 +- .../android/javacard/keymaster/KMType.java | 47 +- .../android/javacard/keymaster/KMUtil.java | 11 +- .../android/javacard/keymaster/KMVector.java | 85 ---- .../keymaster/KMVerificationToken.java | 127 ++++-- .../javacard/test/KMFrameworkTest.java | 62 +-- Applet/default.output | 2 +- 25 files changed, 1428 insertions(+), 1757 deletions(-) delete mode 100644 Applet/Applet/src/com/android/javacard/keymaster/KMVector.java diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMArray.java b/Applet/Applet/src/com/android/javacard/keymaster/KMArray.java index d2eed688..04ee7cfe 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMArray.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMArray.java @@ -18,66 +18,76 @@ import javacard.framework.ISO7816; import javacard.framework.ISOException; +import javacard.framework.Util; public class KMArray extends KMType { - private KMType[] vals; - private short length; - private short startOff; + private static final short ARRAY_HEADER_SIZE = 3; + private static KMArray prototype; + private static short instPtr; - private KMArray() { - init(); + private KMArray() {} + + private static KMArray proto(short ptr) { + if (prototype == null) prototype = new KMArray(); + instPtr = ptr; + return prototype; } - @Override - public void init() { - vals = null; - startOff = 0; - length = 0; + public static short exp() { + short ptr = instance(ARRAY_TYPE, ARRAY_HEADER_SIZE); + heap[(short)(ptr + TLV_HEADER_SIZE)] = 0; + Util.setShort(heap,(short)(ptr + TLV_HEADER_SIZE + 1),(short)0 ); + return ptr; } - @Override - public short length() { - return length; + public static short exp(byte type) { + short ptr = instance(ARRAY_TYPE, ARRAY_HEADER_SIZE); + heap[(short)(ptr + TLV_HEADER_SIZE)] = type; + Util.setShort(heap,(short)(ptr + TLV_HEADER_SIZE + 1),(short)0 ); + return ptr; } - public static void create(KMArray[] arrayRefTable) { - byte index = 0; - while (index < arrayRefTable.length) { - arrayRefTable[index] = new KMArray(); - index++; - } + public static short instance(short length) { + short ptr = KMType.instance(ARRAY_TYPE, (short)(ARRAY_HEADER_SIZE + (length*2))); + heap[(short)(ptr + TLV_HEADER_SIZE)] = 0; + Util.setShort(heap,(short)(ptr + TLV_HEADER_SIZE + 1),length); + return ptr; } - public static KMArray instance() { - return repository.newArray(); + public static short instance(short length, byte type) { + short ptr = instance(length); + heap[(short)(ptr + TLV_HEADER_SIZE)] = type; + return ptr; } - public static KMArray instance(short length) { + public static KMArray cast(short ptr) { + if (heap[ptr] != ARRAY_TYPE) ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + return proto(ptr); + } - KMArray inst = repository.newArray(); - inst.startOff = repository.newTypeArray(length); - inst.vals = repository.getTypeArrayRef(); - inst.length = length; - return inst; + public void add(short index, short objPtr) { + short len = length(); + if (index >= len) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + Util.setShort(heap, (short) (instPtr + TLV_HEADER_SIZE + ARRAY_HEADER_SIZE + (short)(index*2)), objPtr) ; } - public KMArray withLength(short length) { - this.length = length; - return this; + public short get(short index) { + short len = length(); + if (index >= len) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + return Util.getShort(heap,(short) (instPtr + TLV_HEADER_SIZE + 3 + (short)(index*2))); } - public KMArray add(short index, KMType val) { - if (index >= length) { - ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); - } - vals[(short) (startOff + index)] = val; - return this; + public byte containedType(){ return heap[(short)(instPtr + TLV_HEADER_SIZE)];} + + public short getStartOff() { + return (short) (instPtr + TLV_HEADER_SIZE + 3); + } + + public short length() { + return Util.getShort(heap, (short) (instPtr + TLV_HEADER_SIZE + 1)); } - public KMType get(short index) { - if (index >= length) { - ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); - } - return vals[(short) (startOff + index)]; + public byte[] getBuffer() { + return heap; } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMBoolTag.java b/Applet/Applet/src/com/android/javacard/keymaster/KMBoolTag.java index 2e088893..5154cd44 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMBoolTag.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMBoolTag.java @@ -18,8 +18,11 @@ import javacard.framework.ISO7816; import javacard.framework.ISOException; +import javacard.framework.Util; public class KMBoolTag extends KMTag { + private static KMBoolTag prototype; + private static short instPtr; private static final short[] tags = { CALLER_NONCE, @@ -34,59 +37,50 @@ public class KMBoolTag extends KMTag { RESET_SINCE_ID_ROTATION }; - // Array of Tag Values. - private short key; - private byte val; + private KMBoolTag() {} - // assignBlob constructor - private KMBoolTag() { - init(); + private static KMBoolTag proto(short ptr) { + if (prototype == null) prototype = new KMBoolTag(); + instPtr = ptr; + return prototype; } - @Override - public void init() { - key = 0; - val = 1; // always 1. + // pointer to an empty instance used as expression + public static short exp() { + short ptr = KMType.exp(TAG_TYPE); + Util.setShort(heap, (short)(ptr+1), BOOL_TAG); + return ptr; } - public static KMBoolTag instance() { - return repository.newBoolTag(); + public static short instance(short key) { + if (!validateKey(key)) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + short ptr = KMType.instance(TAG_TYPE, (short)5); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE), BOOL_TAG); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE+2), key); + heap[(short)(ptr+TLV_HEADER_SIZE+4)] = 0x01; + return ptr; } - public static void create(KMBoolTag[] boolTagRefTable) { - byte index = 0; - while (index < boolTagRefTable.length) { - boolTagRefTable[index] = new KMBoolTag(); - index++; + public static KMBoolTag cast(short ptr) { + if (heap[ptr] != TAG_TYPE) ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + if (Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)) != BOOL_TAG) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } + return proto(ptr); } - @Override public short getKey() { - return key; - } - - @Override - public short length() { - return 1; + return Util.getShort(heap, (short)(instPtr+TLV_HEADER_SIZE+2)); } - @Override public short getTagType() { return KMType.BOOL_TAG; } public byte getVal() { - return val; - } - // create default assignBlob without any value - public static KMBoolTag instance(short key) { - if (!validateKey(key)) { - ISOException.throwIt(ISO7816.SW_DATA_INVALID); - } - KMBoolTag tag = repository.newBoolTag(); - tag.key = key; - return tag; + return heap[(short)(instPtr+TLV_HEADER_SIZE+4)]; } // validate the tag key diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMByteBlob.java b/Applet/Applet/src/com/android/javacard/keymaster/KMByteBlob.java index 54b0e9e4..a112ce6c 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMByteBlob.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMByteBlob.java @@ -20,93 +20,71 @@ import javacard.framework.ISOException; import javacard.framework.Util; -// Byte val represents contiguous memory buffer. +// Byte blob represents contiguous memory buffer. public class KMByteBlob extends KMType { - private byte[] val; - private short startOff; - private short length; + private static KMByteBlob prototype; + private static short instPtr; - private KMByteBlob() { - init(); - } + private KMByteBlob() {} - @Override - public void init() { - length = 0; - startOff = 0; - val = null; + private static KMByteBlob proto(short ptr) { + if (prototype == null) prototype = new KMByteBlob(); + instPtr = ptr; + return prototype; } - @Override - public short length() { - return length; + // pointer to an empty instance used as expression + public static short exp() { + return KMType.exp(BYTE_BLOB_TYPE); } - public static KMByteBlob instance() { - return repository.newByteBlob(); + // return an empty byte blob instance + public static short instance(short length) { + return KMType.instance(BYTE_BLOB_TYPE, length); } - // copy the blob - public static KMByteBlob instance(byte[] blob, short startOff, short length) { - if ((length <= 0) || ((short)(startOff+length) > blob.length)) { - ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); - } - KMByteBlob inst = instance(length); - Util.arrayCopyNonAtomic(blob, startOff, inst.val, inst.startOff, inst.length); - return inst; + // byte blob from existing buf + public static short instance(byte[] buf, short startOff, short length) { + short ptr = instance(length); + Util.arrayCopyNonAtomic(buf, startOff, heap, (short) (ptr + TLV_HEADER_SIZE), length); + return ptr; } - // returns empty blob with given length - public static KMByteBlob instance(short length) { - if (length <= 0) { - ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + // cast the ptr to KMByteBlob + public static KMByteBlob cast(short ptr) { + if (heap[ptr] != BYTE_BLOB_TYPE) ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + if (Util.getShort(heap, (short) (ptr + 1)) == INVALID_VALUE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } - KMByteBlob inst = instance(); - inst.startOff = repository.newByteArray(length); - inst.val = repository.getByteHeapRef(); - inst.length = length; - return inst; - } - - public static void create(KMByteBlob[] byteBlobRefTable) { - byte index = 0; - while (index < byteBlobRefTable.length) { - byteBlobRefTable[index] = new KMByteBlob(); - index++; - } - } - - // sets the expected length for prototype byte val. - public KMByteBlob withLength(short len) { - this.length = len; - return this; + return proto(ptr); } + // Add the byte public void add(short index, byte val) { - if (index >= this.length) { - ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); - } - if (this.val == null) { - ISOException.throwIt(ISO7816.SW_DATA_INVALID); - } - this.val[(short) (startOff + index)] = val; + short len = length(); + if (index >= len) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + heap[(short) (instPtr + TLV_HEADER_SIZE + index)] = val; } + // Get the byte public byte get(short index) { - if (index >= this.length) { - ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); - } - if (this.val == null) { - ISOException.throwIt(ISO7816.SW_DATA_INVALID); - } - return this.val[(short) (startOff + index)]; + short len = length(); + if (index >= len) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + return heap[(short) (instPtr + TLV_HEADER_SIZE + index)]; + } + + // Get the start of blob + public short getStartOff() { + return (short) (instPtr + TLV_HEADER_SIZE); } - public byte[] getVal() { - return val; + // Get the length of the blob + public short length() { + return Util.getShort(heap, (short) (instPtr + 1)); } - public short getStartOff() { - return startOff; + // Get the buffer pointer in which blob is contained. + public byte[] getBuffer() { + return heap; } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMByteTag.java b/Applet/Applet/src/com/android/javacard/keymaster/KMByteTag.java index 1569394c..6740e9ba 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMByteTag.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMByteTag.java @@ -18,8 +18,11 @@ import javacard.framework.ISO7816; import javacard.framework.ISOException; +import javacard.framework.Util; public class KMByteTag extends KMTag { + private static KMByteTag prototype; + private static short instPtr; private static final short[] tags = { APPLICATION_ID, @@ -41,70 +44,68 @@ public class KMByteTag extends KMTag { CONFIRMATION_TOKEN }; - private short key; - private KMByteBlob val; + private KMByteTag() {} - private KMByteTag() { - init(); + private static KMByteTag proto(short ptr) { + if (prototype == null) prototype = new KMByteTag(); + instPtr = ptr; + return prototype; } - @Override - public void init() { - key = 0; - val = null; + // pointer to an empty instance used as expression + public static short exp() { + short blobPtr = KMByteBlob.exp(); + short ptr = instance(TAG_TYPE, (short)6); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE), BYTES_TAG); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE+2), INVALID_TAG); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE+4), blobPtr); + return ptr; } - @Override - public short getKey() { - return key; - } - - @Override - public short length() { - return val.length(); - } - - @Override - public short getTagType() { - return KMType.BYTES_TAG; - } - - public static KMByteTag instance() { - return repository.newByteTag(); + public static short instance(short key) { + if (!validateKey(key)) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + return instance(key, KMByteBlob.exp()); } - public static KMByteTag instance(short key) { + public static short instance(short key, short byteBlob) { if (!validateKey(key)) { ISOException.throwIt(ISO7816.SW_DATA_INVALID); } - KMByteTag tag = repository.newByteTag(); - tag.key = key; - tag.val = null; - return tag; + if(heap[byteBlob] != BYTE_BLOB_TYPE) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + short ptr = instance(TAG_TYPE, (short)6); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE), BYTES_TAG); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE+2), key); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE+4), byteBlob); + return ptr; } - public static void create(KMByteTag[] byteTagRefTable) { - byte index = 0; - while (index < byteTagRefTable.length) { - byteTagRefTable[index] = new KMByteTag(); - index++; + public static KMByteTag cast(short ptr) { + if (heap[ptr] != TAG_TYPE) ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + if (Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)) != BYTES_TAG) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } + return proto(ptr); } - // create default assignBlob without any value - public static KMByteTag instance(short key, KMByteBlob array) { - if (!validateKey(key)) { - ISOException.throwIt(ISO7816.SW_DATA_INVALID); - } - KMByteTag tag = repository.newByteTag(); - tag.key = key; - tag.val = array; - return tag; + public short getKey() { + return Util.getShort(heap, (short)(instPtr+TLV_HEADER_SIZE+2)); + } + + public short getTagType() { + return KMType.BYTES_TAG; } - public KMByteTag withLength(short length) { - this.val.withLength(length); - return this; + public short getValue() { + return Util.getShort(heap, (short)(instPtr+TLV_HEADER_SIZE+4)); + } + + public short length() { + short blobPtr = Util.getShort(heap, (short)(instPtr+TLV_HEADER_SIZE+4)); + return KMByteBlob.cast(blobPtr).length(); } private static boolean validateKey(short key) { @@ -116,13 +117,4 @@ private static boolean validateKey(short key) { } return false; } - - public KMByteBlob getValue() { - return val; - } - - public KMByteTag setValue(KMByteBlob val) { - this.val = val; - return this; - } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMDecoder.java b/Applet/Applet/src/com/android/javacard/keymaster/KMDecoder.java index a1c43aa3..b4679a3c 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMDecoder.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMDecoder.java @@ -19,7 +19,7 @@ import javacard.framework.ISO7816; import javacard.framework.ISOException; import javacard.framework.Util; -// TODO Clean and refactor the code. + public class KMDecoder { // major types private static final short UINT_TYPE = 0x00; @@ -37,7 +37,6 @@ public class KMDecoder { private static final short UINT32_LENGTH = 0x1A; private static final short UINT64_LENGTH = 0x1B; - // TODO move the following to transient memory. private byte[] buffer; private short startOff; private short length; @@ -50,58 +49,176 @@ public KMDecoder() { length = 0; } - public KMArray decode(KMArray expression, byte[] buffer, short startOff, short length) { + public short decode(short expression, byte[] buffer, short startOff, short length) { this.buffer = buffer; this.startOff = startOff; - this.length = length; + this.length = (short)(startOff+length); return decode(expression); } - private KMEnumArrayTag decode(KMEnumArrayTag exp) { - readTagKey(exp.getTagType()); + private short decode(short exp){ + byte type = KMType.getType(exp); + switch(type){ + case KMType.BYTE_BLOB_TYPE: + return decodeByteBlob(exp); + case KMType.INTEGER_TYPE: + return decodeInteger(exp); + case KMType.ARRAY_TYPE: + return decodeArray(exp); + case KMType.ENUM_TYPE: + return decodeEnum(exp); + case KMType.KEY_PARAM_TYPE: + return decodeKeyParam(exp); + case KMType.KEY_CHAR_TYPE: + return decodeKeyChar(exp); + case KMType.VERIFICATION_TOKEN_TYPE: + return decodeVeriToken(exp); + case KMType.HMAC_SHARING_PARAM_TYPE: + return decodeHmacSharingParam(exp); + case KMType.HW_AUTH_TOKEN_TYPE: + return decodeHwAuthToken(exp); + case KMType.TAG_TYPE: + short tagType = KMTag.getTagType(exp); + return decodeTag(tagType, exp); + default: + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + return 0; + } + } + private short decodeTag(short tagType, short exp){ + switch(tagType){ + case KMType.BYTES_TAG: + return decodeBytesTag(exp); + case KMType.BOOL_TAG: + return decodeBoolTag(exp); + case KMType.UINT_TAG: + case KMType.ULONG_TAG: + case KMType.DATE_TAG: + return decodeIntegerTag(exp); + case KMType.ULONG_ARRAY_TAG: + case KMType.UINT_ARRAY_TAG: + return decodeIntegerArrayTag(exp); + case KMType.ENUM_TAG: + return decodeEnumTag(exp); + case KMType.ENUM_ARRAY_TAG: + return decodeEnumArrayTag(exp); + default: + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + return 0; + } + } + + private short decodeVeriToken(short exp) { + short vals = decode(KMVerificationToken.cast(exp).getVals()); + return KMVerificationToken.instance(vals); + } + + private short decodeHwAuthToken(short exp) { + short vals = decode(KMHardwareAuthToken.cast(exp).getVals()); + return KMHardwareAuthToken.cast(exp).instance(vals); + } + + private short decodeHmacSharingParam(short exp) { + short vals = decode(KMHmacSharingParameters.cast(exp).getVals()); + return KMHmacSharingParameters.instance(vals); + } + + private short decodeKeyChar(short exp) { + short vals = decode(KMKeyCharacteristics.cast(exp).getVals()); + return KMKeyCharacteristics.instance(vals); + } + + private short decodeKeyParam(short exp) { + short payloadLength = readMajorTypeWithPayloadLength(MAP_TYPE); + // allowed tags + short allowedTags = KMKeyParameters.cast(exp).getVals(); + short vals = KMArray.instance(payloadLength); + short length = KMArray.cast(allowedTags).length(); + short index = 0; + boolean tagFound; + short tagInd; + short tagType; + // For each tag in payload ... + while (index < payloadLength) { + tagFound = false; + tagInd = 0; + tagType = peekTagType(); + // Check against the allowed tags ... + while (tagInd < length) { + short tagClass = KMArray.cast(allowedTags).get(tagInd); + short allowedType = KMTag.getTagType(tagClass); + // If it is part of allowed tags ... + if (tagType == allowedType) { + // then decodeByteBlob and add that to the array. + KMArray.cast(vals).add(index, decode(tagClass)); + tagFound = true; + break; + } + tagInd++; + } + if(!tagFound){ + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + else { + index++; + } + } + return KMKeyParameters.instance(vals); + } + + private short decodeEnumArrayTag(short exp) { + readTagKey(KMEnumArrayTag.cast(exp).getTagType()); // The value must be byte blob // TODO check this out. - return exp.instance(this.tagKey, decode(exp.getValues())); + return KMEnumArrayTag.instance(this.tagKey, decode(KMEnumArrayTag.cast(exp).getValues())); } - private KMIntegerArrayTag decode(KMIntegerArrayTag exp) { - readTagKey(exp.getTagType()); + private short decodeIntegerArrayTag(short exp) { + readTagKey(KMIntegerArrayTag.cast(exp).getTagType()); // the values are array of integers. - if (!(exp.getValues().getType() instanceof KMInteger)) { - ISOException.throwIt(ISO7816.SW_DATA_INVALID); - } - return exp.instance(this.tagKey, decode(exp.getValues(), (KMInteger) exp.getValues().getType())); + return KMIntegerArrayTag.instance(KMIntegerArrayTag.cast(exp).getTagType(), + this.tagKey, decode(KMIntegerArrayTag.cast(exp).getValues())); } - private KMIntegerTag decode(KMIntegerTag exp) { - readTagKey(exp.getTagType()); + private short decodeIntegerTag(short exp) { + readTagKey(KMIntegerTag.cast(exp).getTagType()); // the value is an integer - return exp.instance(this.tagKey, decode(exp.getValue())); + return KMIntegerTag.instance(KMIntegerTag.cast(exp).getTagType(), + this.tagKey, decode(KMIntegerTag.cast(exp).getValue())); } - private KMByteTag decode(KMByteTag exp) { - short key = 0; - readTagKey(exp.getTagType()); + private short decodeBytesTag(short exp) { + readTagKey(KMByteTag.cast(exp).getTagType()); // The value must be byte blob - return exp.instance(this.tagKey, decode(exp.getValue())); + return KMByteTag.instance(this.tagKey, decode(KMByteTag.cast(exp).getValue())); } - private KMBoolTag decode(KMBoolTag exp) { - readTagKey(exp.getTagType()); - // BOOL Tag is a leaf node and it must always have tiny encoded uint value = 1. - // TODO check this out. - if ((buffer[startOff] & MAJOR_TYPE_MASK) != UINT_TYPE) { - ISOException.throwIt(ISO7816.SW_DATA_INVALID); + private short decodeArray(short exp) { + short payloadLength = readMajorTypeWithPayloadLength(ARRAY_TYPE); + if (KMArray.cast(exp).length() != payloadLength) { + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } - if ((byte) (buffer[startOff] & ADDITIONAL_MASK) != 0x01) { - ISOException.throwIt(ISO7816.SW_DATA_INVALID); + short arrPtr = KMArray.cast(exp).instance(payloadLength); + short index = 0; + // check whether array contains one type of objects or multiple types + if( KMArray.cast(exp).containedType() == 0){// multiple types specified by expression. + while (index < payloadLength) { + short type = KMArray.cast(exp).get(index); + KMArray.cast(arrPtr).add(index, decode(type)); + index++; + } + }else{ // Array is a Vector containing objects of one type + short type = KMArray.cast(exp).containedType(); + while(index < payloadLength){ + KMArray.cast(arrPtr).add(index, decode(type)); + index++; + } } - incrementStartOff((short) 1); - return exp.instance(tagKey); + return arrPtr; } - private KMEnumTag decode(KMEnumTag exp) { - readTagKey(exp.getTagType()); + private short decodeEnumTag(short exp) { + readTagKey(KMEnumTag.cast(exp).getTagType()); // Enum Tag value will always be integer with max 1 byte length. // TODO Check this out. if ((buffer[startOff] & MAJOR_TYPE_MASK) != UINT_TYPE) { @@ -120,17 +237,30 @@ private KMEnumTag decode(KMEnumTag exp) { enumVal = buffer[startOff]; incrementStartOff((short) 1); } - return exp.instance(tagKey, enumVal); + return KMEnumTag.instance(tagKey, enumVal); } - private KMEnum decode(KMEnum exp) { + private short decodeBoolTag(short exp) { + readTagKey(KMBoolTag.cast(exp).getTagType()); + // BOOL Tag is a leaf node and it must always have tiny encoded uint value = 1. + // TODO check this out. + if ((buffer[startOff] & MAJOR_TYPE_MASK) != UINT_TYPE) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + if ((byte) (buffer[startOff] & ADDITIONAL_MASK) != 0x01) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + incrementStartOff((short) 1); + return KMBoolTag.instance(tagKey); + } + private short decodeEnum(short exp) { // Enum value will always be integer with max 1 byte length. if ((buffer[startOff] & MAJOR_TYPE_MASK) != UINT_TYPE) { ISOException.throwIt(ISO7816.SW_DATA_INVALID); } short len = (short) (buffer[startOff] & ADDITIONAL_MASK); - byte enumVal = 0; + byte enumVal; if (len > UINT8_LENGTH) { ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } @@ -142,11 +272,11 @@ private KMEnum decode(KMEnum exp) { enumVal = buffer[startOff]; incrementStartOff((short) 1); } - return exp.instance(exp.getType(), enumVal); + return KMEnum.instance(KMEnum.cast(exp).getEnumType(), enumVal); } - private KMInteger decode(KMInteger exp) { - KMInteger inst; + private short decodeInteger(short exp) { + short inst; if ((buffer[startOff] & MAJOR_TYPE_MASK) != UINT_TYPE) { ISOException.throwIt(ISO7816.SW_DATA_INVALID); } @@ -156,156 +286,30 @@ private KMInteger decode(KMInteger exp) { } incrementStartOff((short) 1); if (len < UINT8_LENGTH) { - inst = exp.uint_8((byte)(len & ADDITIONAL_MASK)); + inst = KMInteger.uint_8((byte)(len & ADDITIONAL_MASK)); } else if (len == UINT8_LENGTH) { - inst = exp.instance(buffer, startOff, (short) 1); + inst = KMInteger.instance(buffer, startOff, (short) 1); incrementStartOff((short) 1); } else if (len == UINT16_LENGTH) { - inst = exp.instance(buffer, startOff, (short) 2); + inst = KMInteger.instance(buffer, startOff, (short) 2); incrementStartOff((short) 2); } else if (len == UINT32_LENGTH) { - inst = exp.instance(buffer, startOff, (short) 4); + inst = KMInteger.instance(buffer, startOff, (short) 4); incrementStartOff((short) 4); } else { - inst = exp.instance(buffer, startOff, (short) 8); + inst = KMInteger.instance(buffer, startOff, (short) 8); incrementStartOff((short) 8); } return inst; } - private KMByteBlob decode(KMByteBlob exp) { + private short decodeByteBlob(short exp) { short payloadLength = readMajorTypeWithPayloadLength(BYTES_TYPE); - KMByteBlob inst = exp.instance(buffer, startOff, payloadLength); + short inst = KMByteBlob.instance(buffer, startOff, payloadLength); incrementStartOff(payloadLength); return inst; } - private KMArray decode(KMArray exp) { - short payloadLength = readMajorTypeWithPayloadLength(ARRAY_TYPE); - if (exp.length() != payloadLength) { - ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); - } - KMArray inst = exp.instance(payloadLength); - short index = 0; - while (index < payloadLength) { - KMType type = exp.get(index); - inst.add(index, decode(type)); - index++; - } - return inst; - } - - private KMVector decode(KMVector exp, KMInteger type) { - short payloadLength = readMajorTypeWithPayloadLength(ARRAY_TYPE); - KMVector inst = exp.instance(type, payloadLength); - short index = 0; - while (index < payloadLength) { - inst.add(index, decode(type)); - index++; - } - return inst; - } - - private KMVerificationToken decode(KMVerificationToken exp) { - KMArray vals = decode(exp.getVals()); - return exp.instance(vals); - } - - private KMHardwareAuthToken decode(KMHardwareAuthToken exp) { - KMArray vals = decode(exp.getVals()); - return exp.instance(vals); - } - - private KMHmacSharingParameters decode(KMHmacSharingParameters exp) { - KMArray vals = decode(exp.getVals()); - return exp.instance(vals); - } - - private KMKeyParameters decode(KMKeyParameters exp) { - short payloadLength = readMajorTypeWithPayloadLength(MAP_TYPE); - // allowed tags - // TODO expand the logic to handle prototypes with tag values also. - KMArray allowedTags = exp.getVals(); - KMArray vals = KMArray.instance(payloadLength); - short index = 0; - while (index < payloadLength) { - short tagInd = 0; - short tagType = peekTagType(); - while (tagInd < allowedTags.length()) { - KMTag tagClass = ((KMTag) allowedTags.get(tagInd)); - short allowedType = ((KMTag) allowedTags.get(tagInd)).getTagType(); - if (tagType == allowedType) { - vals.add(index, decode(tagClass)); - break; - } - tagInd++; - } - index++; - } - return KMKeyParameters.instance(vals); - } - - private KMKeyCharacteristics decode(KMKeyCharacteristics exp) { - KMArray vals = decode(exp.getVals()); - return exp.instance(vals); - } - - private KMType decode(KMType exp) { - if (exp instanceof KMByteBlob) { - return decode((KMByteBlob) exp); - } - if (exp instanceof KMInteger) { - return decode((KMInteger) exp); - } - if (exp instanceof KMArray) { - return decode((KMArray) exp); - } - if (exp instanceof KMVector) { - if (!((((KMVector) exp).getType()) instanceof KMInteger)) { - ISOException.throwIt(ISO7816.SW_DATA_INVALID); - } - return decode((KMVector) exp, (KMInteger) ((KMVector) exp).getType()); - } - if (exp instanceof KMByteTag) { - return decode((KMByteTag) exp); - } - if (exp instanceof KMBoolTag) { - return decode((KMBoolTag) exp); - } - if (exp instanceof KMIntegerTag) { - return decode((KMIntegerTag) exp); - } - if (exp instanceof KMIntegerArrayTag) { - return decode((KMIntegerArrayTag) exp); - } - if (exp instanceof KMEnumTag) { - return decode((KMEnumTag) exp); - } - if (exp instanceof KMEnum) { - return decode((KMEnum) exp); - } - if (exp instanceof KMEnumArrayTag) { - return decode((KMEnumArrayTag) exp); - } - if (exp instanceof KMKeyParameters) { - return decode((KMKeyParameters) exp); - } - if (exp instanceof KMKeyCharacteristics) { - return decode((KMKeyCharacteristics) exp); - } - if (exp instanceof KMVerificationToken) { - return decode((KMVerificationToken) exp); - } - if (exp instanceof KMHmacSharingParameters) { - return decode((KMHmacSharingParameters) exp); - } - if (exp instanceof KMHardwareAuthToken) { - return decode((KMHardwareAuthToken) exp); - } - ISOException.throwIt(ISO7816.SW_DATA_INVALID); - return null; - } - private short peekTagType() { if ((buffer[startOff] & MAJOR_TYPE_MASK) != UINT_TYPE) { ISOException.throwIt(ISO7816.SW_DATA_INVALID); diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMEncoder.java b/Applet/Applet/src/com/android/javacard/keymaster/KMEncoder.java index ec736de4..5c96387a 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMEncoder.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMEncoder.java @@ -50,7 +50,7 @@ public KMEncoder() { length = 0; } - public short encode(KMArray object, byte[] buffer, short startOff, short length) { + public short encode(short object, byte[] buffer, short startOff, short length) { this.buffer = buffer; this.startOff = startOff; this.length = length; @@ -60,160 +60,153 @@ public short encode(KMArray object, byte[] buffer, short startOff, short length) return this.length; } - private void encode(KMType exp){ - if(exp instanceof KMByteBlob){ - encode((KMByteBlob) exp); - return; + private void encode(short exp){ + byte type = KMType.getType(exp); + switch(type){ + case KMType.BYTE_BLOB_TYPE: + encodeByteBlob(exp); + return; + case KMType.INTEGER_TYPE: + encodeInteger(exp); + return; + case KMType.ARRAY_TYPE: + encodeArray(exp); + return; + case KMType.ENUM_TYPE: + encodeEnum(exp); + return; + case KMType.KEY_PARAM_TYPE: + encodeKeyParam(exp); + return; + case KMType.KEY_CHAR_TYPE: + encodeKeyChar(exp); + return; + case KMType.VERIFICATION_TOKEN_TYPE: + encodeVeriToken(exp); + return; + case KMType.HMAC_SHARING_PARAM_TYPE: + encodeHmacSharingParam(exp); + return; + case KMType.HW_AUTH_TOKEN_TYPE: + encodeHwAuthToken(exp); + return; + case KMType.TAG_TYPE: + short tagType = KMTag.getTagType(exp); + encodeTag(tagType, exp); + return; + default: + ISOException.throwIt(ISO7816.SW_DATA_INVALID); } - if(exp instanceof KMEnum){ - encode((KMEnum) exp); - return; - } - if(exp instanceof KMInteger){ - encode((KMInteger)exp); - return; - } - if(exp instanceof KMArray){ - encode((KMArray)exp); - return; - } - if(exp instanceof KMVector){ - encode((KMVector)exp); - return; - } - if(exp instanceof KMByteTag){ - encode((KMByteTag)exp); - return; - } - if(exp instanceof KMBoolTag){ - encode((KMBoolTag) exp); - return; - } - if(exp instanceof KMIntegerTag){ - encode((KMIntegerTag)exp); - return; - } - if(exp instanceof KMIntegerArrayTag){ - encode((KMIntegerArrayTag)exp); - return; - } - if(exp instanceof KMEnumTag){ - encode((KMEnumTag) exp); - return; - } - if(exp instanceof KMEnumArrayTag){ - encode((KMEnumArrayTag) exp); - return; - } - if(exp instanceof KMKeyParameters){ - encode((KMKeyParameters) exp); - return; - } - if(exp instanceof KMKeyCharacteristics){ - encode((KMKeyCharacteristics) exp); - return; - } - if(exp instanceof KMVerificationToken){ - encode((KMVerificationToken) exp); - return; - } - if(exp instanceof KMHmacSharingParameters){ - encode((KMHmacSharingParameters) exp); - return; - } - if(exp instanceof KMHardwareAuthToken){ - encode((KMHardwareAuthToken) exp); - return; + } + private void encodeTag(short tagType, short exp){ + switch(tagType){ + case KMType.BYTES_TAG: + encodeBytesTag(exp); + return; + case KMType.BOOL_TAG: + encodeBoolTag(exp); + return; + case KMType.UINT_TAG: + case KMType.ULONG_TAG: + case KMType.DATE_TAG: + encodeIntegerTag(exp); + return; + case KMType.ULONG_ARRAY_TAG: + case KMType.UINT_ARRAY_TAG: + encodeIntegerArrayTag(exp); + return; + case KMType.ENUM_TAG: + encodeEnumTag(exp); + return; + case KMType.ENUM_ARRAY_TAG: + encodeEnumArrayTag(exp); + return; + default: + ISOException.throwIt(ISO7816.SW_DATA_INVALID); } - ISOException.throwIt(ISO7816.SW_DATA_INVALID); } - private void encode(KMKeyParameters obj) { - encodeAsMap(obj.getVals()); + private void encodeKeyParam(short obj) { + encodeAsMap(KMKeyParameters.cast(obj).getVals()); } - private void encode(KMKeyCharacteristics obj) { - encode(obj.getVals()); + private void encodeKeyChar(short obj) { + encode(KMKeyCharacteristics.cast(obj).getVals()); } - private void encode(KMVerificationToken obj) { - encode(obj.getVals()); + private void encodeVeriToken(short obj) { + encode(KMVerificationToken.cast(obj).getVals()); } - private void encode(KMHardwareAuthToken obj) { - encode(obj.getVals()); + private void encodeHwAuthToken(short obj) { + encode(KMHardwareAuthToken.cast(obj).getVals()); } - private void encode(KMHmacSharingParameters obj) { - encode(obj.getVals()); + private void encodeHmacSharingParam(short obj) { + encode(KMHmacSharingParameters.cast(obj).getVals()); } - private void encode(KMArray obj) { - writeMajorTypeWithLength(ARRAY_TYPE, obj.length()); + private void encodeArray(short obj) { + writeMajorTypeWithLength(ARRAY_TYPE, KMArray.cast(obj).length()); short index = 0; - while(index < obj.length()){ - encode(obj.get(index)); + short len = KMArray.cast(obj).length(); + while(index < len){ + encode(KMArray.cast(obj).get(index)); index++; } } - private void encodeAsMap(KMArray obj){ - writeMajorTypeWithLength(MAP_TYPE, obj.length()); + private void encodeAsMap(short obj){ + writeMajorTypeWithLength(MAP_TYPE, KMArray.cast(obj).length()); short index = 0; - while(index < obj.length()){ - KMType t = obj.get(index); - encode(t); - //encode(obj.get(index)); + short len = KMArray.cast(obj).length(); + while(index < len){ + short inst = KMArray.cast(obj).get(index); + encode(inst); index++; } } - private void encode(KMVector obj){ - writeMajorTypeWithLength(ARRAY_TYPE, obj.length()); - short index = 0; - while(index 0){ + if(val[(short)(startOff + index)] > 0){ break; } index++; // index will be equal to len if value is 0. @@ -222,26 +215,27 @@ private void encode(KMInteger obj) { short diff = (short)(len - index); if(diff == 0){ writeByte((byte)(UINT_TYPE | 0)); - }else if((diff == 1) && val[index] < UINT8_LENGTH){ - writeByte((byte)(UINT_TYPE | val[index])); + }else if((diff == 1) && val[(short)(startOff + index)] < UINT8_LENGTH){ + writeByte((byte)(UINT_TYPE | val[(short)(startOff + index)])); }else if (diff == 1){ writeByte((byte)(UINT_TYPE | UINT8_LENGTH)); - writeByte(val[index]); + writeByte(val[(short)(startOff + index)]); }else if(diff == 2){ writeByte((byte)(UINT_TYPE | UINT16_LENGTH)); - writeBytes(val, index, (short)2); + writeBytes(val, (short)(startOff + index), (short)2); }else if(diff <= 4){ writeByte((byte)(UINT_TYPE | UINT32_LENGTH)); - writeBytes(val, (short)(len - 4), (short)4); + writeBytes(val, (short)(startOff + len - 4), (short)4); }else { writeByte((byte)(UINT_TYPE | UINT64_LENGTH)); - writeBytes(val, (short)0, (short)8); + writeBytes(val, startOff, (short)8); } } - private void encode(KMByteBlob obj) { - writeMajorTypeWithLength(BYTES_TYPE, obj.length()); - writeBytes(obj.getVal(), obj.getStartOff(), obj.length()); + private void encodeByteBlob(short obj) { + writeMajorTypeWithLength(BYTES_TYPE, KMByteBlob.cast(obj).length()); + writeBytes(KMByteBlob.cast(obj).getBuffer(), KMByteBlob.cast(obj).getStartOff(), + KMByteBlob.cast(obj).length()); } private void writeByteValue(byte val){ @@ -258,7 +252,6 @@ private void writeTag(short tagType, short tagKey){ writeShort(tagType); writeShort(tagKey); } - // TODO bug here private void writeMajorTypeWithLength(byte majorType, short len) { if(len <= TINY_PAYLOAD){ writeByte((byte)(majorType | (byte) (len & ADDITIONAL_MASK))); diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMEnum.java b/Applet/Applet/src/com/android/javacard/keymaster/KMEnum.java index 441fd833..67196a81 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMEnum.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMEnum.java @@ -18,45 +18,63 @@ import javacard.framework.ISO7816; import javacard.framework.ISOException; +import javacard.framework.Util; public class KMEnum extends KMType { + private static KMEnum prototype; + private static short instPtr; + private static short[] types = {HARDWARE_TYPE, KEY_FORMAT, KEY_DERIVATION_FUNCTION}; private static Object[] enums = null; - private short type; - private byte val; + private KMEnum() {} - private KMEnum() { - init(); + private static KMEnum proto(short ptr) { + if (prototype == null) prototype = new KMEnum(); + instPtr = ptr; + return prototype; } - @Override - public void init() { - type = 0; - val = 0; + // pointer to an empty instance used as expression + public static short exp() { + return KMType.exp(ENUM_TYPE); } - @Override + public short length() { - return 1; + return Util.getShort(heap, (short) (instPtr + 1)); } - public static KMEnum instance() { - return repository.newEnum(); + // cast the ptr to KMByteBlob + public static KMEnum cast(short ptr) { + if (heap[ptr] != ENUM_TYPE) ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + if (Util.getShort(heap, (short) (ptr + 1)) == INVALID_VALUE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return proto(ptr); } - public static KMEnum instance(short enumType, byte val) { - KMEnum inst = repository.newEnum(); + public static short instance(short enumType) { + if (!validateEnum(enumType, NO_VALUE)) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + short ptr = KMType.instance(ENUM_TYPE, (short)2); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE), enumType); + return ptr; + } + + public static short instance(short enumType, byte val) { if (!validateEnum(enumType, val)) { ISOException.throwIt(ISO7816.SW_DATA_INVALID); } - inst.type = enumType; - inst.val = val; - return inst; + short ptr = KMType.instance(ENUM_TYPE, (short)3); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE), enumType); + heap[(short)(ptr+TLV_HEADER_SIZE+2)] = val; + return ptr; } - public static void create(KMEnum[] enumRefTable) { + private static void create() { if (enums == null) { enums = new Object[] { @@ -72,32 +90,27 @@ public static void create(KMEnum[] enumRefTable) { } }; } - byte index = 0; - while (index < enumRefTable.length) { - enumRefTable[index] = new KMEnum(); - index++; - } } - public KMEnum setVal(byte val) { - this.val = val; - return this; + public void setVal(byte val) { + heap[(short)(instPtr+TLV_HEADER_SIZE+2)] = val; } public byte getVal() { - return val; + return heap[(short)(instPtr+TLV_HEADER_SIZE+2)]; } - public KMEnum setType(short type) { - this.type = type; - return this; + public void setEnumType(short type) { + Util.setShort(heap, (short)(instPtr+TLV_HEADER_SIZE),type); } - public short getType() { - return type; + public short getEnumType() { + return Util.getShort(heap, (short)(instPtr+TLV_HEADER_SIZE)); } + // validate enumeration keys and values. private static boolean validateEnum(short key, byte value) { + create(); // check if key exists short index = (short) types.length; while (--index >= 0) { diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMEnumArrayTag.java b/Applet/Applet/src/com/android/javacard/keymaster/KMEnumArrayTag.java index 30c40bad..c0601267 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMEnumArrayTag.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMEnumArrayTag.java @@ -18,50 +18,101 @@ import javacard.framework.ISO7816; import javacard.framework.ISOException; +import javacard.framework.Util; public class KMEnumArrayTag extends KMTag { + private static KMEnumArrayTag prototype; + private static short instPtr; + // Arrays given below, together they form multi dimensional array. // Tag private static short[] tags = {PURPOSE, BLOCK_MODE, DIGEST, PADDING}; // Tag Values. private static Object[] enums = null; - // Tag Key - private short key; - // Byte Array of Tag Values. - private KMByteBlob array; - - // assignBlob constructor - private KMEnumArrayTag() { - init(); + + private KMEnumArrayTag() {} + + private static KMEnumArrayTag proto(short ptr) { + if (prototype == null) prototype = new KMEnumArrayTag(); + instPtr = ptr; + return prototype; + } + + // pointer to an empty instance used as expression + public static short exp() { + short blobPtr = KMByteBlob.exp(); + short ptr = instance(TAG_TYPE, (short)6); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE), ENUM_ARRAY_TAG); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE+2), INVALID_TAG); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE+4), blobPtr); + return ptr; } - @Override - public void init() { - key = 0; - array = null; + public static short instance(short key) { + byte[] vals = getAllowedEnumValues(key); + if (vals == null) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + short blobPtr = KMByteBlob.exp(); + return instance(key, blobPtr); + } + + public static short instance(short key, short byteBlob) { + byte[] allowedVals = getAllowedEnumValues(key); + if (allowedVals == null) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + KMByteBlob blob = KMByteBlob.cast(byteBlob); + short byteIndex = 0; + while (byteIndex < blob.length()) { + short enumIndex = 0; + boolean validValue = false; + while (enumIndex < allowedVals.length) { + if (blob.get(byteIndex) == allowedVals[enumIndex]) { + validValue = true; + break; + } + enumIndex++; + } + if (!validValue) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + byteIndex++; + } + short ptr = instance(TAG_TYPE, (short)6); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE), ENUM_ARRAY_TAG); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE+2), key); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE+4), byteBlob); + return ptr; + } + + public static KMEnumArrayTag cast(short ptr) { + if (heap[ptr] != TAG_TYPE) ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + if (Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)) != ENUM_ARRAY_TAG) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return proto(ptr); } - @Override public short getKey() { - return key; + return Util.getShort(heap, (short)(instPtr+TLV_HEADER_SIZE+2)); } - @Override public short getTagType() { return KMType.ENUM_ARRAY_TAG; } - // returns the length - @Override - public short length() { - return array.length(); + + public short getValues() { + return Util.getShort(heap, (short)(instPtr+TLV_HEADER_SIZE+4)); } - public static KMEnumArrayTag instance() { - return repository.newEnumArrayTag(); + public short length() { + short blobPtr = Util.getShort(heap, (short)(instPtr+TLV_HEADER_SIZE+4)); + return KMByteBlob.cast(blobPtr).length(); } - public static void create(KMEnumArrayTag[] enumArrayTagRefTable) { + public static void create() { if (enums == null) { enums = new Object[] { @@ -73,35 +124,10 @@ public static void create(KMEnumArrayTag[] enumArrayTagRefTable) { } }; } - byte index = 0; - while (index < enumArrayTagRefTable.length) { - enumArrayTagRefTable[index] = new KMEnumArrayTag(); - index++; - } - } - - // create default assignBlob without any value array - public static KMEnumArrayTag instance(short key) { - // check if key is valid. - byte[] vals = getAllowedEnumValues(key); - if (vals == null) { - ISOException.throwIt(ISO7816.SW_DATA_INVALID); - } - KMEnumArrayTag tag = repository.newEnumArrayTag(); - tag.key = key; - tag.array = null; - return tag; } - // Set the expected length for the prototype. - public KMEnumArrayTag withLength(short length) { - array.withLength(length); - return this; - } - - // get the allowed enum values for given tag key private static byte[] getAllowedEnumValues(short key) { - // check if key is allowed + create(); short index = (short) tags.length; while (--index >= 0) { if (tags[index] == key) { @@ -111,41 +137,4 @@ private static byte[] getAllowedEnumValues(short key) { return null; } - // get value array of this tag assignBlob. - public KMByteBlob getValues() { - return this.array; - } - - public KMEnumArrayTag setValues(KMByteBlob val) { - this.array = val; - return this; - } - // instantiate enum array pointing to existing array. - public static KMEnumArrayTag instance(short key, KMByteBlob blob) { - // validate key - byte[] allowedVals = getAllowedEnumValues(key); - if (allowedVals == null) { - ISOException.throwIt(ISO7816.SW_DATA_INVALID); - } - short byteIndex = 0; - while (byteIndex < blob.length()) { - short enumIndex = 0; - boolean validValue = false; - while (enumIndex < allowedVals.length) { - if (blob.get(byteIndex) == allowedVals[enumIndex]) { - validValue = true; - break; - } - enumIndex++; - } - if (!validValue) { - ISOException.throwIt(ISO7816.SW_DATA_INVALID); - } - byteIndex++; - } - KMEnumArrayTag tag = repository.newEnumArrayTag(); - tag.key = key; - tag.array = blob; - return tag; - } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMEnumTag.java b/Applet/Applet/src/com/android/javacard/keymaster/KMEnumTag.java index 9a641425..8f66e5e3 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMEnumTag.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMEnumTag.java @@ -18,8 +18,11 @@ import javacard.framework.ISO7816; import javacard.framework.ISOException; +import javacard.framework.Util; public class KMEnumTag extends KMTag { + private static KMEnumTag prototype; + private static short instPtr; private static short[] tags = { ALGORITHM, ECCURVE, BLOB_USAGE_REQ, USER_AUTH_TYPE, ORIGIN, HARDWARE_TYPE @@ -27,49 +30,63 @@ public class KMEnumTag extends KMTag { private static Object[] enums = null; - private short key; - private byte val; + private KMEnumTag() {} - // assignBlob constructor - private KMEnumTag() { - init(); + private static KMEnumTag proto(short ptr) { + if (prototype == null) prototype = new KMEnumTag(); + instPtr = ptr; + return prototype; } - @Override - public void init() { - key = 0; - val = 0; + // pointer to an empty instance used as expression + public static short exp() { + short ptr = instance(TAG_TYPE, (short)2); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE), ENUM_TAG); + return ptr; } - @Override - public short getKey() { - return key; + public static short instance(short key) { + if(!validateEnum(key, NO_VALUE)){ + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + short ptr = KMType.instance(TAG_TYPE, (short)4); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE), ENUM_TAG); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE+2), key); + return ptr; } - @Override - public short length() { - return 1; + public static short instance(short key, byte val) { + if(!validateEnum(key, val)){ + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + short ptr = instance(TAG_TYPE, (short)5); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE), ENUM_TAG); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE+2), key); + heap[(short)(ptr+TLV_HEADER_SIZE+4)]= val; + return ptr; } - @Override - public short getTagType() { - return KMType.ENUM_TAG; + public static KMEnumTag cast(short ptr) { + if (heap[ptr] != TAG_TYPE) ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + if (Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)) != ENUM_TAG) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return proto(ptr); } - public static KMEnumTag instance() { - return repository.newEnumTag(); + public short getKey() { + return Util.getShort(heap, (short)(instPtr+TLV_HEADER_SIZE+2)); } - public static KMEnumTag instance(short key) { - if(!validateEnum(key, NO_VALUE)){ - ISOException.throwIt(ISO7816.SW_DATA_INVALID); - } - KMEnumTag tag = repository.newEnumTag(); - tag.key = key; - return tag; + public short getTagType() { + return KMType.ENUM_TAG; } - public static void create(KMEnumTag[] enumTagRefTable) { + public byte getValue() { + return heap[(short)(instPtr+TLV_HEADER_SIZE+4)]; + } + + public static void create() { if (enums == null) { enums = new Object[] { @@ -81,15 +98,11 @@ public static void create(KMEnumTag[] enumTagRefTable) { new byte[] {SOFTWARE, TRUSTED_ENVIRONMENT, STRONGBOX} }; } - byte index = 0; - while (index < enumTagRefTable.length) { - enumTagRefTable[index] = new KMEnumTag(); - index++; - } } // validate enumeration keys and values. private static boolean validateEnum(short key, byte value) { + create(); // check if key exists short index = (short) tags.length; while (--index >= 0) { @@ -115,19 +128,4 @@ private static boolean validateEnum(short key, byte value) { // return false if key does not exist return false; } - - // get value of this tag assignBlob. - public byte getValue() { - return val; - } - - // instantiate enum tag. - public static KMEnumTag instance(short key, byte val) { - if (!validateEnum(key, val)) { - ISOException.throwIt(ISO7816.SW_DATA_INVALID); - } - KMEnumTag tag = instance(key); - tag.val = val; - return tag; - } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMHardwareAuthToken.java b/Applet/Applet/src/com/android/javacard/keymaster/KMHardwareAuthToken.java index 9c188c67..c5c92d86 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMHardwareAuthToken.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMHardwareAuthToken.java @@ -18,6 +18,7 @@ import javacard.framework.ISO7816; import javacard.framework.ISOException; +import javacard.framework.Util; public class KMHardwareAuthToken extends KMType { public static final byte CHALLENGE = 0x00; @@ -27,76 +28,122 @@ public class KMHardwareAuthToken extends KMType { public static final byte TIMESTAMP = 0x04; public static final byte MAC = 0x05; - private KMArray vals; + private static KMHardwareAuthToken prototype; + private static short instPtr; + + private KMHardwareAuthToken() {} + + public static short exp() { + short arrPtr = KMArray.instance((short)6); + KMArray arr = KMArray.cast(arrPtr); + arr.add(CHALLENGE, KMInteger.exp()); + arr.add(USER_ID, KMInteger.exp()); + arr.add(AUTHENTICATOR_ID, KMInteger.exp()); + arr.add(HW_AUTHENTICATOR_TYPE, KMEnumTag.instance(KMType.USER_AUTH_TYPE)); + arr.add(TIMESTAMP, KMInteger.exp()); + arr.add(MAC, KMByteBlob.exp()); + return instance(arrPtr); + } + + private static KMHardwareAuthToken proto(short ptr) { + if (prototype == null) prototype = new KMHardwareAuthToken(); + instPtr = ptr; + return prototype; + } - private KMHardwareAuthToken() { - init(); + public static short instance() { + short arrPtr = KMArray.instance((short)6); + return instance(arrPtr); } - @Override - public void init() { - vals = null; + public static short instance(short vals) { + KMArray arr = KMArray.cast(vals); + if(arr.length() != 6)ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + short ptr = KMType.instance(HW_AUTH_TOKEN_TYPE, (short)2); + Util.setShort(heap, (short)(ptr + TLV_HEADER_SIZE), vals); + return ptr; + } + + public static KMHardwareAuthToken cast(short ptr) { + if (heap[ptr] != HW_AUTH_TOKEN_TYPE) ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + short arrPtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)); + if(heap[arrPtr] != ARRAY_TYPE) ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + return proto(ptr); + } + + public short getVals() { + return Util.getShort(heap, (short) (instPtr + TLV_HEADER_SIZE)); } - @Override public short length() { - return vals.length(); + short arrPtr = getVals(); + return KMArray.cast(arrPtr).length(); + } + + public short getChallenge() { + short arrPtr = getVals(); + return KMArray.cast(arrPtr).get(CHALLENGE); + } + + public void setChallenge(short vals) { + KMInteger.cast(vals); + short arrPtr = getVals(); + KMArray.cast(arrPtr).add(CHALLENGE, vals); } - public static KMHardwareAuthToken instance() { - KMHardwareAuthToken inst = repository.newHwAuthToken(); - inst.vals = KMArray.instance((short) 6); - inst.vals.add(CHALLENGE, KMInteger.instance()); - inst.vals.add(USER_ID, KMInteger.instance()); - inst.vals.add(AUTHENTICATOR_ID, KMInteger.instance()); - inst.vals.add(HW_AUTHENTICATOR_TYPE, KMEnumTag.instance(KMType.USER_AUTH_TYPE)); - inst.vals.add(TIMESTAMP, KMInteger.instance()); - inst.vals.add(MAC, KMByteBlob.instance()); - return inst; + public short getUserId() { + short arrPtr = getVals(); + return KMArray.cast(arrPtr).get(USER_ID); } - public static KMHardwareAuthToken instance(KMArray vals) { - if (vals.length() != 6) { - ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); - } - KMHardwareAuthToken inst = repository.newHwAuthToken(); - inst.vals = vals; - return inst; + public void setUserId(short vals) { + KMInteger.cast(vals); + short arrPtr = getVals(); + KMArray.cast(arrPtr).add(USER_ID, vals); } - public static void create(KMHardwareAuthToken[] hwAuthTokenRefTable) { - byte index = 0; - while (index < hwAuthTokenRefTable.length) { - hwAuthTokenRefTable[index] = new KMHardwareAuthToken(); - index++; - } + public short getAuthenticatorId() { + short arrPtr = getVals(); + return KMArray.cast(arrPtr).get(AUTHENTICATOR_ID); } - public KMInteger getChallenge() { - return (KMInteger) vals.get(CHALLENGE); + public void setAuthenticatorId(short vals) { + KMInteger.cast(vals); + short arrPtr = getVals(); + KMArray.cast(arrPtr).add(AUTHENTICATOR_ID, vals); } - public KMInteger getUserId() { - return (KMInteger) vals.get(USER_ID); + public short getHwAuthenticatorType() { + short arrPtr = getVals(); + return KMArray.cast(arrPtr).get(HW_AUTHENTICATOR_TYPE); } - public KMInteger getAuthenticatorId() { - return (KMInteger) vals.get(AUTHENTICATOR_ID); + public void setHwAuthenticatorType(short vals) { + short key = KMEnumTag.cast(vals).getKey(); + if(key != USER_AUTH_TYPE) ISOException.throwIt(ISO7816.SW_DATA_INVALID); + short arrPtr = getVals(); + KMArray.cast(arrPtr).add(HW_AUTHENTICATOR_TYPE, vals); } - public byte getHwAuthenticatorType() { - return ((KMEnumTag) vals.get(HW_AUTHENTICATOR_TYPE)).getValue(); + public short getTimestamp() { + short arrPtr = getVals(); + return KMArray.cast(arrPtr).get(TIMESTAMP); } - public KMInteger getTimestamp() { - return (KMInteger) vals.get(TIMESTAMP); + public void setTimestamp(short vals) { + KMInteger.cast(vals); + short arrPtr = getVals(); + KMArray.cast(arrPtr).add(TIMESTAMP, vals); } - public KMByteBlob getMac() { - return (KMByteBlob) vals.get(MAC); + public short getMac() { + short arrPtr = getVals(); + return KMArray.cast(arrPtr).get(MAC); } - public KMArray getVals() { - return vals; + public void setMac(short vals) { + KMByteBlob.cast(vals); + short arrPtr = getVals(); + KMArray.cast(arrPtr).add(MAC, vals); } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMHmacSharingParameters.java b/Applet/Applet/src/com/android/javacard/keymaster/KMHmacSharingParameters.java index a1fca4d1..bc34c217 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMHmacSharingParameters.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMHmacSharingParameters.java @@ -18,60 +18,78 @@ import javacard.framework.ISO7816; import javacard.framework.ISOException; +import javacard.framework.Util; public class KMHmacSharingParameters extends KMType { public static final byte SEED = 0x00; public static final byte NONCE = 0x01; - private KMArray vals; - private KMHmacSharingParameters() { - init(); + private static KMHmacSharingParameters prototype; + private static short instPtr; + + private KMHmacSharingParameters() {} + + public static short exp() { + short arrPtr = KMArray.instance((short)2); + KMArray arr = KMArray.cast(arrPtr); + arr.add(SEED, KMByteBlob.exp()); + arr.add(NONCE, KMByteBlob.exp()); + return instance(arrPtr); } - @Override - public void init() { - vals = null; + private static KMHmacSharingParameters proto(short ptr) { + if (prototype == null) prototype = new KMHmacSharingParameters(); + instPtr = ptr; + return prototype; } - @Override - public short length() { - return vals.length(); + public static short instance() { + short arrPtr = KMArray.instance((short)2); + return instance(arrPtr); + } + + public static short instance(short vals) { + short ptr = KMType.instance(HMAC_SHARING_PARAM_TYPE, (short)2); + if(KMArray.cast(vals).length() != 2) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + Util.setShort(heap, (short)(ptr + TLV_HEADER_SIZE), vals); + return ptr; + } + + public static KMHmacSharingParameters cast(short ptr) { + if (heap[ptr] != HMAC_SHARING_PARAM_TYPE) ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + short arrPtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)); + if(heap[arrPtr] != ARRAY_TYPE) ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + return proto(ptr); } - public static KMHmacSharingParameters instance() { - KMHmacSharingParameters inst = repository.newHmacSharingParameters(); - inst.vals = KMArray.instance((short) 2); - inst.vals.add(SEED, KMByteBlob.instance()); - inst.vals.add(NONCE, KMByteBlob.instance()); - return inst; + public short getVals() { + return Util.getShort(heap, (short) (instPtr + TLV_HEADER_SIZE)); } - public static KMHmacSharingParameters instance(KMArray vals) { - if (vals.length() != 2) { - ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); - } - KMHmacSharingParameters inst = repository.newHmacSharingParameters(); - inst.vals = vals; - return inst; + public short length() { + short arrPtr = getVals(); + return KMArray.cast(arrPtr).length(); } - public static void create(KMHmacSharingParameters[] hmacSharingParamsRefTable) { - byte index = 0; - while (index < hmacSharingParamsRefTable.length) { - hmacSharingParamsRefTable[index] = new KMHmacSharingParameters(); - index++; - } + public void setSeed(short vals) { + KMByteBlob.cast(vals); + short arrPtr = getVals(); + KMArray.cast(arrPtr).add(SEED, vals); } - public KMByteBlob getSeed() { - return (KMByteBlob) vals.get(SEED); + public void setNonce(short vals) { + KMByteBlob.cast(vals); + short arrPtr = getVals(); + KMArray.cast(arrPtr).add(NONCE, vals); } - public KMByteBlob getNonce() { - return (KMByteBlob) vals.get(NONCE); + public short getNonce() { + short arrPtr = getVals(); + return KMArray.cast(arrPtr).get(NONCE); } - public KMArray getVals() { - return vals; + public short getSeed() { + short arrPtr = getVals(); + return KMArray.cast(arrPtr).get(SEED); } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMInteger.java b/Applet/Applet/src/com/android/javacard/keymaster/KMInteger.java index 987a0858..d4efe987 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMInteger.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMInteger.java @@ -18,117 +18,115 @@ import javacard.framework.ISO7816; import javacard.framework.ISOException; +import javacard.framework.JCSystem; import javacard.framework.Util; // Represents 8 bit, 16 bit, 32 bit and 64 bit integers public class KMInteger extends KMType { - private byte[] val; + private static final short UINT_32 = 4; + private static final short UINT_64 = 8; + private static KMInteger prototype; + private static short instPtr; - private KMInteger() { - init(); + private KMInteger() {} + + private static KMInteger proto(short ptr) { + if (prototype == null) prototype = new KMInteger(); + instPtr = ptr; + return prototype; } - @Override - public void init() { - val = null; + public static short exp() { + return KMType.exp(INTEGER_TYPE); } - @Override - public short length() { - return (short) this.val.length; + // return an empty integer instance + public static short instance(short length) { + if ((length <= 0) || (length > 8)) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + if (length > 4) { + length = UINT_64; + } else { + length = UINT_32; + } + return KMType.instance(INTEGER_TYPE, length); + } + + public static short instance(byte[] num, short srcOff, short length) { + if (length > 8) { + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + } + if (length == 1) { + return uint_8(num[srcOff]); + } else if (length == 2) { + return uint_16(Util.getShort(num, srcOff)); + } else if (length == 4) { + return uint_32(num, srcOff); + } else { + return uint_64(num, srcOff); + } } - public static KMInteger instance() { - return repository.newInteger(); + public static KMInteger cast(short ptr) { + byte[] heap = repository.getHeap(); + if (heap[ptr] != INTEGER_TYPE) ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + if (Util.getShort(heap, (short) (ptr + 1)) == INVALID_VALUE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return proto(ptr); } // create integer and copy byte value - public static KMInteger uint_8(byte num) { - KMInteger inst = repository.newInteger(); - inst.val = repository.newIntegerArray((short) 4); - inst.val[3] = num; - return inst; + public static short uint_8(byte num) { + short ptr = instance(UINT_32); + heap[(short) (ptr + TLV_HEADER_SIZE + 3)] = num; + return ptr; } // create integer and copy short value - public static KMInteger uint_16(short num) { - KMInteger inst = repository.newInteger(); - inst.val = repository.newIntegerArray((short) 4); - inst.val[2] = (byte) ((num >> 8) & 0xff); - inst.val[3] = (byte) (num & 0xff); - return inst; + public static short uint_16(short num) { + short ptr = instance(UINT_32); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), num); + return ptr; } // create integer and copy integer value - public static KMInteger uint_32(byte[] num, short offset) { - KMInteger inst = repository.newInteger(); - inst.val = repository.newIntegerArray((short) 4); - Util.arrayCopy(num, offset, inst.val, (short) 0, (short) 4); - return inst; + public static short uint_32(byte[] num, short offset) { + short ptr = instance(UINT_32); + Util.arrayCopy(num, offset, heap, (short) (ptr + TLV_HEADER_SIZE), UINT_32); + return ptr; } // create integer and copy integer value - public static KMInteger uint_64(byte[] num, short offset) { - KMInteger inst = repository.newInteger(); - inst.val = repository.newIntegerArray((short) 8); - Util.arrayCopy(num, offset, inst.val, (short) 0, (short) 8); - return inst; + public static short uint_64(byte[] num, short offset) { + short ptr = instance(UINT_64); + Util.arrayCopy(num, offset, heap, (short) (ptr + TLV_HEADER_SIZE), UINT_64); + return ptr; } - public static void create(KMInteger[] integerRefTable) { - byte index = 0; - while (index < integerRefTable.length) { - integerRefTable[index] = new KMInteger(); - index++; - } + // Get the length of the integer + public short length() { + return Util.getShort(heap, (short) (instPtr + 1)); } - public byte[] getValue() { - return val; + // Get the buffer pointer in which blob is contained. + public byte[] getBuffer() { + return heap; } - public KMInteger setValue(short val) { - this.val[2] = (byte) (val >> 8); - this.val[3] = (byte) (val & 0xFF); - return this; + // Get the start of value + public short getStartOff() { + return (short) (instPtr + TLV_HEADER_SIZE); } - public KMInteger setValue(byte[] val) { - this.val = val; - return this; + public void getValue(byte[] dest, short destOff, short length){ + Util.arrayCopyNonAtomic(heap, (short)(instPtr+TLV_HEADER_SIZE), dest, destOff, length); } public short getShort() { - if (val == null) { - ISOException.throwIt(ISO7816.SW_DATA_INVALID); - } else if (val.length != 4) { - ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); - } - return Util.makeShort(val[2], val[3]); + return Util.getShort(heap, (short) (instPtr + TLV_HEADER_SIZE + 2)); } public byte getByte() { - if (val == null) { - ISOException.throwIt(ISO7816.SW_DATA_INVALID); - } else if (val.length != 4) { - ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); - } - return val[3]; - } - - // copy the integer value from bytes - public static KMInteger instance(byte[] num, short srcOff, short length) { - if(length > 8){ - ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); - } - if (length == 1) { - return uint_8(num[srcOff]); - } else if (length == 2) { - return uint_16(Util.makeShort(num[srcOff], num[(short) (srcOff + 1)])); - } else if (length == 4) { - return uint_32(num, srcOff); - } else { - return uint_64(num, srcOff); - } + return heap[(short) (instPtr + TLV_HEADER_SIZE + 3)]; } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMIntegerArrayTag.java b/Applet/Applet/src/com/android/javacard/keymaster/KMIntegerArrayTag.java index df96e114..775ebfd2 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMIntegerArrayTag.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMIntegerArrayTag.java @@ -18,77 +18,96 @@ import javacard.framework.ISO7816; import javacard.framework.ISOException; +import javacard.framework.Util; public class KMIntegerArrayTag extends KMTag { + private static KMIntegerArrayTag prototype; + private static short instPtr; + private static final short[] tags = {USER_SECURE_ID}; - private short key; - private KMVector vals; - private short tagType; - private KMIntegerArrayTag() { - init(); + private KMIntegerArrayTag() {} + + private static KMIntegerArrayTag proto(short ptr) { + if (prototype == null) prototype = new KMIntegerArrayTag(); + instPtr = ptr; + return prototype; } - @Override - public void init() { - key = 0; - vals = null; - tagType = KMType.UINT_ARRAY_TAG; + public static short exp(short tagType) { + if (!validateTagType(tagType)) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + short arrPtr = KMArray.exp(KMType.INTEGER_TYPE); + short ptr = instance(TAG_TYPE, (short)6); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE), tagType); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE+2), INVALID_TAG); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE+4), arrPtr); + return ptr; } - @Override - public short getKey() { - return key; + public static short instance(short tagType, short key) { + if (!validateTagType(tagType)) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + if (!validateKey(key)) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + short arrPtr = KMArray.exp(); + return instance(tagType, key, arrPtr); } - @Override - public short length() { - return this.vals.length(); + public static short instance(short tagType, short key, short arrObj) { + if (!validateTagType(tagType)) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + if (!validateKey(key)) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + if(heap[arrObj] != ARRAY_TYPE) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + short ptr = instance(TAG_TYPE, (short)6); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE), tagType); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE+2), key); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE+4), arrObj); + return ptr; + } + + public static KMIntegerArrayTag cast(short ptr) { + if (heap[ptr] != TAG_TYPE) ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + short tagType = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)); + if (!validateTagType(tagType)) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return proto(ptr); } - @Override public short getTagType() { - return tagType; + return Util.getShort(heap, (short)(instPtr+TLV_HEADER_SIZE)); } - public static KMIntegerArrayTag instance() { - return repository.newIntegerArrayTag(); + public short getKey() { + return Util.getShort(heap, (short)(instPtr+TLV_HEADER_SIZE+2)); } - public static void create(KMIntegerArrayTag[] intArrayTagRefTable) { - byte index = 0; - while (index < intArrayTagRefTable.length) { - intArrayTagRefTable[index] = new KMIntegerArrayTag(); - index++; - } + public short getValues() { + return Util.getShort(heap, (short)(instPtr+TLV_HEADER_SIZE+4)); } - public KMIntegerArrayTag asUlongArray() { - tagType = KMType.ULONG_ARRAY_TAG; - return this; + public short length() { + short ptr = getValues(); + return KMIntegerArrayTag.cast(ptr).length(); } - public static KMIntegerArrayTag instance(short key) { - if (!validateKey(key)) { - ISOException.throwIt(ISO7816.SW_DATA_INVALID); - } - KMIntegerArrayTag tag = repository.newIntegerArrayTag(); - tag.key = key; - tag.vals = KMVector.instance(KMInteger.instance()); - return tag; + public void add(short index, short val) { + KMArray arr = KMArray.cast(getValues()); + arr.add(index, val); } - public static KMIntegerArrayTag instance(short key, KMVector val) { - if (!(val.getType() instanceof KMInteger)) { - ISOException.throwIt(ISO7816.SW_DATA_INVALID); - } - if (!(validateKey(key))) { - ISOException.throwIt(ISO7816.SW_DATA_INVALID); - } - KMIntegerArrayTag tag = repository.newIntegerArrayTag(); - tag.key = key; - tag.vals = val; - return tag; + public short get(short index) { + KMArray arr = KMArray.cast(getValues()); + return arr.get(index); } private static boolean validateKey(short key) { @@ -101,25 +120,11 @@ private static boolean validateKey(short key) { return false; } - public KMIntegerArrayTag withLength(short length) { - this.vals.withLength(length); - return this; - } - - public KMVector getValues() { - return this.vals; - } - - public KMIntegerArrayTag setValues(KMVector vals) { - this.vals = vals; - return this; - } - - public void add(short index, KMInteger val) { - this.vals.add(index, val); - } - - public KMInteger get(short index) { - return (KMInteger) this.vals.get(index); + // TODO this should be combined with validateKey to actually validate {tagType, tagKey} pair. + private static boolean validateTagType(short tagType) { + if ((tagType == ULONG_ARRAY_TAG) || (tagType == UINT_ARRAY_TAG)) { + return true; + } + return false; } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMIntegerTag.java b/Applet/Applet/src/com/android/javacard/keymaster/KMIntegerTag.java index d583f6b2..6aa3a1b4 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMIntegerTag.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMIntegerTag.java @@ -18,9 +18,13 @@ import javacard.framework.ISO7816; import javacard.framework.ISOException; +import javacard.framework.Util; // Implements UINT, ULONG and DATE tags. public class KMIntegerTag extends KMTag { + private static KMIntegerTag prototype; + private static short instPtr; + private static final short[] tags = { // UINT KEYSIZE, @@ -43,67 +47,81 @@ public class KMIntegerTag extends KMTag { CREATION_DATETIME }; - private short key; - private KMInteger val; - private short tagType; + private KMIntegerTag() {} - private KMIntegerTag() { - init(); + private static KMIntegerTag proto(short ptr) { + if (prototype == null) prototype = new KMIntegerTag(); + instPtr = ptr; + return prototype; } - @Override - public void init() { - key = 0; - val = null; - tagType = KMType.UINT_TAG; + public static short exp(short tagType) { + if (!validateTagType(tagType)) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + short intPtr = KMInteger.exp(); + short ptr = instance(TAG_TYPE, (short)6); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE), tagType); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE+2), INVALID_TAG); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE+4), intPtr); + return ptr; } - @Override - public short getKey() { - return key; + public static short instance(short tagType, short key) { + if (!validateTagType(tagType)) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + if (!validateKey(key)) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + short intPtr = KMInteger.exp(); + return instance(tagType, key, intPtr); } - @Override - public short length() { - return (short) val.getValue().length; + public static short instance(short tagType, short key, short intObj) { + if (!validateTagType(tagType)) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + if (!validateKey(key)) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + if(heap[intObj] != INTEGER_TYPE) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + short ptr = instance(TAG_TYPE, (short)6); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE), tagType); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE+2), key); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE+4), intObj); + return ptr; } - @Override - public short getTagType() { - return tagType; + public static KMIntegerTag cast(short ptr) { + if (heap[ptr] != TAG_TYPE) ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + short tagType = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)); + if (!validateTagType(tagType)) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return proto(ptr); } - public static KMIntegerTag instance() { - return repository.newIntegerTag(); + public short getTagType() { + return Util.getShort(heap, (short)(instPtr+TLV_HEADER_SIZE)); } - public static KMIntegerTag instance(short key) { - if (!validateKey(key)) { - ISOException.throwIt(ISO7816.SW_DATA_INVALID); - } - KMIntegerTag tag = repository.newIntegerTag(); - tag.key = key; - tag.val = null; - return tag; + public short getKey() { + return Util.getShort(heap, (short)(instPtr+TLV_HEADER_SIZE+2)); } - public static KMIntegerTag instance(short givenKey, KMInteger val) { - KMIntegerTag tag = KMIntegerTag.instance(givenKey); - tag.val = val; - if (val.length() == 8) { - tag.tagType = KMType.ULONG_TAG; - } - return tag; + public short getValue() { + return Util.getShort(heap, (short)(instPtr+TLV_HEADER_SIZE+4)); } - public static void create(KMIntegerTag[] intTagRefTable) { - byte index = 0; - while (index < intTagRefTable.length) { - intTagRefTable[index] = new KMIntegerTag(); - index++; - } + public short length() { + KMInteger obj = KMInteger.cast(getValue()); + return obj.length(); } + private static boolean validateKey(short key) { short index = (short) tags.length; while (--index >= 0) { @@ -114,22 +132,12 @@ private static boolean validateKey(short key) { return false; } - public KMInteger getValue() { - return this.val; - } - - public KMIntegerTag setValue(KMInteger val) { - this.val = val; - return this; - } - - public KMIntegerTag asULong() { - tagType = KMType.ULONG_TAG; - return this; + // TODO this should be combined with validateKey to actually validate {tagType, tagKey} pair. + private static boolean validateTagType(short tagType) { + if ((tagType == DATE_TAG) || (tagType == UINT_TAG) || (tagType == ULONG_TAG)) { + return true; + } + return false; } - public KMIntegerTag asDate() { - tagType = KMType.DATE_TAG; - return this; - } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMKeyCharacteristics.java b/Applet/Applet/src/com/android/javacard/keymaster/KMKeyCharacteristics.java index d4c1a9b5..0421e6da 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMKeyCharacteristics.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMKeyCharacteristics.java @@ -18,60 +18,79 @@ import javacard.framework.ISO7816; import javacard.framework.ISOException; +import javacard.framework.Util; public class KMKeyCharacteristics extends KMType { public static final byte SOFTWARE_ENFORCED = 0x00; public static final byte HARDWARE_ENFORCED = 0x01; - private KMArray vals; + private static KMKeyCharacteristics prototype; + private static short instPtr; - private KMKeyCharacteristics() { - init(); + private KMKeyCharacteristics() {} + + public static short exp() { + short softEnf = KMKeyParameters.exp(); + short hwEnf = KMKeyParameters.exp(); + short arrPtr = KMArray.instance((short)2); + KMArray arr = KMArray.cast(arrPtr); + arr.add(SOFTWARE_ENFORCED, softEnf); + arr.add(HARDWARE_ENFORCED, hwEnf); + return instance(arrPtr); } - @Override - public void init() { - vals = null; + private static KMKeyCharacteristics proto(short ptr) { + if (prototype == null) prototype = new KMKeyCharacteristics(); + instPtr = ptr; + return prototype; } - @Override - public short length() { - return vals.length(); + public static short instance() { + short arrPtr = KMArray.instance((short)2); + return instance(arrPtr); + } + + public static short instance(short vals) { + short ptr = KMType.instance(KEY_CHAR_TYPE, (short)2); + if(KMArray.cast(vals).length() != 2) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + Util.setShort(heap, (short)(ptr + TLV_HEADER_SIZE), vals); + return ptr; } - public static KMKeyCharacteristics instance() { - KMKeyCharacteristics inst = repository.newKeyCharacteristics(); - inst.vals = KMArray.instance((short) 2); - inst.vals.add(SOFTWARE_ENFORCED, KMKeyParameters.instance()); - inst.vals.add(HARDWARE_ENFORCED, KMKeyParameters.instance()); - return inst; + public static KMKeyCharacteristics cast(short ptr) { + if (heap[ptr] != KEY_CHAR_TYPE) ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + short arrPtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)); + if(heap[arrPtr] != ARRAY_TYPE) ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + return proto(ptr); } - public static KMKeyCharacteristics instance(KMArray vals) { - if (vals.length() != 2) { - ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); - } - KMKeyCharacteristics inst = repository.newKeyCharacteristics(); - inst.vals = vals; - return inst; + public short getVals() { + return Util.getShort(heap, (short) (instPtr + TLV_HEADER_SIZE)); + } + + public short length() { + short arrPtr = getVals(); + return KMArray.cast(arrPtr).length(); } - public static void create(KMKeyCharacteristics[] keyCharRefTable) { - byte index = 0; - while (index < keyCharRefTable.length) { - keyCharRefTable[index] = new KMKeyCharacteristics(); - index++; - } + public short getSoftwareEnforced() { + short arrPtr = getVals(); + return KMArray.cast(arrPtr).get(SOFTWARE_ENFORCED); } - public KMKeyParameters getSoftwareEnforced() { - return (KMKeyParameters) vals.get(SOFTWARE_ENFORCED); + public short getHardwareEnforced() { + short arrPtr = getVals(); + return KMArray.cast(arrPtr).get(HARDWARE_ENFORCED); } - public KMKeyParameters getHardwareEnforced() { - return (KMKeyParameters) vals.get(HARDWARE_ENFORCED); + public void setSoftwareEnforced(short ptr) { + KMKeyParameters.cast(ptr); + short arrPtr = getVals(); + KMArray.cast(arrPtr).add(SOFTWARE_ENFORCED, ptr); } - public KMArray getVals() { - return vals; + public void setHardwareEnforced(short ptr) { + KMKeyParameters.cast(ptr); + short arrPtr = getVals(); + KMArray.cast(arrPtr).add(HARDWARE_ENFORCED, ptr); } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java b/Applet/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java index 44e25e98..428ef78c 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java @@ -16,53 +16,58 @@ package com.android.javacard.keymaster; + +import javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.Util; + public class KMKeyParameters extends KMType { - private KMArray vals; + private static KMKeyParameters prototype; + private static short instPtr; - private KMKeyParameters() { - init(); - } + private KMKeyParameters() {} - @Override - public void init() { - vals = null; + private static KMKeyParameters proto(short ptr) { + if (prototype == null) prototype = new KMKeyParameters(); + instPtr = ptr; + return prototype; } - @Override - public short length() { - return vals.length(); + public static short exp() { + short arrPtr = KMArray.instance((short)9); + KMArray arr = KMArray.cast(arrPtr); + arr.add((short) 0, KMIntegerTag.exp(UINT_TAG)); + arr.add((short) 1, KMIntegerArrayTag.exp(UINT_ARRAY_TAG)); + arr.add((short) 2, KMIntegerTag.exp(ULONG_TAG)); + arr.add((short) 3, KMIntegerTag.exp(DATE_TAG)); + arr.add((short) 4, KMIntegerArrayTag.exp(ULONG_ARRAY_TAG)); + arr.add((short) 5, KMEnumTag.exp()); + arr.add((short) 6, KMEnumArrayTag.exp()); + arr.add((short) 7, KMByteTag.exp()); + arr.add((short) 8, KMBoolTag.exp()); + return instance(arrPtr); } - public static KMKeyParameters instance() { - KMKeyParameters inst = repository.newKeyParameters(); - inst.vals = KMArray.instance((short) 9); - inst.vals.add((short) 0, KMIntegerTag.instance()); - inst.vals.add((short) 1, KMIntegerArrayTag.instance()); - inst.vals.add((short) 2, KMIntegerTag.instance().asULong()); - inst.vals.add((short) 3, KMIntegerTag.instance().asDate()); - inst.vals.add((short) 4, KMIntegerArrayTag.instance().asUlongArray()); - inst.vals.add((short) 5, KMEnumTag.instance()); - inst.vals.add((short) 6, KMEnumArrayTag.instance()); - inst.vals.add((short) 7, KMByteTag.instance()); - inst.vals.add((short) 8, KMBoolTag.instance()); - return inst; + public static short instance(short vals) { + short ptr = KMType.instance(KEY_PARAM_TYPE, (short)2); + Util.setShort(heap, (short)(ptr + TLV_HEADER_SIZE), vals); + return ptr; } - public static KMKeyParameters instance(KMArray vals) { - KMKeyParameters inst = repository.newKeyParameters(); - inst.vals = vals; - return inst; + public static KMKeyParameters cast(short ptr) { + if (heap[ptr] != KEY_PARAM_TYPE) ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + short arrPtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)); + if(heap[arrPtr] != ARRAY_TYPE) ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + return proto(ptr); } - public static void create(KMKeyParameters[] keyParametersRefTable) { - byte index = 0; - while (index < keyParametersRefTable.length) { - keyParametersRefTable[index] = new KMKeyParameters(); - index++; - } + public short getVals() { + return Util.getShort(heap, (short) (instPtr + TLV_HEADER_SIZE)); } - public KMArray getVals() { - return vals; + public short length() { + short arrPtr = getVals(); + return KMArray.cast(arrPtr).length(); } + } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index 0ee85ff1..427296df 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -22,7 +22,6 @@ import javacard.framework.CardRuntimeException; import javacard.framework.ISO7816; import javacard.framework.ISOException; -import javacard.framework.JCSystem; import javacard.framework.Util; import javacardx.apdu.ExtendedLength; @@ -71,11 +70,6 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe // GetHwInfo information // TODO change this to just filling the buffer - private static final byte[] JavacardKeymasterDevice = { - 0x4A, 0x61, 0x76, 0x61, 0x63, 0x61, 0x72, 0x64, 0x4B, 0x65, 0x79, 0x6D, 0x61, 0x73, 0x74, 0x65, - 0x72, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, - }; - private static final byte[] Google = {0x47, 0x6F, 0x6F, 0x67, 0x6C, 0x65}; private static final short MAX_SEED_SIZE = 2048; // State of the applet. @@ -84,12 +78,15 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe private KMRepository repository; private byte keymasterState = ILLEGAL_STATE; private byte[] buffer; + private short bufferStartOffset; + private short bufferLength; /** Registers this applet. */ protected KMKeymasterApplet() { keymasterState = KMKeymasterApplet.INSTALL_STATE; repository = KMRepository.instance(); KMUtil.init(); + KMType.initialize(); encoder = new KMEncoder(); decoder = new KMDecoder(); register(); @@ -113,7 +110,7 @@ public static void install(byte[] bArray, short bOffset, byte bLength) { */ @Override public boolean select() { - KMRepository.instance().onSelect(); + repository.onSelect(); if (keymasterState == KMKeymasterApplet.INSTALL_STATE) { keymasterState = KMKeymasterApplet.FIRST_SELECT_STATE; } else if (keymasterState == KMKeymasterApplet.INACTIVE_STATE) { @@ -127,7 +124,7 @@ public boolean select() { /** De-selects this applet. */ @Override public void deselect() { - KMRepository.instance().onDeselect(); + repository.onDeselect(); if (keymasterState == KMKeymasterApplet.ACTIVE_STATE) { keymasterState = KMKeymasterApplet.INACTIVE_STATE; } @@ -136,7 +133,7 @@ public void deselect() { /** Uninstalls the applet after cleaning the repository. */ @Override public void uninstall() { - KMRepository.instance().onUninstall(); + repository.onUninstall(); if (keymasterState != KMKeymasterApplet.UNINSTALLED_STATE) { keymasterState = KMKeymasterApplet.UNINSTALLED_STATE; } @@ -149,10 +146,7 @@ public void uninstall() { */ @Override public void process(APDU apdu) { - KMRepository.instance().onProcess(); - if (buffer == null) { - buffer = JCSystem.makeTransientByteArray(MAX_LENGTH, JCSystem.CLEAR_ON_RESET); - } + repository.onProcess(); // Verify whether applet is in correct state. if ((keymasterState != KMKeymasterApplet.ACTIVE_STATE) && (keymasterState != KMKeymasterApplet.FIRST_SELECT_STATE)) { @@ -188,177 +182,174 @@ public void process(APDU apdu) { // Process the apdu try { // Handle the command - handle(apduIns, apdu); + switch (apduIns) { + case INS_GENERATE_KEY_CMD: + processGenerateKey(apdu); + break; + case INS_IMPORT_KEY_CMD: + processImportKeyCmd(apdu); + break; + case INS_IMPORT_WRAPPED_KEY_CMD: + processImportWrappedKeyCmd(apdu); + break; + case INS_EXPORT_KEY_CMD: + processExportKeyCmd(apdu); + break; + case INS_ATTEST_KEY_CMD: + processAttestKeyCmd(apdu); + break; + case INS_UPGRADE_KEY_CMD: + processUpgradeKeyCmd(apdu); + break; + case INS_DELETE_KEY_CMD: + processDeleteKeyCmd(apdu); + break; + case INS_DELETE_ALL_KEYS_CMD: + processDeleteAllKeysCmd(apdu); + break; + case INS_ADD_RNG_ENTROPY_CMD: + processAddRngEntropyCmd(apdu); + break; + case INS_COMPUTE_SHARED_HMAC_CMD: + processComputeSharedHmacCmd(apdu); + break; + case INS_DESTROY_ATT_IDS_CMD: + processDestroyAttIdsCmd(apdu); + break; + case INS_VERIFY_AUTHORIZATION_CMD: + processVerifyAuthenticationCmd(apdu); + break; + case INS_GET_HMAC_SHARING_PARAM_CMD: + processGetHmacSharingParamCmd(apdu); + break; + case INS_GET_KEY_CHARACTERISTICS_CMD: + processGetKeyCharacteristicsCmd(apdu); + break; + case INS_GET_HW_INFO_CMD: + processGetHwInfoCmd(apdu); + break; + case INS_BEGIN_OPERATION_CMD: + processBeginOperationCmd(apdu); + break; + case INS_UPDATE_OPERATION_CMD: + processUpdateOperationCmd(apdu); + break; + case INS_FINISH_OPERATION_CMD: + processFinishOperationCmd(apdu); + break; + case INS_ABORT_OPERATION_CMD: + processAbortOperationCmd(apdu); + break; + case INS_PROVISION_CMD: + processProvisionCmd(apdu); + break; + default: + ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); + } + } catch (CardRuntimeException exception) { if (!(KMException.handle(exception.getReason()))) { CardRuntimeException.throwIt(exception.getReason()); } } finally { - // Reset the buffer. - Util.arrayFillNonAtomic(buffer, (short) 0, MAX_LENGTH, (byte) 0); + repository.clean(); } } /** Sends a response, may be extended response, as requested by the command. */ - private void sendOutgoing(byte[] srcBuffer, short srcLength, APDU apdu) { - if (srcLength > MAX_LENGTH) { + private void sendOutgoing(APDU apdu) { + if (bufferLength > MAX_LENGTH) { ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } // Send data apdu.setOutgoing(); - apdu.setOutgoingLength(srcLength); - apdu.sendBytesLong(srcBuffer, (short) 0, srcLength); + apdu.setOutgoingLength(bufferLength); + apdu.sendBytesLong(buffer, bufferStartOffset, bufferLength); } /** Receives data, which can be extended data, as requested by the command instance. */ - private short receiveIncoming(byte[] destBuffer, APDU apdu) { - // Initialize source + private void receiveIncoming(APDU apdu) { byte[] srcBuffer = apdu.getBuffer(); - // Initialize destination - short destOffset = (short) 0; - // Receive data short recvLen = apdu.setIncomingAndReceive(); short srcOffset = apdu.getOffsetCdata(); - short srcLength = apdu.getIncomingLength(); - if (srcLength > MAX_LENGTH) { + bufferLength = apdu.getIncomingLength(); + bufferStartOffset = repository.alloc(bufferLength); + buffer = repository.getHeap(); + short index = bufferStartOffset; + // Receive data + if (bufferLength > MAX_LENGTH) { ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } - while (recvLen > 0) { - Util.arrayCopyNonAtomic(srcBuffer, srcOffset, destBuffer, destOffset, recvLen); - destOffset += recvLen; + while (recvLen > 0 && ((short)(index - bufferStartOffset) < bufferLength)) { + Util.arrayCopyNonAtomic(srcBuffer, srcOffset, buffer, index, recvLen); + index += recvLen; recvLen = apdu.receiveBytes(srcOffset); } - return srcLength; } - // Commands - private void handle(byte ins, APDU apdu) { - switch (ins) { - case INS_GENERATE_KEY_CMD: - processGenerateKey(apdu); - break; - case INS_IMPORT_KEY_CMD: - processImportKeyCmd(apdu); - break; - case INS_IMPORT_WRAPPED_KEY_CMD: - processImportWrappedKeyCmd(apdu); - break; - case INS_EXPORT_KEY_CMD: - processExportKeyCmd(apdu); - break; - case INS_ATTEST_KEY_CMD: - processAttestKeyCmd(apdu); - break; - case INS_UPGRADE_KEY_CMD: - processUpgradeKeyCmd(apdu); - break; - case INS_DELETE_KEY_CMD: - processDeleteKeyCmd(apdu); - break; - case INS_DELETE_ALL_KEYS_CMD: - processDeleteAllKeysCmd(apdu); - break; - case INS_ADD_RNG_ENTROPY_CMD: - processAddRngEntropyCmd(apdu); - break; - case INS_COMPUTE_SHARED_HMAC_CMD: - processComputeSharedHmacCmd(apdu); - break; - case INS_DESTROY_ATT_IDS_CMD: - processDestroyAttIdsCmd(apdu); - break; - case INS_VERIFY_AUTHORIZATION_CMD: - processVerifyAuthenticationCmd(apdu); - break; - case INS_GET_HMAC_SHARING_PARAM_CMD: - processGetHmacSharingParamCmd(apdu); - break; - case INS_GET_KEY_CHARACTERISTICS_CMD: - processGetKeyCharacteristicsCmd(apdu); - break; - case INS_GET_HW_INFO_CMD: - processGetHwInfoCmd(apdu); - break; - case INS_BEGIN_OPERATION_CMD: - processBeginOperationCmd(apdu); - break; - case INS_UPDATE_OPERATION_CMD: - processUpdateOperationCmd(apdu); - break; - case INS_FINISH_OPERATION_CMD: - processFinishOperationCmd(apdu); - break; - case INS_ABORT_OPERATION_CMD: - processAbortOperationCmd(apdu); - break; - case INS_PROVISION_CMD: - processProvisionCmd(apdu); - break; - default: - ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); - } - } - - private short processProvisionCmd(APDU apdu) { - // Receive the incoming request fully from the master. - short length = receiveIncoming(buffer, apdu); + private void processProvisionCmd(APDU apdu) { + // Receive the incoming request fully from the master into buffer. + receiveIncoming(apdu); // Re-purpose the apdu buffer as scratch pad. byte[] scratchPad = apdu.getBuffer(); Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) apdu.getBuffer().length, (byte) 0); // Argument 1 - KMKeyParameters keyparams = KMKeyParameters.instance(); + short keyparams = KMKeyParameters.exp(); // Argument 2 - KMEnum keyFormat = KMEnum.instance().setType(KMType.KEY_FORMAT); + short keyFormat = KMEnum.instance(KMType.KEY_FORMAT); // Argument 3 - KMByteBlob keyBlob = KMByteBlob.instance(); + short keyBlob = KMByteBlob.exp(); // Array of expected arguments - KMArray argsProto = - KMArray.instance((short) 3) - .add((short) 0, keyparams) - .add((short) 1, keyFormat) - .add((short) 2, keyBlob); + short argsProto = KMArray.instance((short) 3); + KMArray.cast(argsProto).add((short) 0, keyparams); + KMArray.cast(argsProto).add((short) 1, keyFormat); + KMArray.cast(argsProto).add((short) 2, keyBlob); // Decode the argument - KMArray args = decoder.decode(argsProto, buffer, (short) 0, length); + short args = decoder.decode(argsProto, buffer, bufferStartOffset, bufferLength); // TODO execute the function // Change the state to ACTIVE if (keymasterState == KMKeymasterApplet.FIRST_SELECT_STATE) { keymasterState = KMKeymasterApplet.ACTIVE_STATE; } - // nothing to return - return 0; } private void processGetHwInfoCmd(APDU apdu) { // No arguments expected + final byte[] JavacardKeymasterDevice = { + 0x4A, 0x61, 0x76, 0x61, 0x63, 0x61, 0x72, 0x64, 0x4B, 0x65, 0x79, 0x6D, 0x61, 0x73, 0x74, 0x65, + 0x72, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, + }; + final byte[] Google = {0x47, 0x6F, 0x6F, 0x67, 0x6C, 0x65}; + // Make the response - KMArray resp = - KMArray.instance((short) 3) - .add((short) 0, KMEnum.instance(KMType.HARDWARE_TYPE, KMType.STRONGBOX)) - .add( - (short) 1, - KMByteBlob.instance( - JavacardKeymasterDevice, (short) 0, (short) JavacardKeymasterDevice.length)) - .add((short) 2, KMByteBlob.instance(Google, (short) 0, (short) Google.length)); - // Reuse the buffer - Util.arrayFillNonAtomic(buffer, (short) 0, MAX_LENGTH, (byte) 0); - // Encode the response - short len = encoder.encode(resp, buffer, (short) 0, MAX_LENGTH); - sendOutgoing(buffer, len, apdu); + short respPtr = KMArray.instance((short) 3); + KMArray resp = KMArray.cast(respPtr); + resp.add((short) 0, KMEnum.instance(KMType.HARDWARE_TYPE, KMType.STRONGBOX)); + resp.add((short) 1, KMByteBlob.instance( + JavacardKeymasterDevice, (short) 0, (short) JavacardKeymasterDevice.length)); + resp.add((short) 2, KMByteBlob.instance(Google, (short) 0, (short) Google.length)); + //TODO change from MAX_LENGTH to actual length. + buffer = repository.getHeap(); + bufferStartOffset = repository.alloc((short)128); + // Encode the response - actual bufferLength is 86 + bufferLength = encoder.encode(respPtr, buffer, bufferStartOffset, MAX_LENGTH); + // send buffer to master + sendOutgoing(apdu); } - private short processAddRngEntropyCmd(APDU apdu) { + private void processAddRngEntropyCmd(APDU apdu) { // Receive the incoming request fully from the master. - short length = receiveIncoming(buffer, apdu); + receiveIncoming(apdu); // Re-purpose the apdu buffer as scratch pad. byte[] scratchPad = apdu.getBuffer(); Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) apdu.getBuffer().length, (byte) 0); // Argument 1 - KMArray argsProto = KMArray.instance((short) 1).add((short) 0, KMByteBlob.instance()); - + short argsProto = KMArray.instance((short) 1); + KMArray.cast(argsProto).add((short) 0, KMByteBlob.exp()); // Decode the argument - KMArray args = decoder.decode(argsProto, buffer, (short) 0, length); + short args = decoder.decode(argsProto, buffer, bufferStartOffset, bufferLength); // Process - KMByteBlob blob = (KMByteBlob) args.get((short) 0); + KMByteBlob blob = KMByteBlob.cast(KMArray.cast(args).get((short) 0)); // Maximum 2KiB of seed is allowed. if (blob.length() > MAX_SEED_SIZE) { ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); @@ -366,8 +357,8 @@ private short processAddRngEntropyCmd(APDU apdu) { // Get existing entropy pool. byte[] entPool = KMUtil.getEntropyPool(); // Create new temporary pool. - byte[] heapRef = repository.getByteHeapRef(); - short poolStart = repository.newByteArray((short) entPool.length); + byte[] heapRef = repository.getHeap(); + short poolStart = repository.alloc((short) entPool.length); // Populate the new pool with the entropy which is derived from current entropy pool. KMUtil.newRandomNumber(heapRef, poolStart, (short) entPool.length); // Copy the entropy to the current pool - updates the entropy pool. @@ -384,75 +375,73 @@ private short processAddRngEntropyCmd(APDU apdu) { randIndex = 0; } } - // Nothing to return - return 0; } - private short processAbortOperationCmd(APDU apdu) { - return 0; + private void processAbortOperationCmd(APDU apdu) { + } - private short processFinishOperationCmd(APDU apdu) { - return 0; + private void processFinishOperationCmd(APDU apdu) { + } - private short processUpdateOperationCmd(APDU apdu) { - return 0; + private void processUpdateOperationCmd(APDU apdu) { + } - private short processBeginOperationCmd(APDU apdu) { - return 0; + private void processBeginOperationCmd(APDU apdu) { + } - private short processGetKeyCharacteristicsCmd(APDU apdu) { - return 0; + private void processGetKeyCharacteristicsCmd(APDU apdu) { + } - private short processGetHmacSharingParamCmd(APDU apdu) { - return 0; + private void processGetHmacSharingParamCmd(APDU apdu) { + } - private short processVerifyAuthenticationCmd(APDU apdu) { - return 0; + private void processVerifyAuthenticationCmd(APDU apdu) { + } - private short processDestroyAttIdsCmd(APDU apdu) { - return 0; + private void processDestroyAttIdsCmd(APDU apdu) { + } - private short processComputeSharedHmacCmd(APDU apdu) { - return 0; + private void processComputeSharedHmacCmd(APDU apdu) { + } - private short processDeleteAllKeysCmd(APDU apdu) { - return 0; + private void processDeleteAllKeysCmd(APDU apdu) { + } - private short processDeleteKeyCmd(APDU apdu) { - return 0; + private void processDeleteKeyCmd(APDU apdu) { + } - private short processUpgradeKeyCmd(APDU apdu) { - return 0; + private void processUpgradeKeyCmd(APDU apdu) { + } - private short processAttestKeyCmd(APDU apdu) { - return 0; + private void processAttestKeyCmd(APDU apdu) { + } - private short processExportKeyCmd(APDU apdu) { - return 0; + private void processExportKeyCmd(APDU apdu) { + } - private short processImportWrappedKeyCmd(APDU apdu) { - return 0; + private void processImportWrappedKeyCmd(APDU apdu) { + } - private short processImportKeyCmd(APDU apdu) { - return 0; + private void processImportKeyCmd(APDU apdu) { + } - private short processGenerateKey(APDU apdu) { - return 0; + private void processGenerateKey(APDU apdu) { + } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMRepository.java b/Applet/Applet/src/com/android/javacard/keymaster/KMRepository.java index b71aea01..3492d4b5 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMRepository.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMRepository.java @@ -28,63 +28,11 @@ // to handle onInstall and onSelect. public class KMRepository { - private static final byte REF_TABLE_SIZE = 5; - private static final short HEAP_SIZE = 0x1000; - private static final byte INT_TABLE_SIZE = 10; - private static final byte TYPE_ARRAY_SIZE = 100; - private static final byte INT_SIZE = 4; - private static final byte LONG_SIZE = 8; + public static final short HEAP_SIZE = 0x1000; private static KMRepository repository; - private AESKey masterKey = null; - private KMEncoder encoder = null; - private KMDecoder decoder = null; - - private KMByteBlob[] byteBlobRefTable = null; - private byte blobRefIndex = 0; - private KMInteger[] integerRefTable = null; - private byte intRefIndex = 0; - private KMArray[] arrayRefTable = null; - private byte arrayRefIndex = 0; - private KMVector[] vectorRefTable = null; - private byte vectorRefIndex = 0; - private KMEnum[] enumRefTable = null; - private byte enumRefIndex = 0; - private KMByteTag[] byteTagRefTable = null; - private byte byteTagRefIndex = 0; - private KMIntegerTag[] intTagRefTable = null; - private byte intTagRefIndex = 0; - private KMIntegerArrayTag[] intArrayTagRefTable = null; - private byte intArrayTagRefIndex = 0; - private KMEnumTag[] enumTagRefTable = null; - private byte enumTagRefIndex = 0; - private KMEnumArrayTag[] enumArrayTagRefTable = null; - private byte enumArrayTagRefIndex = 0; - private KMBoolTag[] boolTagRefTable = null; - private byte boolTagRefIndex = 0; - private KMKeyParameters[] keyParametersRefTable = null; - private byte keyParametersRefIndex = 0; - private KMKeyCharacteristics[] keyCharRefTable = null; - private byte keyCharRefIndex = 0; - private KMVerificationToken[] verTokenRefTable = null; - private byte verTokenRefIndex = 0; - private KMHmacSharingParameters[] hmacSharingParamsRefTable = null; - private byte hmacSharingParamsRefIndex = 0; - private KMHardwareAuthToken[] hwAuthTokenRefTable = null; - private byte hwAuthTokenRefIndex = 0; - private KMOperationState[] opStateRefTable = null; - private byte opStateRefIndex = 0; - private KMType[] typeRefTable = null; - private byte typeRefIndex = 0; - - private byte[] byteHeap = null; - private short byteHeapIndex = 0; - private Object[] uint32Array = null; - private byte uint32Index = 0; - private Object[] uint64Array = null; - private byte uint64Index = 0; - private KMOperationState[] operationStateTable = null; - private byte[] entropyPool = null; - private byte[] counter; + private AESKey masterKey; + private byte[] heap; + private short heapIndex; public static KMRepository instance() { if (repository == null) { @@ -94,380 +42,42 @@ public static KMRepository instance() { } public KMRepository(){ - initialize(); - } - - private void initialize() { - // Initialize buffers and context. - JCSystem.beginTransaction(); - encoder = new KMEncoder(); - decoder = new KMDecoder(); - operationStateTable = new KMOperationState[4]; + heap = JCSystem.makeTransientByteArray(HEAP_SIZE, JCSystem.CLEAR_ON_RESET); // Initialize masterkey - AES 256 bit key. if (masterKey == null) { masterKey = - (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_256, false); - } - // Initialize types - KMType.initialize(this); - byteBlobRefTable = new KMByteBlob[(short)(REF_TABLE_SIZE*4)]; - KMByteBlob.create(byteBlobRefTable); - integerRefTable = new KMInteger[REF_TABLE_SIZE]; - KMInteger.create(integerRefTable); - arrayRefTable = new KMArray[(short)(REF_TABLE_SIZE*4)]; - KMArray.create(arrayRefTable); - vectorRefTable = new KMVector[REF_TABLE_SIZE]; - KMVector.create(vectorRefTable); - enumRefTable = new KMEnum[(short)(REF_TABLE_SIZE*2)]; - KMEnum.create(enumRefTable); - byteTagRefTable = new KMByteTag[REF_TABLE_SIZE]; - KMByteTag.create(byteTagRefTable); - intTagRefTable = new KMIntegerTag[REF_TABLE_SIZE]; - KMIntegerTag.create(intTagRefTable); - intArrayTagRefTable = new KMIntegerArrayTag[REF_TABLE_SIZE]; - KMIntegerArrayTag.create(intArrayTagRefTable); - enumTagRefTable = new KMEnumTag[REF_TABLE_SIZE]; - KMEnumTag.create(enumTagRefTable); - enumArrayTagRefTable = new KMEnumArrayTag[REF_TABLE_SIZE]; - KMEnumArrayTag.create(enumArrayTagRefTable); - boolTagRefTable = new KMBoolTag[REF_TABLE_SIZE]; - KMBoolTag.create(boolTagRefTable); - keyParametersRefTable = new KMKeyParameters[REF_TABLE_SIZE]; - KMKeyParameters.create(keyParametersRefTable); - keyCharRefTable = new KMKeyCharacteristics[REF_TABLE_SIZE]; - KMKeyCharacteristics.create(keyCharRefTable); - verTokenRefTable = new KMVerificationToken[REF_TABLE_SIZE]; - KMVerificationToken.create(verTokenRefTable); - hmacSharingParamsRefTable = new KMHmacSharingParameters[REF_TABLE_SIZE]; - KMHmacSharingParameters.create(hmacSharingParamsRefTable); - hwAuthTokenRefTable = new KMHardwareAuthToken[REF_TABLE_SIZE]; - KMHardwareAuthToken.create(hwAuthTokenRefTable); - opStateRefTable = new KMOperationState[REF_TABLE_SIZE]; - KMOperationState.create(opStateRefTable); - - byteHeap = new byte[HEAP_SIZE]; - uint32Array = new Object[INT_TABLE_SIZE]; - uint64Array = new Object[INT_TABLE_SIZE]; - typeRefTable = new KMType[TYPE_ARRAY_SIZE]; - - short index = 0; - while (index < INT_TABLE_SIZE) { - uint32Array[index] = new byte[INT_SIZE]; - uint64Array[index] = new byte[LONG_SIZE]; - index++; + (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_256, false); } - - JCSystem.commitTransaction(); - } - - public KMEncoder getEncoder() { - return encoder; - } - - public KMDecoder getDecoder() { - return decoder; } public void onUninstall() { masterKey = null; } - public void onProcess() { - reset(); - } + public void onProcess() {} - private void reset() { - Util.arrayFillNonAtomic(byteHeap, (short) 0, (short) byteHeap.length, (byte) 0); - byteHeapIndex = 0; - short index = 0; - while (index < typeRefTable.length) { - typeRefTable[index] = null; - index++; - } - typeRefIndex = 0; - index = 0; - while (index < uint32Array.length) { - byte[] num = (byte[]) uint32Array[index]; - byte numIndex = 0; - while (numIndex < INT_SIZE) { - num[numIndex] = 0; - numIndex++; - } - index++; - } - uint32Index = 0; - index = 0; - while (index < uint64Array.length) { - byte[] num = (byte[]) uint64Array[index]; - byte numIndex = 0; - while (numIndex < LONG_SIZE) { - num[numIndex] = 0; - numIndex++; - } - index++; - } - uint64Index = 0; - resetTypeObjects(byteBlobRefTable); - resetTypeObjects(integerRefTable); - resetTypeObjects(enumRefTable); - resetTypeObjects(byteTagRefTable); - resetTypeObjects(boolTagRefTable); - resetTypeObjects(arrayRefTable); - resetTypeObjects(enumTagRefTable); - resetTypeObjects(enumArrayTagRefTable); - resetTypeObjects(intTagRefTable); - resetTypeObjects(intArrayTagRefTable); - resetTypeObjects(vectorRefTable); - resetTypeObjects(keyCharRefTable); - resetTypeObjects(keyParametersRefTable); - resetTypeObjects(hmacSharingParamsRefTable); - resetTypeObjects(hwAuthTokenRefTable); - resetTypeObjects(verTokenRefTable); + public void clean(){ + Util.arrayFillNonAtomic(heap, (short) 0, heapIndex, (byte) 0); + heapIndex = 0; } - public void resetTypeObjects(KMType[] type){ - byte index = 0; - while(index < type.length){ - type[index].init(); - index++; - } - } - public void onDeselect() { - // TODO clear operation state? - } + public void onDeselect() {} - public void onSelect() { - // Nothing to be done currently. - } + public void onSelect() {} public AESKey getMasterKey() { return masterKey; } - // Allocate 4 bytes or 8 bytes buffer - public byte[] newIntegerArray(short length) { - if (length == 4) { - if (uint32Index >= uint32Array.length) { - // TODO this is placeholder exception value. This needs to be replaced by 910E, 91A1 or 9210 - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - byte[] ret = (byte[]) uint32Array[uint32Index]; - uint32Index++; - return ret; - } else if (length == 8) { - if (uint64Index >= uint64Array.length) { - // TODO this is placeholder exception value. This needs to be replaced by 910E, 91A1 or 9210 - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - byte[] ret = (byte[]) uint64Array[uint64Index]; - uint64Index++; - return ret; - } else { - ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); - } - return null;// this will never be executed. - } - - public KMByteBlob newByteBlob() { - if (blobRefIndex >= byteBlobRefTable.length) { - // TODO this is placeholder exception value. - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - KMByteBlob ret = byteBlobRefTable[blobRefIndex]; - blobRefIndex++; - return ret; - } - - public KMInteger newInteger() { - if (intRefIndex >= integerRefTable.length) { - // TODO this is placeholder exception value. - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - KMInteger ret = integerRefTable[intRefIndex]; - intRefIndex++; - return ret; - } - - public KMEnumTag newEnumTag() { - if (enumTagRefIndex >= enumTagRefTable.length) { - // TODO this is placeholder exception value. - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - KMEnumTag ret = enumTagRefTable[enumTagRefIndex]; - enumTagRefIndex++; - return ret; - } - - public KMEnumArrayTag newEnumArrayTag() { - if (enumArrayTagRefIndex >= enumArrayTagRefTable.length) { - // TODO this is placeholder exception value. - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - KMEnumArrayTag ret = enumArrayTagRefTable[enumArrayTagRefIndex]; - enumArrayTagRefIndex++; - return ret; - } - - public KMIntegerTag newIntegerTag() { - if (intTagRefIndex >= intTagRefTable.length) { - // TODO this is placeholder exception value. - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - KMIntegerTag ret = intTagRefTable[intTagRefIndex]; - intTagRefIndex++; - return ret; - } - - public KMIntegerArrayTag newIntegerArrayTag() { - if (intArrayTagRefIndex >= intArrayTagRefTable.length) { - // TODO this is placeholder exception value. - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - KMIntegerArrayTag ret = intArrayTagRefTable[intArrayTagRefIndex]; - intArrayTagRefIndex++; - return ret; - } - - public KMBoolTag newBoolTag() { - if (boolTagRefIndex >= boolTagRefTable.length) { - // TODO this is placeholder exception value. - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - KMBoolTag ret = boolTagRefTable[boolTagRefIndex]; - boolTagRefIndex++; - return ret; - } - - public KMByteTag newByteTag() { - if (byteTagRefIndex >= byteTagRefTable.length) { - // TODO this is placeholder exception value. - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - KMByteTag ret = byteTagRefTable[byteTagRefIndex]; - byteTagRefIndex++; - return ret; - } - - public KMKeyParameters newKeyParameters() { - if (keyParametersRefIndex >= keyParametersRefTable.length) { - // TODO this is placeholder exception value. - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - KMKeyParameters ret = keyParametersRefTable[keyParametersRefIndex]; - keyParametersRefIndex++; - return ret; - } - - public KMArray newArray() { - if (arrayRefIndex >= arrayRefTable.length) { - // TODO this is placeholder exception value. - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - KMArray ret = arrayRefTable[arrayRefIndex]; - arrayRefIndex++; - return ret; - } - - public KMKeyCharacteristics newKeyCharacteristics() { - if (keyCharRefIndex >= keyCharRefTable.length) { - // TODO this is placeholder exception value. - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - KMKeyCharacteristics ret = keyCharRefTable[keyCharRefIndex]; - keyCharRefIndex++; - return ret; - } - - public KMHardwareAuthToken newHwAuthToken() { - if (hwAuthTokenRefIndex >= hwAuthTokenRefTable.length) { - // TODO this is placeholder exception value. - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - KMHardwareAuthToken ret = hwAuthTokenRefTable[hwAuthTokenRefIndex]; - hwAuthTokenRefIndex++; - return ret; - } - - public KMHmacSharingParameters newHmacSharingParameters() { - if (hmacSharingParamsRefIndex >= hmacSharingParamsRefTable.length) { - // TODO this is placeholder exception value. - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - KMHmacSharingParameters ret = hmacSharingParamsRefTable[hmacSharingParamsRefIndex]; - hmacSharingParamsRefIndex++; - return ret; - } - - public KMVerificationToken newVerificationToken() { - if (verTokenRefIndex >= verTokenRefTable.length) { - // TODO this is placeholder exception value. + public short alloc(short length) { + if (((short) (heapIndex + length)) > heap.length) { ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } - KMVerificationToken ret = verTokenRefTable[verTokenRefIndex]; - verTokenRefIndex++; - return ret; + heapIndex += length; + return (short) (heapIndex - length); } - public KMOperationState newOperationState() { - if (opStateRefIndex >= opStateRefTable.length) { - // TODO this is placeholder exception value. - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - KMOperationState ret = operationStateTable[opStateRefIndex]; - opStateRefIndex++; - return ret; - } - - public void releaseOperationState(KMOperationState state){ - opStateRefIndex--; - if(opStateRefIndex <0){ - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - opStateRefTable[opStateRefIndex] = state; - } - public KMVector newVector() { - if (vectorRefIndex >= vectorRefTable.length) { - // TODO this is placeholder exception value. - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - KMVector ret = vectorRefTable[vectorRefIndex]; - vectorRefIndex++; - return ret; - } - - public KMEnum newEnum() { - if (enumRefIndex >= enumRefTable.length) { - // TODO this is placeholder exception value. - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - KMEnum ret = enumRefTable[enumRefIndex]; - enumRefIndex++; - return ret; - } - - public KMType[] getTypeArrayRef(){ - return typeRefTable; - } - - public byte[] getByteHeapRef(){ - return byteHeap; - } - - public short newTypeArray(short length) { - if (((short) (typeRefIndex + length)) >= typeRefTable.length) { - // TODO this is placeholder exception value. - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - typeRefIndex += length; - return (short) (typeRefIndex - length); - } - - public short newByteArray(short length) { - if (((short) (byteHeapIndex + length)) >= byteHeap.length) { - // TODO this is placeholder exception value. - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - byteHeapIndex += length; - return (short) (byteHeapIndex - length); + public byte[] getHeap(){ + return heap; } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMTag.java b/Applet/Applet/src/com/android/javacard/keymaster/KMTag.java index c6a1b5ae..709f56f8 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMTag.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMTag.java @@ -16,8 +16,9 @@ package com.android.javacard.keymaster; -public abstract class KMTag extends KMType { - public abstract short getTagType(); +import javacard.framework.Util; - public abstract short getKey(); +public class KMTag extends KMType { + public static short getTagType(short ptr){return Util.getShort(heap, (short)(ptr+TLV_HEADER_SIZE));} + public static short getKey(short ptr){return Util.getShort(heap, (short)(ptr+TLV_HEADER_SIZE+2));} } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMType.java b/Applet/Applet/src/com/android/javacard/keymaster/KMType.java index 2aa08271..2dc74dfd 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMType.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMType.java @@ -16,8 +16,27 @@ package com.android.javacard.keymaster; +import javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.Util; + public abstract class KMType { - public static final short TAG_TYPE_MASK = (short) 0xF000; + public static final short INVALID_VALUE = (short)0x8000; + protected static final byte TLV_HEADER_SIZE = 3; + + // Types + public static final byte BYTE_BLOB_TYPE = 0x01; + public static final byte INTEGER_TYPE = 0x02; + public static final byte ENUM_TYPE = 0x03; + public static final byte TAG_TYPE = 0x04; + public static final byte ARRAY_TYPE = 0x05; + public static final byte KEY_PARAM_TYPE = 0x06; + public static final byte KEY_CHAR_TYPE = 0x07; + public static final byte HW_AUTH_TOKEN_TYPE = 0x08; + public static final byte VERIFICATION_TOKEN_TYPE = 0x09; + public static final byte HMAC_SHARING_PARAM_TYPE = 0x0A; + + // Tags public static final short INVALID_TAG = 0x0000; public static final short ENUM_TAG = 0x1000; public static final short ENUM_ARRAY_TAG = 0x2000; @@ -29,6 +48,7 @@ public abstract class KMType { public static final short BIGNUM_TAG = (short) 0x8000; public static final short BYTES_TAG = (short) 0x9000; public static final short ULONG_ARRAY_TAG = (short) 0xA000; + public static final short TAG_TYPE_MASK = (short) 0xF000; // Enum Tag // Algorithm Enum Tag key and values @@ -225,12 +245,29 @@ public abstract class KMType { public static final byte NO_VALUE = (byte) 0xff; protected static KMRepository repository; + protected static byte[] heap; - public static void initialize(KMRepository repo) { - KMType.repository = repo; + public static void initialize() { + KMType.repository = KMRepository.instance(); + KMType.heap = repository.getHeap(); } - public abstract void init(); + public static byte getType(short ptr){return heap[ptr];} + public static short length(short ptr){return Util.getShort(heap, (short)(ptr+1));} + public static short getValue(short ptr){return Util.getShort(heap, (short)(ptr+TLV_HEADER_SIZE));} + + protected static short instance(byte type, short length){ + if (length <= 0) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + short ptr = repository.alloc((short) (length + TLV_HEADER_SIZE)); + heap[ptr] = type; + Util.setShort(heap, (short) (ptr + 1), length); + return ptr; + } - public abstract short length(); + protected static short exp(byte type) { + short ptr = repository.alloc(TLV_HEADER_SIZE); + heap[ptr] = type; + Util.setShort(heap, (short) (ptr + 1), INVALID_VALUE); + return ptr; + } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMUtil.java b/Applet/Applet/src/com/android/javacard/keymaster/KMUtil.java index 0d1080df..599da84e 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMUtil.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMUtil.java @@ -2,6 +2,7 @@ import javacard.framework.ISO7816; import javacard.framework.ISOException; +import javacard.framework.JCSystem; import javacard.framework.Util; import javacard.security.AESKey; import javacard.security.CryptoException; @@ -18,8 +19,8 @@ public class KMUtil { private static Cipher aesCbc; private static byte[] entropyPool; public static void init() { - entropyPool = new byte[ENTROPY_POOL_SIZE]; - counter = KMRepository.instance().newIntegerArray((short) 8); + entropyPool = JCSystem.makeTransientByteArray(ENTROPY_POOL_SIZE, JCSystem.CLEAR_ON_RESET); + counter = JCSystem.makeTransientByteArray((short)8, JCSystem.CLEAR_ON_RESET); KMUtil.initEntropyPool(entropyPool); try { //Note: ALG_AES_BLOCK_128_CBC_NOPAD not supported by simulator. @@ -58,9 +59,9 @@ public static void initEntropyPool(byte[] pool) { // 8 byte counter and 16 byte block size. public static void newRandomNumber(byte[] num, short startOff, short length) { KMRepository repository = KMRepository.instance(); - byte[] bufPtr = repository.getByteHeapRef(); - short countBufInd = repository.newByteArray(AES_BLOCK_SIZE); - short randBufInd = repository.newByteArray(AES_BLOCK_SIZE); + byte[] bufPtr = repository.getHeap(); + short countBufInd = repository.alloc(AES_BLOCK_SIZE); + short randBufInd = repository.alloc(AES_BLOCK_SIZE); short len = AES_BLOCK_SIZE; aesKey.setKey(entropyPool, (short) 0); aesCbc.init(aesKey, Cipher.MODE_ENCRYPT, aesICV, (short)0, (short)16); diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMVector.java b/Applet/Applet/src/com/android/javacard/keymaster/KMVector.java deleted file mode 100644 index fb9d20f5..00000000 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMVector.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright(C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.javacard.keymaster; - -public class KMVector extends KMType { - private KMType type; - private KMArray vals; - - private KMVector() { - init(); - } - - @Override - public void init() { - vals = null; - type = null; - } - - @Override - public short length() { - return vals.length(); - } - - public static KMVector instance(KMType type) { - KMVector inst = repository.newVector(); - inst.type = type; - inst.vals = KMArray.instance(); - return inst; - } - - public static KMVector instance(KMType type, short length) { - KMVector inst = repository.newVector(); - inst.type = type; - inst.vals = KMArray.instance(length); - return inst; - } - - public static void create(KMVector[] vectorRefTable) { - byte index = 0; - while (index < vectorRefTable.length) { - vectorRefTable[index] = new KMVector(); - index++; - } - } - - public KMArray getVals() { - return vals; - } - - public KMVector withLength(short length) { - this.vals.withLength(length); - return this; - } - - public KMVector add(short index, KMType val) { - vals.add(index, val); - return this; - } - - public KMType get(short index) { - return vals.get(index); - } - - public void setVals(KMArray vals) { - this.vals = vals; - } - - public KMType getType() { - return type; - } -} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMVerificationToken.java b/Applet/Applet/src/com/android/javacard/keymaster/KMVerificationToken.java index d47c80e2..eefa9e60 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMVerificationToken.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMVerificationToken.java @@ -18,6 +18,7 @@ import javacard.framework.ISO7816; import javacard.framework.ISOException; +import javacard.framework.Util; public class KMVerificationToken extends KMType { public static final byte CHALLENGE = 0x00; @@ -25,71 +26,113 @@ public class KMVerificationToken extends KMType { public static final byte PARAMETERS_VERIFIED = 0x02; public static final byte SECURITY_LEVEL = 0x03; public static final byte MAC = 0x04; - private KMArray vals; - private KMVerificationToken() { - init(); + private static KMVerificationToken prototype; + private static short instPtr; + + private KMVerificationToken() {} + + public static short exp() { + short arrPtr = KMArray.instance((short)5); + KMArray arr = KMArray.cast(arrPtr); + arr.add(CHALLENGE, KMInteger.exp()); + arr.add(TIMESTAMP, KMInteger.exp()); + arr.add(PARAMETERS_VERIFIED, KMKeyParameters.exp()); + arr.add(SECURITY_LEVEL, KMEnumTag.instance(KMType.HARDWARE_TYPE)); + arr.add(MAC, KMByteBlob.exp()); + return instance(arrPtr); + } + + private static KMVerificationToken proto(short ptr) { + if (prototype == null) prototype = new KMVerificationToken(); + instPtr = ptr; + return prototype; + } + + + public static short instance() { + short arrPtr = KMArray.instance((short)5); + return instance(arrPtr); } - @Override - public void init() { - vals = null; + public static short instance(short vals) { + KMArray arr = KMArray.cast(vals); + if(arr.length() != 5)ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + short ptr = KMType.instance(VERIFICATION_TOKEN_TYPE, (short)2); + Util.setShort(heap, (short)(ptr + TLV_HEADER_SIZE), vals); + return ptr; + } + + public static KMVerificationToken cast(short ptr) { + if (heap[ptr] != VERIFICATION_TOKEN_TYPE) ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + short arrPtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)); + if(heap[arrPtr] != ARRAY_TYPE) ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + return proto(ptr); + } + + public short getVals() { + return Util.getShort(heap, (short) (instPtr + TLV_HEADER_SIZE)); } - @Override public short length() { - return vals.length(); + short arrPtr = getVals(); + return KMArray.cast(arrPtr).length(); } - public static void create(KMVerificationToken[] verTokenRefTable) { - byte index = 0; - while (index < verTokenRefTable.length) { - verTokenRefTable[index] = new KMVerificationToken(); - index++; - } + public short getChallenge() { + short arrPtr = getVals(); + return KMArray.cast(arrPtr).get(CHALLENGE); } - public static KMVerificationToken instance() { - KMVerificationToken inst = repository.newVerificationToken(); - inst.vals = KMArray.instance((short) 5); - inst.vals.add(CHALLENGE, KMInteger.instance()); - inst.vals.add(TIMESTAMP, KMInteger.instance()); - inst.vals.add(PARAMETERS_VERIFIED, KMKeyParameters.instance()); - inst.vals.add(SECURITY_LEVEL, KMEnumTag.instance(KMType.HARDWARE_TYPE)); - inst.vals.add(MAC, KMByteBlob.instance()); - return inst; + public void setChallenge(short vals) { + KMInteger.cast(vals); + short arrPtr = getVals(); + KMArray.cast(arrPtr).add(CHALLENGE, vals); } - public static KMVerificationToken instance(KMArray vals) { - if (vals.length() != 5) { - ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); - } - KMVerificationToken inst = repository.newVerificationToken(); - inst.vals = vals; - return inst; + public short getTimestamp() { + short arrPtr = getVals(); + return KMArray.cast(arrPtr).get(TIMESTAMP); } - public KMInteger getChallenge() { - return (KMInteger) vals.get(CHALLENGE); + public void setTimestamp(short vals) { + KMInteger.cast(vals); + short arrPtr = getVals(); + KMArray.cast(arrPtr).add(TIMESTAMP, vals); } - public KMInteger getTimestamp() { - return (KMInteger) vals.get(TIMESTAMP); + public short getMac() { + short arrPtr = getVals(); + return KMArray.cast(arrPtr).get(MAC); } - public KMKeyParameters getParametersVerified() { - return (KMKeyParameters) vals.get(PARAMETERS_VERIFIED); + public void setMac(short vals) { + KMByteBlob.cast(vals); + short arrPtr = getVals(); + KMArray.cast(arrPtr).add(MAC, vals); } - public byte getSecurityLevel() { - return ((KMEnumTag) vals.get(SECURITY_LEVEL)).getValue(); + public short getParametersVerified() { + short arrPtr = getVals(); + return KMArray.cast(arrPtr).get(PARAMETERS_VERIFIED); } - public KMByteBlob getMac() { - return (KMByteBlob) vals.get(MAC); + public void setParametersVerified(short vals) { + KMKeyParameters.cast(vals); + short arrPtr = getVals(); + KMArray.cast(arrPtr).add(PARAMETERS_VERIFIED, vals); } - public KMArray getVals() { - return vals; + public short getSecurityLevel() { + short arrPtr = getVals(); + return KMArray.cast(arrPtr).get(SECURITY_LEVEL); } + + public void setSecurityLevel(short vals) { + short key = KMEnumTag.cast(vals).getKey(); + if(key != HARDWARE_TYPE) ISOException.throwIt(ISO7816.SW_DATA_INVALID); + short arrPtr = getVals(); + KMArray.cast(arrPtr).add(SECURITY_LEVEL, vals); + } + } diff --git a/Applet/Applet/test/com/android/javacard/test/KMFrameworkTest.java b/Applet/Applet/test/com/android/javacard/test/KMFrameworkTest.java index 15e61109..c34ac845 100644 --- a/Applet/Applet/test/com/android/javacard/test/KMFrameworkTest.java +++ b/Applet/Applet/test/com/android/javacard/test/KMFrameworkTest.java @@ -60,7 +60,7 @@ public void test_Lifecycle_Success() { public void testProvisionCmd(CardSimulator simulator){ byte[] buf = new byte[512]; // test provision command - KMArray cmd = makeProvisionCmd(); + short cmd = makeProvisionCmd(); KMEncoder enc = new KMEncoder(); short actualLen = enc.encode(cmd, buf, (short) 0, (short)buf.length); CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x23, 0x40, 0x00, buf, 0, actualLen); @@ -74,18 +74,19 @@ public void testGetHwInfoCmd(CardSimulator simulator){ //print(commandAPDU.getBytes()); ResponseAPDU response = simulator.transmitCommand(commandAPDU); KMDecoder dec = new KMDecoder(); - KMArray exp = KMArray.instance((short)3) - .add((short)0, KMEnum.instance().setType(KMType.HARDWARE_TYPE)) - .add((short)1, KMByteBlob.instance()) - .add((short)2, KMByteBlob.instance()); + short arrPtr = KMArray.instance((short)3); + KMArray exp = KMArray.cast(arrPtr); + exp.add((short)0, KMEnum.instance(KMType.HARDWARE_TYPE)); + exp.add((short)1, KMByteBlob.exp()); + exp.add((short)2, KMByteBlob.exp()); byte[] respBuf = response.getBytes(); short len = (short)respBuf.length; - KMArray resp = dec.decode(exp,respBuf, (short)0, len); - Assert.assertEquals(3, resp.length()); - KMEnum secLevel = (KMEnum)resp.get((short)0); - KMByteBlob kmName = (KMByteBlob) resp.get((short)1); - KMByteBlob authorName = (KMByteBlob) resp.get((short)2); - Assert.assertEquals(KMType.HARDWARE_TYPE, secLevel.getType()); + short respPtr = dec.decode(arrPtr,respBuf, (short)0, len); + Assert.assertEquals(3, KMArray.cast(respPtr).length()); + KMEnum secLevel = KMEnum.cast(KMArray.cast(respPtr).get((short)0)); + short kmName = KMArray.cast(respPtr).get((short)1); + short authorName = KMArray.cast(respPtr).get((short)2); + Assert.assertEquals(KMType.HARDWARE_TYPE, secLevel.getEnumType()); Assert.assertEquals(KMType.STRONGBOX, secLevel.getVal()); String kmNameStr = byteBlobToString(kmName); String authorNameStr = byteBlobToString(authorName); @@ -96,7 +97,7 @@ public void testGetHwInfoCmd(CardSimulator simulator){ private void testAddRngEntropyCmd(CardSimulator simulator){ byte[] buf = new byte[512]; // test provision command - KMArray cmd = makeAddRngEntropyCmd(); + short cmd = makeAddRngEntropyCmd(); KMEncoder enc = new KMEncoder(); short actualLen = enc.encode(cmd, buf, (short) 0, (short)buf.length); @@ -107,8 +108,9 @@ private void testAddRngEntropyCmd(CardSimulator simulator){ } - private String byteBlobToString(KMByteBlob blob) { + private String byteBlobToString(short blobPtr) { StringBuilder sb = new StringBuilder(); + KMByteBlob blob = KMByteBlob.cast(blobPtr); for(short i = 0; i Date: Wed, 13 May 2020 20:38:50 -0700 Subject: [PATCH 2/2] ImportKey, GenerateKey and GetKeyCharacteristics - Some Code optimizations. - ImportKey, GenerateKey and GetKeyCharacteristics functionality. - GetKeyCharacteristics only tested on jcardSim. --- .../android/javacard/keymaster/KMArray.java | 2 +- .../android/javacard/keymaster/KMBoolTag.java | 5 +- .../javacard/keymaster/KMByteBlob.java | 12 + .../android/javacard/keymaster/KMByteTag.java | 4 +- .../javacard/keymaster/KMCryptoProvider.java | 80 ++ .../android/javacard/keymaster/KMDecoder.java | 44 +- .../android/javacard/keymaster/KMEncoder.java | 149 +- .../android/javacard/keymaster/KMEnum.java | 16 +- .../javacard/keymaster/KMEnumArrayTag.java | 8 +- .../android/javacard/keymaster/KMEnumTag.java | 18 +- .../android/javacard/keymaster/KMError.java | 78 ++ .../javacard/keymaster/KMException.java | 21 +- .../android/javacard/keymaster/KMInteger.java | 15 +- .../javacard/keymaster/KMIntegerArrayTag.java | 2 +- .../javacard/keymaster/KMIntegerTag.java | 27 +- .../javacard/keymaster/KMKeyParameters.java | 212 ++- .../javacard/keymaster/KMKeymasterApplet.java | 1244 +++++++++++++++-- .../javacard/keymaster/KMOperationState.java | 7 - .../javacard/keymaster/KMRepository.java | 143 +- .../com/android/javacard/keymaster/KMRng.java | 104 ++ .../javacard/keymaster/KMSimulator.java | 424 ++++++ .../android/javacard/keymaster/KMType.java | 18 + .../javacard/test/KMFrameworkTest.java | 551 +++++++- Applet/JavaCardKeymaster.scr | 43 +- Applet/default.output | 13 +- 25 files changed, 3027 insertions(+), 213 deletions(-) create mode 100644 Applet/Applet/src/com/android/javacard/keymaster/KMCryptoProvider.java create mode 100644 Applet/Applet/src/com/android/javacard/keymaster/KMError.java create mode 100644 Applet/Applet/src/com/android/javacard/keymaster/KMRng.java create mode 100644 Applet/Applet/src/com/android/javacard/keymaster/KMSimulator.java diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMArray.java b/Applet/Applet/src/com/android/javacard/keymaster/KMArray.java index 04ee7cfe..2935aa2b 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMArray.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMArray.java @@ -80,7 +80,7 @@ public short get(short index) { public byte containedType(){ return heap[(short)(instPtr + TLV_HEADER_SIZE)];} public short getStartOff() { - return (short) (instPtr + TLV_HEADER_SIZE + 3); + return (short) (instPtr + TLV_HEADER_SIZE + ARRAY_HEADER_SIZE); } public short length() { diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMBoolTag.java b/Applet/Applet/src/com/android/javacard/keymaster/KMBoolTag.java index 5154cd44..e56dc9f3 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMBoolTag.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMBoolTag.java @@ -83,7 +83,7 @@ public byte getVal() { return heap[(short)(instPtr+TLV_HEADER_SIZE+4)]; } - // validate the tag key + // isValidTag the tag key private static boolean validateKey(short key) { short index = (short) tags.length; while (--index >= 0) { @@ -93,4 +93,7 @@ private static boolean validateKey(short key) { } return false; } + public static short[] getTags(){ + return tags; + } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMByteBlob.java b/Applet/Applet/src/com/android/javacard/keymaster/KMByteBlob.java index a112ce6c..423220fa 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMByteBlob.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMByteBlob.java @@ -87,4 +87,16 @@ public short length() { public byte[] getBuffer() { return heap; } + + public void getValue(byte[] destBuf, short destStart, short destLength){ + Util.arrayCopyNonAtomic(heap, getStartOff(), destBuf, destStart, destLength); + } + + public void setValue(byte[] srcBuf, short srcStart, short srcLength){ + if(length() > srcLength){ + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + } + Util.arrayCopyNonAtomic(srcBuf, srcStart, heap, getStartOff(), length()); + } + } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMByteTag.java b/Applet/Applet/src/com/android/javacard/keymaster/KMByteTag.java index 6740e9ba..a00d6c6f 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMByteTag.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMByteTag.java @@ -41,7 +41,9 @@ public class KMByteTag extends KMTag { ATTESTATION_ID_MODEL, ASSOCIATED_DATA, NONCE, - CONFIRMATION_TOKEN + CONFIRMATION_TOKEN, + VERIFIED_BOOT_KEY, + VERIFIED_BOOT_HASH }; private KMByteTag() {} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMCryptoProvider.java b/Applet/Applet/src/com/android/javacard/keymaster/KMCryptoProvider.java new file mode 100644 index 00000000..8c44d28f --- /dev/null +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMCryptoProvider.java @@ -0,0 +1,80 @@ +package com.android.javacard.keymaster; + +import javacard.security.AESKey; +import javacard.security.DESKey; +import javacard.security.ECPrivateKey; +import javacard.security.HMACKey; +import javacard.security.KeyPair; +import javacard.security.RSAPrivateKey; + +public interface KMCryptoProvider { + KeyPair createRsaKeyPair(); + + KeyPair createECKeyPair(); + + AESKey createAESKey(short keysize); + + AESKey createAESKey(byte[] buf, short startOff, short length); + + DESKey createTDESKey(); + + HMACKey createHMACKey(short keysize); + + void newRandomNumber(byte[] num, short offset, short length); + + void addRngEntropy(byte[] num, short offset, short length); + + short aesGCMEncrypt( + AESKey key, + byte[] secret, + short secretStart, + short secretLen, + byte[] encSecret, + short encSecretStart, + byte[] nonce, + short nonceStart, + short nonceLen, + byte[] authData, + short authDataStart, + short authDataLen, + byte[] authTag, + short authTagStart, + short authTagLen); + + boolean aesGCMDecrypt( + AESKey key, + byte[] encSecret, + short encSecretStart, + short encSecretLen, + byte[] secret, + short secretStart, + byte[] nonce, + short nonceStart, + short nonceLen, + byte[] authData, + short authDataStart, + short authDataLen, + byte[] authTag, + short authTagStart, + short authTagLen); + + byte[] getTrueRandomNumber(short len); + + short aesCCMSign( + byte[] bufIn, + short bufInStart, + short buffInLength, + byte[] masterKeySecret, + byte[] bufOut, + short bufStart); + + ECPrivateKey createEcPrivateKey(byte[] pubBuffer, short pubOff, short pubLength, + byte[] privBuffer, short privOff, short privLength); + + HMACKey createHMACKey(byte[] secretBuffer, short secretOff, short secretLength); + + DESKey createTDESKey(byte[] secretBuffer, short secretOff, short secretLength); + + RSAPrivateKey createRsaPrivateKey(byte[] modBuffer, short modOff, short modLength, + byte[] privBuffer, short privOff, short privLength); +} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMDecoder.java b/Applet/Applet/src/com/android/javacard/keymaster/KMDecoder.java index b4679a3c..25c1c1b1 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMDecoder.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMDecoder.java @@ -55,7 +55,27 @@ public short decode(short expression, byte[] buffer, short startOff, short lengt this.length = (short)(startOff+length); return decode(expression); } - + public short decodeArray(short exp, byte[] buffer, short startOff, short length){ + this.buffer = buffer; + this.startOff = startOff; + this.length = (short)(startOff+length); + short payloadLength = readMajorTypeWithPayloadLength(ARRAY_TYPE); + short expLength = KMArray.cast(exp).length(); + if(payloadLength > expLength){ + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + } + short index = 0; + short obj; + short type; + short arrPtr = KMArray.instance(payloadLength); + while (index < payloadLength) { + type = KMArray.cast(exp).get(index); + obj = decode(type); + KMArray.cast(arrPtr).add(index, obj); + index++; + } + return arrPtr; + } private short decode(short exp){ byte type = KMType.getType(exp); switch(type){ @@ -138,6 +158,9 @@ private short decodeKeyParam(short exp) { boolean tagFound; short tagInd; short tagType; + short tagClass; + short allowedType; + short obj; // For each tag in payload ... while (index < payloadLength) { tagFound = false; @@ -145,12 +168,13 @@ private short decodeKeyParam(short exp) { tagType = peekTagType(); // Check against the allowed tags ... while (tagInd < length) { - short tagClass = KMArray.cast(allowedTags).get(tagInd); - short allowedType = KMTag.getTagType(tagClass); + tagClass = KMArray.cast(allowedTags).get(tagInd); + allowedType = KMTag.getTagType(tagClass); // If it is part of allowed tags ... if (tagType == allowedType) { // then decodeByteBlob and add that to the array. - KMArray.cast(vals).add(index, decode(tagClass)); + obj = decode(tagClass); + KMArray.cast(vals).add(index,obj); tagFound = true; break; } @@ -200,17 +224,21 @@ private short decodeArray(short exp) { } short arrPtr = KMArray.cast(exp).instance(payloadLength); short index = 0; + short type; + short obj; // check whether array contains one type of objects or multiple types if( KMArray.cast(exp).containedType() == 0){// multiple types specified by expression. while (index < payloadLength) { - short type = KMArray.cast(exp).get(index); - KMArray.cast(arrPtr).add(index, decode(type)); + type = KMArray.cast(exp).get(index); + obj = decode(type); + KMArray.cast(arrPtr).add(index, obj); index++; } }else{ // Array is a Vector containing objects of one type - short type = KMArray.cast(exp).containedType(); + type = KMArray.cast(exp).containedType(); while(index < payloadLength){ - KMArray.cast(arrPtr).add(index, decode(type)); + obj = decode(type); + KMArray.cast(arrPtr).add(index, obj); index++; } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMEncoder.java b/Applet/Applet/src/com/android/javacard/keymaster/KMEncoder.java index 5c96387a..01fb137a 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMEncoder.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMEncoder.java @@ -18,6 +18,8 @@ import javacard.framework.ISO7816; import javacard.framework.ISOException; +import javacard.framework.JCSystem; +import javacard.framework.SystemException; import javacard.framework.Util; public class KMEncoder { @@ -43,59 +45,100 @@ public class KMEncoder { private byte[] buffer; private short startOff; private short length; + private static short[] stack; + private static byte stackPtr; public KMEncoder() { buffer = null; startOff = 0; length = 0; + stack = JCSystem.makeTransientShortArray((short)50, JCSystem.CLEAR_ON_RESET); } - public short encode(short object, byte[] buffer, short startOff, short length) { + private static void push (short objPtr){ + stack[stackPtr] = objPtr; + stackPtr++; + } + private static short pop(){ + stackPtr--; + return stack[stackPtr]; + } + private void encode(short obj){ + push(obj); + } + public short encode(short object, byte[] buffer, short startOff) { + stackPtr = 0; this.buffer = buffer; this.startOff = startOff; - this.length = length; - encode(object); - this.length = this.startOff; + short len = (short) buffer.length; + if((len <0) || (len > KMKeymasterApplet.MAX_LENGTH)){ + this.length = KMKeymasterApplet.MAX_LENGTH; + }else{ + this.length = (short)buffer.length; + } + //this.length = (short)(startOff + length); + push(object); + encode(); + return (short)(this.startOff - startOff); + } + + + public short encodeError(short err, byte[] buffer, short startOff, short length) { + this.buffer = buffer; this.startOff = startOff; - return this.length; + this.length = (short)(startOff + length); + // encode the err as UINT with value in err - should not be greater then 5 bytes. + if(err < UINT8_LENGTH){ + writeByte((byte)(UINT_TYPE | err )); + }else if(err < 0x100){ + writeByte((byte)(UINT_TYPE | UINT8_LENGTH)); + writeByte((byte)err); + }else { + writeByte((byte)(UINT_TYPE | UINT16_LENGTH)); + writeShort(err); + } + return (short)(this.startOff - startOff); } - private void encode(short exp){ - byte type = KMType.getType(exp); - switch(type){ - case KMType.BYTE_BLOB_TYPE: - encodeByteBlob(exp); - return; - case KMType.INTEGER_TYPE: - encodeInteger(exp); - return; - case KMType.ARRAY_TYPE: - encodeArray(exp); - return; - case KMType.ENUM_TYPE: - encodeEnum(exp); - return; - case KMType.KEY_PARAM_TYPE: - encodeKeyParam(exp); - return; - case KMType.KEY_CHAR_TYPE: - encodeKeyChar(exp); - return; - case KMType.VERIFICATION_TOKEN_TYPE: - encodeVeriToken(exp); - return; - case KMType.HMAC_SHARING_PARAM_TYPE: - encodeHmacSharingParam(exp); - return; - case KMType.HW_AUTH_TOKEN_TYPE: - encodeHwAuthToken(exp); - return; - case KMType.TAG_TYPE: - short tagType = KMTag.getTagType(exp); - encodeTag(tagType, exp); - return; - default: - ISOException.throwIt(ISO7816.SW_DATA_INVALID); + private void encode(){ + while (stackPtr > 0) { + short exp = pop(); + byte type = KMType.getType(exp); + switch (type) { + case KMType.BYTE_BLOB_TYPE: + encodeByteBlob(exp); + break; + case KMType.INTEGER_TYPE: + encodeInteger(exp); + break; + case KMType.ARRAY_TYPE: + encodeArray(exp); + break; + case KMType.ENUM_TYPE: + encodeEnum(exp); + break; + case KMType.KEY_PARAM_TYPE: + encodeKeyParam(exp); + break; + case KMType.KEY_CHAR_TYPE: + encodeKeyChar(exp); + break; + case KMType.VERIFICATION_TOKEN_TYPE: + encodeVeriToken(exp); + break; + case KMType.HMAC_SHARING_PARAM_TYPE: + encodeHmacSharingParam(exp); + break; + case KMType.HW_AUTH_TOKEN_TYPE: + encodeHwAuthToken(exp); + break; + case KMType.TAG_TYPE: + short tagType = KMTag.getTagType(exp); + encodeTag(tagType, exp); + break; + default: + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } } } private void encodeTag(short tagType, short exp){ @@ -147,22 +190,23 @@ private void encodeHmacSharingParam(short obj) { private void encodeArray(short obj) { writeMajorTypeWithLength(ARRAY_TYPE, KMArray.cast(obj).length()); - short index = 0; short len = KMArray.cast(obj).length(); - while(index < len){ + short index = (short)(len-1); + while(index >= 0){ encode(KMArray.cast(obj).get(index)); - index++; + index--; } } private void encodeAsMap(short obj){ writeMajorTypeWithLength(MAP_TYPE, KMArray.cast(obj).length()); - short index = 0; short len = KMArray.cast(obj).length(); - while(index < len){ - short inst = KMArray.cast(obj).get(index); + short index = (short)(len-1); + short inst; + while(index >= 0){ + inst = KMArray.cast(obj).get(index); encode(inst); - index++; + index--; } } @@ -208,6 +252,8 @@ private void encodeInteger(short obj) { while(index < len){ if(val[(short)(startOff + index)] > 0){ break; + }else if(val[(short)(startOff + index)] < 0){ + break; } index++; // index will be equal to len if value is 0. } @@ -215,7 +261,8 @@ private void encodeInteger(short obj) { short diff = (short)(len - index); if(diff == 0){ writeByte((byte)(UINT_TYPE | 0)); - }else if((diff == 1) && val[(short)(startOff + index)] < UINT8_LENGTH){ + }else if((diff == 1) && (val[(short)(startOff + index)] < UINT8_LENGTH) + &&(val[(short)(startOff + index)] >= 0)){ writeByte((byte)(UINT_TYPE | val[(short)(startOff + index)])); }else if (diff == 1){ writeByte((byte)(UINT_TYPE | UINT8_LENGTH)); @@ -239,11 +286,11 @@ private void encodeByteBlob(short obj) { } private void writeByteValue(byte val){ - if(val < UINT8_LENGTH){ + if((val < UINT8_LENGTH) && (val >=0)){ writeByte((byte)(UINT_TYPE | val)); }else{ writeByte((byte)(UINT_TYPE | UINT8_LENGTH)); - writeByte(val); + writeByte((byte)val); } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMEnum.java b/Applet/Applet/src/com/android/javacard/keymaster/KMEnum.java index 67196a81..00ba0cd1 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMEnum.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMEnum.java @@ -24,7 +24,8 @@ public class KMEnum extends KMType { private static KMEnum prototype; private static short instPtr; - private static short[] types = {HARDWARE_TYPE, KEY_FORMAT, KEY_DERIVATION_FUNCTION}; + private static short[] types = {HARDWARE_TYPE, KEY_FORMAT, KEY_DERIVATION_FUNCTION, + VERIFIED_BOOT_STATE, DEVICE_LOCKED}; private static Object[] enums = null; @@ -87,7 +88,9 @@ private static void create() { ISO18033_2_KDF1_SHA256, ISO18033_2_KDF2_SHA1, ISO18033_2_KDF2_SHA256 - } + }, + new byte[] {SELF_SIGNED_BOOT, VERIFIED_BOOT}, + new byte[] {DEVICE_LOCKED_TRUE, DEVICE_LOCKED_FALSE} }; } } @@ -108,9 +111,11 @@ public short getEnumType() { return Util.getShort(heap, (short)(instPtr+TLV_HEADER_SIZE)); } - // validate enumeration keys and values. + // isValidTag enumeration keys and values. private static boolean validateEnum(short key, byte value) { create(); + byte[] vals; + short enumInd; // check if key exists short index = (short) types.length; while (--index >= 0) { @@ -118,8 +123,8 @@ private static boolean validateEnum(short key, byte value) { // check if value given if (value != NO_VALUE) { // check if the value exist - byte[] vals = (byte[]) enums[index]; - short enumInd = (short) vals.length; + vals = (byte[]) enums[index]; + enumInd = (short) vals.length; while (--enumInd >= 0) { if (vals[enumInd] == value) { // return true if value exist @@ -136,4 +141,5 @@ private static boolean validateEnum(short key, byte value) { // return false if key does not exist return false; } + } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMEnumArrayTag.java b/Applet/Applet/src/com/android/javacard/keymaster/KMEnumArrayTag.java index c0601267..d29903e1 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMEnumArrayTag.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMEnumArrayTag.java @@ -65,9 +65,11 @@ public static short instance(short key, short byteBlob) { } KMByteBlob blob = KMByteBlob.cast(byteBlob); short byteIndex = 0; + short enumIndex; + boolean validValue; while (byteIndex < blob.length()) { - short enumIndex = 0; - boolean validValue = false; + enumIndex = 0; + validValue = false; while (enumIndex < allowedVals.length) { if (blob.get(byteIndex) == allowedVals[enumIndex]) { validValue = true; @@ -117,7 +119,7 @@ public static void create() { enums = new Object[] { new byte[] {ENCRYPT, DECRYPT, SIGN, VERIFY, WRAP_KEY, ATTEST_KEY}, - new byte[] {ECB, CBC, CTR}, + new byte[] {ECB, CBC, CTR, GCM}, new byte[] {DIGEST_NONE, MD5, SHA1, SHA2_224, SHA2_256, SHA2_384, SHA2_512}, new byte[] { PADDING_NONE, RSA_OAEP, RSA_PSS, RSA_PKCS1_1_5_ENCRYPT, RSA_PKCS1_1_5_SIGN, PKCS7 diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMEnumTag.java b/Applet/Applet/src/com/android/javacard/keymaster/KMEnumTag.java index 8f66e5e3..b75563d6 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMEnumTag.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMEnumTag.java @@ -93,16 +93,18 @@ public static void create() { new byte[] {RSA, DES, EC, AES, HMAC}, new byte[] {P_224, P_256, P_384, P_521}, new byte[] {STANDALONE, REQUIRES_FILE_SYSTEM}, - new byte[] {USER_AUTH_NONE, PASSWORD, FINGERPRINT, ANY}, + new byte[] {USER_AUTH_NONE, PASSWORD, FINGERPRINT, (byte)(PASSWORD & FINGERPRINT),ANY}, new byte[] {GENERATED, DERIVED, IMPORTED, UNKNOWN, SECURELY_IMPORTED}, new byte[] {SOFTWARE, TRUSTED_ENVIRONMENT, STRONGBOX} }; } } - // validate enumeration keys and values. + // isValidTag enumeration keys and values. private static boolean validateEnum(short key, byte value) { create(); + byte[] vals; + short enumInd; // check if key exists short index = (short) tags.length; while (--index >= 0) { @@ -110,8 +112,8 @@ private static boolean validateEnum(short key, byte value) { // check if value given if (value != NO_VALUE) { // check if the value exist - byte[] vals = (byte[]) enums[index]; - short enumInd = (short) vals.length; + vals = (byte[]) enums[index]; + enumInd = (short) vals.length; while (--enumInd >= 0) { if (vals[enumInd] == value) { // return true if value exist @@ -128,4 +130,12 @@ private static boolean validateEnum(short key, byte value) { // return false if key does not exist return false; } + + public static short getValue(short tagType, short keyParameters){ + short tagPtr = KMKeyParameters.findTag(KMType.ENUM_TAG, tagType, keyParameters); + if(tagPtr != KMType.INVALID_VALUE){ + return heap[(short)(tagPtr+TLV_HEADER_SIZE+4)]; + } + return KMType.INVALID_VALUE; + } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMError.java b/Applet/Applet/src/com/android/javacard/keymaster/KMError.java new file mode 100644 index 00000000..25557d27 --- /dev/null +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMError.java @@ -0,0 +1,78 @@ +package com.android.javacard.keymaster; + +public class KMError { + public static short OK = 0 ; + public static short ROOT_OF_TRUST_ALREADY_SET = 1; + public static short UNSUPPORTED_PURPOSE = 2; + public static short INCOMPATIBLE_PURPOSE = 3; + public static short UNSUPPORTED_ALGORITHM = 4; + public static short INCOMPATIBLE_ALGORITHM = 5; + public static short UNSUPPORTED_KEY_SIZE = 6; + public static short UNSUPPORTED_BLOCK_MODE = 7; + public static short INCOMPATIBLE_BLOCK_MODE = 8; + public static short UNSUPPORTED_MAC_LENGTH = 9; + public static short UNSUPPORTED_PADDING_MODE = 10; + public static short INCOMPATIBLE_PADDING_MODE = 11; + public static short UNSUPPORTED_DIGEST = 12; + public static short INCOMPATIBLE_DIGEST = 13; + public static short INVALID_EXPIRATION_TIME = 14; + public static short INVALID_USER_ID = 15; + public static short INVALID_AUTHORIZATION_TIMEOUT = 16; + public static short UNSUPPORTED_KEY_FORMAT = 17; + public static short INCOMPATIBLE_KEY_FORMAT = 18; + public static short UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM = 19; /** For PKCS8 & PKCS12 */ + public static short UNSUPPORTED_KEY_VERIFICATION_ALGORITHM = 20; /** For PKCS8 & PKCS12 */ + public static short INVALID_INPUT_LENGTH = 21; + public static short KEY_EXPORT_OPTIONS_INVALID = 22; + public static short DELEGATION_NOT_ALLOWED = 23; + public static short KEY_NOT_YET_VALID = 24; + public static short KEY_EXPIRED = 25; + public static short KEY_USER_NOT_AUTHENTICATED = 26; + public static short OUTPUT_PARAMETER_NULL = 27; + public static short INVALID_OPERATION_HANDLE = 28; + public static short INSUFFICIENT_BUFFER_SPACE = 29; + public static short VERIFICATION_FAILED = 30; + public static short TOO_MANY_OPERATIONS = 31; + public static short UNEXPECTED_NULL_POINTER = 32; + public static short INVALID_KEY_BLOB = 33; + public static short IMPORTED_KEY_NOT_ENCRYPTED = 34; + public static short IMPORTED_KEY_DECRYPTION_FAILED = 35; + public static short IMPORTED_KEY_NOT_SIGNED = 36; + public static short IMPORTED_KEY_VERIFICATION_FAILED = 37; + public static short INVALID_ARGUMENT = 38; + public static short UNSUPPORTED_TAG = 39; + public static short INVALID_TAG = 40; + public static short MEMORY_ALLOCATION_FAILED = 41; + public static short IMPORT_PARAMETER_MISMATCH = 44; + public static short SECURE_HW_ACCESS_DENIED = 45; + public static short OPERATION_CANCELLED = 46; + public static short CONCURRENT_ACCESS_CONFLICT = 47; + public static short SECURE_HW_BUSY = 48; + public static short SECURE_HW_COMMUNICATION_FAILED = 49; + public static short UNSUPPORTED_EC_FIELD = 50; + public static short MISSING_NONCE = 51; + public static short INVALID_NONCE = 52; + public static short MISSING_MAC_LENGTH = 53; + public static short KEY_RATE_LIMIT_EXCEEDED = 54; + public static short CALLER_NONCE_PROHIBITED = 55; + public static short KEY_MAX_OPS_EXCEEDED = 56; + public static short INVALID_MAC_LENGTH = 57; + public static short MISSING_MIN_MAC_LENGTH = 58; + public static short UNSUPPORTED_MIN_MAC_LENGTH = 59; + public static short UNSUPPORTED_KDF = 60; + public static short UNSUPPORTED_EC_CURVE = 61; + public static short KEY_REQUIRES_UPGRADE = 62; + public static short ATTESTATION_CHALLENGE_MISSING = 63; + public static short KEYMASTER_NOT_CONFIGURED = 64; + public static short ATTESTATION_APPLICATION_ID_MISSING = 65; + public static short CANNOT_ATTEST_IDS = 66; + public static short ROLLBACK_RESISTANCE_UNAVAILABLE = 67; + public static short HARDWARE_TYPE_UNAVAILABLE = 68; + public static short PROOF_OF_PRESENCE_REQUIRED = 69; + public static short CONCURRENT_PROOF_OF_PRESENCE_REQUESTED = 70; + public static short NO_USER_CONFIRMATION = 71; + public static short DEVICE_LOCKED = 72; + public static short UNIMPLEMENTED = 100; + public static short VERSION_MISMATCH = 101; + public static short UNKNOWN_ERROR = 1000; +} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMException.java b/Applet/Applet/src/com/android/javacard/keymaster/KMException.java index 3a26b047..1805a911 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMException.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMException.java @@ -16,13 +16,22 @@ package com.android.javacard.keymaster; -public class KMException { - - // The Applet is not in a correct state in order to execute the command. - public static final short CMD_NOT_ACCEPTED_WRONG_STATE = (short) 0x6901; +public class KMException extends RuntimeException { + public static short reason; + public static KMException exception; + private KMException(){ + } + public static void throwIt(short reason){ + KMException.reason = reason; + throw instance(); + } + public static KMException instance(){ + if(exception == null ) exception = new KMException(); + return exception; + } - public static boolean handle(short reason) { - return false; + public void clear(){ + reason = KMError.UNKNOWN_ERROR; } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMInteger.java b/Applet/Applet/src/com/android/javacard/keymaster/KMInteger.java index d4efe987..994a94cc 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMInteger.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMInteger.java @@ -23,8 +23,8 @@ // Represents 8 bit, 16 bit, 32 bit and 64 bit integers public class KMInteger extends KMType { - private static final short UINT_32 = 4; - private static final short UINT_64 = 8; + public static final short UINT_32 = 4; + public static final short UINT_64 = 8; private static KMInteger prototype; private static short instPtr; @@ -121,11 +121,20 @@ public short getStartOff() { public void getValue(byte[] dest, short destOff, short length){ Util.arrayCopyNonAtomic(heap, (short)(instPtr+TLV_HEADER_SIZE), dest, destOff, length); } + public void setValue(byte[] src, short srcOff){ + Util.arrayCopyNonAtomic(src, srcOff, heap, (short)(instPtr+TLV_HEADER_SIZE), length()); + } + public short value(byte[] dest, short destOff){ + Util.arrayCopyNonAtomic(heap, (short)(instPtr+TLV_HEADER_SIZE), dest, destOff, length()); + return length(); + } public short getShort() { return Util.getShort(heap, (short) (instPtr + TLV_HEADER_SIZE + 2)); } - + public short getSignificantShort(){ + return Util.getShort(heap, (short) (instPtr + TLV_HEADER_SIZE)); + } public byte getByte() { return heap[(short) (instPtr + TLV_HEADER_SIZE + 3)]; } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMIntegerArrayTag.java b/Applet/Applet/src/com/android/javacard/keymaster/KMIntegerArrayTag.java index 775ebfd2..5f0e6731 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMIntegerArrayTag.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMIntegerArrayTag.java @@ -120,7 +120,7 @@ private static boolean validateKey(short key) { return false; } - // TODO this should be combined with validateKey to actually validate {tagType, tagKey} pair. + // TODO this should be combined with validateKey to actually isValidTag {tagType, tagKey} pair. private static boolean validateTagType(short tagType) { if ((tagType == ULONG_ARRAY_TAG) || (tagType == UINT_ARRAY_TAG)) { return true; diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMIntegerTag.java b/Applet/Applet/src/com/android/javacard/keymaster/KMIntegerTag.java index 6aa3a1b4..0ac49e06 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMIntegerTag.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMIntegerTag.java @@ -132,7 +132,7 @@ private static boolean validateKey(short key) { return false; } - // TODO this should be combined with validateKey to actually validate {tagType, tagKey} pair. + // TODO this should be combined with validateKey to actually isValidTag {tagType, tagKey} pair. private static boolean validateTagType(short tagType) { if ((tagType == DATE_TAG) || (tagType == UINT_TAG) || (tagType == ULONG_TAG)) { return true; @@ -140,4 +140,29 @@ private static boolean validateTagType(short tagType) { return false; } + public static short getShortValue(short tagType, short tagKey, short keyParameters){ + short ptr; + if(tagType == UINT_TAG){ + ptr = KMKeyParameters.findTag(KMType.UINT_TAG, tagKey, keyParameters); + if (ptr != KMType.INVALID_VALUE) { + ptr = KMIntegerTag.cast(ptr).getValue(); + if(KMInteger.cast(ptr).getSignificantShort() == 0){ + return KMInteger.cast(ptr).getShort(); + } + } + } + return KMType.INVALID_VALUE; + } + + public static short getValue(byte[] buf, short offset, short tagType, short tagKey, short keyParameters){ + short ptr; + if((tagType == UINT_TAG) || (tagType == ULONG_TAG) || (tagType == DATE_TAG)){ + ptr = KMKeyParameters.findTag(tagType, tagKey, keyParameters); + if (ptr != KMType.INVALID_VALUE) { + ptr = KMIntegerTag.cast(ptr).getValue(); + return KMInteger.cast(ptr).value(buf,offset); + } + } + return KMType.INVALID_VALUE; + } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java b/Applet/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java index 428ef78c..d3bf5af1 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java @@ -16,12 +16,24 @@ package com.android.javacard.keymaster; - import javacard.framework.ISO7816; import javacard.framework.ISOException; import javacard.framework.Util; public class KMKeyParameters extends KMType { + + private static final short[] swEnforcedTags = {}; + + private static final short[] ignoredTags = { + KMType.ROOT_OF_TRUST, + KMType.RESET_SINCE_ID_ROTATION, + KMType.ALLOW_WHILE_ON_BODY, + KMType.ATTESTATION_CHALLENGE, + KMType.OS_VERSION, // not in hidden + KMType.OS_PATCH_LEVEL // not in hidden + // ALL_APPLICATIONS missing from types.hal + }; + private static KMKeyParameters prototype; private static short instPtr; @@ -34,7 +46,7 @@ private static KMKeyParameters proto(short ptr) { } public static short exp() { - short arrPtr = KMArray.instance((short)9); + short arrPtr = KMArray.instance((short) 9); KMArray arr = KMArray.cast(arrPtr); arr.add((short) 0, KMIntegerTag.exp(UINT_TAG)); arr.add((short) 1, KMIntegerArrayTag.exp(UINT_ARRAY_TAG)); @@ -49,15 +61,15 @@ public static short exp() { } public static short instance(short vals) { - short ptr = KMType.instance(KEY_PARAM_TYPE, (short)2); - Util.setShort(heap, (short)(ptr + TLV_HEADER_SIZE), vals); + short ptr = KMType.instance(KEY_PARAM_TYPE, (short) 2); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), vals); return ptr; } public static KMKeyParameters cast(short ptr) { if (heap[ptr] != KEY_PARAM_TYPE) ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); short arrPtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)); - if(heap[arrPtr] != ARRAY_TYPE) ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + if (heap[arrPtr] != ARRAY_TYPE) ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); return proto(ptr); } @@ -70,4 +82,194 @@ public short length() { return KMArray.cast(arrPtr).length(); } + public static short findTag(short tagType, short tagKey, short keyParam) { + KMKeyParameters instParam = KMKeyParameters.cast(keyParam); + KMArray vals = KMArray.cast(instParam.getVals()); + short index = 0; + short length = vals.length(); + short key; + short type; + short ret = KMType.INVALID_VALUE; + short obj; + while (index < length) { + obj = vals.get(index); + key = KMTag.getKey(obj); + type = KMTag.getTagType(obj); + if ((tagKey == key) && (tagType == type)) { + ret = obj; + break; + } + index++; + } + return ret; + } + + // KDF, ECIES_SINGLE_HASH_MODE missing from types.hal + public static short makeHwEnforced( + short keyParamsPtr, byte origin, short osVersionObjPtr, short osPatchObjPtr, byte[] scratchPad) { + final short[] hwEnforcedTagArr = { + // HW Enforced + KMType.ENUM_TAG, KMType.ORIGIN, + KMType.ENUM_ARRAY_TAG, KMType.PURPOSE, + KMType.ENUM_TAG, KMType.ALGORITHM, + KMType.UINT_TAG, KMType.KEYSIZE, + KMType.ULONG_TAG, KMType.RSA_PUBLIC_EXPONENT, + KMType.ENUM_TAG, KMType.BLOB_USAGE_REQ, + KMType.ENUM_ARRAY_TAG, KMType.DIGEST, + KMType.ENUM_ARRAY_TAG, KMType.PADDING, + KMType.ENUM_ARRAY_TAG, KMType.BLOCK_MODE, + KMType.UINT_TAG, KMType.MIN_SEC_BETWEEN_OPS, + KMType.UINT_TAG, KMType.MAX_USES_PER_BOOT, + KMType.ULONG_ARRAY_TAG, KMType.USER_SECURE_ID, + KMType.BOOL_TAG, KMType.NO_AUTH_REQUIRED, + KMType.UINT_TAG, KMType.AUTH_TIMEOUT, + KMType.BOOL_TAG, KMType.CALLER_NONCE, + KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, + KMType.ENUM_TAG, KMType.ECCURVE, + KMType.BOOL_TAG, KMType.TRUSTED_CONFIRMATION_REQUIRED, + KMType.BOOL_TAG, KMType.INCLUDE_UNIQUE_ID, + KMType.BOOL_TAG, KMType.ROLLBACK_RESISTANCE, + KMType.ENUM_TAG, KMType.USER_AUTH_TYPE + }; + byte index = 0; + short tagInd = 0; + short arrInd = 0; + short tagPtr = 0; + short tagKey = 0; + short tagType = 0; + short arrPtr = KMKeyParameters.cast(keyParamsPtr).getVals(); + short len = KMArray.cast(arrPtr).length(); + while (index < len) { + tagInd = 0; + tagPtr = KMArray.cast(arrPtr).get(index); + tagKey = KMTag.getKey(tagPtr); + tagType = KMTag.getTagType(tagPtr); + if (!isValidTag(tagType, tagKey)) { + KMException.throwIt(KMError.INVALID_KEY_BLOB); + } + while (tagInd < (short) hwEnforcedTagArr.length) { + if ((hwEnforcedTagArr[tagInd] == tagType) + && (hwEnforcedTagArr[(short) (tagInd + 1)] == tagKey)) { + Util.setShort(scratchPad, arrInd, tagPtr); + arrInd += 2; + break; + } + tagInd += 2; + } + index++; + } + short originTag = KMEnumTag.instance(KMType.ORIGIN, origin); + Util.setShort(scratchPad, arrInd, originTag); + arrInd += 2; + short osVersionTag = KMIntegerTag.instance(KMType.UINT_TAG, KMType.OS_VERSION, osVersionObjPtr); + Util.setShort(scratchPad, arrInd, osVersionTag); + arrInd += 2; + short osPatchTag = KMIntegerTag.instance(KMType.UINT_TAG, KMType.OS_PATCH_LEVEL, osPatchObjPtr); + Util.setShort(scratchPad, arrInd, osPatchTag); + arrInd += 2; + return createKeyParameters(scratchPad, (short) (arrInd / 2)); + } + + // ALL_USERS, EXPORTABLE missing from types.hal + public static short makeSwEnforced(short keyParamsPtr, byte[] scratchPad) { + final short[] swEnforcedTagsArr = { + KMType.DATE_TAG, KMType.ACTIVE_DATETIME, + KMType.DATE_TAG, KMType.ORIGINATION_EXPIRE_DATETIME, + KMType.DATE_TAG, KMType.USAGE_EXPIRE_DATETIME, + KMType.UINT_TAG, KMType.USERID, + KMType.DATE_TAG, KMType.CREATION_DATETIME + }; + byte index = 0; + short tagInd = 0; + short arrInd = 0; + short tagPtr = 0; + short tagKey = 0; + short tagType = 0; + short arrPtr = KMKeyParameters.cast(keyParamsPtr).getVals(); + short len = KMArray.cast(arrPtr).length(); + while (index < len) { + tagInd = 0; + tagPtr = KMArray.cast(arrPtr).get(index); + tagKey = KMTag.getKey(tagPtr); + tagType = KMTag.getTagType(tagPtr); + if (!isValidTag(tagType, tagKey)){ + KMException.throwIt(KMError.INVALID_KEY_BLOB); + } + while (tagInd < (short) swEnforcedTagsArr.length) { + if ((swEnforcedTagsArr[tagInd] == tagType) + && (swEnforcedTagsArr[(short) (tagInd + 1)] == tagKey)) { + Util.setShort(scratchPad, arrInd, tagPtr); + arrInd += 2; + break; + } + tagInd += 2; + } + index++; + } + return createKeyParameters(scratchPad, (short) (arrInd / 2)); + } + + public static short makeHidden(short keyParamsPtr, short rootOfTrustBlob, byte[] scratchPad) { + short appId = KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_ID, keyParamsPtr); + if (appId != KMTag.INVALID_VALUE) { + appId = KMByteTag.cast(appId).getValue(); + } + short appData = + KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_DATA, keyParamsPtr); + if (appData != KMTag.INVALID_VALUE) { + appData = KMByteTag.cast(appData).getValue(); + } + return makeHidden(appId, appData, rootOfTrustBlob, scratchPad); + } + + public static short makeHidden(short appIdBlob, short appDataBlob, short rootOfTrustBlob, byte[] scratchPad){ + // Order in which the hidden array is created should not change. + short index = 0; + KMByteBlob.cast(rootOfTrustBlob); + Util.setShort(scratchPad, index, rootOfTrustBlob); + index += 2; + if (appIdBlob != KMTag.INVALID_VALUE) { + KMByteBlob.cast(appIdBlob); + Util.setShort(scratchPad, index, appIdBlob); + index += 2; + } + if (appDataBlob != KMTag.INVALID_VALUE) { + Util.setShort(scratchPad, index, appDataBlob); + index += 2; + } + return createKeyParameters(scratchPad, (short)(index/2)); + + } + public static boolean isValidTag(short tagType, short tagKey) { + short[] invalidTagsArr = { + KMType.BYTES_TAG, KMType.NONCE, + KMType.BYTES_TAG, KMType.ASSOCIATED_DATA, + KMType.BYTES_TAG, KMType.UNIQUE_ID, + KMType.UINT_TAG, KMType.MAC_LENGTH, + KMType.BOOL_TAG, KMType.BOOTLOADER_ONLY + }; + short index = 0; + if (tagKey == KMType.INVALID_TAG) { + return false; + } + while (index < invalidTagsArr.length) { + if ((tagType == invalidTagsArr[index]) && (tagKey == invalidTagsArr[(short) (index + 1)])) { + return false; + } + index += 2; + } + return true; + } + + public static short createKeyParameters(byte[] ptrArr, short len) { + short arrPtr = KMArray.instance(len); + short index = 0; + short ptr = 0; + while (index < len) { + KMArray.cast(arrPtr).add(index, Util.getShort(ptrArr, ptr)); + index++; + ptr += 2; + } + return KMKeyParameters.instance(arrPtr); + } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index 427296df..8ea7ad74 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -19,10 +19,17 @@ import javacard.framework.APDU; import javacard.framework.Applet; import javacard.framework.AppletEvent; -import javacard.framework.CardRuntimeException; import javacard.framework.ISO7816; import javacard.framework.ISOException; +import javacard.framework.JCSystem; import javacard.framework.Util; +import javacard.security.AESKey; +import javacard.security.DESKey; +import javacard.security.ECPrivateKey; +import javacard.security.ECPublicKey; +import javacard.security.HMACKey; +import javacard.security.KeyPair; +import javacard.security.RSAPrivateKey; import javacardx.apdu.ExtendedLength; /** @@ -34,10 +41,12 @@ // - remove this in future. public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLength { // Constants. - private static final short MAX_LENGTH = (short) 0x1000; // TODO: make this value configurable. + public static final byte AES_BLOCK_SIZE = 16; + public static final short MAX_LENGTH = (short) 0x1000; private static final byte CLA_ISO7816_NO_SM_NO_CHAN = (byte) 0x80; private static final short KM_HAL_VERSION = (short) 0x4000; - + private static final short MAX_AUTH_DATA_SIZE = (short) 128; + private static final short MAX_IO_LENGTH = 0x400; // Possible states of the applet. private static final byte ILLEGAL_STATE = 0x00; private static final byte INSTALL_STATE = 0x01; @@ -45,7 +54,6 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe private static final byte ACTIVE_STATE = 0x03; private static final byte INACTIVE_STATE = 0x04; private static final byte UNINSTALLED_STATE = 0x05; - // Commands private static final byte INS_GENERATE_KEY_CMD = 0x10; private static final byte INS_IMPORT_KEY_CMD = 0x11; @@ -67,25 +75,63 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe private static final byte INS_FINISH_OPERATION_CMD = 0x21; private static final byte INS_ABORT_OPERATION_CMD = 0x22; private static final byte INS_PROVISION_CMD = 0x23; - - // GetHwInfo information - // TODO change this to just filling the buffer + private static final byte INS_SET_BOOT_PARAMS_CMD = 0x24; + // Data Dictionary items + public static final byte DATA_ARRAY_SIZE = 25; + public static final byte TMP_VARIABLE_ARRAY_SIZE = 20; + public static final byte UPDATE_PARAM_ARRAY_SIZE = 40; + public static final byte KEY_PARAMETERS = 0; + public static final byte KEY_CHARACTERISTICS = 1; + public static final byte HIDDEN_PARAMETERS = 2; + public static final byte HW_PARAMETERS = 3; + public static final byte SW_PARAMETERS = 4; + public static final byte AUTH_DATA = 5; + public static final byte AUTH_TAG = 6; + public static final byte NONCE = 7; + public static final byte KEY_BLOB = 8; + public static final byte AUTH_DATA_LENGTH = 9; + public static final byte SECRET = 10; + public static final byte ROT = 11; + public static final byte DERIVED_KEY = 12; + public static final byte RSA_PUB_EXPONENT = 13; + public static final byte APP_ID = 14; + public static final byte APP_DATA = 15; + public static final byte PUB_KEY = 16; + public static final byte IMPORTED_KEY_BLOB = 17; + public static final byte ORIGIN = 18; + // AddRngEntropy private static final short MAX_SEED_SIZE = 2048; - - // State of the applet. - private KMEncoder encoder; - private KMDecoder decoder; - private KMRepository repository; - private byte keymasterState = ILLEGAL_STATE; - private byte[] buffer; - private short bufferStartOffset; - private short bufferLength; + // Keyblob constants + public static final byte KEY_BLOB_SECRET = 0; + public static final byte KEY_BLOB_NONCE = 1; + public static final byte KEY_BLOB_AUTH_TAG = 2; + public static final byte KEY_BLOB_KEYCHAR = 3; + public static final byte KEY_BLOB_PUB_KEY = 4; + // AES GCM constants + private static final byte AES_GCM_AUTH_TAG_LENGTH = 12; + private static final byte AES_GCM_NONCE_LENGTH = 12; + // Keymaster Applet attributes + private static byte keymasterState = ILLEGAL_STATE; + private static KMEncoder encoder; + private static KMDecoder decoder; + private static KMRepository repository; + private static KMCryptoProvider cryptoProvider; + private static byte[] buffer; + private static short bufferLength; + private static short bufferStartOffset; + private static boolean provisionDone; + private static boolean setBootParamsDone; + private static short[] tmpVariables; + private static short[] data; /** Registers this applet. */ protected KMKeymasterApplet() { + // TODO change this to make this compile time variation. + cryptoProvider = new KMSimulator(); keymasterState = KMKeymasterApplet.INSTALL_STATE; - repository = KMRepository.instance(); - KMUtil.init(); + data = JCSystem.makeTransientShortArray((short) DATA_ARRAY_SIZE, JCSystem.CLEAR_ON_RESET); + tmpVariables = JCSystem.makeTransientShortArray((short) TMP_VARIABLE_ARRAY_SIZE, JCSystem.CLEAR_ON_RESET); + repository = new KMRepository(cryptoProvider.getTrueRandomNumber((short) 256)); KMType.initialize(); encoder = new KMEncoder(); decoder = new KMDecoder(); @@ -100,7 +146,7 @@ protected KMKeymasterApplet() { * @param bLength the length in bytes of the parameter data in bArray */ public static void install(byte[] bArray, short bOffset, byte bLength) { - KMKeymasterApplet keymaster = new KMKeymasterApplet(); + new KMKeymasterApplet(); } /** @@ -163,6 +209,8 @@ public void process(APDU apdu) { byte apduClass = apduBuffer[ISO7816.OFFSET_CLA]; byte apduIns = apduBuffer[ISO7816.OFFSET_INS]; short P1P2 = Util.getShort(apduBuffer, ISO7816.OFFSET_P1); + buffer = repository.getHeap(); + bufferStartOffset = repository.alloc(MAX_IO_LENGTH); // Validate APDU Header. if ((apduClass != CLA_ISO7816_NO_SM_NO_CHAN)) { ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED); @@ -170,12 +218,18 @@ public void process(APDU apdu) { ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2); } // Validate whether INS can be supported - if (!(apduIns >= INS_GENERATE_KEY_CMD && apduIns <= INS_PROVISION_CMD)) { + if (!(apduIns >= INS_GENERATE_KEY_CMD && apduIns <= INS_SET_BOOT_PARAMS_CMD)) { ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); } // Validate if INS is provision command if applet is in FIRST_SELECT_STATE. if (keymasterState == KMKeymasterApplet.FIRST_SELECT_STATE) { - if (apduIns != INS_PROVISION_CMD) { + if ((apduIns != INS_PROVISION_CMD) && (apduIns != INS_SET_BOOT_PARAMS_CMD)) { + ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED); + } + if (apduIns == INS_PROVISION_CMD && provisionDone) { + ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED); + } + if (apduIns == INS_SET_BOOT_PARAMS_CMD && setBootParamsDone) { ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED); } } @@ -243,44 +297,44 @@ public void process(APDU apdu) { case INS_PROVISION_CMD: processProvisionCmd(apdu); break; + case INS_SET_BOOT_PARAMS_CMD: + processSetBootParamsCmd(apdu); + break; default: ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); } - - } catch (CardRuntimeException exception) { - if (!(KMException.handle(exception.getReason()))) { - CardRuntimeException.throwIt(exception.getReason()); - } + } catch (KMException exception) { + sendError(apdu, exception.reason); + exception.clear(); } finally { repository.clean(); } } /** Sends a response, may be extended response, as requested by the command. */ - private void sendOutgoing(APDU apdu) { - if (bufferLength > MAX_LENGTH) { + public static void sendOutgoing(APDU apdu) { + if (bufferLength > MAX_IO_LENGTH) { ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } // Send data apdu.setOutgoing(); + // short currentBlockSize = apdu.getOutBlockSize(); apdu.setOutgoingLength(bufferLength); apdu.sendBytesLong(buffer, bufferStartOffset, bufferLength); } /** Receives data, which can be extended data, as requested by the command instance. */ - private void receiveIncoming(APDU apdu) { + public static void receiveIncoming(APDU apdu) { byte[] srcBuffer = apdu.getBuffer(); short recvLen = apdu.setIncomingAndReceive(); short srcOffset = apdu.getOffsetCdata(); - bufferLength = apdu.getIncomingLength(); - bufferStartOffset = repository.alloc(bufferLength); - buffer = repository.getHeap(); + bufferLength = apdu.getIncomingLength(); short index = bufferStartOffset; // Receive data - if (bufferLength > MAX_LENGTH) { + if (bufferLength > MAX_IO_LENGTH) { ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } - while (recvLen > 0 && ((short)(index - bufferStartOffset) < bufferLength)) { + while (recvLen > 0 && ((short) (index - bufferStartOffset) < bufferLength)) { Util.arrayCopyNonAtomic(srcBuffer, srcOffset, buffer, index, recvLen); index += recvLen; recvLen = apdu.receiveBytes(srcOffset); @@ -293,31 +347,33 @@ private void processProvisionCmd(APDU apdu) { // Re-purpose the apdu buffer as scratch pad. byte[] scratchPad = apdu.getBuffer(); Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) apdu.getBuffer().length, (byte) 0); - // Argument 1 + // Arguments short keyparams = KMKeyParameters.exp(); - // Argument 2 short keyFormat = KMEnum.instance(KMType.KEY_FORMAT); - // Argument 3 short keyBlob = KMByteBlob.exp(); - // Array of expected arguments short argsProto = KMArray.instance((short) 3); KMArray.cast(argsProto).add((short) 0, keyparams); KMArray.cast(argsProto).add((short) 1, keyFormat); KMArray.cast(argsProto).add((short) 2, keyBlob); // Decode the argument short args = decoder.decode(argsProto, buffer, bufferStartOffset, bufferLength); + // key params should have os patch, os version and verified root of trust + // TODO execute the function // Change the state to ACTIVE if (keymasterState == KMKeymasterApplet.FIRST_SELECT_STATE) { - keymasterState = KMKeymasterApplet.ACTIVE_STATE; + provisionDone = true; + if (setBootParamsDone) { + keymasterState = KMKeymasterApplet.ACTIVE_STATE; + } } } private void processGetHwInfoCmd(APDU apdu) { // No arguments expected final byte[] JavacardKeymasterDevice = { - 0x4A, 0x61, 0x76, 0x61, 0x63, 0x61, 0x72, 0x64, 0x4B, 0x65, 0x79, 0x6D, 0x61, 0x73, 0x74, 0x65, - 0x72, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, + 0x4A, 0x61, 0x76, 0x61, 0x63, 0x61, 0x72, 0x64, 0x4B, 0x65, 0x79, 0x6D, 0x61, 0x73, 0x74, + 0x65, 0x72, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, }; final byte[] Google = {0x47, 0x6F, 0x6F, 0x67, 0x6C, 0x65}; @@ -325,14 +381,13 @@ private void processGetHwInfoCmd(APDU apdu) { short respPtr = KMArray.instance((short) 3); KMArray resp = KMArray.cast(respPtr); resp.add((short) 0, KMEnum.instance(KMType.HARDWARE_TYPE, KMType.STRONGBOX)); - resp.add((short) 1, KMByteBlob.instance( - JavacardKeymasterDevice, (short) 0, (short) JavacardKeymasterDevice.length)); + resp.add( + (short) 1, + KMByteBlob.instance( + JavacardKeymasterDevice, (short) 0, (short) JavacardKeymasterDevice.length)); resp.add((short) 2, KMByteBlob.instance(Google, (short) 0, (short) Google.length)); - //TODO change from MAX_LENGTH to actual length. - buffer = repository.getHeap(); - bufferStartOffset = repository.alloc((short)128); // Encode the response - actual bufferLength is 86 - bufferLength = encoder.encode(respPtr, buffer, bufferStartOffset, MAX_LENGTH); + bufferLength = encoder.encode(respPtr, buffer, bufferStartOffset); // send buffer to master sendOutgoing(apdu); } @@ -352,96 +407,1109 @@ private void processAddRngEntropyCmd(APDU apdu) { KMByteBlob blob = KMByteBlob.cast(KMArray.cast(args).get((short) 0)); // Maximum 2KiB of seed is allowed. if (blob.length() > MAX_SEED_SIZE) { - ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + KMException.throwIt(KMError.INVALID_ARGUMENT); } - // Get existing entropy pool. - byte[] entPool = KMUtil.getEntropyPool(); - // Create new temporary pool. - byte[] heapRef = repository.getHeap(); - short poolStart = repository.alloc((short) entPool.length); - // Populate the new pool with the entropy which is derived from current entropy pool. - KMUtil.newRandomNumber(heapRef, poolStart, (short) entPool.length); - // Copy the entropy to the current pool - updates the entropy pool. - Util.arrayCopy(heapRef, poolStart, entPool, (short) 0, (short) entPool.length); - short index = 0; - short randIndex = 0; - // Mix (XOR) the seed received from the master in the entropy pool - 32 bytes (entPool.length). - // at a time. - while (index < blob.length()) { - entPool[randIndex] = (byte) (entPool[randIndex] ^ blob.get(index)); - randIndex++; - index++; - if (randIndex >= entPool.length) { - randIndex = 0; + cryptoProvider.addRngEntropy(blob.getBuffer(), blob.getStartOff(), blob.length()); + } + + private void processAbortOperationCmd(APDU apdu) {} + + private void processFinishOperationCmd(APDU apdu) {} + + private void processUpdateOperationCmd(APDU apdu) {} + + private void processBeginOperationCmd(APDU apdu) {} + + private void processGetKeyCharacteristicsCmd(APDU apdu) { + // Receive the incoming request fully from the master. + receiveIncoming(apdu); + // Re-purpose the apdu buffer as scratch pad. + byte[] scratchPad = apdu.getBuffer(); + // Arguments + tmpVariables[0] = KMArray.instance((short) 3); + KMArray.cast(tmpVariables[0]).add((short) 0, KMByteBlob.exp()); + KMArray.cast(tmpVariables[0]).add((short) 1, KMByteBlob.exp()); + KMArray.cast(tmpVariables[0]).add((short) 2, KMByteBlob.exp()); + // Decode the arguments + tmpVariables[0] = decoder.decode(tmpVariables[0], buffer, bufferStartOffset, bufferLength); + data[KEY_BLOB] = KMArray.cast(tmpVariables[0]).get((short) 0); + data[APP_ID] = KMArray.cast(tmpVariables[0]).get((short) 1); + data[APP_DATA] = KMArray.cast(tmpVariables[0]).get((short) 2); + if(KMByteBlob.cast(data[APP_ID]).length() == 2){ + if(Util.getShort(repository.getHeap(),KMByteBlob.cast(data[APP_ID]).getStartOff()) == KMType.INVALID_VALUE){ + data[APP_ID] = KMType.INVALID_VALUE; } } + if(KMByteBlob.cast(data[APP_DATA]).length() == 2){ + if(Util.getShort(repository.getHeap(),KMByteBlob.cast(data[APP_DATA]).getStartOff()) == KMType.INVALID_VALUE){ + data[APP_DATA] = KMType.INVALID_VALUE; + } + } + // Parse Key Blob + parseEncryptedKeyBlob(scratchPad); + // Check Version and Patch Level + checkVersionAndPatchLevel(scratchPad); + // make response. + tmpVariables[0] = KMArray.instance((short) 2); + KMArray.cast(tmpVariables[0]).add((short) 0, KMInteger.uint_16(KMError.OK)); + KMArray.cast(tmpVariables[0]).add((short) 1, data[KEY_CHARACTERISTICS]); + // Encode the response + bufferLength = encoder.encode(tmpVariables[0], buffer, bufferStartOffset); + sendOutgoing(apdu); } - private void processAbortOperationCmd(APDU apdu) { + private void processGetHmacSharingParamCmd(APDU apdu) {} - } + private void processVerifyAuthenticationCmd(APDU apdu) {} - private void processFinishOperationCmd(APDU apdu) { + private void processDestroyAttIdsCmd(APDU apdu) {} - } + private void processComputeSharedHmacCmd(APDU apdu) {} - private void processUpdateOperationCmd(APDU apdu) { + private void processDeleteAllKeysCmd(APDU apdu) {} - } + private void processDeleteKeyCmd(APDU apdu) {} - private void processBeginOperationCmd(APDU apdu) { + private void processUpgradeKeyCmd(APDU apdu) {} - } + private void processAttestKeyCmd(APDU apdu) {} + + private void processExportKeyCmd(APDU apdu) {} - private void processGetKeyCharacteristicsCmd(APDU apdu) { + private void processImportWrappedKeyCmd(APDU apdu) {} + private void processImportKeyCmd(APDU apdu) { + if (repository.keyBlobCount > repository.MAX_BLOB_STORAGE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + // Receive the incoming request fully from the master into buffer. + receiveIncoming(apdu); + byte[] scratchPad = apdu.getBuffer(); + tmpVariables[1] = KMArray.instance((short)3); + // Arguments + tmpVariables[2] = KMKeyParameters.exp(); + KMArray.cast(tmpVariables[1]).add((short) 0, tmpVariables[2]); + KMArray.cast(tmpVariables[1]).add((short) 1, KMEnum.instance(KMType.KEY_FORMAT)); + KMArray.cast(tmpVariables[1]).add((short) 2, KMByteBlob.exp()); + // Decode the arguments + tmpVariables[2] = decoder.decode(tmpVariables[1], buffer, bufferStartOffset, bufferLength); + data[KEY_PARAMETERS] = KMArray.cast(tmpVariables[2]).get((short) 0); + tmpVariables[3] = KMArray.cast(tmpVariables[2]).get((short) 1); + data[IMPORTED_KEY_BLOB] = KMArray.cast(tmpVariables[2]).get((short) 2); + // Keyformat must be RAW format - X509 and PKCS8 not implemented. + tmpVariables[3] = KMEnum.cast(tmpVariables[3]).getVal(); + if (tmpVariables[3] != KMType.RAW) { + KMException.throwIt(KMError.UNIMPLEMENTED); + } + // get algorithm + tmpVariables[3] = KMEnumTag.getValue(KMType.ALGORITHM, data[KEY_PARAMETERS]); + if (tmpVariables[3] == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + // Check algorithm and dispatch to appropriate handler. + switch (tmpVariables[3]) { + case KMType.RSA: + importRSAKey(scratchPad); + break; + case KMType.AES: + importAESKey(scratchPad); + break; + case KMType.DES: + importTDESKey(scratchPad); + break; + case KMType.HMAC: + importHmacKey(scratchPad); + break; + case KMType.EC: + importECKeys(scratchPad); + break; + default: + KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); + break; + } + // create key blob + data[ORIGIN] = KMType.IMPORTED; + createEncryptedKeyBlob(scratchPad); + // persist auth tag for rollback resistance. + repository.persistAuthTag(data[AUTH_TAG]); + // prepare the response + tmpVariables[0] = KMArray.instance((short) 3); + KMArray.cast(tmpVariables[0]).add((short) 0, KMInteger.uint_16(KMError.OK)); + KMArray.cast(tmpVariables[0]).add((short) 1, data[KEY_BLOB]); + KMArray.cast(tmpVariables[0]).add((short) 2, data[KEY_CHARACTERISTICS]); + // Encode the response + bufferLength = encoder.encode(tmpVariables[0], buffer, bufferStartOffset); + sendOutgoing(apdu); + } + + private void importECKeys(byte[] scratchPad) { + // Decode key material + tmpVariables[0] = KMArray.instance((short) 3); + KMArray.cast(tmpVariables[0]).add((short) 0, KMByteBlob.exp()); // secret + KMArray.cast(tmpVariables[0]).add((short) 1, KMByteBlob.exp()); // public key + KMArray.cast(tmpVariables[0]).add((short) 2, KMEnumTag.exp()); // curve + tmpVariables[0] = + decoder.decode( + tmpVariables[0], + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getBuffer(), + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getStartOff(), + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).length()); + data[SECRET] = KMArray.cast(tmpVariables[0]).get((short) 0); + data[PUB_KEY] = KMArray.cast(tmpVariables[0]).get((short) 1); + tmpVariables[1] = KMArray.cast(tmpVariables[0]).get((short) 2); + tmpVariables[1] = KMEnumTag.cast(tmpVariables[1]).getValue(); + // curve must be P_256 + if(tmpVariables[1] != KMType.P_256){ + KMException.throwIt(KMError.UNSUPPORTED_EC_CURVE); + } + // initialize 256 bit p256 key for given private key and public key. + ECPrivateKey ecKey = + cryptoProvider.createEcPrivateKey( + KMByteBlob.cast(data[PUB_KEY]).getBuffer(), + KMByteBlob.cast(data[PUB_KEY]).getStartOff(), + KMByteBlob.cast(data[PUB_KEY]).length(), + KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length() + ); + tmpVariables[4] = 0; // index for update list in scratchPad + // check whether the keysize tag is present in key parameters. + tmpVariables[2] = + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); + if (tmpVariables[2] != KMType.INVALID_VALUE) { + if (tmpVariables[2] != 256) { + KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); + } + }else{ + // add the key size to scratchPad + tmpVariables[5] = KMInteger.uint_16((short)256); + tmpVariables[6] = KMIntegerTag.instance(KMType.UINT_TAG,KMType.KEYSIZE, tmpVariables[5]); + Util.setShort(scratchPad,tmpVariables[4],tmpVariables[6]); + tmpVariables[4] +=2; + } + // check the curve if present in key parameters. + tmpVariables[3] = KMEnumTag.getValue(KMType.ECCURVE,data[KEY_PARAMETERS]); + if(tmpVariables[3] != KMType.INVALID_VALUE){ + if(tmpVariables[3] != tmpVariables[1]){ + KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); + } + }else{ + // add the curve to scratchPad + tmpVariables[5] = KMEnumTag.instance(KMType.ECCURVE,KMType.P_256); + Util.setShort(scratchPad,tmpVariables[4],tmpVariables[5]); + tmpVariables[4] +=2; + } + // add scratch pad to key parameters + updateKeyParameters(scratchPad, tmpVariables[4]); + // validate updated key parameters. + validateECKeys(scratchPad); + data[KEY_BLOB] = KMArray.instance((short)5); + KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PUB_KEY, data[PUB_KEY]); } - private void processGetHmacSharingParamCmd(APDU apdu) { + private void importHmacKey(byte[] scratchPad) { + // Get Key + tmpVariables[0] = KMArray.instance((short) 1); + KMArray.cast(tmpVariables[0]).add((short) 0, KMByteBlob.exp()); // secret + tmpVariables[0] = + decoder.decode( + tmpVariables[0], + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getBuffer(), + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getStartOff(), + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).length()); + data[SECRET] = KMArray.cast(tmpVariables[0]).get((short) 0); + // create HMAC key of up to 512 bit + HMACKey hmacKey = cryptoProvider.createHMACKey( + KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length() + ); + tmpVariables[4] = 0; // index in scratchPad for update params + // check the keysize tag if present in key parameters. + tmpVariables[2] = + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); + if (tmpVariables[2] != KMType.INVALID_VALUE) { + if (!(tmpVariables[2] > 64 && tmpVariables[2] <= 512 && tmpVariables[2]%8 ==0)) { + KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); + } + }else{ + // add the key size to scratchPad + tmpVariables[5] = KMInteger.uint_16(KMByteBlob.cast(data[SECRET]).length()); + tmpVariables[6] = KMIntegerTag.instance(KMType.UINT_TAG,KMType.KEYSIZE, tmpVariables[5]); + Util.setShort(scratchPad,tmpVariables[4],tmpVariables[6]); + tmpVariables[4] +=2; + } + // update the key parameters list + updateKeyParameters(scratchPad, tmpVariables[4]); + // validate HMAC Key parameters + validateHmacKey(scratchPad); + data[KEY_BLOB] = KMArray.instance((short)4); } - private void processVerifyAuthenticationCmd(APDU apdu) { + private void importTDESKey(byte[] scratchPad) { + // Decode Key Material + tmpVariables[0] = KMArray.instance((short) 1); + KMArray.cast(tmpVariables[0]).add((short) 0, KMByteBlob.exp()); // secret + tmpVariables[0] = + decoder.decode( + tmpVariables[0], + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getBuffer(), + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getStartOff(), + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).length()); + data[SECRET] = KMArray.cast(tmpVariables[0]).get((short) 0); + DESKey desKey = cryptoProvider.createTDESKey( + KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length() + ); + tmpVariables[4] = 0; // index in scratchPad for update params + // check the keysize tag if present in key parameters. + tmpVariables[2] = + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); + if (tmpVariables[2] != KMType.INVALID_VALUE) { + if (tmpVariables[2] != 168) { + KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); + } + }else{ + // add the key size to scratchPad + tmpVariables[5] = KMInteger.uint_16((short)168); + tmpVariables[6] = KMIntegerTag.instance(KMType.UINT_TAG,KMType.KEYSIZE, tmpVariables[5]); + Util.setShort(scratchPad,tmpVariables[4],tmpVariables[6]); + tmpVariables[4] +=2; + } + // update the key parameters list + updateKeyParameters(scratchPad, tmpVariables[4]); + // validate TDES Key parameters + validateTDESKey(scratchPad); + data[KEY_BLOB] = KMArray.instance((short)4); } - private void processDestroyAttIdsCmd(APDU apdu) { + private void importAESKey(byte[] scratchPad) { + // Get Key + tmpVariables[0] = KMArray.instance((short) 1); + KMArray.cast(tmpVariables[0]).add((short) 0, KMByteBlob.exp()); // secret + tmpVariables[0] = + decoder.decode( + tmpVariables[0], + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getBuffer(), + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getStartOff(), + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).length()); + data[SECRET] = KMArray.cast(tmpVariables[0]).get((short) 0); + // create 128 or 256 bit AES key + AESKey aesKey = cryptoProvider.createAESKey( + KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length() + ); + tmpVariables[4] = 0; // index in scratchPad for update params + // check the keysize tag if present in key parameters. + tmpVariables[2] = + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); + if (tmpVariables[2] != KMType.INVALID_VALUE) { + if (tmpVariables[2] != 128 && tmpVariables[2] != 256) { + KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); + } + }else{ + // add the key size to scratch pad + // add the key size to scratchPad + tmpVariables[5] = KMInteger.uint_16(KMByteBlob.cast(data[SECRET]).length()); + tmpVariables[6] = KMIntegerTag.instance(KMType.UINT_TAG,KMType.KEYSIZE, tmpVariables[5]); + Util.setShort(scratchPad,tmpVariables[4],tmpVariables[6]); + tmpVariables[4] +=2; + } + // update the key parameters list + updateKeyParameters(scratchPad, tmpVariables[4]); + // validate AES Key parameters + validateAESKey(scratchPad); + data[KEY_BLOB] = KMArray.instance((short)4); + } + private void importRSAKey(byte[] scratchPad) { + // Decode key material + tmpVariables[0] = KMArray.instance((short) 2); + KMArray.cast(tmpVariables[0]).add((short) 0, KMByteBlob.exp()); // secret = private exponent + KMArray.cast(tmpVariables[0]).add((short) 1, KMByteBlob.exp()); // modulus + tmpVariables[0] = + decoder.decode( + tmpVariables[0], + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getBuffer(), + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getStartOff(), + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).length()); + data[SECRET] = KMArray.cast(tmpVariables[0]).get((short) 0); + data[PUB_KEY] = KMArray.cast(tmpVariables[0]).get((short) 1); + tmpVariables[4] = 0; // index in scratchPad for update parameters. + // validate public exponent if present in key params - it must be 0x010001 + tmpVariables[2] = + KMIntegerTag.getValue( + scratchPad, + (short) 10, // using offset 10 as first 10 bytes reserved for update params + KMType.ULONG_TAG, + KMType.RSA_PUBLIC_EXPONENT, + data[KEY_PARAMETERS]); + if (tmpVariables[2] != KMTag.INVALID_VALUE) { + if ( tmpVariables[2] != 4 || Util.getShort(scratchPad, (short) 10) != 0x01 + || Util.getShort(scratchPad, (short) 12) != 0x01) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + }else{ + // add public exponent to scratchPad + Util.setShort(scratchPad,(short)10, (short)0x01); + Util.setShort(scratchPad,(short)12, (short)0x01); + tmpVariables[5] = KMInteger.uint_32(scratchPad,(short)10); + tmpVariables[6] = KMIntegerTag.instance(KMType.ULONG_TAG,KMType.RSA_PUBLIC_EXPONENT, tmpVariables[5]); + Util.setShort(scratchPad,tmpVariables[4],tmpVariables[6]); + tmpVariables[4] +=2; + } + + // initialize 2048 bit private key for given private exp and modulus. + RSAPrivateKey rsaKey = + cryptoProvider.createRsaPrivateKey( + KMByteBlob.cast(data[PUB_KEY]).getBuffer(), + KMByteBlob.cast(data[PUB_KEY]).getStartOff(), + KMByteBlob.cast(data[PUB_KEY]).length(), + KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length() + ); + // check the keysize tag if present in key parameters. + tmpVariables[2] = + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); + if (tmpVariables[2] != KMType.INVALID_VALUE) { + if (tmpVariables[2] != 2048) { + KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); + } + }else{ + // add the key size to scratchPad + tmpVariables[5] = KMInteger.uint_16((short)2048); + tmpVariables[6] = KMIntegerTag.instance(KMType.UINT_TAG,KMType.KEYSIZE, tmpVariables[5]); + Util.setShort(scratchPad,tmpVariables[4],tmpVariables[6]); + tmpVariables[4] +=2; + } + // update the key parameters list + updateKeyParameters(scratchPad, tmpVariables[4]); + // validate RSA Key parameters + validateRSAKey(scratchPad); + data[KEY_BLOB] = KMArray.instance((short)5); + KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PUB_KEY, data[PUB_KEY]); } - private void processComputeSharedHmacCmd(APDU apdu) { + private void updateKeyParameters(byte[] ptrArr, short len){ + if(len == 0) { + return; // nothing to update + } + // Create Update Param array and copy current params + tmpVariables[0] = KMKeyParameters.cast(data[KEY_PARAMETERS]).getVals(); + tmpVariables[1] = (short)(KMArray.cast(tmpVariables[0]).length()+(short)(len/2)); + tmpVariables[1] = KMArray.instance(tmpVariables[1]);// update params + tmpVariables[2] = KMArray.cast(tmpVariables[0]).length(); + tmpVariables[3] = 0; + // copy the existing key parameters to updated array + while(tmpVariables[3] < tmpVariables[2]){ + tmpVariables[4] = KMArray.cast(tmpVariables[0]).get(tmpVariables[3]); + KMArray.cast(tmpVariables[1]).add(tmpVariables[3],tmpVariables[4]); + tmpVariables[3]++; + } + // copy new parameters to updated array + tmpVariables[2] = KMArray.cast(tmpVariables[1]).length(); + tmpVariables[5] = 0; // index in ptrArr + while(tmpVariables[3] < tmpVariables[2]){ + tmpVariables[4] = Util.getShort(ptrArr,tmpVariables[5]); + KMArray.cast(tmpVariables[1]).add(tmpVariables[3],tmpVariables[4]); + tmpVariables[3]++; + tmpVariables[5] +=2; + } + // replace with updated key parameters. + data[KEY_PARAMETERS] = KMKeyParameters.instance(tmpVariables[1]); + } + // TODO Add Signature verification. + private void processSetBootParamsCmd(APDU apdu) { + receiveIncoming(apdu); + // Re-purpose the apdu buffer as scratch pad. + byte[] scratchPad = apdu.getBuffer(); + Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) apdu.getBuffer().length, (byte) 0); + // Argument 1 OS Version + // short osVersionExp = KMIntegerTag.exp(KMType.UINT_TAG); + tmpVariables[0] = KMIntegerTag.exp(KMType.UINT_TAG); + // Argument 2 OS Patch level + // short osPatchExp = KMIntegerTag.exp(KMType.UINT_TAG); + tmpVariables[1] = KMIntegerTag.exp(KMType.UINT_TAG); + // Argument 3 Verified Boot Key + // short bootKeyExp = KMByteBlob.exp(); + tmpVariables[2] = KMByteBlob.exp(); + // Argument 4 Verified Boot Hash + // short bootHashExp = KMByteBlob.exp(); + tmpVariables[3] = KMByteBlob.exp(); + // Argument 5 Verified Boot State + // short bootStateExp = KMEnum.instance(KMType.VERIFIED_BOOT_STATE); + tmpVariables[4] = KMEnum.instance(KMType.VERIFIED_BOOT_STATE); + // Argument 6 Device Locked + // short deviceLockedExp = KMEnum.instance(KMType.DEVICE_LOCKED); + tmpVariables[5] = KMEnum.instance(KMType.DEVICE_LOCKED); + // Array of expected arguments + short argsProto = KMArray.instance((short) 6); + KMArray.cast(argsProto).add((short) 0, tmpVariables[0]); + KMArray.cast(argsProto).add((short) 1, tmpVariables[1]); + KMArray.cast(argsProto).add((short) 2, tmpVariables[2]); + KMArray.cast(argsProto).add((short) 3, tmpVariables[3]); + KMArray.cast(argsProto).add((short) 4, tmpVariables[4]); + KMArray.cast(argsProto).add((short) 5, tmpVariables[5]); + // Decode the arguments + short args = decoder.decode(argsProto, buffer, bufferStartOffset, bufferLength); + // short osVersionTagPtr = KMArray.cast(args).get((short) 0); + tmpVariables[0] = KMArray.cast(args).get((short) 0); + // short osPatchTagPtr = KMArray.cast(args).get((short) 1); + tmpVariables[1] = KMArray.cast(args).get((short) 1); + // short verifiedBootKeyPtr = KMArray.cast(args).get((short) 2); + tmpVariables[2] = KMArray.cast(args).get((short) 2); + // short verifiedBootHashPtr = KMArray.cast(args).get((short) 3); + tmpVariables[3] = KMArray.cast(args).get((short) 3); + // short verifiedBootStatePtr = KMArray.cast(args).get((short) 4); + tmpVariables[4] = KMArray.cast(args).get((short) 4); + // short deviceLockedPtr = KMArray.cast(args).get((short) 5); + tmpVariables[5] = KMArray.cast(args).get((short) 5); + if (KMByteBlob.cast(tmpVariables[2]).length() > repository.BOOT_KEY_MAX_SIZE) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + if (KMByteBlob.cast(tmpVariables[3]).length() > repository.BOOT_HASH_MAX_SIZE) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + // Begin transaction + JCSystem.beginTransaction(); + short valPtr = KMIntegerTag.cast(tmpVariables[0]).getValue(); + KMInteger.cast(valPtr).getValue(repository.osVersion, (short) 0, (short) 4); + valPtr = KMIntegerTag.cast(tmpVariables[1]).getValue(); + KMInteger.cast(valPtr).getValue(repository.osPatch, (short) 0, (short) 4); + repository.actualBootKeyLength = KMByteBlob.cast(tmpVariables[2]).length(); + KMByteBlob.cast(tmpVariables[2]) + .getValue(repository.verifiedBootKey, (short) 0, repository.actualBootKeyLength); + repository.actualBootHashLength = KMByteBlob.cast(tmpVariables[3]).length(); + KMByteBlob.cast(tmpVariables[3]) + .getValue(repository.verifiedBootHash, (short) 0, repository.actualBootHashLength); + byte enumVal = KMEnum.cast(tmpVariables[4]).getVal(); + if (enumVal == KMTag.SELF_SIGNED_BOOT) { + repository.selfSignedBootFlag = true; + repository.verifiedBootFlag = false; + } else { + repository.selfSignedBootFlag = false; + repository.verifiedBootFlag = true; + } + enumVal = KMEnum.cast(tmpVariables[5]).getVal(); + if (enumVal == KMType.DEVICE_LOCKED_TRUE) { + repository.deviceLockedFlag = true; + } else { + repository.deviceLockedFlag = false; + } + if (keymasterState == KMKeymasterApplet.FIRST_SELECT_STATE) { + setBootParamsDone = true; + if (provisionDone) { + keymasterState = KMKeymasterApplet.ACTIVE_STATE; + } + } + // end transaction + JCSystem.commitTransaction(); } - private void processDeleteAllKeysCmd(APDU apdu) { + private static void processGenerateKey(APDU apdu) { + // before generating key, check whether max count reached + if (repository.keyBlobCount > repository.MAX_BLOB_STORAGE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + // Receive the incoming request fully from the master into buffer. + receiveIncoming(apdu); + // Re-purpose the apdu buffer as scratch pad. + byte[] scratchPad = apdu.getBuffer(); + // Argument + tmpVariables[0] = KMKeyParameters.exp(); + // Array of expected arguments + tmpVariables[1] = KMArray.instance((short) 1); + KMArray.cast(tmpVariables[1]).add((short) 0, tmpVariables[0]); + // Decode the argument + tmpVariables[2] = decoder.decode(tmpVariables[1], buffer, bufferStartOffset, bufferLength); + data[KEY_PARAMETERS] = KMArray.cast(tmpVariables[2]).get((short) 0); + // get algorithm + tmpVariables[3] = KMEnumTag.getValue(KMType.ALGORITHM, data[KEY_PARAMETERS]); + if (tmpVariables[3] == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + // Check algorithm and dispatch to appropriate handler. + switch (tmpVariables[3]) { + case KMType.RSA: + generateRSAKey(scratchPad); + break; + case KMType.AES: + generateAESKey(scratchPad); + break; + case KMType.DES: + generateTDESKey(scratchPad); + break; + case KMType.HMAC: + generateHmacKey(scratchPad); + break; + case KMType.EC: + generateECKeys(scratchPad); + break; + default: + KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); + break; + } + // create key blob + data[ORIGIN] = KMType.GENERATED; + createEncryptedKeyBlob(scratchPad); + // persist auth tag for rollback resistance. + repository.persistAuthTag(data[AUTH_TAG]); + // prepare the response + tmpVariables[0] = KMArray.instance((short) 3); + KMArray.cast(tmpVariables[0]).add((short) 0, KMInteger.uint_16(KMError.OK)); + KMArray.cast(tmpVariables[0]).add((short) 1, data[KEY_BLOB]); + KMArray.cast(tmpVariables[0]).add((short) 2, data[KEY_CHARACTERISTICS]); + // Encode the response + bufferLength = encoder.encode(tmpVariables[0], buffer, bufferStartOffset); + sendOutgoing(apdu); + } + private static void validateRSAKey(byte[] scratchPad){ + // Read key size + tmpVariables[0] = + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); + if (tmpVariables[0] == KMTag.INVALID_VALUE) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + if (tmpVariables[0] != 2048) { + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); + } + // Read public exponent into scratch pad + tmpVariables[1] = + KMIntegerTag.getValue( + scratchPad, + (short) 0, + KMType.ULONG_TAG, + KMType.RSA_PUBLIC_EXPONENT, + data[KEY_PARAMETERS]); + if ((tmpVariables[1] == KMTag.INVALID_VALUE) || (tmpVariables[1] != 4)) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + // Only exponent support is F4 - 65537 which is 0x00010001. + if (Util.getShort(scratchPad, (short) 0) != 0x01 + || Util.getShort(scratchPad, (short) 2) != 0x01) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } } - private void processDeleteKeyCmd(APDU apdu) { + // Generate key handlers + private static void generateRSAKey(byte[] scratchPad) { + // Validate RSA Key + validateRSAKey(scratchPad); + // Now generate 2048 bit RSA keypair for the given exponent + KeyPair rsaKey = cryptoProvider.createRsaKeyPair(); + // store the pub exponent + data[RSA_PUB_EXPONENT] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[1]); + // extract modulus + tmpVariables[0] = ((RSAPrivateKey) rsaKey.getPrivate()).getModulus(scratchPad, (short) 0); + data[PUB_KEY] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[0]); + // extract private key + tmpVariables[0] = ((RSAPrivateKey) rsaKey.getPrivate()).getExponent(scratchPad, (short) 0); + data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[0]); + data[KEY_BLOB] = KMArray.instance((short) 5); + KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PUB_KEY, data[PUB_KEY]); + } + private static void validateAESKey(byte[] scratchPad){ + // Read key size + tmpVariables[0] = + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); + if (tmpVariables[0] == KMTag.INVALID_VALUE) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + if ((tmpVariables[0] != 256) && (tmpVariables[0] != 128)) { + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); + } + // Read Block mode - array of byte values + tmpVariables[1] = + KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.BLOCK_MODE, data[KEY_PARAMETERS]); + if (tmpVariables[1] != KMTag.INVALID_VALUE) { // block mode specified + tmpVariables[2] = KMEnumArrayTag.cast(tmpVariables[0]).getValues(); // byte blob + tmpVariables[3] = KMByteBlob.cast(tmpVariables[2]).length(); // length + tmpVariables[4] = 0; // index + tmpVariables[5] = AES_BLOCK_SIZE; // block size + tmpVariables[5] = + KMKeyParameters.findTag( + KMType.UINT_TAG, + KMType.MIN_MAC_LENGTH, + data[KEY_PARAMETERS]); // Find Minimum Mac length + while (tmpVariables[4] < tmpVariables[3]) { // for each value in block mode array + if (KMByteBlob.cast(tmpVariables[2]).get(tmpVariables[4]) == KMType.GCM) { // if GCM mode + if (tmpVariables[5] == KMTag.INVALID_VALUE) { // minimum mac length must be specified + KMException.throwIt(KMError.MISSING_MAC_LENGTH); + } + tmpVariables[6] = KMInteger.cast(KMIntegerTag.cast(tmpVariables[5]).getValue()).getByte(); + if (tmpVariables[6] < 12 || tmpVariables[6] > 16) { + KMException.throwIt(KMError.UNSUPPORTED_MAC_LENGTH); + } + tmpVariables[6] = 12; // simulator supports only 12 bits tag for GCM. + } else { // if not GCM mode + if (tmpVariables[5] != KMTag.INVALID_VALUE) { // no mac length should be specified + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + } + tmpVariables[4]++; + } + } + } + private static void generateAESKey(byte[] scratchPad) { + validateAESKey(scratchPad); + AESKey aesKey = cryptoProvider.createAESKey(tmpVariables[0]); + tmpVariables[0] = aesKey.getKey(scratchPad, (short) 0); + data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[0]); + data[KEY_BLOB] = KMArray.instance((short) 4); + } + private static void validateECKeys(byte[] scratchPad){ + // Read key size + tmpVariables[0] = + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); + if (tmpVariables[0] == KMTag.INVALID_VALUE) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + if (tmpVariables[0] != 256) { + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); + } + // Read EC_CURVE + tmpVariables[1] = KMEnumTag.getValue(KMType.ECCURVE, data[KEY_PARAMETERS]); + if (tmpVariables[1] != KMType.INVALID_VALUE) { + if (tmpVariables[1] != KMType.P_256) { + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); + } + } } - private void processUpgradeKeyCmd(APDU apdu) { + private static void generateECKeys(byte[] scratchPad) { + validateECKeys(scratchPad); + KeyPair ecKey = cryptoProvider.createECKeyPair(); + tmpVariables[5] = ((ECPublicKey) ecKey.getPublic()).getW(scratchPad, (short) 0); + data[PUB_KEY] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[5]); + tmpVariables[5] = ((ECPrivateKey) ecKey.getPrivate()).getS(scratchPad, (short) 0); + data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[5]); + data[KEY_BLOB] = KMArray.instance((short) 5); + KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PUB_KEY, data[PUB_KEY]); + } + private static void validateTDESKey(byte[] scratchPad){ + // Read Minimum Mac length - it must not be present + tmpVariables[0] = + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, data[KEY_PARAMETERS]); + if (tmpVariables[0] != KMType.INVALID_VALUE) { + KMException.throwIt(KMError.INVALID_TAG); + } + // Read keysize + tmpVariables[1] = + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); + if (tmpVariables[1] == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + if (tmpVariables[1] != 168) { + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); + } + } + private static void generateTDESKey(byte[] scratchPad) { + validateTDESKey(scratchPad); + DESKey desKey = cryptoProvider.createTDESKey(); + tmpVariables[0] = desKey.getKey(scratchPad, (short) 0); + data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[0]); + data[KEY_BLOB] = KMArray.instance((short) 4); + } + + private static void validateHmacKey(byte[] scratchPad){ + // Read Minimum Mac length + tmpVariables[0] = + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, data[KEY_PARAMETERS]); + if (tmpVariables[0] == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.MISSING_MIN_MAC_LENGTH); + } + if (((short) (tmpVariables[0] % 8) != 0) || (tmpVariables[0] < (short) 64)) { + KMException.throwIt(KMError.UNSUPPORTED_MIN_MAC_LENGTH); + } + // Read keysize + tmpVariables[1] = + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); + if (tmpVariables[1] == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + if ((tmpVariables[1] > 512) || ((short) (tmpVariables[1] % 8) != 0)) { + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); + } + // Read digests + tmpVariables[2] = + KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, data[KEY_PARAMETERS]); + if (tmpVariables[2] == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + tmpVariables[3] = KMEnumArrayTag.cast(tmpVariables[2]).getValues(); + tmpVariables[4] = KMByteBlob.cast(tmpVariables[3]).length(); + tmpVariables[5] = 0; + // check whether digest sizes are greater then or equal to min mac length. + while (tmpVariables[5] < tmpVariables[4]) { + tmpVariables[6] = KMByteBlob.cast(tmpVariables[3]).get(tmpVariables[5]); + switch (tmpVariables[6]) { + case KMType.DIGEST_NONE: + KMException.throwIt(KMError.UNSUPPORTED_DIGEST); + break; + case KMType.MD5: + tmpVariables[7] = 128; + break; + case KMType.SHA1: + tmpVariables[7] = 160; + break; + case KMType.SHA2_224: + tmpVariables[7] = 224; + break; + case KMType.SHA2_256: + tmpVariables[7] = 256; + break; + case KMType.SHA2_384: + tmpVariables[7] = 384; + break; + case KMType.SHA2_512: + tmpVariables[7] = 512; + break; + default: + tmpVariables[7] = 0; + break; + } + if (tmpVariables[7] == 0) { + KMException.throwIt(KMError.UNSUPPORTED_DIGEST); + } + if (tmpVariables[0] > tmpVariables[7]) { + KMException.throwIt(KMError.UNSUPPORTED_MIN_MAC_LENGTH); + } + tmpVariables[5]++; + } + } + private static void generateHmacKey(byte[] scratchPad) { + validateHmacKey(scratchPad); + // Read Minimum Mac length + tmpVariables[0] = + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, data[KEY_PARAMETERS]); + if (tmpVariables[0] == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.MISSING_MIN_MAC_LENGTH); + } + if (((short) (tmpVariables[0] % 8) != 0) || (tmpVariables[0] < (short) 64)) { + KMException.throwIt(KMError.UNSUPPORTED_MIN_MAC_LENGTH); + } + // Read keysize + tmpVariables[1] = + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); + if (tmpVariables[1] == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + if ((tmpVariables[1] > 512) || ((short) (tmpVariables[1] % 8) != 0)) { + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); + } + // Read digests + tmpVariables[2] = + KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, data[KEY_PARAMETERS]); + if (tmpVariables[2] == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + tmpVariables[3] = KMEnumArrayTag.cast(tmpVariables[2]).getValues(); + tmpVariables[4] = KMByteBlob.cast(tmpVariables[3]).length(); + tmpVariables[5] = 0; + // check whether digest sizes are greater then or equal to min mac length. + while (tmpVariables[5] < tmpVariables[4]) { + tmpVariables[6] = KMByteBlob.cast(tmpVariables[3]).get(tmpVariables[5]); + switch (tmpVariables[6]) { + case KMType.DIGEST_NONE: + KMException.throwIt(KMError.UNSUPPORTED_DIGEST); + break; + case KMType.MD5: + tmpVariables[7] = 128; + break; + case KMType.SHA1: + tmpVariables[7] = 160; + break; + case KMType.SHA2_224: + tmpVariables[7] = 224; + break; + case KMType.SHA2_256: + tmpVariables[7] = 256; + break; + case KMType.SHA2_384: + tmpVariables[7] = 384; + break; + case KMType.SHA2_512: + tmpVariables[7] = 512; + break; + default: + tmpVariables[7] = 0; + break; + } + if (tmpVariables[7] == 0) { + KMException.throwIt(KMError.UNSUPPORTED_DIGEST); + } + if (tmpVariables[0] > tmpVariables[7]) { + KMException.throwIt(KMError.UNSUPPORTED_MIN_MAC_LENGTH); + } + tmpVariables[5]++; + } + // generate HMAC Key + HMACKey hmacKey = cryptoProvider.createHMACKey(tmpVariables[1]); + tmpVariables[0] = hmacKey.getKey(scratchPad, (short) 0); + data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[0]); + data[KEY_BLOB] = KMArray.instance((short) 4); } - private void processAttestKeyCmd(APDU apdu) { + private void checkVersionAndPatchLevel(byte[] scratchPad) { + tmpVariables[0] = + KMIntegerTag.getValue( + scratchPad, (short) 0, KMType.UINT_TAG, KMType.OS_VERSION, data[HW_PARAMETERS]); + if ((tmpVariables[0] != KMType.INVALID_VALUE) + && (Util.arrayCompare( + repository.osVersion, (short) 0, scratchPad, (short) 0, tmpVariables[0]) + != 0)) { + if (Util.arrayCompare(repository.osVersion, (short) 0, scratchPad, (short) 0, tmpVariables[0]) + == -1) { + // If the key characteristics has os version > current os version + KMException.throwIt(KMError.INVALID_KEY_BLOB); + } else { + KMException.throwIt(KMError.KEY_REQUIRES_UPGRADE); + } + } + tmpVariables[0] = + KMIntegerTag.getValue( + scratchPad, (short) 0, KMType.UINT_TAG, KMType.OS_PATCH_LEVEL, data[HW_PARAMETERS]); + if ((tmpVariables[0] != KMType.INVALID_VALUE) + && (Util.arrayCompare(repository.osPatch, (short) 0, scratchPad, (short) 0, tmpVariables[0]) + != 0)) { + if (Util.arrayCompare(repository.osPatch, (short) 0, scratchPad, (short) 0, tmpVariables[0]) + == -1) { + // If the key characteristics has os patch level > current os patch + KMException.throwIt(KMError.INVALID_KEY_BLOB); + } else { + KMException.throwIt(KMError.KEY_REQUIRES_UPGRADE); + } + } + } + private static void makeKeyCharacteristics(byte[] scratchPad) { + tmpVariables[0] = + KMInteger.instance(repository.osPatch, (short) 0, (short) repository.osPatch.length); + tmpVariables[1] = + KMInteger.instance(repository.osVersion, (short) 0, (short) repository.osVersion.length); + data[HW_PARAMETERS] = + KMKeyParameters.makeHwEnforced( + data[KEY_PARAMETERS], (byte)data[ORIGIN], tmpVariables[1], tmpVariables[0], scratchPad); + data[SW_PARAMETERS] = KMKeyParameters.makeSwEnforced(data[KEY_PARAMETERS], scratchPad); + data[KEY_CHARACTERISTICS] = KMKeyCharacteristics.instance(); + KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).setHardwareEnforced(data[HW_PARAMETERS]); + KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).setSoftwareEnforced(data[SW_PARAMETERS]); } - private void processExportKeyCmd(APDU apdu) { + private static void createEncryptedKeyBlob(byte[] scratchPad) { + // make key characteristics - returns key characteristics in data[KEY_CHARACTERISTICS] + makeKeyCharacteristics(scratchPad); + // make root of trust blob + data[ROT] = + KMByteBlob.instance( + repository.verifiedBootKey, (short) 0, (short) repository.verifiedBootKey.length); + // make hidden key params list + data[HIDDEN_PARAMETERS] = + KMKeyParameters.makeHidden(data[KEY_PARAMETERS], data[ROT], scratchPad); + // make authorization data + makeAuthData(scratchPad); + // encrypt the secret and cryptographically attach that to authorization data + encryptSecret(scratchPad); + // create key blob array + KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_SECRET, data[SECRET]); + KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_AUTH_TAG, data[AUTH_TAG]); + KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_NONCE, data[NONCE]); + KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_KEYCHAR, data[KEY_CHARACTERISTICS]); + tmpVariables[0] = repository.alloc((short) 256); // TODO use buffer + tmpVariables[1] = encoder.encode(data[KEY_BLOB], repository.getHeap(), tmpVariables[0]); + data[KEY_BLOB] = KMByteBlob.instance(repository.getHeap(), tmpVariables[0], tmpVariables[1]); + } + private static void parseEncryptedKeyBlob(byte[] scratchPad) { + tmpVariables[0] = KMByteBlob.cast(data[KEY_BLOB]).getStartOff(); + tmpVariables[1] = KMArray.instance((short)5); + KMArray.cast(tmpVariables[1]).add(KMKeymasterApplet.KEY_BLOB_SECRET, KMByteBlob.exp()); + KMArray.cast(tmpVariables[1]).add(KMKeymasterApplet.KEY_BLOB_AUTH_TAG, KMByteBlob.exp()); + KMArray.cast(tmpVariables[1]).add(KMKeymasterApplet.KEY_BLOB_NONCE, KMByteBlob.exp()); + tmpVariables[2] = KMKeyCharacteristics.exp(); + KMArray.cast(tmpVariables[1]).add(KMKeymasterApplet.KEY_BLOB_KEYCHAR, tmpVariables[2]); + KMArray.cast(tmpVariables[1]).add(KMKeymasterApplet.KEY_BLOB_PUB_KEY, KMByteBlob.exp()); + data[KEY_BLOB]=decoder.decodeArray(tmpVariables[1], + KMByteBlob.cast(data[KEY_BLOB]).getBuffer(), + KMByteBlob.cast(data[KEY_BLOB]).getStartOff(), + KMByteBlob.cast(data[KEY_BLOB]).length()); + tmpVariables[0] = KMArray.cast(data[KEY_BLOB]).length(); + if (tmpVariables[0] < 4) { + KMException.throwIt(KMError.INVALID_KEY_BLOB); + } + // Validate Auth Tag + data[AUTH_TAG] = KMArray.cast(data[KEY_BLOB]).get(KEY_BLOB_AUTH_TAG); + if (!KMRepository.validateAuthTag(data[AUTH_TAG])) { + KMException.throwIt(KMError.INVALID_KEY_BLOB); + } + // initialize data + data[NONCE] = KMArray.cast(data[KEY_BLOB]).get(KEY_BLOB_NONCE); + data[SECRET] = KMArray.cast(data[KEY_BLOB]).get(KEY_BLOB_SECRET); + data[KEY_CHARACTERISTICS] = KMArray.cast(data[KEY_BLOB]).get(KEY_BLOB_KEYCHAR); + data[PUB_KEY] = KMType.INVALID_VALUE; + if (tmpVariables[0] == 5) { + data[PUB_KEY] = KMArray.cast(data[KEY_BLOB]).get(KEY_BLOB_PUB_KEY); + } + data[HW_PARAMETERS] = KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).getHardwareEnforced(); + data[SW_PARAMETERS] = KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).getSoftwareEnforced(); + // make root of trust blob + data[ROT] = + KMByteBlob.instance( + repository.verifiedBootKey, (short) 0, (short) repository.verifiedBootKey.length); + data[HIDDEN_PARAMETERS] = + KMKeyParameters.makeHidden(data[APP_ID], data[APP_DATA], data[ROT], scratchPad); + // Decrypt Secret and verify auth tag + decryptSecret(scratchPad); } - private void processImportWrappedKeyCmd(APDU apdu) { + private static void decryptSecret(byte[] scratchPad) { + // derive master key - stored in derivedKey + tmpVariables[0] = deriveKey(scratchPad); + AESKey derivedKey = + cryptoProvider.createAESKey(repository.getHeap(), data[DERIVED_KEY], tmpVariables[0]); + if (derivedKey == null) { + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); + } + boolean verification = + cryptoProvider.aesGCMDecrypt( + derivedKey, + KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length(), + scratchPad, + (short) 0, + KMByteBlob.cast(data[NONCE]).getBuffer(), + KMByteBlob.cast(data[NONCE]).getStartOff(), + KMByteBlob.cast(data[NONCE]).length(), + repository.getHeap(), + data[AUTH_DATA], + data[AUTH_DATA_LENGTH], + KMByteBlob.cast(data[AUTH_TAG]).getBuffer(), + KMByteBlob.cast(data[AUTH_TAG]).getStartOff(), + KMByteBlob.cast(data[AUTH_TAG]).length()); + if (verification != true) { + KMException.throwIt(KMError.INVALID_KEY_BLOB); + } + } + private static void encryptSecret(byte[] scratchPad) { + // make nonce + data[NONCE] = KMByteBlob.instance((short) AES_GCM_NONCE_LENGTH); + data[AUTH_TAG] = KMByteBlob.instance(AES_GCM_AUTH_TAG_LENGTH); + Util.arrayCopy( + KMByteBlob.cast(data[NONCE]).getBuffer(), + KMByteBlob.cast(data[NONCE]).getStartOff(), + scratchPad, + (short) 0, + KMByteBlob.cast(data[NONCE]).length()); + cryptoProvider.newRandomNumber( + KMByteBlob.cast(data[NONCE]).getBuffer(), + KMByteBlob.cast(data[NONCE]).getStartOff(), + KMByteBlob.cast(data[NONCE]).length()); + // derive master key - stored in derivedKey + tmpVariables[0] = deriveKey(scratchPad); + AESKey derivedKey = + cryptoProvider.createAESKey(repository.getHeap(), data[DERIVED_KEY], tmpVariables[0]); + if (derivedKey == null) { + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); + } + tmpVariables[1] = + cryptoProvider.aesGCMEncrypt( + derivedKey, + KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length(), + scratchPad, + (short) 0, + KMByteBlob.cast(data[NONCE]).getBuffer(), + KMByteBlob.cast(data[NONCE]).getStartOff(), + KMByteBlob.cast(data[NONCE]).length(), + repository.getHeap(), + data[AUTH_DATA], + data[AUTH_DATA_LENGTH], + KMByteBlob.cast(data[AUTH_TAG]).getBuffer(), + KMByteBlob.cast(data[AUTH_TAG]).getStartOff(), + KMByteBlob.cast(data[AUTH_TAG]).length()); + if (tmpVariables[1] > 0) { + if (tmpVariables[1] != KMByteBlob.cast(data[SECRET]).length()) { + KMException.throwIt(KMError.INVALID_KEY_BLOB); + } + KMByteBlob.cast(data[SECRET]).setValue(scratchPad, (short) 0, tmpVariables[1]); + } } - private void processImportKeyCmd(APDU apdu) { + private static void makeAuthData(byte[] scratchPad) { + tmpVariables[0] = + addPtrToAAD(KMKeyParameters.cast(data[HW_PARAMETERS]).getVals(), scratchPad, (short) 0); + tmpVariables[0] += + addPtrToAAD( + KMKeyParameters.cast(data[SW_PARAMETERS]).getVals(), scratchPad, tmpVariables[0]); + tmpVariables[0] += + addPtrToAAD( + KMKeyParameters.cast(data[HIDDEN_PARAMETERS]).getVals(), scratchPad, tmpVariables[0]); + // convert scratch pad to KMArray + tmpVariables[1] = KMArray.instance(tmpVariables[0]); + short index = 0; + short objPtr = 0; + while (index < tmpVariables[0]) { + objPtr = Util.getShort(scratchPad, (short) (index * 2)); + KMArray.cast(tmpVariables[1]).add(index, objPtr); + index++; + } + data[AUTH_DATA] = repository.alloc(MAX_AUTH_DATA_SIZE); + short len = encoder.encode(tmpVariables[1], repository.getHeap(), data[AUTH_DATA]); + data[AUTH_DATA_LENGTH] = len; + } + private static short addPtrToAAD(short dataArrPtr, byte[] aadBuf, short offset) { + short index = (short) (offset * 2); + short tagInd = 0; + short tagPtr = 0; + short arrLen = KMArray.cast(dataArrPtr).length(); + while (tagInd < arrLen) { + tagPtr = KMArray.cast(dataArrPtr).get(tagInd); + Util.setShort(aadBuf, index, tagPtr); + index += 2; + tagInd++; + } + return tagInd; } - private void processGenerateKey(APDU apdu) { + private static short deriveKey(byte[] scratchPad) { + tmpVariables[0] = KMKeyParameters.cast(data[HIDDEN_PARAMETERS]).getVals(); + tmpVariables[1] = repository.alloc((short) 256); + // generate derivation material from hidden parameters + tmpVariables[2] = encoder.encode(tmpVariables[0], repository.getHeap(), tmpVariables[1]); + // create derived key i.e. MAC + tmpVariables[3] = + cryptoProvider.aesCCMSign( + repository.getHeap(), + tmpVariables[1], + tmpVariables[2], + repository.getMasterKeySecret(), + scratchPad, + (short) 0); + if (tmpVariables[3] < 0) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + // store the derived secret in data dictionary + data[DERIVED_KEY] = repository.alloc(tmpVariables[3]); + Util.arrayCopyNonAtomic( + scratchPad, (short) 0, repository.getHeap(), data[DERIVED_KEY], tmpVariables[3]); + return tmpVariables[3]; + } + private static void sendError(APDU apdu, short err) { + bufferLength = encoder.encodeError(err, buffer, bufferStartOffset, (short) 5); + sendOutgoing(apdu); } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMOperationState.java b/Applet/Applet/src/com/android/javacard/keymaster/KMOperationState.java index d1671328..47cdabf8 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMOperationState.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMOperationState.java @@ -44,11 +44,4 @@ public KMInteger getOperationHandle() { public void setOperationHandle(KMInteger operationHandle) { this.operationHandle = operationHandle; } -/* - public void release(KMContext context) { - // TODO release handle - context.getRepository().releaseOperationState(this); - } - - */ } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMRepository.java b/Applet/Applet/src/com/android/javacard/keymaster/KMRepository.java index 3492d4b5..8e0f01c3 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMRepository.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMRepository.java @@ -20,43 +20,62 @@ import javacard.framework.ISOException; import javacard.framework.JCSystem; import javacard.framework.Util; -import javacard.security.AESKey; -import javacard.security.KeyBuilder; - -// TODO cleanup, move most of the buffers to transient memory with "clear on deselect". Only -// exception may be OperationState - TBD. The initialize and reset functions will be refactored -// to handle onInstall and onSelect. public class KMRepository { public static final short HEAP_SIZE = 0x1000; + public static final short MAX_BLOB_STORAGE = 32; + public static final short AES_GCM_AUTH_TAG_LENGTH = 12; + // Boot params constants + public static final byte BOOT_KEY_MAX_SIZE = 32; + public static final byte BOOT_HASH_MAX_SIZE = 32; + // Repository attributes private static KMRepository repository; - private AESKey masterKey; + private byte[] masterKey; private byte[] heap; private short heapIndex; + // boot parameters + public Object[] authTagRepo; + public short keyBlobCount; + public byte[] osVersion; + public byte[] osPatch; + public byte[] verifiedBootKey; + public short actualBootKeyLength; + public byte[] verifiedBootHash; + public short actualBootHashLength; + public boolean verifiedBootFlag; + public boolean selfSignedBootFlag; + public boolean deviceLockedFlag ; public static KMRepository instance() { - if (repository == null) { - repository = new KMRepository(); - } return repository; } - public KMRepository(){ + public KMRepository(byte[] masterKey) { heap = JCSystem.makeTransientByteArray(HEAP_SIZE, JCSystem.CLEAR_ON_RESET); - // Initialize masterkey - AES 256 bit key. - if (masterKey == null) { - masterKey = - (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_256, false); + this.masterKey = new byte[(short)masterKey.length]; + // Initialize masterkey + Util.arrayCopy(masterKey, (short)0, this.masterKey, (short)0, (short)masterKey.length); + authTagRepo = new Object[MAX_BLOB_STORAGE]; + short index = 0; + while (index < MAX_BLOB_STORAGE) { + authTagRepo[index] = new byte[AES_GCM_AUTH_TAG_LENGTH]; + index++; } + osVersion = new byte[4]; + osPatch = new byte[4]; + verifiedBootKey = new byte[BOOT_KEY_MAX_SIZE]; + verifiedBootHash = new byte[BOOT_HASH_MAX_SIZE]; + repository = this; } public void onUninstall() { - masterKey = null; + //TODO change this + Util.arrayFillNonAtomic(masterKey,(short)0,(short)masterKey.length,(byte) 0); } public void onProcess() {} - public void clean(){ + public void clean() { Util.arrayFillNonAtomic(heap, (short) 0, heapIndex, (byte) 0); heapIndex = 0; } @@ -65,7 +84,7 @@ public void onDeselect() {} public void onSelect() {} - public AESKey getMasterKey() { + public byte[] getMasterKeySecret() { return masterKey; } @@ -77,7 +96,93 @@ public short alloc(short length) { return (short) (heapIndex - length); } - public byte[] getHeap(){ + public byte[] getHeap() { return heap; } + + public static void persistAuthTag(short authTag) { + final byte[] compare = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; // length equal to AES_GCM_AUTH_TAG_LENGTH. + short index = 0; + byte ret = 0; + while (index < MAX_BLOB_STORAGE) { + ret = + Util.arrayCompare( + (byte[]) (repository.authTagRepo[index]), + (short) 0, + compare, + (short) 0, + AES_GCM_AUTH_TAG_LENGTH); + if (ret == 0) { + break; + } + index++; + } + // This should never happen + if (index >= repository.MAX_BLOB_STORAGE){ + ISOException.throwIt(ISO7816.SW_BYTES_REMAINING_00); + } + JCSystem.beginTransaction(); + Util.arrayCopy( + KMByteBlob.cast(authTag).getBuffer(), + KMByteBlob.cast(authTag).getStartOff(), + (byte[]) (repository.authTagRepo[index]), + (short) 0, + AES_GCM_AUTH_TAG_LENGTH); + repository.keyBlobCount++; + JCSystem.commitTransaction(); + } + + public static void removeAuthTag(short authTag) { + final byte[] zeroTag = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; // length equal to AES_GCM_AUTH_TAG_LENGTH. + short index = 0; + byte ret = 0; + while (index < repository.MAX_BLOB_STORAGE) { + ret = + Util.arrayCompare( + (byte[]) (repository.authTagRepo[index]), + (short) 0, + KMByteBlob.cast(authTag).getBuffer(), + KMByteBlob.cast(authTag).getStartOff(), + AES_GCM_AUTH_TAG_LENGTH); + if (ret == 0) { + break; + } + index++; + } + if (index >= MAX_BLOB_STORAGE) { + ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED); + } + JCSystem.beginTransaction(); + Util.arrayCopy( + zeroTag, (short) 0, (byte[]) (repository.authTagRepo[index]), (short) 0, AES_GCM_AUTH_TAG_LENGTH); + repository.keyBlobCount--; + JCSystem.commitTransaction(); + } + + public static boolean validateAuthTag(short authTag) { + short index = 0; + byte ret = 0; + while (index < MAX_BLOB_STORAGE) { + ret = + Util.arrayCompare( + (byte[]) (repository.authTagRepo[index]), + (short) 0, + KMByteBlob.cast(authTag).getBuffer(), + KMByteBlob.cast(authTag).getStartOff(), + AES_GCM_AUTH_TAG_LENGTH); + if (ret == 0) { + break; + } + index++; + } + if (index >= MAX_BLOB_STORAGE) { + return false; + } + return true; + } + } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMRng.java b/Applet/Applet/src/com/android/javacard/keymaster/KMRng.java new file mode 100644 index 00000000..a2e79901 --- /dev/null +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMRng.java @@ -0,0 +1,104 @@ +package com.android.javacard.keymaster; + +import javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.JCSystem; +import javacard.framework.Util; +import javacard.security.AESKey; +import javacard.security.CryptoException; +import javacard.security.KeyBuilder; +import javacard.security.RandomData; +import javacardx.crypto.Cipher; + +public class KMRng { + public static final short ENTROPY_POOL_SIZE = 16; // simulator does not support 256 bit aes keys + public static final byte[] aesICV = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + private static byte[] counter; + private static AESKey aesKey; + private static Cipher aesCbc; + private static byte[] entropyPool; + public static void init() { + entropyPool = JCSystem.makeTransientByteArray(ENTROPY_POOL_SIZE, JCSystem.CLEAR_ON_RESET); + counter = JCSystem.makeTransientByteArray((short)8, JCSystem.CLEAR_ON_RESET); + KMRng.initEntropyPool(entropyPool); + try { + aesCbc = Cipher.getInstance(Cipher.ALG_AES_BLOCK_128_CBC_NOPAD, false); + } catch (CryptoException exp) { + ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED); + } + aesKey = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_128, false); + } + + public static void initEntropyPool(byte[] pool) { + byte index = 0; + RandomData trng; + while (index < counter.length) { + counter[index++] = 0; + } + try { + trng = RandomData.getInstance(RandomData.ALG_TRNG); + trng.nextBytes(pool, (short) 0, (short) pool.length); + } catch (CryptoException exp) { + if (exp.getReason() == CryptoException.NO_SUCH_ALGORITHM) { + //TODO change this when possible + // simulator does not support TRNG algorithm. So, PRNG algorithm (deprecated) is used. + trng = RandomData.getInstance(RandomData.ALG_PSEUDO_RANDOM); + trng.nextBytes(pool, (short) 0, (short) pool.length); + } else { + // TODO change this to proper error code + ISOException.throwIt(ISO7816.SW_UNKNOWN); + } + } + + } + + // Generate a secure random number from existing entropy pool. This uses aes ecb algorithm with + // 8 byte counter and 16 byte block size. + public static void newRandomNumber(byte[] num, short startOff, short length) { + KMRepository repository = KMRepository.instance(); + byte[] bufPtr = repository.getHeap(); + short countBufInd = repository.alloc(KMKeymasterApplet.AES_BLOCK_SIZE); + short randBufInd = repository.alloc(KMKeymasterApplet.AES_BLOCK_SIZE); + short len = KMKeymasterApplet.AES_BLOCK_SIZE; + aesKey.setKey(entropyPool, (short) 0); + aesCbc.init(aesKey, Cipher.MODE_ENCRYPT, aesICV, (short)0, (short)16); + while (length > 0) { + if (length < len ) len = length; + // increment counter by one + incrementCounter(); + // copy the 8 byte counter into the 16 byte counter buffer. + Util.arrayCopy(counter, (short) 0, bufPtr, countBufInd, (short) counter.length); + // encrypt the counter buffer with existing entropy which forms the aes key. + aesCbc.doFinal(bufPtr, countBufInd, KMKeymasterApplet.AES_BLOCK_SIZE, bufPtr, randBufInd); + // copy the encrypted counter block to buffer passed in the argument + Util.arrayCopy(bufPtr, randBufInd, num, startOff, len); + length = (short) (length - len); + startOff = (short)(startOff + len); + } + } + + // increment 8 byte counter by one + private static void incrementCounter() { + // start with least significant byte + short index = (short) (counter.length - 1); + while (index >= 0) { + // if the msb of current byte is set then it will be negative + if (counter[index] < 0) { + // then increment the counter + counter[index]++; + // is the msb still set? i.e. no carry over + if (counter[index] < 0) break; // then break + else index--; // else go to the higher order byte + } else { + // if msb is not set then increment the counter + counter[index]++; + break; + } + } + } + + public static byte[] getEntropyPool() { + return entropyPool; + } + +} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMSimulator.java b/Applet/Applet/src/com/android/javacard/keymaster/KMSimulator.java new file mode 100644 index 00000000..0d0c1394 --- /dev/null +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMSimulator.java @@ -0,0 +1,424 @@ +/* + * Copyright(C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.javacard.keymaster; + +import javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.JCSystem; +import javacard.framework.Util; +import javacard.security.AESKey; +import javacard.security.CryptoException; +import javacard.security.DESKey; +import javacard.security.ECPrivateKey; +import javacard.security.ECPublicKey; +import javacard.security.HMACKey; +import javacard.security.KeyBuilder; +import javacard.security.KeyPair; +import javacard.security.RSAPrivateKey; +import javacard.security.RandomData; +import javacard.security.Signature; +import javacardx.crypto.AEADCipher; +import javacardx.crypto.Cipher; + +/** + * Simulator only supports 512 bit RSA key pair, 128 AES Key, 128 bit 3Des key, less then 256 bit EC + * Key, and upto 512 bit HMAC key. Also simulator does not support TRNG, so this implementation just + * creates its own RNG using PRNG. + */ +public class KMSimulator implements KMCryptoProvider { + public static final short AES_GCM_TAG_LENGTH = 12; + public static final short AES_GCM_NONCE_LENGTH = 12; + public static final short MAX_RND_NUM_SIZE = 64; + public static final short ENTROPY_POOL_SIZE = 16; // simulator does not support 256 bit aes keys + public static final byte[] aesICV = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + public static boolean jcardSim = false; + + private static KeyPair rsa512KeyPair; + private static KeyPair ec192KeyPair; + private static AESKey aes128Key; + private static DESKey triDesKey; + private static HMACKey hmac128Key; + private static HMACKey hmac256Key; + private static AEADCipher aesGcmCipher; + private static AESKey derivedKey; + private static Signature kdf; + + private static byte[] rngCounter; + private static AESKey aesRngKey; + private static Cipher aesRngCipher; + private static byte[] entropyPool; + private static byte[] rndNum; + + // Implements Oracle Simulator based restricted crypto provider + public KMSimulator() { + // Various Keys + rsa512KeyPair = new KeyPair(KeyPair.ALG_RSA, KeyBuilder.LENGTH_RSA_512); + ec192KeyPair = new KeyPair(KeyPair.ALG_EC_FP, KeyBuilder.LENGTH_EC_FP_192); + aes128Key = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_128, false); + triDesKey = + (DESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_DES, KeyBuilder.LENGTH_DES3_2KEY, false); + hmac128Key = (HMACKey) KeyBuilder.buildKey(KeyBuilder.TYPE_HMAC, (short) 128, false); + hmac256Key = (HMACKey) KeyBuilder.buildKey(KeyBuilder.TYPE_HMAC, (short) 256, false); + derivedKey = + (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_128, false); + kdf = Signature.getInstance(Signature.ALG_AES_CMAC_128, false); + + // RNG + rndNum = JCSystem.makeTransientByteArray(MAX_RND_NUM_SIZE, JCSystem.CLEAR_ON_RESET); + entropyPool = JCSystem.makeTransientByteArray(ENTROPY_POOL_SIZE, JCSystem.CLEAR_ON_RESET); + rngCounter = JCSystem.makeTransientByteArray((short) 8, JCSystem.CLEAR_ON_RESET); + initEntropyPool(entropyPool); + try { + aesRngCipher = Cipher.getInstance(Cipher.ALG_AES_BLOCK_128_CBC_NOPAD, false); + } catch (CryptoException exp) { + ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED); + } + aesRngKey = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_128, false); + } + + @Override + public KeyPair createRsaKeyPair() { + // By default 65537 is used as public exponent no need to set the public exponent. Now generate + // 512 bit RSA keypair for the given exponent + rsa512KeyPair.genKeyPair(); + return rsa512KeyPair; + } + + @Override + public KeyPair createECKeyPair() { + // Simulator does not support 256 bit keys. + // Generate default 192 bit key pair supported by simulator. + ec192KeyPair.genKeyPair(); + return ec192KeyPair; + } + + @Override + public AESKey createAESKey(short keysize) { + // keysize is ignored as simulator only supports 128 bit aes key + newRandomNumber(rndNum, (short) 0, (short) 16); + aes128Key.setKey(rndNum, (short) 0); + return aes128Key; + } + + @Override + public AESKey createAESKey(byte[] buf, short startOff, short length) { + if (length > 16) length = 16; + else if(length < 16) return null; + aes128Key.setKey(buf, startOff); + return aes128Key; + } + + @Override + public DESKey createTDESKey() { + // only 128 bit keys are supported + newRandomNumber(rndNum, (short) 0, (short) 21); + triDesKey.setKey(rndNum, (short) 0); + return triDesKey; + } + + @Override + public HMACKey createHMACKey(short keysize) { + // simulator only supports HMAC keys for SHA1 and SHA256 with block size 64. + // So only 128 and 256 bit HMAC keys are supported. + HMACKey key; + if (keysize == 128) { + key = hmac128Key; + keysize = 16; + } else if (keysize == 256) { + key = hmac256Key; + keysize = 32; + } else { + key = hmac128Key; // by default the simulator will return 128 bit keys for SHA1 + keysize = 16; + } + newRandomNumber(rndNum, (short) 0, keysize); + key.setKey(rndNum, (short) 0, keysize); + return key; + } + + @Override + public void addRngEntropy(byte[] num, short offset, short length) { + // Maximum length can be 256 bytes. But currently we support max 32 bytes seed. + // Get existing entropy pool. + if (length > 32) length = 32; + // Create new temporary pool. + // Populate the new pool with the entropy which is derived from current entropy pool. + newRandomNumber(rndNum, (short) 0, (short) entropyPool.length); + // Copy the entropy to the current pool - updates the entropy pool. + Util.arrayCopy(rndNum, (short) 0, entropyPool, (short) 0, (short) entropyPool.length); + short index = 0; + short randIndex = 0; + // XOR the seed received from the master in the entropy pool - 16 bytes (entPool.length). + // at a time. + while (index < length) { + entropyPool[randIndex] = (byte) (entropyPool[randIndex] ^ num[(short) (offset + index)]); + randIndex++; + index++; + if (randIndex >= entropyPool.length) { + randIndex = 0; + } + } + } + + @Override + public short aesGCMEncrypt( + AESKey key, + byte[] secret, + short secretStart, + short secretLen, + byte[] encSecret, + short encSecretStart, + byte[] nonce, + short nonceStart, + short nonceLen, + byte[] authData, + short authDataStart, + short authDataLen, + byte[] authTag, + short authTagStart, + short authTagLen) { + if (jcardSim) return -1; + if(authTagLen != AES_GCM_TAG_LENGTH){ + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + if(nonceLen != AES_GCM_NONCE_LENGTH){ + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + if (aesGcmCipher == null) { + aesGcmCipher = (AEADCipher) Cipher.getInstance(AEADCipher.ALG_AES_GCM, false); + } + byte[] aad = JCSystem.makeTransientByteArray(authDataLen, JCSystem.CLEAR_ON_RESET); + Util.arrayCopyNonAtomic(authData, authDataStart, aad, (short) 0, authDataLen); + try { + aesGcmCipher.init(key, Cipher.MODE_ENCRYPT, nonce, nonceStart, nonceLen); + } catch (CryptoException exp) { + KMException.throwIt(exp.getReason()); + } + // add the auth data + try { + aesGcmCipher.updateAAD(aad, (short) 0, authDataLen); + } catch (CryptoException exp) { + KMException.throwIt(exp.getReason()); + } + // encrypt the secret + short ciphLen = aesGcmCipher.doFinal(secret, secretStart, secretLen, encSecret, encSecretStart); + // The tag buffer must be exact size otherwise simulator returns 0 tag. + byte[] tag = JCSystem.makeTransientByteArray(AES_GCM_TAG_LENGTH, JCSystem.CLEAR_ON_RESET); + aesGcmCipher.retrieveTag(tag, (short) 0, AES_GCM_TAG_LENGTH); + Util.arrayCopyNonAtomic(tag,(short)0, authTag, authTagStart,AES_GCM_TAG_LENGTH); + return ciphLen; +/* aesGcmCipher.init(key, Cipher.MODE_DECRYPT, nonce, nonceStart, nonceLen); + try { + aesGcmCipher.updateAAD(aad, (short) 0, authDataLen); + } catch (CryptoException exp) { + KMException.throwIt(exp.getReason()); + } + byte[] plain = JCSystem.makeTransientByteArray(secretLen, JCSystem.CLEAR_ON_RESET); + // encrypt the secret + ciphLen = aesGcmCipher.doFinal(encSecret, encSecretStart, ciphLen, plain, (short) 0); + boolean ver = aesGcmCipher.verifyTag(tag, (short) 0, (short) 12, (short) 12); + if (ver == true) { + KMException.throwIt((short) 10); + } else { + KMException.throwIt((short) 20); + } + return 0; + */ + } + + public boolean aesGCMDecrypt( + AESKey key, + byte[] encSecret, + short encSecretStart, + short encSecretLen, + byte[] secret, + short secretStart, + byte[] nonce, + short nonceStart, + short nonceLen, + byte[] authData, + short authDataStart, + short authDataLen, + byte[] authTag, + short authTagStart, + short authTagLen) { + if(jcardSim) return true; + if (aesGcmCipher == null) { + aesGcmCipher = (AEADCipher) Cipher.getInstance(AEADCipher.ALG_AES_GCM, false); + } + // allocate aad buffer of exact size - otherwise simulator throws exception + byte[] aad = JCSystem.makeTransientByteArray(authDataLen, JCSystem.CLEAR_ON_RESET); + Util.arrayCopyNonAtomic(authData, authDataStart, aad, (short) 0, authDataLen); + // allocate tag of exact size. + byte[] tag = JCSystem.makeTransientByteArray(AES_GCM_TAG_LENGTH, JCSystem.CLEAR_ON_RESET); + Util.arrayCopyNonAtomic(authTag, authTagStart, tag, (short) 0, authTagLen); + boolean verification = false; + try { + aesGcmCipher.init(key, Cipher.MODE_DECRYPT, nonce, nonceStart, nonceLen); + aesGcmCipher.updateAAD(aad, (short) 0, authDataLen); + //byte[] plain = JCSystem.makeTransientByteArray(encSecretLen, JCSystem.CLEAR_ON_RESET); + // encrypt the secret + aesGcmCipher.doFinal(encSecret, encSecretStart, encSecretLen, secret, secretStart); + verification = aesGcmCipher.verifyTag(tag, (short) 0, (short) 12, (short) 12); + } catch (CryptoException exp) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + return verification; + } + + @Override + public byte[] getTrueRandomNumber(short i) { + // ignore the size as simulator only supports 128 bit entropy + return entropyPool; + } + + @Override + public short aesCCMSign( + byte[] bufIn, + short bufInStart, + short buffInLength, + byte[] masterKeySecret, + byte[] bufOut, + short bufStart) { + if (masterKeySecret.length > 16) { + return -1; + } + aes128Key.setKey(masterKeySecret, (short) 0); + kdf.init(aes128Key, Signature.MODE_SIGN); + return kdf.sign(bufIn, bufInStart, buffInLength, bufOut, bufStart); + } + + @Override + public ECPrivateKey createEcPrivateKey(byte[] pubBuffer, short pubOff, short pubLength, + byte[] privBuffer, short privOff, short privLength) { + // Simulator does not support NamedParameterSpec or 256 bit keys + ECPrivateKey privKey = (ECPrivateKey) ec192KeyPair.getPrivate(); + if(privLength > 24){ + privLength = 24;// simulator does not support more then 24 bytes - 192 bit key. + }else if(privLength <= 20){ + return null; + } + privKey.setS(privBuffer,privOff, privLength); + return privKey; + } + + @Override + public HMACKey createHMACKey(byte[] secretBuffer, short secretOff, short secretLength) { + // simulator only supports HMAC keys for SHA1 and SHA256 with block size 64. + // So only 128 and 256 bit HMAC keys are supported. + HMACKey key; + if (secretLength == 16) { + key = hmac128Key; + key.setKey(secretBuffer, secretOff, secretLength); + } else if (secretLength == 32) { + key = hmac256Key; + key.setKey(secretBuffer, secretOff, secretLength); + } else { + key = hmac128Key; // by default the simulator will return 128 bit keys for SHA1 + key.setKey(secretBuffer, secretOff, (short)16); + } + return key; + } + @Override + public DESKey createTDESKey(byte[] secretBuffer, short secretOff, short secretLength) { + // only 128 bit keys are supported + if(secretLength < 128) return null; + triDesKey.setKey(secretBuffer, secretOff); + return triDesKey; + } + + @Override + public RSAPrivateKey createRsaPrivateKey(byte[] modBuffer, short modOff, short modLength, byte[] privBuffer, short privOff, short privLength) { + RSAPrivateKey privKey = (RSAPrivateKey) rsa512KeyPair.getPrivate(); + if(privLength > 64) privLength = 64; + else if(privLength < 64)return null; + if(modLength > 64) modLength = 64; + else if( modLength < 64) return null; + privKey.setExponent(privBuffer, privOff, privLength); + privKey.setModulus(modBuffer, modOff, modLength); + return privKey; + } + + private void initEntropyPool(byte[] pool) { + byte index = 0; + RandomData trng; + while (index < rngCounter.length) { + rngCounter[index++] = 0; + } + try { + trng = RandomData.getInstance(RandomData.ALG_TRNG); + trng.nextBytes(pool, (short) 0, (short) pool.length); + } catch (CryptoException exp) { + if (exp.getReason() == CryptoException.NO_SUCH_ALGORITHM) { + // TODO change this when possible + // simulator does not support TRNG algorithm. So, PRNG algorithm (deprecated) is used. + trng = RandomData.getInstance(RandomData.ALG_PSEUDO_RANDOM); + trng.nextBytes(pool, (short) 0, (short) pool.length); + } else { + // TODO change this to proper error code + ISOException.throwIt(ISO7816.SW_UNKNOWN); + } + } + } + + // Generate a secure random number from existing entropy pool. This uses aes ecb algorithm with + // 8 byte rngCounter and 16 byte block size. + @Override + public void newRandomNumber(byte[] num, short startOff, short length) { + KMRepository repository = KMRepository.instance(); + byte[] bufPtr = repository.getHeap(); + short countBufInd = repository.alloc(KMKeymasterApplet.AES_BLOCK_SIZE); + short randBufInd = repository.alloc(KMKeymasterApplet.AES_BLOCK_SIZE); + short len = KMKeymasterApplet.AES_BLOCK_SIZE; + aesRngKey.setKey(entropyPool, (short) 0); + aesRngCipher.init(aesRngKey, Cipher.MODE_ENCRYPT, aesICV, (short) 0, (short) 16); + while (length > 0) { + if (length < len) len = length; + // increment rngCounter by one + incrementCounter(); + // copy the 8 byte rngCounter into the 16 byte rngCounter buffer. + Util.arrayCopy(rngCounter, (short) 0, bufPtr, countBufInd, (short) rngCounter.length); + // encrypt the rngCounter buffer with existing entropy which forms the aes key. + aesRngCipher.doFinal( + bufPtr, countBufInd, KMKeymasterApplet.AES_BLOCK_SIZE, bufPtr, randBufInd); + // copy the encrypted rngCounter block to buffer passed in the argument + Util.arrayCopy(bufPtr, randBufInd, num, startOff, len); + length = (short) (length - len); + startOff = (short) (startOff + len); + } + } + + // increment 8 byte rngCounter by one + private void incrementCounter() { + // start with least significant byte + short index = (short) (rngCounter.length - 1); + while (index >= 0) { + // if the msb of current byte is set then it will be negative + if (rngCounter[index] < 0) { + // then increment the rngCounter + rngCounter[index]++; + // is the msb still set? i.e. no carry over + if (rngCounter[index] < 0) break; // then break + else index--; // else go to the higher order byte + } else { + // if msb is not set then increment the rngCounter + rngCounter[index]++; + break; + } + } + } +} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMType.java b/Applet/Applet/src/com/android/javacard/keymaster/KMType.java index 2dc74dfd..1e023647 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMType.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMType.java @@ -76,6 +76,7 @@ public abstract class KMType { public static final byte USER_AUTH_NONE = 0x00; public static final byte PASSWORD = 0x01; public static final byte FINGERPRINT = 0x02; + // have to be power of 2 public static final byte ANY = (byte) 0xFF; // Origin Enum Tag key and values. @@ -108,6 +109,22 @@ public abstract class KMType { public static final byte PKCS8 = 0x01; public static final byte RAW = 0x03; + // Verified Boot State + public static final short VERIFIED_BOOT_STATE = (short) 0xF003; + public static final byte SELF_SIGNED_BOOT = 0x01; + public static final byte VERIFIED_BOOT = 0x02; + + // Verified Boot Key + public static final short VERIFIED_BOOT_KEY = (short) 0xF004; + + // Verified Boot Hash + public static final short VERIFIED_BOOT_HASH = (short) 0xF005; + + // Device Locked + public static final short DEVICE_LOCKED = (short) 0xF006; + public static final byte DEVICE_LOCKED_TRUE = 0x01; + public static final byte DEVICE_LOCKED_FALSE = 0x00; + // Enum Array Tag // Purpose public static final short PURPOSE = 0x0002; @@ -123,6 +140,7 @@ public abstract class KMType { public static final byte ECB = 0x01; public static final byte CBC = 0x02; public static final byte CTR = 0x04; + public static final byte GCM = 0x20; // Digest public static final short DIGEST = 0x0005; diff --git a/Applet/Applet/test/com/android/javacard/test/KMFrameworkTest.java b/Applet/Applet/test/com/android/javacard/test/KMFrameworkTest.java index c34ac845..5f43ebfa 100644 --- a/Applet/Applet/test/com/android/javacard/test/KMFrameworkTest.java +++ b/Applet/Applet/test/com/android/javacard/test/KMFrameworkTest.java @@ -18,17 +18,34 @@ import com.android.javacard.keymaster.KMArray; import com.android.javacard.keymaster.KMByteBlob; +import com.android.javacard.keymaster.KMByteTag; import com.android.javacard.keymaster.KMDecoder; import com.android.javacard.keymaster.KMEncoder; import com.android.javacard.keymaster.KMEnum; +import com.android.javacard.keymaster.KMEnumArrayTag; import com.android.javacard.keymaster.KMEnumTag; +import com.android.javacard.keymaster.KMError; +import com.android.javacard.keymaster.KMInteger; +import com.android.javacard.keymaster.KMIntegerArrayTag; +import com.android.javacard.keymaster.KMIntegerTag; +import com.android.javacard.keymaster.KMKeyCharacteristics; import com.android.javacard.keymaster.KMKeyParameters; import com.android.javacard.keymaster.KMKeymasterApplet; +import com.android.javacard.keymaster.KMSimulator; import com.android.javacard.keymaster.KMType; import com.licel.jcardsim.smartcardio.CardSimulator; import com.licel.jcardsim.utils.AIDUtil; import javacard.framework.AID; import javacard.framework.Util; +import javacard.security.AESKey; +import javacard.security.DESKey; +import javacard.security.ECPrivateKey; +import javacard.security.ECPublicKey; +import javacard.security.HMACKey; +import javacard.security.Key; +import javacard.security.KeyBuilder; +import javacard.security.KeyPair; +import javacard.security.RSAPrivateKey; import javacard.security.RandomData; import javax.smartcardio.CommandAPDU; import javax.smartcardio.ResponseAPDU; @@ -37,10 +54,16 @@ import org.junit.experimental.theories.suppliers.TestedOn; public class KMFrameworkTest { + private short status; + private short keyCharacteristics; + private short keyBlob; + private KMSimulator sim; @Test public void test_Lifecycle_Success() { // Create simulator + KMSimulator.jcardSim = true; + sim = new KMSimulator(); CardSimulator simulator = new CardSimulator(); // Install applet @@ -50,24 +73,320 @@ public void test_Lifecycle_Success() { // Select applet simulator.selectApplet(appletAID1); testProvisionCmd(simulator); + testSetBootParams(simulator); testGetHwInfoCmd(simulator); testAddRngEntropyCmd(simulator); - + testGenerateRsaKey(simulator); + testImportRsaKey(simulator); + testGetKeyCharacteristics(simulator); + testGenerateAesKey(simulator); + testImportAesKey(simulator); + testGetKeyCharacteristics(simulator); + testGenerateEcKey(simulator); + testImportEcKey(simulator); + testGetKeyCharacteristics(simulator); + testGenerate3DesKey(simulator); + testImportDesKey(simulator); + testGetKeyCharacteristics(simulator); + testGenerateHmacKey(simulator); + testImportHmacKey(simulator); + testGetKeyCharacteristics(simulator); // Delete i.e. uninstall applet simulator.deleteApplet(appletAID1); } + private void testGetKeyCharacteristics(CardSimulator simulator) { + byte[] buf = new byte[512]; + buf[0] = (byte)0x80; + buf[1] = (byte)0x1D; + buf[2] = (byte)0x40; + buf[3] = (byte)0x00; + buf[4] = 0; + short keyChar = keyCharacteristics; + short len = KMKeyCharacteristics.cast(keyChar).length(); + // test provision command + short cmd = makeGetKeyCharKeyCmd(); + KMEncoder enc = new KMEncoder(); + short actualLen = enc.encode(cmd, buf, (short) 7); + Util.setShort(buf, (short)5, actualLen); + byte[] apdu = new byte[7+actualLen]; + Util.arrayCopyNonAtomic(buf,(short)0,apdu,(short)0,(short)(7+actualLen)); + //CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x10, 0x40, 0x00, buf, 0, actualLen); + CommandAPDU commandAPDU = new CommandAPDU(apdu); + //print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(commandAPDU); + extractKeyChar(response); + short len2 = KMKeyCharacteristics.cast(keyCharacteristics).length(); + short hwList = KMKeyCharacteristics.cast(keyCharacteristics).getHardwareEnforced(); + short len3 = KMKeyParameters.cast(hwList).length(); + short swList = KMKeyCharacteristics.cast(keyCharacteristics).getSoftwareEnforced(); + short len4 = KMKeyParameters.cast(swList).length(); + Assert.assertEquals(0x9000, response.getSW()); + } + public void testProvisionCmd(CardSimulator simulator){ byte[] buf = new byte[512]; // test provision command short cmd = makeProvisionCmd(); KMEncoder enc = new KMEncoder(); - short actualLen = enc.encode(cmd, buf, (short) 0, (short)buf.length); + short actualLen = enc.encode(cmd, buf, (short) 0); CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x23, 0x40, 0x00, buf, 0, actualLen); //print(commandAPDU.getBytes());; ResponseAPDU response = simulator.transmitCommand(commandAPDU); Assert.assertEquals(0x9000, response.getSW()); } + public void testSetBootParams(CardSimulator simulator){ + byte[] buf = new byte[512]; + // test provision command + short cmd = makeSetBootParamsCmd(); + KMEncoder enc = new KMEncoder(); + short actualLen = enc.encode(cmd, buf, (short) 0); + CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x24, 0x40, 0x00, buf, 0, actualLen); + //print(commandAPDU.getBytes());; + ResponseAPDU response = simulator.transmitCommand(commandAPDU); + Assert.assertEquals(0x9000, response.getSW()); + } + + public void testGenerateRsaKey(CardSimulator simulator){ + byte[] buf = new byte[512]; + buf[0] = (byte)0x80; + buf[1] = (byte)0x10; + buf[2] = (byte)0x40; + buf[3] = (byte)0x00; + buf[4] = 0; + // test provision command + short cmd = makeGenerateKeyCmd(KMType.RSA, (short)2048); + KMEncoder enc = new KMEncoder(); + short actualLen = enc.encode(cmd, buf, (short) 7); + Util.setShort(buf, (short)5, actualLen); + byte[] apdu = new byte[7+actualLen]; + Util.arrayCopyNonAtomic(buf,(short)0,apdu,(short)0,(short)(7+actualLen)); + //CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x10, 0x40, 0x00, buf, 0, actualLen); + CommandAPDU commandAPDU = new CommandAPDU(apdu); + //print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(commandAPDU); + extractKeyCharAndBlob(response); + Assert.assertEquals(0x9000, response.getSW()); + } + + public void testImportRsaKey(CardSimulator simulator){ + byte[] buf = new byte[512]; + buf[0] = (byte)0x80; + buf[1] = (byte)0x11; + buf[2] = (byte)0x40; + buf[3] = (byte)0x00; + buf[4] = 0; + short cmd = makeImportKeyRsaCmd(); + KMEncoder enc = new KMEncoder(); + short actualLen = enc.encode(cmd, buf, (short) 7); + Util.setShort(buf, (short)5, actualLen); + byte[] apdu = new byte[7+actualLen]; + Util.arrayCopyNonAtomic(buf,(short)0,apdu,(short)0,(short)(7+actualLen)); + //CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x10, 0x40, 0x00, buf, 0, actualLen); + CommandAPDU commandAPDU = new CommandAPDU(apdu); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(commandAPDU); + extractKeyCharAndBlob(response); + Assert.assertEquals(0x9000, response.getSW()); + } + + public void testImportEcKey(CardSimulator simulator){ + byte[] buf = new byte[512]; + buf[0] = (byte)0x80; + buf[1] = (byte)0x11; + buf[2] = (byte)0x40; + buf[3] = (byte)0x00; + buf[4] = 0; + short cmd = makeImportKeyEcCmd(); + KMEncoder enc = new KMEncoder(); + short actualLen = enc.encode(cmd, buf, (short) 7); + Util.setShort(buf, (short)5, actualLen); + byte[] apdu = new byte[7+actualLen]; + Util.arrayCopyNonAtomic(buf,(short)0,apdu,(short)0,(short)(7+actualLen)); + //CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x10, 0x40, 0x00, buf, 0, actualLen); + CommandAPDU commandAPDU = new CommandAPDU(apdu); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(commandAPDU); + extractKeyCharAndBlob(response); + Assert.assertEquals(0x9000, response.getSW()); + } + + private void extractKeyCharAndBlob(ResponseAPDU response) { + short ret = KMArray.instance((short) 3); + KMArray.cast(ret).add((short) 0, KMInteger.exp()); + KMArray.cast(ret).add((short) 1, KMByteBlob.exp()); + short inst = KMKeyCharacteristics.exp(); + KMArray.cast(ret).add((short) 2, inst); + KMDecoder dec = new KMDecoder(); + byte[] respBuf = response.getBytes(); + short len = (short) respBuf.length; + ret = dec.decode(ret, respBuf, (short) 0, len); + status = KMArray.cast(ret).get((short)0); + keyBlob = KMArray.cast(ret).get((short)1); + keyCharacteristics = KMArray.cast(ret).get((short)2); + } + + private void extractKeyChar(ResponseAPDU response) { + short ret = KMArray.instance((short) 2); + KMArray.cast(ret).add((short) 0, KMInteger.exp()); + short inst = KMKeyCharacteristics.exp(); + KMArray.cast(ret).add((short) 1, inst); + KMDecoder dec = new KMDecoder(); + byte[] respBuf = response.getBytes(); + short len = (short) respBuf.length; + ret = dec.decode(ret, respBuf, (short) 0, len); + status = KMArray.cast(ret).get((short)0); + keyCharacteristics = KMArray.cast(ret).get((short)1); + } + + public void testGenerateAesKey(CardSimulator simulator){ + byte[] buf = new byte[512]; + buf[0] = (byte)0x80; + buf[1] = (byte)0x10; + buf[2] = (byte)0x40; + buf[3] = (byte)0x00; + buf[4] = 0; + // test provision command + short cmd = makeGenerateKeyCmd(KMType.AES, (short)256); + KMEncoder enc = new KMEncoder(); + short actualLen = enc.encode(cmd, buf, (short) 7); + Util.setShort(buf, (short)5, actualLen); + byte[] apdu = new byte[7+actualLen]; + Util.arrayCopyNonAtomic(buf,(short)0,apdu,(short)0,(short)(7+actualLen)); + //CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x10, 0x40, 0x00, buf, 0, actualLen); + CommandAPDU commandAPDU = new CommandAPDU(apdu); + //print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(commandAPDU); + extractKeyCharAndBlob(response); + Assert.assertEquals(0x9000, response.getSW()); + } + + public void testImportAesKey(CardSimulator simulator){ + byte[] buf = new byte[512]; + buf[0] = (byte)0x80; + buf[1] = (byte)0x11; + buf[2] = (byte)0x40; + buf[3] = (byte)0x00; + buf[4] = 0; + short cmd = makeImportKeySymmCmd(KMType.AES, (short)256); + KMEncoder enc = new KMEncoder(); + short actualLen = enc.encode(cmd, buf, (short) 7); + Util.setShort(buf, (short)5, actualLen); + byte[] apdu = new byte[7+actualLen]; + Util.arrayCopyNonAtomic(buf,(short)0,apdu,(short)0,(short)(7+actualLen)); + //CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x10, 0x40, 0x00, buf, 0, actualLen); + CommandAPDU commandAPDU = new CommandAPDU(apdu); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(commandAPDU); + extractKeyCharAndBlob(response); + Assert.assertEquals(0x9000, response.getSW()); + } + public void testImportHmacKey(CardSimulator simulator){ + byte[] buf = new byte[512]; + buf[0] = (byte)0x80; + buf[1] = (byte)0x11; + buf[2] = (byte)0x40; + buf[3] = (byte)0x00; + buf[4] = 0; + short cmd = makeImportKeySymmCmd(KMType.HMAC, (short)128); + KMEncoder enc = new KMEncoder(); + short actualLen = enc.encode(cmd, buf, (short) 7); + Util.setShort(buf, (short)5, actualLen); + byte[] apdu = new byte[7+actualLen]; + Util.arrayCopyNonAtomic(buf,(short)0,apdu,(short)0,(short)(7+actualLen)); + //CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x10, 0x40, 0x00, buf, 0, actualLen); + CommandAPDU commandAPDU = new CommandAPDU(apdu); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(commandAPDU); + extractKeyCharAndBlob(response); + Assert.assertEquals(0x9000, response.getSW()); + } + public void testImportDesKey(CardSimulator simulator){ + byte[] buf = new byte[512]; + buf[0] = (byte)0x80; + buf[1] = (byte)0x11; + buf[2] = (byte)0x40; + buf[3] = (byte)0x00; + buf[4] = 0; + short cmd = makeImportKeySymmCmd(KMType.DES, (short)168); + KMEncoder enc = new KMEncoder(); + short actualLen = enc.encode(cmd, buf, (short) 7); + Util.setShort(buf, (short)5, actualLen); + byte[] apdu = new byte[7+actualLen]; + Util.arrayCopyNonAtomic(buf,(short)0,apdu,(short)0,(short)(7+actualLen)); + //CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x10, 0x40, 0x00, buf, 0, actualLen); + CommandAPDU commandAPDU = new CommandAPDU(apdu); + print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(commandAPDU); + extractKeyCharAndBlob(response); + Assert.assertEquals(0x9000, response.getSW()); + } + + public void testGenerateHmacKey(CardSimulator simulator){ + byte[] buf = new byte[512]; + buf[0] = (byte)0x80; + buf[1] = (byte)0x10; + buf[2] = (byte)0x40; + buf[3] = (byte)0x00; + buf[4] = 0; + // test provision command + short cmd = makeGenerateKeyCmdHmac(KMType.HMAC, (short)128); + KMEncoder enc = new KMEncoder(); + short actualLen = enc.encode(cmd, buf, (short) 7); + Util.setShort(buf, (short)5, actualLen); + byte[] apdu = new byte[7+actualLen]; + Util.arrayCopyNonAtomic(buf,(short)0,apdu,(short)0,(short)(7+actualLen)); + //CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x10, 0x40, 0x00, buf, 0, actualLen); + CommandAPDU commandAPDU = new CommandAPDU(apdu); + //print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(commandAPDU); + extractKeyCharAndBlob(response); + Assert.assertEquals(0x9000, response.getSW()); + } + + public void testGenerate3DesKey(CardSimulator simulator){ + byte[] buf = new byte[512]; + buf[0] = (byte)0x80; + buf[1] = (byte)0x10; + buf[2] = (byte)0x40; + buf[3] = (byte)0x00; + buf[4] = 0; + // test provision command + short cmd = makeGenerateKeyCmd(KMType.DES, (short)168); + KMEncoder enc = new KMEncoder(); + short actualLen = enc.encode(cmd, buf, (short) 7); + Util.setShort(buf, (short)5, actualLen); + byte[] apdu = new byte[7+actualLen]; + Util.arrayCopyNonAtomic(buf,(short)0,apdu,(short)0,(short)(7+actualLen)); + //CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x10, 0x40, 0x00, buf, 0, actualLen); + CommandAPDU commandAPDU = new CommandAPDU(apdu); + //print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(commandAPDU); + extractKeyCharAndBlob(response); + Assert.assertEquals(0x9000, response.getSW()); + } + + public void testGenerateEcKey(CardSimulator simulator){ + byte[] buf = new byte[512]; + buf[0] = (byte)0x80; + buf[1] = (byte)0x10; + buf[2] = (byte)0x40; + buf[3] = (byte)0x00; + buf[4] = 0; + // test provision command + short cmd = makeGenerateKeyCmd(KMType.EC, (short)256); + KMEncoder enc = new KMEncoder(); + short actualLen = enc.encode(cmd, buf, (short) 7); + Util.setShort(buf, (short)5, actualLen); + byte[] apdu = new byte[7+actualLen]; + Util.arrayCopyNonAtomic(buf,(short)0,apdu,(short)0,(short)(7+actualLen)); + //CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x10, 0x40, 0x00, buf, 0, actualLen); + CommandAPDU commandAPDU = new CommandAPDU(apdu); + //print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(commandAPDU); + extractKeyCharAndBlob(response); + Assert.assertEquals(0x9000, response.getSW()); + } public void testGetHwInfoCmd(CardSimulator simulator){ CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x1E, 0x40, 0x00); @@ -99,7 +418,7 @@ private void testAddRngEntropyCmd(CardSimulator simulator){ // test provision command short cmd = makeAddRngEntropyCmd(); KMEncoder enc = new KMEncoder(); - short actualLen = enc.encode(cmd, buf, (short) 0, (short)buf.length); + short actualLen = enc.encode(cmd, buf, (short) 0); CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x18, 0x40, 0x00, buf, 0, actualLen); //print(commandAPDU.getBytes()); @@ -150,6 +469,232 @@ private short makeProvisionCmd() { arg.add((short) 2, keyBlobPtr); return argPtr; } + + private short makeGenerateKeyCmd(byte alg, short keysize) { + // Argument + short arrPtr = KMArray.instance((short) 5); + KMArray vals = KMArray.cast(arrPtr); + byte[] val = "Test".getBytes(); + byte[] intVal = {1, 2, 3, 4}; + byte[] pubVal = {0x00, 0x01, 0x00, 0x01}; + byte[] digest = {KMType.SHA1, KMType.SHA2_256}; + vals.add((short)0, KMEnumTag.instance(KMType.ALGORITHM, alg)); + vals.add((short)1, KMIntegerTag.instance(KMType.UINT_TAG, KMType.USERID, KMInteger.uint_32(intVal, (short)0))); + vals.add((short)2, KMByteTag.instance(KMType.APPLICATION_ID, KMByteBlob.instance(val, (short)0, (short)val.length))); + vals.add((short)3, KMIntegerTag.instance(KMType.ULONG_TAG, KMType.RSA_PUBLIC_EXPONENT, KMInteger.uint_32(pubVal,(short)0))); + vals.add((short)4, KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16(keysize))); + short keyParamsPtr = KMKeyParameters.instance(arrPtr); + // Array of expected arguments + short argPtr = KMArray.instance((short) 1); + KMArray arg = KMArray.cast(argPtr); + arg.add((short) 0, keyParamsPtr); + return argPtr; + } + private short makeImportKeySymmCmd(short alg, short size) { + // Argument 1 + short arrPtr; + if(alg == KMType.HMAC) { + arrPtr = KMArray.instance((short) 6); + } else{ + arrPtr = KMArray.instance((short) 5); + } + KMArray vals = KMArray.cast(arrPtr); + byte[] val = "Test".getBytes(); + byte[] intVal = {1, 2, 3, 4}; + byte[] pubVal = {0x00, 0x01, 0x00, 0x01}; + byte[] digest = {KMType.SHA1, KMType.SHA2_256}; + vals.add((short)0, KMEnumTag.instance(KMType.ALGORITHM, (byte)alg)); + vals.add((short)1, KMIntegerTag.instance(KMType.UINT_TAG, KMType.USERID, KMInteger.uint_32(intVal, (short)0))); + vals.add((short)2, KMByteTag.instance(KMType.APPLICATION_ID, KMByteBlob.instance(val, (short)0, (short)val.length))); + vals.add((short)3, KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16(size))); + vals.add((short)4, KMEnumArrayTag.instance(KMType.DIGEST, KMByteBlob.instance(digest,(short)0, (short)digest.length))); + if (alg == KMType.HMAC) { + vals.add( + (short) 5,KMIntegerTag.instance(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, KMInteger.uint_16(size))); + } + short keyParamsPtr = KMKeyParameters.instance(arrPtr); + // Argument 2 + short keyFormatPtr = KMEnum.instance(KMType.KEY_FORMAT, KMType.RAW); + short len = 0; + byte[] secret = new byte[32]; + Key key; + // Argument 3 + switch (alg){ + case KMType.AES: + key = sim.createAESKey(size); + len = ((AESKey)key).getKey(secret, (short)0); + break; + case KMType.DES: + key = sim.createTDESKey(); + size = 168; + len = ((DESKey)key).getKey(secret, (short)0); + break; + case KMType.HMAC: + key = sim.createHMACKey(size); + len = ((HMACKey)key).getKey(secret, (short)0); + break; + default: + return 0; + } + short keyBlob = KMArray.instance((short)1); + KMArray.cast(keyBlob).add((short)0, KMByteBlob.instance(secret,(short)0,len)); + KMEncoder encoder = new KMEncoder(); + byte[] blob = new byte[256]; + len = encoder.encode(keyBlob,blob,(short)0); + keyBlob = KMByteBlob.instance(blob, (short)0, len); + // Array of expected arguments + short argPtr = KMArray.instance((short) 3); + KMArray arg = KMArray.cast(argPtr); + arg.add((short) 0, keyParamsPtr); + arg.add((short)1, keyFormatPtr); + arg.add((short)2, keyBlob); + return argPtr; + } + + private short makeImportKeyRsaCmd() { + // Argument 1 + short arrPtr = KMArray.instance((short) 4); + KMArray vals = KMArray.cast(arrPtr); + byte[] val = "Test".getBytes(); + byte[] intVal = {1, 2, 3, 4}; + byte[] pubVal = {0x00, 0x01, 0x00, 0x01}; + byte[] digest = {KMType.SHA1, KMType.SHA2_256}; + vals.add((short)0, KMEnumTag.instance(KMType.ALGORITHM, KMType.RSA)); + vals.add((short)1, KMIntegerTag.instance(KMType.UINT_TAG, KMType.USERID, KMInteger.uint_32(intVal, (short)0))); + vals.add((short)2, KMByteTag.instance(KMType.APPLICATION_ID, KMByteBlob.instance(val, (short)0, (short)val.length))); + vals.add((short)3, KMIntegerTag.instance(KMType.ULONG_TAG, KMType.RSA_PUBLIC_EXPONENT, KMInteger.uint_32(pubVal,(short)0))); + //vals.add((short)4, KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16((short)2048))); + short keyParamsPtr = KMKeyParameters.instance(arrPtr); + // Argument 2 + short keyFormatPtr = KMEnum.instance(KMType.KEY_FORMAT, KMType.RAW); + // Argument 3 + KeyPair rsa512KeyPair = new KeyPair(KeyPair.ALG_RSA, KeyBuilder.LENGTH_RSA_512); + rsa512KeyPair.genKeyPair(); + byte[] secret = new byte[64]; + byte[] modulus = new byte[64]; + short keyBlob = KMArray.instance((short)2); + RSAPrivateKey key = (RSAPrivateKey) rsa512KeyPair.getPrivate(); + short len = key.getExponent(secret, (short)0); + KMArray.cast(keyBlob).add((short)0, KMByteBlob.instance(secret,(short)0,len)); + len = key.getModulus(modulus, (short)0); + KMArray.cast(keyBlob).add((short)1, KMByteBlob.instance(modulus,(short)0,len)); + KMEncoder encoder = new KMEncoder(); + byte[] blob = new byte[256]; + len = encoder.encode(keyBlob,blob,(short)0); + keyBlob = KMByteBlob.instance(blob, (short)0, len); + // Array of expected arguments + short argPtr = KMArray.instance((short) 3); + KMArray arg = KMArray.cast(argPtr); + arg.add((short) 0, keyParamsPtr); + arg.add((short)1, keyFormatPtr); + arg.add((short)2, keyBlob); + return argPtr; + } + + private short makeImportKeyEcCmd() { + // Argument 1 + short arrPtr = KMArray.instance((short) 4); + KMArray vals = KMArray.cast(arrPtr); + byte[] val = "Test".getBytes(); + byte[] intVal = {1, 2, 3, 4}; + byte[] pubVal = {0x00, 0x01, 0x00, 0x01}; + byte[] digest = {KMType.SHA1, KMType.SHA2_256}; + vals.add((short)0, KMEnumTag.instance(KMType.ALGORITHM, KMType.EC)); + vals.add((short)1, KMIntegerTag.instance(KMType.UINT_TAG, KMType.USERID, KMInteger.uint_32(intVal, (short)0))); + vals.add((short)2, KMByteTag.instance(KMType.APPLICATION_ID, KMByteBlob.instance(val, (short)0, (short)val.length))); + vals.add((short)3, KMIntegerTag.instance(KMType.ULONG_TAG, KMType.RSA_PUBLIC_EXPONENT, KMInteger.uint_32(pubVal,(short)0))); + //vals.add((short)4, KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16((short)2048))); + short keyParamsPtr = KMKeyParameters.instance(arrPtr); + // Argument 2 + short keyFormatPtr = KMEnum.instance(KMType.KEY_FORMAT, KMType.RAW); + // Argument 3 + KeyPair ec192KeyPair = new KeyPair(KeyPair.ALG_EC_FP, KeyBuilder.LENGTH_EC_FP_192); + ec192KeyPair.genKeyPair(); + byte[] secret = new byte[24]; + byte[] pubKey = new byte[52]; + short keyBlob = KMArray.instance((short)3); + ECPrivateKey key1 = (ECPrivateKey) ec192KeyPair.getPrivate(); + ECPublicKey key2 = (ECPublicKey) ec192KeyPair.getPublic(); + short len = key1.getS(secret, (short)0); + KMArray.cast(keyBlob).add((short)0, KMByteBlob.instance(secret,(short)0,len)); + len = key2.getW(pubKey, (short)0); + KMArray.cast(keyBlob).add((short)1, KMByteBlob.instance(pubKey,(short)0,len)); + KMArray.cast(keyBlob).add((short)2, KMEnumTag.instance(KMType.ECCURVE, KMType.P_256)); + KMEncoder encoder = new KMEncoder(); + byte[] blob = new byte[256]; + len = encoder.encode(keyBlob,blob,(short)0); + keyBlob = KMByteBlob.instance(blob, (short)0, len); + // Array of expected arguments + short argPtr = KMArray.instance((short) 3); + KMArray arg = KMArray.cast(argPtr); + arg.add((short) 0, keyParamsPtr); + arg.add((short)1, keyFormatPtr); + arg.add((short)2, keyBlob); + return argPtr; + } + + private short makeGetKeyCharKeyCmd() { + // Argument + short argPtr = KMArray.instance((short) 3); + KMArray vals = KMArray.cast(argPtr); + byte[] val = "Test".getBytes(); + byte[] intVal = {1, 2, 3, 4}; + byte[] pubVal = {0x00, 0x01, 0x00, 0x01}; + byte[] digest = {KMType.SHA1, KMType.SHA2_256}; + vals.add((short)0, keyBlob); + vals.add((short)1, KMByteBlob.instance(val, (short)0, (short)val.length)); + Util.setShort(val,(short)0,KMType.INVALID_VALUE); + vals.add((short)2, KMByteBlob.instance(val, (short)0, (short)2));// No App Data + return argPtr; + } + private short makeGenerateKeyCmdHmac(byte alg, short keysize) { + // Argument + short arrPtr = KMArray.instance((short) 6); + KMArray vals = KMArray.cast(arrPtr); + byte[] val = "Test".getBytes(); + byte[] intVal = {1, 2, 3, 4}; + byte[] digest = {KMType.SHA1, KMType.SHA2_256}; + vals.add((short)0, KMEnumTag.instance(KMType.ALGORITHM, alg)); + vals.add((short)1, KMIntegerTag.instance(KMType.UINT_TAG, KMType.USERID, KMInteger.uint_32(intVal, (short)0))); + vals.add((short)2, KMByteTag.instance(KMType.APPLICATION_ID, KMByteBlob.instance(val, (short)0, (short)val.length))); + vals.add((short)3, KMIntegerTag.instance(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, KMInteger.uint_16(keysize))); + vals.add((short)4, KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16(keysize))); + vals.add((short)5, KMEnumArrayTag.instance(KMType.DIGEST, KMByteBlob.instance(digest,(short)0, (short)digest.length))); + short keyParamsPtr = KMKeyParameters.instance(arrPtr); + // Array of expected arguments + short argPtr = KMArray.instance((short) 1); + KMArray arg = KMArray.cast(argPtr); + arg.add((short) 0, keyParamsPtr); + return argPtr; + } + + private short makeSetBootParamsCmd() { + // Argument 1 OS Version + short versionPatchPtr = KMInteger.uint_16((short)1); + short versionTagPtr = KMIntegerTag.instance(KMType.UINT_TAG, KMType.OS_VERSION,versionPatchPtr); + // Argument 2 OS Patch level + short patchTagPtr = KMIntegerTag.instance(KMType.UINT_TAG, KMType.OS_PATCH_LEVEL, versionPatchPtr); + // Argument 3 Verified Boot Key + byte[] bootKeyHash = "00011122233344455566677788899900".getBytes(); + short bootKeyPtr = KMByteBlob.instance(bootKeyHash,(short)0, (short)bootKeyHash.length); + // Argument 4 Verified Boot Hash + short bootHashPtr = KMByteBlob.instance(bootKeyHash,(short)0, (short)bootKeyHash.length); + // Argument 5 Verified Boot State + short bootStatePtr = KMEnum.instance(KMType.VERIFIED_BOOT_STATE,KMType.VERIFIED_BOOT); + // Argument 6 Device Locked + short deviceLockedPtr = KMEnum.instance(KMType.DEVICE_LOCKED, KMType.DEVICE_LOCKED_FALSE); + // Arguments + short arrPtr = KMArray.instance((short) 6); + KMArray vals = KMArray.cast(arrPtr); + vals.add((short)0, versionTagPtr); + vals.add((short) 1, patchTagPtr); + vals.add((short) 2, bootKeyPtr); + vals.add((short) 3, bootHashPtr); + vals.add((short) 4, bootStatePtr); + vals.add((short) 5, deviceLockedPtr); + return arrPtr; + } + private short makeAddRngEntropyCmd() { // Argument 1 byte[] byteBlob = new byte[32]; diff --git a/Applet/JavaCardKeymaster.scr b/Applet/JavaCardKeymaster.scr index ce15b5c9..b1a669be 100644 --- a/Applet/JavaCardKeymaster.scr +++ b/Applet/JavaCardKeymaster.scr @@ -3,15 +3,48 @@ output on; //create applet instance 0x80 0xB8 0x00 0x00 0x0c 0x0a 0xa0 0x00 0x00 0x00 0x62 0x03 0x01 0xc 0x01 0x01 0x00 0x7F; - // Select JavaCardKeymaster //aid/A000000062/03010C0101 -0x00 0xa4 0x04 0x00 0x0a 0xa0 0x00 0x00 0x00 0x62 0x03 0x01 0xc 0x01 0x01 0x7F; + 0x00 0xa4 0x04 0x00 0x0a 0xa0 0x00 0x00 0x00 0x62 0x03 0x01 0xc 0x01 0x01 0x7F; // Send provision command - this will change in future -0x80 0x23 0x40 0x00 0x3B 0x83 0xA1 0x1A 0x10 0x00 0x00 0x02 0x01 0x00 0x58 0x30 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F 0x10 0x11 0x12 0x13 0x14 0x15 0x16 0x17 0x18 0x19 0x1A 0x1B 0x1C 0x1D 0x1E 0x1F 0x20 0x21 0x22 0x23 0x24 0x25 0x26 0x27 0x28 0x29 0x2A 0x2B 0x2C 0x2D 0x2E 0x2F 0x7F; + 0x80 0x23 0x40 0x00 0x3B 0x83 0xA1 0x1A 0x10 0x00 0x00 0x02 0x01 0x00 0x58 0x30 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F 0x10 0x11 0x12 0x13 0x14 0x15 0x16 0x17 0x18 0x19 0x1A 0x1B 0x1C 0x1D 0x1E 0x1F 0x20 0x21 0x22 0x23 0x24 0x25 0x26 0x27 0x28 0x29 0x2A 0x2B 0x2C 0x2D 0x2E 0x2F 0x7F; + +// Send Set Boot Params command + 0x80 0x24 0x40 0x00 0x53 0x86 0x1A 0x30 0x00 0x02 0xC1 0x01 0x1A 0x30 0x00 0x02 0xC2 0x01 0x58 0x20 0x30 0x30 0x30 0x31 0x31 0x31 0x32 0x32 0x32 0x33 0x33 0x33 0x34 0x34 0x34 0x35 0x35 0x35 0x36 0x36 0x36 0x37 0x37 0x37 0x38 0x38 0x38 0x39 0x39 0x39 0x30 0x30 0x58 0x20 0x30 0x30 0x30 0x31 0x31 0x31 0x32 0x32 0x32 0x33 0x33 0x33 0x34 0x34 0x34 0x35 0x35 0x35 0x36 0x36 0x36 0x37 0x37 0x37 0x38 0x38 0x38 0x39 0x39 0x39 0x30 0x30 0x02 0x00 0x7F; // Send getHardwareInfo command -0x80 0x1E 0x40 0x00 0x00 0x7F; + 0x80 0x1E 0x40 0x00 0x00 0x7F; // Send AddRngEntropy command -0x80 0x18 0x40 0x00 0x23 0x81 0x58 0x20 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F 0x10 0x11 0x12 0x13 0x14 0x15 0x16 0x17 0x18 0x19 0x1A 0x1B 0x1C 0x1D 0x1E 0x1F 0x7F; \ No newline at end of file + 0x80 0x18 0x40 0x00 0x23 0x81 0x58 0x20 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F 0x10 0x11 0x12 0x13 0x14 0x15 0x16 0x17 0x18 0x19 0x1A 0x1B 0x1C 0x1D 0x1E 0x1F 0x7F; + +// Generate Key - RSA Key command + 0x80 0x10 0x40 0x00 0x2E 0x81 0xA5 0x1A 0x10 0x00 0x00 0x02 0x01 0x1A 0x30 0x00 0x01 0xF5 0x1A 0x01 0x02 0x03 0x04 0x1A 0x90 0x00 0x02 0x59 0x44 0x54 0x65 0x73 0x74 0x1A 0x50 0x00 0x00 0xC8 0x1A 0x00 0x01 0x00 0x01 0x1A 0x30 0x00 0x00 0x03 0x19 0x08 0x00 0x7F; + +// Generate Key - AES Key command + 0x80 0x10 0x40 0x00 0x2F 0x81 0xA5 0x1A 0x10 0x00 0x00 0x02 0x18 0x20 0x1A 0x30 0x00 0x01 0xF5 0x1A 0x01 0x02 0x03 0x04 0x1A 0x90 0x00 0x02 0x59 0x44 0x54 0x65 0x73 0x74 0x1A 0x50 0x00 0x00 0xC8 0x1A 0x00 0x01 0x00 0x01 0x1A 0x30 0x00 0x00 0x03 0x19 0x01 0x00 0x7F; + +// Generate Key - ECC Key command + 0x80 0x10 0x40 0x00 0x2E 0x81 0xA5 0x1A 0x10 0x00 0x00 0x02 0x03 0x1A 0x30 0x00 0x01 0xF5 0x1A 0x01 0x02 0x03 0x04 0x1A 0x90 0x00 0x02 0x59 0x44 0x54 0x65 0x73 0x74 0x1A 0x50 0x00 0x00 0xC8 0x1A 0x00 0x01 0x00 0x01 0x1A 0x30 0x00 0x00 0x03 0x19 0x01 0x00 0x7F; + +// Generate Key - DES Key command + 0x80 0x10 0x40 0x00 0x2E 0x81 0xA5 0x1A 0x10 0x00 0x00 0x02 0x18 0x21 0x1A 0x30 0x00 0x01 0xF5 0x1A 0x01 0x02 0x03 0x04 0x1A 0x90 0x00 0x02 0x59 0x44 0x54 0x65 0x73 0x74 0x1A 0x50 0x00 0x00 0xC8 0x1A 0x00 0x01 0x00 0x01 0x1A 0x30 0x00 0x00 0x03 0x18 0xA8 0x7F; + +// Generate Key - HMAC Key command + 0x80 0x10 0x40 0x00 0x33 0x81 0xA6 0x1A 0x10 0x00 0x00 0x02 0x18 0x80 0x1A 0x30 0x00 0x01 0xF5 0x1A 0x01 0x02 0x03 0x04 0x1A 0x90 0x00 0x02 0x59 0x44 0x54 0x65 0x73 0x74 0x1A 0x30 0x00 0x00 0x08 0x18 0x80 0x1A 0x30 0x00 0x00 0x03 0x18 0x80 0x1A 0x20 0x00 0x00 0x05 0x42 0x02 0x04 0x7F; + +// Import RSA Key + 0x80 0x11 0x40 0x00 0xAE 0x83 0xA4 0x1A 0x10 0x00 0x00 0x02 0x01 0x1A 0x30 0x00 0x01 0xF5 0x1A 0x01 0x02 0x03 0x04 0x1A 0x90 0x00 0x02 0x59 0x44 0x54 0x65 0x73 0x74 0x1A 0x50 0x00 0x00 0xC8 0x1A 0x00 0x01 0x00 0x01 0x03 0x58 0x85 0x82 0x58 0x40 0x1F 0x1E 0xBD 0xB0 0xE0 0x3A 0x42 0xD7 0x7A 0x2C 0xBE 0xB7 0x00 0x7F 0x98 0x3C 0x32 0xC3 0x43 0x80 0x16 0x75 0x09 0xF4 0xAA 0x4E 0x45 0x85 0xE0 0x8B 0x34 0xC3 0x5B 0x3C 0x03 0x4A 0x2D 0x35 0x7D 0xFC 0x31 0x6A 0xB6 0x49 0xB1 0x3F 0x92 0x90 0x21 0x4C 0x99 0x5B 0x8B 0x4E 0xD6 0x00 0x7D 0x01 0x5D 0xEC 0x1F 0x87 0x6E 0x81 0x58 0x40 0x80 0x94 0x2B 0xC3 0x69 0x81 0x10 0xF9 0x66 0x3A 0xB2 0x4D 0x43 0x05 0x03 0xD6 0x79 0x4B 0x25 0xFB 0x30 0x98 0xE0 0x8E 0x5A 0x1A 0xA8 0xC0 0xFE 0x5F 0x0C 0x79 0x66 0x2C 0x0B 0x9D 0xDA 0xA5 0x45 0x4F 0x29 0x2C 0x80 0x9E 0xAF 0xB0 0x0A 0x6C 0xEA 0x5B 0xAE 0x22 0xF2 0x6C 0x37 0xFB 0x69 0x5F 0xED 0xDF 0xDB 0x3F 0x17 0xE5 0x7F; + +// Import EC Key + 0x80 0x11 0x40 0x00 0x7D 0x83 0xA4 0x1A 0x10 0x00 0x00 0x02 0x03 0x1A 0x30 0x00 0x01 0xF5 0x1A 0x01 0x02 0x03 0x04 0x1A 0x90 0x00 0x02 0x59 0x44 0x54 0x65 0x73 0x74 0x1A 0x50 0x00 0x00 0xC8 0x1A 0x00 0x01 0x00 0x01 0x03 0x58 0x54 0x83 0x58 0x18 0xA8 0x90 0x6D 0x38 0x3A 0x25 0x67 0x08 0x85 0xD3 0x07 0x17 0x3A 0x7C 0x0E 0x81 0x96 0x46 0xA0 0xDA 0x2C 0xBD 0xD2 0xEA 0x58 0x31 0x04 0x95 0xCE 0xB7 0x75 0x21 0xD8 0xAE 0xC9 0xA2 0x99 0xC0 0x00 0x6C 0x2E 0xC2 0x11 0x7D 0x79 0x52 0x56 0xB7 0x3D 0xC0 0xC8 0xE7 0x07 0x6D 0xD1 0xBD 0xCD 0xF6 0x05 0xD3 0xB3 0xF4 0xD1 0x56 0x86 0x90 0xED 0xD3 0x4F 0xA2 0x85 0xC2 0x3B 0xCE 0x45 0x1A 0x10 0x00 0x00 0x0A 0x01 0x7F; + +// Import AES Key + 0x80 0x11 0x40 0x00 0x41 0x83 0xA5 0x1A 0x10 0x00 0x00 0x02 0x18 0x20 0x1A 0x30 0x00 0x01 0xF5 0x1A 0x01 0x02 0x03 0x04 0x1A 0x90 0x00 0x02 0x59 0x44 0x54 0x65 0x73 0x74 0x1A 0x30 0x00 0x00 0x03 0x19 0x01 0x00 0x1A 0x20 0x00 0x00 0x05 0x42 0x02 0x04 0x03 0x52 0x81 0x50 0xF6 0x16 0x03 0x1C 0x26 0xB5 0x6F 0x2E 0x42 0xCB 0x7C 0x37 0x96 0x1C 0x27 0xBA 0x7F; + +// Import Hmac Key + 0x80 0x11 0x40 0x00 0x47 0x83 0xA6 0x1A 0x10 0x00 0x00 0x02 0x18 0x80 0x1A 0x30 0x00 0x01 0xF5 0x1A 0x01 0x02 0x03 0x04 0x1A 0x90 0x00 0x02 0x59 0x44 0x54 0x65 0x73 0x74 0x1A 0x30 0x00 0x00 0x03 0x18 0x80 0x1A 0x20 0x00 0x00 0x05 0x42 0x02 0x04 0x1A 0x30 0x00 0x00 0x08 0x18 0x80 0x03 0x52 0x81 0x50 0xA8 0x66 0xA3 0xE5 0xBA 0x53 0xE9 0x93 0x00 0xE9 0x1A 0xEC 0xBF 0x8F 0xEC 0x90 0x7F; + +// Import Des Key + 0x80 0x11 0x40 0x00 0x40 0x83 0xA5 0x1A 0x10 0x00 0x00 0x02 0x18 0x21 0x1A 0x30 0x00 0x01 0xF5 0x1A 0x01 0x02 0x03 0x04 0x1A 0x90 0x00 0x02 0x59 0x44 0x54 0x65 0x73 0x74 0x1A 0x30 0x00 0x00 0x03 0x18 0xA8 0x1A 0x20 0x00 0x00 0x05 0x42 0x02 0x04 0x03 0x52 0x81 0x50 0x80 0x5B 0x6F 0xCB 0xBB 0x1B 0x07 0xD3 0xB6 0xE1 0x67 0xAB 0x51 0xC6 0x34 0x29 0x7F; + diff --git a/Applet/default.output b/Applet/default.output index 81fa92ba..3763ffa7 100644 --- a/Applet/default.output +++ b/Applet/default.output @@ -5,5 +5,16 @@ OUTPUT ON; CLA: 80, INS: b8, P1: 00, P2: 00, Lc: 0c, 0a, a0, 00, 00, 00, 62, 03, 01, 0c, 01, 01, 00, Le: 0a, a0, 00, 00, 00, 62, 03, 01, 0c, 01, 01, SW1: 90, SW2: 00 CLA: 00, INS: a4, P1: 04, P2: 00, Lc: 0a, a0, 00, 00, 00, 62, 03, 01, 0c, 01, 01, Le: 00, SW1: 90, SW2: 00 CLA: 80, INS: 23, P1: 40, P2: 00, Lc: 3b, 83, a1, 1a, 10, 00, 00, 02, 01, 00, 58, 30, 00, 01, 02, 03, 04, 05, 06, 07, 08, 09, 0a, 0b, 0c, 0d, 0e, 0f, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 1a, 1b, 1c, 1d, 1e, 1f, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 2a, 2b, 2c, 2d, 2e, 2f, Le: 00, SW1: 90, SW2: 00 -CLA: 80, INS: 1e, P1: 40, P2: 00, Lc: 00, Le: 56, 83, 02, 57, 4a, 61, 76, 61, 63, 61, 72, 64, 4b, 65, 79, 6d, 61, 73, 74, 65, 72, 44, 65, 76, 69, 63, 65, 46, 47, 6f, 6f, 67, 6c, 65, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, SW1: 90, SW2: 00 +CLA: 80, INS: 24, P1: 40, P2: 00, Lc: 53, 86, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, 58, 20, 30, 30, 30, 31, 31, 31, 32, 32, 32, 33, 33, 33, 34, 34, 34, 35, 35, 35, 36, 36, 36, 37, 37, 37, 38, 38, 38, 39, 39, 39, 30, 30, 58, 20, 30, 30, 30, 31, 31, 31, 32, 32, 32, 33, 33, 33, 34, 34, 34, 35, 35, 35, 36, 36, 36, 37, 37, 37, 38, 38, 38, 39, 39, 39, 30, 30, 02, 00, Le: 00, SW1: 90, SW2: 00 +CLA: 80, INS: 1e, P1: 40, P2: 00, Lc: 00, Le: 21, 83, 02, 57, 4a, 61, 76, 61, 63, 61, 72, 64, 4b, 65, 79, 6d, 61, 73, 74, 65, 72, 44, 65, 76, 69, 63, 65, 46, 47, 6f, 6f, 67, 6c, 65, SW1: 90, SW2: 00 CLA: 80, INS: 18, P1: 40, P2: 00, Lc: 23, 81, 58, 20, 00, 01, 02, 03, 04, 05, 06, 07, 08, 09, 0a, 0b, 0c, 0d, 0e, 0f, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 1a, 1b, 1c, 1d, 1e, 1f, Le: 00, SW1: 90, SW2: 00 +CLA: 80, INS: 10, P1: 40, P2: 00, Lc: 2e, 81, a5, 1a, 10, 00, 00, 02, 01, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, 1a, 90, 00, 02, 59, 44, 54, 65, 73, 74, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 30, 00, 00, 03, 19, 08, 00, Le: 11, 83, 00, 58, d6, 85, 58, 40, 11, 8c, a2, 01, 8d, 5d, d0, d5, 79, dc, fc, 71, a5, a0, ea, fc, e5, 11, 32, e7, 8a, bd, d1, 5c, a0, d8, 5f, 01, 8e, 4d, b1, f0, 09, 79, 5c, b9, e3, 26, 09, 26, dc, 7c, 21, b4, ee, 9b, 28, aa, c2, 73, 0c, ca, e4, 9b, 29, 5d, a0, f4, aa, 87, 7a, 51, 31, 7a, 4c, 62, 92, 2f, 4c, 7d, da, 2a, 3d, 6d, 92, d2, 0a, 4c, 63, 8e, 40, 1f, 03, a1, b1, 1c, 32, 52, 22, 73, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a6, 1a, 10, 00, 00, 02, 01, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 30, 00, 00, 03, 19, 08, 00, 1a, 10, 00, 02, be, 00, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, 58, 40, b4, 05, c4, 0e, 08, 15, 0e, a5, 86, 84, b2, 03, 00, 2c, cb, d7, 23, 85, c9, c1, 0b, 74, cb, 20, 10, b1, 25, 8e, 4b, 38, d4, 72, da, 2c, 2c, 64, 0e, e9, 2f, b2, d6, 74, 2e, 02, 77, 69, 74, 9f, a9, 98, e7, 7a, 4f, 9e, d4, 06, 62, 0c, 33, 6e, 80, 3a, 3f, e3, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a6, 1a, 10, 00, 00, 02, 01, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 30, 00, 00, 03, 19, 08, 00, 1a, 10, 00, 02, be, 00, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, SW1: 90, SW2: 00 +CLA: 80, INS: 10, P1: 40, P2: 00, Lc: 2f, 81, a5, 1a, 10, 00, 00, 02, 18, 20, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, 1a, 90, 00, 02, 59, 44, 54, 65, 73, 74, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 30, 00, 00, 03, 19, 01, 00, Le: a0, 83, 00, 58, 64, 84, 50, 4d, eb, 33, dc, 4d, 36, 74, 3a, 38, d4, b6, 1d, 0c, 48, 70, a6, 4c, fa, 0f, 3b, 66, 89, 7a, 20, 81, 20, c6, f5, f3, 4c, 14, cc, a8, 4b, 27, 04, 49, a0, 4f, 95, 93, 07, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a6, 1a, 10, 00, 00, 02, 18, 20, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 30, 00, 00, 03, 19, 01, 00, 1a, 10, 00, 02, be, 00, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a6, 1a, 10, 00, 00, 02, 18, 20, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 30, 00, 00, 03, 19, 01, 00, 1a, 10, 00, 02, be, 00, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, SW1: 90, SW2: 00 +CLA: 80, INS: 10, P1: 40, P2: 00, Lc: 2e, 81, a5, 1a, 10, 00, 00, 02, 03, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, 1a, 90, 00, 02, 59, 44, 54, 65, 73, 74, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 30, 00, 00, 03, 19, 01, 00, Le: da, 83, 00, 58, 9f, 85, 58, 18, a7, 1b, 67, ff, 9e, ef, 1a, 20, 88, ce, dc, 29, 49, f1, 69, 0a, c7, 99, 3f, 68, cf, c1, 12, 2a, 4c, ee, 2c, 52, c6, 4d, 6f, e7, 9e, 60, 6c, 96, 50, 4c, 86, 14, 3a, 79, 7a, c6, 2e, cb, 70, 33, 83, c2, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a6, 1a, 10, 00, 00, 02, 03, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 30, 00, 00, 03, 19, 01, 00, 1a, 10, 00, 02, be, 00, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, 58, 31, 04, 5a, 10, b8, d7, e9, be, 0a, e9, 47, 6c, 79, e4, 64, 15, 9c, 9d, 32, c6, 74, dd, 17, 11, 61, b7, 9a, 0c, 57, 8e, a1, 5f, 64, 1e, 8d, 51, 5b, 1e, 19, 4e, cc, 0e, 33, 15, d0, 9e, f6, f5, 8d, 2e, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a6, 1a, 10, 00, 00, 02, 03, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 30, 00, 00, 03, 19, 01, 00, 1a, 10, 00, 02, be, 00, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, SW1: 90, SW2: 00 +CLA: 80, INS: 10, P1: 40, P2: 00, Lc: 2e, 81, a5, 1a, 10, 00, 00, 02, 18, 21, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, 1a, 90, 00, 02, 59, 44, 54, 65, 73, 74, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 30, 00, 00, 03, 18, a8, Le: 9e, 83, 00, 58, 63, 84, 50, 6d, fd, 69, 01, 75, 25, d1, 54, 24, f1, 2c, c2, 6e, 6d, 8c, d3, 4c, e6, a1, 6a, dd, 67, 55, 62, df, f2, ed, 1c, e9, 4c, 42, ef, f8, a4, 56, 52, 52, 03, 73, d7, d7, f2, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a6, 1a, 10, 00, 00, 02, 18, 21, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 30, 00, 00, 03, 18, a8, 1a, 10, 00, 02, be, 00, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a6, 1a, 10, 00, 00, 02, 18, 21, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 30, 00, 00, 03, 18, a8, 1a, 10, 00, 02, be, 00, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, SW1: 90, SW2: 00 +CLA: 80, INS: 10, P1: 40, P2: 00, Lc: 33, 81, a6, 1a, 10, 00, 00, 02, 18, 80, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, 1a, 90, 00, 02, 59, 44, 54, 65, 73, 74, 1a, 30, 00, 00, 08, 18, 80, 1a, 30, 00, 00, 03, 18, 80, 1a, 20, 00, 00, 05, 42, 02, 04, Le: a8, 83, 00, 58, 68, 84, 50, af, ee, 95, 3a, d3, f1, 6a, 0a, e6, 88, 30, 63, fa, 8d, e5, 27, 4c, de, af, d7, 29, c4, 08, 59, 9d, 3b, 55, 59, 4e, 4c, 11, 1f, 0b, 47, 4e, a9, e5, d5, 74, 5a, 14, 20, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a7, 1a, 10, 00, 00, 02, 18, 80, 1a, 30, 00, 00, 08, 18, 80, 1a, 30, 00, 00, 03, 18, 80, 1a, 20, 00, 00, 05, 42, 02, 04, 1a, 10, 00, 02, be, 00, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a7, 1a, 10, 00, 00, 02, 18, 80, 1a, 30, 00, 00, 08, 18, 80, 1a, 30, 00, 00, 03, 18, 80, 1a, 20, 00, 00, 05, 42, 02, 04, 1a, 10, 00, 02, be, 00, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, SW1: 90, SW2: 00 +CLA: 80, INS: 11, P1: 40, P2: 00, Lc: ae, 83, a4, 1a, 10, 00, 00, 02, 01, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, 1a, 90, 00, 02, 59, 44, 54, 65, 73, 74, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 03, 58, 85, 82, 58, 40, 1f, 1e, bd, b0, e0, 3a, 42, d7, 7a, 2c, be, b7, 00, 7f, 98, 3c, 32, c3, 43, 80, 16, 75, 09, f4, aa, 4e, 45, 85, e0, 8b, 34, c3, 5b, 3c, 03, 4a, 2d, 35, 7d, fc, 31, 6a, b6, 49, b1, 3f, 92, 90, 21, 4c, 99, 5b, 8b, 4e, d6, 00, 7d, 01, 5d, ec, 1f, 87, 6e, 81, 58, 40, 80, 94, 2b, c3, 69, 81, 10, f9, 66, 3a, b2, 4d, 43, 05, 03, d6, 79, 4b, 25, fb, 30, 98, e0, 8e, 5a, 1a, a8, c0, fe, 5f, 0c, 79, 66, 2c, 0b, 9d, da, a5, 45, 4f, 29, 2c, 80, 9e, af, b0, 0a, 6c, ea, 5b, ae, 22, f2, 6c, 37, fb, 69, 5f, ed, df, db, 3f, 17, e5, Le: 11, 83, 00, 58, d6, 85, 58, 40, 3b, 5a, c7, 40, 44, 8e, b4, 9b, 73, 68, 11, bc, d7, 6b, 14, fb, 2f, 76, ea, 91, d1, 92, ba, 6c, 70, ce, 13, 5d, f9, 96, b0, 17, 6d, 4a, 6d, 16, fc, 60, 17, cd, 08, 8a, 47, 3a, a9, 7b, e5, 36, 70, 87, 2f, af, 65, cd, 9e, bc, 62, c2, a0, fe, 47, 6b, fa, 73, 4c, f4, b4, 76, af, 25, 7d, eb, 88, 6e, ca, f9, b8, 4c, a4, b9, a1, 20, 29, 43, 74, ac, 06, 8f, 61, 62, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a6, 1a, 10, 00, 00, 02, 01, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 30, 00, 00, 03, 19, 08, 00, 1a, 10, 00, 02, be, 02, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, 58, 40, 80, 94, 2b, c3, 69, 81, 10, f9, 66, 3a, b2, 4d, 43, 05, 03, d6, 79, 4b, 25, fb, 30, 98, e0, 8e, 5a, 1a, a8, c0, fe, 5f, 0c, 79, 66, 2c, 0b, 9d, da, a5, 45, 4f, 29, 2c, 80, 9e, af, b0, 0a, 6c, ea, 5b, ae, 22, f2, 6c, 37, fb, 69, 5f, ed, df, db, 3f, 17, e5, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a6, 1a, 10, 00, 00, 02, 01, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 30, 00, 00, 03, 19, 08, 00, 1a, 10, 00, 02, be, 02, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, SW1: 90, SW2: 00 +CLA: 80, INS: 11, P1: 40, P2: 00, Lc: 7d, 83, a4, 1a, 10, 00, 00, 02, 03, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, 1a, 90, 00, 02, 59, 44, 54, 65, 73, 74, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 03, 58, 54, 83, 58, 18, a8, 90, 6d, 38, 3a, 25, 67, 08, 85, d3, 07, 17, 3a, 7c, 0e, 81, 96, 46, a0, da, 2c, bd, d2, ea, 58, 31, 04, 95, ce, b7, 75, 21, d8, ae, c9, a2, 99, c0, 00, 6c, 2e, c2, 11, 7d, 79, 52, 56, b7, 3d, c0, c8, e7, 07, 6d, d1, bd, cd, f6, 05, d3, b3, f4, d1, 56, 86, 90, ed, d3, 4f, a2, 85, c2, 3b, ce, 45, 1a, 10, 00, 00, 0a, 01, Le: e6, 83, 00, 58, a5, 85, 58, 18, e7, 0d, ce, 40, c4, 39, a6, 32, 09, 23, 63, 1d, cf, 03, 2c, c3, b4, 84, 8a, 1e, ab, d6, 3b, ab, 4c, 6b, 1d, 1e, 6d, 24, 79, 22, 9a, fe, 81, 37, 7c, 4c, 0f, 4a, 14, 9a, 91, 31, 39, 94, ea, a5, 2d, 14, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a7, 1a, 10, 00, 00, 02, 03, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 30, 00, 00, 03, 19, 01, 00, 1a, 10, 00, 00, 0a, 01, 1a, 10, 00, 02, be, 02, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, 58, 31, 04, 95, ce, b7, 75, 21, d8, ae, c9, a2, 99, c0, 00, 6c, 2e, c2, 11, 7d, 79, 52, 56, b7, 3d, c0, c8, e7, 07, 6d, d1, bd, cd, f6, 05, d3, b3, f4, d1, 56, 86, 90, ed, d3, 4f, a2, 85, c2, 3b, ce, 45, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a7, 1a, 10, 00, 00, 02, 03, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 30, 00, 00, 03, 19, 01, 00, 1a, 10, 00, 00, 0a, 01, 1a, 10, 00, 02, be, 02, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, SW1: 90, SW2: 00 +CLA: 80, INS: 11, P1: 40, P2: 00, Lc: 41, 83, a5, 1a, 10, 00, 00, 02, 18, 20, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, 1a, 90, 00, 02, 59, 44, 54, 65, 73, 74, 1a, 30, 00, 00, 03, 19, 01, 00, 1a, 20, 00, 00, 05, 42, 02, 04, 03, 52, 81, 50, f6, 16, 03, 1c, 26, b5, 6f, 2e, 42, cb, 7c, 37, 96, 1c, 27, ba, Le: 9c, 83, 00, 58, 62, 84, 50, b8, 4e, b2, a0, 85, 11, 94, 9a, e3, f6, 13, fe, f9, a1, b3, dd, 4c, f6, 10, 1c, 18, 2b, 4f, 80, 8d, e3, 76, ed, 92, 4c, 41, ab, d8, 54, 0c, 54, 18, 01, 33, 45, 09, 1e, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a6, 1a, 10, 00, 00, 02, 18, 20, 1a, 30, 00, 00, 03, 19, 01, 00, 1a, 20, 00, 00, 05, 42, 02, 04, 1a, 10, 00, 02, be, 02, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a6, 1a, 10, 00, 00, 02, 18, 20, 1a, 30, 00, 00, 03, 19, 01, 00, 1a, 20, 00, 00, 05, 42, 02, 04, 1a, 10, 00, 02, be, 02, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, SW1: 90, SW2: 00 +CLA: 80, INS: 11, P1: 40, P2: 00, Lc: 47, 83, a6, 1a, 10, 00, 00, 02, 18, 80, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, 1a, 90, 00, 02, 59, 44, 54, 65, 73, 74, 1a, 30, 00, 00, 03, 18, 80, 1a, 20, 00, 00, 05, 42, 02, 04, 1a, 30, 00, 00, 08, 18, 80, 03, 52, 81, 50, a8, 66, a3, e5, ba, 53, e9, 93, 00, e9, 1a, ec, bf, 8f, ec, 90, Le: a8, 83, 00, 58, 68, 84, 50, 63, 2d, d8, 58, c8, 5b, 66, 98, da, de, 51, 5f, e5, c8, 90, 99, 4c, 8a, 7e, 7c, 1e, 4c, ba, 19, e5, 66, d5, 27, 2d, 4c, 3d, ed, 1d, 79, c8, d8, b7, 3b, 93, 1e, fd, ad, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a7, 1a, 10, 00, 00, 02, 18, 80, 1a, 30, 00, 00, 03, 18, 80, 1a, 20, 00, 00, 05, 42, 02, 04, 1a, 30, 00, 00, 08, 18, 80, 1a, 10, 00, 02, be, 02, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a7, 1a, 10, 00, 00, 02, 18, 80, 1a, 30, 00, 00, 03, 18, 80, 1a, 20, 00, 00, 05, 42, 02, 04, 1a, 30, 00, 00, 08, 18, 80, 1a, 10, 00, 02, be, 02, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, SW1: 90, SW2: 00 +CLA: 80, INS: 11, P1: 40, P2: 00, Lc: 40, 83, a5, 1a, 10, 00, 00, 02, 18, 21, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, 1a, 90, 00, 02, 59, 44, 54, 65, 73, 74, 1a, 30, 00, 00, 03, 18, a8, 1a, 20, 00, 00, 05, 42, 02, 04, 03, 52, 81, 50, 80, 5b, 6f, cb, bb, 1b, 07, d3, b6, e1, 67, ab, 51, c6, 34, 29, Le: 9a, 83, 00, 58, 61, 84, 50, c0, ec, 75, 64, dd, a9, 3f, 81, 56, aa, 95, c5, 60, c7, 7f, 5b, 4c, ac, 0a, 0f, 27, 80, f4, 2e, 25, 76, 68, 62, 21, 4c, ae, c2, 77, b3, f7, 43, e6, f5, 19, ac, 77, 12, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a6, 1a, 10, 00, 00, 02, 18, 21, 1a, 30, 00, 00, 03, 18, a8, 1a, 20, 00, 00, 05, 42, 02, 04, 1a, 10, 00, 02, be, 02, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a6, 1a, 10, 00, 00, 02, 18, 21, 1a, 30, 00, 00, 03, 18, a8, 1a, 20, 00, 00, 05, 42, 02, 04, 1a, 10, 00, 02, be, 02, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, SW1: 90, SW2: 00