I am using incremental encrpyption, in combination with Android KeyStore provider.
val cipher = Cipher.getInstance(TRANSFORMATION)
cipher.init(Cipher.ENCRYPT_MODE, getSecretKey())
val chunks = textToEncrypt.chunked(CHUNK_SIZE)
val encryptedChunks: MutableList<ByteArray?> = mutableListOf()
chunks.forEachIndexed { index, chunk ->
if (index == chunks.size - 1) {
encryptedChunks.add(cipher.doFinal(chunk.toByteArray(StandardCharsets.UTF_8)))
} else {
encryptedChunks.add(cipher.update(chunk.toByteArray(StandardCharsets.UTF_8)))
}
}
val result = encryptedChunks.filterNotNull().reduce { acc, item -> acc.plus(item) }
These are the constants I am using:
const val TRANSFORMATION = "AES/GCM/NoPadding"
const val CHUNK_SIZE = 32768 // 32KiB
Now, this code has been heavily tested on more than 30 different devices, and never had any problem whatsoever, except with one phone (Xperia XA with Android 7.0). For this phone, if the input (textToEncrypt) is small enough that everything can be encrypted in a single chunk then it is fine, but if it is bigger (normally around 100KiB), so that it needs more chunks, then it will not be able to encrypt the data. This is what I get:
Caused by javax.crypto.IllegalBlockSizeException
at android.security.keystore.AndroidKeyStoreCipherSpiBase.engineDoFinal(AndroidKeyStoreCipherSpiBase.java:491)
at javax.crypto.Cipher.doFinal(Cipher.java:2056)
Caused by android.security.KeyStoreException: Memory allocation failed
at android.security.KeyStore.getKeyStoreException(KeyStore.java:685)
at android.security.keystore.KeyStoreCryptoOperationChunkedStreamer.update(KeyStoreCryptoOperationChunkedStreamer.java:132)
at android.security.keystore.AndroidKeyStoreCipherSpiBase.engineUpdate(AndroidKeyStoreCipherSpiBase.java:338)
at javax.crypto.Cipher.update(Cipher.java:1683)
NOTE: Only for this device the cipher.update() returns null with ENCRYPT_MODE , that's why in my code I allow to return null, and then discard them for forming the encrypted data.
This means that cipher.doFinal is supposed to return in one go the whole encrypted data.
EDIT: So apparently only for this phone the chunk size is not fine: it cannot be 32Kb, but 8Kb works fine