2

I did some tests with Cipher methods on Android to encrypt text (String) and decrypt it back to original string.

I need the fastest method (best performance). And it seems Blowfish is the fastest.

Am I right? Or is there something faster? Mb some other methods? I just need to encrypt strings

I'm also thinking about adding C/C++ implementation of Blowfish to Android (NDK, Cmake). Performance should be better.

Test code:

companion object {
    private const val PLAY_TEST_TEXT = "Some Text Line !"

    private val ISO = charset("ISO-8859-1") // is used because we can get our bytes from string back without any problems
    private val UTF = charset("UTF-8")
}

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    val handler = Handler()
    handler.postDelayed({

        testFastestCipher("AES/ECB/PKCS5Padding", "AES", false)
        testFastestCipher("AES/CBC/PKCS5Padding", "AES", true)
        testFastestCipher("AES/CFB/PKCS5Padding", "AES", true)
        testFastestCipher("AES/OFB/PKCS5Padding", "AES", true)
        testFastestCipher("AES/CTR/PKCS5Padding", "AES", true)

        testFastestCipher("Blowfish/ECB/PKCS5Padding", "Blowfish", false)
        testFastestCipher("Blowfish/CBC/PKCS5Padding", "Blowfish", true)
        testFastestCipher("Blowfish/CFB/PKCS5Padding", "Blowfish", true)
        testFastestCipher("Blowfish/OFB/PKCS5Padding", "Blowfish", true)
        testFastestCipher("Blowfish/CTR/PKCS5Padding", "Blowfish", true)
        testFastestCipher("Blowfish/CTR/PKCS5Padding", "Blowfish", true)

        if (Build.VERSION.SDK_INT >= 28) {
            testFastestCipher("ChaCha20/None/NoPadding", "ChaCha20", true)
        }
    }, 2000)
}

private fun testFastestCipher(transformation: String, keyAlgorithm: String, requiresIV: Boolean) {

    val cipherEnc = Cipher.getInstance(transformation)
    val cipherDec = Cipher.getInstance(transformation)

    val secretKeyString = when (keyAlgorithm) {
        "ChaCha20" -> "aNdRgUkXp2s5v8y/B?E(G+KbPeShVmYq"
        else -> "C&E)H@McQfTjWnZr"
    }
    val secretKey = SecretKeySpec(secretKeyString.toByteArray(), keyAlgorithm)

    if (requiresIV) {
        val ivKey = when (keyAlgorithm) {
            "Blowfish" -> "12345678"
            "ChaCha20" -> "123456789123"
            else -> "1234567891230000"
        }
        val iv = IvParameterSpec(ivKey.toByteArray())
        cipherEnc.init(Cipher.ENCRYPT_MODE, secretKey, iv)
        cipherDec.init(Cipher.DECRYPT_MODE, secretKey, iv)
    } else {
        cipherEnc.init(Cipher.ENCRYPT_MODE, secretKey)
        cipherDec.init(Cipher.DECRYPT_MODE, secretKey)
    }

    var encMsg = ""
    var decMsg = ""

    var timeTook = System.currentTimeMillis()

    for (i in 0..5000) {
        encMsg = encryptMsg(PLAY_TEST_TEXT, cipherEnc)
        decMsg = decryptMsg(encMsg, cipherDec)
    }

    timeTook = System.currentTimeMillis() - timeTook

    textView.text = "${textView.text}\n\n$transformation\nEnc (bytes displayed as ISO-8859-1): $encMsg\nDec: $decMsg\n$timeTook ms"
}

private fun encryptMsg(message: String, cipher: Cipher): String {
    val bytes = cipher.doFinal(message.toByteArray(UTF))
    return String(bytes, ISO)
}

private fun decryptMsg(cipherText: String, cipher: Cipher): String {
    val encryptedString = cipherText.toByteArray(ISO)
    return String(cipher.doFinal(encryptedString), UTF)
}

Results:

enter image description here

Update

More results (including external library ChaCha and internal):

1) Android 28 Emulator x86

for i in 0..500000

ChaCha (from third-party library bcprov-jdk15on) 3724 ms
Blowfish/ECB/PKCS5Padding 4216 ms
Blowfish/CBC/PKCS5Padding 4508 ms
Blowfish/OFB/PKCS5Padding 4991 ms
Blowfish/CTR/PKCS5Padding 5158 ms
Blowfish/CFB/PKCS5Padding 5296 ms
ChaCha20/None/NoPadding 5530 ms
AES/OFB/PKCS5Padding 6649 ms
AES/CTR/PKCS5Padding 6703 ms
AES/CFB/PKCS5Padding 7126 ms
AES/ECB/PKCS5Padding 10170 ms
AES/CBC/PKCS5Padding 10619 ms

2) Lenovo Android 21 ARM64

for i in 0..50000

Blowfish/ECB/PKCS5Padding 3270 ms
Blowfish/CTR/PKCS5Padding 3404 ms
Blowfish/CBC/PKCS5Padding 3483 ms
Blowfish/CFB/PKCS5Padding 3490 ms
Blowfish/OFB/PKCS5Padding 3502 ms
AES/CTR/PKCS5Padding 3577 ms
AES/CFB/PKCS5Padding 3692 ms
AES/OFB/PKCS5Padding 3709 ms
AES/ECB/PKCS5Padding 4739 ms
AES/CBC/PKCS5Padding 5248 ms
ChaCha (from third-party library bcprov-jdk15on) 10195 ms

3) Samsung Android 16 ARMv7

for i in 0..50000

ChaCha (from third-party library bcprov-jdk15on) 3969 ms
Blowfish/ECB/PKCS5Padding 5282 ms
Blowfish/CTR/PKCS5Padding 5569 ms
AES/CTR/PKCS5Padding 6160 ms
Blowfish/CBC/PKCS5Padding 6201 ms
AES/CFB/PKCS5Padding 6217 ms
AES/CBC/PKCS5Padding 6320 ms
Blowfish/OFB/PKCS5Padding 6382 ms
Blowfish/CFB/PKCS5Padding 6808 ms
AES/OFB/PKCS5Padding 7224 ms
AES/ECB/PKCS5Padding 7247 ms

ChaCha (from third-party library bcprov-jdk15on) works the best or the worst on different devices...

user924
  • 8,146
  • 7
  • 57
  • 139
  • 1
    "I'm also thinking about adding C/C++ implementation of Blowfish to Android (NDK, Cmake). Performance should be better" -- the Android implementation of the `javax.crypto` API has been in native code for quite some time. – CommonsWare Jun 02 '19 at 21:28
  • @CommonsWare I tested `AES/ECP/NoPadding` to encrypt text with Android default SDK and with Android C++ using "Tiny AES C" https://github.com/anonym24/Android-Tiny-AES-NDK and the latest method worked faster – user924 Jun 02 '19 at 21:35
  • 1
    There are probably some environments where blowfish in software is faster than AES in software, but the future belongs to AES. It's faster on 64 bit CPUs, and has hardware support on many CPUs now and many more in the future. – President James K. Polk Jun 02 '19 at 21:39
  • That sample basically has no Java code, whereas the code in your question is driving the test loop from within Java. Make sure that you are doing an "apples to apples" comparison between your tests. Also, the switch to native code was in Android 6.0; if you are testing on (or are concerned about) older devices, then an NDK implementation may well be worth considering. – CommonsWare Jun 02 '19 at 21:40
  • @CommonsWare yes I have tested on Android 6, I used that library in Android like this https://github.com/kokke/tiny-AES-c/issues/136#issuecomment-498055984 – user924 Jun 02 '19 at 21:43
  • @CommonsWare it seems that library works faster only on x86 Emulator, but not on real device with ARM (I tested on Android 5 Lenovo Device arm64), you said that native code was added in Android 4.3 https://stackoverflow.com/a/37411233/7767664 – user924 Jun 02 '19 at 22:26
  • That older answer is probably more accurate -- I did some light research to try to determine when the OpenSSL conversion was made for my earlier comment and came up with 6.0. – CommonsWare Jun 02 '19 at 22:37
  • @CommonsWare I also have Samsung with Android 4.1.2 and it gave 381 ms for Blowfish (same test `0..5000`), for Lenovo Android 5.1 - 316 ms. There is no big difference. – user924 Jun 02 '19 at 22:47
  • @JamesKPolk I believe most Android devices give faster results for Blowfish – user924 Jun 02 '19 at 22:49
  • 1
    I'm fairly certain we were still using Bouncy Castle for `javax.crypto` in the 4.1 timeframe, and that was implemented in Java. I would have expected some improvement for OpenSSL. But, I never ran these sorts of benchmarks, so `¯\_(ツ)_/¯`. – CommonsWare Jun 02 '19 at 22:58

1 Answers1

1

I was looking for a fast cipher before for other platforms, you may see following site https://rweather.github.io/arduinolibs/crypto.html

I found the ChaCha cipher fast and still commonly available. Speed of AES may depend on the platform version whether hardware acceleration is supported https://android.stackexchange.com/questions/186664/do-android-phones-have-hardware-chips-for-encryption-if-its-software-only-the (Blowfish is not yet broken, but its author has already expressed the cipher should be considered obsolete)

as improvement - cipher modes (ctr, ofb) need no padding, you may use 'NoPadding' value there

gusto2
  • 11,210
  • 2
  • 17
  • 36
  • Then why they specify padding for CTR/OFB there? https://developer.android.com/reference/javax/crypto/Cipher – user924 Jun 03 '19 at 06:50
  • I see that ChaCha only available from 28 Android. Mb is there some library for Android? – user924 Jun 03 '19 at 07:35
  • @user924 You may try to use the BouncyCastle provider. The padding - with the stream-like modes you may use the padding if you specify it, but it's not necessary – gusto2 Jun 03 '19 at 09:02
  • @user924 BouncyCastle is pure inline implementation. You don't need to use the `ChaChaEngine` you may use the default java crypto with `ChaCha` cipher (https://stackoverflow.com/questions/32672241/using-bouncycastles-chacha-for-file-encryption) – gusto2 Jun 03 '19 at 13:55
  • `Security.addProvider(BouncyCastleProvider())` returns -1 (the preference position in which the provider was added, or -1 if the provider was not added because it is already installed.) – user924 Jun 03 '19 at 18:43
  • Actually `ChaChaEngine` from `bcprov-jdk15on` is much slower than any internal methods in Android – user924 Jun 03 '19 at 19:00
  • I guess we need to use ChaCha in C++ to get a comparable speed – user924 Jun 03 '19 at 19:00
  • I tested internal ChaCha20 from Android 28 (Emulator x86) and it's not faster than Blowfish. On weird thing that ChaChaEngine from bcprov-jdk15on library works very slow on real devices but it's the best on Emulators x86 – user924 Jun 03 '19 at 19:18
  • Actually external ChaCha worked the best also on old Samsung with Android 4.1.2 but it the worst on Lenovo Android 5.1. I've updated my question with additional test results – user924 Jun 03 '19 at 19:47