Enforce Android lifecycle and presentation security (#100)
This commit is contained in:
1 parent
c0ba6d43d8
commit
a06f39af23
37 files changed
+1129
-110
No files matched your search
+1
@@ -39,6 +39,7 @@ class AndroidKeystoreAdapter(context: Context) : AndroidKeystore {
|
||||
private val handler = Handler(Looper.getMainLooper())
|
||||
private var activity = WeakReference<FragmentActivity>(null)
|
||||
private val pending = AtomicReference<Pending?>()
|
||||
val authenticationPending: Boolean get() = pending.get() != null
|
||||
|
||||
fun attach(host: FragmentActivity) {
|
||||
activity = WeakReference(host)
|
||||
|
||||
+38
-6
@@ -1,29 +1,61 @@
|
||||
package de.rfc1437.ironstorage.platform
|
||||
|
||||
import android.content.ClipData
|
||||
import android.content.ClipDescription
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.os.PersistableBundle
|
||||
import de.rfc1437.ironstorage.core.SecretClipboard
|
||||
import java.io.IOException
|
||||
import java.util.UUID
|
||||
|
||||
class AndroidSecretClipboard(context: Context) : SecretClipboard {
|
||||
class AndroidSecretClipboard(private val context: Context) : SecretClipboard {
|
||||
private val manager = context.getSystemService(ClipboardManager::class.java)
|
||||
override fun read(): ByteArray? = manager.primaryClip?.let { clip ->
|
||||
if (clip.itemCount == 1) clip.getItemAt(0).text?.toString()?.toByteArray() else null
|
||||
private val identity = UUID.randomUUID().toString()
|
||||
private var timestamp: Long? = null
|
||||
override fun beginCopy(): SecretClipboard = AndroidSecretClipboard(context)
|
||||
private fun owns(clip: ClipData) = clip.description.extras?.getString(OWNER) == identity &&
|
||||
timestamp != null && clip.description.timestamp == timestamp
|
||||
override fun read(): ByteArray? = access {
|
||||
manager.primaryClip?.takeIf(::owns)?.let { clip ->
|
||||
if (clip.itemCount == 1) clip.getItemAt(0).text?.toString()?.toByteArray() else null
|
||||
}
|
||||
}
|
||||
override fun write(value: ByteArray) {
|
||||
override fun write(value: ByteArray) = access {
|
||||
val clip = ClipData.newPlainText("IronStorage", value.toString(Charsets.UTF_8))
|
||||
clip.description.extras = PersistableBundle().apply {
|
||||
putBoolean("android.content.extra.IS_SENSITIVE", true)
|
||||
putBoolean(
|
||||
if (Build.VERSION.SDK_INT >=
|
||||
33
|
||||
) {
|
||||
ClipDescription.EXTRA_IS_SENSITIVE
|
||||
} else {
|
||||
"android.content.extra.IS_SENSITIVE"
|
||||
},
|
||||
true
|
||||
)
|
||||
putString(OWNER, identity)
|
||||
}
|
||||
manager.setPrimaryClip(clip)
|
||||
timestamp = manager.primaryClipDescription?.takeIf {
|
||||
it.extras?.getString(OWNER) == identity
|
||||
}?.timestamp
|
||||
}
|
||||
override fun clear() {
|
||||
override fun clear() = access {
|
||||
if (manager.primaryClip?.let(::owns) != true) return@access
|
||||
if (Build.VERSION.SDK_INT >= 28) {
|
||||
manager.clearPrimaryClip()
|
||||
} else {
|
||||
manager.setPrimaryClip(ClipData.newPlainText("", ""))
|
||||
}
|
||||
}
|
||||
private fun <T> access(block: () -> T): T = try {
|
||||
block()
|
||||
} catch (_: RuntimeException) {
|
||||
throw IOException("Android clipboard access is unavailable.")
|
||||
}
|
||||
private companion object {
|
||||
const val OWNER = "de.rfc1437.ironstorage.clipboard-owner"
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user