Merge "Add challenge-less resetLockout for new AIDL interfaces" into sc-dev

This commit is contained in:
Kevin Chyn
2021-03-18 03:24:08 +00:00
committed by Android (Google) Code Review
7 changed files with 148 additions and 49 deletions

View File

@@ -393,14 +393,13 @@ public class FaceService extends SystemService implements BiometricServiceCallba
final IFaceServiceReceiver receiver, final String opPackageName) {
Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
final Pair<Integer, ServiceProvider> provider = getSingleProvider();
if (provider == null) {
Slog.w(TAG, "Null provider for removeAll");
return;
for (ServiceProvider provider : mServiceProviders) {
List<FaceSensorPropertiesInternal> props = provider.getSensorProperties();
for (FaceSensorPropertiesInternal prop : props) {
provider.scheduleRemoveAll(prop.sensorId, token, userId, receiver,
opPackageName);
}
}
provider.second.scheduleRemoveAll(provider.first, token, userId, receiver,
opPackageName);
}
@Override // Binder call

View File

@@ -23,11 +23,11 @@ import android.hardware.biometrics.face.V1_0.IBiometricsFaceClientCallback;
import android.hardware.biometrics.face.V1_0.OptionalBool;
import android.hardware.biometrics.face.V1_0.OptionalUint64;
import android.hardware.biometrics.face.V1_0.Status;
import android.os.NativeHandle;
import android.os.RemoteException;
import android.util.Slog;
import java.util.ArrayList;
import java.util.Arrays;
public class TestHal extends IBiometricsFace.Stub {
private static final String TAG = "face.hidl.TestHal";
@@ -107,8 +107,12 @@ public class TestHal extends IBiometricsFace.Stub {
}
@Override
public int remove(int faceId) {
public int remove(int faceId) throws RemoteException {
Slog.w(TAG, "remove");
if (mCallback != null) {
mCallback.onRemoved(0 /* deviceId */, new ArrayList<Integer>(Arrays.asList(faceId)),
0 /* userId */);
}
return 0;
}

View File

@@ -506,13 +506,13 @@ public class FingerprintService extends SystemService implements BiometricServic
final IFingerprintServiceReceiver receiver, final String opPackageName) {
Utils.checkPermission(getContext(), MANAGE_FINGERPRINT);
final Pair<Integer, ServiceProvider> provider = getSingleProvider();
if (provider == null) {
Slog.w(TAG, "Null provider for removeAll");
return;
for (ServiceProvider provider : mServiceProviders) {
List<FingerprintSensorPropertiesInternal> props = provider.getSensorProperties();
for (FingerprintSensorPropertiesInternal prop : props) {
provider.scheduleRemoveAll(prop.sensorId, token, receiver, userId,
opPackageName);
}
}
provider.second.scheduleRemoveAll(provider.first, token, receiver, userId,
opPackageName);
}
@Override // Binder call

View File

@@ -527,7 +527,9 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
// Fingerprint2.1 keeps track of lockout in the framework. Let's just do it on the handler
// thread.
mHandler.post(() -> {
mLockoutTracker.resetFailedAttemptsForUser(true /* clearAttemptCounter */, userId);
final FingerprintResetLockoutClient client = new FingerprintResetLockoutClient(mContext,
userId, mContext.getOpPackageName(), sensorId, mLockoutTracker);
mScheduler.scheduleClientMonitor(client);
});
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.biometrics.sensors.fingerprint.hidl;
import android.annotation.NonNull;
import android.content.Context;
import android.hardware.biometrics.BiometricsProtoEnums;
import com.android.server.biometrics.BiometricsProto;
import com.android.server.biometrics.sensors.BaseClientMonitor;
/**
* Clears lockout, which is handled in the framework (and not the HAL) for the
* IBiometricsFingerprint@2.1 interface.
*/
public class FingerprintResetLockoutClient extends BaseClientMonitor {
@NonNull final LockoutFrameworkImpl mLockoutTracker;
public FingerprintResetLockoutClient(@NonNull Context context, int userId,
@NonNull String owner, int sensorId, @NonNull LockoutFrameworkImpl lockoutTracker) {
super(context, null /* token */, null /* listener */, userId, owner, 0 /* cookie */,
sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN,
BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN);
mLockoutTracker = lockoutTracker;
}
@Override
public void start(@NonNull Callback callback) {
super.start(callback);
mLockoutTracker.resetFailedAttemptsForUser(true /* clearAttemptCounter */,
getTargetUserId());
callback.onClientFinished(this, true /* success */);
}
@Override
public int getProtoEnum() {
return BiometricsProto.CM_RESET_LOCKOUT;
}
}

View File

@@ -93,8 +93,11 @@ public class TestHal extends IBiometricsFingerprint.Stub {
}
@Override
public int remove(int gid, int fid) {
public int remove(int gid, int fid) throws RemoteException {
Slog.w(TAG, "Remove");
if (mCallback != null) {
mCallback.onRemoved(0 /* deviceId */, fid, gid, 0 /* remaining */);
}
return 0;
}

View File

@@ -119,19 +119,11 @@ public class BiometricDeferredQueue {
for (UserAuthInfo userAuthInfo : pendingResetLockuts) {
Slog.d(TAG, "Resetting face lockout for sensor: " + sensorId
+ ", user: " + userAuthInfo.userId);
final VerifyCredentialResponse response = spManager.verifyChallengeInternal(
getGatekeeperService(), userAuthInfo.gatekeeperPassword, challenge,
userAuthInfo.userId);
if (response == null) {
Slog.wtf(TAG, "VerifyChallenge failed, null response");
continue;
final byte[] hat = requestHatFromGatekeeperPassword(spManager, userAuthInfo,
challenge);
if (hat != null) {
faceManager.resetLockout(sensorId, userAuthInfo.userId, hat);
}
if (response.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK) {
Slog.wtf(TAG, "VerifyChallenge failed, response: "
+ response.getResponseCode());
}
faceManager.resetLockout(sensorId, userAuthInfo.userId,
response.getGatekeeperHAT());
}
sensorIds.remove(sensorId);
@@ -146,15 +138,6 @@ public class BiometricDeferredQueue {
finishCallback.onFinished();
}
}
synchronized IGateKeeperService getGatekeeperService() {
final IBinder service = ServiceManager.getService(Context.GATEKEEPER_SERVICE);
if (service == null) {
Slog.e(TAG, "Unable to acquire GateKeeperService");
return null;
}
return IGateKeeperService.Stub.asInterface(service);
}
}
@Nullable private FaceResetLockoutTask mFaceResetLockoutTask;
@@ -214,10 +197,19 @@ public class BiometricDeferredQueue {
mFingerprintManager.resetLockout(prop.sensorId, user.userId,
null /* hardwareAuthToken */);
}
} else if (!prop.resetLockoutRequiresChallenge) {
for (UserAuthInfo user : pendingResetLockouts) {
Slog.d(TAG, "Resetting fingerprint lockout for sensor: " + prop.sensorId
+ ", user: " + user.userId);
final byte[] hat = requestHatFromGatekeeperPassword(mSpManager, user,
0 /* challenge */);
if (hat != null) {
mFingerprintManager.resetLockout(prop.sensorId, user.userId, hat);
}
}
} else {
Slog.e(TAG, "Fingerprint resetLockout with HAT not supported yet");
// TODO(b/152414803): Implement this when resetLockout is implemented below
// the framework.
Slog.w(TAG, "No fingerprint HAL interface requires HAT with challenge"
+ ", sensorId: " + prop.sensorId);
}
}
}
@@ -228,11 +220,6 @@ public class BiometricDeferredQueue {
* in-flight challenge, we generate a single challenge to reset lockout for all profiles. This
* hopefully reduces/eliminates issues such as overwritten challenge, incorrectly revoked
* challenge, or other race conditions.
*
* TODO(b/162965646) This logic can be avoided if multiple in-flight challenges are supported.
* Though it will need to continue to exist to support existing HIDLs, each profile that
* requires resetLockout could have its own challenge, and the `mPendingResetLockouts` queue
* can be avoided.
*/
private void processPendingLockoutsForFace(List<UserAuthInfo> pendingResetLockouts) {
if (mFaceManager != null) {
@@ -251,10 +238,60 @@ public class BiometricDeferredQueue {
mFaceResetLockoutTask = new FaceResetLockoutTask(mFaceFinishCallback, mFaceManager,
mSpManager, sensorIds, pendingResetLockouts);
for (final FaceSensorPropertiesInternal prop : faceSensorProperties) {
// Generate a challenge for each sensor. The challenge does not need to be
// per-user, since the HAT returned by gatekeeper contains userId.
mFaceManager.generateChallenge(prop.sensorId, mFaceResetLockoutTask);
if (prop.resetLockoutRequiresHardwareAuthToken) {
if (prop.resetLockoutRequiresChallenge) {
// Generate a challenge for each sensor. The challenge does not need to be
// per-user, since the HAT returned by gatekeeper contains userId.
mFaceManager.generateChallenge(prop.sensorId, mFaceResetLockoutTask);
} else {
for (UserAuthInfo user : pendingResetLockouts) {
Slog.d(TAG, "Resetting face lockout for sensor: " + prop.sensorId
+ ", user: " + user.userId);
final byte[] hat = requestHatFromGatekeeperPassword(mSpManager, user,
0 /* challenge */);
if (hat != null) {
mFaceManager.resetLockout(prop.sensorId, user.userId, hat);
}
}
}
} else {
Slog.w(TAG, "Lockout is below the HAL for all face authentication interfaces"
+ ", sensorId: " + prop.sensorId);
}
}
}
}
@Nullable
private static byte[] requestHatFromGatekeeperPassword(
@NonNull SyntheticPasswordManager spManager,
@NonNull UserAuthInfo userAuthInfo, long challenge) {
final VerifyCredentialResponse response = spManager.verifyChallengeInternal(
getGatekeeperService(), userAuthInfo.gatekeeperPassword, challenge,
userAuthInfo.userId);
if (response == null) {
Slog.wtf(TAG, "VerifyChallenge failed, null response");
return null;
}
if (response.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK) {
Slog.wtf(TAG, "VerifyChallenge failed, response: "
+ response.getResponseCode());
return null;
}
if (response.getGatekeeperHAT() == null) {
Slog.e(TAG, "Null HAT received from spManager");
}
return response.getGatekeeperHAT();
}
@Nullable
private static synchronized IGateKeeperService getGatekeeperService() {
final IBinder service = ServiceManager.getService(Context.GATEKEEPER_SERVICE);
if (service == null) {
Slog.e(TAG, "Unable to acquire GateKeeperService");
return null;
}
return IGateKeeperService.Stub.asInterface(service);
}
}