BITAU-172 Rename Authenticator Bridge SDK (#3959)

This commit is contained in:
Andrew Haisting 2024-09-24 17:09:27 -05:00 committed by GitHub
parent 3d0dd2996e
commit 4f34f6da21
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
48 changed files with 212 additions and 207 deletions

View file

@ -136,7 +136,7 @@ dependencies {
}
// TODO: this should use a versioned AAR instead of referencing a local AAR BITAU-94
implementation(files("libs/bridge-0.1.0-SNAPSHOT-release.aar"))
implementation(files("libs/authenticatorbridge-0.1.0-SNAPSHOT-release.aar"))
implementation(libs.androidx.activity.compose)
implementation(libs.androidx.appcompat)

View file

@ -20,8 +20,8 @@ import com.x8bit.bitwarden.data.platform.manager.AssetManager
import com.x8bit.bitwarden.data.platform.manager.AssetManagerImpl
import com.x8bit.bitwarden.data.platform.manager.BiometricsEncryptionManager
import com.x8bit.bitwarden.data.platform.manager.BiometricsEncryptionManagerImpl
import com.x8bit.bitwarden.data.platform.processor.BridgeServiceProcessor
import com.x8bit.bitwarden.data.platform.processor.BridgeServiceProcessorImpl
import com.x8bit.bitwarden.data.platform.processor.AuthenticatorBridgeProcessor
import com.x8bit.bitwarden.data.platform.processor.AuthenticatorBridgeProcessorImpl
import com.x8bit.bitwarden.data.platform.manager.CrashLogsManager
import com.x8bit.bitwarden.data.platform.manager.CrashLogsManagerImpl
import com.x8bit.bitwarden.data.platform.manager.DebugMenuFeatureFlagManagerImpl
@ -51,7 +51,7 @@ import com.x8bit.bitwarden.data.platform.manager.garbage.GarbageCollectionManage
import com.x8bit.bitwarden.data.platform.manager.garbage.GarbageCollectionManagerImpl
import com.x8bit.bitwarden.data.platform.manager.restriction.RestrictionManager
import com.x8bit.bitwarden.data.platform.manager.restriction.RestrictionManagerImpl
import com.x8bit.bitwarden.data.platform.repository.BridgeRepository
import com.x8bit.bitwarden.data.platform.repository.AuthenticatorBridgeRepository
import com.x8bit.bitwarden.data.platform.repository.DebugMenuRepository
import com.x8bit.bitwarden.data.platform.repository.EnvironmentRepository
import com.x8bit.bitwarden.data.platform.repository.ServerConfigRepository
@ -80,12 +80,12 @@ object PlatformManagerModule {
@Provides
@Singleton
fun provideBridgeServiceProcessor(
bridgeRepository: BridgeRepository,
fun provideAuthenticatorBridgeProcessor(
authenticatorBridgeRepository: AuthenticatorBridgeRepository,
dispatcherManager: DispatcherManager,
featureFlagManager: FeatureFlagManager,
): BridgeServiceProcessor = BridgeServiceProcessorImpl(
bridgeRepository = bridgeRepository,
): AuthenticatorBridgeProcessor = AuthenticatorBridgeProcessorImpl(
authenticatorBridgeRepository = authenticatorBridgeRepository,
dispatcherManager = dispatcherManager,
featureFlagManager = featureFlagManager,
)

View file

@ -0,0 +1,16 @@
package com.x8bit.bitwarden.data.platform.processor
import com.bitwarden.authenticatorbridge.IAuthenticatorBridgeService
/**
* Provides implementation of [IAuthenticatorBridgeService] APIs in an injectable and testable
* manner.
*/
interface AuthenticatorBridgeProcessor {
/**
* Binder that implements [IAuthenticatorBridgeService]. Null can be returned to represent a
* no-op binder.
*/
val binder: IAuthenticatorBridgeService.Stub?
}

View file

@ -4,36 +4,36 @@ import android.content.Intent
import android.os.Build
import android.os.IInterface
import android.os.RemoteCallbackList
import com.bitwarden.bridge.IBridgeService
import com.bitwarden.bridge.IBridgeServiceCallback
import com.bitwarden.bridge.model.EncryptedAddTotpLoginItemData
import com.bitwarden.bridge.model.SymmetricEncryptionKeyData
import com.bitwarden.bridge.model.SymmetricEncryptionKeyFingerprintData
import com.bitwarden.bridge.util.NATIVE_BRIDGE_SDK_VERSION
import com.bitwarden.bridge.util.encrypt
import com.bitwarden.bridge.util.toFingerprint
import com.bitwarden.bridge.util.toSymmetricEncryptionKeyData
import com.bitwarden.authenticatorbridge.IAuthenticatorBridgeService
import com.bitwarden.authenticatorbridge.IAuthenticatorBridgeServiceCallback
import com.bitwarden.authenticatorbridge.model.EncryptedAddTotpLoginItemData
import com.bitwarden.authenticatorbridge.model.SymmetricEncryptionKeyData
import com.bitwarden.authenticatorbridge.model.SymmetricEncryptionKeyFingerprintData
import com.bitwarden.authenticatorbridge.util.AUTHENTICATOR_BRIDGE_SDK_VERSION
import com.bitwarden.authenticatorbridge.util.encrypt
import com.bitwarden.authenticatorbridge.util.toFingerprint
import com.bitwarden.authenticatorbridge.util.toSymmetricEncryptionKeyData
import com.x8bit.bitwarden.data.platform.manager.FeatureFlagManager
import com.x8bit.bitwarden.data.platform.manager.dispatcher.DispatcherManager
import com.x8bit.bitwarden.data.platform.manager.model.FlagKey
import com.x8bit.bitwarden.data.platform.repository.BridgeRepository
import com.x8bit.bitwarden.data.platform.repository.AuthenticatorBridgeRepository
import com.x8bit.bitwarden.data.platform.util.isBuildVersionBelow
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
/**
* Default implementation of [BridgeServiceProcessor].
* Default implementation of [AuthenticatorBridgeProcessor].
*/
class BridgeServiceProcessorImpl(
private val bridgeRepository: BridgeRepository,
class AuthenticatorBridgeProcessorImpl(
private val authenticatorBridgeRepository: AuthenticatorBridgeRepository,
private val featureFlagManager: FeatureFlagManager,
dispatcherManager: DispatcherManager,
) : BridgeServiceProcessor {
) : AuthenticatorBridgeProcessor {
private val callbacks by lazy { RemoteCallbackList<IBridgeServiceCallback>() }
private val callbacks by lazy { RemoteCallbackList<IAuthenticatorBridgeServiceCallback>() }
private val scope by lazy { CoroutineScope(dispatcherManager.default) }
override val binder: IBridgeService.Stub?
override val binder: IAuthenticatorBridgeService.Stub?
get() {
return if (
!featureFlagManager.getFeatureFlag(FlagKey.AuthenticatorSync) ||
@ -51,16 +51,16 @@ class BridgeServiceProcessorImpl(
/**
* Default implementation of the bridge service binder.
*/
private val defaultBinder = object : IBridgeService.Stub() {
private val defaultBinder = object : IAuthenticatorBridgeService.Stub() {
override fun getVersionNumber(): String = NATIVE_BRIDGE_SDK_VERSION
override fun getVersionNumber(): String = AUTHENTICATOR_BRIDGE_SDK_VERSION
override fun checkSymmetricEncryptionKeyFingerprint(
symmetricKeyFingerprint: SymmetricEncryptionKeyFingerprintData?,
): Boolean {
if (symmetricKeyFingerprint == null) return false
val localSymmetricKeyFingerprint =
bridgeRepository.authenticatorSyncSymmetricKey
authenticatorBridgeRepository.authenticatorSyncSymmetricKey
?.toSymmetricEncryptionKeyData()
?.toFingerprint()
?.getOrNull()
@ -68,14 +68,18 @@ class BridgeServiceProcessorImpl(
}
override fun getSymmetricEncryptionKeyData(): SymmetricEncryptionKeyData? =
bridgeRepository.authenticatorSyncSymmetricKey?.toSymmetricEncryptionKeyData()
authenticatorBridgeRepository
.authenticatorSyncSymmetricKey
?.toSymmetricEncryptionKeyData()
override fun registerBridgeServiceCallback(callback: IBridgeServiceCallback?) {
override fun registerBridgeServiceCallback(callback: IAuthenticatorBridgeServiceCallback?) {
if (callback == null) return
callbacks.register(callback)
}
override fun unregisterBridgeServiceCallback(callback: IBridgeServiceCallback?) {
override fun unregisterBridgeServiceCallback(
callback: IAuthenticatorBridgeServiceCallback?,
) {
if (callback == null) return
callbacks.unregister(callback)
}
@ -84,7 +88,7 @@ class BridgeServiceProcessorImpl(
val symmetricEncryptionKey = symmetricEncryptionKeyData ?: return
scope.launch {
// Encrypt the shared account data with the symmetric key:
val encryptedSharedAccountData = bridgeRepository
val encryptedSharedAccountData = authenticatorBridgeRepository
.getSharedAccounts()
.encrypt(symmetricEncryptionKey)
.getOrNull()

View file

@ -1,14 +0,0 @@
package com.x8bit.bitwarden.data.platform.processor
import com.bitwarden.bridge.IBridgeService
/**
* Provides access to [IBridgeService] APIs in an injectable and testable manner.
*/
interface BridgeServiceProcessor {
/**
* Binder that implements [IBridgeService]. Null can be returned to represent a no-op binder.
*/
val binder: IBridgeService.Stub?
}

View file

@ -1,11 +1,12 @@
package com.x8bit.bitwarden.data.platform.repository
import com.bitwarden.bridge.model.SharedAccountData
import com.bitwarden.authenticatorbridge.model.SharedAccountData
/**
* Provides an API for querying disk sources required by Bridge service implementation.
* Provides an API for querying disk sources required by Authenticator Bridge
* service implementation.
*/
interface BridgeRepository {
interface AuthenticatorBridgeRepository {
/**
* The currently persisted authenticator sync symmetric key. This key is used for

View file

@ -1,6 +1,6 @@
package com.x8bit.bitwarden.data.platform.repository
import com.bitwarden.bridge.model.SharedAccountData
import com.bitwarden.authenticatorbridge.model.SharedAccountData
import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSource
import com.x8bit.bitwarden.data.auth.repository.AuthRepository
import com.x8bit.bitwarden.data.platform.datasource.disk.SettingsDiskSource
@ -14,16 +14,16 @@ import com.x8bit.bitwarden.data.vault.repository.util.toEncryptedSdkCipher
import kotlinx.coroutines.flow.first
/**
* Default implementation of [BridgeRepository].
* Default implementation of [AuthenticatorBridgeRepository].
*/
class BridgeRepositoryImpl(
class AuthenticatorBridgeRepositoryImpl(
private val authRepository: AuthRepository,
private val authDiskSource: AuthDiskSource,
private val vaultRepository: VaultRepository,
private val vaultDiskSource: VaultDiskSource,
private val vaultSdkSource: VaultSdkSource,
private val settingsDiskSource: SettingsDiskSource,
) : BridgeRepository {
) : AuthenticatorBridgeRepository {
override val authenticatorSyncSymmetricKey: ByteArray?
get() = authDiskSource.authenticatorSyncSymmetricKey

View file

@ -1,7 +1,7 @@
package com.x8bit.bitwarden.data.platform.repository
import android.view.autofill.AutofillManager
import com.bitwarden.bridge.util.generateSecretKey
import com.bitwarden.authenticatorbridge.util.generateSecretKey
import com.x8bit.bitwarden.BuildConfig
import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSource
import com.x8bit.bitwarden.data.auth.repository.model.PolicyInformation

View file

@ -13,8 +13,8 @@ import com.x8bit.bitwarden.data.platform.datasource.network.service.ConfigServic
import com.x8bit.bitwarden.data.platform.manager.BiometricsEncryptionManager
import com.x8bit.bitwarden.data.platform.manager.PolicyManager
import com.x8bit.bitwarden.data.platform.manager.dispatcher.DispatcherManager
import com.x8bit.bitwarden.data.platform.repository.BridgeRepository
import com.x8bit.bitwarden.data.platform.repository.BridgeRepositoryImpl
import com.x8bit.bitwarden.data.platform.repository.AuthenticatorBridgeRepository
import com.x8bit.bitwarden.data.platform.repository.AuthenticatorBridgeRepositoryImpl
import com.x8bit.bitwarden.data.platform.repository.DebugMenuRepository
import com.x8bit.bitwarden.data.platform.repository.DebugMenuRepositoryImpl
import com.x8bit.bitwarden.data.platform.repository.EnvironmentRepository
@ -42,14 +42,14 @@ object PlatformRepositoryModule {
@Provides
@Singleton
fun providesBridgeRepository(
fun providesAuthenticatorBridgeRepository(
authRepository: AuthRepository,
authDiskSource: AuthDiskSource,
vaultRepository: VaultRepository,
vaultDiskSource: VaultDiskSource,
vaultSdkSource: VaultSdkSource,
settingsDiskSource: SettingsDiskSource,
): BridgeRepository = BridgeRepositoryImpl(
): AuthenticatorBridgeRepository = AuthenticatorBridgeRepositoryImpl(
authRepository = authRepository,
authDiskSource = authDiskSource,
vaultRepository = vaultRepository,

View file

@ -0,0 +1,27 @@
package com.x8bit.bitwarden.data.platform.service
import android.app.Service
import android.content.Intent
import com.x8bit.bitwarden.data.platform.annotation.OmitFromCoverage
import com.x8bit.bitwarden.data.platform.processor.AuthenticatorBridgeProcessor
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
/**
* Service exposed via a custom permission
*/
@AndroidEntryPoint
@OmitFromCoverage
class AuthenticatorBridgeService : Service() {
@Inject
lateinit var authenticatorBridgeProcessor: AuthenticatorBridgeProcessor
/**
* When binding this service, delegate logic to the [AuthenticatorBridgeProcessor].
*
* Note that [AuthenticatorBridgeProcessor.binder] can return a null binder, which the OS
* will accept but never connect to, effectively making a null binder a noop binder.
*/
override fun onBind(intent: Intent) = authenticatorBridgeProcessor.binder
}

View file

@ -1,27 +0,0 @@
package com.x8bit.bitwarden.data.platform.service
import android.app.Service
import android.content.Intent
import com.x8bit.bitwarden.data.platform.processor.BridgeServiceProcessor
import com.bitwarden.bridge.IBridgeService
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
/**
* Service exposed via a custom permission
*/
@AndroidEntryPoint
class BridgeService : Service() {
@Inject
lateinit var bridgeServiceProcessor: BridgeServiceProcessor
/**
* When binding this service, logic to the [BridgeServiceProcessor], which implements
* [IBridgeService].
*
* Note that [BridgeServiceProcessor.binder] can return a null binder, which the OS will accept
* but never connect to, effectively making a null binder a noop binder.
*/
override fun onBind(intent: Intent) = bridgeServiceProcessor.binder
}

View file

@ -2,7 +2,7 @@ package com.x8bit.bitwarden.data.auth.datasource.disk
import androidx.core.content.edit
import app.cash.turbine.test
import com.bitwarden.bridge.util.generateSecretKey
import com.bitwarden.authenticatorbridge.util.generateSecretKey
import com.x8bit.bitwarden.data.auth.datasource.disk.model.AccountJson
import com.x8bit.bitwarden.data.auth.datasource.disk.model.AccountTokensJson
import com.x8bit.bitwarden.data.auth.datasource.disk.model.EnvironmentUrlDataJson

View file

@ -2,19 +2,19 @@ package com.x8bit.bitwarden.data.platform.processor
import android.os.Build
import android.os.RemoteCallbackList
import com.bitwarden.bridge.IBridgeService
import com.bitwarden.bridge.IBridgeServiceCallback
import com.bitwarden.bridge.model.EncryptedSharedAccountData
import com.bitwarden.bridge.model.SharedAccountData
import com.bitwarden.bridge.util.NATIVE_BRIDGE_SDK_VERSION
import com.bitwarden.bridge.util.encrypt
import com.bitwarden.bridge.util.generateSecretKey
import com.bitwarden.bridge.util.toFingerprint
import com.bitwarden.bridge.util.toSymmetricEncryptionKeyData
import com.bitwarden.authenticatorbridge.IAuthenticatorBridgeService
import com.bitwarden.authenticatorbridge.IAuthenticatorBridgeServiceCallback
import com.bitwarden.authenticatorbridge.model.EncryptedSharedAccountData
import com.bitwarden.authenticatorbridge.model.SharedAccountData
import com.bitwarden.authenticatorbridge.util.AUTHENTICATOR_BRIDGE_SDK_VERSION
import com.bitwarden.authenticatorbridge.util.encrypt
import com.bitwarden.authenticatorbridge.util.generateSecretKey
import com.bitwarden.authenticatorbridge.util.toFingerprint
import com.bitwarden.authenticatorbridge.util.toSymmetricEncryptionKeyData
import com.x8bit.bitwarden.data.platform.base.FakeDispatcherManager
import com.x8bit.bitwarden.data.platform.manager.FeatureFlagManager
import com.x8bit.bitwarden.data.platform.manager.model.FlagKey
import com.x8bit.bitwarden.data.platform.repository.BridgeRepository
import com.x8bit.bitwarden.data.platform.repository.AuthenticatorBridgeRepository
import com.x8bit.bitwarden.data.platform.util.asSuccess
import com.x8bit.bitwarden.data.platform.util.isBuildVersionBelow
import io.mockk.coEvery
@ -34,17 +34,17 @@ import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
class BridgeServiceProcessorTest {
class AuthenticatorBridgeProcessorTest {
private val featureFlagManager = mockk<FeatureFlagManager>()
private val bridgeRepository = mockk<BridgeRepository>()
private val authenticatorBridgeRepository = mockk<AuthenticatorBridgeRepository>()
private lateinit var bridgeServiceProcessor: BridgeServiceProcessorImpl
private lateinit var bridgeServiceProcessor: AuthenticatorBridgeProcessorImpl
@BeforeEach
fun setup() {
bridgeServiceProcessor = BridgeServiceProcessorImpl(
bridgeRepository = bridgeRepository,
bridgeServiceProcessor = AuthenticatorBridgeProcessorImpl(
authenticatorBridgeRepository = authenticatorBridgeRepository,
featureFlagManager = featureFlagManager,
dispatcherManager = FakeDispatcherManager(),
)
@ -88,7 +88,7 @@ class BridgeServiceProcessorTest {
fun `versionNumber should match version of compiled bridge sdk`() {
val binder = getDefaultBinder()
assertEquals(
NATIVE_BRIDGE_SDK_VERSION,
AUTHENTICATOR_BRIDGE_SDK_VERSION,
binder.versionNumber,
)
}
@ -98,7 +98,7 @@ class BridgeServiceProcessorTest {
fun `checkSymmetricEncryptionKeyFingerprint should return false when given fingerprint is null`() {
val binder = getDefaultBinder()
// Set disk symmetric key to null so that it is technically equal to given null fingerprint:
every { bridgeRepository.authenticatorSyncSymmetricKey } returns null
every { authenticatorBridgeRepository.authenticatorSyncSymmetricKey } returns null
// Binder should still return false in this case:
assertFalse(binder.checkSymmetricEncryptionKeyFingerprint(null))
}
@ -107,7 +107,7 @@ class BridgeServiceProcessorTest {
@Suppress("MaxLineLength")
fun `checkSymmetricEncryptionKeyFingerprint should return false if fingerprint doesn't match`() {
val binder = getDefaultBinder()
every { bridgeRepository.authenticatorSyncSymmetricKey } returns ByteArray(1)
every { authenticatorBridgeRepository.authenticatorSyncSymmetricKey } returns ByteArray(1)
assertFalse(binder.checkSymmetricEncryptionKeyFingerprint(SYMMETRIC_KEY_FINGERPRINT))
}
@ -116,7 +116,7 @@ class BridgeServiceProcessorTest {
fun `checkSymmetricEncryptionKeyFingerprint should return true if fingerprint does match`() {
val binder = getDefaultBinder()
every {
bridgeRepository.authenticatorSyncSymmetricKey
authenticatorBridgeRepository.authenticatorSyncSymmetricKey
} returns SYMMETRIC_KEY.symmetricEncryptionKey.byteArray
assertTrue(binder.checkSymmetricEncryptionKeyFingerprint(SYMMETRIC_KEY_FINGERPRINT))
}
@ -125,7 +125,7 @@ class BridgeServiceProcessorTest {
@Suppress("MaxLineLength")
fun `getSymmetricEncryptionKeyData should return null when there is no symmetric key stored on disk`() {
val binder = getDefaultBinder()
every { bridgeRepository.authenticatorSyncSymmetricKey } returns null
every { authenticatorBridgeRepository.authenticatorSyncSymmetricKey } returns null
assertNull(binder.symmetricEncryptionKeyData)
}
@ -134,7 +134,7 @@ class BridgeServiceProcessorTest {
fun `getSymmetricEncryptionKeyData should return the symmetric key stored on disk`() {
val binder = getDefaultBinder()
every {
bridgeRepository.authenticatorSyncSymmetricKey
authenticatorBridgeRepository.authenticatorSyncSymmetricKey
} returns SYMMETRIC_KEY.symmetricEncryptionKey.byteArray
assertEquals(SYMMETRIC_KEY, binder.symmetricEncryptionKeyData)
}
@ -144,7 +144,7 @@ class BridgeServiceProcessorTest {
private var lastAccountsSync: EncryptedSharedAccountData? = null
private val serviceCallback = object : IBridgeServiceCallback.Stub() {
private val serviceCallback = object : IAuthenticatorBridgeServiceCallback.Stub() {
override fun onAccountsSync(data: EncryptedSharedAccountData?) {
lastAccountsSync = data
}
@ -155,15 +155,15 @@ class BridgeServiceProcessorTest {
// Setup RemoteCallbackList to call back to serviceCallback:
mockkConstructor(RemoteCallbackList::class)
every {
anyConstructed<RemoteCallbackList<IBridgeServiceCallback>>()
anyConstructed<RemoteCallbackList<IAuthenticatorBridgeServiceCallback>>()
.register(serviceCallback)
} returns true
every {
anyConstructed<RemoteCallbackList<IBridgeServiceCallback>>()
anyConstructed<RemoteCallbackList<IAuthenticatorBridgeServiceCallback>>()
.beginBroadcast()
} returns 1
every {
anyConstructed<RemoteCallbackList<IBridgeServiceCallback>>()
anyConstructed<RemoteCallbackList<IAuthenticatorBridgeServiceCallback>>()
.getBroadcastItem(0)
} returns serviceCallback
lastAccountsSync = null
@ -171,7 +171,7 @@ class BridgeServiceProcessorTest {
@Test
fun `syncAccounts when symmetricEncryptionKeyData is null should do nothing`() {
every { bridgeRepository.authenticatorSyncSymmetricKey } returns null
every { authenticatorBridgeRepository.authenticatorSyncSymmetricKey } returns null
getDefaultBinder().syncAccounts()
assertNull(lastAccountsSync)
}
@ -181,25 +181,25 @@ class BridgeServiceProcessorTest {
val sharedAccountData = mockk<SharedAccountData>()
val expected = mockk<EncryptedSharedAccountData>()
every {
bridgeRepository.authenticatorSyncSymmetricKey
authenticatorBridgeRepository.authenticatorSyncSymmetricKey
} returns SYMMETRIC_KEY.symmetricEncryptionKey.byteArray
coEvery { bridgeRepository.getSharedAccounts() } returns sharedAccountData
coEvery { authenticatorBridgeRepository.getSharedAccounts() } returns sharedAccountData
mockkStatic(SharedAccountData::encrypt)
every { sharedAccountData.encrypt(SYMMETRIC_KEY) } returns expected.asSuccess()
getDefaultBinder().syncAccounts()
assertEquals(expected, lastAccountsSync)
coVerify { bridgeRepository.getSharedAccounts() }
coVerify { authenticatorBridgeRepository.getSharedAccounts() }
}
}
/**
* Helper function for accessing the default implementation of [IBridgeService.Stub]. This
* is particularly useful because the binder is nullable on [BridgeServiceProcessor] behind
* a feature flag.
* Helper function for accessing the default implementation of
* [IAuthenticatorBridgeService.Stub]. This is particularly useful because the binder
* is nullable on [AuthenticatorBridgeProcessor] behind a feature flag.
*/
private fun getDefaultBinder(): IBridgeService.Stub {
private fun getDefaultBinder(): IAuthenticatorBridgeService.Stub {
mockkStatic(::isBuildVersionBelow)
every { isBuildVersionBelow(Build.VERSION_CODES.S) } returns false
every { featureFlagManager.getFeatureFlag(FlagKey.AuthenticatorSync) } returns true

View file

@ -1,8 +1,8 @@
package com.x8bit.bitwarden.data.platform.repository
import com.bitwarden.bridge.model.SharedAccountData
import com.bitwarden.bridge.util.generateSecretKey
import com.bitwarden.bridge.util.toSymmetricEncryptionKeyData
import com.bitwarden.authenticatorbridge.model.SharedAccountData
import com.bitwarden.authenticatorbridge.util.generateSecretKey
import com.bitwarden.authenticatorbridge.util.toSymmetricEncryptionKeyData
import com.bitwarden.vault.Cipher
import com.bitwarden.vault.CipherView
import com.x8bit.bitwarden.data.auth.datasource.disk.util.FakeAuthDiskSource
@ -36,7 +36,7 @@ import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import java.time.Instant
class BridgeRepositoryTest {
class AuthenticatorBridgeRepositoryTest {
private val authRepository = mockk<AuthRepository>()
private val vaultSdkSource = mockk<VaultSdkSource>()
@ -45,7 +45,7 @@ class BridgeRepositoryTest {
private val fakeAuthDiskSource = FakeAuthDiskSource()
private val fakeSettingsDiskSource = FakeSettingsDiskSource()
private val bridgeRepository = BridgeRepositoryImpl(
private val authenticatorBridgeRepository = AuthenticatorBridgeRepositoryImpl(
authRepository = authRepository,
authDiskSource = fakeAuthDiskSource,
vaultRepository = vaultRepository,
@ -129,7 +129,7 @@ class BridgeRepositoryTest {
@Suppress("MaxLineLength")
fun `syncAccounts with user 1 vault unlocked and all data present should send expected shared accounts data`() =
runTest {
val sharedAccounts = bridgeRepository.getSharedAccounts()
val sharedAccounts = authenticatorBridgeRepository.getSharedAccounts()
assertEquals(
BOTH_ACCOUNT_SUCCESS,
sharedAccounts,
@ -155,7 +155,7 @@ class BridgeRepositoryTest {
fun `syncAccounts when userStateFlow is null should return an empty list`() = runTest {
every { authRepository.userStateFlow } returns MutableStateFlow(null)
val sharedData = bridgeRepository.getSharedAccounts()
val sharedData = authenticatorBridgeRepository.getSharedAccounts()
assertTrue(sharedData.accounts.isEmpty())
verify { authRepository.userStateFlow }
@ -172,7 +172,7 @@ class BridgeRepositoryTest {
assertEquals(
SharedAccountData(listOf(USER_2_SHARED_ACCOUNT)),
bridgeRepository.getSharedAccounts(),
authenticatorBridgeRepository.getSharedAccounts(),
)
verify { authRepository.userStateFlow }
@ -199,7 +199,7 @@ class BridgeRepositoryTest {
} returns VaultUnlockResult.Success
every { vaultRepository.lockVault(USER_1_ID) } returns Unit
val sharedAccounts = bridgeRepository.getSharedAccounts()
val sharedAccounts = authenticatorBridgeRepository.getSharedAccounts()
assertEquals(
BOTH_ACCOUNT_SUCCESS,
sharedAccounts,
@ -232,7 +232,7 @@ class BridgeRepositoryTest {
@Test
fun `syncAccounts when getLastSyncTime is null should omit account from list`() = runTest {
fakeSettingsDiskSource.storeLastSyncTime(USER_1_ID, null)
val sharedAccounts = bridgeRepository.getSharedAccounts()
val sharedAccounts = authenticatorBridgeRepository.getSharedAccounts()
assertEquals(SharedAccountData(listOf(USER_2_SHARED_ACCOUNT)), sharedAccounts)
verify { vaultRepository.vaultUnlockDataStateFlow }
verify { vaultDiskSource.getCiphers(USER_1_ID) }
@ -261,7 +261,7 @@ class BridgeRepositoryTest {
vaultRepository.unlockVaultWithDecryptedUserKey(USER_1_ID, USER_1_UNLOCK_KEY)
} returns VaultUnlockResult.InvalidStateError
val sharedAccounts = bridgeRepository.getSharedAccounts()
val sharedAccounts = authenticatorBridgeRepository.getSharedAccounts()
assertEquals(SharedAccountData(listOf(USER_2_SHARED_ACCOUNT)), sharedAccounts)
assertNull(fakeAuthDiskSource.getAuthenticatorSyncUnlockKey(USER_1_ID))
verify { vaultRepository.vaultUnlockDataStateFlow }
@ -298,7 +298,7 @@ class BridgeRepositoryTest {
)
every { vaultRepository.vaultUnlockDataStateFlow } returns vaultUnlockStateFlow
val deferred = async {
val sharedAccounts = bridgeRepository.getSharedAccounts()
val sharedAccounts = authenticatorBridgeRepository.getSharedAccounts()
assertEquals(BOTH_ACCOUNT_SUCCESS, sharedAccounts)
}
@ -336,12 +336,12 @@ class BridgeRepositoryTest {
@Test
fun `authenticatorSyncSymmetricKey should read from authDiskSource`() {
fakeAuthDiskSource.authenticatorSyncSymmetricKey = null
assertNull(bridgeRepository.authenticatorSyncSymmetricKey)
assertNull(authenticatorBridgeRepository.authenticatorSyncSymmetricKey)
val syncKey = generateSecretKey().getOrThrow().encoded
fakeAuthDiskSource.authenticatorSyncSymmetricKey = syncKey
assertEquals(syncKey, bridgeRepository.authenticatorSyncSymmetricKey)
assertEquals(syncKey, authenticatorBridgeRepository.authenticatorSyncSymmetricKey)
}
}

View file

@ -2,7 +2,7 @@ package com.x8bit.bitwarden.data.platform.repository
import android.view.autofill.AutofillManager
import app.cash.turbine.test
import com.bitwarden.bridge.util.generateSecretKey
import com.bitwarden.authenticatorbridge.util.generateSecretKey
import com.bitwarden.core.DerivePinKeyResponse
import com.x8bit.bitwarden.data.auth.datasource.disk.model.AccountJson
import com.x8bit.bitwarden.data.auth.datasource.disk.model.EnvironmentUrlDataJson

View file

@ -1,4 +1,4 @@
# Bitwarden Native Bridge SDK
# Authenticator Bridge SDK
## Contents
@ -22,7 +22,7 @@
To build an AAR for inclusion in consumer applications, run:
```sh
$ ./gradlew bridge:assembleRelease
$ ./gradlew authenticatorbridge:assembleRelease
```
## Versioning

View file

@ -11,7 +11,7 @@ plugins {
}
android {
namespace = "com.bitwarden.bridge"
namespace = "com.bitwarden.authenticatorbridge"
compileSdk = libs.versions.compileSdk.get().toInt()
defaultConfig {
@ -46,7 +46,7 @@ android {
outputs
.map { it as com.android.build.gradle.internal.api.BaseVariantOutputImpl }
.forEach { output ->
val outputFileName = "bridge-${version}-SNAPSHOT-${variant.baseName}.aar"
val outputFileName = "authenticatorbridge-${version}-SNAPSHOT-${variant.baseName}.aar"
output.outputFileName = outputFileName
}
}

View file

@ -1,20 +1,20 @@
package com.bitwarden.bridge;
package com.bitwarden.authenticatorbridge;
import com.bitwarden.bridge.model.EncryptedAddTotpLoginItemData;
import com.bitwarden.bridge.model.SymmetricEncryptionKeyData;
import com.bitwarden.bridge.model.SymmetricEncryptionKeyFingerprintData;
import com.bitwarden.bridge.IBridgeServiceCallback;
import com.bitwarden.authenticatorbridge.model.EncryptedAddTotpLoginItemData;
import com.bitwarden.authenticatorbridge.model.SymmetricEncryptionKeyData;
import com.bitwarden.authenticatorbridge.model.SymmetricEncryptionKeyFingerprintData;
import com.bitwarden.authenticatorbridge.IAuthenticatorBridgeServiceCallback;
interface IBridgeService {
interface IAuthenticatorBridgeService {
// ==============
// Configuration
// ==============
// Returns the version number string of the Bridge SDK. This is useful so that callers
// can compare the version of their Bridge SDK with this value and ensure that the two are
// compatible.
// Returns the version number string of the Authenticator Bridge SDK. This is useful so that
// callers can compare the version of their Authenticator Bridge SDK with this value and
// ensure that the two are compatible.
//
// For more info about versioning the Bridge SDK, see the Bridge SDK README.
// For more info about versioning the Authenticator Bridge SDK, see the README.
String getVersionNumber();
// Returns true when the given symmetric fingerprint data matches that contained by the SDK.
@ -32,10 +32,10 @@ interface IBridgeService {
// ==============
// Register the given callback to receive updates after syncAccounts is called.
void registerBridgeServiceCallback(IBridgeServiceCallback callback);
void registerBridgeServiceCallback(IAuthenticatorBridgeServiceCallback callback);
// Unregister the given callback from receiving updates.
void unregisterBridgeServiceCallback(IBridgeServiceCallback callback);
void unregisterBridgeServiceCallback(IAuthenticatorBridgeServiceCallback callback);
// ==============
// Data Syncing

View file

@ -0,0 +1,10 @@
package com.bitwarden.authenticatorbridge;
import com.bitwarden.authenticatorbridge.model.EncryptedSharedAccountData;
interface IAuthenticatorBridgeServiceCallback {
// This function will be called when there is updated shared account data.
void onAccountsSync(in EncryptedSharedAccountData data);
}

View file

@ -0,0 +1,3 @@
package com.bitwarden.authenticatorbridge.model;
parcelable EncryptedAddTotpLoginItemData;

View file

@ -0,0 +1,3 @@
package com.bitwarden.authenticatorbridge.model;
parcelable EncryptedSharedAccountData;

View file

@ -0,0 +1,3 @@
package com.bitwarden.authenticatorbridge.model;
parcelable SymmetricEncryptionKeyData;

View file

@ -1,3 +1,3 @@
package com.bitwarden.bridge.model;
package com.bitwarden.authenticatorbridge.model;
parcelable SymmetricEncryptionKeyFingerprintData;

View file

@ -1,7 +1,4 @@
package com.bitwarden.bridge.model
import android.os.Parcelable
import kotlinx.parcelize.Parcelize
package com.bitwarden.authenticatorbridge.model
/**
* Domain level model for a TOTP item to be added to the Bitwarden app.

View file

@ -1,4 +1,4 @@
package com.bitwarden.bridge.model
package com.bitwarden.authenticatorbridge.model
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

View file

@ -1,4 +1,4 @@
package com.bitwarden.bridge.model
package com.bitwarden.authenticatorbridge.model
import android.os.Parcelable
import kotlinx.parcelize.Parcelize

View file

@ -1,4 +1,4 @@
package com.bitwarden.bridge.model
package com.bitwarden.authenticatorbridge.model
import android.os.Parcelable
import kotlinx.parcelize.Parcelize

View file

@ -1,4 +1,4 @@
package com.bitwarden.bridge.model
package com.bitwarden.authenticatorbridge.model
import android.os.Parcelable
import kotlinx.parcelize.Parcelize

View file

@ -1,4 +1,4 @@
package com.bitwarden.bridge.model
package com.bitwarden.authenticatorbridge.model
import java.time.Instant

View file

@ -1,4 +1,4 @@
package com.bitwarden.bridge.model
package com.bitwarden.authenticatorbridge.model
import kotlinx.serialization.Contextual
import kotlinx.serialization.SerialName

View file

@ -1,4 +1,4 @@
package com.bitwarden.bridge.model
package com.bitwarden.authenticatorbridge.model
import android.os.Parcelable
import kotlinx.parcelize.Parcelize

View file

@ -1,4 +1,4 @@
package com.bitwarden.bridge.model
package com.bitwarden.authenticatorbridge.model
import android.os.Parcelable
import kotlinx.parcelize.Parcelize

View file

@ -1,6 +1,6 @@
package com.bitwarden.bridge.util
package com.bitwarden.authenticatorbridge.util
import com.bitwarden.bridge.BuildConfig
import com.bitwarden.authenticatorbridge.BuildConfig
import kotlinx.serialization.KSerializer
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
@ -13,12 +13,12 @@ import kotlinx.serialization.modules.contextual
import java.time.Instant
/**
* Version of the native bridge sdk.
* Version of the Authenticator Bridge SDK.
*/
const val NATIVE_BRIDGE_SDK_VERSION = BuildConfig.VERSION
const val AUTHENTICATOR_BRIDGE_SDK_VERSION = BuildConfig.VERSION
/**
* Common instance of [Json] that should be used throughout the app.
* Common instance of [Json] that should be used throughout the SDK.
*/
internal val JSON = Json {
// If there are keys returned by the server not modeled by a serializable class,

View file

@ -1,16 +1,16 @@
package com.bitwarden.bridge.util
package com.bitwarden.authenticatorbridge.util
import android.security.keystore.KeyProperties
import com.bitwarden.bridge.IBridgeService
import com.bitwarden.bridge.model.AddTotpLoginItemData
import com.bitwarden.bridge.model.AddTotpLoginItemDataJson
import com.bitwarden.bridge.model.EncryptedAddTotpLoginItemData
import com.bitwarden.bridge.model.EncryptedSharedAccountData
import com.bitwarden.bridge.model.SharedAccountData
import com.bitwarden.bridge.model.SharedAccountDataJson
import com.bitwarden.bridge.model.SymmetricEncryptionKeyData
import com.bitwarden.bridge.model.SymmetricEncryptionKeyFingerprintData
import com.bitwarden.bridge.model.toByteArrayContainer
import com.bitwarden.authenticatorbridge.IAuthenticatorBridgeService
import com.bitwarden.authenticatorbridge.model.AddTotpLoginItemData
import com.bitwarden.authenticatorbridge.model.AddTotpLoginItemDataJson
import com.bitwarden.authenticatorbridge.model.EncryptedAddTotpLoginItemData
import com.bitwarden.authenticatorbridge.model.EncryptedSharedAccountData
import com.bitwarden.authenticatorbridge.model.SharedAccountData
import com.bitwarden.authenticatorbridge.model.SharedAccountDataJson
import com.bitwarden.authenticatorbridge.model.SymmetricEncryptionKeyData
import com.bitwarden.authenticatorbridge.model.SymmetricEncryptionKeyFingerprintData
import com.bitwarden.authenticatorbridge.model.toByteArrayContainer
import kotlinx.serialization.encodeToString
import java.security.MessageDigest
import javax.crypto.Cipher
@ -22,7 +22,8 @@ import javax.crypto.spec.SecretKeySpec
/**
* Generate a symmetric [SecretKey] that will used for encrypting IPC traffic.
*
* This is intended to be used for implementing [IBridgeService.getSymmetricEncryptionKeyData].
* This is intended to be used for implementing
* [IAuthenticatorBridgeService.getSymmetricEncryptionKeyData].
*/
fun generateSecretKey(): Result<SecretKey> = runCatching {
val keygen = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES)
@ -34,7 +35,7 @@ fun generateSecretKey(): Result<SecretKey> = runCatching {
* Generate a fingerprint for the given symmetric key.
*
* This is intended to be used for implementing
* [IBridgeService.checkSymmetricEncryptionKeyFingerprint], which allows callers of the service
* [IAuthenticatorBridgeService.checkSymmetricEncryptionKeyFingerprint], which allows callers of the service
* to verify that they have the correct symmetric key without actually having to send the key.
*/
fun SymmetricEncryptionKeyData.toFingerprint(): Result<SymmetricEncryptionKeyFingerprintData> =
@ -48,7 +49,8 @@ fun SymmetricEncryptionKeyData.toFingerprint(): Result<SymmetricEncryptionKeyFin
/**
* Encrypt [SharedAccountData].
*
* This is intended to be used by the main Bitwarden app during a [IBridgeService.syncAccounts] call.
* This is intended to be used by the main Bitwarden app during a
* [IAuthenticatorBridgeService.syncAccounts] call.
*
* @param symmetricEncryptionKeyData Symmetric key used for encryption.
*/

View file

@ -1,17 +1,16 @@
package com.bitwarden.bridge.util
package com.bitwarden.authenticatorbridge.util
import android.security.keystore.KeyProperties
import com.bitwarden.bridge.model.AddTotpLoginItemData
import com.bitwarden.bridge.model.SharedAccountData
import com.bitwarden.bridge.model.SymmetricEncryptionKeyData
import com.bitwarden.bridge.model.toByteArrayContainer
import com.bitwarden.authenticatorbridge.model.AddTotpLoginItemData
import com.bitwarden.authenticatorbridge.model.SharedAccountData
import com.bitwarden.authenticatorbridge.model.SymmetricEncryptionKeyData
import com.bitwarden.authenticatorbridge.model.toByteArrayContainer
import io.mockk.every
import io.mockk.mockkStatic
import io.mockk.unmockkStatic
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertNotNull
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import java.security.MessageDigest
import java.security.NoSuchAlgorithmException

View file

@ -1,10 +0,0 @@
package com.bitwarden.bridge;
import com.bitwarden.bridge.model.EncryptedSharedAccountData;
interface IBridgeServiceCallback {
// This function will be called when there is updated shared account data.
void onAccountsSync(in EncryptedSharedAccountData data);
}

View file

@ -1,3 +0,0 @@
package com.bitwarden.bridge.model;
parcelable EncryptedAddTotpLoginItemData;

View file

@ -1,3 +0,0 @@
package com.bitwarden.bridge.model;
parcelable EncryptedSharedAccountData;

View file

@ -1,3 +0,0 @@
package com.bitwarden.bridge.model;
parcelable SymmetricEncryptionKeyData;

View file

@ -46,4 +46,4 @@ buildCache {
rootProject.name = "Bitwarden"
include(":app")
include(":bridge")
include(":authenticatorbridge")