Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add regular fido service #2184

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 8 additions & 0 deletions play-services-fido/core/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>
<service
android:name=".regular.Fido2AppService"
android:exported="true">
<intent-filter>
<action android:name="com.google.android.gms.fido.fido2.regular.START" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>
<activity
android:name=".ui.AuthenticatorActivity"
android:configChanges="orientation|keyboard|keyboardHidden|screenSize"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package org.microg.gms.fido.core

import com.google.android.gms.common.Feature

val FEATURES = arrayOf(
Feature("cancel_target_direct_transfer", 1),
Feature("delete_credential", 1),
Feature("delete_device_public_key", 1),
Feature("get_or_generate_device_public_key", 1),
Feature("get_passkeys", 1),
Feature("update_passkey", 1),
Feature("is_user_verifying_platform_authenticator_available_for_credential", 1),
Feature("is_user_verifying_platform_authenticator_available", 1),
Feature("privileged_api_list_credentials", 2),
Feature("start_target_direct_transfer", 1),
Feature("first_party_api_get_link_info", 1),
Feature("get_browser_hybrid_client_sign_pending_intent", 1),
Feature("get_browser_hybrid_client_registration_pending_intent", 1),
Feature("privileged_authenticate_passkey", 1),
Feature("privileged_register_passkey_with_sync_account", 1)
)
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import android.os.Parcel
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import com.google.android.gms.common.Feature
import com.google.android.gms.common.api.CommonStatusCodes
import com.google.android.gms.common.api.Status
import com.google.android.gms.common.internal.ConnectionInfo
Expand All @@ -32,6 +31,7 @@ import com.google.android.gms.fido.fido2.internal.privileged.IFido2PrivilegedSer
import org.microg.gms.BaseService
import org.microg.gms.common.GmsService
import org.microg.gms.common.GmsService.FIDO2_PRIVILEGED
import org.microg.gms.fido.core.FEATURES
import org.microg.gms.fido.core.ui.AuthenticatorActivity
import org.microg.gms.fido.core.ui.AuthenticatorActivity.Companion.SOURCE_BROWSER
import org.microg.gms.fido.core.ui.AuthenticatorActivity.Companion.KEY_SOURCE
Expand All @@ -49,12 +49,7 @@ class Fido2PrivilegedService : BaseService(TAG, FIDO2_PRIVILEGED) {
callback.onPostInitCompleteWithConnectionInfo(
CommonStatusCodes.SUCCESS,
Fido2PrivilegedServiceImpl(this, lifecycle).asBinder(),
ConnectionInfo().apply {
features = arrayOf(
Feature("is_user_verifying_platform_authenticator_available", 1),
Feature("is_user_verifying_platform_authenticator_available_for_credential", 1)
)
}
ConnectionInfo().apply { features = FEATURES }
);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*
* SPDX-FileCopyrightText: 2022 microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/

package org.microg.gms.fido.core.regular

import android.app.KeyguardManager
import android.app.PendingIntent
import android.app.PendingIntent.FLAG_IMMUTABLE
import android.app.PendingIntent.FLAG_UPDATE_CURRENT
import android.content.Context
import android.content.Context.KEYGUARD_SERVICE
import android.content.Intent
import android.os.Build.VERSION.SDK_INT
import android.os.Parcel
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import com.google.android.gms.common.api.CommonStatusCodes
import com.google.android.gms.common.api.Status
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.common.PublicKeyCredentialCreationOptions
import com.google.android.gms.fido.fido2.api.common.PublicKeyCredentialRequestOptions
import com.google.android.gms.fido.fido2.internal.regular.IFido2AppCallbacks
import com.google.android.gms.fido.fido2.internal.regular.IFido2AppService
import org.microg.gms.BaseService
import org.microg.gms.common.GmsService
import org.microg.gms.common.GmsService.FIDO2_REGULAR
import org.microg.gms.fido.core.FEATURES
import org.microg.gms.fido.core.ui.AuthenticatorActivity
import org.microg.gms.fido.core.ui.AuthenticatorActivity.Companion.SOURCE_APP
import org.microg.gms.fido.core.ui.AuthenticatorActivity.Companion.KEY_SOURCE
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_TYPE
import org.microg.gms.fido.core.ui.AuthenticatorActivity.Companion.TYPE_REGISTER
import org.microg.gms.fido.core.ui.AuthenticatorActivity.Companion.TYPE_SIGN
import org.microg.gms.utils.warnOnTransactionIssues

const val TAG = "Fido2Regular"

class Fido2AppService : BaseService(TAG, FIDO2_REGULAR) {
override fun handleServiceRequest(callback: IGmsCallbacks, request: GetServiceRequest, service: GmsService) {
callback.onPostInitCompleteWithConnectionInfo(
CommonStatusCodes.SUCCESS,
Fido2AppServiceImpl(this, lifecycle).asBinder(),
ConnectionInfo().apply { features = FEATURES }
);
}
}

class Fido2AppServiceImpl(private val context: Context, override val lifecycle: Lifecycle) :
IFido2AppService.Stub(), LifecycleOwner {
override fun getRegisterPendingIntent(callbacks: IFido2AppCallbacks, options: PublicKeyCredentialCreationOptions) {
lifecycleScope.launchWhenStarted {
val intent = Intent(context, AuthenticatorActivity::class.java)
.putExtra(KEY_SERVICE, FIDO2_REGULAR.SERVICE_ID)
.putExtra(KEY_SOURCE, SOURCE_APP)
.putExtra(KEY_TYPE, TYPE_REGISTER)
.putExtra(KEY_OPTIONS, options.serializeToBytes())

val pendingIntent =
PendingIntent.getActivity(context, options.hashCode(), intent, FLAG_UPDATE_CURRENT or FLAG_IMMUTABLE)
callbacks.onPendingIntent(Status.SUCCESS, pendingIntent)
}
}

override fun getSignPendingIntent(callbacks: IFido2AppCallbacks, options: PublicKeyCredentialRequestOptions) {
lifecycleScope.launchWhenStarted {
val intent = Intent(context, AuthenticatorActivity::class.java)
.putExtra(KEY_SERVICE, FIDO2_REGULAR.SERVICE_ID)
.putExtra(KEY_SOURCE, SOURCE_APP)
.putExtra(KEY_TYPE, TYPE_SIGN)
.putExtra(KEY_OPTIONS, options.serializeToBytes())

val pendingIntent =
PendingIntent.getActivity(context, options.hashCode(), intent, FLAG_UPDATE_CURRENT or FLAG_IMMUTABLE)
callbacks.onPendingIntent(Status.SUCCESS, pendingIntent)
}
}

override fun isUserVerifyingPlatformAuthenticatorAvailable(callbacks: IBooleanCallback) {
lifecycleScope.launchWhenStarted {
if (SDK_INT < 24) {
callbacks.onBoolean(false)
} else {
val keyguardManager = context.getSystemService(KEYGUARD_SERVICE) as? KeyguardManager?
callbacks.onBoolean(keyguardManager?.isDeviceSecure == true)
}
}
}

override fun onTransact(code: Int, data: Parcel, reply: Parcel?, flags: Int): Boolean =
warnOnTransactionIssues(code, reply, flags, TAG) { super.onTransact(code, data, reply, flags) }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.google.android.gms.fido.fido2.api.common;

parcelable PublicKeyCredentialCreationOptions;
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.google.android.gms.fido.fido2.api.common;

parcelable PublicKeyCredentialRequestOptions;
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.google.android.gms.fido.fido2.internal.regular;

import android.app.PendingIntent;
import com.google.android.gms.common.api.Status;

interface IFido2AppCallbacks {
void onPendingIntent(in Status status, in PendingIntent pendingIntent);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.google.android.gms.fido.fido2.internal.regular;

import com.google.android.gms.fido.fido2.internal.regular.IFido2AppCallbacks;
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.PublicKeyCredentialCreationOptions;
import com.google.android.gms.fido.fido2.api.common.PublicKeyCredentialRequestOptions;

interface IFido2AppService {
void getRegisterPendingIntent(IFido2AppCallbacks callbacks, in PublicKeyCredentialCreationOptions options) = 0;
void getSignPendingIntent(IFido2AppCallbacks callbacks, in PublicKeyCredentialRequestOptions options) = 1;
void isUserVerifyingPlatformAuthenticatorAvailable(IBooleanCallback callbacks) = 2;
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,30 @@

import android.app.PendingIntent;
import android.content.Context;
import android.os.RemoteException;

import com.google.android.gms.common.api.Api;
import com.google.android.gms.common.api.ApiException;
import com.google.android.gms.common.api.GoogleApi;
import com.google.android.gms.common.api.Status;
import com.google.android.gms.fido.fido2.api.IBooleanCallback;
import com.google.android.gms.fido.fido2.api.common.PublicKeyCredentialCreationOptions;
import com.google.android.gms.fido.fido2.api.common.PublicKeyCredentialRequestOptions;
import com.google.android.gms.fido.fido2.internal.regular.IFido2AppCallbacks;
import com.google.android.gms.tasks.Task;
import com.google.android.gms.tasks.Tasks;

import org.microg.gms.common.PublicApi;
import org.microg.gms.common.api.PendingGoogleApiCall;
import org.microg.gms.fido.fido2.Fido2GmsClient;
import org.microg.gms.fido.fido2.Fido2PendingIntentImpl;

/**
* The entry point for interacting with FIDO2 APIs.
*/
@PublicApi
public class Fido2ApiClient extends GoogleApi<Api.ApiOptions.NoOptions> {
private static final Api<Api.ApiOptions.NoOptions> API = null;
private static final Api<Api.ApiOptions.NoOptions> API = new Api<>((options, context, looper, clientSettings, callbacks, connectionFailedListener) -> new Fido2GmsClient(context, callbacks, connectionFailedListener));

@PublicApi(exclude = true)
public Fido2ApiClient(Context context) {
Expand All @@ -49,7 +56,22 @@ public Task<Fido2PendingIntent> getRegisterIntent(PublicKeyCredentialCreationOpt
* @return Task with PendingIntent to launch FIDO2 registration request
*/
public Task<PendingIntent> getRegisterPendingIntent(PublicKeyCredentialCreationOptions requestOptions) {
throw new UnsupportedOperationException();
return scheduleTask((PendingGoogleApiCall<PendingIntent, Fido2GmsClient>) (client, completionSource) -> {
try {
client.getRegisterPendingIntent(new IFido2AppCallbacks.Stub() {
@Override
public void onPendingIntent(Status status, PendingIntent pendingIntent) throws RemoteException {
if (status.isSuccess()) {
completionSource.setResult(pendingIntent);
} else {
completionSource.setException(new ApiException(status));
}
}
}, requestOptions);
} catch (Exception e) {
completionSource.setException(e);
}
});
}

/**
Expand All @@ -68,14 +90,45 @@ public Task<Fido2PendingIntent> getSignIntent(PublicKeyCredentialRequestOptions
* @return Task with PendingIntent to launch FIDO2 signature request
*/
public Task<PendingIntent> getSignPendingIntent(PublicKeyCredentialRequestOptions requestOptions) {
throw new UnsupportedOperationException();
return scheduleTask((PendingGoogleApiCall<PendingIntent, Fido2GmsClient>) (client, completionSource) -> {
try {
client.getSignPendingIntent(new IFido2AppCallbacks.Stub() {
@Override
public void onPendingIntent(Status status, PendingIntent pendingIntent) throws RemoteException {
if (status.isSuccess()) {
completionSource.setResult(pendingIntent);
} else {
completionSource.setException(new ApiException(status));
}
}
}, requestOptions);
} catch (Exception e) {
completionSource.setException(e);
}
});
}

/**
* Creates a Task with {@link Boolean}, which check if a user verifying platform authenticator is available on the
* device.
*/
public Task<Boolean> isUserVerifyingPlatformAuthenticatorAvailable() {
throw new UnsupportedOperationException();
return scheduleTask((PendingGoogleApiCall<Boolean, Fido2GmsClient>) (client, completionSource) -> {
try {
client.isUserVerifyingPlatformAuthenticatorAvailable(new IBooleanCallback.Stub() {
@Override
public void onBoolean(boolean value) throws RemoteException {
completionSource.setResult(value);
}

@Override
public void onError(Status status) throws RemoteException {
completionSource.setException(new ApiException(status));
}
});
} catch (Exception e) {
completionSource.setException(e);
}
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* SPDX-FileCopyrightText: 2022 microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/

package org.microg.gms.fido.fido2;

import android.content.Context;
import android.os.IBinder;
import android.os.RemoteException;

import com.google.android.gms.fido.fido2.api.IBooleanCallback;
import com.google.android.gms.fido.fido2.api.common.PublicKeyCredentialCreationOptions;
import com.google.android.gms.fido.fido2.api.common.PublicKeyCredentialRequestOptions;
import com.google.android.gms.fido.fido2.internal.regular.IFido2AppCallbacks;
import com.google.android.gms.fido.fido2.internal.regular.IFido2AppService;

import org.microg.gms.common.GmsClient;
import org.microg.gms.common.GmsService;
import org.microg.gms.common.api.ConnectionCallbacks;
import org.microg.gms.common.api.OnConnectionFailedListener;

public class Fido2GmsClient extends GmsClient<IFido2AppService> {
public Fido2GmsClient(Context context, ConnectionCallbacks callbacks, OnConnectionFailedListener connectionFailedListener) {
super(context, callbacks, connectionFailedListener, GmsService.FIDO2_REGULAR.ACTION);
serviceId = GmsService.FIDO2_REGULAR.SERVICE_ID;
}

public void getRegisterPendingIntent(IFido2AppCallbacks callbacks, PublicKeyCredentialCreationOptions options) throws RemoteException {
getServiceInterface().getRegisterPendingIntent(callbacks, options);
}

public void getSignPendingIntent(IFido2AppCallbacks callbacks, PublicKeyCredentialRequestOptions options) throws RemoteException {
getServiceInterface().getSignPendingIntent(callbacks, options);
}

public void isUserVerifyingPlatformAuthenticatorAvailable(IBooleanCallback callback) throws RemoteException {
getServiceInterface().isUserVerifyingPlatformAuthenticatorAvailable(callback);
}

@Override
protected IFido2AppService interfaceFromBinder(IBinder binder) {
return IFido2AppService.Stub.asInterface(binder);
}
}