@@ -6,21 +6,17 @@ import com.bitwarden.fido.Origin
6
6
import com.bitwarden.fido.UnverifiedAssetLink
7
7
import com.bitwarden.sdk.Fido2CredentialStore
8
8
import com.bitwarden.vault.CipherView
9
- import com.x8bit.bitwarden.data.autofill.fido2.datasource.network.model.DigitalAssetLinkResponseJson
10
- import com.x8bit.bitwarden.data.autofill.fido2.datasource.network.service.DigitalAssetLinkService
11
9
import com.x8bit.bitwarden.data.autofill.fido2.model.Fido2CreateCredentialRequest
12
10
import com.x8bit.bitwarden.data.autofill.fido2.model.Fido2CredentialAssertionRequest
13
11
import com.x8bit.bitwarden.data.autofill.fido2.model.Fido2CredentialAssertionResult
14
12
import com.x8bit.bitwarden.data.autofill.fido2.model.Fido2RegisterCredentialResult
15
13
import com.x8bit.bitwarden.data.autofill.fido2.model.Fido2ValidateOriginResult
16
14
import com.x8bit.bitwarden.data.autofill.fido2.model.PasskeyAssertionOptions
17
15
import com.x8bit.bitwarden.data.autofill.fido2.model.PasskeyAttestationOptions
18
- import com.x8bit.bitwarden.data.platform.manager.AssetManager
19
16
import com.x8bit.bitwarden.data.platform.util.decodeFromStringOrNull
20
17
import com.x8bit.bitwarden.data.platform.util.getAppOrigin
21
18
import com.x8bit.bitwarden.data.platform.util.getAppSigningSignatureFingerprint
22
19
import com.x8bit.bitwarden.data.platform.util.getSignatureFingerprintAsHexString
23
- import com.x8bit.bitwarden.data.platform.util.validatePrivilegedApp
24
20
import com.x8bit.bitwarden.data.vault.datasource.sdk.VaultSdkSource
25
21
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.AuthenticateFido2CredentialRequest
26
22
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.RegisterFido2CredentialRequest
@@ -31,18 +27,14 @@ import kotlinx.serialization.SerializationException
31
27
import kotlinx.serialization.encodeToString
32
28
import kotlinx.serialization.json.Json
33
29
34
- private const val GOOGLE_ALLOW_LIST_FILE_NAME = " fido2_privileged_google.json"
35
- private const val COMMUNITY_ALLOW_LIST_FILE_NAME = " fido2_privileged_community.json"
36
-
37
30
/* *
38
31
* Primary implementation of [Fido2CredentialManager].
39
32
*/
40
33
@Suppress(" TooManyFunctions" )
41
34
class Fido2CredentialManagerImpl (
42
- private val assetManager : AssetManager ,
43
- private val digitalAssetLinkService : DigitalAssetLinkService ,
44
35
private val vaultSdkSource : VaultSdkSource ,
45
36
private val fido2CredentialStore : Fido2CredentialStore ,
37
+ private val fido2OriginManager : Fido2OriginManager ,
46
38
private val json : Json ,
47
39
) : Fido2CredentialManager,
48
40
Fido2CredentialStore by fido2CredentialStore {
@@ -108,16 +100,14 @@ class Fido2CredentialManagerImpl(
108
100
)
109
101
}
110
102
111
- override suspend fun validateOrigin (
103
+ private suspend fun validateOrigin (
112
104
callingAppInfo : CallingAppInfo ,
113
105
relyingPartyId : String ,
114
- ): Fido2ValidateOriginResult {
115
- return if (callingAppInfo.isOriginPopulated()) {
116
- validatePrivilegedAppOrigin(callingAppInfo)
117
- } else {
118
- validateCallingApplicationAssetLinks(callingAppInfo, relyingPartyId)
119
- }
120
- }
106
+ ): Fido2ValidateOriginResult = fido2OriginManager
107
+ .validateOrigin(
108
+ callingAppInfo = callingAppInfo,
109
+ relyingPartyId = relyingPartyId,
110
+ )
121
111
122
112
override fun getPasskeyAttestationOptionsOrNull (
123
113
requestJson : String ,
@@ -168,7 +158,7 @@ class Fido2CredentialManagerImpl(
168
158
Fido2CredentialAssertionResult .Error
169
159
}
170
160
171
- Fido2ValidateOriginResult .Success -> {
161
+ is Fido2ValidateOriginResult .Success -> {
172
162
vaultSdkSource
173
163
.authenticateFido2Credential(
174
164
request = AuthenticateFido2CredentialRequest (
@@ -200,127 +190,6 @@ class Fido2CredentialManagerImpl(
200
190
}
201
191
}
202
192
203
- private suspend fun validateCallingApplicationAssetLinks (
204
- callingAppInfo : CallingAppInfo ,
205
- relyingPartyId : String ,
206
- ): Fido2ValidateOriginResult {
207
- return digitalAssetLinkService
208
- .getDigitalAssetLinkForRp(relyingParty = relyingPartyId)
209
- .onFailure {
210
- return Fido2ValidateOriginResult .Error .AssetLinkNotFound
211
- }
212
- .map { statements ->
213
- statements
214
- .filterMatchingAppStatementsOrNull(
215
- rpPackageName = callingAppInfo.packageName,
216
- )
217
- ? : return Fido2ValidateOriginResult .Error .ApplicationNotFound
218
- }
219
- .map { matchingStatements ->
220
- callingAppInfo
221
- .getSignatureFingerprintAsHexString()
222
- ?.let { certificateFingerprint ->
223
- matchingStatements
224
- .filterMatchingAppSignaturesOrNull(
225
- signature = certificateFingerprint,
226
- )
227
- }
228
- ? : return Fido2ValidateOriginResult .Error .ApplicationNotVerified
229
- }
230
- .fold(
231
- onSuccess = {
232
- Fido2ValidateOriginResult .Success
233
- },
234
- onFailure = {
235
- Fido2ValidateOriginResult .Error .Unknown
236
- },
237
- )
238
- }
239
-
240
- private suspend fun validatePrivilegedAppOrigin (
241
- callingAppInfo : CallingAppInfo ,
242
- ): Fido2ValidateOriginResult {
243
- val googleAllowListResult =
244
- validatePrivilegedAppSignatureWithGoogleList(callingAppInfo)
245
- return when (googleAllowListResult) {
246
- is Fido2ValidateOriginResult .Success -> {
247
- // Application was found and successfully validated against the Google allow list so
248
- // we can return the result as the final validation result.
249
- googleAllowListResult
250
- }
251
-
252
- is Fido2ValidateOriginResult .Error -> {
253
- // Check the community allow list if the Google allow list failed, and return the
254
- // result as the final validation result.
255
- validatePrivilegedAppSignatureWithCommunityList(callingAppInfo)
256
- }
257
- }
258
- }
259
-
260
- private suspend fun validatePrivilegedAppSignatureWithGoogleList (
261
- callingAppInfo : CallingAppInfo ,
262
- ): Fido2ValidateOriginResult =
263
- validatePrivilegedAppSignatureWithAllowList(
264
- callingAppInfo = callingAppInfo,
265
- fileName = GOOGLE_ALLOW_LIST_FILE_NAME ,
266
- )
267
-
268
- private suspend fun validatePrivilegedAppSignatureWithCommunityList (
269
- callingAppInfo : CallingAppInfo ,
270
- ): Fido2ValidateOriginResult =
271
- validatePrivilegedAppSignatureWithAllowList(
272
- callingAppInfo = callingAppInfo,
273
- fileName = COMMUNITY_ALLOW_LIST_FILE_NAME ,
274
- )
275
-
276
- private suspend fun validatePrivilegedAppSignatureWithAllowList (
277
- callingAppInfo : CallingAppInfo ,
278
- fileName : String ,
279
- ): Fido2ValidateOriginResult =
280
- assetManager
281
- .readAsset(fileName)
282
- .map { allowList ->
283
- callingAppInfo.validatePrivilegedApp(
284
- allowList = allowList,
285
- )
286
- }
287
- .fold(
288
- onSuccess = { it },
289
- onFailure = { Fido2ValidateOriginResult .Error .Unknown },
290
- )
291
-
292
- /* *
293
- * Returns statements targeting the calling Android application, or null.
294
- */
295
- private fun List<DigitalAssetLinkResponseJson>.filterMatchingAppStatementsOrNull (
296
- rpPackageName : String ,
297
- ): List <DigitalAssetLinkResponseJson >? =
298
- filter { statement ->
299
- val target = statement.target
300
- target.namespace == " android_app" &&
301
- target.packageName == rpPackageName &&
302
- statement.relation.containsAll(
303
- listOf (
304
- " delegate_permission/common.get_login_creds" ,
305
- " delegate_permission/common.handle_all_urls" ,
306
- ),
307
- )
308
- }
309
- .takeUnless { it.isEmpty() }
310
-
311
- /* *
312
- * Returns statements that match the given [signature], or null.
313
- */
314
- private fun List<DigitalAssetLinkResponseJson>.filterMatchingAppSignaturesOrNull (
315
- signature : String ,
316
- ): List <DigitalAssetLinkResponseJson >? =
317
- filter { statement ->
318
- statement.target.sha256CertFingerprints
319
- ?.contains(signature)
320
- ? : false
321
- }
322
- .takeUnless { it.isEmpty() }
323
-
324
193
override fun hasAuthenticationAttemptsRemaining (): Boolean =
325
194
authenticationAttempts < MAX_AUTHENTICATION_ATTEMPTS
326
195
0 commit comments