Skip to content

Commit 9beb3ec

Browse files
committed
Implement privileged app management for FIDO2
Introduces the ability to trust privileged applications for FIDO2 operations. If a calling application is acting as a privileged application but is not yet trusted, the user will be given the option to trust the app and continue the operation, or to cancel the operation without trusting the application. If the application is trusted, subsequent requests from the application will be treated as a trusted application without user interaction.
1 parent ad6bc88 commit 9beb3ec

File tree

43 files changed

+1649
-852
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+1649
-852
lines changed

app/src/main/assets/fido2_privileged_community.json

-2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
]
1313
}
1414
},
15-
1615
{
1716
"type": "android",
1817
"info": {
@@ -25,7 +24,6 @@
2524
]
2625
}
2726
},
28-
2927
{
3028
"type": "android",
3129
"info": {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package com.x8bit.bitwarden.data.autofill.fido2.datasource.disk
2+
3+
import com.x8bit.bitwarden.data.autofill.fido2.datasource.disk.entity.Fido2PrivilegedAppInfoEntity
4+
import kotlinx.coroutines.flow.Flow
5+
6+
/**
7+
* Primary access point for disk information related to privileged apps trusted for FIDO2
8+
* operations.
9+
*/
10+
interface Fido2PrivilegedAppDiskSource {
11+
12+
/**
13+
* Flow of the user's trusted privileged apps.
14+
*/
15+
val userTrustedPrivilegedAppsFlow: Flow<List<Fido2PrivilegedAppInfoEntity>>
16+
17+
/**
18+
* Retrieves all the user's trusted privileged apps.
19+
*/
20+
suspend fun getAllUserTrustedPrivilegedApps(): List<Fido2PrivilegedAppInfoEntity>
21+
22+
/**
23+
* Adds a privileged app to the user's trusted list.
24+
*/
25+
suspend fun addTrustedPrivilegedApp(packageName: String, signature: String)
26+
27+
/**
28+
* Removes a privileged app from the user's trusted list.
29+
*/
30+
suspend fun removeTrustedPrivilegedApp(packageName: String, signature: String)
31+
32+
/**
33+
* Checks if a privileged app is trusted.
34+
*/
35+
suspend fun isPrivilegedAppTrustedByUser(packageName: String, signature: String): Boolean
36+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package com.x8bit.bitwarden.data.autofill.fido2.datasource.disk
2+
3+
import android.content.SharedPreferences
4+
import com.x8bit.bitwarden.data.autofill.fido2.datasource.disk.dao.Fido2PrivilegedAppInfoDao
5+
import com.x8bit.bitwarden.data.autofill.fido2.datasource.disk.entity.Fido2PrivilegedAppInfoEntity
6+
import com.x8bit.bitwarden.data.platform.datasource.disk.BaseDiskSource
7+
import kotlinx.coroutines.flow.Flow
8+
9+
/**
10+
* Implementation of the [Fido2PrivilegedAppDiskSource] interface.
11+
*/
12+
class Fido2PrivilegedAppDiskSourceImpl(
13+
private val privilegedAppDao: Fido2PrivilegedAppInfoDao,
14+
sharedPreferences: SharedPreferences,
15+
) : Fido2PrivilegedAppDiskSource, BaseDiskSource(sharedPreferences = sharedPreferences) {
16+
17+
override val userTrustedPrivilegedAppsFlow: Flow<List<Fido2PrivilegedAppInfoEntity>> =
18+
privilegedAppDao.getUserTrustedPrivilegedAppsFlow()
19+
20+
override suspend fun getAllUserTrustedPrivilegedApps(): List<Fido2PrivilegedAppInfoEntity> {
21+
return privilegedAppDao.getAllUserTrustedPrivilegedApps()
22+
}
23+
24+
override suspend fun isPrivilegedAppTrustedByUser(
25+
packageName: String,
26+
signature: String,
27+
): Boolean = privilegedAppDao
28+
.getAllUserTrustedPrivilegedApps()
29+
.any { it.packageName == packageName && it.signature == signature }
30+
31+
override suspend fun addTrustedPrivilegedApp(
32+
packageName: String,
33+
signature: String,
34+
) {
35+
privilegedAppDao.addTrustedPrivilegedApp(
36+
appInfo = Fido2PrivilegedAppInfoEntity(
37+
packageName = packageName,
38+
signature = signature,
39+
),
40+
)
41+
}
42+
43+
override suspend fun removeTrustedPrivilegedApp(
44+
packageName: String,
45+
signature: String,
46+
) {
47+
privilegedAppDao.removeTrustedPrivilegedApp(
48+
packageName = packageName,
49+
signature = signature,
50+
)
51+
}
52+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package com.x8bit.bitwarden.data.autofill.fido2.datasource.disk.dao
2+
3+
import androidx.room.Dao
4+
import androidx.room.Insert
5+
import androidx.room.OnConflictStrategy
6+
import androidx.room.Query
7+
import com.x8bit.bitwarden.data.autofill.fido2.datasource.disk.entity.Fido2PrivilegedAppInfoEntity
8+
import kotlinx.coroutines.flow.Flow
9+
10+
/**
11+
* Data Access Object (DAO) for trusted privileged apps.
12+
*/
13+
@Dao
14+
interface Fido2PrivilegedAppInfoDao {
15+
16+
/**
17+
* A flow of all the trusted privileged apps.
18+
*/
19+
@Query("SELECT * FROM fido2_privileged_apps")
20+
fun getUserTrustedPrivilegedAppsFlow(): Flow<List<Fido2PrivilegedAppInfoEntity>>
21+
22+
/**
23+
* Retrieves all the trusted privileged apps.
24+
*/
25+
@Query("SELECT * FROM fido2_privileged_apps")
26+
suspend fun getAllUserTrustedPrivilegedApps(): List<Fido2PrivilegedAppInfoEntity>
27+
28+
/**
29+
* Adds a trusted privileged app.
30+
*/
31+
@Insert(onConflict = OnConflictStrategy.REPLACE)
32+
suspend fun addTrustedPrivilegedApp(appInfo: Fido2PrivilegedAppInfoEntity)
33+
34+
/**
35+
* Removes a trusted privileged app.
36+
*/
37+
@Suppress("MaxLineLength")
38+
@Query("DELETE FROM fido2_privileged_apps WHERE package_name = :packageName AND signature = :signature")
39+
suspend fun removeTrustedPrivilegedApp(packageName: String, signature: String)
40+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package com.x8bit.bitwarden.data.autofill.fido2.datasource.disk.database
2+
3+
import androidx.room.Database
4+
import androidx.room.RoomDatabase
5+
import com.x8bit.bitwarden.data.autofill.fido2.datasource.disk.dao.Fido2PrivilegedAppInfoDao
6+
import com.x8bit.bitwarden.data.autofill.fido2.datasource.disk.entity.Fido2PrivilegedAppInfoEntity
7+
8+
/**
9+
* Room database for storing trusted FIDO2 privileged apps.
10+
*/
11+
@Database(
12+
entities = [
13+
Fido2PrivilegedAppInfoEntity::class,
14+
],
15+
version = 1,
16+
)
17+
abstract class Fido2PrivilegedAppDatabase : RoomDatabase() {
18+
/**
19+
* Provides the DAO for accessing trusted FIDO2 privileged apps.
20+
*/
21+
abstract fun fido2PrivilegedAppInfoDao(): Fido2PrivilegedAppInfoDao
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package com.x8bit.bitwarden.data.autofill.fido2.datasource.disk.di
2+
3+
import android.app.Application
4+
import android.content.SharedPreferences
5+
import androidx.room.Room
6+
import com.x8bit.bitwarden.data.autofill.fido2.datasource.disk.Fido2PrivilegedAppDiskSource
7+
import com.x8bit.bitwarden.data.autofill.fido2.datasource.disk.Fido2PrivilegedAppDiskSourceImpl
8+
import com.x8bit.bitwarden.data.autofill.fido2.datasource.disk.dao.Fido2PrivilegedAppInfoDao
9+
import com.x8bit.bitwarden.data.autofill.fido2.datasource.disk.database.Fido2PrivilegedAppDatabase
10+
import com.x8bit.bitwarden.data.platform.datasource.di.UnencryptedPreferences
11+
import dagger.Module
12+
import dagger.Provides
13+
import dagger.hilt.InstallIn
14+
import dagger.hilt.components.SingletonComponent
15+
import javax.inject.Singleton
16+
17+
/**
18+
* Provides persistence-related dependencies in the FIDO2 package.
19+
*/
20+
@Module
21+
@InstallIn(SingletonComponent::class)
22+
object Fido2DiskModule {
23+
24+
@Provides
25+
@Singleton
26+
fun provideFido2PrivilegedAppDatabase(
27+
app: Application,
28+
): Fido2PrivilegedAppDatabase =
29+
Room
30+
.databaseBuilder(
31+
context = app,
32+
klass = Fido2PrivilegedAppDatabase::class.java,
33+
name = "fido2_privileged_apps_database",
34+
)
35+
.fallbackToDestructiveMigration()
36+
.build()
37+
38+
@Provides
39+
@Singleton
40+
fun provideFido2PrivilegedAppDao(
41+
database: Fido2PrivilegedAppDatabase,
42+
): Fido2PrivilegedAppInfoDao = database.fido2PrivilegedAppInfoDao()
43+
44+
@Provides
45+
@Singleton
46+
fun provideFido2PrivilegedAppDiskSource(
47+
fido2PrivilegedAppDao: Fido2PrivilegedAppInfoDao,
48+
@UnencryptedPreferences sharedPreferences: SharedPreferences,
49+
): Fido2PrivilegedAppDiskSource =
50+
Fido2PrivilegedAppDiskSourceImpl(
51+
privilegedAppDao = fido2PrivilegedAppDao,
52+
sharedPreferences = sharedPreferences,
53+
)
54+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package com.x8bit.bitwarden.data.autofill.fido2.datasource.disk.entity
2+
3+
import androidx.room.ColumnInfo
4+
import androidx.room.Entity
5+
6+
/**
7+
* Entity representing a trusted privileged app in the database.
8+
*/
9+
@Entity(
10+
tableName = "fido2_privileged_apps",
11+
primaryKeys = ["package_name", "signature"],
12+
)
13+
data class Fido2PrivilegedAppInfoEntity(
14+
@ColumnInfo(name = "package_name")
15+
val packageName: String,
16+
17+
@ColumnInfo(name = "signature")
18+
val signature: String,
19+
)

app/src/main/java/com/x8bit/bitwarden/data/autofill/fido2/di/Fido2ProviderModule.kt

+20
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,16 @@ import android.os.Build
55
import androidx.annotation.RequiresApi
66
import com.bitwarden.sdk.Fido2CredentialStore
77
import com.x8bit.bitwarden.data.auth.repository.AuthRepository
8+
import com.x8bit.bitwarden.data.autofill.fido2.datasource.disk.Fido2PrivilegedAppDiskSource
89
import com.x8bit.bitwarden.data.autofill.fido2.datasource.network.service.DigitalAssetLinkService
910
import com.x8bit.bitwarden.data.autofill.fido2.manager.Fido2CredentialManager
1011
import com.x8bit.bitwarden.data.autofill.fido2.manager.Fido2CredentialManagerImpl
1112
import com.x8bit.bitwarden.data.autofill.fido2.manager.Fido2OriginManager
1213
import com.x8bit.bitwarden.data.autofill.fido2.manager.Fido2OriginManagerImpl
1314
import com.x8bit.bitwarden.data.autofill.fido2.processor.Fido2ProviderProcessor
1415
import com.x8bit.bitwarden.data.autofill.fido2.processor.Fido2ProviderProcessorImpl
16+
import com.x8bit.bitwarden.data.autofill.fido2.repository.PrivilegedAppRepository
17+
import com.x8bit.bitwarden.data.autofill.fido2.repository.PrivilegedAppRepositoryImpl
1518
import com.x8bit.bitwarden.data.platform.manager.AssetManager
1619
import com.x8bit.bitwarden.data.platform.manager.BiometricsEncryptionManager
1720
import com.x8bit.bitwarden.data.platform.manager.FeatureFlagManager
@@ -69,12 +72,27 @@ object Fido2ProviderModule {
6972
vaultSdkSource: VaultSdkSource,
7073
fido2CredentialStore: Fido2CredentialStore,
7174
fido2OriginManager: Fido2OriginManager,
75+
intentManager: IntentManager,
7276
json: Json,
77+
@ApplicationContext context: Context,
7378
): Fido2CredentialManager =
7479
Fido2CredentialManagerImpl(
7580
vaultSdkSource = vaultSdkSource,
7681
fido2CredentialStore = fido2CredentialStore,
7782
fido2OriginManager = fido2OriginManager,
83+
intentManager = intentManager,
84+
context = context,
85+
json = json,
86+
)
87+
88+
@Provides
89+
@Singleton
90+
fun provideFido2PrivilegedAppRepository(
91+
fido2PrivilegedAppDiskSource: Fido2PrivilegedAppDiskSource,
92+
json: Json,
93+
): PrivilegedAppRepository =
94+
PrivilegedAppRepositoryImpl(
95+
fido2PrivilegedAppDiskSource = fido2PrivilegedAppDiskSource,
7896
json = json,
7997
)
8098

@@ -83,9 +101,11 @@ object Fido2ProviderModule {
83101
fun provideFido2OriginManager(
84102
assetManager: AssetManager,
85103
digitalAssetLinkService: DigitalAssetLinkService,
104+
privilegedAppRepository: PrivilegedAppRepository,
86105
): Fido2OriginManager =
87106
Fido2OriginManagerImpl(
88107
assetManager = assetManager,
89108
digitalAssetLinkService = digitalAssetLinkService,
109+
privilegedAppRepository = privilegedAppRepository,
90110
)
91111
}

app/src/main/java/com/x8bit/bitwarden/data/autofill/fido2/manager/Fido2CredentialManager.kt

+11
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
package com.x8bit.bitwarden.data.autofill.fido2.manager
22

3+
import com.bitwarden.fido.Fido2CredentialAutofillView
34
import com.bitwarden.vault.CipherView
45
import com.x8bit.bitwarden.data.autofill.fido2.model.Fido2CreateCredentialRequest
56
import com.x8bit.bitwarden.data.autofill.fido2.model.Fido2CredentialAssertionRequest
67
import com.x8bit.bitwarden.data.autofill.fido2.model.Fido2CredentialAssertionResult
8+
import com.x8bit.bitwarden.data.autofill.fido2.model.Fido2GetCredentialsRequest
9+
import com.x8bit.bitwarden.data.autofill.fido2.model.Fido2GetCredentialsResult
710
import com.x8bit.bitwarden.data.autofill.fido2.model.Fido2RegisterCredentialResult
811
import com.x8bit.bitwarden.data.autofill.fido2.model.PasskeyAssertionOptions
912
import com.x8bit.bitwarden.data.autofill.fido2.model.PasskeyAttestationOptions
@@ -47,6 +50,14 @@ interface Fido2CredentialManager {
4750
selectedCipherView: CipherView,
4851
): Fido2RegisterCredentialResult
4952

53+
/**
54+
* Retrieve FIDO 2 credentials for a given relying party.
55+
*/
56+
fun getCredentialsForRelyingParty(
57+
request: Fido2GetCredentialsRequest,
58+
autofillViews: List<Fido2CredentialAutofillView>,
59+
): Fido2GetCredentialsResult
60+
5061
/**
5162
* Authenticate a FIDO credential against a cipher in the users vault.
5263
*/

0 commit comments

Comments
 (0)