mirror of
https://github.com/bitwarden/android.git
synced 2026-08-30 17:33:42 -05:00
BITAU-70 Implement symmetric key storage (#226)
This commit is contained in:
+5
@@ -30,4 +30,9 @@ interface AuthDiskSource {
|
||||
* Stores the biometrics key.
|
||||
*/
|
||||
fun storeUserBiometricUnlockKey(biometricsKey: String?)
|
||||
|
||||
/**
|
||||
* Stores the symmetric key data used for encrypting TOTP data.
|
||||
*/
|
||||
var authenticatorBridgeSymmetricSyncKey: ByteArray?
|
||||
}
|
||||
|
||||
+10
@@ -5,6 +5,8 @@ import com.bitwarden.authenticator.data.platform.datasource.disk.BaseDiskSource.
|
||||
import com.bitwarden.authenticator.data.platform.datasource.disk.BaseEncryptedDiskSource
|
||||
import com.bitwarden.authenticator.data.platform.datasource.disk.BaseEncryptedDiskSource.Companion.ENCRYPTED_BASE_KEY
|
||||
|
||||
private const val AUTHENTICATOR_SYNC_SYMMETRIC_KEY =
|
||||
"$ENCRYPTED_BASE_KEY:authenticatorSyncSymmetricKey"
|
||||
private const val LAST_ACTIVE_TIME_KEY = "$BASE_KEY:lastActiveTime"
|
||||
private const val BIOMETRICS_UNLOCK_KEY = "$ENCRYPTED_BASE_KEY:userKeyBiometricUnlock"
|
||||
|
||||
@@ -43,4 +45,12 @@ class AuthDiskSourceImpl(
|
||||
value = biometricsKey,
|
||||
)
|
||||
}
|
||||
|
||||
override var authenticatorBridgeSymmetricSyncKey: ByteArray?
|
||||
set(value) {
|
||||
val asString = value?.let { value.toString(Charsets.ISO_8859_1) }
|
||||
putEncryptedString(AUTHENTICATOR_SYNC_SYMMETRIC_KEY, asString)
|
||||
}
|
||||
get() = getEncryptedString(AUTHENTICATOR_SYNC_SYMMETRIC_KEY)
|
||||
?.toByteArray(Charsets.ISO_8859_1)
|
||||
}
|
||||
|
||||
+12
-6
@@ -1,10 +1,11 @@
|
||||
package com.bitwarden.authenticator.data.authenticator.repository.di
|
||||
|
||||
import android.content.Context
|
||||
import com.bitwarden.authenticator.data.auth.datasource.disk.AuthDiskSource
|
||||
import com.bitwarden.authenticator.data.authenticator.repository.util.SymmetricKeyStorageProviderImpl
|
||||
import com.bitwarden.authenticatorbridge.factory.AuthenticatorBridgeFactory
|
||||
import com.bitwarden.authenticatorbridge.manager.AuthenticatorBridgeManager
|
||||
import com.bitwarden.authenticatorbridge.manager.model.AuthenticatorBridgeConnectionType
|
||||
import com.bitwarden.authenticatorbridge.model.SymmetricEncryptionKeyData
|
||||
import com.bitwarden.authenticatorbridge.provider.SymmetricKeyStorageProvider
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
@@ -31,12 +32,17 @@ object AuthenticatorBridgeModule {
|
||||
@Singleton
|
||||
fun provideAuthenticatorBridgeManager(
|
||||
factory: AuthenticatorBridgeFactory,
|
||||
symmetricKeyStorageProvider: SymmetricKeyStorageProvider,
|
||||
): AuthenticatorBridgeManager = factory.getAuthenticatorBridgeManager(
|
||||
connectionType = AuthenticatorBridgeConnectionType.DEV,
|
||||
symmetricKeyStorageProvider = object : SymmetricKeyStorageProvider {
|
||||
|
||||
// TODO: Implement symmetric key storage: BITAU-70
|
||||
override var symmetricKey: SymmetricEncryptionKeyData? = null
|
||||
},
|
||||
symmetricKeyStorageProvider = symmetricKeyStorageProvider,
|
||||
)
|
||||
|
||||
@Provides
|
||||
fun providesSymmetricKeyStorageProvider(
|
||||
authDiskSource: AuthDiskSource,
|
||||
): SymmetricKeyStorageProvider =
|
||||
SymmetricKeyStorageProviderImpl(
|
||||
authDiskSource = authDiskSource,
|
||||
)
|
||||
}
|
||||
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package com.bitwarden.authenticator.data.authenticator.repository.util
|
||||
|
||||
import com.bitwarden.authenticator.data.auth.datasource.disk.AuthDiskSource
|
||||
import com.bitwarden.authenticatorbridge.model.SymmetricEncryptionKeyData
|
||||
import com.bitwarden.authenticatorbridge.provider.SymmetricKeyStorageProvider
|
||||
import com.bitwarden.authenticatorbridge.util.toSymmetricEncryptionKeyData
|
||||
|
||||
/**
|
||||
* Implementation of [SymmetricKeyStorageProvider] that stores symmetric key data in encrypted
|
||||
* shared preferences.
|
||||
*/
|
||||
class SymmetricKeyStorageProviderImpl(
|
||||
private val authDiskSource: AuthDiskSource,
|
||||
) : SymmetricKeyStorageProvider {
|
||||
|
||||
override var symmetricKey: SymmetricEncryptionKeyData?
|
||||
get() = authDiskSource.authenticatorBridgeSymmetricSyncKey?.toSymmetricEncryptionKeyData()
|
||||
set(value) {
|
||||
authDiskSource.authenticatorBridgeSymmetricSyncKey =
|
||||
value?.symmetricEncryptionKey?.byteArray
|
||||
}
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
package com.bitwarden.authenticator.data.auth.datasource.disk
|
||||
|
||||
import com.bitwarden.authenticator.data.platform.base.FakeSharedPreferences
|
||||
import com.bitwarden.authenticatorbridge.util.generateSecretKey
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertNull
|
||||
import org.junit.jupiter.api.Assertions.assertTrue
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class AuthDiskSourceTest {
|
||||
|
||||
private val fakeEncryptedSharedPreferences = FakeSharedPreferences()
|
||||
private val fakeSharedPreferences = FakeSharedPreferences()
|
||||
|
||||
private val authDiskSource = AuthDiskSourceImpl(
|
||||
encryptedSharedPreferences = fakeEncryptedSharedPreferences,
|
||||
sharedPreferences = fakeSharedPreferences,
|
||||
)
|
||||
|
||||
@Test
|
||||
@Suppress("MaxLineLength")
|
||||
fun `authenticatorBridgeSymmetricSyncKey should store and update from EncryptedSharedPreferences`() {
|
||||
val sharedPrefsKey = "bwSecureStorage:authenticatorSyncSymmetricKey"
|
||||
|
||||
// Shared preferences and the repository start with the same value:
|
||||
assertNull(authDiskSource.authenticatorBridgeSymmetricSyncKey)
|
||||
assertNull(fakeEncryptedSharedPreferences.getString(sharedPrefsKey, null))
|
||||
|
||||
// Updating the repository updates shared preferences:
|
||||
val symmetricKey = generateSecretKey().getOrThrow().encoded
|
||||
authDiskSource.authenticatorBridgeSymmetricSyncKey = symmetricKey
|
||||
assertEquals(
|
||||
symmetricKey.toString(Charsets.ISO_8859_1),
|
||||
fakeEncryptedSharedPreferences.getString(sharedPrefsKey, null),
|
||||
)
|
||||
|
||||
// Retrieving the key from repository should give same byte array despite String conversion:
|
||||
assertTrue(authDiskSource.authenticatorBridgeSymmetricSyncKey.contentEquals(symmetricKey))
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package com.bitwarden.authenticator.data.auth.datasource.disk.util
|
||||
|
||||
import com.bitwarden.authenticator.data.auth.datasource.disk.AuthDiskSource
|
||||
|
||||
class FakeAuthDiskSource : AuthDiskSource {
|
||||
|
||||
private var lastActiveTimeMillis: Long? = null
|
||||
private var userBiometricUnlockKey: String? = null
|
||||
|
||||
override fun getLastActiveTimeMillis(): Long? = lastActiveTimeMillis
|
||||
|
||||
override fun storeLastActiveTimeMillis(lastActiveTimeMillis: Long?) {
|
||||
this@FakeAuthDiskSource.lastActiveTimeMillis = lastActiveTimeMillis
|
||||
}
|
||||
|
||||
override fun getUserBiometricUnlockKey(): String? = userBiometricUnlockKey
|
||||
|
||||
override fun storeUserBiometricUnlockKey(biometricsKey: String?) {
|
||||
this@FakeAuthDiskSource.userBiometricUnlockKey = biometricsKey
|
||||
}
|
||||
|
||||
override var authenticatorBridgeSymmetricSyncKey: ByteArray? = null
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
package com.bitwarden.authenticator.data.authenticator.repository.util
|
||||
|
||||
import com.bitwarden.authenticator.data.auth.datasource.disk.util.FakeAuthDiskSource
|
||||
import com.bitwarden.authenticatorbridge.util.generateSecretKey
|
||||
import com.bitwarden.authenticatorbridge.util.toSymmetricEncryptionKeyData
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertNull
|
||||
import org.junit.jupiter.api.Assertions.assertTrue
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class SymmetricKeyStorageProviderTest {
|
||||
|
||||
private val fakeAuthDiskSource = FakeAuthDiskSource()
|
||||
|
||||
private val provider = SymmetricKeyStorageProviderImpl(
|
||||
authDiskSource = fakeAuthDiskSource,
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `symmetricKey get should return null when disk source has no symmetric key`() {
|
||||
fakeAuthDiskSource.authenticatorBridgeSymmetricSyncKey = null
|
||||
assertNull(provider.symmetricKey)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `symmetricKey get should return symmetric key when disk source has symmetric key`() {
|
||||
val key = generateSecretKey().getOrThrow()
|
||||
fakeAuthDiskSource.authenticatorBridgeSymmetricSyncKey = key.encoded
|
||||
assertEquals(
|
||||
key.encoded.toSymmetricEncryptionKeyData(),
|
||||
provider.symmetricKey,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `symmetricKey set should store key in AuthDiskSource`() {
|
||||
val key = generateSecretKey().getOrThrow()
|
||||
fakeAuthDiskSource.authenticatorBridgeSymmetricSyncKey = null
|
||||
|
||||
provider.symmetricKey = key.encoded.toSymmetricEncryptionKeyData()
|
||||
assertTrue(
|
||||
key.encoded.contentEquals(
|
||||
fakeAuthDiskSource.authenticatorBridgeSymmetricSyncKey,
|
||||
),
|
||||
)
|
||||
|
||||
provider.symmetricKey = null
|
||||
assertNull(fakeAuthDiskSource.authenticatorBridgeSymmetricSyncKey)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user