Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -323,6 +323,7 @@ class BitwardenCredentialManagerImpl(
createPublicKeyCredentialRequest = createPublicKeyCredentialRequest,
selectedCipherView = selectedCipherView,
clientData = clientData,
callingPackageName = callingAppInfo.packageName,
)
}

Expand All @@ -347,6 +348,7 @@ class BitwardenCredentialManagerImpl(
createPublicKeyCredentialRequest = createPublicKeyCredentialRequest,
selectedCipherView = selectedCipherView,
clientData = clientData,
callingPackageName = callingAppInfo.packageName,
)
}

Expand All @@ -356,6 +358,7 @@ class BitwardenCredentialManagerImpl(
createPublicKeyCredentialRequest: CreatePublicKeyCredentialRequest,
selectedCipherView: CipherView,
clientData: ClientData,
callingPackageName: String,
): Fido2RegisterCredentialResult = vaultSdkSource
.registerFido2Credential(
request = RegisterFido2CredentialRequest(
Expand All @@ -370,7 +373,9 @@ class BitwardenCredentialManagerImpl(
),
fido2CredentialStore = this,
)
.map { it.toAndroidAttestationResponse() }
.map {
it.toAndroidAttestationResponse(callingPackageName = callingPackageName)
}
.mapCatching { json.encodeToString(it) }
.fold(
onSuccess = { Fido2RegisterCredentialResult.Success(it) },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,33 @@ import android.util.Base64
import com.bitwarden.fido.PublicKeyCredentialAuthenticatorAttestationResponse
import com.x8bit.bitwarden.data.credentials.model.Fido2AttestationResponse

private const val BINANCE_PACKAGE_NAME = "com.binance.dev"

/**
* Converts the SDK attestation response to a [Fido2AttestationResponse] that can be serialized into
* the expected system JSON.
*/
@Suppress("MaxLineLength")
fun PublicKeyCredentialAuthenticatorAttestationResponse.toAndroidAttestationResponse(): Fido2AttestationResponse =
Fido2AttestationResponse(
fun PublicKeyCredentialAuthenticatorAttestationResponse.toAndroidAttestationResponse(
callingPackageName: String?,
): Fido2AttestationResponse {
val registrationResponse = Fido2AttestationResponse.RegistrationResponse(
clientDataJson = response.clientDataJson.base64EncodeForFido2Response(),
attestationObject = response.attestationObject.base64EncodeForFido2Response(),
transports = response.transports.takeUnless {
// Setting transports as null, otherwise Binance labels the passkey broken
// PM-26734 remove this flow if not necessary anymore
callingPackageName == BINANCE_PACKAGE_NAME
},
publicKeyAlgorithm = response.publicKeyAlgorithm,
publicKey = response.publicKey?.base64EncodeForFido2Response(),
authenticatorData = response.authenticatorData.base64EncodeForFido2Response(),
)

return Fido2AttestationResponse(
id = id,
type = ty,
rawId = rawId.base64EncodeForFido2Response(),
response = Fido2AttestationResponse.RegistrationResponse(
clientDataJson = response.clientDataJson.base64EncodeForFido2Response(),
attestationObject = response.attestationObject.base64EncodeForFido2Response(),
transports = response.transports,
publicKeyAlgorithm = response.publicKeyAlgorithm,
publicKey = response.publicKey?.base64EncodeForFido2Response(),
authenticatorData = response.authenticatorData.base64EncodeForFido2Response(),
),
response = registrationResponse,
clientExtensionResults = clientExtensionResults
.credProps
?.rk
Expand All @@ -34,6 +43,7 @@ fun PublicKeyCredentialAuthenticatorAttestationResponse.toAndroidAttestationResp
} ?: Fido2AttestationResponse.ClientExtensionResults(),
authenticatorAttachment = authenticatorAttachment,
)
}

/**
* Attestation response fields of type [ByteArray] must be base 64 encoded in a url safe format
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class PublicKeyCredentialAuthenticatorAttestationResponseExtensionsTest {
@Test
fun `authenticatorAttachment should be null when SDK value is null`() {
val mockSdkResponse = createMockSdkAttestationResponse(number = 1)
val result = mockSdkResponse.toAndroidAttestationResponse()
val result = mockSdkResponse.toAndroidAttestationResponse(callingPackageName = "")
assertNull(result.authenticatorAttachment)
}

Expand All @@ -43,14 +43,14 @@ class PublicKeyCredentialAuthenticatorAttestationResponseExtensionsTest {
number = 1,
authenticatorAttachment = "mockAuthenticatorAttachment",
)
val result = mockSdkResponse.toAndroidAttestationResponse()
val result = mockSdkResponse.toAndroidAttestationResponse(callingPackageName = "")
assertNotNull(result.authenticatorAttachment)
}

@Test
fun `clientExtensionResults should be populated when SDK value is null`() {
val mockSdkResponse = createMockSdkAttestationResponse(number = 1)
val result = mockSdkResponse.toAndroidAttestationResponse()
val result = mockSdkResponse.toAndroidAttestationResponse(callingPackageName = "")
assertNotNull(result.clientExtensionResults)
}

Expand All @@ -63,9 +63,41 @@ class PublicKeyCredentialAuthenticatorAttestationResponseExtensionsTest {
authenticatorDisplayName = null,
),
)
val result = mockSdkResponse.toAndroidAttestationResponse()
val result = mockSdkResponse.toAndroidAttestationResponse(callingPackageName = "")
assert(result.clientExtensionResults.credentialProperties?.residentKey ?: false)
}

@Suppress("MaxLineLength")
@Test
fun `toAndroidAttestationResponse should build specific response when package name is Binance`() {
val binancePackageName = "com.binance.dev"
val mockSdkResponse = createMockSdkAttestationResponse(number = 1)

val result = mockSdkResponse.toAndroidAttestationResponse(callingPackageName = binancePackageName)

assertNull(result.response.transports)
assertNotNull(result.response.publicKey)
assertNotNull(result.response.publicKeyAlgorithm)
assertNotNull(result.response.authenticatorData)
assertNotNull(result.response.clientDataJson)
assertNotNull(result.response.attestationObject)
}

@Suppress("MaxLineLength")
@Test
fun `toAndroidAttestationResponse should build full response for any package name other than Binance`() {
val otherPackageName = "com.any.app"
val mockSdkResponse = createMockSdkAttestationResponse(number = 1)

val result = mockSdkResponse.toAndroidAttestationResponse(callingPackageName = otherPackageName)

assertNotNull(result.response.transports)
assertNotNull(result.response.publicKey)
assertNotNull(result.response.publicKeyAlgorithm)
assertNotNull(result.response.authenticatorData)
assertNotNull(result.response.clientDataJson)
assertNotNull(result.response.attestationObject)
}
}

private fun createMockSdkAttestationResponse(
Expand Down
Loading