[PM-17870] Always include clientExtensionResults in Fido2AttestationResponse (#5964)

This commit is contained in:
aj-rosado 2025-10-01 14:58:57 +01:00 committed by GitHub
parent 116bfd6351
commit 13e6728d46
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 96 additions and 3 deletions

View File

@ -20,7 +20,7 @@ data class Fido2AttestationResponse(
@SerialName("response")
val response: RegistrationResponse,
@SerialName("clientExtensionResults")
val clientExtensionResults: ClientExtensionResults?,
val clientExtensionResults: ClientExtensionResults,
@SerialName("authenticatorAttachment")
val authenticatorAttachment: String?,
) {
@ -50,7 +50,7 @@ data class Fido2AttestationResponse(
@Serializable
data class ClientExtensionResults(
@SerialName("credProps")
val credentialProperties: CredentialProperties,
val credentialProperties: CredentialProperties? = null,
) {
/**
* Represents properties for newly created credential.

View File

@ -31,7 +31,7 @@ fun PublicKeyCredentialAuthenticatorAttestationResponse.toAndroidAttestationResp
.ClientExtensionResults
.CredentialProperties(residentKey = residentKey),
)
},
} ?: Fido2AttestationResponse.ClientExtensionResults(),
authenticatorAttachment = authenticatorAttachment,
)

View File

@ -0,0 +1,93 @@
package com.x8bit.bitwarden.data.vault.datasource.sdk.util
import android.util.Base64
import com.bitwarden.fido.AuthenticatorAttestationResponse
import com.bitwarden.fido.ClientExtensionResults
import com.bitwarden.fido.CredPropsResult
import com.bitwarden.fido.PublicKeyCredentialAuthenticatorAttestationResponse
import com.bitwarden.fido.SelectedCredential
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockCipherView
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockFido2CredentialView
import io.mockk.every
import io.mockk.mockkStatic
import io.mockk.unmockkStatic
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.Assertions.assertNotNull
import org.junit.jupiter.api.Assertions.assertNull
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
class PublicKeyCredentialAuthenticatorAttestationResponseExtensionsTest {
@BeforeEach
fun setUp() {
mockkStatic(Base64::class)
every { Base64.encodeToString(any(), any()) } returns ""
}
@AfterEach
fun tearDown() {
unmockkStatic(Base64::class)
}
@Test
fun `authenticatorAttachment should be null when SDK value is null`() {
val mockSdkResponse = createMockSdkAttestationResponse(number = 1)
val result = mockSdkResponse.toAndroidAttestationResponse()
assertNull(result.authenticatorAttachment)
}
@Test
fun `authenticatorAttachment should be populated when SDK value is non-null`() {
val mockSdkResponse = createMockSdkAttestationResponse(
number = 1,
authenticatorAttachment = "mockAuthenticatorAttachment",
)
val result = mockSdkResponse.toAndroidAttestationResponse()
assertNotNull(result.authenticatorAttachment)
}
@Test
fun `clientExtensionResults should be populated when SDK value is null`() {
val mockSdkResponse = createMockSdkAttestationResponse(number = 1)
val result = mockSdkResponse.toAndroidAttestationResponse()
assertNotNull(result.clientExtensionResults)
}
@Test
fun `residentKey should be populated when SDK value is non-null`() {
val mockSdkResponse = createMockSdkAttestationResponse(
number = 1,
credProps = CredPropsResult(
rk = true,
authenticatorDisplayName = null,
),
)
val result = mockSdkResponse.toAndroidAttestationResponse()
assert(result.clientExtensionResults.credentialProperties?.residentKey ?: false)
}
}
private fun createMockSdkAttestationResponse(
number: Int,
authenticatorAttachment: String? = null,
credProps: CredPropsResult? = null,
) = PublicKeyCredentialAuthenticatorAttestationResponse(
id = "mockId-$number",
rawId = byteArrayOf(0),
ty = "mockTy-$number",
authenticatorAttachment = authenticatorAttachment,
clientExtensionResults = ClientExtensionResults(credProps),
response = AuthenticatorAttestationResponse(
clientDataJson = byteArrayOf(0),
authenticatorData = byteArrayOf(0),
publicKey = byteArrayOf(0),
publicKeyAlgorithm = Long.MIN_VALUE,
attestationObject = byteArrayOf(0),
transports = listOf("internal"),
),
selectedCredential = SelectedCredential(
cipher = createMockCipherView(number = 1),
credential = createMockFido2CredentialView(number = 1),
),
)