Skip to content

Commit

Permalink
refactor: move away from keystore encryption
Browse files Browse the repository at this point in the history
Remove the encryption done by the keystore library, and instead migrate to just store the tokens in
shared preferences. They should be safe without being encrypted as they are just some tokens, and
require root to access anyway.

BREAKING CHANGE: Old shared preference values will be deprecated and no longer be valid
  • Loading branch information
Chesire committed Sep 10, 2021
1 parent e3277cc commit 8811f5b
Show file tree
Hide file tree
Showing 5 changed files with 159 additions and 46 deletions.
Original file line number Diff line number Diff line change
@@ -1,58 +1,43 @@
package com.chesire.nekome.datasource.auth.local

import android.content.SharedPreferences
import androidx.core.content.edit
import com.chesire.nekome.encryption.Cryption
import javax.inject.Inject

private const val ACCESS_TOKEN = "KEY_KITSU_ACCESS_TOKEN"
private const val REFRESH_TOKEN = "KEY_KITSU_REFRESH_TOKEN"
private const val ALIAS = "kitsuPrivateAuth"

/**
* Provides authorization for Kitsu access.
*
* Auth is encrypted using [cryption], it is then stored in the [preferences] for access.
*/
class AuthProvider @Inject constructor(
private val preferences: SharedPreferences,
private val cryption: Cryption
private val v1Auth: LocalAuthV1,
private val v2Auth: LocalAuthV2
) {
/**
* Access token to put into the Kitsu requests.
*/

init {
migrateFromV1ToV2()
}

var accessToken: String
get() = decryptedToken(preferences.getString(ACCESS_TOKEN, "") ?: "")
set(newAccessToken) {
preferences.edit {
putString(ACCESS_TOKEN, cryption.encrypt(newAccessToken, ALIAS))
}
get() = v2Auth.accessToken
set(value) {
v2Auth.accessToken = value
}

/**
* Refresh token to request a new access token.
*/
var refreshToken: String
get() = decryptedToken(preferences.getString(REFRESH_TOKEN, "") ?: "")
set(newRefreshToken) {
preferences.edit {
putString(REFRESH_TOKEN, cryption.encrypt(newRefreshToken, ALIAS))
}
get() = v2Auth.refreshToken
set(value) {
v2Auth.refreshToken = value
}

private fun decryptedToken(preferenceValue: String): String {
return if (preferenceValue.isNotEmpty()) {
cryption.decrypt(preferenceValue, ALIAS)
} else {
""
fun clearAuth() = v2Auth.clear()

private fun migrateFromV1ToV2() {
if (!v1Auth.hasCredentials) {
return
}
}

/**
* Clears out any currently stored auth tokens.
*/
fun clearAuth() = preferences.edit {
remove(ACCESS_TOKEN)
remove(REFRESH_TOKEN)
val v1AccessToken = v1Auth.accessToken
val v1RefreshToken = v1Auth.refreshToken

v2Auth.accessToken = v1AccessToken
v2Auth.refreshToken = v1RefreshToken
v1Auth.clear()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.chesire.nekome.datasource.auth.local

internal interface LocalAuth {

/**
* Checks if there are credentials for this local auth.
*/
val hasCredentials: Boolean

/**
* Access token to put into the api requests.
*/
var accessToken: String

/**
* Refresh token to request a new access token.
*/
var refreshToken: String

/**
* Clears out any currently stored auth tokens for this local auth.
*/
fun clear()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package com.chesire.nekome.datasource.auth.local

import android.content.SharedPreferences
import androidx.core.content.edit
import com.chesire.nekome.encryption.Cryption
import javax.inject.Inject

private const val ACCESS_TOKEN = "KEY_KITSU_ACCESS_TOKEN"
private const val REFRESH_TOKEN = "KEY_KITSU_REFRESH_TOKEN"
private const val ALIAS = "kitsuPrivateAuth"

@Deprecated("This is legacy and will be removed in a later release once migration has completed")
class LocalAuthV1 @Inject constructor(
private val preferences: SharedPreferences,
private val cryption: Cryption
) : LocalAuth {

override val hasCredentials: Boolean
get() = preferences.contains(ACCESS_TOKEN) || preferences.contains(REFRESH_TOKEN)

override var accessToken: String
get() {
val token = preferences.getString(ACCESS_TOKEN, null)
return if (token == null) {
""
} else {
decryptToken(token)
}
}
set(newAccessToken) {
preferences.edit {
putString(ACCESS_TOKEN, cryption.encrypt(newAccessToken, ALIAS))
}
}

override var refreshToken: String
get() {
val token = preferences.getString(REFRESH_TOKEN, null)
return if (token == null) {
""
} else {
decryptToken(token)
}
}
set(newRefreshToken) {
preferences.edit {
putString(REFRESH_TOKEN, cryption.encrypt(newRefreshToken, ALIAS))
}
}

override fun clear() = preferences.edit {
remove(ACCESS_TOKEN)
remove(REFRESH_TOKEN)
}

private fun decryptToken(preferenceValue: String): String {
return try {
if (preferenceValue.isNotEmpty()) {
cryption.decrypt(preferenceValue, ALIAS)
} else {
""
}
} catch (ex: Exception) {
""
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.chesire.nekome.datasource.auth.local

import android.content.SharedPreferences
import androidx.core.content.edit
import javax.inject.Inject

private const val ACCESS_TOKEN = "KEY_KITSU_ACCESS_TOKEN_V2"
private const val REFRESH_TOKEN = "KEY_KITSU_REFRESH_TOKEN_v2"

class LocalAuthV2 @Inject constructor(
private val preferences: SharedPreferences
) : LocalAuth {

override val hasCredentials: Boolean
get() = preferences.contains(ACCESS_TOKEN) || preferences.contains(REFRESH_TOKEN)

override var accessToken: String
get() = preferences.getString(ACCESS_TOKEN, "") ?: ""
set(newAccessToken) {
preferences.edit {
putString(ACCESS_TOKEN, newAccessToken)
}
}

override var refreshToken: String
get() = preferences.getString(REFRESH_TOKEN, "") ?: ""
set(newRefreshToken) {
preferences.edit {
putString(ACCESS_TOKEN, newRefreshToken)
}
}

override fun clear() = preferences.edit {
remove(ACCESS_TOKEN)
remove(REFRESH_TOKEN)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@ import com.thz.keystorehelper.KeyStoreManager
/**
* Used to encrypt and decrypt strings using the [KeyStoreManager].
*/
class KeystoreEncryption(context: Context) : Cryption {
class KeystoreEncryption(private val context: Context) : Cryption {

init {
override fun encrypt(text: String, alias: String): String {
KeyStoreManager.init(context)
return KeyStoreManager.encryptData(text, alias)
}

override fun encrypt(text: String, alias: String): String =
KeyStoreManager.encryptData(text, alias)

override fun decrypt(text: String, alias: String): String =
KeyStoreManager.decryptData(text, alias)
override fun decrypt(text: String, alias: String): String {
KeyStoreManager.init(context)
return KeyStoreManager.decryptData(text, alias)
}
}

0 comments on commit 8811f5b

Please sign in to comment.