Skip to content

Commit

Permalink
Add support for regular fido service
Browse files Browse the repository at this point in the history
  • Loading branch information
p1gp1g committed Feb 19, 2024
1 parent e4c1c5a commit 1cdb499
Show file tree
Hide file tree
Showing 8 changed files with 260 additions and 4 deletions.
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.Fido2RegularService"
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,113 @@
/*
* 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.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
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.PublicKeyCredentialCreationOptions
import com.google.android.gms.fido.fido2.api.common.PublicKeyCredentialRequestOptions
import com.google.android.gms.fido.fido2.internal.regular.IFido2RegularCallbacks
import com.google.android.gms.fido.fido2.internal.regular.IFido2RegularService
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.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 Fido2RegularService : BaseService(TAG, FIDO2_REGULAR) {
override fun handleServiceRequest(callback: IGmsCallbacks, request: GetServiceRequest, service: GmsService) {
callback.onPostInitCompleteWithConnectionInfo(
CommonStatusCodes.SUCCESS,
Fido2RegularServiceImpl(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)
)
}
);
}
}

class Fido2RegularServiceImpl(private val context: Context, private val lifecycle: Lifecycle) :
IFido2RegularService.Stub(), LifecycleOwner {
override fun getRegisterPendingIntent(callbacks: IFido2RegularCallbacks, 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: IFido2RegularCallbacks, 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 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 =
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 IFido2RegularCallbacks {
void onPendingIntent(in Status status, in PendingIntent pendingIntent);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.google.android.gms.fido.fido2.internal.regular;

import com.google.android.gms.fido.fido2.internal.regular.IFido2RegularCallbacks;
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 IFido2RegularService {
void getRegisterPendingIntent(IFido2RegularCallbacks callbacks, in PublicKeyCredentialCreationOptions options) = 0;
void getSignPendingIntent(IFido2RegularCallbacks callbacks, in PublicKeyCredentialRequestOptions options) = 1;
void isUserVerifyingPlatformAuthenticatorAvailable(IBooleanCallback callbacks) = 2;
void getCredentialList(ICredentialListCallback callbacks, String rpId) = 3;
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,31 @@

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.IFido2RegularCallbacks;
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;
import org.microg.gms.fido.fido2.Fido2PrivilegedGmsClient;

/**
* 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 +57,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 IFido2RegularCallbacks.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 +91,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 IFido2RegularCallbacks.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,53 @@
/*
* SPDX-FileCopyrightText: 2022 microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/

package org.microg.gms.fido.fido2;

import static org.microg.gms.fido.fido2.Constants.MICROG_FIDO_SERVICE_PACKAGE_NAME;

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.ICredentialListCallback;
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.IFido2RegularCallbacks;
import com.google.android.gms.fido.fido2.internal.regular.IFido2RegularService;

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<IFido2RegularService> {
public Fido2GmsClient(Context context, ConnectionCallbacks callbacks, OnConnectionFailedListener connectionFailedListener) {
super(context, callbacks, connectionFailedListener, GmsService.FIDO2_REGULAR.ACTION);
serviceId = GmsService.FIDO2_REGULAR.SERVICE_ID;
additionalTargetApplication = MICROG_FIDO_SERVICE_PACKAGE_NAME;
}

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

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

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

public void getCredentialList(ICredentialListCallback callbacks, String rpId) throws RemoteException {
getServiceInterface().getCredentialList(callbacks, rpId);
}

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

0 comments on commit 1cdb499

Please sign in to comment.