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 @@ -43,6 +43,8 @@ public class BeginSignInRequest extends AbstractSafeParcelable {
private final PasskeysRequestOptions passkeysRequestOptions;
@Field(value = 7, getterName = "getPasskeyJsonRequestOptions")
private final PasskeyJsonRequestOptions passkeyJsonRequestOptions;
@Field(value = 8, getterName = "isPreferImmediatelyAvailableCredentials")
private final boolean preferImmediatelyAvailableCredentials;

@NonNull
@Override
Expand All @@ -55,18 +57,20 @@ public String toString() {
.field("theme", theme)
.field("PasskeysRequestOptions", passkeysRequestOptions)
.field("PasskeyJsonRequestOptions", passkeyJsonRequestOptions)
.field("preferImmediatelyAvailableCredentials", preferImmediatelyAvailableCredentials)
.end();
}

@Constructor
BeginSignInRequest(@Param(1) PasswordRequestOptions passwordRequestOptions, @Param(2) GoogleIdTokenRequestOptions googleIdTokenRequestOptions, @Param(3) String sessionId, @Param(4) boolean autoSelectEnabled, @Param(5) int theme, @Param(6) PasskeysRequestOptions passkeysRequestOptions, @Param(7) PasskeyJsonRequestOptions passkeyJsonRequestOptions) {
BeginSignInRequest(@Param(1) PasswordRequestOptions passwordRequestOptions, @Param(2) GoogleIdTokenRequestOptions googleIdTokenRequestOptions, @Param(3) String sessionId, @Param(4) boolean autoSelectEnabled, @Param(5) int theme, @Param(6) PasskeysRequestOptions passkeysRequestOptions, @Param(7) PasskeyJsonRequestOptions passkeyJsonRequestOptions, @Param(8) boolean preferImmediatelyAvailableCredentials) {
this.passwordRequestOptions = passwordRequestOptions;
this.googleIdTokenRequestOptions = googleIdTokenRequestOptions;
this.sessionId = sessionId;
this.autoSelectEnabled = autoSelectEnabled;
this.theme = theme;
this.passkeysRequestOptions = passkeysRequestOptions;
this.passkeyJsonRequestOptions = passkeyJsonRequestOptions;
this.preferImmediatelyAvailableCredentials = preferImmediatelyAvailableCredentials;
}

@NonNull
Expand Down Expand Up @@ -107,6 +111,10 @@ public boolean isAutoSelectEnabled() {
return autoSelectEnabled;
}

public boolean isPreferImmediatelyAvailableCredentials() {
return preferImmediatelyAvailableCredentials;
}

public static class Builder {

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import org.microg.gms.auth.signin.CLIENT_PACKAGE_NAME
import org.microg.gms.auth.signin.GOOGLE_SIGN_IN_OPTIONS
import org.microg.gms.auth.signin.performSignOut
import org.microg.gms.common.GmsService
import org.microg.gms.fido.core.Database
import org.microg.gms.fido.core.ui.AuthenticatorActivity.Companion.KEY_OPTIONS
import org.microg.gms.fido.core.ui.AuthenticatorActivity.Companion.KEY_SERVICE
import org.microg.gms.fido.core.ui.AuthenticatorActivity.Companion.KEY_SOURCE
Expand Down Expand Up @@ -94,6 +95,7 @@ class IdentitySignInServiceImpl(private val context: Context, private val client
fun <T> JSONArray.map(fn: (JSONObject) -> T): List<T> = (0 until length()).map { fn(getJSONObject(it)) }
fun <T> JSONArray.map(fn: (String) -> T): List<T> = (0 until length()).map { fn(getString(it)) }
val json = JSONObject(request.passkeyJsonRequestOptions.requestJson)
val rpId = json.getString("rpId")
val options = PublicKeyCredentialRequestOptions.Builder()
.setAllowList(json.getArrayOrNull("allowCredentials")?.let { allowCredentials -> allowCredentials.map { credential: JSONObject ->
PublicKeyCredentialDescriptor(
Expand All @@ -106,9 +108,14 @@ class IdentitySignInServiceImpl(private val context: Context, private val client
} })
.setChallenge(Base64.decode(json.getString("challenge"), Base64.URL_SAFE))
.setRequireUserVerification(json.optString("userVerification").takeIf { it.isNotBlank() }?.let { UserVerificationRequirement.fromString(it) })
.setRpId(json.getString("rpId"))
.setRpId(rpId)
.setTimeoutSeconds(json.optDouble("timeout", -1.0).takeIf { it > 0 })
.build()
if (request.isPreferImmediatelyAvailableCredentials && Database(context).getKnownRegistrationInfo(rpId).isEmpty()) {
Log.d(TAG, "need available Credential")
callback.onResult(Status.CANCELED, null)
return
}
val bundle = bundleOf(
KEY_SERVICE to GmsService.IDENTITY_SIGN_IN.SERVICE_ID,
KEY_SOURCE to "app",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,14 +94,14 @@ class KeyRetrievalServiceImpl(val context: Context) : IKeyRetrievalService.Stub(
callback: ISharedKeyCallback?, accountName: String?, metadata: ApiMetadata?
) {
Log.d(TAG, "Not implemented getKeyMaterial accountName:$accountName metadata:$metadata")
callback?.onResult(Status.SUCCESS, emptyArray<SharedKey>())
callback?.onResult(Status.INTERNAL_ERROR, emptyArray<SharedKey>())
}

override fun setKeyMaterial(
callback: IKeyRetrievalCallback?, accountName: String?, keys: Array<out SharedKey?>?, metadata: ApiMetadata?
) {
Log.d(TAG, "Not implemented setKeyMaterial accountName:$accountName keys:$keys metadata:$metadata")
callback?.onResult(Status.SUCCESS)
callback?.onResult(Status.INTERNAL_ERROR)
}

override fun getRecoveredSecurityDomains(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ package org.microg.gms.fido.core
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 android.util.Log
import androidx.core.database.getLongOrNull
import androidx.core.database.getStringOrNull
import org.microg.gms.fido.core.transport.Transport
import org.microg.gms.fido.core.ui.TAG

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

Expand All @@ -31,6 +33,23 @@ class Database(context: Context) : SQLiteOpenHelper(context, "fido.db", null, VE
}
}

fun getKnownRegistrationInfo(rpId: String) = readableDatabase.use {
val cursor = it.query(
TABLE_KNOWN_REGISTRATIONS, arrayOf(COLUMN_CREDENTIAL_ID, COLUMN_REGISTER_USER, COLUMN_TRANSPORT), "$COLUMN_RP_ID=?", arrayOf(rpId), null, null, null
)
val result = mutableListOf<CredentialUserInfo>()
cursor.use { c ->
while (c.moveToNext()) {
val credentialId = c.getString(0)
val userJson = c.getStringOrNull(1) ?: continue
val transport = c.getStringOrNull(2) ?: continue
Log.d(TAG, "getKnownRegistrationInfo: credential: $credentialId user: $userJson transport: $transport")
result.add(CredentialUserInfo(credentialId, userJson, Transport.valueOf(transport)))
}
}
result
}

fun insertPrivileged(packageName: String, signatureDigest: String) = writableDatabase.use {
it.insertWithOnConflict(TABLE_PRIVILEGED_APPS, null, ContentValues().apply {
put(COLUMN_PACKAGE_NAME, packageName)
Expand All @@ -39,13 +58,33 @@ class Database(context: Context) : SQLiteOpenHelper(context, "fido.db", null, VE
}, 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)
fun insertKnownRegistration(rpId: String, credentialId: String, transport: Transport, userJson: String? = null) = writableDatabase.use {
Log.d(TAG, "insertKnownRegistration: $rpId $credentialId $transport $userJson")
val values = ContentValues().apply {
put(COLUMN_CREDENTIAL_ID, credentialId)
put(COLUMN_TRANSPORT, transport.name)
put(COLUMN_TIMESTAMP, System.currentTimeMillis())
}, CONFLICT_REPLACE)
if (userJson != null) {
put(COLUMN_REGISTER_USER, userJson)
}
}

val updated = if (userJson == null) {
it.update(TABLE_KNOWN_REGISTRATIONS, values, "$COLUMN_RP_ID = ? AND $COLUMN_CREDENTIAL_ID = ?", arrayOf(rpId, credentialId))
} else {
it.update(TABLE_KNOWN_REGISTRATIONS, values, "$COLUMN_RP_ID = ? AND $COLUMN_REGISTER_USER = ?", arrayOf(rpId, userJson))
}

if (updated == 0) {
val insertValues = ContentValues().apply {
put(COLUMN_RP_ID, rpId)
put(COLUMN_CREDENTIAL_ID, credentialId)
put(COLUMN_TRANSPORT, transport.name)
put(COLUMN_TIMESTAMP, System.currentTimeMillis())
userJson?.let { json -> put(COLUMN_REGISTER_USER, json) }
}
it.insert(TABLE_KNOWN_REGISTRATIONS, null, insertValues)
}
}

override fun onCreate(db: SQLiteDatabase) {
Expand All @@ -59,10 +98,13 @@ class Database(context: Context) : SQLiteOpenHelper(context, "fido.db", null, VE
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)")
}
if (oldVersion < 3) {
db.execSQL("ALTER TABLE $TABLE_KNOWN_REGISTRATIONS ADD COLUMN $COLUMN_REGISTER_USER TEXT")
}
}

companion object {
const val VERSION = 2
const val VERSION = 3
private const val TABLE_PRIVILEGED_APPS = "privileged_apps"
private const val TABLE_KNOWN_REGISTRATIONS = "known_registrations"
private const val COLUMN_PACKAGE_NAME = "package_name"
Expand All @@ -71,6 +113,7 @@ class Database(context: Context) : SQLiteOpenHelper(context, "fido.db", null, VE
private const val COLUMN_RP_ID = "rp_id"
private const val COLUMN_CREDENTIAL_ID = "credential_id"
private const val COLUMN_TRANSPORT = "transport"
private const val COLUMN_REGISTER_USER = "register_user"
}
}

Expand Down
Loading