fix: isolate login credentials across providers
This commit is contained in:
+7
@@ -24,4 +24,11 @@ class AndroidKeystoreCredentialCipherTest {
|
||||
assertArrayEquals(plaintext, cipher.decrypt(profile, first))
|
||||
assertArrayEquals(plaintext, cipher.decrypt(profile, second))
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException::class)
|
||||
fun rejectsTruncatedPayloadBeforeReadingHeader() {
|
||||
AndroidKeystoreCredentialCipher(
|
||||
keyAlias = "abox_manager_test_${System.nanoTime()}",
|
||||
).decrypt(ProviderProfileId("profile"), byteArrayOf(1))
|
||||
}
|
||||
}
|
||||
|
||||
+2
@@ -27,6 +27,7 @@ class AndroidKeystoreCredentialCipher(
|
||||
}
|
||||
|
||||
override fun decrypt(profileId: ProviderProfileId, encrypted: ByteArray): ByteArray {
|
||||
require(encrypted.size >= HEADER_SIZE) { "Credential payload is truncated" }
|
||||
val buffer = ByteBuffer.wrap(encrypted)
|
||||
require(buffer.get() == VERSION) { "Unsupported credential format" }
|
||||
val ivLength = buffer.get().toInt() and 0xFF
|
||||
@@ -63,6 +64,7 @@ class AndroidKeystoreCredentialCipher(
|
||||
private const val TRANSFORMATION = "AES/GCM/NoPadding"
|
||||
private const val KEY_SIZE_BITS = 256
|
||||
private const val GCM_TAG_BITS = 128
|
||||
private const val HEADER_SIZE = 2
|
||||
private const val VERSION: Byte = 1
|
||||
}
|
||||
}
|
||||
|
||||
+23
@@ -33,6 +33,29 @@ class CredentialStoreTest {
|
||||
assertEquals(secondCredentials, (store.loadCredentials(second) as DomainResult.Success).value)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun malformedCredentialIsRemoved() = runTest {
|
||||
val file = File.createTempFile("malformed-credentials", ".preferences_pb").apply { delete() }
|
||||
val dataStore = PreferenceDataStoreFactory.create(scope = backgroundScope) { file }
|
||||
val profile = ProviderProfileId("profile")
|
||||
val store = KeystoreCredentialStore(dataStore, RejectingCipher())
|
||||
store.saveCredentials(
|
||||
profile,
|
||||
LoginCredentials(Username("demo"), Password("secret")),
|
||||
)
|
||||
|
||||
assertNull((store.loadCredentials(profile) as DomainResult.Success).value)
|
||||
assertNull((store.loadCredentials(profile) as DomainResult.Success).value)
|
||||
}
|
||||
|
||||
private class RejectingCipher : CredentialCipher {
|
||||
override fun encrypt(profileId: ProviderProfileId, plaintext: ByteArray): ByteArray = byteArrayOf(1)
|
||||
|
||||
override fun decrypt(profileId: ProviderProfileId, encrypted: ByteArray): ByteArray {
|
||||
throw IllegalArgumentException("Credential payload is truncated")
|
||||
}
|
||||
}
|
||||
|
||||
private class ReversingCipher : CredentialCipher {
|
||||
override fun encrypt(profileId: ProviderProfileId, plaintext: ByteArray): ByteArray =
|
||||
(profileId.value.toByteArray() + byteArrayOf(0) + plaintext).reversedArray()
|
||||
|
||||
Reference in New Issue
Block a user