diff --git a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAndroidSEProvider.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAndroidSEProvider.java index e5ede6e9..ddd45ae8 100644 --- a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAndroidSEProvider.java +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAndroidSEProvider.java @@ -80,6 +80,7 @@ public class KMAndroidSEProvider implements KMSEProvider { private KMRsaOAEPEncoding rsaOaepDecipher; private KMPoolManager poolMgr; + private KMOperationImpl globalOperation; // Entropy private RandomData rng; @@ -112,6 +113,8 @@ public KMAndroidSEProvider() { kdf = Signature.getInstance(Signature.ALG_AES_CMAC_128, false); hmacSignature = Signature.getInstance(Signature.ALG_HMAC_SHA_256, false); + globalOperation = new KMOperationImpl(); + // Temporary transient array created to use locally inside functions. tmpArray = JCSystem.makeTransientByteArray(TMP_ARRAY_SIZE, JCSystem.CLEAR_ON_DESELECT); @@ -131,7 +134,7 @@ void initStatics() { } public void clean() { - Util.arrayFillNonAtomic(tmpArray, (short) 0, (short) 256, (byte) 0); + Util.arrayFillNonAtomic(tmpArray, (short) 0, TMP_ARRAY_SIZE, (byte) 0); } public AESKey createAESKey(short keysize) { @@ -352,7 +355,9 @@ public short aesGCMEncrypt(AESKey key, false); } aesGcmCipher.init(key, Cipher.MODE_ENCRYPT, nonce, nonceStart, nonceLen); - aesGcmCipher.updateAAD(authData, authDataStart, authDataLen); + if (authDataLen != 0) { + aesGcmCipher.updateAAD(authData, authDataStart, authDataLen); + } short ciphLen = aesGcmCipher.doFinal(secret, secretStart, secretLen, encSecret, encSecretStart); aesGcmCipher.retrieveTag(authTag, authTagStart, authTagLen); @@ -398,7 +403,9 @@ public boolean aesGCMDecrypt(byte[] aesKey, short aesKeyStart, boolean verification = false; AESKey key = createAESKey(aesKey, aesKeyStart, aesKeyLen); aesGcmCipher.init(key, Cipher.MODE_DECRYPT, nonce, nonceStart, nonceLen); - aesGcmCipher.updateAAD(authData, authDataStart, authDataLen); + if (authDataLen != 0) { + aesGcmCipher.updateAAD(authData, authDataStart, authDataLen); + } // encrypt the secret aesGcmCipher.doFinal(encSecret, encSecretStart, encSecretLen, secret, secretStart); @@ -479,9 +486,8 @@ public short hmacKDF(KMMasterKey masterkey, byte[] data, short dataStart, try { KMAESKey aesKey = (KMAESKey) masterkey; short keyLen = (short) (aesKey.getKeySizeBits() / 8); - byte[] keyData = new byte[keyLen]; - aesKey.getKey(keyData, (short) 0); - return hmacSign(keyData, (short) 0, keyLen, data, dataStart, dataLength, + aesKey.getKey(tmpArray, (short) 0); + return hmacSign(tmpArray, (short) 0, keyLen, data, dataStart, dataLength, signature, signatureStart); } finally { clean(); @@ -697,6 +703,43 @@ public KMOperation initSymmetricOperation(byte purpose, byte alg, return opr; } + @Override + public KMOperation initSymmetricOperation(byte purpose, byte alg, byte digest, byte padding, byte blockMode, + Object key, byte interfaceType, byte[] ivBuf, short ivStart, short ivLength, short macLength, + boolean oneShot) { + short keyLen = 0; + globalOperation.setPurpose(purpose); + globalOperation.setAlgorithmType(alg); + globalOperation.setPaddingAlgorithm(padding); + globalOperation.setBlockMode(blockMode); + try { + switch (interfaceType) { + case KMDataStoreConstants.INTERFACE_TYPE_MASTER_KEY: + KMAESKey aesKey = (KMAESKey) key; + keyLen = (short) (aesKey.getKeySizeBits() / 8); + aesKey.getKey(tmpArray, (short) 0); + break; + + default: + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + + switch (alg) { + case KMType.HMAC: + HMACKey hmackey = createHMACKey(tmpArray, (short)0, keyLen); + globalOperation.setSignature(hmacSignature); + globalOperation.init(hmackey, digest, null, (short)0, (short)0); + break; + + default: + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + } finally { + clean(); + } + return globalOperation; + } + @Override public KMOperation initTrustedConfirmationSymmetricOperation(KMComputedHmacKey computedHmacKey) { KMHmacKey key = (KMHmacKey) computedHmacKey; @@ -1101,18 +1144,12 @@ public void onSave(Element element, byte interfaceType, Object object) { return; } switch (interfaceType) { - case KMDataStoreConstants.INTERFACE_TYPE_COMPUTED_HMAC_KEY: - KMHmacKey.onSave(element, (KMHmacKey) object); - break; case KMDataStoreConstants.INTERFACE_TYPE_MASTER_KEY: KMAESKey.onSave(element, (KMAESKey) object); break; case KMDataStoreConstants.INTERFACE_TYPE_PRE_SHARED_KEY: KMHmacKey.onSave(element, (KMHmacKey) object); break; - case KMDataStoreConstants.INTERFACE_TYPE_ATTESTATION_KEY: - KMECPrivateKey.onSave(element, (KMECPrivateKey) object); - break; case KMDataStoreConstants.INTERFACE_TYPE_DEVICE_UNIQUE_KEY_PAIR: KMECDeviceUniqueKey.onSave(element, (KMECDeviceUniqueKey) object); break; @@ -1137,8 +1174,6 @@ public Object onRestore(Element element) { return KMAESKey.onRestore((AESKey) element.readObject()); case KMDataStoreConstants.INTERFACE_TYPE_PRE_SHARED_KEY: return KMHmacKey.onRestore((HMACKey) element.readObject()); - case KMDataStoreConstants.INTERFACE_TYPE_ATTESTATION_KEY: - return KMECPrivateKey.onRestore((KeyPair) element.readObject()); case KMDataStoreConstants.INTERFACE_TYPE_DEVICE_UNIQUE_KEY_PAIR: return KMECDeviceUniqueKey.onRestore((KeyPair) element.readObject()); case KMDataStoreConstants.INTERFACE_TYPE_RKP_MAC_KEY: @@ -1153,18 +1188,12 @@ public Object onRestore(Element element) { public short getBackupPrimitiveByteCount(byte interfaceType) { short primitiveCount = 1; // interface type switch (interfaceType) { - case KMDataStoreConstants.INTERFACE_TYPE_COMPUTED_HMAC_KEY: - primitiveCount += KMHmacKey.getBackupPrimitiveByteCount(); - break; case KMDataStoreConstants.INTERFACE_TYPE_MASTER_KEY: primitiveCount += KMAESKey.getBackupPrimitiveByteCount(); break; case KMDataStoreConstants.INTERFACE_TYPE_PRE_SHARED_KEY: primitiveCount += KMHmacKey.getBackupPrimitiveByteCount(); break; - case KMDataStoreConstants.INTERFACE_TYPE_ATTESTATION_KEY: - primitiveCount += KMECPrivateKey.getBackupPrimitiveByteCount(); - break; case KMDataStoreConstants.INTERFACE_TYPE_DEVICE_UNIQUE_KEY_PAIR: primitiveCount += KMECDeviceUniqueKey.getBackupPrimitiveByteCount(); break; @@ -1180,14 +1209,10 @@ public short getBackupPrimitiveByteCount(byte interfaceType) { @Override public short getBackupObjectCount(byte interfaceType) { switch (interfaceType) { - case KMDataStoreConstants.INTERFACE_TYPE_COMPUTED_HMAC_KEY: - return KMHmacKey.getBackupObjectCount(); case KMDataStoreConstants.INTERFACE_TYPE_MASTER_KEY: return KMAESKey.getBackupObjectCount(); case KMDataStoreConstants.INTERFACE_TYPE_PRE_SHARED_KEY: return KMHmacKey.getBackupObjectCount(); - case KMDataStoreConstants.INTERFACE_TYPE_ATTESTATION_KEY: - return KMECPrivateKey.getBackupObjectCount(); case KMDataStoreConstants.INTERFACE_TYPE_DEVICE_UNIQUE_KEY_PAIR: return KMECDeviceUniqueKey.getBackupObjectCount(); case KMDataStoreConstants.INTERFACE_TYPE_RKP_MAC_KEY: diff --git a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java index d77f2167..2a99f81b 100644 --- a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java @@ -562,6 +562,43 @@ KMOperation initSymmetricOperation( short ivLength, short macLength); + /** + * This creates a persistent operation for signing, verify, encryption and decryption using HMAC, + * AES and DES algorithms when keymaster hal's beginOperation function is executed. The + * KMOperation instance can be reclaimed by the seProvider when KMOperation is finished or + * aborted. It throws CryptoException if algorithm is not supported. + * + * @param purpose is KMType.ENCRYPT or KMType.DECRYPT for AES and DES algorithm. It will be + * KMType.SIGN and KMType.VERIFY for HMAC algorithm + * @param alg is KMType.HMAC, KMType.AES or KMType.DES. + * @param digest is KMType.SHA2_256 in case of HMAC else it will be KMType.DIGEST_NONE. + * @param padding is KMType.PADDING_NONE or KMType.PKCS7 (in case of AES and DES). + * @param blockMode is KMType.CTR, KMType.GCM. KMType.CBC or KMType.ECB for AES or DES else it is + * 0. + * @param key is a key object. + * @param interfaceType defines the type of key in the key object. + * @param ivBuf is the iv buffer (in case on AES and DES algorithm without ECB mode) + * @param ivStart is the start of the iv buffer. + * @param ivLength is the length of the iv buffer. It will be zero in case of HMAC and AES/DES + * with ECB mode. + * @param macLength is the mac length in case of signing operation for hmac algorithm. + * @param oneShot if true, creates oneshot operation. + * @return KMOperation instance. + */ + KMOperation initSymmetricOperation( + byte purpose, + byte alg, + byte digest, + byte padding, + byte blockMode, + Object key, + byte interfaceType, + byte[] ivBuf, + short ivStart, + short ivLength, + short macLength, + boolean oneShot); + /** * This function creates an Operation instance only for RKP module. * diff --git a/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMJCardSimulator.java b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMJCardSimulator.java index 35c458bb..7f423f00 100644 --- a/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMJCardSimulator.java +++ b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMJCardSimulator.java @@ -359,10 +359,12 @@ public short aesGCMEncrypt( e.printStackTrace(); CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); } - // Create auth data - byte[] aad = new byte[authDataLen]; - Util.arrayCopyNonAtomic(authData, authDataStart, aad, (short) 0, authDataLen); - cipher.updateAAD(aad); + if (authDataLen != 0) { + // Create auth data + byte[] aad = new byte[authDataLen]; + Util.arrayCopyNonAtomic(authData, authDataStart, aad, (short) 0, authDataLen); + cipher.updateAAD(aad); + } // Encrypt secret short len = 0; byte[] outputBuf = new byte[cipher.getOutputSize(secretLen)]; @@ -444,11 +446,13 @@ public boolean aesGCMDecrypt( e.printStackTrace(); CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); } - // Create auth data - byte[] aad = new byte[authDataLen]; - Util.arrayCopyNonAtomic(authData, authDataStart, aad, (short) 0, + if (authDataLen != 0) { + // Create auth data + byte[] aad = new byte[authDataLen]; + Util.arrayCopyNonAtomic(authData, authDataStart, aad, (short) 0, authDataLen); - cipher.updateAAD(aad); + cipher.updateAAD(aad); + } // Append the auth tag at the end of data byte[] inputBuf = new byte[(short) (encSecretLen + authTagLen)]; Util.arrayCopyNonAtomic(encSecret, encSecretStart, inputBuf, (short) 0, @@ -666,6 +670,39 @@ public KMOperation initSymmetricOperation(byte purpose, byte alg, byte digest, b return null; } + @Override + public KMOperation initSymmetricOperation(byte purpose, byte alg, byte digest, byte padding, byte blockMode, + Object key, byte interfaceType, byte[] ivBuf, short ivStart, short ivLength, short macLength, + boolean oneShot) { + KMOperationImpl operation = null; + short keyLen = 0; + byte[] keyData = null; + + switch (interfaceType) { + case KMDataStoreConstants.INTERFACE_TYPE_MASTER_KEY: + KMAESKey aesKey = (KMAESKey) key; + keyLen = (short) (aesKey.getKeySizeBits() / 8); + keyData = new byte[keyLen]; + aesKey.getKey(keyData, (short) 0); + break; + + default: + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + + switch (alg){ + case KMType.HMAC: + Signature signerVerifier = createHmacSignerVerifier(purpose, digest, keyData, (short)0, + keyLen); + operation = new KMOperationImpl(signerVerifier); + break; + + default: + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + return operation; + } + @Override public KMOperation initTrustedConfirmationSymmetricOperation(KMComputedHmacKey computedHmacKey) { KMOperationImpl opr = null; diff --git a/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java index d77f2167..c758889f 100644 --- a/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java +++ b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java @@ -562,6 +562,43 @@ KMOperation initSymmetricOperation( short ivLength, short macLength); + /** + * This creates a persistent operation for signing, verify, encryption and decryption using HMAC, + * AES and DES algorithms when keymaster hal's beginOperation function is executed. The + * KMOperation instance can be reclaimed by the seProvider when KMOperation is finished or + * aborted. It throws CryptoException if algorithm is not supported. + * + * @param purpose is KMType.ENCRYPT or KMType.DECRYPT for AES and DES algorithm. It will be + * KMType.SIGN and KMType.VERIFY for HMAC algorithm + * @param alg is KMType.HMAC, KMType.AES or KMType.DES. + * @param digest is KMType.SHA2_256 in case of HMAC else it will be KMType.DIGEST_NONE. + * @param padding is KMType.PADDING_NONE or KMType.PKCS7 (in case of AES and DES). + * @param blockMode is KMType.CTR, KMType.GCM. KMType.CBC or KMType.ECB for AES or DES else it is + * 0. + * @param key is a key object. + * @param interfaceType defines the type of key in the key object. + * @param ivBuf is the iv buffer (in case on AES and DES algorithm without ECB mode) + * @param ivStart is the start of the iv buffer. + * @param ivLength is the length of the iv buffer. It will be zero in case of HMAC and AES/DES + * with ECB mode. + * @param macLength is the mac length in case of signing operation for hmac algorithm. + * @param oneShot if true, creates oneshot operation. + * @return KMOperation instance. + */ + KMOperation initSymmetricOperation( + byte purpose, + byte alg, + byte digest, + byte padding, + byte blockMode, + Object key, + byte interfaceType, + byte[] ivBuf, + short ivStart, + short ivLength, + short macLength, + boolean oneShot); + /** * This function creates an Operation instance only for RKP module. * diff --git a/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java b/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java index 40bcbc2c..0b6038cd 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java @@ -77,8 +77,8 @@ public class KMKeyParameters extends KMType { KMType.ULONG_ARRAY_TAG, KMType.USER_SECURE_ID, KMType.UINT_TAG, KMType.AUTH_TIMEOUT, KMType.ENUM_TAG, KMType.USER_AUTH_TYPE, - KMType.BOOL_TAG, KMType.UNLOCKED_DEVICE_REQUIRED, - KMType.BOOL_TAG, KMType.TRUSTED_CONFIRMATION_REQUIRED, + KMType.BOOL_TAG, KMType.UNLOCKED_DEVICE_REQUIRED, + KMType.BOOL_TAG, KMType.TRUSTED_CONFIRMATION_REQUIRED, }; private static final short[] invalidTagsArr = { diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index 79708430..c9347864 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -17,8 +17,10 @@ package com.android.javacard.keymaster; import com.android.javacard.seprovider.KMAttestationCert; +import com.android.javacard.seprovider.KMDataStoreConstants; import com.android.javacard.seprovider.KMDeviceUniqueKeyPair; import com.android.javacard.seprovider.KMException; +import com.android.javacard.seprovider.KMOperation; import com.android.javacard.seprovider.KMSEProvider; import javacard.framework.APDU; import javacard.framework.Applet; @@ -28,6 +30,7 @@ import javacard.framework.JCSystem; import javacard.framework.Util; import javacard.security.CryptoException; +import javacard.security.Signature; import javacardx.apdu.ExtendedLength; /** @@ -247,12 +250,12 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe // 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 = 2; + public static final short KEYBLOB_CURRENT_VERSION = 3; // KeyBlob Verion 1 constant. public static final short KEYBLOB_VERSION_1 = 1; // KeyBlob array size constants. - public static final byte SYM_KEY_BLOB_SIZE_V2 = 6; - public static final byte ASYM_KEY_BLOB_SIZE_V2 = 7; + public static final byte SYM_KEY_BLOB_SIZE_V2_V3 = 6; + public static final byte ASYM_KEY_BLOB_SIZE_V2_V3 = 7; public static final byte SYM_KEY_BLOB_SIZE_V1 = 5; public static final byte ASYM_KEY_BLOB_SIZE_V1 = 6; public static final byte SYM_KEY_BLOB_SIZE_V0 = 4; @@ -895,7 +898,8 @@ private short createKeyBlobExp(short version) { KMArray.cast(keyBlob).add((short) 5, byteBlobExp);// PubKey break; case (short) 2: - keyBlob = KMArray.instance(ASYM_KEY_BLOB_SIZE_V2); + case (short) 3: + keyBlob = KMArray.instance(ASYM_KEY_BLOB_SIZE_V2_V3); KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_VERSION_OFFSET, KMInteger.exp()); KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_SECRET, byteBlobExp); KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_AUTH_TAG, byteBlobExp); @@ -914,10 +918,10 @@ private static short createKeyBlobInstance(byte keyType) { short arrayLen = 0; switch (keyType) { case ASYM_KEY_TYPE: - arrayLen = ASYM_KEY_BLOB_SIZE_V2; + arrayLen = ASYM_KEY_BLOB_SIZE_V2_V3; break; case SYM_KEY_TYPE: - arrayLen = SYM_KEY_BLOB_SIZE_V2; + arrayLen = SYM_KEY_BLOB_SIZE_V2_V3; break; default: KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); @@ -1140,11 +1144,11 @@ private void processUpgradeKeyCmd(APDU apdu) { byte keyType = getKeyType(data[HW_PARAMETERS]); switch (keyType) { case ASYM_KEY_TYPE: - data[KEY_BLOB] = KMArray.instance(ASYM_KEY_BLOB_SIZE_V2); + data[KEY_BLOB] = KMArray.instance(ASYM_KEY_BLOB_SIZE_V2_V3); 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_V2); + data[KEY_BLOB] = KMArray.instance(SYM_KEY_BLOB_SIZE_V2_V3); break; default: KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); @@ -2630,7 +2634,7 @@ private void authorizeAndBeginOperation(KMOperationState op, byte[] scratchPad) KMError.INVALID_KEY_BLOB); //Validate early boot - //VTS expects error code EARLY_BOOT_ONLY during begin operation if eary boot ended tag is present + //VTS expects error code EARLY_BOOT_ONLY during begin operation if early boot ended tag is present if (kmDataStore.getEarlyBootEndedStatus()) { KMTag.assertAbsence(data[HW_PARAMETERS], KMType.BOOL_TAG, KMType.EARLY_BOOT_ONLY, KMError.EARLY_BOOT_ENDED); @@ -3889,8 +3893,6 @@ private static void createEncryptedKeyBlob(byte[] 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(KEYBLOB_CURRENT_VERSION, scratchPad); // encrypt the secret and cryptographically attach that to authorization data encryptSecret(scratchPad); // create key blob array @@ -3993,6 +3995,7 @@ private void readKeyBlobParams(short version, short parsedKeyBlob) { } break; case (short) 2: + case (short) 3: 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); @@ -4002,7 +4005,7 @@ private void readKeyBlobParams(short version, short parsedKeyBlob) { 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_V2) { + if (KMArray.cast(parsedKeyBlob).length() == ASYM_KEY_BLOB_SIZE_V2_V3) { data[PUB_KEY] = KMArray.cast(parsedKeyBlob).get(KEY_BLOB_PUB_KEY); } break; @@ -4026,7 +4029,8 @@ private void decodeKeyBlob(short version, short keyBlob) { minArraySize = SYM_KEY_BLOB_SIZE_V1; break; case 2: - minArraySize = SYM_KEY_BLOB_SIZE_V2; + case 3: + minArraySize = SYM_KEY_BLOB_SIZE_V2_V3; break; default: KMException.throwIt(KMError.INVALID_KEY_BLOB); @@ -4045,10 +4049,8 @@ private void processDecryptSecret(short version, short appId, short appData, byt data[HW_PARAMETERS] = KMKeyParameters.makeHwEnforced(data[SB_PARAMETERS], data[TEE_PARAMETERS]); data[HIDDEN_PARAMETERS] = KMKeyParameters.makeHidden(appId, appData, data[ROT], scratchPad); - // make auth data - makeAuthData(version, scratchPad); // Decrypt Secret and verify auth tag - decryptSecret(scratchPad); + decryptSecret(scratchPad, version); short keyBlobSecretOff = 0; switch(version) { case 0: @@ -4075,6 +4077,7 @@ private void processDecryptSecret(short version, short appId, short appData, byt keyBlobSecretOff = (short) 1; break; case 2: + case 3: // V2 KeyBlob // KEY_BLOB = [ // VERSION, @@ -4130,10 +4133,31 @@ public static short readROT(byte[] scratchPad, short version) { return KMByteBlob.instance(scratchPad, (short) 0, len); } - private void decryptSecret(byte[] scratchPad) { + private void decryptSecret(byte[] scratchPad, short version) { // derive master key - stored in derivedKey - short len = deriveKey(scratchPad); - if (!seProvider.aesGCMDecrypt( + short len; + short authDataOff = 0; + short authDataLen = 0; + byte[] authDataBuff = null; + switch (version) { + case 3: + len = deriveKey(scratchPad); + break; + + case 2: + case 1: + case 0: + makeAuthData(version, scratchPad); + len = deriveKeyForOldKeyBlobs(scratchPad); + authDataBuff = repository.getHeap(); + authDataOff = data[AUTH_DATA]; + authDataLen = data[AUTH_DATA_LENGTH]; + break; + default: + KMException.throwIt(KMError.INVALID_KEY_BLOB); + + } + if (!seProvider.aesGCMDecrypt( KMByteBlob.cast(data[DERIVED_KEY]).getBuffer(), KMByteBlob.cast(data[DERIVED_KEY]).getStartOff(), KMByteBlob.cast(data[DERIVED_KEY]).length(), @@ -4144,7 +4168,7 @@ private void decryptSecret(byte[] scratchPad) { KMByteBlob.cast(data[NONCE]).getBuffer(), KMByteBlob.cast(data[NONCE]).getStartOff(), KMByteBlob.cast(data[NONCE]).length(), - repository.getHeap(), data[AUTH_DATA], data[AUTH_DATA_LENGTH], + authDataBuff, authDataOff, authDataLen, KMByteBlob.cast(data[AUTH_TAG]).getBuffer(), KMByteBlob.cast(data[AUTH_TAG]).getStartOff(), KMByteBlob.cast(data[AUTH_TAG]).length())) { @@ -4165,7 +4189,7 @@ private static void encryptSecret(byte[] scratchPad) { KMByteBlob.cast(data[NONCE]).length()); // derive master key - stored in derivedKey short len = deriveKey(scratchPad); - len = seProvider.aesGCMEncrypt( + len = seProvider.aesGCMEncrypt( KMByteBlob.cast(data[DERIVED_KEY]).getBuffer(), KMByteBlob.cast(data[DERIVED_KEY]).getStartOff(), KMByteBlob.cast(data[DERIVED_KEY]).length(), @@ -4177,9 +4201,9 @@ private static void encryptSecret(byte[] scratchPad) { KMByteBlob.cast(data[NONCE]).getBuffer(), KMByteBlob.cast(data[NONCE]).getStartOff(), KMByteBlob.cast(data[NONCE]).length(), - repository.getHeap(), - data[AUTH_DATA], - data[AUTH_DATA_LENGTH], + null, + (short)0, + (short)0, KMByteBlob.cast(data[AUTH_TAG]).getBuffer(), KMByteBlob.cast(data[AUTH_TAG]).getStartOff(), KMByteBlob.cast(data[AUTH_TAG]).length()); @@ -4275,7 +4299,7 @@ private static void makeAuthData(short version, byte[] scratchPad) { data[AUTH_DATA_LENGTH] = len; } - private static short deriveKey(byte[] scratchPad) { + private static short deriveKeyForOldKeyBlobs(byte[] scratchPad) { // KeyDerivation: // 1. Do HMAC Sign, Auth data. // 2. HMAC Sign generates an output of 32 bytes length. @@ -4296,6 +4320,77 @@ private static short deriveKey(byte[] scratchPad) { return len; } + private static short deriveKey(byte[] scratchPad) { + // For KeyBlob V3: Auth Data includes HW_PARAMETERS, HIDDEN_PARAMETERS, CUSTOM_TAGS, VERSION and PUB_KEY. + short index = 0; + Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 10, (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. CUSTOM_TAGS + // 3. VERSION ( Only Version >= 1) + // 4. PUB_KEY ( Only for Asymmetric Keys) + short numParams = 4; + Util.setShort(scratchPad, (short) 0, KMKeyParameters.cast(data[HW_PARAMETERS]).getVals()); + Util.setShort(scratchPad, (short) 2, KMKeyParameters.cast(data[HIDDEN_PARAMETERS]).getVals()); + Util.setShort(scratchPad, (short) 4, KMKeyParameters.cast(data[CUSTOM_TAGS]).getVals()); + Util.setShort(scratchPad, (short) 6, data[KEY_BLOB_VERSION_DATA_OFFSET]); + // For Asymmetric Keys include the PUB_KEY. + if (keyType == ASYM_KEY_TYPE) { + numParams = 5; + Util.setShort(scratchPad, (short) 8, data[PUB_KEY]); + } + short prevReclaimIndex = repository.getHeapReclaimIndex(); + short authIndex = repository.allocReclaimableMemory(MAX_AUTH_DATA_SIZE); + Util.arrayFillNonAtomic(repository.getHeap(), authIndex, MAX_AUTH_DATA_SIZE, (byte) 0); + short len = 0; + KMOperation operation = null; + try { + operation = seProvider.initSymmetricOperation( + KMType.SIGN, + KMType.HMAC, + KMType.SHA2_256, + KMType.PADDING_NONE, + (byte)KMType.INVALID_VALUE, + (Object)kmDataStore.getMasterKey(), + KMDataStoreConstants.INTERFACE_TYPE_MASTER_KEY, + (byte[])null, + (short)0, + (short)0, + (short)0, false); + + byte arrayHeader = (byte) 0x80; + arrayHeader |= (byte) numParams; + ((byte[])repository.getHeap())[authIndex] = arrayHeader; + operation.update(repository.getHeap(), authIndex, (short) 1); + + while (index < numParams) { + short tag = Util.getShort(scratchPad, (short) (index * 2)); + len = encoder.encode(tag, repository.getHeap(), (short) authIndex, prevReclaimIndex); + operation.update(repository.getHeap(), authIndex, len); + index++; + } + repository.reclaimMemory(MAX_AUTH_DATA_SIZE); + // KeyDerivation: + // 1. Do HMAC Sign, Auth data. + // 2. HMAC Sign generates an output of 32 bytes length. + // Consume only first 16 bytes as derived key. + // Hmac sign. + len = operation.sign(scratchPad, (short)0, (short)0, scratchPad, (short)0); + } finally { + if (operation != null) { + operation.abort(); + } + } + if (len < 16) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + len = 16; + data[DERIVED_KEY] = KMByteBlob.instance(scratchPad, (short)0, len); + return len; + } + public static void sendResponse(APDU apdu, short err) { short resp = KMArray.instance((short)1); err = KMError.translate(err); diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymintDataStore.java b/Applet/src/com/android/javacard/keymaster/KMKeymintDataStore.java index a2edeb82..c79a0a8c 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymintDataStore.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymintDataStore.java @@ -910,7 +910,7 @@ private void handlePreviousVersionUpgrade(Element element) { element.readObject(); // pop verifiedHash element.readObject(); //pop bootKey element.readObject(); // pop bootPatchLevel - additionalCertChain = (byte[]) element.readObject(); // + additionalCertChain = (byte[]) element.readObject(); bcc = (byte[]) element.readObject(); // Read Key Objects @@ -953,12 +953,13 @@ void handleProvisionStatusUpgrade(byte[] dataTable, short dataTableIndex){ short dInex = repository.allocReclaimableMemory((short)2); byte data[] = repository.getHeap(); getProvisionStatus(dataTable, data, dInex); - JCSystem.beginTransaction(); - provisionStatus = (short)( data[dInex] & 0x00ff); + short pStatus = (short)( data[dInex] & 0x00ff); if( KMKeymasterApplet.PROVISION_STATUS_PROVISIONING_LOCKED - == (provisionStatus & KMKeymasterApplet.PROVISION_STATUS_PROVISIONING_LOCKED)) { - provisionStatus |= KMKeymasterApplet.PROVISION_STATUS_SE_LOCKED; + == (pStatus & KMKeymasterApplet.PROVISION_STATUS_PROVISIONING_LOCKED)) { + pStatus |= KMKeymasterApplet.PROVISION_STATUS_SE_LOCKED; } + JCSystem.beginTransaction(); + provisionStatus = pStatus; JCSystem.commitTransaction(); repository.reclaimMemory((short)2); } @@ -980,12 +981,10 @@ public short getBackupObjectCount() { // BCC - 1 // oemRootPublicKey - 1 return (short) (11 + - seProvider.getBackupObjectCount(KMDataStoreConstants.INTERFACE_TYPE_COMPUTED_HMAC_KEY) + seProvider.getBackupObjectCount(KMDataStoreConstants.INTERFACE_TYPE_MASTER_KEY) + seProvider.getBackupObjectCount(KMDataStoreConstants.INTERFACE_TYPE_PRE_SHARED_KEY) + seProvider.getBackupObjectCount(KMDataStoreConstants.INTERFACE_TYPE_DEVICE_UNIQUE_KEY_PAIR) + - seProvider.getBackupObjectCount( - KMDataStoreConstants.INTERFACE_TYPE_RKP_MAC_KEY)); + seProvider.getBackupObjectCount(KMDataStoreConstants.INTERFACE_TYPE_RKP_MAC_KEY)); } } diff --git a/Applet/src/com/android/javacard/keymaster/KMOperationState.java b/Applet/src/com/android/javacard/keymaster/KMOperationState.java index 8c9bb545..9ad4f5e0 100644 --- a/Applet/src/com/android/javacard/keymaster/KMOperationState.java +++ b/Applet/src/com/android/javacard/keymaster/KMOperationState.java @@ -311,8 +311,9 @@ public byte getBufferingMode() { short padding = getPadding(); short blockMode = getBlockMode(); - if (alg == KMType.RSA && digest == KMType.DIGEST_NONE && purpose == KMType.SIGN) { - return KMType.BUF_RSA_NO_DIGEST; + if (alg == KMType.RSA && ((digest == KMType.DIGEST_NONE && purpose == KMType.SIGN) || + purpose == KMType.DECRYPT)) { + return KMType.BUF_RSA_DECRYPT_OR_NO_DIGEST; } if (alg == KMType.EC && digest == KMType.DIGEST_NONE && purpose == KMType.SIGN) { diff --git a/Applet/src/com/android/javacard/keymaster/KMType.java b/Applet/src/com/android/javacard/keymaster/KMType.java index ccb9e37d..2bee7326 100644 --- a/Applet/src/com/android/javacard/keymaster/KMType.java +++ b/Applet/src/com/android/javacard/keymaster/KMType.java @@ -346,7 +346,7 @@ public abstract class KMType { public static final byte FAKE_CERT = 3; // Buffering Mode public static final byte BUF_NONE = 0; - public static final byte BUF_RSA_NO_DIGEST = 1; + public static final byte BUF_RSA_DECRYPT_OR_NO_DIGEST = 1; public static final byte BUF_EC_NO_DIGEST = 2; public static final byte BUF_AES_ENCRYPT_PKCS7_BLOCK_ALIGN = 3; public static final byte BUF_AES_DECRYPT_PKCS7_BLOCK_ALIGN = 4; diff --git a/HAL/JavacardKeyMintOperation.cpp b/HAL/JavacardKeyMintOperation.cpp index 4d97c6d4..2dc0c71c 100644 --- a/HAL/JavacardKeyMintOperation.cpp +++ b/HAL/JavacardKeyMintOperation.cpp @@ -62,7 +62,7 @@ ScopedAStatus JavacardKeyMintOperation::update(const vector& input, return km_utils::kmError2ScopedAStatus(err); } if (!(bufferingMode_ == BufferingMode::EC_NO_DIGEST || - bufferingMode_ == BufferingMode::RSA_NO_DIGEST)) { + bufferingMode_ == BufferingMode::RSA_DECRYPT_OR_NO_DIGEST)) { if (view.length > MAX_CHUNK_SIZE) { err = updateInChunks(view, aToken, tToken, output); if (err != KM_ERROR_OK) { @@ -86,7 +86,7 @@ ScopedAStatus JavacardKeyMintOperation::finish( DataView view = {.buffer = {}, .data = inData, .start = 0, .length = inData.size()}; const vector sign = signature.value_or(vector()); if (!(bufferingMode_ == BufferingMode::EC_NO_DIGEST || - bufferingMode_ == BufferingMode::RSA_NO_DIGEST)) { + bufferingMode_ == BufferingMode::RSA_DECRYPT_OR_NO_DIGEST)) { appendBufferedData(view); if (view.length > MAX_CHUNK_SIZE) { auto err = updateInChunks(view, aToken, tToken, output); @@ -166,7 +166,7 @@ uint16_t JavacardKeyMintOperation::getDataViewOffset(DataView& view, uint16_t bl keymaster_error_t JavacardKeyMintOperation::bufferData(DataView& view) { if (view.data.empty()) return KM_ERROR_OK; // nothing to buffer switch (bufferingMode_) { - case BufferingMode::RSA_NO_DIGEST: + case BufferingMode::RSA_DECRYPT_OR_NO_DIGEST: buffer_.insert(buffer_.end(), view.data.begin(), view.data.end()); if (buffer_.size() > RSA_BUFFER_SIZE) { abort(); diff --git a/HAL/JavacardKeyMintOperation.h b/HAL/JavacardKeyMintOperation.h index 2f9815e2..0bfeaa86 100644 --- a/HAL/JavacardKeyMintOperation.h +++ b/HAL/JavacardKeyMintOperation.h @@ -41,11 +41,11 @@ using std::vector; // Bufferig modes for update enum class BufferingMode : int32_t { - NONE = 0, // Send everything to javacard - most of the assymteric operations - RSA_NO_DIGEST = 1, // Buffer everything in update upto 256 bytes and send in finish. If - // input data is greater then 256 bytes then it is an error. Javacard - // will further check according to exact key size and crypto provider. - EC_NO_DIGEST = 2, // Buffer upto 65 bytes and then truncate. Javacard will further truncate + NONE = 0, // Send everything to javacard - most of the assymteric operations + RSA_DECRYPT_OR_NO_DIGEST = 1, // Buffer everything in update upto 256 bytes and send in finish. If + // input data is greater then 256 bytes then it is an error. Javacard + // will further check according to exact key size and crypto provider. + EC_NO_DIGEST = 2, // Buffer upto 65 bytes and then truncate. Javacard will further truncate // upto exact keysize. BUF_AES_ENCRYPT_PKCS7_BLOCK_ALIGNED = 3, // Buffer 16 bytes. BUF_AES_DECRYPT_PKCS7_BLOCK_ALIGNED = 4, // Buffer 16 bytes.