Implement biometric encryption (WIP)
This commit is contained in:
parent
9259f669cf
commit
8fbf44b75a
@ -69,8 +69,6 @@ public class MainActivity extends BaseActivity {
|
||||
|
||||
setLocale(SettingsUtil.getLocale(this));
|
||||
|
||||
OTPDatabase.promptLoadDatabase(this, this::launchApp, this::finishAffinity);
|
||||
|
||||
startQRCodeScan = registerForActivityResult(new QRScannerContract(), obj -> {
|
||||
if(obj == null) return; // Cancelled
|
||||
|
||||
@ -85,6 +83,8 @@ public class MainActivity extends BaseActivity {
|
||||
for(OTPData d : obj.getData()) frag.addOTP(d);
|
||||
}
|
||||
});
|
||||
|
||||
OTPDatabase.promptLoadDatabase(this, this::launchApp, this::finishAffinity);
|
||||
}
|
||||
|
||||
public void setLocale(Locale locale) {
|
||||
|
@ -59,7 +59,8 @@ public class OTPListAdapter extends RecyclerView.Adapter<OTPListItem> {
|
||||
holder.getBinding().progress.setVisibility(data.getType() == OTPType.TOTP ? View.VISIBLE : View.INVISIBLE);
|
||||
|
||||
holder.getBinding().getRoot().setOnClickListener(view -> {
|
||||
Log.i("CLICKED", "CLICKED: " + view.isClickable());
|
||||
if(!view.isClickable()) return;
|
||||
|
||||
if(data.getType() != OTPType.HOTP) return;
|
||||
|
||||
// Click delay for HOTP
|
||||
|
@ -5,6 +5,9 @@ import static androidx.biometric.BiometricManager.Authenticators.DEVICE_CREDENTI
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.security.KeyStoreParameter;
|
||||
import android.security.keystore.KeyGenParameterSpec;
|
||||
import android.security.keystore.KeyProtection;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
@ -24,8 +27,14 @@ import com.cringe_studios.cringe_authenticator.util.OTPDatabaseException;
|
||||
import com.cringe_studios.cringe_authenticator.util.SettingsUtil;
|
||||
import com.cringe_studios.cringe_authenticator.util.ThemeUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.KeyStore;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import javax.crypto.KeyGenerator;
|
||||
import javax.crypto.SecretKey;
|
||||
|
||||
public class UnlockActivity extends AppCompatActivity {
|
||||
@ -41,24 +50,33 @@ public class UnlockActivity extends AppCompatActivity {
|
||||
ThemeUtil.loadTheme(this);
|
||||
|
||||
if(!SettingsUtil.isDatabaseEncrypted(this)) {
|
||||
launchApp();
|
||||
success();
|
||||
return;
|
||||
}
|
||||
|
||||
binding = ActivityUnlockBinding.inflate(getLayoutInflater());
|
||||
setContentView(binding.getRoot());
|
||||
|
||||
try {
|
||||
KeyStore ks = KeyStore.getInstance("AndroidKeyStore");
|
||||
ks.load(null);
|
||||
SecretKey key = KeyGenerator.getInstance("AES").generateKey();
|
||||
ks.setEntry("amogus", new KeyStore.SecretKeyEntry(key), new KeyStoreParameter.Builder(this).build());
|
||||
} catch (KeyStoreException | CertificateException | IOException | NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
if(SettingsUtil.isBiometricLock(this)) {
|
||||
Executor executor = ContextCompat.getMainExecutor(this);
|
||||
BiometricPrompt prompt = new BiometricPrompt(this, executor, new BiometricPrompt.AuthenticationCallback() {
|
||||
@Override
|
||||
public void onAuthenticationError(int errorCode, @NonNull CharSequence errString) {
|
||||
//finishAffinity();
|
||||
failure();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAuthenticationSucceeded(@NonNull BiometricPrompt.AuthenticationResult result) {
|
||||
launchApp();
|
||||
success();
|
||||
}
|
||||
});
|
||||
|
||||
@ -78,7 +96,7 @@ public class UnlockActivity extends AppCompatActivity {
|
||||
//prompt.authenticate(info);
|
||||
});
|
||||
}else {
|
||||
launchApp();
|
||||
success();
|
||||
}
|
||||
}
|
||||
|
||||
@ -93,7 +111,7 @@ public class UnlockActivity extends AppCompatActivity {
|
||||
try {
|
||||
SecretKey key = Crypto.generateKey(SettingsUtil.getCryptoParameters(this), password);
|
||||
OTPDatabase.loadDatabase(this, key);
|
||||
launchApp();
|
||||
success();
|
||||
}catch(CryptoException e) {
|
||||
DialogUtil.showErrorDialog(this, "Failed to load database: Invalid password or database corrupted", this::failure);
|
||||
} catch (OTPDatabaseException e) {
|
||||
@ -102,7 +120,7 @@ public class UnlockActivity extends AppCompatActivity {
|
||||
});
|
||||
}
|
||||
|
||||
private void launchApp() {
|
||||
private void success() {
|
||||
if(getIntent() != null && getIntent().hasExtra("contract")) {
|
||||
setResult(RESULT_OK);
|
||||
finish();
|
||||
|
@ -2,11 +2,14 @@ package com.cringe_studios.cringe_authenticator.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.util.Base64;
|
||||
|
||||
import com.cringe_studios.cringe_authenticator.R;
|
||||
import com.cringe_studios.cringe_authenticator.crypto.CryptoParameters;
|
||||
import com.google.gson.Gson;
|
||||
|
||||
import org.bouncycastle.jcajce.provider.symmetric.ARC4;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
@ -85,28 +88,6 @@ public class SettingsUtil {
|
||||
.apply();
|
||||
}*/
|
||||
|
||||
public static boolean isDatabaseEncrypted(Context ctx) {
|
||||
return ctx.getSharedPreferences(GROUPS_PREFS_NAME, Context.MODE_PRIVATE).getBoolean("encryption", false);
|
||||
}
|
||||
|
||||
public static void enableEncryption(Context ctx, CryptoParameters parameters) {
|
||||
ctx.getSharedPreferences(GROUPS_PREFS_NAME, Context.MODE_PRIVATE).edit()
|
||||
.putBoolean("encryption", true)
|
||||
.putString("encryption.parameters", GSON.toJson(parameters))
|
||||
.apply();
|
||||
}
|
||||
|
||||
public static void disableEncryption(Context ctx) {
|
||||
ctx.getSharedPreferences(GROUPS_PREFS_NAME, Context.MODE_PRIVATE).edit()
|
||||
.putBoolean("encryption", false)
|
||||
.remove("encryption.parameters")
|
||||
.apply();
|
||||
}
|
||||
|
||||
public static CryptoParameters getCryptoParameters(Context ctx) {
|
||||
return GSON.fromJson(ctx.getSharedPreferences(GROUPS_PREFS_NAME, Context.MODE_PRIVATE).getString("encryption.parameters", "{}"), CryptoParameters.class);
|
||||
}
|
||||
|
||||
public static String getGroupName(Context ctx, String group) {
|
||||
return ctx.getSharedPreferences(GROUPS_PREFS_NAME, Context.MODE_PRIVATE).getString("group." + group + ".name", group);
|
||||
}
|
||||
@ -124,6 +105,57 @@ public class SettingsUtil {
|
||||
.apply();
|
||||
}
|
||||
|
||||
public static boolean isDatabaseEncrypted(Context ctx) {
|
||||
return ctx.getSharedPreferences(GENERAL_PREFS_NAME, Context.MODE_PRIVATE).getBoolean("encryption", false);
|
||||
}
|
||||
|
||||
public static void enableEncryption(Context ctx, CryptoParameters parameters) {
|
||||
ctx.getSharedPreferences(GENERAL_PREFS_NAME, Context.MODE_PRIVATE).edit()
|
||||
.putBoolean("encryption", true)
|
||||
.putString("encryption.parameters", GSON.toJson(parameters))
|
||||
.apply();
|
||||
}
|
||||
|
||||
public static void disableEncryption(Context ctx) {
|
||||
ctx.getSharedPreferences(GENERAL_PREFS_NAME, Context.MODE_PRIVATE).edit()
|
||||
.putBoolean("encryption", false)
|
||||
.remove("encryption.parameters")
|
||||
.apply();
|
||||
}
|
||||
|
||||
public static CryptoParameters getCryptoParameters(Context ctx) {
|
||||
return GSON.fromJson(ctx.getSharedPreferences(GENERAL_PREFS_NAME, Context.MODE_PRIVATE).getString("encryption.parameters", "{}"), CryptoParameters.class);
|
||||
}
|
||||
|
||||
public static void enableBiometricEncryption(Context ctx, byte[] encryptedBiometricKey) {
|
||||
ctx.getSharedPreferences(GENERAL_PREFS_NAME, Context.MODE_PRIVATE).edit()
|
||||
.putBoolean("encryption.biometric", true)
|
||||
.putString("encryption.biometric.key", Base64.encodeToString(encryptedBiometricKey, Base64.DEFAULT))
|
||||
.apply();
|
||||
}
|
||||
|
||||
public static void disableBiometricEncryption(Context ctx) {
|
||||
ctx.getSharedPreferences(GENERAL_PREFS_NAME, Context.MODE_PRIVATE).edit()
|
||||
.putBoolean("encryption.biometric", false)
|
||||
.remove("encryption.biometric.key")
|
||||
.apply();
|
||||
}
|
||||
|
||||
public static byte[] getEncryptedBiometricKey(Context ctx) {
|
||||
String encoded = ctx.getSharedPreferences(GENERAL_PREFS_NAME, Context.MODE_PRIVATE).getString("encryption.biometric.key", null);
|
||||
if(encoded == null) return null;
|
||||
return Base64.decode(encoded, Base64.DEFAULT);
|
||||
}
|
||||
|
||||
/*public static void setBiometricLock(Context ctx, boolean biometricLock) {
|
||||
SharedPreferences prefs = ctx.getSharedPreferences(GENERAL_PREFS_NAME, Context.MODE_PRIVATE);
|
||||
prefs.edit().putBoolean("biometricLock", biometricLock).apply();
|
||||
}
|
||||
|
||||
public static boolean isBiometricLock(Context ctx) {
|
||||
return ctx.getSharedPreferences(GENERAL_PREFS_NAME, Context.MODE_PRIVATE).getBoolean("biometricLock", true);
|
||||
}*/
|
||||
|
||||
public static void setEnableIntroVideo(Context ctx, boolean enableIntroVideo) {
|
||||
SharedPreferences prefs = ctx.getSharedPreferences(GENERAL_PREFS_NAME, Context.MODE_PRIVATE);
|
||||
prefs.edit().putBoolean("enableIntroVideo", enableIntroVideo).apply();
|
||||
@ -133,15 +165,6 @@ public class SettingsUtil {
|
||||
return ctx.getSharedPreferences(GENERAL_PREFS_NAME, Context.MODE_PRIVATE).getBoolean("enableIntroVideo", true);
|
||||
}
|
||||
|
||||
public static void setBiometricLock(Context ctx, boolean biometricLock) {
|
||||
SharedPreferences prefs = ctx.getSharedPreferences(GENERAL_PREFS_NAME, Context.MODE_PRIVATE);
|
||||
prefs.edit().putBoolean("biometricLock", biometricLock).apply();
|
||||
}
|
||||
|
||||
public static boolean isBiometricLock(Context ctx) {
|
||||
return ctx.getSharedPreferences(GENERAL_PREFS_NAME, Context.MODE_PRIVATE).getBoolean("biometricLock", true);
|
||||
}
|
||||
|
||||
public static void setTheme(Context ctx, String theme) {
|
||||
SharedPreferences prefs = ctx.getSharedPreferences(GENERAL_PREFS_NAME, Context.MODE_PRIVATE);
|
||||
prefs.edit().putString("theme", theme).apply();
|
||||
|
Loading…
Reference in New Issue
Block a user