mirror of
https://github.com/bitwarden/android.git
synced 2024-10-31 07:05:35 +03:00
[PM-13101] Validate FIDO2 privileged apps against community allow list (#4022)
This commit is contained in:
parent
60fce08c7e
commit
73a802a483
3 changed files with 81 additions and 7 deletions
|
@ -31,7 +31,8 @@ import kotlinx.serialization.SerializationException
|
|||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
private const val ALLOW_LIST_FILE_NAME = "fido2_privileged_allow_list.json"
|
||||
private const val GOOGLE_ALLOW_LIST_FILE_NAME = "fido2_privileged_google.json"
|
||||
private const val COMMUNITY_ALLOW_LIST_FILE_NAME = "fido2_privileged_community.json"
|
||||
|
||||
/**
|
||||
* Primary implementation of [Fido2CredentialManager].
|
||||
|
@ -203,7 +204,8 @@ class Fido2CredentialManagerImpl(
|
|||
callingAppInfo: CallingAppInfo,
|
||||
relyingPartyId: String,
|
||||
): Fido2ValidateOriginResult {
|
||||
return digitalAssetLinkService.getDigitalAssetLinkForRp(relyingParty = relyingPartyId)
|
||||
return digitalAssetLinkService
|
||||
.getDigitalAssetLinkForRp(relyingParty = relyingPartyId)
|
||||
.onFailure {
|
||||
return Fido2ValidateOriginResult.Error.AssetLinkNotFound
|
||||
}
|
||||
|
@ -215,7 +217,8 @@ class Fido2CredentialManagerImpl(
|
|||
?: return Fido2ValidateOriginResult.Error.ApplicationNotFound
|
||||
}
|
||||
.map { matchingStatements ->
|
||||
callingAppInfo.getSignatureFingerprintAsHexString()
|
||||
callingAppInfo
|
||||
.getSignatureFingerprintAsHexString()
|
||||
?.let { certificateFingerprint ->
|
||||
matchingStatements
|
||||
.filterMatchingAppSignaturesOrNull(
|
||||
|
@ -236,9 +239,46 @@ class Fido2CredentialManagerImpl(
|
|||
|
||||
private suspend fun validatePrivilegedAppOrigin(
|
||||
callingAppInfo: CallingAppInfo,
|
||||
): Fido2ValidateOriginResult {
|
||||
val googleAllowListResult =
|
||||
validatePrivilegedAppSignatureWithGoogleList(callingAppInfo)
|
||||
return when (googleAllowListResult) {
|
||||
is Fido2ValidateOriginResult.Success -> {
|
||||
// Application was found and successfully validated against the Google allow list so
|
||||
// we can return the result as the final validation result.
|
||||
googleAllowListResult
|
||||
}
|
||||
|
||||
is Fido2ValidateOriginResult.Error -> {
|
||||
// Check the community allow list if the Google allow list failed, and return the
|
||||
// result as the final validation result.
|
||||
validatePrivilegedAppSignatureWithCommunityList(callingAppInfo)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun validatePrivilegedAppSignatureWithGoogleList(
|
||||
callingAppInfo: CallingAppInfo,
|
||||
): Fido2ValidateOriginResult =
|
||||
validatePrivilegedAppSignatureWithAllowList(
|
||||
callingAppInfo = callingAppInfo,
|
||||
fileName = GOOGLE_ALLOW_LIST_FILE_NAME,
|
||||
)
|
||||
|
||||
private suspend fun validatePrivilegedAppSignatureWithCommunityList(
|
||||
callingAppInfo: CallingAppInfo,
|
||||
): Fido2ValidateOriginResult =
|
||||
validatePrivilegedAppSignatureWithAllowList(
|
||||
callingAppInfo = callingAppInfo,
|
||||
fileName = COMMUNITY_ALLOW_LIST_FILE_NAME,
|
||||
)
|
||||
|
||||
private suspend fun validatePrivilegedAppSignatureWithAllowList(
|
||||
callingAppInfo: CallingAppInfo,
|
||||
fileName: String,
|
||||
): Fido2ValidateOriginResult =
|
||||
assetManager
|
||||
.readAsset(ALLOW_LIST_FILE_NAME)
|
||||
.readAsset(fileName)
|
||||
.map { allowList ->
|
||||
callingAppInfo.validatePrivilegedApp(
|
||||
allowList = allowList,
|
||||
|
|
|
@ -34,6 +34,7 @@ import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockPublicKeyAt
|
|||
import com.x8bit.bitwarden.data.vault.datasource.sdk.util.toAndroidFido2PublicKeyCredential
|
||||
import com.x8bit.bitwarden.ui.vault.feature.addedit.util.createMockPasskeyAssertionOptions
|
||||
import com.x8bit.bitwarden.ui.vault.feature.addedit.util.createMockPasskeyAttestationOptions
|
||||
import io.mockk.Ordering
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.every
|
||||
|
@ -139,11 +140,43 @@ class Fido2CredentialManagerTest {
|
|||
|
||||
coVerify(exactly = 1) {
|
||||
assetManager.readAsset(
|
||||
fileName = DEFAULT_ALLOW_LIST_FILENAME,
|
||||
fileName = GOOGLE_ALLOW_LIST_FILENAME,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `validateOrigin should validate with community allow list when google allow list validation fails`() =
|
||||
runTest {
|
||||
coEvery {
|
||||
assetManager.readAsset(GOOGLE_ALLOW_LIST_FILENAME)
|
||||
} returns MISSING_PACKAGE_ALLOW_LIST.asSuccess()
|
||||
every {
|
||||
mockPrivilegedCallingAppInfo.getOrigin(
|
||||
privilegedAllowlist = MISSING_PACKAGE_ALLOW_LIST,
|
||||
)
|
||||
} throws IllegalStateException()
|
||||
coEvery {
|
||||
assetManager.readAsset(COMMUNITY_ALLOW_LIST_FILENAME)
|
||||
} returns DEFAULT_ALLOW_LIST.asSuccess()
|
||||
every {
|
||||
mockPrivilegedCallingAppInfo.getOrigin(
|
||||
privilegedAllowlist = DEFAULT_ALLOW_LIST,
|
||||
)
|
||||
} returns DEFAULT_PACKAGE_NAME
|
||||
|
||||
fido2CredentialManager.validateOrigin(
|
||||
mockPrivilegedAppRequest.callingAppInfo,
|
||||
mockPrivilegedAppRequest.requestJson,
|
||||
)
|
||||
|
||||
coVerify(ordering = Ordering.ORDERED) {
|
||||
assetManager.readAsset(GOOGLE_ALLOW_LIST_FILENAME)
|
||||
assetManager.readAsset(COMMUNITY_ALLOW_LIST_FILENAME)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `validateOrigin should return Success when privileged app is allowed`() =
|
||||
runTest {
|
||||
|
@ -934,7 +967,7 @@ class Fido2CredentialManagerTest {
|
|||
)
|
||||
|
||||
coVerify {
|
||||
assetManager.readAsset(DEFAULT_ALLOW_LIST_FILENAME)
|
||||
assetManager.readAsset(GOOGLE_ALLOW_LIST_FILENAME)
|
||||
mockVaultSdkSource.authenticateFido2Credential(
|
||||
request = any(),
|
||||
fido2CredentialStore = any(),
|
||||
|
@ -1033,7 +1066,8 @@ private val DEFAULT_STATEMENT = DigitalAssetLinkResponseJson(
|
|||
),
|
||||
),
|
||||
)
|
||||
private const val DEFAULT_ALLOW_LIST_FILENAME = "fido2_privileged_allow_list.json"
|
||||
private const val GOOGLE_ALLOW_LIST_FILENAME = "fido2_privileged_google.json"
|
||||
private const val COMMUNITY_ALLOW_LIST_FILENAME = "fido2_privileged_community.json"
|
||||
private val DEFAULT_STATEMENT_LIST = listOf(DEFAULT_STATEMENT)
|
||||
private const val DEFAULT_ALLOW_LIST = """
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue