2018-01-12 01:53:49 +08:00
|
|
|
package com.topjohnwu.magisk.utils;
|
|
|
|
|
|
|
|
import android.annotation.TargetApi;
|
2018-09-29 01:57:51 -04:00
|
|
|
import android.app.Activity;
|
2018-01-12 01:53:49 +08:00
|
|
|
import android.app.KeyguardManager;
|
2018-09-29 01:57:51 -04:00
|
|
|
import android.content.res.Resources;
|
|
|
|
import android.content.res.TypedArray;
|
|
|
|
import android.graphics.Color;
|
|
|
|
import android.graphics.drawable.Drawable;
|
2018-01-12 01:53:49 +08:00
|
|
|
import android.hardware.fingerprint.FingerprintManager;
|
|
|
|
import android.os.Build;
|
|
|
|
import android.os.CancellationSignal;
|
|
|
|
import android.security.keystore.KeyGenParameterSpec;
|
|
|
|
import android.security.keystore.KeyPermanentlyInvalidatedException;
|
|
|
|
import android.security.keystore.KeyProperties;
|
2018-09-29 01:57:51 -04:00
|
|
|
import android.view.Gravity;
|
|
|
|
import android.widget.Toast;
|
2018-01-12 01:53:49 +08:00
|
|
|
|
2019-01-30 03:10:12 -05:00
|
|
|
import com.topjohnwu.magisk.App;
|
|
|
|
import com.topjohnwu.magisk.Config;
|
|
|
|
import com.topjohnwu.magisk.Const;
|
2018-09-29 01:57:51 -04:00
|
|
|
import com.topjohnwu.magisk.R;
|
|
|
|
import com.topjohnwu.magisk.components.CustomAlertDialog;
|
2018-01-12 01:53:49 +08:00
|
|
|
|
|
|
|
import java.security.KeyStore;
|
|
|
|
|
|
|
|
import javax.crypto.Cipher;
|
|
|
|
import javax.crypto.KeyGenerator;
|
|
|
|
import javax.crypto.SecretKey;
|
|
|
|
|
|
|
|
@TargetApi(Build.VERSION_CODES.M)
|
2018-01-13 05:49:47 +08:00
|
|
|
public abstract class FingerprintHelper {
|
2018-01-12 01:53:49 +08:00
|
|
|
|
|
|
|
private FingerprintManager manager;
|
|
|
|
private Cipher cipher;
|
|
|
|
private CancellationSignal cancel;
|
|
|
|
|
2018-10-28 00:54:56 -04:00
|
|
|
public static boolean useFingerPrint() {
|
2019-01-21 15:49:03 -05:00
|
|
|
boolean fp = Config.get(Config.Key.SU_FINGERPRINT);
|
2018-10-28 00:54:56 -04:00
|
|
|
if (fp && !canUseFingerprint()) {
|
2019-01-21 15:49:03 -05:00
|
|
|
Config.set(Config.Key.SU_FINGERPRINT, false);
|
2018-10-28 00:54:56 -04:00
|
|
|
fp = false;
|
|
|
|
}
|
|
|
|
return fp;
|
|
|
|
}
|
|
|
|
|
2018-01-12 01:53:49 +08:00
|
|
|
public static boolean canUseFingerprint() {
|
|
|
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M)
|
|
|
|
return false;
|
2018-12-13 04:35:50 -05:00
|
|
|
KeyguardManager km = App.self.getSystemService(KeyguardManager.class);
|
|
|
|
FingerprintManager fm = App.self.getSystemService(FingerprintManager.class);
|
2018-01-12 11:23:47 +08:00
|
|
|
return km.isKeyguardSecure() && fm != null && fm.isHardwareDetected() && fm.hasEnrolledFingerprints();
|
2018-01-12 01:53:49 +08:00
|
|
|
}
|
|
|
|
|
2018-09-29 01:57:51 -04:00
|
|
|
public static void showAuthDialog(Activity activity, Runnable onSuccess) {
|
|
|
|
CustomAlertDialog dialog = new CustomAlertDialog(activity);
|
|
|
|
CustomAlertDialog.ViewHolder vh = dialog.getViewHolder();
|
|
|
|
try {
|
|
|
|
FingerprintHelper helper = new FingerprintHelper() {
|
|
|
|
@Override
|
|
|
|
public void onAuthenticationError(int errorCode, CharSequence errString) {
|
|
|
|
vh.messageView.setTextColor(Color.RED);
|
|
|
|
vh.messageView.setText(errString);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onAuthenticationHelp(int helpCode, CharSequence helpString) {
|
|
|
|
vh.messageView.setTextColor(Color.RED);
|
|
|
|
vh.messageView.setText(helpString);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onAuthenticationFailed() {
|
|
|
|
vh.messageView.setTextColor(Color.RED);
|
|
|
|
vh.messageView.setText(R.string.auth_fail);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) {
|
|
|
|
dialog.dismiss();
|
|
|
|
onSuccess.run();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
Drawable fingerprint = activity.getResources().getDrawable(R.drawable.ic_fingerprint);
|
|
|
|
fingerprint.setBounds(0, 0, Utils.dpInPx(50), Utils.dpInPx(50));
|
|
|
|
Resources.Theme theme = activity.getTheme();
|
|
|
|
TypedArray ta = theme.obtainStyledAttributes(new int[] {R.attr.imageColorTint});
|
|
|
|
fingerprint.setTint(ta.getColor(0, Color.GRAY));
|
|
|
|
ta.recycle();
|
|
|
|
vh.messageView.setCompoundDrawables(null, null, null, fingerprint);
|
|
|
|
vh.messageView.setCompoundDrawablePadding(Utils.dpInPx(20));
|
|
|
|
vh.messageView.setGravity(Gravity.CENTER);
|
|
|
|
dialog.setMessage(R.string.auth_fingerprint)
|
|
|
|
.setNegativeButton(R.string.close, (d, w) -> helper.cancel())
|
|
|
|
.setOnCancelListener(d -> helper.cancel())
|
|
|
|
.show();
|
|
|
|
helper.authenticate();
|
|
|
|
} catch (Exception e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
Utils.toast(R.string.auth_fail, Toast.LENGTH_SHORT);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2018-01-12 01:53:49 +08:00
|
|
|
protected FingerprintHelper() throws Exception {
|
|
|
|
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
|
2018-12-13 04:35:50 -05:00
|
|
|
manager = App.self.getSystemService(FingerprintManager.class);
|
2018-01-12 01:53:49 +08:00
|
|
|
cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/"
|
|
|
|
+ KeyProperties.BLOCK_MODE_CBC + "/"
|
|
|
|
+ KeyProperties.ENCRYPTION_PADDING_PKCS7);
|
|
|
|
keyStore.load(null);
|
|
|
|
SecretKey key = (SecretKey) keyStore.getKey(Const.SU_KEYSTORE_KEY, null);
|
|
|
|
if (key == null) {
|
|
|
|
key = generateKey();
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
cipher.init(Cipher.ENCRYPT_MODE, key);
|
|
|
|
} catch (KeyPermanentlyInvalidatedException e) {
|
|
|
|
// Only happens on Marshmallow
|
|
|
|
key = generateKey();
|
|
|
|
cipher.init(Cipher.ENCRYPT_MODE, key);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-13 05:49:47 +08:00
|
|
|
public abstract void onAuthenticationError(int errorCode, CharSequence errString);
|
|
|
|
|
|
|
|
public abstract void onAuthenticationHelp(int helpCode, CharSequence helpString);
|
|
|
|
|
|
|
|
public abstract void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result);
|
|
|
|
|
|
|
|
public abstract void onAuthenticationFailed();
|
|
|
|
|
2018-08-22 15:05:00 -04:00
|
|
|
public void authenticate() {
|
2018-01-12 01:53:49 +08:00
|
|
|
cancel = new CancellationSignal();
|
|
|
|
FingerprintManager.CryptoObject cryptoObject = new FingerprintManager.CryptoObject(cipher);
|
2018-08-22 15:05:00 -04:00
|
|
|
manager.authenticate(cryptoObject, cancel, 0, new Callback(), null);
|
2018-01-12 01:53:49 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
public void cancel() {
|
|
|
|
if (cancel != null)
|
|
|
|
cancel.cancel();
|
|
|
|
}
|
|
|
|
|
|
|
|
private SecretKey generateKey() throws Exception {
|
|
|
|
KeyGenerator keygen = KeyGenerator
|
|
|
|
.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
|
|
|
|
KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(
|
|
|
|
Const.SU_KEYSTORE_KEY,
|
|
|
|
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
|
|
|
|
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
|
|
|
|
.setUserAuthenticationRequired(true)
|
|
|
|
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7);
|
|
|
|
|
|
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
|
|
|
builder.setInvalidatedByBiometricEnrollment(false);
|
|
|
|
}
|
|
|
|
keygen.init(builder.build());
|
|
|
|
return keygen.generateKey();
|
|
|
|
}
|
2018-08-22 15:05:00 -04:00
|
|
|
|
|
|
|
private class Callback extends FingerprintManager.AuthenticationCallback {
|
|
|
|
@Override
|
|
|
|
public void onAuthenticationError(int errorCode, CharSequence errString) {
|
|
|
|
FingerprintHelper.this.onAuthenticationError(errorCode, errString);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onAuthenticationHelp(int helpCode, CharSequence helpString) {
|
|
|
|
FingerprintHelper.this.onAuthenticationHelp(helpCode, helpString);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) {
|
|
|
|
FingerprintHelper.this.onAuthenticationSucceeded(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onAuthenticationFailed() {
|
|
|
|
FingerprintHelper.this.onAuthenticationFailed();
|
|
|
|
}
|
|
|
|
}
|
2018-01-12 01:53:49 +08:00
|
|
|
}
|