diff --git a/core/src/main/java/de/raphaelebner/roomdatabasebackup/core/AESEncryptionHelper.kt b/core/src/main/java/de/raphaelebner/roomdatabasebackup/core/AESEncryptionHelper.kt index 3226c09..0ae8dbe 100644 --- a/core/src/main/java/de/raphaelebner/roomdatabasebackup/core/AESEncryptionHelper.kt +++ b/core/src/main/java/de/raphaelebner/roomdatabasebackup/core/AESEncryptionHelper.kt @@ -45,11 +45,8 @@ class AESEncryptionHelper { */ @Throws(Exception::class) fun readFile(file: File): ByteArray { - val fileContents = file.readBytes() - val inputBuffer = BufferedInputStream(FileInputStream(file)) - inputBuffer.read(fileContents) - inputBuffer.close() - return fileContents + // read file to ByteArray + return file.readBytes() } /** @@ -59,10 +56,12 @@ class AESEncryptionHelper { */ @Throws(Exception::class) fun saveFile(fileData: ByteArray, file: File) { - val bos = BufferedOutputStream(FileOutputStream(file, false)) - bos.write(fileData) - bos.flush() - bos.close() + // Even if an exception occurs in the use block, the resource will be closed correctly, + // which helps prevent resource leaks and improve application stability and performance. + BufferedOutputStream(FileOutputStream(file, false)).use { bos -> + bos.write(fileData) + bos.flush() + } } /** @@ -84,15 +83,8 @@ class AESEncryptionHelper { val charset = ('a'..'z') + ('A'..'Z') + ('1'..'9') password = (1..stringLength).map { charset.random() }.joinToString("") - val secretKey = generateSecretKey(password, iv) - // the key can be saved plain, because i am using EncryptedSharedPreferences - val editor = sharedPref.edit() - editor.putString(BACKUP_SECRET_KEY, password) - // I use .commit because when using .apply the needed app restart is faster then apply - // and the preferences wont be saved - editor.commit() - - return secretKey + // save key to shared pref + sharedPref.edit().putString(BACKUP_SECRET_KEY, password).commit() } // generate secretKey, and return it diff --git a/core/src/main/java/de/raphaelebner/roomdatabasebackup/core/AESEncryptionManager.kt b/core/src/main/java/de/raphaelebner/roomdatabasebackup/core/AESEncryptionManager.kt index 7611362..195a9b5 100644 --- a/core/src/main/java/de/raphaelebner/roomdatabasebackup/core/AESEncryptionManager.kt +++ b/core/src/main/java/de/raphaelebner/roomdatabasebackup/core/AESEncryptionManager.kt @@ -22,6 +22,10 @@ class AESEncryptionManager { private val aesEncryptionHelper = AESEncryptionHelper() + // Creating a new Cipher instance for each encryption and decryption operation may cause performance degradation because creating a Cipher instance is an expensive operation. + // Optimize this problem by creating a Cipher instance at the class level and reusing it when needed. + private val cipher = Cipher.getInstance("AES/GCM/NoPadding") + /** * This method will encrypt the given data * @param sharedPref : the sharedPref, to fetch the key @@ -50,7 +54,6 @@ class AESEncryptionManager { val secretKey = if (encryptPassword != null) { aesEncryptionHelper.getSecretKeyWithCustomPw(encryptPassword, iv) } else aesEncryptionHelper.getSecretKey(sharedPref, iv) - val cipher = Cipher.getInstance("AES/GCM/NoPadding") val parameterSpec = GCMParameterSpec(128, iv) //Encryption mode on! @@ -84,24 +87,21 @@ class AESEncryptionManager { ) fun decryptData(sharedPref: SharedPreferences, encryptPassword: String?, encryptedData: ByteArray): ByteArray { - - //Wrap the data into a byte buffer to ease the reading process - val byteBuffer = ByteBuffer.wrap(encryptedData) - val noonceSize = byteBuffer.int + // There is no need to convert to ByteBuffer, which will cause additional performance overhead. + val noonceSize = encryptedData[0].toInt() //Make sure that the file was encrypted properly require(!(noonceSize < 12 || noonceSize >= 16)) { "Nonce size is incorrect. Make sure that the incoming data is an AES encrypted file." } - val iv = ByteArray(noonceSize) - byteBuffer[iv] + + val iv = encryptedData.sliceArray(1 until noonceSize+1) //Prepare your key/password val secretKey = if (encryptPassword != null) { aesEncryptionHelper.getSecretKeyWithCustomPw(encryptPassword, iv) } else aesEncryptionHelper.getSecretKey(sharedPref, iv) //get the rest of encrypted data - val cipherBytes = ByteArray(byteBuffer.remaining()) - byteBuffer[cipherBytes] - val cipher = Cipher.getInstance("AES/GCM/NoPadding") + val cipherBytes = encryptedData.sliceArray(noonceSize+1 until encryptedData.size) + val parameterSpec = GCMParameterSpec(128, iv) //Encryption mode on! diff --git a/core/src/main/java/de/raphaelebner/roomdatabasebackup/core/RoomBackup.kt b/core/src/main/java/de/raphaelebner/roomdatabasebackup/core/RoomBackup.kt index dbb5181..6a4a376 100644 --- a/core/src/main/java/de/raphaelebner/roomdatabasebackup/core/RoomBackup.kt +++ b/core/src/main/java/de/raphaelebner/roomdatabasebackup/core/RoomBackup.kt @@ -386,10 +386,10 @@ class RoomBackup(var context: Context) { roomDatabase = null if (backupIsEncrypted) { val encryptedBytes = encryptBackup() ?: return - val bos = BufferedOutputStream(FileOutputStream(destination, false)) - bos.write(encryptedBytes) - bos.flush() - bos.close() + BufferedOutputStream(FileOutputStream(destination, false)).use { bos -> + bos.write(encryptedBytes) + bos.flush() + } } else { // Copy current database to save location (/files dir) copy(DATABASE_FILE, destination) @@ -577,10 +577,10 @@ class RoomBackup(var context: Context) { if (backupIsEncrypted) { copy(source, TEMP_BACKUP_FILE) val decryptedBytes = decryptBackup() ?: return - val bos = BufferedOutputStream(FileOutputStream(DATABASE_FILE, false)) - bos.write(decryptedBytes) - bos.flush() - bos.close() + BufferedOutputStream(FileOutputStream(DATABASE_FILE, false)).use { bos -> + bos.write(decryptedBytes) + bos.flush() + } } else { if (fileExtension == "aes") { if (enableLogDebug) @@ -622,10 +622,10 @@ class RoomBackup(var context: Context) { roomDatabase!!.close() roomDatabase = null - val bos = BufferedOutputStream(FileOutputStream(DATABASE_FILE, false)) - bos.write(decryptedBytes) - bos.flush() - bos.close() + BufferedOutputStream(FileOutputStream(DATABASE_FILE, false)).use { bos -> + bos.write(decryptedBytes) + bos.flush() + } } else { // Close the database roomDatabase!!.close()