Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
6d2b7b0
SDK Update - com.bitwarden:sdk-android 1.0.0-3966-a09e691a
bw-ghapp[bot] Dec 2, 2025
cdcd411
SDK Update - com.bitwarden:sdk-android 1.0.0-3967-069d7829
bw-ghapp[bot] Dec 2, 2025
0319173
SDK Update - com.bitwarden:sdk-android 1.0.0-3972-7e4b2cc6
bw-ghapp[bot] Dec 2, 2025
9feb7e7
SDK Update - com.bitwarden:sdk-android 1.0.0-3973-ab7ae369
bw-ghapp[bot] Dec 3, 2025
d7fec88
SDK Update - com.bitwarden:sdk-android 1.0.0-4001-5c178bed
bw-ghapp[bot] Dec 8, 2025
32c4790
SDK Update - com.bitwarden:sdk-android 1.0.0-4005-7840f554
bw-ghapp[bot] Dec 8, 2025
e255f41
SDK Update - com.bitwarden:sdk-android 1.0.0-4006-0d52f617
bw-ghapp[bot] Dec 8, 2025
f954ff7
SDK Update - com.bitwarden:sdk-android 1.0.0-4016-a8a19d3f
bw-ghapp[bot] Dec 8, 2025
b157b8e
SDK Update - com.bitwarden:sdk-android 1.0.0-4033-1cc3a8d0
bw-ghapp[bot] Dec 9, 2025
60e548e
SDK Update - com.bitwarden:sdk-android 1.0.0-4036-becb420b
bw-ghapp[bot] Dec 9, 2025
5458dba
SDK Update - com.bitwarden:sdk-android 1.0.0-4044-14a6450a
bw-ghapp[bot] Dec 10, 2025
bab53e7
SDK Update - com.bitwarden:sdk-android 1.0.0-4046-b86e0206
bw-ghapp[bot] Dec 10, 2025
656e062
SDK Update - com.bitwarden:sdk-android 1.0.0-4047-26dffec3
bw-ghapp[bot] Dec 10, 2025
f26bf67
SDK Update - com.bitwarden:sdk-android 1.0.0-4049-f0d4b0e7
bw-ghapp[bot] Dec 10, 2025
1268bf2
SDK Update - com.bitwarden:sdk-android 1.0.0-4063-ec0231c0
bw-ghapp[bot] Dec 10, 2025
03d4366
SDK Update - com.bitwarden:sdk-android 2.0.0-4076-26df6719
bw-ghapp[bot] Dec 10, 2025
345212b
SDK Update - com.bitwarden:sdk-android 2.0.0-4078-a004d825
bw-ghapp[bot] Dec 10, 2025
93f9e7d
SDK Update - com.bitwarden:sdk-android 2.0.0-4088-30879c14
bw-ghapp[bot] Dec 11, 2025
f0e05c2
SDK Update - com.bitwarden:sdk-android 2.0.0-4101-fc6d3170
bw-ghapp[bot] Dec 11, 2025
edb1a93
SDK Update - com.bitwarden:sdk-android 2.0.0-4109-fd11c49b
bw-ghapp[bot] Dec 12, 2025
e88b750
SDK Update - com.bitwarden:sdk-android 2.0.0-4110-bd636a4f
bw-ghapp[bot] Dec 12, 2025
29cbcaf
pm-29777 Fix breaking changes
LRNcardozoWDF Dec 15, 2025
8443f38
Merge branch 'main' into sdlc/sdk-update
LRNcardozoWDF Dec 16, 2025
207a3f1
pm-29777 Update sdk to 2.0.0-4151-b444e590
LRNcardozoWDF Dec 17, 2025
0235cfe
Merge remote-tracking branch 'origin/main' into sdlc/sdk-update
LRNcardozoWDF Dec 17, 2025
58535b0
pm-29777 Add tests for new extension
LRNcardozoWDF Dec 18, 2025
1b5ff1c
pm-29777 Added tests
LRNcardozoWDF Dec 18, 2025
d1357d3
Merge remote-tracking branch 'origin/main' into sdlc/sdk-update
LRNcardozoWDF Dec 18, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.x8bit.bitwarden.data.auth.repository

import com.bitwarden.core.AuthRequestMethod
import com.bitwarden.core.InitUserCryptoMethod
import com.bitwarden.core.WrappedAccountCryptographicState
import com.bitwarden.core.data.manager.dispatcher.DispatcherManager
import com.bitwarden.core.data.repository.error.MissingPropertyException
import com.bitwarden.core.data.repository.util.bufferedMutableSharedFlow
Expand Down Expand Up @@ -113,6 +114,7 @@ import com.x8bit.bitwarden.data.vault.datasource.sdk.VaultSdkSource
import com.x8bit.bitwarden.data.vault.repository.VaultRepository
import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockError
import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockResult
import com.x8bit.bitwarden.data.vault.repository.util.createWrappedAccountCryptographicState
import com.x8bit.bitwarden.data.vault.repository.util.toSdkMasterPasswordUnlock
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
Expand Down Expand Up @@ -517,17 +519,21 @@ class AuthRepositoryImpl(
)
val signingKey = accountKeys?.signatureKeyPair?.wrappedSigningKey
val securityState = accountKeys?.securityState?.securityState
val signedPublicKey = accountKeys?.publicKeyEncryptionKeyPair?.signedPublicKey

checkForVaultUnlockError(
onVaultUnlockError = { error ->
return error.toLoginErrorResult()
},
) {
unlockVault(
accountCryptographicState = createWrappedAccountCryptographicState(
privateKey = privateKey,
securityState = securityState,
signingKey = signingKey,
signedPublicKey = signedPublicKey,
),
accountProfile = profile,
privateKey = privateKey,
signingKey = signingKey,
securityState = securityState,
initUserCryptoMethod = InitUserCryptoMethod.AuthRequest(
requestPrivateKey = requestPrivateKey,
method = AuthRequestMethod.UserKey(protectedUserKey = asymmetricalKey),
Expand Down Expand Up @@ -1809,14 +1815,23 @@ class AuthRepositoryImpl(
)
.map {
unlockVault(
accountCryptographicState = createWrappedAccountCryptographicState(
privateKey = privateKey,
securityState = loginResponse.accountKeys
?.securityState
?.securityState,
signingKey = loginResponse.accountKeys
?.signatureKeyPair
?.wrappedSigningKey,
signedPublicKey = loginResponse.accountKeys
?.publicKeyEncryptionKeyPair
?.signedPublicKey,
),
accountProfile = profile,
privateKey = privateKey,
initUserCryptoMethod = InitUserCryptoMethod.KeyConnector(
masterKey = it.masterKey,
userKey = key,
),
securityState = loginResponse.accountKeys?.securityState?.securityState,
signingKey = loginResponse.accountKeys?.signatureKeyPair?.wrappedSigningKey,
)
}
.fold(
Expand All @@ -1837,11 +1852,21 @@ class AuthRepositoryImpl(
organizationIdentifier = orgIdentifier,
)
.map { keyConnectorResponse ->
val accountKeys = loginResponse.accountKeys
val result = unlockVault(
accountCryptographicState = createWrappedAccountCryptographicState(
privateKey = keyConnectorResponse.keys.private,
securityState = accountKeys
?.securityState
?.securityState,
signingKey = accountKeys
?.signatureKeyPair
?.wrappedSigningKey,
signedPublicKey = accountKeys
?.publicKeyEncryptionKeyPair
?.signedPublicKey,
),
accountProfile = profile,
privateKey = keyConnectorResponse.keys.private,
securityState = loginResponse.accountKeys?.securityState?.securityState,
signingKey = loginResponse.accountKeys?.signatureKeyPair?.wrappedSigningKey,
initUserCryptoMethod = InitUserCryptoMethod.KeyConnector(
masterKey = keyConnectorResponse.masterKey,
userKey = keyConnectorResponse.encryptedUserKey,
Expand Down Expand Up @@ -1897,17 +1922,27 @@ class AuthRepositoryImpl(
)

return unlockVault(
accountCryptographicState = createWrappedAccountCryptographicState(
privateKey = privateKey,
securityState = loginResponse.accountKeys
?.securityState
?.securityState,
signingKey = loginResponse.accountKeys
?.signatureKeyPair
?.wrappedSigningKey,
signedPublicKey = loginResponse.accountKeys
?.publicKeyEncryptionKeyPair
?.signedPublicKey,
),
accountProfile = profile,
privateKey = privateKey,
securityState = loginResponse.accountKeys?.securityState?.securityState,
signingKey = loginResponse.accountKeys?.signatureKeyPair?.wrappedSigningKey,
initUserCryptoMethod = initUserCryptoMethod,
)
}

/**
* Attempt to unlock the current user's vault with trusted device specific data.
*/
@Suppress("LongMethod")
private suspend fun unlockVaultWithTdeOnLoginSuccess(
loginResponse: GetTokenResponseJson.Success,
profile: AccountJson.Profile,
Expand All @@ -1920,10 +1955,19 @@ class AuthRepositoryImpl(
if (privateKey != null && key != null) {
deviceData?.let { model ->
return unlockVault(
accountCryptographicState = createWrappedAccountCryptographicState(
privateKey = privateKey,
securityState = loginResponse.accountKeys
?.securityState
?.securityState,
signingKey = loginResponse.accountKeys
?.signatureKeyPair
?.wrappedSigningKey,
signedPublicKey = loginResponse.accountKeys
?.publicKeyEncryptionKeyPair
?.signedPublicKey,
),
accountProfile = profile,
privateKey = privateKey,
securityState = loginResponse.accountKeys?.securityState?.securityState,
signingKey = loginResponse.accountKeys?.signatureKeyPair?.wrappedSigningKey,
initUserCryptoMethod = InitUserCryptoMethod.AuthRequest(
requestPrivateKey = model.privateKey,
method = model
Expand Down Expand Up @@ -1953,9 +1997,18 @@ class AuthRepositoryImpl(
unlockVaultWithTrustedDeviceUserDecryptionOptionsAndStoreKeys(
options = options,
profile = profile,
privateKey = accountKeys.publicKeyEncryptionKeyPair.wrappedPrivateKey,
securityState = accountKeys.securityState?.securityState,
signingKey = accountKeys.signatureKeyPair?.wrappedSigningKey,
privateKey = accountKeys
.publicKeyEncryptionKeyPair
.wrappedPrivateKey,
securityState = accountKeys
.securityState
?.securityState,
signedPublicKey = accountKeys
.publicKeyEncryptionKeyPair
.signedPublicKey,
signingKey = accountKeys
.signatureKeyPair
?.wrappedSigningKey,
)
}
?: loginResponse.privateKey
Expand All @@ -1965,6 +2018,7 @@ class AuthRepositoryImpl(
profile = profile,
privateKey = privateKey,
securityState = null,
signedPublicKey = null,
signingKey = null,
)
}
Expand All @@ -1980,6 +2034,7 @@ class AuthRepositoryImpl(
profile: AccountJson.Profile,
privateKey: String,
securityState: String?,
signedPublicKey: String?,
signingKey: String?,
): VaultUnlockResult? {
var vaultUnlockResult: VaultUnlockResult? = null
Expand All @@ -1997,10 +2052,13 @@ class AuthRepositoryImpl(
// For approved requests the key will always be present.
val userKey = requireNotNull(request.key)
vaultUnlockResult = unlockVault(
accountCryptographicState = createWrappedAccountCryptographicState(
privateKey = privateKey,
securityState = securityState,
signingKey = signingKey,
signedPublicKey = signedPublicKey,
),
accountProfile = profile,
privateKey = privateKey,
signingKey = signingKey,
securityState = securityState,
initUserCryptoMethod = InitUserCryptoMethod.AuthRequest(
requestPrivateKey = pendingRequest.requestPrivateKey,
method = AuthRequestMethod.UserKey(protectedUserKey = userKey),
Expand All @@ -2026,10 +2084,13 @@ class AuthRepositoryImpl(
}

vaultUnlockResult = unlockVault(
accountCryptographicState = createWrappedAccountCryptographicState(
privateKey = privateKey,
securityState = securityState,
signingKey = signingKey,
signedPublicKey = signedPublicKey,
),
accountProfile = profile,
privateKey = privateKey,
securityState = securityState,
signingKey = signingKey,
initUserCryptoMethod = InitUserCryptoMethod.DeviceKey(
deviceKey = deviceKey,
protectedDevicePrivateKey = encryptedPrivateKey,
Expand All @@ -2047,20 +2108,16 @@ class AuthRepositoryImpl(
* A helper function to unlock the vault for the user associated with the [accountProfile].
*/
private suspend fun unlockVault(
accountCryptographicState: WrappedAccountCryptographicState,
accountProfile: AccountJson.Profile,
privateKey: String,
securityState: String?,
signingKey: String?,
initUserCryptoMethod: InitUserCryptoMethod,
): VaultUnlockResult {
val userId = accountProfile.userId
return vaultRepository.unlockVault(
accountCryptographicState = accountCryptographicState,
userId = userId,
email = accountProfile.email,
kdf = accountProfile.toSdkParams(),
privateKey = privateKey,
signingKey = signingKey,
securityState = securityState,
initUserCryptoMethod = initUserCryptoMethod,
// The value for the organization keys here will typically be null. We can separately
// unlock the vault for organization data after receiving the sync response if this
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@ class BitwardenCredentialManagerImpl(
userId = userId,
fido2CredentialStore = fido2CredentialStore,
relyingPartyId = relyingPartyId,
userHandle = null,
)
.fold(
onSuccess = { it },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import com.x8bit.bitwarden.data.vault.datasource.disk.VaultDiskSource
import com.x8bit.bitwarden.data.vault.datasource.sdk.ScopedVaultSdkSource
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.InitializeCryptoResult
import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockResult
import com.x8bit.bitwarden.data.vault.repository.util.createWrappedAccountCryptographicState
import com.x8bit.bitwarden.data.vault.repository.util.toEncryptedSdkCipher
import com.x8bit.bitwarden.data.vault.repository.util.toVaultUnlockResult

Expand Down Expand Up @@ -137,17 +138,21 @@ class AuthenticatorBridgeRepositoryImpl(
?.securityState
?.securityState
val signingKey = accountKeys?.signatureKeyPair?.wrappedSigningKey
val signedPublicKey = accountKeys?.publicKeyEncryptionKeyPair?.signedPublicKey

return scopedVaultSdkSource
.initializeCrypto(
userId = userId,
request = InitUserCryptoRequest(
accountCryptographicState = createWrappedAccountCryptographicState(
privateKey = privateKey,
securityState = securityState,
signingKey = signingKey,
signedPublicKey = signedPublicKey,
),
userId = userId,
kdfParams = account.profile.toSdkParams(),
email = account.profile.email,
privateKey = privateKey,
securityState = securityState,
signingKey = signingKey,
method = InitUserCryptoMethod.DecryptedKey(
decryptedUserKey = decryptedUserKey,
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,7 @@ interface VaultSdkSource {
userId: String,
fido2CredentialStore: Fido2CredentialStore,
relyingPartyId: String,
userHandle: String?,
): Result<List<Fido2CredentialAutofillView>>

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -599,6 +599,7 @@ class VaultSdkSourceImpl(
userId: String,
fido2CredentialStore: Fido2CredentialStore,
relyingPartyId: String,
userHandle: String?,
): Result<List<Fido2CredentialAutofillView>> = runCatchingWithLogs {
getClient(userId)
.platform()
Expand All @@ -607,7 +608,7 @@ class VaultSdkSourceImpl(
userInterface = Fido2CredentialSearchUserInterfaceImpl(),
credentialStore = fido2CredentialStore,
)
.silentlyDiscoverCredentials(relyingPartyId)
.silentlyDiscoverCredentials(relyingPartyId, userHandle?.toByteArray())
}

override suspend fun makeUpdateKdf(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class Fido2CredentialAuthenticationUserInterfaceImpl(
newCredential: Fido2CredentialNewView,
): CheckUserAndPickCredentialForCreationResult = throw IllegalStateException()

override suspend fun isVerificationEnabled(): Boolean = isVerificationSupported
override fun isVerificationEnabled(): Boolean = isVerificationSupported

override suspend fun pickCredentialForAuthentication(
availableCredentials: List<CipherView>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class Fido2CredentialRegistrationUserInterfaceImpl(
checkUserResult = CheckUserResult(userPresent = true, userVerified = true),
)

override suspend fun isVerificationEnabled(): Boolean = isVerificationSupported
override fun isVerificationEnabled(): Boolean = isVerificationSupported

override suspend fun pickCredentialForAuthentication(
availableCredentials: List<CipherView>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class Fido2CredentialSearchUserInterfaceImpl : Fido2UserInterface {

// Always return true for this property because any problems with verification should
// be handled downstream where the app can actually offer verification methods.
override suspend fun isVerificationEnabled(): Boolean = true
override fun isVerificationEnabled(): Boolean = true

override suspend fun pickCredentialForAuthentication(
availableCredentials: List<CipherView>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,11 @@ class Fido2CredentialStoreImpl(
* @param ids Optional list of FIDO 2 credential ID's to find.
* @param ripId Relying Party ID to find.
*/
override suspend fun findCredentials(ids: List<ByteArray>?, ripId: String): List<CipherView> =
override suspend fun findCredentials(
ids: List<ByteArray>?,
ripId: String,
userHandle: ByteArray?,
): List<CipherView> =
vaultRepository
.decryptCipherListResultStateFlow
.value
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.x8bit.bitwarden.data.vault.manager

import com.bitwarden.core.InitUserCryptoMethod
import com.bitwarden.core.WrappedAccountCryptographicState
import com.bitwarden.crypto.Kdf
import com.bitwarden.sdk.AuthClient
import com.x8bit.bitwarden.data.vault.manager.model.VaultStateEvent
Expand Down Expand Up @@ -61,12 +62,10 @@ interface VaultLockManager {
*/
@Suppress("LongParameterList")
suspend fun unlockVault(
accountCryptographicState: WrappedAccountCryptographicState,
userId: String,
email: String,
kdf: Kdf,
privateKey: String,
signingKey: String?,
securityState: String?,
initUserCryptoMethod: InitUserCryptoMethod,
organizationKeys: Map<String, String>?,
): VaultUnlockResult
Expand Down
Loading
Loading