Skip to content

Commit f0e2434

Browse files
Fido: Add credential selection for discoverable keys (#3031)
Co-authored-by: Marvin W <[email protected]>
1 parent a1efcd7 commit f0e2434

37 files changed

+1367
-105
lines changed

play-services-auth/src/main/java/com/google/android/gms/auth/api/identity/BeginSignInRequest.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ public class BeginSignInRequest extends AbstractSafeParcelable {
4343
private final PasskeysRequestOptions passkeysRequestOptions;
4444
@Field(value = 7, getterName = "getPasskeyJsonRequestOptions")
4545
private final PasskeyJsonRequestOptions passkeyJsonRequestOptions;
46+
@Field(value = 8, getterName = "isPreferImmediatelyAvailableCredentials")
47+
private final boolean preferImmediatelyAvailableCredentials;
4648

4749
@NonNull
4850
@Override
@@ -55,18 +57,20 @@ public String toString() {
5557
.field("theme", theme)
5658
.field("PasskeysRequestOptions", passkeysRequestOptions)
5759
.field("PasskeyJsonRequestOptions", passkeyJsonRequestOptions)
60+
.field("preferImmediatelyAvailableCredentials", preferImmediatelyAvailableCredentials)
5861
.end();
5962
}
6063

6164
@Constructor
62-
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) {
65+
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) {
6366
this.passwordRequestOptions = passwordRequestOptions;
6467
this.googleIdTokenRequestOptions = googleIdTokenRequestOptions;
6568
this.sessionId = sessionId;
6669
this.autoSelectEnabled = autoSelectEnabled;
6770
this.theme = theme;
6871
this.passkeysRequestOptions = passkeysRequestOptions;
6972
this.passkeyJsonRequestOptions = passkeyJsonRequestOptions;
73+
this.preferImmediatelyAvailableCredentials = preferImmediatelyAvailableCredentials;
7074
}
7175

7276
@NonNull
@@ -107,6 +111,10 @@ public boolean isAutoSelectEnabled() {
107111
return autoSelectEnabled;
108112
}
109113

114+
public boolean isPreferImmediatelyAvailableCredentials() {
115+
return preferImmediatelyAvailableCredentials;
116+
}
117+
110118
public static class Builder {
111119

112120
}

play-services-core/src/main/kotlin/org/microg/gms/auth/credentials/identity/IdentitySignInService.kt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ import org.microg.gms.auth.signin.CLIENT_PACKAGE_NAME
4646
import org.microg.gms.auth.signin.GOOGLE_SIGN_IN_OPTIONS
4747
import org.microg.gms.auth.signin.performSignOut
4848
import org.microg.gms.common.GmsService
49+
import org.microg.gms.fido.core.Database
4950
import org.microg.gms.fido.core.ui.AuthenticatorActivity.Companion.KEY_OPTIONS
5051
import org.microg.gms.fido.core.ui.AuthenticatorActivity.Companion.KEY_SERVICE
5152
import org.microg.gms.fido.core.ui.AuthenticatorActivity.Companion.KEY_SOURCE
@@ -94,6 +95,7 @@ class IdentitySignInServiceImpl(private val context: Context, private val client
9495
fun <T> JSONArray.map(fn: (JSONObject) -> T): List<T> = (0 until length()).map { fn(getJSONObject(it)) }
9596
fun <T> JSONArray.map(fn: (String) -> T): List<T> = (0 until length()).map { fn(getString(it)) }
9697
val json = JSONObject(request.passkeyJsonRequestOptions.requestJson)
98+
val rpId = json.getString("rpId")
9799
val options = PublicKeyCredentialRequestOptions.Builder()
98100
.setAllowList(json.getArrayOrNull("allowCredentials")?.let { allowCredentials -> allowCredentials.map { credential: JSONObject ->
99101
PublicKeyCredentialDescriptor(
@@ -106,9 +108,14 @@ class IdentitySignInServiceImpl(private val context: Context, private val client
106108
} })
107109
.setChallenge(Base64.decode(json.getString("challenge"), Base64.URL_SAFE))
108110
.setRequireUserVerification(json.optString("userVerification").takeIf { it.isNotBlank() }?.let { UserVerificationRequirement.fromString(it) })
109-
.setRpId(json.getString("rpId"))
111+
.setRpId(rpId)
110112
.setTimeoutSeconds(json.optDouble("timeout", -1.0).takeIf { it > 0 })
111113
.build()
114+
if (request.isPreferImmediatelyAvailableCredentials && Database(context).getKnownRegistrationInfo(rpId).isEmpty()) {
115+
Log.d(TAG, "need available Credential")
116+
callback.onResult(Status.CANCELED, null)
117+
return
118+
}
112119
val bundle = bundleOf(
113120
KEY_SERVICE to GmsService.IDENTITY_SIGN_IN.SERVICE_ID,
114121
KEY_SOURCE to "app",

play-services-core/src/main/kotlin/org/microg/gms/auth/folsom/KeyRetrievalService.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,14 +94,14 @@ class KeyRetrievalServiceImpl(val context: Context) : IKeyRetrievalService.Stub(
9494
callback: ISharedKeyCallback?, accountName: String?, metadata: ApiMetadata?
9595
) {
9696
Log.d(TAG, "Not implemented getKeyMaterial accountName:$accountName metadata:$metadata")
97-
callback?.onResult(Status.SUCCESS, emptyArray<SharedKey>())
97+
callback?.onResult(Status.INTERNAL_ERROR, emptyArray<SharedKey>())
9898
}
9999

100100
override fun setKeyMaterial(
101101
callback: IKeyRetrievalCallback?, accountName: String?, keys: Array<out SharedKey?>?, metadata: ApiMetadata?
102102
) {
103103
Log.d(TAG, "Not implemented setKeyMaterial accountName:$accountName keys:$keys metadata:$metadata")
104-
callback?.onResult(Status.SUCCESS)
104+
callback?.onResult(Status.INTERNAL_ERROR)
105105
}
106106

107107
override fun getRecoveredSecurityDomains(

play-services-fido/core/src/main/kotlin/org/microg/gms/fido/core/Database.kt

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,13 @@ package org.microg.gms.fido.core
88
import android.content.ContentValues
99
import android.content.Context
1010
import android.database.sqlite.SQLiteDatabase
11-
import android.database.sqlite.SQLiteDatabase.CONFLICT_IGNORE
1211
import android.database.sqlite.SQLiteDatabase.CONFLICT_REPLACE
1312
import android.database.sqlite.SQLiteOpenHelper
13+
import android.util.Log
1414
import androidx.core.database.getLongOrNull
15+
import androidx.core.database.getStringOrNull
1516
import org.microg.gms.fido.core.transport.Transport
17+
import org.microg.gms.fido.core.ui.TAG
1618

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

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

36+
fun getKnownRegistrationInfo(rpId: String) = readableDatabase.use {
37+
val cursor = it.query(
38+
TABLE_KNOWN_REGISTRATIONS, arrayOf(COLUMN_CREDENTIAL_ID, COLUMN_REGISTER_USER, COLUMN_TRANSPORT), "$COLUMN_RP_ID=?", arrayOf(rpId), null, null, null
39+
)
40+
val result = mutableListOf<CredentialUserInfo>()
41+
cursor.use { c ->
42+
while (c.moveToNext()) {
43+
val credentialId = c.getString(0)
44+
val userJson = c.getStringOrNull(1) ?: continue
45+
val transport = c.getStringOrNull(2) ?: continue
46+
Log.d(TAG, "getKnownRegistrationInfo: credential: $credentialId user: $userJson transport: $transport")
47+
result.add(CredentialUserInfo(credentialId, userJson, Transport.valueOf(transport)))
48+
}
49+
}
50+
result
51+
}
52+
3453
fun insertPrivileged(packageName: String, signatureDigest: String) = writableDatabase.use {
3554
it.insertWithOnConflict(TABLE_PRIVILEGED_APPS, null, ContentValues().apply {
3655
put(COLUMN_PACKAGE_NAME, packageName)
@@ -39,13 +58,33 @@ class Database(context: Context) : SQLiteOpenHelper(context, "fido.db", null, VE
3958
}, CONFLICT_REPLACE)
4059
}
4160

42-
fun insertKnownRegistration(rpId: String, credentialId: String, transport: Transport) = writableDatabase.use {
43-
it.insertWithOnConflict(TABLE_KNOWN_REGISTRATIONS, null, ContentValues().apply {
44-
put(COLUMN_RP_ID, rpId)
61+
fun insertKnownRegistration(rpId: String, credentialId: String, transport: Transport, userJson: String? = null) = writableDatabase.use {
62+
Log.d(TAG, "insertKnownRegistration: $rpId $credentialId $transport $userJson")
63+
val values = ContentValues().apply {
4564
put(COLUMN_CREDENTIAL_ID, credentialId)
4665
put(COLUMN_TRANSPORT, transport.name)
4766
put(COLUMN_TIMESTAMP, System.currentTimeMillis())
48-
}, CONFLICT_REPLACE)
67+
if (userJson != null) {
68+
put(COLUMN_REGISTER_USER, userJson)
69+
}
70+
}
71+
72+
val updated = if (userJson == null) {
73+
it.update(TABLE_KNOWN_REGISTRATIONS, values, "$COLUMN_RP_ID = ? AND $COLUMN_CREDENTIAL_ID = ?", arrayOf(rpId, credentialId))
74+
} else {
75+
it.update(TABLE_KNOWN_REGISTRATIONS, values, "$COLUMN_RP_ID = ? AND $COLUMN_REGISTER_USER = ?", arrayOf(rpId, userJson))
76+
}
77+
78+
if (updated == 0) {
79+
val insertValues = ContentValues().apply {
80+
put(COLUMN_RP_ID, rpId)
81+
put(COLUMN_CREDENTIAL_ID, credentialId)
82+
put(COLUMN_TRANSPORT, transport.name)
83+
put(COLUMN_TIMESTAMP, System.currentTimeMillis())
84+
userJson?.let { json -> put(COLUMN_REGISTER_USER, json) }
85+
}
86+
it.insert(TABLE_KNOWN_REGISTRATIONS, null, insertValues)
87+
}
4988
}
5089

5190
override fun onCreate(db: SQLiteDatabase) {
@@ -59,10 +98,13 @@ class Database(context: Context) : SQLiteOpenHelper(context, "fido.db", null, VE
5998
if (oldVersion < 2) {
6099
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)")
61100
}
101+
if (oldVersion < 3) {
102+
db.execSQL("ALTER TABLE $TABLE_KNOWN_REGISTRATIONS ADD COLUMN $COLUMN_REGISTER_USER TEXT")
103+
}
62104
}
63105

64106
companion object {
65-
const val VERSION = 2
107+
const val VERSION = 3
66108
private const val TABLE_PRIVILEGED_APPS = "privileged_apps"
67109
private const val TABLE_KNOWN_REGISTRATIONS = "known_registrations"
68110
private const val COLUMN_PACKAGE_NAME = "package_name"
@@ -71,6 +113,7 @@ class Database(context: Context) : SQLiteOpenHelper(context, "fido.db", null, VE
71113
private const val COLUMN_RP_ID = "rp_id"
72114
private const val COLUMN_CREDENTIAL_ID = "credential_id"
73115
private const val COLUMN_TRANSPORT = "transport"
116+
private const val COLUMN_REGISTER_USER = "register_user"
74117
}
75118
}
76119

0 commit comments

Comments
 (0)