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

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

Fido: Add support for CTAP 1 HID devices

parent 4ea05759
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -10,7 +10,8 @@

    <uses-permission android:name="android.permission.USE_BIOMETRIC" />
    <uses-permission android:name="android.permission.USE_FINGERPRINT" />
    <uses-permission android:name="android.permission.MANAGE_USB"
    <uses-permission
        android:name="android.permission.MANAGE_USB"
        tools:ignore="ProtectedPermissions" />

    <application>
@@ -24,6 +25,7 @@
        </service>
        <activity
            android:name=".ui.AuthenticatorActivity"
            android:configChanges="orientation|keyboard|keyboardHidden|screenSize"
            android:exported="false"
            android:process=":ui"
            android:theme="@style/Theme.Fido.Translucent" />
+28 −2
Original line number Diff line number Diff line
@@ -9,8 +9,10 @@ import android.content.ContentValues
import android.content.Context
import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteDatabase.CONFLICT_IGNORE
import android.database.sqlite.SQLiteDatabase.CONFLICT_REPLACE
import android.database.sqlite.SQLiteOpenHelper
import androidx.core.database.getLongOrNull
import org.microg.gms.fido.core.transport.Transport

class Database(context: Context) : SQLiteOpenHelper(context, "fido.db", null, VERSION) {

@@ -18,12 +20,29 @@ class Database(context: Context) : SQLiteOpenHelper(context, "fido.db", null, VE
        it.count(TABLE_PRIVILEGED_APPS, "$COLUMN_PACKAGE_NAME = ? AND $COLUMN_SIGNATURE_DIGEST = ?", packageName, signatureDigest) > 0
    }

    fun wasUsed(): Boolean = readableDatabase.use { it.count(TABLE_KNOWN_REGISTRATIONS) > 0 }

    fun getKnownRegistrationTransport(rpId: String, credentialId: String) = readableDatabase.use {
        it.query(TABLE_KNOWN_REGISTRATIONS, arrayOf(COLUMN_TRANSPORT), "$COLUMN_RP_ID = ? AND $COLUMN_CREDENTIAL_ID = ?", arrayOf(rpId, credentialId), null, null, null).use {
            if (it.moveToFirst()) Transport.valueOf(it.getString(0)) else null
        }
    }

    fun insertPrivileged(packageName: String, signatureDigest: String) = writableDatabase.use {
        it.insertWithOnConflict(TABLE_PRIVILEGED_APPS, null, ContentValues().apply {
            put(COLUMN_PACKAGE_NAME, packageName)
            put(COLUMN_SIGNATURE_DIGEST, signatureDigest)
            put(COLUMN_TIMESTAMP, System.currentTimeMillis())
        }, CONFLICT_IGNORE)
        }, CONFLICT_REPLACE)
    }

    fun insertKnownRegistration(rpId: String, credentialId: String, transport: Transport) = writableDatabase.use {
        it.insertWithOnConflict(TABLE_KNOWN_REGISTRATIONS, null, ContentValues().apply {
            put(COLUMN_RP_ID, rpId)
            put(COLUMN_CREDENTIAL_ID, credentialId)
            put(COLUMN_TRANSPORT, transport.name)
            put(COLUMN_TIMESTAMP, System.currentTimeMillis())
        }, CONFLICT_REPLACE)
    }

    override fun onCreate(db: SQLiteDatabase) {
@@ -34,14 +53,21 @@ class Database(context: Context) : SQLiteOpenHelper(context, "fido.db", null, VE
        if (oldVersion < 1) {
            db.execSQL("CREATE TABLE $TABLE_PRIVILEGED_APPS($COLUMN_PACKAGE_NAME TEXT, $COLUMN_SIGNATURE_DIGEST TEXT, $COLUMN_TIMESTAMP INT, UNIQUE($COLUMN_PACKAGE_NAME, $COLUMN_SIGNATURE_DIGEST) ON CONFLICT REPLACE);")
        }
        if (oldVersion < 2) {
            db.execSQL("CREATE TABLE $TABLE_KNOWN_REGISTRATIONS($COLUMN_RP_ID TEXT, $COLUMN_CREDENTIAL_ID TEXT, $COLUMN_TRANSPORT TEXT, $COLUMN_TIMESTAMP INT, UNIQUE($COLUMN_RP_ID, $COLUMN_CREDENTIAL_ID) ON CONFLICT REPLACE)")
        }
    }

    companion object {
        const val VERSION = 1
        const val VERSION = 2
        private const val TABLE_PRIVILEGED_APPS = "privileged_apps"
        private const val TABLE_KNOWN_REGISTRATIONS = "known_registrations"
        private const val COLUMN_PACKAGE_NAME = "package_name"
        private const val COLUMN_SIGNATURE_DIGEST = "signature_digest"
        private const val COLUMN_TIMESTAMP = "timestamp"
        private const val COLUMN_RP_ID = "rp_id"
        private const val COLUMN_CREDENTIAL_ID = "credential_id"
        private const val COLUMN_TRANSPORT = "transport"
    }
}

+1 −1
Original line number Diff line number Diff line
@@ -14,6 +14,6 @@ abstract class AttestationObject(val authData: AuthenticatorData) {
    fun encode(): ByteArray = CBORObject.NewMap().apply {
        set("fmt", fmt.encodeAsCbor())
        set("attStmt", attStmt)
        set("authData", authData.toCBOR())
        set("authData", authData.encodeAsCbor())
    }.EncodeToBytes()
}
+5 −5
Original line number Diff line number Diff line
@@ -30,20 +30,20 @@ class AuthenticatorData(
            .array()
    }

    fun toCBOR(): CBORObject = encode().encodeAsCbor()
    fun encodeAsCbor(): CBORObject = encode().encodeAsCbor()

    companion object {
        /** User Present **/
        private const val FLAG_UP: Byte = 1
        private const val FLAG_UP: Byte = 0x01

        /** User Verified **/
        private const val FLAG_UV: Byte = 4
        private const val FLAG_UV: Byte = 0x04

        /** Attested credential data included **/
        private const val FLAG_AT: Byte = 64
        private const val FLAG_AT: Byte = 0x40

        /** Extension data included **/
        private const val FLAG_ED: Byte = -128
        private const val FLAG_ED: Byte = -0x80

        private fun buildFlags(up: Boolean, uv: Boolean, at: Boolean, ed: Boolean): Byte =
            (if (up) FLAG_UP else 0) or (if (uv) FLAG_UV else 0) or (if (at) FLAG_AT else 0) or (if (ed) FLAG_ED else 0)
+1 −1
Original line number Diff line number Diff line
@@ -69,7 +69,7 @@ fun CBORObject.decodeAsPublicKeyCredentialDescriptor() = PublicKeyCredentialDesc
    get("transports")?.AsStringSequence()?.map { Transport.fromString(it) }
)

fun<T> List<T>.encodeAsCbor(f: (T) -> CBORObject) = CBORObject.NewArray().apply { this@encodeAsCbor.forEachIndexed { index, t -> set(index, f(t)) } }
fun<T> List<T>.encodeAsCbor(f: (T) -> CBORObject) = CBORObject.NewArray().apply { this@encodeAsCbor.forEach { Add(f(it)) } }
fun<T> Map<String,T>.encodeAsCbor(f: (T) -> CBORObject) = CBORObject.NewMap().apply {
    for (entry in this@encodeAsCbor) {
        set(entry.key, f(entry.value))
Loading