diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2de4310..058bc97 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -4,6 +4,7 @@ package="com.edison.fingerface"> + FingerprintManager.CryptoObject(cipher) + mac != null -> FingerprintManager.CryptoObject(mac) + signature != null -> FingerprintManager.CryptoObject(signature) + else -> null + } + + override fun getCryptoObject(): FingerprintManager.CryptoObject? { + return result.cryptoObject?.toLegacy() + } +} diff --git a/app/src/main/java/android/hardware/fingerprint/MyFingerprintManager.java b/app/src/main/java/android/hardware/fingerprint/MyFingerprintManager.java deleted file mode 100644 index fd028d7..0000000 --- a/app/src/main/java/android/hardware/fingerprint/MyFingerprintManager.java +++ /dev/null @@ -1,175 +0,0 @@ -package android.hardware.fingerprint; - -import android.content.Context; -import android.content.DialogInterface; -import android.hardware.biometrics.BiometricPrompt; -import android.os.CancellationSignal; -import android.os.Handler; -import android.security.keystore.KeyGenParameterSpec; -import android.security.keystore.KeyPermanentlyInvalidatedException; -import android.security.keystore.KeyProperties; -import android.util.Log; - -import javax.crypto.Cipher; -import javax.crypto.KeyGenerator; -import javax.crypto.NoSuchPaddingException; -import javax.crypto.SecretKey; -import java.io.IOException; -import java.security.*; -import java.security.cert.CertificateException; - -public class MyFingerprintManager extends FingerprintManager { - - private Context mContext; - - private KeyGenerator keyGenerator; - private KeyStore keyStore; - private Cipher cipher; - - private String KEY_NAME = "MyFingerprintManager"; - - public void SetContext(Context context) { - mContext = context; - } - - @Override - public boolean isHardwareDetected() { - - Log.d("XposedHandler", "isHardwareDetected"); - return true; - } - - @Override - public boolean hasEnrolledFingerprints() { - Log.d("XposedHandler", "hasEnrolledFingerprints"); - return true; - } - - @Override - public void authenticate(final CryptoObject crypto, CancellationSignal cancel, - int flags, final AuthenticationCallback callback, Handler handler) { - - BiometricPrompt biometricPrompt = new BiometricPrompt.Builder(mContext).setTitle("FaceID").setNegativeButton(mContext.getString(android.R.string.cancel), mContext.getMainExecutor(), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - dialog.dismiss(); - } - }).build(); - - BiometricPrompt.CryptoObject c = null; - - if (crypto == null) { - - generateKey(); - initCipher(); - c = new BiometricPrompt.CryptoObject(cipher); - - } else if (crypto.getCipher() != null) { - c = new BiometricPrompt.CryptoObject(crypto.getCipher()); - } else if (crypto.getMac() != null) { - c = new BiometricPrompt.CryptoObject(crypto.getMac()); - } else if (crypto.getSignature() != null) { - c = new BiometricPrompt.CryptoObject(crypto.getSignature()); - } - - biometricPrompt.authenticate(c, cancel, mContext.getMainExecutor(), new BiometricPrompt.AuthenticationCallback() { - @Override - public void onAuthenticationError(int errorCode, CharSequence errString) { - callback.onAuthenticationError(errorCode, errString); - } - - @Override - public void onAuthenticationHelp(int helpCode, CharSequence helpString) { - callback.onAuthenticationHelp(helpCode, helpString); - } - - @Override - public void onAuthenticationSucceeded(BiometricPrompt.AuthenticationResult result) { - - BiometricPrompt.CryptoObject BioCrypto = result.getCryptoObject(); - - CryptoObject c = null; - - if (BioCrypto.getCipher() != null) { - c = new CryptoObject(BioCrypto.getCipher()); - } else if (BioCrypto.getMac() != null) { - c = new CryptoObject(BioCrypto.getMac()); - } else if (BioCrypto.getSignature() != null) { - c = new CryptoObject(BioCrypto.getSignature()); - } - - AuthenticationResult fingerprintResult = new AuthenticationResult(); - - if (crypto != null) { - fingerprintResult = new MyAuthenticationResult(); - ((MyAuthenticationResult) fingerprintResult).setCryptoObject(c); - } - - callback.onAuthenticationSucceeded(fingerprintResult); - } - - @Override - public void onAuthenticationFailed() { - callback.onAuthenticationFailed(); - } - }); - } - - - private void generateKey() { - try { - - keyStore = KeyStore.getInstance("AndroidKeyStore"); - keyStore.load(null); - - keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore"); - keyGenerator.init(new - KeyGenParameterSpec.Builder(KEY_NAME, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) - .setBlockModes(KeyProperties.BLOCK_MODE_CBC) - .setUserAuthenticationRequired(true) - .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7) - .build()); - - keyGenerator.generateKey(); - - } catch (KeyStoreException - | NoSuchAlgorithmException - | NoSuchProviderException - | InvalidAlgorithmParameterException - | CertificateException - | IOException exc) { - exc.printStackTrace(); - } - } - - private boolean initCipher() { - try { - cipher = Cipher.getInstance( - KeyProperties.KEY_ALGORITHM_AES + "/" - + KeyProperties.BLOCK_MODE_CBC + "/" - + KeyProperties.ENCRYPTION_PADDING_PKCS7); - - } catch (NoSuchAlgorithmException | - NoSuchPaddingException e) { - throw new RuntimeException("Failed to get Cipher", e); - } - - try { - keyStore.load(null); - SecretKey key = (SecretKey) keyStore.getKey(KEY_NAME, - null); - cipher.init(Cipher.ENCRYPT_MODE, key); - return true; - - - } catch (KeyPermanentlyInvalidatedException e) { - return false; - - } catch (KeyStoreException | CertificateException - | UnrecoverableKeyException | IOException - | NoSuchAlgorithmException | InvalidKeyException e) { - - throw new RuntimeException("Failed to init Cipher", e); - } - } -} diff --git a/app/src/main/java/android/hardware/fingerprint/MyFingerprintManager.kt b/app/src/main/java/android/hardware/fingerprint/MyFingerprintManager.kt new file mode 100644 index 0000000..f444b8b --- /dev/null +++ b/app/src/main/java/android/hardware/fingerprint/MyFingerprintManager.kt @@ -0,0 +1,75 @@ +@file:Suppress("DEPRECATION") + +package android.hardware.fingerprint + +import android.content.Context +import android.content.DialogInterface +import android.hardware.biometrics.BiometricManager +import android.hardware.biometrics.BiometricPrompt +import android.os.CancellationSignal +import android.os.Handler +import android.util.Log +import com.edison.fingerface.HandlerExecutor + +class MyFingerprintManager(private val context: Context) : FingerprintManager() { + + private val manager = context.getSystemService(BiometricManager::class.java)!! + + override fun isHardwareDetected(): Boolean { + Log.d(TAG, "isHardwareDetected") + return when (manager.canAuthenticate()) { + BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED, + BiometricManager.BIOMETRIC_SUCCESS -> true + BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE, + BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE -> false + else -> false + } + } + + override fun hasEnrolledFingerprints(): Boolean { + Log.d(TAG, "hasEnrolledFingerprints") + return when (manager.canAuthenticate()) { + BiometricManager.BIOMETRIC_SUCCESS -> true + BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED, + BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE, + BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE -> false + else -> false + } + } + + private fun CryptoObject.toBio() = when { + cipher != null -> BiometricPrompt.CryptoObject(cipher) + mac != null -> BiometricPrompt.CryptoObject(mac) + signature != null -> BiometricPrompt.CryptoObject(signature) + else -> null + } + + override fun authenticate( + crypto: CryptoObject?, cancel: CancellationSignal?, + flags: Int, legacyCallback: AuthenticationCallback, handler: Handler? + ) { + Log.d(TAG, "authenticate") + + val bioCrypto = crypto?.toBio() + val cancelSignal = cancel ?: CancellationSignal() + val executor = handler?.let { HandlerExecutor(it) } ?: context.mainExecutor + val callback = MyAuthenticationCallback(legacyCallback) + + val biometricPrompt = BiometricPrompt.Builder(context).setTitle("Fingerface") + .setNegativeButton( + context.getString(android.R.string.cancel), + executor, + DialogInterface.OnClickListener { dialog, _ -> dialog.dismiss() }) + .build() + + if (bioCrypto == null) { + biometricPrompt.authenticate(cancelSignal, executor, callback) + } else { + biometricPrompt.authenticate(bioCrypto, cancelSignal, executor, callback) + } + } + + companion object { + const val TAG = "Fingerface" + } +} diff --git a/app/src/main/java/com/edison/fingerface/HandlerExecutor.kt b/app/src/main/java/com/edison/fingerface/HandlerExecutor.kt new file mode 100644 index 0000000..2ed0ba9 --- /dev/null +++ b/app/src/main/java/com/edison/fingerface/HandlerExecutor.kt @@ -0,0 +1,13 @@ +package com.edison.fingerface + +import android.os.Handler +import java.util.concurrent.Executor +import java.util.concurrent.RejectedExecutionException + +class HandlerExecutor(private val handler: Handler) : Executor { + override fun execute(command: Runnable) { + if (!handler.post(command)) { + throw RejectedExecutionException("$handler is shutting down"); + } + } +} diff --git a/app/src/main/java/com/edison/fingerface/ModFingerface.kt b/app/src/main/java/com/edison/fingerface/ModFingerface.kt deleted file mode 100644 index 34287dc..0000000 --- a/app/src/main/java/com/edison/fingerface/ModFingerface.kt +++ /dev/null @@ -1,36 +0,0 @@ -package com.edison.fingerface - -import android.content.Context -import android.content.pm.PackageManager -import android.content.res.XModuleResources -import android.hardware.fingerprint.FingerprintManager -import android.util.Log -import de.robv.android.xposed.XC_MethodHook -import de.robv.android.xposed.XSharedPreferences -import de.robv.android.xposed.XposedBridge -import android.hardware.fingerprint.MyFingerprintManager - - -class ModFingerface { - companion object { - @JvmStatic - fun initAndroid(prefs: XSharedPreferences, classLoader: ClassLoader, modRes: XModuleResources) { - val getSystemServiceHook = object : XC_MethodHook() { - override fun afterHookedMethod(param: MethodHookParam?) { - if (param!!.args[0].toString() == Context.FINGERPRINT_SERVICE) { - Log.d("XposedHandler", "Get Fingerpring service, call from:${param!!.method.name}, thisObj:${param!!.result}") - val mgr = MyFingerprintManager() - mgr.SetContext(param!!.thisObject as Context) - param!!.result = mgr - } - } - } - - XposedBridge.hookAllMethods( - Class.forName("android.app.ContextImpl"), - "getSystemService", - getSystemServiceHook - ) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/edison/fingerface/ModPackageManager.kt b/app/src/main/java/com/edison/fingerface/ModPackageManager.kt deleted file mode 100644 index cc7999b..0000000 --- a/app/src/main/java/com/edison/fingerface/ModPackageManager.kt +++ /dev/null @@ -1,46 +0,0 @@ -package com.edison.fingerface - -import android.content.Context -import android.content.pm.PackageManager -import android.content.res.XModuleResources -import android.hardware.fingerprint.FingerprintManager -import android.util.Log -import de.robv.android.xposed.XC_MethodHook -import de.robv.android.xposed.XSharedPreferences -import de.robv.android.xposed.XposedBridge -import android.hardware.fingerprint.MyFingerprintManager -import de.robv.android.xposed.XposedHelpers -import android.R.attr.classLoader - - -class ModPackageManagerService { - companion object { - @JvmStatic - fun initAndroid(prefs: XSharedPreferences, classLoader: ClassLoader, modRes: XModuleResources) { - - val hasSystemFeatureHook = object : XC_MethodHook() { - - override fun afterHookedMethod(param: MethodHookParam?) { - if (param!!.args[0].toString() == PackageManager.FEATURE_FINGERPRINT) { - param!!.result = true - } - } - } - - XposedHelpers.findAndHookMethod("android.app.ContextImpl", classLoader, - "getPackageManager", object : XC_MethodHook() { - @Throws(Throwable::class) - override fun afterHookedMethod(param: MethodHookParam?) { - if (param!!.result is PackageManager) { - val clazz = param.result.javaClass - val packageMethod = clazz.getDeclaredMethod("hasSystemFeature", String::class.java) - - XposedBridge.hookMethod(packageMethod, hasSystemFeatureHook) - - } - } - }) - - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/edison/fingerface/XposedHandler.java b/app/src/main/java/com/edison/fingerface/XposedHandler.java deleted file mode 100644 index 6d006f8..0000000 --- a/app/src/main/java/com/edison/fingerface/XposedHandler.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.edison.fingerface; - -import android.app.AndroidAppHelper; -import android.content.Context; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.content.pm.PermissionInfo; -import android.content.res.XModuleResources; -import android.content.res.XResources; -import android.util.Log; -import android.util.Pair; -import de.robv.android.xposed.*; -import de.robv.android.xposed.callbacks.XC_InitPackageResources; -import de.robv.android.xposed.callbacks.XC_LoadPackage; - -import java.util.ArrayList; -import java.util.List; - -public class XposedHandler implements IXposedHookZygoteInit, IXposedHookLoadPackage, IXposedHookInitPackageResources { - public static final String PACKAGE_NAME = XposedHandler.class.getPackage().getName(); - private static final String TAG = "Fingerface"; - - private static XSharedPreferences prefs; - public static XModuleResources modRes; - private static String MODULE_PATH = null; - - public static List> packageResources = new ArrayList<>(); - - @Override - public void initZygote(StartupParam startupParam) throws Throwable { - MODULE_PATH = startupParam.modulePath; - modRes = XModuleResources.createInstance(MODULE_PATH, null); - prefs = new XSharedPreferences(PACKAGE_NAME); - - } - - @Override - public void handleInitPackageResources(XC_InitPackageResources.InitPackageResourcesParam resparam) throws Throwable { - - if (resparam.packageName.equals("android")) { - return; - } - - XModuleResources modRes = XModuleResources.createInstance(MODULE_PATH, resparam.res); - - } - - @Override - public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) { - - if (!lpparam.processName.equals("android") && !lpparam.processName.equals("com.android.systemui")) { - ModFingerface.initAndroid(prefs, lpparam.classLoader, modRes); - ModPackageManagerService.initAndroid(prefs, lpparam.classLoader, modRes); - } - } - -} diff --git a/app/src/main/java/com/edison/fingerface/XposedHandler.kt b/app/src/main/java/com/edison/fingerface/XposedHandler.kt new file mode 100644 index 0000000..d4aa42c --- /dev/null +++ b/app/src/main/java/com/edison/fingerface/XposedHandler.kt @@ -0,0 +1,62 @@ +package com.edison.fingerface + +import android.content.Context +import android.content.pm.PackageManager +import android.content.res.XModuleResources +import android.hardware.fingerprint.MyFingerprintManager +import de.robv.android.xposed.* +import de.robv.android.xposed.callbacks.XC_LoadPackage + +class XposedHandler : IXposedHookZygoteInit, IXposedHookLoadPackage { + + private lateinit var modulePath: String + private lateinit var modRes: XModuleResources + private lateinit var prefs: XSharedPreferences + + override fun initZygote(startupParam: IXposedHookZygoteInit.StartupParam) { + modulePath = startupParam.modulePath + modRes = XModuleResources.createInstance(modulePath, null) + prefs = XSharedPreferences(BuildConfig.APPLICATION_ID) + + } + + override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam) { + if (lpparam.processName != "android" && lpparam.processName != "com.android.systemui") { + hookFingerprintService(lpparam.classLoader) + hookPackageManager(lpparam.classLoader) + } + } + + private fun hookFingerprintService(cl: ClassLoader) { + val getSystemServiceHook = object : XC_MethodHook() { + override fun afterHookedMethod(param: MethodHookParam) { + if (param.args[0].toString() == Context.FINGERPRINT_SERVICE) { + param.result = MyFingerprintManager(param.thisObject as Context) + } + } + } + + XposedHelpers.findAndHookMethod( + "android.app.ContextImpl", cl, + "getSystemService", String::class.java, + getSystemServiceHook + ) + } + + private fun hookPackageManager(cl: ClassLoader) { + val hasSystemFeatureHook = object : XC_MethodHook() { + override fun afterHookedMethod(param: MethodHookParam) { + if (param.args[0].toString() == PackageManager.FEATURE_FINGERPRINT) { + param.result = true + } + } + } + + XposedHelpers.findAndHookMethod( + "android.app.ApplicationPackageManager", cl, + "hasSystemFeature", String::class.java, + hasSystemFeatureHook + ) + } + +}