I generated AES key on Android 10 device with StrongBox (Pixel 4) with setIsStrongBoxBacked=true, as follows:
fun generateKey(): SecretKey {
val generator = KeyGenerator.getInstance("AES", "AndroidKeyStore")
generator.init(
KeyGenParameterSpec.Builder("alias", PURPOSE_ENCRYPT or PURPOSE_DECRYPT)
.setBlockModes(BLOCK_MODE_GCM)
.setEncryptionPaddings(ENCRYPTION_PADDING_NONE)
.setIsStrongBoxBacked(true)
.build()
)
return generator.generateKey()
}
Next, using this key I encrypted some ByteArray. Some time later, during a decryption with the same key and the same IV vector, I got wrong ByteArray as a result with no exception.
Both encryption and decryption methods are pretty standard:
fun encrypt(plaintext: ByteArray, iv: ByteArray, key: SecretKey): ByteArray {
val cipher = Cipher.getInstance("AES/GCM/NoPadding")
cipher.init(ENCRYPT_MODE, key, GCMParameterSpec(128, iv))
return cipher.doFinal(plaintext)
}
fun decrypt(ciphertext: ByteArray, iv: ByteArray, key: SecretKey): ByteArray {
val cipher = Cipher.getInstance("AES/GCM/NoPadding")
cipher.init(DECRYPT_MODE, key, GCMParameterSpec(128, iv))
return cipher.doFinal(ciphertext)
}
Strangely, using the same key generation code but with setIsStrongBoxBacked=false, and the same encryption/decryption logic, everything works as expected.
Official documentation says nothing that could be helpful in my situation. For now, I can disable StrongBox backing logic, but I would like to know where the problem lies - is my code missing something crucial or is it Android 10/Pixel 4 problem?
P.S.
Upon further inspection I realised that something is wrong with the encryption logic as well. With setIsStrongBoxBacked=true, every time I encrypt any ByteArray using AES/GCM/NoPadding algorithm, ciphertext looks the same (only authentication tag part differs) even if IV is generated randomly. From what I know about GCM mode, something is't right.
encryption 1 with setIsStrongBoxBacked=true:
IV=[40, 91, -4, -98, 104, 94, -39, -83, -34, 22, -33, 12]
ciphertext=[13, -48, -83, -34, 13, -48, -83, -34, 13, -48, -83, -34, 13, -48, -83, -34, 13, -48, -83, -34, 13, -48, -83, -34, 13, -48, -83, -34, 13, -48, -83, -34, 12, -86, -16, -38, 93, 100, 82, 108, 101, -27, -42, -107, 36, 49, 109, 47]
encryption 2 with setIsStrongBoxBacked=true:
IV=[-123, -96, -66, -87, 43, -103, -126, -107, 8, -73, 68, -49]
ciphertext=[13, -48, -83, -34, 13, -48, -83, -34, 13, -48, -83, -34, 13, -48, -83, -34, 13, -48, -83, -34, 13, -48, -83, -34, 13, -48, -83, -34, 13, -48, -83, -34, 85, 108, -23, -62, 18, 74, -81, -2, 111, -94, 7, 15, -56, 106, -114, -69]
With setIsStrongBoxBacked=false the same encryption logic generates totally different ciphertexts as long as IV is differs every time, just like it supposed to:
encryption 1 with setIsStrongBoxBacked=false:
IV=[-113, -48, -5, -106, -86, -1, 121, -40, -13, -54, -56, 10]
ciphertext=[126, -97, 118, 42, -110, 67, 77, 9, 120, -79, 74, -101, -123, 106, -21, -18, -49, -123, -10, -33, -4, -99, 58, -125, 69, -37, 111, -66, -114, -47, 17, 41, -3, 72, -52, -36, -67, 47, 39, -108, -46, -113, 114, -46, -114, 24, -51, -95]
encryption 2 with setIsStrongBoxBacked=false:
IV=[-106, 12, -84, -108, -102, -11, 38, 30, 10, 22, -123, -28]
ciphertext=[-51, -85, -125, -34, -72, 47, -13, 6, -111, 14, -85, 53, -8, -68, -17, -81, -106, 50, -94, -22, 66, 74, -39, 12, 12, 74, 48, -41, -41, -55, -28, 2, 61, 11, 120, -3, 124, 8, -46, 78, -93, 97, 87, -97, -26, -42, -105, 74]
P.S.2
This problem occur only when use AES/GCM/NoPadding algorithm. For other AES modes (CBC, CRT, etc.) both encryption and decryption works as expected even for setIsStrongBoxBacked=true
P.S.3 issue - https://issuetracker.google.com/issues/147039506
P.S.4 After applying the newest security patch available for Pixel 4, problem has disappeared.