Merge "Biometric time-based resetLockout for multi-biometric devices" into sc-dev

This commit is contained in:
Kevin Chyn
2021-03-18 06:49:37 +00:00
committed by Android (Google) Code Review
16 changed files with 183 additions and 2 deletions

View File

@@ -29,6 +29,7 @@ import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.content.Context;
import android.os.IBinder;
import android.os.RemoteException;
import android.security.keystore.KeyProperties;
import android.util.Slog;
@@ -409,6 +410,36 @@ public class BiometricManager {
}
}
/**
* Requests all other biometric sensors to resetLockout. Note that this is a "time bound"
* See the {@link android.hardware.biometrics.fingerprint.ISession#resetLockout(int,
* HardwareAuthToken)} and {@link android.hardware.biometrics.face.ISession#resetLockout(int,
* HardwareAuthToken)} documentation for complete details.
*
* @param token A binder from the caller, for the service to linkToDeath
* @param opPackageName Caller's package name
* @param fromSensorId The originating sensor that just authenticated. Note that this MUST
* be a sensor that meets {@link Authenticators#BIOMETRIC_STRONG} strength.
* The strength will also be enforced on the BiometricService side.
* @param userId The user that authentication succeeded for, and also the user that resetLockout
* should be applied to.
* @param hardwareAuthToken A valid HAT generated upon successful biometric authentication. Note
* that it is not necessary for the HAT to contain a challenge.
* @hide
*/
@RequiresPermission(USE_BIOMETRIC_INTERNAL)
public void resetLockoutTimeBound(IBinder token, String opPackageName, int fromSensorId,
int userId, byte[] hardwareAuthToken) {
if (mService != null) {
try {
mService.resetLockoutTimeBound(token, opPackageName, fromSensorId, userId,
hardwareAuthToken);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
}
/**
* Provides a localized string that may be used as the label for a button that invokes
* {@link BiometricPrompt}.

View File

@@ -69,6 +69,10 @@ interface IAuthService {
// land as SIDs, and are used during key generation.
long[] getAuthenticatorIds();
// See documentation in BiometricManager.
void resetLockoutTimeBound(IBinder token, String opPackageName, int fromSensorId, int userId,
in byte[] hardwareAuthToken);
// Provides a localized string that may be used as the label for a button that invokes
// BiometricPrompt.
CharSequence getButtonLabel(int userId, String opPackageName, int authenticators);

View File

@@ -70,4 +70,8 @@ interface IBiometricAuthenticator {
// Gets the authenticator ID representing the current set of enrolled templates
long getAuthenticatorId(int callingUserId);
// Requests the sensor to reset its lockout state
void resetLockout(IBinder token, String opPackageName, int userId,
in byte[] hardwareAuthToken);
}

View File

@@ -74,6 +74,10 @@ interface IBiometricService {
// land as SIDs, and are used during key generation.
long[] getAuthenticatorIds(int callingUserId);
// See documentation in BiometricManager.
void resetLockoutTimeBound(IBinder token, String opPackageName, int fromSensorId, int userId,
in byte[] hardwareAuthToken);
int getCurrentStrength(int sensorId);
// Returns a bit field of the modality (or modalities) that are will be used for authentication.

View File

@@ -125,6 +125,13 @@ message SensorStateProto {
// User states for this sensor.
repeated UserStateProto user_states = 4;
// True if resetLockout requires a HAT to be verified in the TEE or equivalent.
optional bool reset_lockout_requires_hardware_auth_token = 5;
// True if a HAT is required (field above) AND a challenge needs to be generated by the
// biometric TEE (or equivalent), and wrapped within the HAT.
optional bool reset_lockout_requires_challenge = 6;
}
// State of a specific user for a specific sensor.

View File

@@ -339,6 +339,20 @@ public class AuthService extends SystemService {
}
}
@Override
public void resetLockoutTimeBound(IBinder token, String opPackageName, int fromSensorId,
int userId, byte[] hardwareAuthToken) throws RemoteException {
checkInternalPermission();
final long identity = Binder.clearCallingIdentity();
try {
mBiometricService.resetLockoutTimeBound(token, opPackageName, fromSensorId, userId,
hardwareAuthToken);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
@Override
public CharSequence getButtonLabel(
int userId,

View File

@@ -754,7 +754,7 @@ public class BiometricService extends SystemService {
}
}
@Override
@Override // Binder call
public void invalidateAuthenticatorIds(int userId, int fromSensorId,
IInvalidationCallback callback) {
checkInternalPermission();
@@ -789,6 +789,45 @@ public class BiometricService extends SystemService {
return result;
}
@Override // Binder call
public void resetLockoutTimeBound(IBinder token, String opPackageName, int fromSensorId,
int userId, byte[] hardwareAuthToken) {
checkInternalPermission();
// Check originating strength
if (!Utils.isAtLeastStrength(getSensorForId(fromSensorId).getCurrentStrength(),
Authenticators.BIOMETRIC_STRONG)) {
Slog.w(TAG, "Sensor: " + fromSensorId + " is does not meet the required strength to"
+ " request resetLockout");
return;
}
// Request resetLockout for applicable sensors
for (BiometricSensor sensor : mSensors) {
if (sensor.id == fromSensorId) {
continue;
}
try {
final SensorPropertiesInternal props = sensor.impl
.getSensorProperties(getContext().getOpPackageName());
final boolean supportsChallengelessHat =
props.resetLockoutRequiresHardwareAuthToken
&& !props.resetLockoutRequiresChallenge;
final boolean doesNotRequireHat = !props.resetLockoutRequiresHardwareAuthToken;
if (supportsChallengelessHat || doesNotRequireHat) {
Slog.d(TAG, "resetLockout from: " + fromSensorId
+ ", for: " + sensor.id
+ ", userId: " + userId);
sensor.impl.resetLockout(token, opPackageName, userId,
hardwareAuthToken);
}
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception", e);
}
}
}
@Override // Binder call
public int getCurrentStrength(int sensorId) {
checkInternalPermission();
@@ -1294,6 +1333,16 @@ public class BiometricService extends SystemService {
}
}
@Nullable
private BiometricSensor getSensorForId(int sensorId) {
for (BiometricSensor sensor : mSensors) {
if (sensor.id == sensorId) {
return sensor;
}
}
return null;
}
private void dumpInternal(PrintWriter pw) {
pw.println("Sensors:");
for (BiometricSensor sensor : mSensors) {

View File

@@ -176,7 +176,8 @@ public class Utils {
* @param requestedStrength the strength that it must meet
* @return true only if the sensor is at least as strong as the requested strength
*/
public static boolean isAtLeastStrength(int sensorStrength, int requestedStrength) {
public static boolean isAtLeastStrength(@Authenticators.Types int sensorStrength,
@Authenticators.Types int requestedStrength) {
// Clear out any bits that are not reserved for biometric
sensorStrength &= Authenticators.BIOMETRIC_MIN_STRENGTH;

View File

@@ -26,6 +26,7 @@ import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.os.IBinder;
import android.os.RemoteException;
@@ -50,6 +51,7 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T>
private final boolean mIsStrongBiometric;
private final boolean mRequireConfirmation;
private final ActivityTaskManager mActivityTaskManager;
private final BiometricManager mBiometricManager;
@Nullable private final TaskStackListener mTaskStackListener;
private final LockoutTracker mLockoutTracker;
private final boolean mIsRestricted;
@@ -73,6 +75,7 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T>
mOperationId = operationId;
mRequireConfirmation = requireConfirmation;
mActivityTaskManager = ActivityTaskManager.getInstance();
mBiometricManager = context.getSystemService(BiometricManager.class);
mTaskStackListener = taskStackListener;
mLockoutTracker = lockoutTracker;
mIsRestricted = restricted;
@@ -207,6 +210,13 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T>
for (int i = 0; i < hardwareAuthToken.size(); i++) {
byteToken[i] = hardwareAuthToken.get(i);
}
if (mIsStrongBiometric) {
mBiometricManager.resetLockoutTimeBound(getToken(),
getContext().getOpPackageName(),
getSensorId(), getTargetUserId(), byteToken);
}
if (isBiometricPrompt() && listener != null) {
// BiometricService will add the token to keystore
listener.onAuthenticationSucceeded(getSensorId(), identifier, byteToken,

View File

@@ -104,4 +104,11 @@ public final class FaceAuthenticator extends IBiometricAuthenticator.Stub {
public long getAuthenticatorId(int callingUserId) throws RemoteException {
return mFaceService.getAuthenticatorId(mSensorId, callingUserId);
}
@Override
public void resetLockout(IBinder token, String opPackageName, int userId,
byte[] hardwareAuthToken) throws RemoteException {
mFaceService.resetLockout(token, mSensorId, userId, hardwareAuthToken,
opPackageName);
}
}

View File

@@ -495,6 +495,15 @@ public class Sensor {
Slog.w(mTag, "setTestHalEnabled: " + enabled);
if (enabled != mTestHalEnabled) {
// The framework should retrieve a new session from the HAL.
try {
if (mCurrentSession != null && mCurrentSession.mSession != null) {
// TODO(181984005): This should be scheduled instead of directly invoked
Slog.d(mTag, "Closing old session");
mCurrentSession.mSession.close(888 /* cookie */);
}
} catch (RemoteException e) {
Slog.e(mTag, "RemoteException", e);
}
mCurrentSession = null;
}
mTestHalEnabled = enabled;
@@ -519,6 +528,11 @@ public class Sensor {
proto.end(userToken);
}
proto.write(SensorStateProto.RESET_LOCKOUT_REQUIRES_HARDWARE_AUTH_TOKEN,
mSensorProperties.resetLockoutRequiresHardwareAuthToken);
proto.write(SensorStateProto.RESET_LOCKOUT_REQUIRES_CHALLENGE,
mSensorProperties.resetLockoutRequiresChallenge);
proto.end(sensorToken);
}

View File

@@ -798,6 +798,11 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
proto.end(userToken);
}
proto.write(SensorStateProto.RESET_LOCKOUT_REQUIRES_HARDWARE_AUTH_TOKEN,
mSensorProperties.resetLockoutRequiresHardwareAuthToken);
proto.write(SensorStateProto.RESET_LOCKOUT_REQUIRES_CHALLENGE,
mSensorProperties.resetLockoutRequiresChallenge);
proto.end(sensorToken);
}

View File

@@ -105,4 +105,11 @@ public final class FingerprintAuthenticator extends IBiometricAuthenticator.Stub
public long getAuthenticatorId(int callingUserId) throws RemoteException {
return mFingerprintService.getAuthenticatorId(mSensorId, callingUserId);
}
@Override
public void resetLockout(IBinder token, String opPackageName, int userId,
byte[] hardwareAuthToken) throws RemoteException {
mFingerprintService.resetLockout(token, mSensorId, userId, hardwareAuthToken,
opPackageName);
}
}

View File

@@ -475,6 +475,15 @@ class Sensor {
Slog.w(mTag, "setTestHalEnabled: " + enabled);
if (enabled != mTestHalEnabled) {
// The framework should retrieve a new session from the HAL.
try {
if (mCurrentSession != null && mCurrentSession.mSession != null) {
// TODO(181984005): This should be scheduled instead of directly invoked
Slog.d(mTag, "Closing old session");
mCurrentSession.mSession.close(999 /* cookie */);
}
} catch (RemoteException e) {
Slog.e(mTag, "RemoteException", e);
}
mCurrentSession = null;
}
mTestHalEnabled = enabled;
@@ -499,6 +508,11 @@ class Sensor {
proto.end(userToken);
}
proto.write(SensorStateProto.RESET_LOCKOUT_REQUIRES_HARDWARE_AUTH_TOKEN,
mSensorProperties.resetLockoutRequiresHardwareAuthToken);
proto.write(SensorStateProto.RESET_LOCKOUT_REQUIRES_CHALLENGE,
mSensorProperties.resetLockoutRequiresChallenge);
proto.end(sensorToken);
}

View File

@@ -761,6 +761,11 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
proto.end(userToken);
}
proto.write(SensorStateProto.RESET_LOCKOUT_REQUIRES_HARDWARE_AUTH_TOKEN,
mSensorProperties.resetLockoutRequiresHardwareAuthToken);
proto.write(SensorStateProto.RESET_LOCKOUT_REQUIRES_CHALLENGE,
mSensorProperties.resetLockoutRequiresChallenge);
proto.end(sensorToken);
}

View File

@@ -95,4 +95,9 @@ public final class IrisAuthenticator extends IBiometricAuthenticator.Stub {
public long getAuthenticatorId(int callingUserId) throws RemoteException {
return 0;
}
@Override
public void resetLockout(IBinder token, String opPackageName, int userId,
byte[] hardwareAuthToken) throws RemoteException {
}
}