diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMArray.java b/Applet/Applet/src/com/android/javacard/keymaster/KMArray.java index d2eed688..2935aa2b 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 + ARRAY_HEADER_SIZE); + } + + 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..e56dc9f3 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,62 +37,53 @@ 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 + // isValidTag the tag key private static boolean validateKey(short key) { short index = (short) tags.length; while (--index >= 0) { @@ -99,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 54b0e9e4..423220fa 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMByteBlob.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMByteBlob.java @@ -20,93 +20,83 @@ 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() {} + + private static KMByteBlob proto(short ptr) { + if (prototype == null) prototype = new KMByteBlob(); + instPtr = ptr; + return prototype; } - @Override - public void init() { - length = 0; - startOff = 0; - val = null; + // pointer to an empty instance used as expression + public static short exp() { + return KMType.exp(BYTE_BLOB_TYPE); } - @Override - public short length() { - return length; + // return an empty byte blob instance + public static short instance(short length) { + return KMType.instance(BYTE_BLOB_TYPE, length); } - public static KMByteBlob instance() { - return repository.newByteBlob(); + // 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; } - // 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); + // 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(length); - Util.arrayCopyNonAtomic(blob, startOff, inst.val, inst.startOff, inst.length); - return inst; + return proto(ptr); } - // returns empty blob with given length - public static KMByteBlob instance(short length) { - if (length <= 0) { - ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); - } - KMByteBlob inst = instance(); - inst.startOff = repository.newByteArray(length); - inst.val = repository.getByteHeapRef(); - inst.length = length; - return inst; + // Add the byte + public void add(short index, byte val) { + short len = length(); + if (index >= len) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + heap[(short) (instPtr + TLV_HEADER_SIZE + index)] = val; } - public static void create(KMByteBlob[] byteBlobRefTable) { - byte index = 0; - while (index < byteBlobRefTable.length) { - byteBlobRefTable[index] = new KMByteBlob(); - index++; - } + // Get the byte + public byte get(short index) { + short len = length(); + if (index >= len) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + return heap[(short) (instPtr + TLV_HEADER_SIZE + index)]; } - // sets the expected length for prototype byte val. - public KMByteBlob withLength(short len) { - this.length = len; - return this; + // Get the start of blob + public short getStartOff() { + return (short) (instPtr + TLV_HEADER_SIZE); } - 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; + // Get the length of the blob + public short length() { + return Util.getShort(heap, (short) (instPtr + 1)); } - 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)]; + // Get the buffer pointer in which blob is contained. + public byte[] getBuffer() { + return heap; } - public byte[] getVal() { - return val; + public void getValue(byte[] destBuf, short destStart, short destLength){ + Util.arrayCopyNonAtomic(heap, getStartOff(), destBuf, destStart, destLength); } - public short getStartOff() { - return startOff; + 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 1569394c..a00d6c6f 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, @@ -38,73 +41,73 @@ public class KMByteTag extends KMTag { ATTESTATION_ID_MODEL, ASSOCIATED_DATA, NONCE, - CONFIRMATION_TOKEN + CONFIRMATION_TOKEN, + VERIFIED_BOOT_KEY, + VERIFIED_BOOT_HASH }; - 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 +119,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/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 a1c43aa3..25c1c1b1 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,204 @@ 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); } + 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){ + 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 KMEnumArrayTag decode(KMEnumArrayTag exp) { - readTagKey(exp.getTagType()); + 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; + short tagClass; + short allowedType; + short obj; + // For each tag in payload ... + while (index < payloadLength) { + tagFound = false; + tagInd = 0; + tagType = peekTagType(); + // Check against the allowed tags ... + while (tagInd < length) { + 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. + obj = decode(tagClass); + KMArray.cast(vals).add(index,obj); + 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; + 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) { + 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 + type = KMArray.cast(exp).containedType(); + while(index < payloadLength){ + obj = decode(type); + KMArray.cast(arrPtr).add(index, obj); + 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 +265,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 +300,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 +314,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..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,177 +45,214 @@ 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(KMArray 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; - this.startOff = startOff; - return this.length; + 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); } - private void encode(KMType exp){ - if(exp instanceof KMByteBlob){ - encode((KMByteBlob) exp); - return; - } - 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; + + public short encodeError(short err, byte[] buffer, short startOff, short length) { + this.buffer = buffer; + this.startOff = startOff; + 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); } - if(exp instanceof KMHmacSharingParameters){ - encode((KMHmacSharingParameters) exp); - return; + return (short)(this.startOff - startOff); + } + + 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); + } } - 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()); - short index = 0; - while(index < obj.length()){ - encode(obj.get(index)); - index++; + private void encodeArray(short obj) { + writeMajorTypeWithLength(ARRAY_TYPE, KMArray.cast(obj).length()); + short len = KMArray.cast(obj).length(); + short index = (short)(len-1); + while(index >= 0){ + encode(KMArray.cast(obj).get(index)); + index--; } } - private void encodeAsMap(KMArray obj){ - writeMajorTypeWithLength(MAP_TYPE, obj.length()); - short index = 0; - while(index < obj.length()){ - KMType t = obj.get(index); - encode(t); - //encode(obj.get(index)); - index++; + private void encodeAsMap(short obj){ + writeMajorTypeWithLength(MAP_TYPE, KMArray.cast(obj).length()); + short len = KMArray.cast(obj).length(); + short index = (short)(len-1); + short inst; + while(index >= 0){ + 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; + }else if(val[(short)(startOff + index)] < 0){ break; } index++; // index will be equal to len if value is 0. @@ -222,34 +261,36 @@ 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) + &&(val[(short)(startOff + index)] >= 0)){ + 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){ - 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); } } @@ -258,7 +299,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..00ba0cd1 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMEnum.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMEnum.java @@ -18,45 +18,64 @@ import javacard.framework.ISO7816; import javacard.framework.ISOException; +import javacard.framework.Util; public class KMEnum extends KMType { - private static short[] types = {HARDWARE_TYPE, KEY_FORMAT, KEY_DERIVATION_FUNCTION}; + private static KMEnum prototype; + private static short instPtr; + + private static short[] types = {HARDWARE_TYPE, KEY_FORMAT, KEY_DERIVATION_FUNCTION, + VERIFIED_BOOT_STATE, DEVICE_LOCKED}; 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[] { @@ -69,35 +88,34 @@ public static void create(KMEnum[] enumRefTable) { 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} }; } - 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. + + // 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) { @@ -105,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 @@ -123,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 30c40bad..d29903e1 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMEnumArrayTag.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMEnumArrayTag.java @@ -18,90 +18,118 @@ 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; + short enumIndex; + boolean validValue; + while (byteIndex < blob.length()) { + enumIndex = 0; + 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[] { 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 } }; } - 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 +139,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..b75563d6 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,69 +30,81 @@ 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 byte getValue() { + return heap[(short)(instPtr+TLV_HEADER_SIZE+4)]; } - public static void create(KMEnumTag[] enumTagRefTable) { + public static void create() { if (enums == null) { enums = new Object[] { 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} }; } - byte index = 0; - while (index < enumTagRefTable.length) { - enumTagRefTable[index] = new KMEnumTag(); - index++; - } } - // 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) { @@ -97,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 @@ -116,18 +131,11 @@ private static boolean validateEnum(short key, byte value) { 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); + 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)]; } - KMEnumTag tag = instance(key); - tag.val = val; - return tag; + 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/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..994a94cc 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMInteger.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMInteger.java @@ -18,117 +18,124 @@ 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; + public static final short UINT_32 = 4; + public 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 KMInteger instance() { - return repository.newInteger(); + 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 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 void create(KMInteger[] integerRefTable) { - byte index = 0; - while (index < integerRefTable.length) { - integerRefTable[index] = new KMInteger(); - index++; - } + 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 byte[] getValue() { - return val; + // Get the length of the integer + public short length() { + return Util.getShort(heap, (short) (instPtr + 1)); } - public KMInteger setValue(short val) { - this.val[2] = (byte) (val >> 8); - this.val[3] = (byte) (val & 0xFF); - return this; + // Get the buffer pointer in which blob is contained. + public byte[] getBuffer() { + return heap; } - public KMInteger setValue(byte[] val) { - this.val = val; - return this; + // Get the start of value + public short getStartOff() { + return (short) (instPtr + TLV_HEADER_SIZE); } - 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]); + public void getValue(byte[] dest, short destOff, short length){ + Util.arrayCopyNonAtomic(heap, (short)(instPtr+TLV_HEADER_SIZE), dest, destOff, length); } - - 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]; + 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(); } - // 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); - } + 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 df96e114..5f0e6731 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 isValidTag {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..0ac49e06 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,37 @@ private static boolean validateKey(short key) { return false; } - public KMInteger getValue() { - return this.val; - } - - public KMIntegerTag setValue(KMInteger val) { - this.val = val; - return this; + // 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; + } + return false; } - public KMIntegerTag asULong() { - tagType = KMType.ULONG_TAG; - return this; + 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 KMIntegerTag asDate() { - tagType = KMType.DATE_TAG; - return this; + 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/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..d3bf5af1 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java @@ -16,53 +16,260 @@ 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 KMKeyParameters() { - init(); + 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; + + private KMKeyParameters() {} + + private static KMKeyParameters proto(short ptr) { + if (prototype == null) prototype = new KMKeyParameters(); + instPtr = ptr; + return prototype; + } + + 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); } - @Override - public void init() { - vals = null; + 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 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 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 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 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; } - public static KMKeyParameters instance(KMArray vals) { - KMKeyParameters inst = repository.newKeyParameters(); - inst.vals = vals; - return inst; + // 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)); } - public static void create(KMKeyParameters[] keyParametersRefTable) { + // 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; - while (index < keyParametersRefTable.length) { - keyParametersRefTable[index] = new KMKeyParameters(); + 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 KMArray getVals() { - return vals; + 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 0ee85ff1..8ea7ad74 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -19,11 +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; /** @@ -35,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; @@ -46,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; @@ -68,28 +75,64 @@ 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[] 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 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; + // 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(); register(); @@ -103,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(); } /** @@ -113,7 +156,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 +170,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 +179,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 +192,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)) { @@ -169,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); @@ -176,283 +218,1298 @@ 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); } } // Process the apdu try { // Handle the command - handle(apduIns, apdu); - } catch (CardRuntimeException exception) { - if (!(KMException.handle(exception.getReason()))) { - CardRuntimeException.throwIt(exception.getReason()); + 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; + case INS_SET_BOOT_PARAMS_CMD: + processSetBootParamsCmd(apdu); + break; + default: + ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); } + } catch (KMException exception) { + sendError(apdu, exception.reason); + exception.clear(); } 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) { + public static void sendOutgoing(APDU apdu) { + if (bufferLength > MAX_IO_LENGTH) { ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } // Send data apdu.setOutgoing(); - apdu.setOutgoingLength(srcLength); - apdu.sendBytesLong(srcBuffer, (short) 0, srcLength); + // 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 short receiveIncoming(byte[] destBuffer, APDU apdu) { - // Initialize source + public static 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(); + short index = bufferStartOffset; + // Receive data + if (bufferLength > MAX_IO_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(); - // Argument 2 - KMEnum keyFormat = KMEnum.instance().setType(KMType.KEY_FORMAT); - // Argument 3 - KMByteBlob keyBlob = KMByteBlob.instance(); - // Array of expected arguments - KMArray argsProto = - KMArray.instance((short) 3) - .add((short) 0, keyparams) - .add((short) 1, keyFormat) - .add((short) 2, keyBlob); + // Arguments + short keyparams = KMKeyParameters.exp(); + short keyFormat = KMEnum.instance(KMType.KEY_FORMAT); + short keyBlob = KMByteBlob.exp(); + 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); + // 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; + } } - // 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)); + // Encode the response - actual bufferLength is 86 + bufferLength = encoder.encode(respPtr, buffer, bufferStartOffset); + // 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); + KMException.throwIt(KMError.INVALID_ARGUMENT); } - // Get existing entropy pool. - byte[] entPool = KMUtil.getEntropyPool(); - // Create new temporary pool. - byte[] heapRef = repository.getByteHeapRef(); - short poolStart = repository.newByteArray((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 processGetHmacSharingParamCmd(APDU apdu) {} + + private void processVerifyAuthenticationCmd(APDU apdu) {} + + private void processDestroyAttIdsCmd(APDU apdu) {} + + private void processComputeSharedHmacCmd(APDU apdu) {} + + private void processDeleteAllKeysCmd(APDU apdu) {} + + private void processDeleteKeyCmd(APDU apdu) {} + + private void processUpgradeKeyCmd(APDU apdu) {} + + private void processAttestKeyCmd(APDU apdu) {} + + private void processExportKeyCmd(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 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 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; } - // Nothing to return - return 0; + // update the key parameters list + updateKeyParameters(scratchPad, tmpVariables[4]); + // validate TDES Key parameters + validateTDESKey(scratchPad); + + data[KEY_BLOB] = KMArray.instance((short)4); } - private short processAbortOperationCmd(APDU apdu) { - return 0; + 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 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]); } - private short processFinishOperationCmd(APDU apdu) { - return 0; + // 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 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 short processUpdateOperationCmd(APDU apdu) { - return 0; + // 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 short processBeginOperationCmd(APDU apdu) { - return 0; + 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 short processGetKeyCharacteristicsCmd(APDU apdu) { - return 0; + 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 short processGetHmacSharingParamCmd(APDU apdu) { - return 0; + 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 short processVerifyAuthenticationCmd(APDU apdu) { - return 0; + 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 short processDestroyAttIdsCmd(APDU apdu) { - return 0; + 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 short processComputeSharedHmacCmd(APDU apdu) { - return 0; + 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 short processDeleteAllKeysCmd(APDU apdu) { - return 0; + 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 short processDeleteKeyCmd(APDU apdu) { - return 0; + 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 short processUpgradeKeyCmd(APDU apdu) { - return 0; + 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 short processAttestKeyCmd(APDU apdu) { - return 0; + 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 short processExportKeyCmd(APDU apdu) { - return 0; + 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 short processImportWrappedKeyCmd(APDU apdu) { - return 0; + 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 short processImportKeyCmd(APDU apdu) { - return 0; + 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 short processGenerateKey(APDU apdu) { - return 0; + 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 b71aea01..8e0f01c3 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMRepository.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMRepository.java @@ -20,454 +20,169 @@ 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 { - 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; + 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 = 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 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(){ - initialize(); - } - - private void initialize() { - // Initialize buffers and context. - JCSystem.beginTransaction(); - encoder = new KMEncoder(); - decoder = new KMDecoder(); - operationStateTable = new KMOperationState[4]; - // 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]; - + public KMRepository(byte[] masterKey) { + heap = JCSystem.makeTransientByteArray(HEAP_SIZE, JCSystem.CLEAR_ON_RESET); + 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 < INT_TABLE_SIZE) { - uint32Array[index] = new byte[INT_SIZE]; - uint64Array[index] = new byte[LONG_SIZE]; + while (index < MAX_BLOB_STORAGE) { + authTagRepo[index] = new byte[AES_GCM_AUTH_TAG_LENGTH]; index++; } - - JCSystem.commitTransaction(); - } - - public KMEncoder getEncoder() { - return encoder; - } - - public KMDecoder getDecoder() { - return decoder; + 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() { - 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() { + public byte[] getMasterKeySecret() { 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. + public short alloc(short length) { + if (((short) (heapIndex + length)) > heap.length) { ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } - KMIntegerTag ret = intTagRefTable[intTagRefIndex]; - intTagRefIndex++; - return ret; + heapIndex += length; + return (short) (heapIndex - length); } - 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 byte[] getHeap() { + return heap; } - 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. - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + 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++; } - KMVerificationToken ret = verTokenRefTable[verTokenRefIndex]; - verTokenRefIndex++; - return ret; - } - - public KMOperationState newOperationState() { - if (opStateRefIndex >= opStateRefTable.length) { - // TODO this is placeholder exception value. - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + // This should never happen + if (index >= repository.MAX_BLOB_STORAGE){ + ISOException.throwIt(ISO7816.SW_BYTES_REMAINING_00); } - KMOperationState ret = operationStateTable[opStateRefIndex]; - opStateRefIndex++; - return ret; + 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 void releaseOperationState(KMOperationState state){ - opStateRefIndex--; - if(opStateRefIndex <0){ - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + 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++; } - opStateRefTable[opStateRefIndex] = state; - } - public KMVector newVector() { - if (vectorRefIndex >= vectorRefTable.length) { - // TODO this is placeholder exception value. - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + if (index >= MAX_BLOB_STORAGE) { + ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED); } - KMVector ret = vectorRefTable[vectorRefIndex]; - vectorRefIndex++; - return ret; + JCSystem.beginTransaction(); + Util.arrayCopy( + zeroTag, (short) 0, (byte[]) (repository.authTagRepo[index]), (short) 0, AES_GCM_AUTH_TAG_LENGTH); + repository.keyBlobCount--; + JCSystem.commitTransaction(); } - public KMEnum newEnum() { - if (enumRefIndex >= enumRefTable.length) { - // TODO this is placeholder exception value. - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + 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++; } - 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); + if (index >= MAX_BLOB_STORAGE) { + return false; } - typeRefIndex += length; - return (short) (typeRefIndex - length); + return true; } - 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); - } } 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/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..1e023647 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 @@ -56,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. @@ -88,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; @@ -103,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; @@ -225,12 +263,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));} - public abstract short length(); + 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; + } + + 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..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,42 +73,339 @@ 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 - KMArray cmd = makeProvisionCmd(); + 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); //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,9 +416,9 @@ 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); + short actualLen = enc.encode(cmd, buf, (short) 0); CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x18, 0x40, 0x00, buf, 0, actualLen); //print(commandAPDU.getBytes()); @@ -107,8 +427,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