Decrypt multiple 10Mb files with defined password One operation of my Android project is to decrypt one file (chosen in a set of ~30 files for pagination1) of ~10 Mb each and I do this with the following:

fun InputStream.cipherDecryptWithSessionPwd(password: String): String {
    var payloadArray: List&lt;Byte&gt;? = this.readBytes().toList()
    var salt = payloadArray?.subList(0,32)
    var spec: Rfc2898DeriveBytes? = Rfc2898DeriveBytes(password, salt?.toByteArray(), 50000)
    var realData = payloadArray?.subList(32, payloadArray.size)

    val keyBytes = spec?.getBytes(256 / 8)
    val ivBytes = spec?.getBytes(128 / 8)

    var cipher: Cipher? = Cipher.getInstance("AES/CFB/PKCS7Padding")
    var secretKey: SecretKeySpec? = SecretKeySpec(keyBytes, "AES")
    var ivSpec: IvParameterSpec? = IvParameterSpec(ivBytes)
    cipher?.init(Cipher.DECRYPT_MODE, secretKey, ivSpec)
    var byteArrayInputStream: ByteArrayInputStream? = ByteArrayInputStream(realData?.toByteArray())
    var decipherStream: CipherInputStream? = CipherInputStream(byteArrayInputStream, cipher)

    val d = BufferedReader(InputStreamReader(decipherStream, Charsets.UTF_8), 0xFFFF)
    return d.readLine()
}


The problem with this is that at around the 72nd time2 I repeat the operation Android kills my application because it runs out of memory3.

This is the second version of the decryption function, the first one caused the OOM error around the 25th time the operation was made. The problem here clearly is the number of conversions done (InputStream -> ByteArray -> List&lt;Byte&gt; and others).

I don't know how to optimize more, the first version of the method was not mine and I never did encryption (and optimization) before.

Scenario (and what have been tried):
 1 - Files are stored encrypted on the device, can't have them decrypted but at runtime
 2 - With the decrypted result (JSON) I fill a model using [Gson] (https://github.com/google/gson), but the OOME seem to be thrown when my function is running (the exception points at the first line of the function)
 3 - Calculations are done and the model is disposed of when the next page is shown, then the whole operation has to be repeated
 # - We tried reducing the number of JSON "records" inside each file from 5000 per file to 1000 per file reducing the file size to 2 Mb but the problem persists

Notes:
 1 - We event tried reducing the pagination reaching files size to 2 Mb but the problem persists
 2 - This has to be done at least 130 times per session
 3 - With, Knox my application is the only one allowed to run on the device
I can't help too much as I don't have running code, but I can at least get rid of all List and Array conversions, that will make it run faster and reduce lot of memory garbage. I also removed all the optionals as immutability is our best friend :)

Is it ran once per stream and then it is closed or more than once per stream? There is no information about how stream is being handled and if it is closed properly. That can be issue.

fun InputStream.cipherDecryptWithSessionPwd(password: String): String {
    val payloadArray: ByteArray = this.readBytes()
    val salt = payloadArray.sliceArray(0 until 32)
    val spec = Rfc2898DeriveBytes(password, salt, 50000)
    val realData = payloadArray.sliceArray(32 until payloadArray.size)

    val keyBytes = spec.getBytes(256 / 8)
    val ivBytes = spec.getBytes(128 / 8)

    val cipher = Cipher.getInstance("AES/CFB/PKCS7Padding")
    val secretKey: SecretKeySpec? = SecretKeySpec(keyBytes, "AES")
    val ivSpec: IvParameterSpec? = IvParameterSpec(ivBytes)
    cipher.init(Cipher.DECRYPT_MODE, secretKey, ivSpec)
    val byteArrayInputStream = ByteArrayInputStream(realData)
    val decipherStream = CipherInputStream(byteArrayInputStream, cipher)

    val d = BufferedReader(InputStreamReader(decipherStream, Charsets.UTF_8), 0xFFFF)
    return d.readLine()
}


Edit:

I was thinking what you could cache and only possible variable is I guess cipher:

val cipher = Cipher.getInstance("AES/CFB/PKCS7Padding")

It probably won't speed up performance as I expect it to be cached factory method, but you can try putting variable outside of function anyway :)