Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Unverified Commit 046218ee authored by Marvin W.'s avatar Marvin W. 🐿️
Browse files

Fido: Update to latest API version features

Also handle empty strings from apps as none.

Fixes #2021, #2022
parent 7f5fbe5a
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -38,4 +38,6 @@ dependencies {
    api project(':play-services-base')
    api project(':play-services-basement')
    api project(':play-services-tasks')

    annotationProcessor project(':safe-parcel-processor')
}
+9 −2
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import com.google.android.gms.common.internal.ConnectionInfo
import com.google.android.gms.common.internal.GetServiceRequest
import com.google.android.gms.common.internal.IGmsCallbacks
import com.google.android.gms.fido.fido2.api.IBooleanCallback
import com.google.android.gms.fido.fido2.api.ICredentialListCallback
import com.google.android.gms.fido.fido2.api.common.BrowserPublicKeyCredentialCreationOptions
import com.google.android.gms.fido.fido2.api.common.BrowserPublicKeyCredentialRequestOptions
import com.google.android.gms.fido.fido2.internal.privileged.IFido2PrivilegedCallbacks
@@ -60,7 +61,7 @@ class Fido2PrivilegedService : BaseService(TAG, FIDO2_PRIVILEGED) {

class Fido2PrivilegedServiceImpl(private val context: Context, private val lifecycle: Lifecycle) :
    IFido2PrivilegedService.Stub(), LifecycleOwner {
    override fun register(callbacks: IFido2PrivilegedCallbacks, options: BrowserPublicKeyCredentialCreationOptions) {
    override fun getRegisterPendingIntent(callbacks: IFido2PrivilegedCallbacks, options: BrowserPublicKeyCredentialCreationOptions) {
        lifecycleScope.launchWhenStarted {
            val intent = Intent(context, AuthenticatorActivity::class.java)
                .putExtra(KEY_SERVICE, FIDO2_PRIVILEGED.SERVICE_ID)
@@ -74,7 +75,7 @@ class Fido2PrivilegedServiceImpl(private val context: Context, private val lifec
        }
    }

    override fun sign(callbacks: IFido2PrivilegedCallbacks, options: BrowserPublicKeyCredentialRequestOptions) {
    override fun getSignPendingIntent(callbacks: IFido2PrivilegedCallbacks, options: BrowserPublicKeyCredentialRequestOptions) {
        lifecycleScope.launchWhenStarted {
            val intent = Intent(context, AuthenticatorActivity::class.java)
                .putExtra(KEY_SERVICE, FIDO2_PRIVILEGED.SERVICE_ID)
@@ -99,6 +100,12 @@ class Fido2PrivilegedServiceImpl(private val context: Context, private val lifec
        }
    }

    override fun getCredentialList(callbacks: ICredentialListCallback, rpId: String) {
        lifecycleScope.launchWhenStarted {
            runCatching { callbacks.onCredentialList(emptyList()) }
        }
    }

    override fun getLifecycle(): Lifecycle = lifecycle

    override fun onTransact(code: Int, data: Parcel, reply: Parcel?, flags: Int): Boolean =
+10 −5
Original line number Diff line number Diff line
@@ -34,15 +34,15 @@ fun Boolean.encodeAsCbor() = CBORObject.FromObject(this)

fun PublicKeyCredentialRpEntity.encodeAsCbor() = CBORObject.NewMap().apply {
    set("id", id.encodeAsCbor())
    if (name != null) set("name", name.encodeAsCbor())
    if (icon != null) set("icon", icon.encodeAsCbor())
    if (!name.isNullOrBlank()) set("name", name.encodeAsCbor())
    if (!icon.isNullOrBlank()) set("icon", icon.encodeAsCbor())
}

fun PublicKeyCredentialUserEntity.encodeAsCbor() = CBORObject.NewMap().apply {
    set("id", id.encodeAsCbor())
    if (name != null) set("name", name.encodeAsCbor())
    if (icon != null) set("icon", icon.encodeAsCbor())
    if (displayName != null) set("displayName", displayName.encodeAsCbor())
    if (!name.isNullOrBlank()) set("name", name.encodeAsCbor())
    if (!icon.isNullOrBlank()) set("icon", icon.encodeAsCbor())
    if (!displayName.isNullOrBlank()) set("displayName", displayName.encodeAsCbor())
}

fun CBORObject.decodeAsPublicKeyCredentialUserEntity() = PublicKeyCredentialUserEntity(
@@ -57,6 +57,11 @@ fun PublicKeyCredentialParameters.encodeAsCbor() = CBORObject.NewMap().apply {
    set("type", typeAsString.encodeAsCbor())
}

fun CBORObject.decodeAsPublicKeyCredentialParameters() = PublicKeyCredentialParameters(
    get("type").AsString(),
    get("alg").AsInt32Value()
)

fun PublicKeyCredentialDescriptor.encodeAsCbor() = CBORObject.NewMap().apply {
    set("type", typeAsString.encodeAsCbor())
    set("id", id.encodeAsCbor())
+57 −3
Original line number Diff line number Diff line
@@ -5,9 +5,11 @@

package org.microg.gms.fido.core.protocol.msgs

import com.google.android.gms.fido.fido2.api.common.PublicKeyCredentialParameters
import com.upokecenter.cbor.CBORObject
import org.microg.gms.fido.core.protocol.AsInt32Sequence
import org.microg.gms.fido.core.protocol.AsStringSequence
import org.microg.gms.fido.core.protocol.decodeAsPublicKeyCredentialParameters
import org.microg.gms.utils.ToStringHelper

class AuthenticatorGetInfoCommand : Ctap2Command<AuthenticatorGetInfoRequest, AuthenticatorGetInfoResponse>(AuthenticatorGetInfoRequest()) {
@@ -22,7 +24,22 @@ class AuthenticatorGetInfoResponse(
    val aaguid: ByteArray,
    val options: Options,
    val maxMsgSize: Int?,
    val pinProtocols: List<Int>
    val pinUvAuthProtocols: List<Int>,
    val maxCredentialCountInList: Int?,
    val maxCredentialIdLength: Int?,
    val transports: List<String>?,
    val algorithms: List<PublicKeyCredentialParameters>?,
    val maxSerializedLargeBlobArray: Int?,
    val forcePINChange: Boolean,
    val minPINLength: Int?,
    val firmwareVersion: Int?,
    val maxCredBlobLength: Int?,
    val maxRPIDsForSetMinPINLength: Int?,
    val preferredPlatformUvAttempts: Int?,
    val uvModality: Int?,
    val certifications: Map<String, Int>?,
    val remainingDiscoverableCredentials: Int?,
    val vendorPrototypeConfigCommands: List<Int>?,
) : Ctap2Response {

    companion object {
@@ -103,11 +120,48 @@ class AuthenticatorGetInfoResponse(
                ?: throw IllegalArgumentException("Not a valid response for authenticatorGetInfo"),
            options = Options.decodeFromCbor(obj.get(4)),
            maxMsgSize = obj.get(5)?.AsInt32Value(),
            pinProtocols = obj.get(6)?.AsInt32Sequence()?.toList().orEmpty()
            pinUvAuthProtocols = obj.get(6)?.AsInt32Sequence()?.toList().orEmpty(),
            maxCredentialCountInList = obj.get(7)?.AsInt32Value(),
            maxCredentialIdLength = obj.get(8)?.AsInt32Value(),
            transports = obj.get(9)?.AsStringSequence()?.toList(),
            algorithms = runCatching { obj.get(10)?.values?.map { it.decodeAsPublicKeyCredentialParameters() } }.getOrNull(),
            maxSerializedLargeBlobArray = obj.get(11)?.AsInt32Value(),
            forcePINChange = obj.get(12)?.AsBoolean() == true,
            minPINLength = obj.get(13)?.AsInt32Value(),
            firmwareVersion = obj.get(14)?.AsInt32Value(),
            maxCredBlobLength = obj.get(15)?.AsInt32Value(),
            maxRPIDsForSetMinPINLength = obj.get(16)?.AsInt32Value(),
            preferredPlatformUvAttempts = obj.get(17)?.AsInt32Value(),
            uvModality = obj.get(18)?.AsInt32Value(),
            certifications = obj.get(19)?.entries?.mapNotNull { runCatching { it.key.AsString() to it.value.AsInt32Value() }.getOrNull() }?.toMap(),
            remainingDiscoverableCredentials = obj.get(20)?.AsInt32Value(),
            vendorPrototypeConfigCommands = obj.get(20)?.AsInt32Sequence()?.toList(),
        )
    }

    override fun toString(): String {
        return "AuthenticatorGetInfoResponse(versions=$versions, extensions=$extensions, aaguid=${aaguid.contentToString()}, options=$options, maxMsgSize=$maxMsgSize, pinProtocols=$pinProtocols)"
        return ToStringHelper.name("AuthenticatorGetInfoResponse")
            .field("versions", versions)
            .field("extensions", extensions)
            .field("aaguid", aaguid)
            .field("options", options)
            .field("maxMsgSize", maxMsgSize)
            .field("pinUvAuthProtocols", pinUvAuthProtocols)
            .field("maxCredentialCountInList", maxCredentialCountInList)
            .field("maxCredentialIdLength", maxCredentialIdLength)
            .field("transports", transports)
            .field("algorithms", algorithms)
            .field("maxSerializedLargeBlobArray", maxSerializedLargeBlobArray)
            .field("forcePINChange", forcePINChange)
            .field("minPINLength", minPINLength)
            .field("firmwareVersion", firmwareVersion)
            .field("maxCredBlobLength", maxCredBlobLength)
            .field("maxRPIDsForSetMinPINLength", maxRPIDsForSetMinPINLength)
            .field("preferredPlatformUvAttempts", preferredPlatformUvAttempts)
            .field("uvModality", uvModality)
            .field("certifications", certifications)
            .field("remainingDiscoverableCredentials", remainingDiscoverableCredentials)
            .field("vendorPrototypeConfigCommands", vendorPrototypeConfigCommands)
            .end()
    }
}
+7 −0
Original line number Diff line number Diff line
@@ -15,9 +15,12 @@ const val CAPABILITY_CTAP_2_1 = 1 shl 2
const val CAPABILITY_CLIENT_PIN = 1 shl 3
const val CAPABILITY_WINK = 1 shl 4
const val CAPABILITY_MAKE_CRED_WITHOUT_UV = 1 shl 5
const val CAPABILITY_USER_VERIFICATION = 1 shl 6
const val CAPABILITY_RESIDENT_KEY = 1 shl 7

interface CtapConnection {
    val capabilities: Int
    val transports: List<String>

    val hasCtap1Support: Boolean
        get() = capabilities and CAPABILITY_CTAP_1 > 0
@@ -31,6 +34,10 @@ interface CtapConnection {
        get() = capabilities and CAPABILITY_WINK > 0
    val canMakeCredentialWithoutUserVerification: Boolean
        get() = capabilities and CAPABILITY_MAKE_CRED_WITHOUT_UV > 0
    val hasUserVerificationSupport: Boolean
        get() = capabilities and CAPABILITY_USER_VERIFICATION > 0
    val hasResidentKey: Boolean
        get() = capabilities and CAPABILITY_RESIDENT_KEY > 0

    suspend fun <Q : Ctap1Request, S : Ctap1Response> runCommand(command: Ctap1Command<Q, S>): S
    suspend fun <Q : Ctap2Request, S : Ctap2Response> runCommand(command: Ctap2Command<Q, S>): S
Loading