diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java index 41f468ed..4c283770 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java @@ -125,6 +125,23 @@ public class KMAndroidSEProvider implements KMSEProvider { private static final short CERT_EXPIRY_OFFSET = (short) (CERT_ISSUER_OFFSET + KMConfigurations.CERT_ISSUER_MAX_SIZE); + public static final short MAX_OPERATION_INSTANCES = 4; + private static final short HMAC_MAX_OPERATION_INSTANCES = 8; + + public static final byte AES_128 = 0x04; + public static final byte AES_256 = 0x05; + //Resource type constants + public static final byte RESOURCE_TYPE_CRYPTO = 0x00; + public static final byte RESOURCE_TYPE_KEY = 0x01; + + final byte[] KEY_ALGS = { + AES_128, + AES_256, + KMType.DES, + KMType.RSA, + KMType.EC, + KMType.HMAC}; + private static final byte[] CIPHER_ALGS = { Cipher.ALG_AES_BLOCK_128_CBC_NOPAD, Cipher.ALG_AES_BLOCK_128_ECB_NOPAD, @@ -173,8 +190,11 @@ public class KMAndroidSEProvider implements KMSEProvider { private Object[] sigPool; // KMOperationImpl pool private Object[] operationPool; - // Hmac signer pool which is used to support TRUSTED_CONFIRMATION_REQUIRED tag. - private Object[] hmacSignOperationPool; + + //Hmac signer pool which is used to support TRUSTED_CONFIRMATION_REQUIRED tag. + private Object[] hmacSignOperationPool; + + private Object[] keysPool; private Signature kdf; @@ -216,16 +236,19 @@ public KMAndroidSEProvider() { cipherPool = new Object[(short) (CIPHER_ALGS.length * 4)]; // Extra 4 algorithms are used to support TRUSTED_CONFIRMATION_REQUIRED feature. sigPool = new Object[(short) ((SIG_ALGS.length * 4) + 4)]; - operationPool = new Object[4]; - - //maintain seperate operation pool for hmac signer used to support trusted confirmation - hmacSignOperationPool = new Object[4]; + operationPool = new Object[MAX_OPERATION_INSTANCES]; + hmacSignOperationPool = new Object[MAX_OPERATION_INSTANCES]; + // Reserve (KEY_ALGS.length * 4) + 4) size of key pool + // Extra 4 keys for TRUSTED_CONFIRMATION_REQUIRED feature. + keysPool = new Object[(short) ((KEY_ALGS.length * 4) + 4)]; + // Creates an instance of each cipher algorithm once. initializeCipherPool(); // Creates an instance of each signature algorithm once. initializeSigPool(); initializeOperationPool(); initializeHmacSignOperationPool(); + initializeKeysPool(); //RsaOAEP Decipher rsaOaepDecipher = new KMRsaOAEPEncoding(KMRsaOAEPEncoding.ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA1); @@ -301,15 +324,15 @@ private boolean isSignerAlgorithm(byte alg) { private void initializeOperationPool() { short index = 0; - while (index < 4) { + while (index < MAX_OPERATION_INSTANCES) { operationPool[index] = new KMOperationImpl(); index++; } } - + private void initializeHmacSignOperationPool() { short index = 0; - while (index < 4) { + while (index < MAX_OPERATION_INSTANCES) { hmacSignOperationPool[index] = new KMOperationImpl(); index++; } @@ -324,6 +347,14 @@ private void initializeSigPool() { } } + private void initializeKeysPool() { + short index = 0; + while (index < KEY_ALGS.length) { + keysPool[index] = createKeyObjectInstance(KEY_ALGS[index]); + index++; + } + } + private Signature getSignatureInstance(byte alg) { if (KMRsa2048NoDigestSignature.ALG_RSA_SIGN_NOPAD == alg || KMRsa2048NoDigestSignature.ALG_RSA_PKCS1_NODIGEST == alg) { @@ -388,11 +419,11 @@ private Cipher getCipherInstanceFromPool(byte alg) { return (Cipher) getInstanceFromPool(cipherPool, alg); } - private boolean isResourceBusy(Object obj) { + private boolean isResourceBusy(Object obj, byte resourceType) { short index = 0; - while (index < MAX_OPERATIONS) { - if (((KMOperationImpl) operationPool[index]).isResourceMatches(obj) - || ((KMOperationImpl) hmacSignOperationPool[index]).isResourceMatches(obj)) { + while (index < MAX_OPERATION_INSTANCES) { + if (((KMOperationImpl) operationPool[index]).isResourceMatches(obj, resourceType) + || ((KMOperationImpl) hmacSignOperationPool[index]).isResourceMatches(obj, resourceType)) { return true; } index++; @@ -435,7 +466,7 @@ private Object getInstanceFromPool(Object[] pool, byte alg) { } if ((isCipher && (alg == ((Cipher) pool[index]).getAlgorithm())) || ((isSigner && (alg == ((Signature) pool[index]).getAlgorithm())))) { - if (!isResourceBusy(pool[index])) { + if (!isResourceBusy(pool[index], RESOURCE_TYPE_CRYPTO)) { return pool[index]; } instanceCount++; @@ -444,6 +475,47 @@ private Object getInstanceFromPool(Object[] pool, byte alg) { } return null; } + + public KMKeyObject getKeyObjectFromPool(byte algo, short secretLength) { + KMKeyObject keyObject = null; + short maxOperations = MAX_OPERATION_INSTANCES; + if (KMType.HMAC == algo) { + maxOperations = HMAC_MAX_OPERATION_INSTANCES; + } + if(algo == KMType.AES) { + if (secretLength == 16) { + algo = AES_128; + } else if (secretLength == 32) { + algo = AES_256; + } else { + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + } + short index = 0; + short usageCount = 0; + while (index < keysPool.length) { + if (usageCount >= maxOperations) { + KMException.throwIt(KMError.TOO_MANY_OPERATIONS); + } + if (keysPool[index] == null) { + keyObject = createKeyObjectInstance(algo); + JCSystem.beginTransaction(); + keysPool[index] = keyObject; + JCSystem.commitTransaction(); + break; + } + keyObject = (KMKeyObject) keysPool[index]; + if (algo == keyObject.getAlgorithm()) { + // Check if the Object instance is not busy and free to use. + if (!isResourceBusy(keyObject, RESOURCE_TYPE_KEY)) { + break; + } + usageCount++; + } + index++; + } + return keyObject; + } public AESKey createAESKey(short keysize) { try { @@ -915,20 +987,15 @@ private byte mapCipherAlg(byte alg, byte padding, byte blockmode, byte digest) { public Cipher createSymmetricCipher(short alg, short purpose, short blockMode, short padding, byte[] secret, short secretStart, - short secretLength, byte[] ivBuffer, short ivStart, short ivLength) { - Key key = null; + short secretLength, byte[] ivBuffer, short ivStart, short ivLength, KMKeyObject keyObject) { + Key key = (Key) keyObject.getKeyObjectInstance(); Cipher symmCipher = null; switch (secretLength) { - case 32: - key = aesKeys[KEYSIZE_256_OFFSET]; - ((AESKey) key).setKey(secret, secretStart); - break; - case 16: - key = aesKeys[KEYSIZE_128_OFFSET]; + case 16: + case 32: ((AESKey) key).setKey(secret, secretStart); break; case 24: - key = triDesKey; ((DESKey) key).setKey(secret, secretStart); break; default: @@ -963,8 +1030,9 @@ public Cipher createSymmetricCipher(short alg, short purpose, } private Signature createHmacSignerVerifier(short purpose, short digest, - byte[] secret, short secretStart, short secretLength) { - HMACKey key = createHMACKey(secret, secretStart, secretLength); + byte[] secret, short secretStart, short secretLength, KMKeyObject keyObject) { + HMACKey key = (HMACKey) keyObject.getKeyObjectInstance(); + key.setKey(secret, secretStart, secretLength); return createHmacSignerVerifier(purpose, digest, key); } @@ -984,14 +1052,17 @@ public KMOperation initSymmetricOperation(byte purpose, byte alg, short keyLength, byte[] ivBuf, short ivStart, short ivLength, short macLength) { KMOperationImpl opr = null; + KMKeyObject keyObject = null; switch (alg) { case KMType.AES: case KMType.DES: + keyObject = getKeyObjectFromPool(alg, keyLength); Cipher cipher = createSymmetricCipher(alg, purpose, blockMode, padding, - keyBuf, keyStart, keyLength, ivBuf, ivStart, ivLength); + keyBuf, keyStart, keyLength, ivBuf, ivStart, ivLength, keyObject); opr = getOperationInstanceFromPool(); // Convert macLength to bytes macLength = (short) (macLength / 8); + opr.setKeyObject(keyObject); opr.setCipher(cipher); opr.setCipherAlgorithm(alg); opr.setBlockMode(blockMode); @@ -1000,9 +1071,11 @@ public KMOperation initSymmetricOperation(byte purpose, byte alg, opr.setMacLength(macLength); break; case KMType.HMAC: + keyObject = getKeyObjectFromPool(alg, keyLength); Signature signerVerifier = createHmacSignerVerifier(purpose, digest, - keyBuf, keyStart, keyLength); + keyBuf, keyStart, keyLength, keyObject); opr = getOperationInstanceFromPool(); + opr.setKeyObject(keyObject); opr.setMode(purpose); opr.setSignature(signerVerifier); break; @@ -1017,8 +1090,12 @@ public KMOperation initSymmetricOperation(byte purpose, byte alg, public KMOperation initTrustedConfirmationSymmetricOperation(KMComputedHmacKey computedHmacKey) { KMOperationImpl opr = null; KMHmacKey key = (KMHmacKey) computedHmacKey; - Signature signerVerifier = createHmacSignerVerifier(KMType.VERIFY, KMType.SHA2_256, key.getKey()); + short len = key.getKey(tmpArray, (short) 0); + KMKeyObject keyObject = getKeyObjectFromPool(KMType.HMAC, len); + Signature signerVerifier = createHmacSignerVerifier(KMType.VERIFY, KMType.SHA2_256, tmpArray, + (short) 0, len, keyObject); opr = getHmacSignOperationInstanceFromPool(); + opr.setKeyObject(keyObject); opr.setMode(KMType.VERIFY); opr.setSignature(signerVerifier); return opr; @@ -1026,7 +1103,7 @@ public KMOperation initTrustedConfirmationSymmetricOperation(KMComputedHmacKey c public Signature createRsaSigner(short digest, short padding, byte[] secret, short secretStart, short secretLength, byte[] modBuffer, short modOff, - short modLength) { + short modLength, KMKeyObject keyObject) { byte alg = mapSignature256Alg(KMType.RSA, (byte) padding, (byte) digest); byte opMode; if (padding == KMType.PADDING_NONE @@ -1036,7 +1113,7 @@ public Signature createRsaSigner(short digest, short padding, byte[] secret, opMode = Signature.MODE_SIGN; } Signature rsaSigner = getSignatureInstanceFromPool(alg); - RSAPrivateKey key = (RSAPrivateKey) rsaKeyPair.getPrivate(); + RSAPrivateKey key = (RSAPrivateKey) ((KeyPair)(keyObject.getKeyObjectInstance())).getPrivate(); key.setExponent(secret, secretStart, secretLength); key.setModulus(modBuffer, modOff, modLength); rsaSigner.init(key, opMode); @@ -1045,10 +1122,10 @@ public Signature createRsaSigner(short digest, short padding, byte[] secret, public Cipher createRsaDecipher(short padding, short digest, byte[] secret, short secretStart, short secretLength, byte[] modBuffer, short modOff, - short modLength) { + short modLength, KMKeyObject keyObject) { byte cipherAlg = mapCipherAlg(KMType.RSA, (byte) padding, (byte) 0, (byte) digest); Cipher rsaCipher = getCipherInstanceFromPool(cipherAlg); - RSAPrivateKey key = (RSAPrivateKey) rsaKeyPair.getPrivate(); + RSAPrivateKey key = (RSAPrivateKey) ((KeyPair)(keyObject.getKeyObjectInstance())).getPrivate(); key.setExponent(secret, secretStart, secretLength); key.setModulus(modBuffer, modOff, modLength); rsaCipher.init(key, Cipher.MODE_DECRYPT); @@ -1056,10 +1133,10 @@ public Cipher createRsaDecipher(short padding, short digest, byte[] secret, } public Signature createEcSigner(short digest, byte[] secret, - short secretStart, short secretLength) { + short secretStart, short secretLength, KMKeyObject keyObject) { byte alg = mapSignature256Alg(KMType.EC, (byte) 0, (byte) digest); Signature ecSigner = null; - ECPrivateKey key = (ECPrivateKey) ecKeyPair.getPrivate(); + ECPrivateKey key = (ECPrivateKey) ((KeyPair)(keyObject.getKeyObjectInstance())).getPrivate(); key.setS(secret, secretStart, secretLength); ecSigner = getSignatureInstanceFromPool(alg); ecSigner.init(key, Signature.MODE_SIGN); @@ -1072,21 +1149,26 @@ public KMOperation initAsymmetricOperation(byte purpose, byte alg, short privKeyLength, byte[] pubModBuf, short pubModStart, short pubModLength) { KMOperationImpl opr = null; + KMKeyObject keyObject = null; if (alg == KMType.RSA) { switch (purpose) { case KMType.SIGN: + keyObject = getKeyObjectFromPool(alg, privKeyLength); Signature signer = createRsaSigner(digest, padding, privKeyBuf, - privKeyStart, privKeyLength, pubModBuf, pubModStart, pubModLength); + privKeyStart, privKeyLength, pubModBuf, pubModStart, pubModLength, keyObject); opr = getOperationInstanceFromPool(); + opr.setKeyObject(keyObject); opr.setSignature(signer); opr.setCipherAlgorithm(alg); opr.setPaddingAlgorithm(padding); opr.setMode(purpose); break; case KMType.DECRYPT: + keyObject = getKeyObjectFromPool(alg, privKeyLength); Cipher decipher = createRsaDecipher(padding, digest, privKeyBuf, - privKeyStart, privKeyLength, pubModBuf, pubModStart, pubModLength); + privKeyStart, privKeyLength, pubModBuf, pubModStart, pubModLength, keyObject); opr = getOperationInstanceFromPool(); + opr.setKeyObject(keyObject); opr.setCipher(decipher); opr.setCipherAlgorithm(alg); opr.setPaddingAlgorithm(padding); @@ -1099,9 +1181,11 @@ public KMOperation initAsymmetricOperation(byte purpose, byte alg, } else if (alg == KMType.EC) { switch (purpose) { case KMType.SIGN: + keyObject = getKeyObjectFromPool(alg, privKeyLength); Signature signer = createEcSigner(digest, privKeyBuf, privKeyStart, - privKeyLength); + privKeyLength, keyObject); opr = getOperationInstanceFromPool(); + opr.setKeyObject(keyObject); opr.setMode(purpose); opr.setSignature(signer); break; @@ -1394,5 +1478,40 @@ public short messageDigest256(byte[] inBuff, short inOffset, } return len; } - + + private KMKeyObject createKeyObjectInstance(byte alg) { + Object keyObject = null; + switch (alg) { + case AES_128: + keyObject = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES_TRANSIENT_RESET, + KeyBuilder.LENGTH_AES_128, false); + break; + case AES_256: + keyObject = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES_TRANSIENT_RESET, + KeyBuilder.LENGTH_AES_256, false); + break; + case KMType.DES: + keyObject = (DESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_DES_TRANSIENT_RESET, + KeyBuilder.LENGTH_DES3_3KEY, false); + break; + case KMType.RSA: + keyObject = new KeyPair(KeyPair.ALG_RSA, KeyBuilder.LENGTH_RSA_2048); + break; + case KMType.EC: + keyObject = new KeyPair(KeyPair.ALG_EC_FP, KeyBuilder.LENGTH_EC_FP_256); + initECKey((KeyPair) keyObject); + break; + case KMType.HMAC: + keyObject = (HMACKey) KeyBuilder.buildKey(KeyBuilder.TYPE_HMAC_TRANSIENT_RESET, + (short) 512, false); + break; + default: + KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); + } + KMKeyObject ptr = new KMKeyObject(); + ptr.setKeyObjectData(alg, keyObject); + return ptr; + } + + } diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMOperationImpl.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMOperationImpl.java index de304d8f..57cc0f61 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMOperationImpl.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMOperationImpl.java @@ -38,7 +38,7 @@ public class KMOperationImpl implements KMOperation { public KMOperationImpl() { parameters = JCSystem.makeTransientShortArray((short) 6, JCSystem.CLEAR_ON_RESET); - operationInst = JCSystem.makeTransientObjectArray((short) 1, JCSystem.CLEAR_ON_RESET); + operationInst = JCSystem.makeTransientObjectArray((short) 2, JCSystem.CLEAR_ON_RESET); } public short getMode() { @@ -82,19 +82,28 @@ public void setCipherAlgorithm(short cipherAlg) { } public void setCipher(Cipher cipher) { - operationInst[0] = cipher; + operationInst[KMAndroidSEProvider.RESOURCE_TYPE_CRYPTO] = cipher; } public void setSignature(Signature signer) { - operationInst[0] = signer; + operationInst[KMAndroidSEProvider.RESOURCE_TYPE_CRYPTO] = signer; } - public boolean isResourceMatches(Object object) { - return operationInst[0] == object; + public void setKeyObject(KMKeyObject keyObject) { + operationInst[KMAndroidSEProvider.RESOURCE_TYPE_KEY] = keyObject; + } + + public KMKeyObject getKeyObject() { + return (KMKeyObject) operationInst[KMAndroidSEProvider.RESOURCE_TYPE_KEY]; + } + + public boolean isResourceMatches(Object object, byte resourceType) { + return operationInst[resourceType] == object; } private void reset() { - operationInst[0] = null; + operationInst[KMAndroidSEProvider.RESOURCE_TYPE_CRYPTO] = null; + operationInst[KMAndroidSEProvider.RESOURCE_TYPE_KEY] = null; parameters[MAC_LENGTH_OFFSET] = KMType.INVALID_VALUE; parameters[AES_GCM_UPDATE_LEN_OFFSET] = 0; parameters[BLOCK_MODE_OFFSET] = KMType.INVALID_VALUE;; @@ -106,7 +115,7 @@ private void reset() { @Override public short update(byte[] inputDataBuf, short inputDataStart, short inputDataLength, byte[] outputDataBuf, short outputDataStart) { - short len = ((Cipher) operationInst[0]).update(inputDataBuf, inputDataStart, inputDataLength, + short len = ((Cipher) operationInst[KMAndroidSEProvider.RESOURCE_TYPE_CRYPTO]).update(inputDataBuf, inputDataStart, inputDataLength, outputDataBuf, outputDataStart); if (parameters[CIPHER_ALG_OFFSET] == KMType.AES && parameters[BLOCK_MODE_OFFSET] == KMType.GCM) { // Every time Block size data is stored as intermediate result. @@ -118,7 +127,7 @@ public short update(byte[] inputDataBuf, short inputDataStart, @Override public short update(byte[] inputDataBuf, short inputDataStart, short inputDataLength) { - ((Signature) operationInst[0]).update(inputDataBuf, inputDataStart, inputDataLength); + ((Signature) operationInst[KMAndroidSEProvider.RESOURCE_TYPE_CRYPTO]).update(inputDataBuf, inputDataStart, inputDataLength); return 0; } @@ -126,7 +135,7 @@ public short update(byte[] inputDataBuf, short inputDataStart, public short finish(byte[] inputDataBuf, short inputDataStart, short inputDataLen, byte[] outputDataBuf, short outputDataStart) { byte[] tmpArray = KMAndroidSEProvider.getInstance().tmpArray; - Cipher cipher = (Cipher) operationInst[0]; + Cipher cipher = (Cipher) operationInst[KMAndroidSEProvider.RESOURCE_TYPE_CRYPTO]; short cipherAlg = parameters[CIPHER_ALG_OFFSET]; short blockMode = parameters[BLOCK_MODE_OFFSET]; short mode = parameters[OPER_MODE_OFFSET]; @@ -211,7 +220,7 @@ public short sign(byte[] inputDataBuf, short inputDataStart, short inputDataLength, byte[] signBuf, short signStart) { short len = 0; try { - len = ((Signature) operationInst[0]).sign(inputDataBuf, inputDataStart, inputDataLength, + len = ((Signature) operationInst[KMAndroidSEProvider.RESOURCE_TYPE_CRYPTO]).sign(inputDataBuf, inputDataStart, inputDataLength, signBuf, signStart); } finally { reset(); @@ -224,7 +233,7 @@ public boolean verify(byte[] inputDataBuf, short inputDataStart, short inputDataLength, byte[] signBuf, short signStart, short signLength) { boolean ret = false; try { - ret = ((Signature) operationInst[0]).verify(inputDataBuf, inputDataStart, inputDataLength, + ret = ((Signature) operationInst[KMAndroidSEProvider.RESOURCE_TYPE_CRYPTO]).verify(inputDataBuf, inputDataStart, inputDataLength, signBuf, signStart, signLength); } finally { reset(); @@ -237,10 +246,10 @@ public void abort() { // Few simulators does not reset the Hmac signer instance on init so as // a workaround to reset the hmac signer instance in case of abort/failure of the operation // the corresponding sign / verify function is called. - if (operationInst[0] != null) { + if (operationInst[KMAndroidSEProvider.RESOURCE_TYPE_CRYPTO] != null) { if ((parameters[OPER_MODE_OFFSET] == KMType.SIGN || parameters[OPER_MODE_OFFSET] == KMType.VERIFY) && - (((Signature) operationInst[0]).getAlgorithm() == Signature.ALG_HMAC_SHA_256)) { - Signature signer = (Signature) operationInst[0]; + (((Signature) operationInst[KMAndroidSEProvider.RESOURCE_TYPE_CRYPTO]).getAlgorithm() == Signature.ALG_HMAC_SHA_256)) { + Signature signer = (Signature) operationInst[KMAndroidSEProvider.RESOURCE_TYPE_CRYPTO]; try { if (parameters[OPER_MODE_OFFSET] == KMType.SIGN) { signer.sign(EMPTY, (short) 0, (short) 0, EMPTY, (short) 0); @@ -257,7 +266,7 @@ public void abort() { @Override public void updateAAD(byte[] dataBuf, short dataStart, short dataLength) { - ((AEADCipher) operationInst[0]).updateAAD(dataBuf, dataStart, dataLength); + ((AEADCipher) operationInst[KMAndroidSEProvider.RESOURCE_TYPE_CRYPTO]).updateAAD(dataBuf, dataStart, dataLength); } @Override diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java index e41663ec..7be6bce1 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java @@ -352,10 +352,12 @@ public static void add(byte[] buf, short op1, short op2, short result) { byte index = 7; byte carry = 0; short tmp; + short val1 = 0; + short val2 = 0; while (index >= 0) { - tmp = - (short) ((buf[(short) (op1 + index)] & 0xFF) + - (buf[(short) (op2 + index)] & 0xFF) + carry); + val1 = (short) (buf[(short) (op1 + index)] & 0x00FF); + val2 = (short) (buf[(short) (op2 + index)] & 0x00FF); + tmp = (short) (val1 + val2 + carry); carry = 0; if (tmp > 255) { carry = 1; // max unsigned byte value is 255 diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMUtils.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMUtils.java index 88b7b4d1..a572da17 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMUtils.java +++ b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMUtils.java @@ -350,10 +350,12 @@ public static void add(byte[] buf, short op1, short op2, short result) { byte index = 7; byte carry = 0; short tmp; + short val1 = 0; + short val2 = 0; while (index >= 0) { - tmp = - (short) ((buf[(short) (op1 + index)] & 0xFF) + - (buf[(short) (op2 + index)] & 0xFF) + carry); + val1 = (short) (buf[(short) (op1 + index)] & 0x00FF); + val2 = (short) (buf[(short) (op2 + index)] & 0x00FF); + tmp = (short) (val1 + val2 + carry); carry = 0; if (tmp > 255) { carry = 1; // max unsigned byte value is 255 diff --git a/Applet/src/com/android/javacard/keymaster/KMArray.java b/Applet/src/com/android/javacard/keymaster/KMArray.java index adf61723..bfa09269 100644 --- a/Applet/src/com/android/javacard/keymaster/KMArray.java +++ b/Applet/src/com/android/javacard/keymaster/KMArray.java @@ -92,12 +92,6 @@ public void add(short index, short objPtr) { (short) (instanceTable[KM_ARRAY_OFFSET] + TLV_HEADER_SIZE + ARRAY_HEADER_SIZE + (short) (index * 2)), objPtr); } - - public void deleteLastEntry() { - short len = length(); - Util.setShort(heap, (short) (instanceTable[KM_ARRAY_OFFSET] + TLV_HEADER_SIZE + 2), (short) (len -1)); - } - public short get(short index) { short len = length(); diff --git a/Applet/src/com/android/javacard/keymaster/KMDecoder.java b/Applet/src/com/android/javacard/keymaster/KMDecoder.java index 7bd0e6ec..6fe3189d 100644 --- a/Applet/src/com/android/javacard/keymaster/KMDecoder.java +++ b/Applet/src/com/android/javacard/keymaster/KMDecoder.java @@ -468,5 +468,22 @@ public short getCborBytesStartOffset(byte[] buf, short bufOffset, short bufLen) readMajorTypeWithPayloadLength(BYTES_TYPE); return scratchBuf[START_OFFSET]; } + + public short readKeyblobVersion(byte[] buf, short bufOffset, short bufLen) { + bufferRef[0] = buf; + scratchBuf[START_OFFSET] = bufOffset; + scratchBuf[LEN_OFFSET] = (short) (bufOffset + bufLen); + short arrayLen = readMajorTypeWithPayloadLength(ARRAY_TYPE); + if (arrayLen == 0) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + short version = KMType.INVALID_VALUE; + try { + version = decodeInteger(KMInteger.exp()); + } catch(Exception e) { + // Fail to decode Integer. It can happen if it is an old KeyBlob. + } + return version; + } } diff --git a/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java b/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java index 709b604d..bd6edf4a 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java @@ -234,9 +234,6 @@ public static short makeHwEnforced(short keyParamsPtr, byte origin, .instance(KMType.UINT_TAG, KMType.BOOT_PATCH_LEVEL, bootPatchObjPtr); Util.setShort(scratchPad, arrInd, bootPatchTag); arrInd += 2; - // Add custom tags at the end of the array. So it becomes easy to - // delete them when sending key characteristics back to HAL. - arrInd = addCustomTags(keyParamsPtr, scratchPad, arrInd); return createKeyParameters(scratchPad, (short) (arrInd / 2)); } @@ -276,11 +273,19 @@ public static short makeHidden(short keyParamsPtr, short rootOfTrustBlob, byte[] short appId = KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_ID, keyParamsPtr); if (appId != KMTag.INVALID_VALUE) { appId = KMByteTag.cast(appId).getValue(); + if (KMByteBlob.cast(appId).length() == 0) { + // Treat empty as INVALID. + return KMType.INVALID_VALUE; + } } short appData = KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_DATA, keyParamsPtr); if (appData != KMTag.INVALID_VALUE) { appData = KMByteTag.cast(appData).getValue(); + if (KMByteBlob.cast(appData).length() == 0) { + // Treat empty as INVALID. + return KMType.INVALID_VALUE; + } } return makeHidden(appId, appData, rootOfTrustBlob, scratchPad); } @@ -331,42 +336,30 @@ public static short createKeyParameters(byte[] ptrArr, short len) { return KMKeyParameters.instance(arrPtr); } - public static short addCustomTags(short keyParams, byte[] scratchPad, short offset) { + public static short makeCustomTags(short keyParams, byte[] scratchPad) { short index = 0; short tagPtr; + short offset = 0; short len = (short) customTags.length; short tagType; while (index < len) { tagType = customTags[(short) (index + 1)]; switch(tagType) { - case KMType.AUTH_TIMEOUT_MILLIS: - short authTimeOutTag = - KMKeyParameters.cast(keyParams).findTag(KMType.UINT_TAG, KMType.AUTH_TIMEOUT); - if (authTimeOutTag != KMType.INVALID_VALUE) { - tagPtr = createAuthTimeOutMillisTag(authTimeOutTag, scratchPad, offset); - Util.setShort(scratchPad, offset, tagPtr); - offset += 2; - } - break; - default: - break; + case KMType.AUTH_TIMEOUT_MILLIS: + short authTimeOutTag = + KMKeyParameters.cast(keyParams).findTag(KMType.UINT_TAG, KMType.AUTH_TIMEOUT); + if (authTimeOutTag != KMType.INVALID_VALUE) { + tagPtr = createAuthTimeOutMillisTag(authTimeOutTag, scratchPad, offset); + Util.setShort(scratchPad, offset, tagPtr); + offset += 2; + } + break; + default: + break; } index += 2; } - return offset; - } - - public void deleteCustomTags() { - short arrPtr = getVals(); - short index = (short) (customTags.length - 1); - short obj; - while (index >= 0) { - obj = findTag(customTags[(short) (index - 1)], customTags[index]); - if (obj != KMType.INVALID_VALUE) { - KMArray.cast(arrPtr).deleteLastEntry(); - } - index -= 2; - } + return createKeyParameters(scratchPad, (short) (offset / 2)); } public static short createAuthTimeOutMillisTag(short authTimeOutTag, byte[] scratchPad, short offset) { diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index 0eebb569..5a2a6f82 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -40,7 +40,7 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe public static final short MAX_LENGTH = (short) 0x2000; 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) 256; + private static final short MAX_AUTH_DATA_SIZE = (short) 512; private static final short DERIVE_KEY_INPUT_SIZE = (short) 256; private static final short POWER_RESET_MASK_FLAG = (short) 0x4000; // Magic number version @@ -139,7 +139,7 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe protected static final byte PROVISION_STATUS_PROVISIONING_LOCKED = 0x20; // Data Dictionary items - public static final byte DATA_ARRAY_SIZE = 30; + public static final byte DATA_ARRAY_SIZE = 31; 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; @@ -171,15 +171,30 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe public static final byte HW_TOKEN = 26; public static final byte VERIFICATION_TOKEN = 27; public static final byte SIGNATURE = 28; + public static final byte KEY_BLOB_VERSION_DATA_OFFSET = 29; + public static final byte CUSTOM_TAGS = 30; + // AddRngEntropy protected static final short MAX_SEED_SIZE = 2048; // 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; + public static final byte KEY_BLOB_VERSION_OFFSET = 0; + public static final byte KEY_BLOB_SECRET = 1; + public static final byte KEY_BLOB_NONCE = 2; + public static final byte KEY_BLOB_AUTH_TAG = 3; + public static final byte KEY_BLOB_KEYCHAR = 4; + public static final byte KEY_BLOB_CUSTOM_TAGS = 5; + public static final byte KEY_BLOB_PUB_KEY = 6; + + //KeyBlob array size constants. + public static final byte SYM_KEY_BLOB_SIZE_V1 = 6; + public static final byte ASYM_KEY_BLOB_SIZE_V1 = 7; + public static final byte SYM_KEY_BLOB_SIZE_V0 = 4; + public static final byte ASYM_KEY_BLOB_SIZE_V0 = 5; + // Key type constants + public static final byte SYM_KEY_TYPE = 0; + public static final byte ASYM_KEY_TYPE = 1; + // AES GCM constants private static final byte AES_GCM_AUTH_TAG_LENGTH = 16; private static final byte AES_GCM_NONCE_LENGTH = 12; @@ -190,6 +205,14 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe // Buffer constants. private static final short BUF_START_OFFSET = 0; private static final short BUF_LEN_OFFSET = 2; + + //KEYBLOB_CURRENT_VERSION goes into KeyBlob and will affect all + // the KeyBlobs if it is changed. please increment this + // version number whenever you change anything related to + // KeyBlob (structure, encryption algorithm etc). + public static final short KEYBLOB_CURRENT_VERSION = 1; + // KeyBlob Verion 1 constant. + public static final short KEYBLOB_VERSION_0 = 0; // Keymaster Applet attributes protected static byte keymasterState = ILLEGAL_STATE; @@ -968,13 +991,11 @@ private void processGetKeyCharacteristicsCmd(APDU apdu) { if (!KMByteBlob.cast(data[APP_DATA]).isValid()) { data[APP_DATA] = KMType.INVALID_VALUE; } - // Parse Key Blob - parseEncryptedKeyBlob(scratchPad); - // Check Version and Patch Level - checkVersionAndPatchLevel(scratchPad); - // Remove custom tags from key characteristics - short hwParams = KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).getHardwareEnforced(); - KMKeyParameters.cast(hwParams).deleteCustomTags(); + // Check if key requires upgrade. The KeyBlob is parsed inside isKeyUpgradeRequired + // function itself. + if (isKeyUpgradeRequired(data[KEY_BLOB], data[APP_ID], data[APP_DATA], scratchPad)) { + KMException.throwIt(KMError.KEY_REQUIRES_UPGRADE); + } // make response. tmpVariables[0] = KMArray.instance((short) 2); KMArray.cast(tmpVariables[0]).add((short) 0, buildErrorStatus(KMError.OK)); @@ -1011,41 +1032,6 @@ private void processDeleteAllKeysCmd(APDU apdu) { } private void processDeleteKeyCmd(APDU apdu) { - - // Receive the incoming request fully from the master. - receiveIncoming(apdu); - // Arguments - short argsProto = KMArray.instance((short) 1); - KMArray.cast(argsProto).add((short) 0, KMByteBlob.exp()); - // Decode the argument - short args = decoder.decode(argsProto, (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], bufferProp[BUF_LEN_OFFSET]); - //reclaim memory - repository.reclaimMemory(bufferProp[BUF_LEN_OFFSET]); - - // Process - data[KEY_BLOB] = KMArray.cast(args).get((short) 0); - 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()); - try { - 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()); - } catch (ISOException e) { - // As per VTS, deleteKey should return KMError.OK but in case if - // input is empty then VTS accepts UNIMPLEMENTED errorCode as well. - KMException.throwIt(KMError.UNIMPLEMENTED); - } - tmpVariables[0] = KMArray.cast(data[KEY_BLOB]).length(); - if (tmpVariables[0] < 4) { - KMException.throwIt(KMError.INVALID_KEY_BLOB); - } // Send ok sendError(apdu, KMError.OK); } @@ -1207,35 +1193,39 @@ private void processUpgradeKeyCmd(APDU apdu) { //reclaim memory repository.reclaimMemory(bufferProp[BUF_LEN_OFFSET]); - data[KEY_BLOB] = KMArray.cast(tmpVariables[2]).get((short) 0); + short keyBlob = KMArray.cast(tmpVariables[2]).get((short) 0); data[KEY_PARAMETERS] = KMArray.cast(tmpVariables[2]).get((short) 1); - tmpVariables[0] = - KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_ID, data[KEY_PARAMETERS]); - if (tmpVariables[0] != KMTag.INVALID_VALUE) { - data[APP_ID] = KMByteTag.cast(tmpVariables[0]).getValue(); - } - tmpVariables[0] = - KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_DATA, data[KEY_PARAMETERS]); - if (tmpVariables[0] != KMTag.INVALID_VALUE) { - data[APP_DATA] = KMByteTag.cast(tmpVariables[0]).getValue(); - } - // parse existing key blob - parseEncryptedKeyBlob(scratchPad); - boolean isKeyUpgradeRequired = false; - // Check if key requires upgrade. - isKeyUpgradeRequired |= isKeyUpgradeRequired(KMType.OS_VERSION, repository.getOsVersion()); - isKeyUpgradeRequired |= isKeyUpgradeRequired(KMType.OS_PATCH_LEVEL, repository.getOsPatch()); - isKeyUpgradeRequired |= isKeyUpgradeRequired(KMType.VENDOR_PATCH_LEVEL, repository.getVendorPatchLevel()); - isKeyUpgradeRequired |= isKeyUpgradeRequired(KMType.BOOT_PATCH_LEVEL, repository.getBootPatchLevel()); + short appId = getApplicationId(data[KEY_PARAMETERS]); + short appData = getApplicationData(data[KEY_PARAMETERS]); + // Check if the KeyBlob requires upgrade. The KeyBlob is parsed inside isKeyUpgradeRequired + // function itself, but if there is a difference in the KeyBlob version isKeyUpgradeRequired() + // does not parse the KeyBlob. + boolean isKeyUpgradeRequired = isKeyUpgradeRequired(keyBlob, appId, appData, scratchPad); if (isKeyUpgradeRequired) { // copy origin data[ORIGIN] = KMEnumTag.getValue(KMType.ORIGIN, data[HW_PARAMETERS]); + byte keyType = getKeyType(data[HW_PARAMETERS]); + switch (keyType) { + case ASYM_KEY_TYPE: + data[KEY_BLOB] = KMArray.instance(ASYM_KEY_BLOB_SIZE_V1); + KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PUB_KEY, data[PUB_KEY]); + break; + case SYM_KEY_TYPE: + data[KEY_BLOB] = KMArray.instance(SYM_KEY_BLOB_SIZE_V1); + break; + default: + KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); + } + // Update the system properties to the latest values and also re-create the KeyBlob's + // KeyCharacteristics to make sure all the values are up-to-date with the latest applet + // changes. + upgradeKeyBlobKeyCharacteristics(data[HW_PARAMETERS], data[SW_PARAMETERS], scratchPad); // create new key blob with current os version etc. createEncryptedKeyBlob(scratchPad); - } else { - data[KEY_BLOB] = KMByteBlob.instance((short) 0); - } + } else { + data[KEY_BLOB] = KMByteBlob.instance((short) 0); + } // prepare the response tmpVariables[0] = KMArray.instance((short) 2); KMArray.cast(tmpVariables[0]).add((short) 0, buildErrorStatus(KMError.OK)); @@ -1243,8 +1233,31 @@ private void processUpgradeKeyCmd(APDU apdu) { bufferProp[BUF_START_OFFSET] = repository.allocAvailableMemory(); // Encode the response - bufferProp[BUF_LEN_OFFSET] = encoder.encode(tmpVariables[0], (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET]); - sendOutgoing(apdu); + bufferProp[BUF_LEN_OFFSET] = encoder.encode(tmpVariables[0], (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET]); + sendOutgoing(apdu); + } + + // This function is only called from processUpgradeKey command. + // 1. Update the latest values of OSVersion, OSPatch, VendorPatch and BootPatch in the + // KeyBlob's KeyCharacteristics. + // 2. Re-create KeyBlob's KeyCharacteristics from HW_PARAMS to make sure we don't miss + // anything which happens in these functions makeSbEnforced and makeTeeEnforced in + // the future. Like validations. + // 3. No need to create Keystore Enforced list here as it is not required to be included in + // the KeyBlob's KeyCharacteristics. + // 4. No need to create KeyCharacteristics as upgradeKey does not require to return any + // KeyCharacteristics back. + private static void upgradeKeyBlobKeyCharacteristics(short hwParams, short swParams, byte[] scratchPad) { + short osVersion = repository.getOsVersion(); + short osPatch = repository.getOsPatch(); + short vendorPatch = repository.getVendorPatchLevel(); + short bootPatch = repository.getBootPatchLevel(); + data[HW_PARAMETERS] = KMKeyParameters.makeHwEnforced(hwParams, (byte) data[ORIGIN], + osVersion, osPatch, vendorPatch, bootPatch, scratchPad); + data[SW_PARAMETERS] = KMKeyParameters.makeSwEnforced(swParams, scratchPad); + data[KEY_CHARACTERISTICS] = KMKeyCharacteristics.instance(); + KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).setHardwareEnforced(data[HW_PARAMETERS]); + KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).setSoftwareEnforced(data[SW_PARAMETERS]); } private void processExportKeyCmd(APDU apdu) { @@ -1304,8 +1317,11 @@ private void processImportWrappedKeyCmd(APDU apdu) { if (data[APP_DATA] != KMTag.INVALID_VALUE) { data[APP_DATA] = KMByteTag.cast(tmpVariables[3]).getValue(); } - // parse the wrapping key blob - parseEncryptedKeyBlob(scratchPad); + // Check if key requires upgrade. The KeyBlob is parsed inside isKeyUpgradeRequired + // function itself. + if (isKeyUpgradeRequired(data[KEY_BLOB], data[APP_ID], data[APP_DATA], scratchPad)) { + KMException.throwIt(KMError.KEY_REQUIRES_UPGRADE); + } // check whether the wrapping key is RSA with purpose KEY_WRAP, padding RSA_OAEP and Digest // SHA2_256. if (KMEnumTag.getValue(KMType.ALGORITHM, data[HW_PARAMETERS]) != KMType.RSA) { @@ -1419,9 +1435,14 @@ private void processAttestKeyCmd(APDU apdu) { data[KEY_BLOB] = KMArray.cast(args).get((short) 0); data[KEY_PARAMETERS] = KMArray.cast(args).get((short) 1); - - // parse key blob - parseEncryptedKeyBlob(scratchPad); + short appId = getApplicationId(data[KEY_PARAMETERS]); + short appData = getApplicationData(data[KEY_PARAMETERS]); + + // Check if key requires upgrade. The KeyBlob is parsed inside isKeyUpgradeRequired + // function itself. + if (isKeyUpgradeRequired(data[KEY_BLOB], appId, appData, scratchPad)) { + KMException.throwIt(KMError.KEY_REQUIRES_UPGRADE); + } // This below code is added to pass one of the VTS 4.1 tests. tmpVariables[0] = KMKeyParameters.findTag( @@ -2258,18 +2279,13 @@ private void processBeginOperationCmd(APDU apdu) { data[KEY_PARAMETERS] = KMArray.cast(args).get((short) 2); data[KEY_BLOB] = KMArray.cast(args).get((short) 1); // Check for app id and app data. - data[APP_ID] = - KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_ID, data[KEY_PARAMETERS]); - data[APP_DATA] = - KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_DATA, data[KEY_PARAMETERS]); - if (data[APP_ID] != KMTag.INVALID_VALUE) { - data[APP_ID] = KMByteTag.cast(data[APP_ID]).getValue(); + data[APP_ID] = getApplicationId(data[KEY_PARAMETERS]); + data[APP_DATA] = getApplicationData(data[KEY_PARAMETERS]); + // Check if key requires upgrade. The KeyBlob is parsed inside isKeyUpgradeRequired + // function itself. + if (isKeyUpgradeRequired(data[KEY_BLOB], data[APP_ID], data[APP_DATA], scratchPad)) { + KMException.throwIt(KMError.KEY_REQUIRES_UPGRADE); } - if (data[APP_DATA] != KMTag.INVALID_VALUE) { - data[APP_DATA] = KMByteTag.cast(data[APP_DATA]).getValue(); - } - // Parse the encrypted blob and decrypt it. - parseEncryptedKeyBlob(scratchPad); // Authorize the begin operation and reserve op - data[OP_HANDLE] will have the handle. // It will also set data[IV] field if required. tmpVariables[0] = KMArray.cast(args).get((short) 0); @@ -2582,7 +2598,7 @@ private void authorizeAndBeginOperation(KMOperationState op, byte[] scratchPad) if (tmpVariables[0] != KMType.INVALID_VALUE && repository.getEarlyBootEndedStatus()) { KMException.throwIt(KMError.INVALID_KEY_BLOB); } - + // Authorize Caller Nonce - if caller nonce absent in key char and nonce present in // key params then fail if it is not a Decrypt operation data[IV] = KMType.INVALID_VALUE; @@ -2822,7 +2838,7 @@ private void authorizeUserSecureIdAuthTimeout(KMOperationState op, byte[] scratc } authTimeoutTagPtr = - KMKeyParameters.findTag(KMType.ULONG_TAG, KMType.AUTH_TIMEOUT_MILLIS, data[HW_PARAMETERS]); + KMKeyParameters.findTag(KMType.ULONG_TAG, KMType.AUTH_TIMEOUT_MILLIS, data[CUSTOM_TAGS]); if (authTimeoutTagPtr == KMType.INVALID_VALUE) { KMException.throwIt(KMError.INVALID_KEY_BLOB); } @@ -3061,6 +3077,8 @@ private void importKey(APDU apdu, byte[] scratchPad, byte keyFormat) { KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); break; } + // make key characteristics - returns key characteristics in data[KEY_CHARACTERISTICS] + makeKeyCharacteristics(scratchPad); // create key blob createEncryptedKeyBlob(scratchPad); @@ -3165,7 +3183,7 @@ private void importECKeys(byte[] scratchPad, byte keyFormat) { updateKeyParameters(scratchPad, tmpVariables[4]); // validate updated key parameters. validateECKeys(); - data[KEY_BLOB] = KMArray.instance((short) 5); + data[KEY_BLOB] = createKeyBlobInstance(ASYM_KEY_TYPE); KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PUB_KEY, data[PUB_KEY]); } @@ -3207,7 +3225,7 @@ private void importHmacKey(byte[] scratchPad) { // validate HMAC Key parameters validateHmacKey(); - data[KEY_BLOB] = KMArray.instance((short) 4); + data[KEY_BLOB] = createKeyBlobInstance(SYM_KEY_TYPE); } private void importTDESKey(byte[] scratchPad) { @@ -3250,7 +3268,7 @@ private void importTDESKey(byte[] scratchPad) { if (tmpVariables[0] != KMType.INVALID_VALUE) { KMException.throwIt(KMError.INVALID_TAG); } - data[KEY_BLOB] = KMArray.instance((short) 4); + data[KEY_BLOB] = createKeyBlobInstance(SYM_KEY_TYPE); } private void validateAesKeySize(short keySizeBits) { @@ -3294,7 +3312,7 @@ private void importAESKey(byte[] scratchPad) { updateKeyParameters(scratchPad, tmpVariables[4]); // validate AES Key parameters validateAESKey(); - data[KEY_BLOB] = KMArray.instance((short) 4); + data[KEY_BLOB] = createKeyBlobInstance(SYM_KEY_TYPE); } private void importRSAKey(byte[] scratchPad) { @@ -3371,7 +3389,7 @@ private void importRSAKey(byte[] scratchPad) { updateKeyParameters(scratchPad, tmpVariables[4]); // validate RSA Key parameters validateRSAKey(scratchPad); - data[KEY_BLOB] = KMArray.instance((short) 5); + data[KEY_BLOB] = createKeyBlobInstance(ASYM_KEY_TYPE); KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PUB_KEY, data[PUB_KEY]); } @@ -3571,6 +3589,8 @@ private static void processGenerateKey(APDU apdu) { } // create key blob data[ORIGIN] = KMType.GENERATED; + // make key characteristics - returns key characteristics in data[KEY_CHARACTERISTICS] + makeKeyCharacteristics(scratchPad); createEncryptedKeyBlob(scratchPad); // prepare the response @@ -3632,7 +3652,7 @@ private static void generateRSAKey(byte[] scratchPad) { KMByteBlob.cast(data[PUB_KEY]).length(), lengths); - data[KEY_BLOB] = KMArray.instance((short) 5); + data[KEY_BLOB] = createKeyBlobInstance(ASYM_KEY_TYPE); KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PUB_KEY, data[PUB_KEY]); } @@ -3679,7 +3699,7 @@ private static void generateAESKey(byte[] scratchPad) { tmpVariables[0] = seProvider.createSymmetricKey(KMType.AES, tmpVariables[0], scratchPad, (short) 0); data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[0]); - data[KEY_BLOB] = KMArray.instance((short) 4); + data[KEY_BLOB] = createKeyBlobInstance(SYM_KEY_TYPE); } private static void validateECKeys() { @@ -3710,7 +3730,7 @@ private static void generateECKeys(byte[] scratchPad) { lengths); data[PUB_KEY] = KMByteBlob.instance(scratchPad, (short) 128, lengths[1]); data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, lengths[0]); - data[KEY_BLOB] = KMArray.instance((short) 5); + data[KEY_BLOB] = createKeyBlobInstance(ASYM_KEY_TYPE);; KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PUB_KEY, data[PUB_KEY]); } @@ -3737,7 +3757,7 @@ private static void generateTDESKey(byte[] scratchPad) { validateTDESKey(); tmpVariables[0] = seProvider.createSymmetricKey(KMType.DES, (short) 168, scratchPad, (short) 0); data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[0]); - data[KEY_BLOB] = KMArray.instance((short) 4); + data[KEY_BLOB] = createKeyBlobInstance(SYM_KEY_TYPE); } private static void validateHmacKey() { @@ -3789,47 +3809,7 @@ private static void generateHmacKey(byte[] scratchPad) { tmpVariables[0] = seProvider.createSymmetricKey(KMType.HMAC, tmpVariables[0], scratchPad, (short) 0); data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[0]); - data[KEY_BLOB] = KMArray.instance((short) 4); - } - - 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) { - tmpVariables[1] = repository.getOsVersion(); - tmpVariables[1] = - KMInteger.unsignedByteArrayCompare( - KMInteger.cast(tmpVariables[1]).getBuffer(), - KMInteger.cast(tmpVariables[1]).getStartOff(), - scratchPad, - (short) 0, - tmpVariables[0]); - if (tmpVariables[1] == -1) { - // If the key characteristics has os version > current os version - KMException.throwIt(KMError.INVALID_KEY_BLOB); - } else if (tmpVariables[1] == 1) { - 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) { - tmpVariables[1] = repository.getOsPatch(); - tmpVariables[1] = - KMInteger.unsignedByteArrayCompare( - KMInteger.cast(tmpVariables[1]).getBuffer(), - KMInteger.cast(tmpVariables[1]).getStartOff(), - scratchPad, - (short) 0, - tmpVariables[0]); - if (tmpVariables[1] == -1) { - KMException.throwIt(KMError.INVALID_KEY_BLOB); - } else if (tmpVariables[1] == 1) { - KMException.throwIt(KMError.KEY_REQUIRES_UPGRADE); - } - } + data[KEY_BLOB] = createKeyBlobInstance(SYM_KEY_TYPE); } private static void makeKeyCharacteristics(byte[] scratchPad) { @@ -3853,10 +3833,8 @@ private static void makeKeyCharacteristics(byte[] scratchPad) { } 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] = repository.readROT(); + data[ROT] = repository.readROT(KEYBLOB_CURRENT_VERSION); if (data[ROT] == KMType.INVALID_VALUE) { KMException.throwIt(KMError.UNKNOWN_ERROR); } @@ -3864,76 +3842,144 @@ private static void createEncryptedKeyBlob(byte[] scratchPad) { // make hidden key params list data[HIDDEN_PARAMETERS] = KMKeyParameters.makeHidden(data[KEY_PARAMETERS], data[ROT], scratchPad); + data[KEY_BLOB_VERSION_DATA_OFFSET] = KMInteger.uint_16(KEYBLOB_CURRENT_VERSION); + // create custom tags + data[CUSTOM_TAGS] = KMKeyParameters.makeCustomTags(data[HW_PARAMETERS], scratchPad); // make authorization data - makeAuthData(scratchPad); + makeAuthData(KEYBLOB_CURRENT_VERSION, scratchPad); // encrypt the secret and cryptographically attach that to authorization data encryptSecret(scratchPad); // create key blob array + //KMArray.cast(ENC_TRANSPORT_KEY); 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_VERSION_OFFSET, data[KEY_BLOB_VERSION_DATA_OFFSET]); KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_KEYCHAR, data[KEY_CHARACTERISTICS]); + KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_CUSTOM_TAGS, data[CUSTOM_TAGS]); - // allocate reclaimable memory. tmpVariables[0] = repository.alloc((short) 1024); tmpVariables[1] = encoder.encode(data[KEY_BLOB], repository.getHeap(), tmpVariables[0]); data[KEY_BLOB] = KMByteBlob.instance(repository.getHeap(), tmpVariables[0], tmpVariables[1]); } - private static void parseEncryptedKeyBlob(byte[] scratchPad) { - data[ROT] = repository.readROT(); - if (data[ROT] == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.UNKNOWN_ERROR); - } + private void parseEncryptedKeyBlob(short keyBlob, short appId, short appData, + byte[] scratchPad, short version) { + // make root of trust blob + data[ROT] = repository.readROT(version); + if (data[ROT] == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } try { - 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); - } - data[AUTH_TAG] = KMArray.cast(data[KEY_BLOB]).get(KEY_BLOB_AUTH_TAG); - - // 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(); - - data[HIDDEN_PARAMETERS] = KMKeyParameters.makeHidden(data[APP_ID], - data[APP_DATA], data[ROT], scratchPad); - // make auth data - makeAuthData(scratchPad); - // Decrypt Secret and verify auth tag - decryptSecret(scratchPad); + decodeKeyBlob(version, keyBlob); + processDecryptSecret(version, appId, appData, scratchPad); } catch (Exception e) { KMException.throwIt(KMError.INVALID_KEY_BLOB); } } + + private void decodeKeyBlob(short version, short keyBlob) { + // Decode KeyBlob and read the KeyBlob params based on the version. + short parsedBlob = decoder.decodeArray(createKeyBlobExp(version), + KMByteBlob.cast(keyBlob).getBuffer(), + KMByteBlob.cast(keyBlob).getStartOff(), + KMByteBlob.cast(keyBlob).length()); + short minArraySize = 0; + switch(version) { + case 0: + minArraySize = SYM_KEY_BLOB_SIZE_V0; + break; + case 1: + minArraySize = SYM_KEY_BLOB_SIZE_V1; + break; + default: + KMException.throwIt(KMError.INVALID_KEY_BLOB); + }; + // KeyBlob size should not be less than the minimum KeyBlob size. + if (KMArray.cast(parsedBlob).length() < minArraySize) { + KMException.throwIt(KMError.INVALID_KEY_BLOB); + } + readKeyBlobParams(version, parsedBlob); + } + + private void readKeyBlobParams(short version, short parsedKeyBlob) { + data[KEY_BLOB] = parsedKeyBlob; + // initialize data + switch (version) { + case (short) 0: + data[SECRET] = KMArray.cast(parsedKeyBlob).get((short) 0); + data[NONCE]= KMArray.cast(parsedKeyBlob).get((short) 1); + data[AUTH_TAG] = KMArray.cast(parsedKeyBlob).get((short) 2); + data[KEY_CHARACTERISTICS] = KMArray.cast(parsedKeyBlob).get((short) 3); + data[PUB_KEY] = KMType.INVALID_VALUE; + if (KMArray.cast(parsedKeyBlob).length() == ASYM_KEY_BLOB_SIZE_V0) { + data[PUB_KEY] = KMArray.cast(parsedKeyBlob).get((short) 4); + } + // Set the data[KEY_BLOB_VERSION_DATA_OFFSET] with integer value of 0 so + // that it will used at later point of time. + data[KEY_BLOB_VERSION_DATA_OFFSET] = KMInteger.uint_8((byte) 0); + break; + case (short) 1: + data[SECRET] = KMArray.cast(parsedKeyBlob).get(KEY_BLOB_SECRET); + data[NONCE]= KMArray.cast(parsedKeyBlob).get(KEY_BLOB_NONCE); + data[AUTH_TAG] = KMArray.cast(parsedKeyBlob).get(KEY_BLOB_AUTH_TAG); + data[KEY_CHARACTERISTICS] = KMArray.cast(parsedKeyBlob).get(KEY_BLOB_KEYCHAR); + data[KEY_BLOB_VERSION_DATA_OFFSET] = KMArray.cast(parsedKeyBlob).get( + KEY_BLOB_VERSION_OFFSET); + data[CUSTOM_TAGS] = KMArray.cast(parsedKeyBlob).get( + KEY_BLOB_CUSTOM_TAGS); + data[PUB_KEY] = KMType.INVALID_VALUE; + if (KMArray.cast(parsedKeyBlob).length() == ASYM_KEY_BLOB_SIZE_V1) { + data[PUB_KEY] = KMArray.cast(parsedKeyBlob).get(KEY_BLOB_PUB_KEY); + } + break; + default: + KMException.throwIt(KMError.INVALID_KEY_BLOB); + } + } + + private void processDecryptSecret(short version, short appId, short appData, byte[] scratchPad) { + data[HW_PARAMETERS] = KMKeyCharacteristics + .cast(data[KEY_CHARACTERISTICS]).getHardwareEnforced(); + data[SW_PARAMETERS] = KMKeyCharacteristics + .cast(data[KEY_CHARACTERISTICS]).getSoftwareEnforced(); + data[HIDDEN_PARAMETERS] = KMKeyParameters.makeHidden(appId, appData, data[ROT], scratchPad); + // make auth data + makeAuthData(version, scratchPad); + // Decrypt Secret and verify auth tag + decryptSecret(scratchPad); + short keyBlobSecretOff = 0; + switch(version) { + case 0: + // V0 KeyBlob + // KEY_BLOB = [ + // SECRET, + // NONCE, + // AUTH_TAG, + // KEY_CHARACTERISTICS, + // PUBKEY + // ] + keyBlobSecretOff = (short) 0; + break; + case 1: + // V1 KeyBlob + // KEY_BLOB = [ + // VERSION, + // SECRET, + // NONCE, + // AUTH_TAG, + // KEY_CHARACTERISTICS, + // CUSTOM_TAGS + // PUBKEY + // ] + keyBlobSecretOff = (short) 1; + break; + default: + KMException.throwIt(KMError.INVALID_KEY_BLOB); + }; + KMArray.cast(data[KEY_BLOB]).add(keyBlobSecretOff, data[SECRET]); + } private static void decryptSecret(byte[] scratchPad) { // derive master key - stored in derivedKey @@ -4006,27 +4052,63 @@ private static void encryptSecret(byte[] scratchPad) { } } - private static void makeAuthData(byte[] scratchPad) { - - short arrayLen = 3; - if (KMArray.cast(data[KEY_BLOB]).length() == 5) { - arrayLen = 4; + private static byte getKeyType(short hardwareParams) { + short alg = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, hardwareParams); + if (KMEnumTag.cast(alg).getValue() == KMType.RSA + || KMEnumTag.cast(alg).getValue() == KMType.EC) { + return ASYM_KEY_TYPE; } - short params = KMArray.instance((short) arrayLen); - KMArray.cast(params).add((short) 0, KMKeyParameters.cast(data[HW_PARAMETERS]).getVals()); - KMArray.cast(params).add((short) 1, KMKeyParameters.cast(data[SW_PARAMETERS]).getVals()); - KMArray.cast(params).add((short) 2, KMKeyParameters.cast(data[HIDDEN_PARAMETERS]).getVals()); - if (4 == arrayLen) { - KMArray.cast(params).add((short) 3, data[PUB_KEY]); + return SYM_KEY_TYPE; + } + + private static void makeAuthData(short version, byte[] scratchPad) { + // For KeyBlob V1: Auth Data includes HW_PARAMETERS, SW_PARAMTERS, HIDDEN_PARAMETERS, CUSTOM_TAGS, VERSION and PUB_KEY. + // For KeyBlob V0: Auth Data includes HW_PARAMETERS, SW_PARAMTERS, HIDDEN_PARAMETERS and PUB_KEY. + // VERSION is included only for KeyBlobs having version >= 1. + // PUB_KEY is included for only ASYMMETRIC KeyBlobs. + short index = 0; + short numParams = 0; + Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 12, (byte) 0); + byte keyType = getKeyType(data[HW_PARAMETERS]); + // Copy the relevant parameters in the scratchPad in the order + // 1. HW_PARAMETERS + // 2. HIDDEN_PARAMETERS + // 3. VERSION ( Only Version >= 1) + // 4. PUB_KEY ( Only for Asymmetric Keys) + switch (version) { + case (short) 0: + numParams = 3; + Util.setShort(scratchPad, (short) 0, KMKeyParameters.cast(data[HW_PARAMETERS]).getVals()); + Util.setShort(scratchPad, (short) 2, KMKeyParameters.cast(data[SW_PARAMETERS]).getVals()); + Util.setShort(scratchPad, (short) 4, KMKeyParameters.cast(data[HIDDEN_PARAMETERS]).getVals()); + // For Asymmetric Keys include the PUB_KEY. + if (keyType == ASYM_KEY_TYPE) { + numParams = 4; + Util.setShort(scratchPad, (short) 6, data[PUB_KEY]); + } + break; + case (short) 1: + numParams = 5; + Util.setShort(scratchPad, (short) 0, KMKeyParameters.cast(data[HW_PARAMETERS]).getVals()); + Util.setShort(scratchPad, (short) 2, KMKeyParameters.cast(data[SW_PARAMETERS]).getVals()); + Util.setShort(scratchPad, (short) 4, KMKeyParameters.cast(data[HIDDEN_PARAMETERS]).getVals()); + Util.setShort(scratchPad, (short) 6, KMKeyParameters.cast(data[CUSTOM_TAGS]).getVals()); + Util.setShort(scratchPad, (short) 8, data[KEY_BLOB_VERSION_DATA_OFFSET]); + // For Asymmetric Keys include the PUB_KEY. + if (keyType == ASYM_KEY_TYPE) { + numParams = 6; + Util.setShort(scratchPad, (short) 10, data[PUB_KEY]); + } + break; + default: + KMException.throwIt(KMError.INVALID_KEY_BLOB); } - + short authIndex = repository.alloc(MAX_AUTH_DATA_SIZE); - short index = 0; short len = 0; - short paramsLen = KMArray.cast(params).length(); Util.arrayFillNonAtomic(repository.getHeap(), authIndex, (short) MAX_AUTH_DATA_SIZE, (byte) 0); - while (index < paramsLen) { - short tag = KMArray.cast(params).get(index); + while (index < numParams) { + short tag = Util.getShort(scratchPad, (short) (index * 2)); len = encoder.encode(tag, repository.getHeap(), (short) (authIndex + 32)); Util.arrayCopyNonAtomic(repository.getHeap(), (short) authIndex, repository.getHeap(), (short) (authIndex + len + 32), (short) 32); @@ -4040,7 +4122,7 @@ private static void makeAuthData(byte[] scratchPad) { data[AUTH_DATA] = authIndex; data[AUTH_DATA_LENGTH] = len; } - + private static short deriveKey(byte[] scratchPad) { // KeyDerivation: // 1. Do HMAC Sign, Auth data. @@ -4150,5 +4232,150 @@ private void finishTrustedConfirmationOperation(KMOperationState op) { } } } + + private short createKeyBlobExp(short version) { + short keyBlob = KMType.INVALID_VALUE; + short byteBlobExp = KMByteBlob.exp(); + short keyChar = KMKeyCharacteristics.exp(); + short keyParam = KMKeyParameters.exp(); + switch(version) { + case (short) 0: + // Old KeyBlob has a maximum of 5 elements. + keyBlob = KMArray.instance(ASYM_KEY_BLOB_SIZE_V0); + KMArray.cast(keyBlob).add((short) 0, byteBlobExp);// Secret + KMArray.cast(keyBlob).add((short) 1, byteBlobExp);// Nonce + KMArray.cast(keyBlob).add((short) 2, byteBlobExp);// AuthTag + KMArray.cast(keyBlob).add((short) 3, keyChar);// KeyChars + KMArray.cast(keyBlob).add((short) 4, byteBlobExp);// PubKey + break; + case (short) 1: + keyBlob = KMArray.instance(ASYM_KEY_BLOB_SIZE_V1); + KMArray.cast(keyBlob).add(KEY_BLOB_VERSION_OFFSET, KMInteger.exp());// Version + KMArray.cast(keyBlob).add(KEY_BLOB_SECRET, byteBlobExp);// Secret + KMArray.cast(keyBlob).add(KEY_BLOB_NONCE, byteBlobExp);// Nonce + KMArray.cast(keyBlob).add(KEY_BLOB_AUTH_TAG, byteBlobExp);// AuthTag + KMArray.cast(keyBlob).add(KEY_BLOB_KEYCHAR, keyChar);// KeyChars + KMArray.cast(keyBlob).add(KEY_BLOB_CUSTOM_TAGS, keyParam);// KeyChars + KMArray.cast(keyBlob).add(KEY_BLOB_PUB_KEY, byteBlobExp);// PubKey + break; + default: + KMException.throwIt(KMError.INVALID_KEY_BLOB); + } + return keyBlob; + } + + private static short createKeyBlobInstance(byte keyType) { + short arrayLen = 0; + switch (keyType) { + case ASYM_KEY_TYPE: + arrayLen = ASYM_KEY_BLOB_SIZE_V1; + break; + case SYM_KEY_TYPE: + arrayLen = SYM_KEY_BLOB_SIZE_V1; + break; + default: + KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); + } + return KMArray.instance(arrayLen); + } + + private boolean isKeyUpgradeRequired(short keyBlob, short appId, short appData, byte[] scratchPad) { + // Check if the KeyBlob is compatible. If there is any change in the KeyBlob, the version + // Parameter in the KeyBlob should be updated to the next version. + short version = readKeyBlobVersion(keyBlob); + parseEncryptedKeyBlob(keyBlob, appId, appData, scratchPad, version); + if (version < KEYBLOB_CURRENT_VERSION) { + return true; + } + short bootPatchLevel = repository.getBootPatchLevel();; + // Fill the key-value properties in the scratchpad + Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 16, (byte) 0); + Util.setShort(scratchPad, (short) 0, KMType.OS_VERSION); + Util.setShort(scratchPad, (short) 2, repository.getOsVersion()); + Util.setShort(scratchPad, (short) 4, KMType.OS_PATCH_LEVEL); + Util.setShort(scratchPad, (short) 6, repository.getOsPatch()); + Util.setShort(scratchPad, (short) 8, KMType.VENDOR_PATCH_LEVEL); + Util.setShort(scratchPad, (short) 10, repository.getVendorPatchLevel()); + Util.setShort(scratchPad, (short) 12, KMType.BOOT_PATCH_LEVEL); + Util.setShort(scratchPad, (short) 14, bootPatchLevel); + short index = 0; + short tag; + short systemParam; + while(index < 16) { + tag = Util.getShort(scratchPad, index); + systemParam = Util.getShort(scratchPad, (short) (index + 2)); + // validate the tag and check if key needs upgrade. + short tagValue = KMKeyParameters.findTag(KMType.UINT_TAG, tag, data[HW_PARAMETERS]); + tagValue = KMIntegerTag.cast(tagValue).getValue(); + short zero = KMInteger.uint_8((byte) 0); + if (tagValue != KMType.INVALID_VALUE) { + // OS version in key characteristics must be less the OS version stored in Javacard or the + // stored version must be zero. Then only upgrade is allowed else it is invalid argument. + if ((tag == KMType.OS_VERSION + && KMInteger.compare(tagValue, systemParam) == 1 + && KMInteger.compare(systemParam, zero) == 0)) { + // Key needs upgrade. + return true; + } else if ((KMInteger.compare(tagValue, systemParam) == -1)) { + // Each os version or patch level associated with the key must be less than it's + // corresponding value stored in Javacard, then only upgrade is allowed otherwise it + // is invalid argument. + return true; + } else if (KMInteger.compare(tagValue, systemParam) == 1) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + } else { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + index += 4; + } + return false; + } + private short readKeyBlobVersion(short keyBlob) { + short version = KMType.INVALID_VALUE; + try { + version = decoder.readKeyblobVersion( + KMByteBlob.cast(keyBlob).getBuffer(), + KMByteBlob.cast(keyBlob).getStartOff(), + KMByteBlob.cast(keyBlob).length()); + if (version == KMType.INVALID_VALUE) { + // If Version is not present. Then it is either an old KeyBlob or + // corrupted KeyBlob. + version = 0; + } else { + version = KMInteger.cast(version).getShort(); + if (version > KEYBLOB_CURRENT_VERSION || version < 0) { + KMException.throwIt(KMError.INVALID_KEY_BLOB); + } + } + } catch(Exception e) { + KMException.throwIt(KMError.INVALID_KEY_BLOB); + } + return version; + } + + private short getApplicationId(short params) { + short appId = KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_ID, params); + if (appId != KMTag.INVALID_VALUE) { + appId = KMByteTag.cast(appId).getValue(); + if (KMByteBlob.cast(appId).length() == 0) { + // Treat empty as INVALID. + return KMType.INVALID_VALUE; + } + } + return appId; + } + + private short getApplicationData(short params) { + short appData = KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_DATA, params); + if (appData != KMTag.INVALID_VALUE) { + appData = KMByteTag.cast(appData).getValue(); + if (KMByteBlob.cast(appData).length() == 0) { + // Treat empty as INVALID. + return KMType.INVALID_VALUE; + } + } + return appData; + } } diff --git a/Applet/src/com/android/javacard/keymaster/KMRepository.java b/Applet/src/com/android/javacard/keymaster/KMRepository.java index 1cfe8ef9..df380507 100644 --- a/Applet/src/com/android/javacard/keymaster/KMRepository.java +++ b/Applet/src/com/android/javacard/keymaster/KMRepository.java @@ -631,17 +631,24 @@ public short getOsPatch() { } } - public short readROT() { + public short readROT(short version) { short totalLength = 0; short length = dataLength(BOOT_VERIFIED_BOOT_KEY); if (length == 0) { return KMType.INVALID_VALUE; } totalLength += length; - if ((length = dataLength(BOOT_VERIFIED_BOOT_HASH)) == 0) { - return KMType.INVALID_VALUE; - } - totalLength += length; + // As per specification The root of trust + // consists of verifyBootKey, boot state and device locked. + if (version <= KMKeymasterApplet.KEYBLOB_VERSION_0) { + // To parse old keyblobs verified boot hash is included in + // the root of trust. + + if ((length = dataLength(BOOT_VERIFIED_BOOT_HASH)) == 0) { + return KMType.INVALID_VALUE; + } + totalLength += length; + } if ((length = dataLength(BOOT_VERIFIED_BOOT_STATE)) == 0) { return KMType.INVALID_VALUE; } @@ -655,10 +662,11 @@ public short readROT() { length = readDataEntry(BOOT_VERIFIED_BOOT_KEY, KMByteBlob.cast(blob) .getBuffer(), KMByteBlob.cast(blob).getStartOff()); - length += readDataEntry(BOOT_VERIFIED_BOOT_HASH, KMByteBlob.cast(blob) - .getBuffer(), - (short) (KMByteBlob.cast(blob).getStartOff() + length)); - + if (version <= KMKeymasterApplet.KEYBLOB_VERSION_0) { + length += readDataEntry(BOOT_VERIFIED_BOOT_HASH, KMByteBlob.cast(blob) + .getBuffer(), + (short) (KMByteBlob.cast(blob).getStartOff() + length)); + } length += readDataEntry(BOOT_VERIFIED_BOOT_STATE, KMByteBlob.cast(blob) .getBuffer(), (short) (KMByteBlob.cast(blob).getStartOff() + length)); @@ -1018,5 +1026,4 @@ public void handleDataUpgradeToVersion2_0() { oldDataTable = null; JCSystem.requestObjectDeletion(); } - } diff --git a/HAL/keymaster/4.1/JavacardSoftKeymasterContext.cpp b/HAL/keymaster/4.1/JavacardSoftKeymasterContext.cpp index bbdfba28..99097662 100644 --- a/HAL/keymaster/4.1/JavacardSoftKeymasterContext.cpp +++ b/HAL/keymaster/4.1/JavacardSoftKeymasterContext.cpp @@ -186,13 +186,30 @@ keymaster_error_t JavaCardSoftKeymasterContext::ParseKeyBlob(const KeymasterKeyB } std::tie(item, errorCode) = cc.decodeData(cborKey, false); if (item != nullptr) { + // V0 KeyBlobs had no version. + uint64_t version = 0; + int pubKeyOffset; + int keyCharsOffset; + cc.getUint64(item, 0, version); + switch (version) { + case 0: + pubKeyOffset = 4; + keyCharsOffset = 3; + break; + case 1: + pubKeyOffset = 6; + keyCharsOffset = 4; + break; + default: + return KM_ERROR_INVALID_KEY_BLOB; + } std::vector temp(0); - if(cc.getBinaryArray(item, 4, temp)) { + if(cc.getBinaryArray(item, pubKeyOffset, temp)) { key_material = {temp.data(), temp.size()}; temp.clear(); } KeyCharacteristics keyCharacteristics; - cc.getKeyCharacteristics(item, 3, keyCharacteristics); + cc.getKeyCharacteristics(item, keyCharsOffset, keyCharacteristics); sw_enforced.Reinitialize(KmParamSet(keyCharacteristics.softwareEnforced)); hw_enforced.Reinitialize(KmParamSet(keyCharacteristics.hardwareEnforced));