2

Im investigating the use of com.google.crypto.tink:tink-android:1.6.1 in my current Android project.

The data I am encrypting includes the OAuth2 Access Token/Refresh Token I employ for my remote API calls, e.g. Access Token is my Bearer token for the Authorisation HTTP header.

Im concerned I have made an error in my encryption/decryption logic as I am experiencing an intermittent problem where I cannot Refresh the Token. The error from the server

{"error_description":"unknown, invalid, or expired refresh token","error":"invalid_grant"}

The refresh token cannot be expired as it lasts 24 hours.

My code that initialises Tink resembles this:-

private fun manageTink() {
    try {
        AeadConfig.register()
        authenticatedEncryption = generateNewKeysetHandle().getPrimitive(Aead::class.java)
    } catch (e: GeneralSecurityException) {
        throw RuntimeException(e)
    } catch (e: IOException) {
        throw RuntimeException(e)
    }
}

@Throws(IOException::class, GeneralSecurityException::class)
private fun generateNewKeysetHandle(): KeysetHandle =
    AndroidKeysetManager
        .Builder()
        .withSharedPref(this, TINK_KEYSET_NAME, PREF_FILE_NAME)
        .withKeyTemplate(KeyTemplates.get("AES256_GCM"))
        .withMasterKeyUri(MASTER_KEY_URI)
        .build()
        .keysetHandle

Here is my code for encryption/decryption

import android.util.Base64
import com.google.crypto.tink.Aead
import javax.inject.Inject

const val BASE64_ENCODE_SETTINGS = Base64.NO_WRAP or Base64.NO_PADDING

data class Security @Inject constructor(private val authenticatedEncryption: Aead) {

    fun conceal(plainText: String, associatedData: String): String {
        val plain64 = Base64.encode(plainText.encodeToByteArray(), BASE64_ENCODE_SETTINGS)
        val associated64 = Base64.encode(associatedData.encodeToByteArray(), BASE64_ENCODE_SETTINGS)
        val encrypted: ByteArray? = authenticatedEncryption.encrypt(plain64, associated64)

        return Base64.encodeToString(encrypted, BASE64_ENCODE_SETTINGS)
    }

    fun reveal(encrypted64: String, associatedData: String): String {
        val encrypted = Base64.decode(encrypted64.encodeToByteArray(), BASE64_ENCODE_SETTINGS)
        val associated64 = Base64.encode(associatedData.encodeToByteArray(), BASE64_ENCODE_SETTINGS)
        val decrypted: ByteArray? = authenticatedEncryption.decrypt(encrypted, associated64)

        return String(Base64.decode(decrypted, BASE64_ENCODE_SETTINGS))
    }
}

Could the use of Base64 encode/decode be the issue? Where is my mistake?

Hector
  • 4,016
  • 21
  • 112
  • 211
  • 1
    Just a note- why do you Base64 encoding the plaintext and aad data before encryption? – Michael Fehr Jul 30 '21 at 15:05
  • @MichaelFehr, I Base64 encode because some of my data may have unprintable characters. Its just to be on the safe side. This was causing me issues as my Base64 flags did include URL_SAFE, which was "corrupting/changing" data between encode and decode. – Hector Jul 30 '21 at 15:08
  • 1
    Can you reproduce an intermittent encryption/decryption issue _without_ an OAuth2 token? If not, maybe it's the token after all (in some unforeseen way). The intermittent thing smells kind of token validation related issue. – Topaco Aug 07 '21 at 10:27
  • @Topaco, my issue only occurs when processing a token... I believe you are correct its the token itself that is invalid, the Tink is not causing any problems – Hector Aug 08 '21 at 09:31

1 Answers1

1

If Tink can decrypt your token, the issue should be with your token/the encoding and not with Tink (It is using authenticated encryption, so you are guaranteed that the bytes that you encrypted are going to be the bytes that you decrypt, or you get an error).

To also address the concern in the comment: Tink will happily encrypt and decrypt any byte strings (with the associated data also being allowed to be any byte string), and not be concerned with whether they are printable or even valid Unicode. You will have to base64 encode the output of Tink if you want to use it as a string, though, as it will be uniformly random distributed bytes, i.e. contain invalid Unicode and non printable characters with high probability.