Biometric encryption

This commit is contained in:
MrLetsplay 2023-09-18 20:27:56 +02:00
parent 8fbf44b75a
commit ffba642fa0
Signed by: mr
SSH Key Fingerprint: SHA256:92jBH80vpXyaZHjaIl47pjRq+Yt7XGTArqQg1V7hSqg
12 changed files with 266 additions and 65 deletions

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="deploymentTargetDropDown">
<runningDeviceTargetSelectedWithDropDown>
<Target>
<type value="RUNNING_DEVICE_TARGET" />
<deviceKey>
<Key>
<type value="SERIAL_NUMBER" />
<value value="R38N50464FV" />
</Key>
</deviceKey>
</Target>
</runningDeviceTargetSelectedWithDropDown>
<timeTargetWasSelectedWithDropDown value="2023-09-18T17:20:49.048823275Z" />
</component>
</project>

View File

@ -10,8 +10,8 @@
<option name="gradleJvm" value="jbr-17" /> <option name="gradleJvm" value="jbr-17" />
<option name="modules"> <option name="modules">
<set> <set>
<option value="$PROJECT_DIR$" /> <option value="/mnt/sshd/Files/Desktop/testing/android/Cringe-Authenticator" />
<option value="$PROJECT_DIR$/app" /> <option value="/mnt/sshd/Files/Desktop/testing/android/Cringe-Authenticator/app" />
</set> </set>
</option> </option>
</GradleProjectSettings> </GradleProjectSettings>

View File

@ -74,7 +74,6 @@ dependencies {
implementation 'com.google.mlkit:barcode-scanning:17.1.0' implementation 'com.google.mlkit:barcode-scanning:17.1.0'
implementation 'com.google.code.gson:gson:2.8.9' implementation 'com.google.code.gson:gson:2.8.9'
def camerax_version = "1.2.3" def camerax_version = "1.2.3"
implementation "androidx.camera:camera-core:${camerax_version}" implementation "androidx.camera:camera-core:${camerax_version}"
implementation "androidx.camera:camera-camera2:${camerax_version}" implementation "androidx.camera:camera-camera2:${camerax_version}"

View File

@ -0,0 +1,26 @@
package com.cringe_studios.cringe_authenticator.crypto;
public class BiometricKey {
private final String id;
private final byte[] encryptedKey;
private final byte[] iv;
public BiometricKey(String id, byte[] encryptedKey, byte[] iv) {
this.id = id;
this.encryptedKey = encryptedKey;
this.iv = iv;
}
public String getId() {
return id;
}
public byte[] getEncryptedKey() {
return encryptedKey;
}
public byte[] getIV() {
return iv;
}
}

View File

@ -1,17 +1,37 @@
package com.cringe_studios.cringe_authenticator.crypto; package com.cringe_studios.cringe_authenticator.crypto;
import android.content.Context;
import android.os.Build;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
import android.util.Log;
import androidx.annotation.RequiresApi;
import com.cringe_studios.cringe_authenticator.util.OTPDatabase;
import com.cringe_studios.cringe_authenticator.util.SettingsUtil;
import org.bouncycastle.crypto.generators.Argon2BytesGenerator; import org.bouncycastle.crypto.generators.Argon2BytesGenerator;
import org.bouncycastle.crypto.params.Argon2Parameters; import org.bouncycastle.crypto.params.Argon2Parameters;
import java.io.IOException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException; import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException; import java.security.InvalidKeyException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.util.Arrays;
import java.util.UUID;
import javax.crypto.BadPaddingException; import javax.crypto.BadPaddingException;
import javax.crypto.Cipher; import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException; import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException; import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey; import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec; import javax.crypto.spec.GCMParameterSpec;
@ -19,6 +39,8 @@ import javax.crypto.spec.SecretKeySpec;
public class Crypto { public class Crypto {
private static final String KEY_STORE = "AndroidKeyStore";
public static byte[] generateHash(CryptoParameters parameters, String password) throws CryptoException { public static byte[] generateHash(CryptoParameters parameters, String password) throws CryptoException {
Argon2Parameters params = new Argon2Parameters.Builder() Argon2Parameters params = new Argon2Parameters.Builder()
.withVersion(parameters.getArgon2Version()) .withVersion(parameters.getArgon2Version())
@ -55,12 +77,16 @@ public class Crypto {
return generateBytes(parameters.getEncryptionIVLength()); return generateBytes(parameters.getEncryptionIVLength());
} }
public static byte[] encrypt(CryptoParameters parameters, byte[] bytes, SecretKey key) throws CryptoException { public static CryptoResult encryptWithResult(CryptoParameters parameters, byte[] bytes, SecretKey key, boolean useIV) throws CryptoException {
try { try {
Cipher cipher = Cipher.getInstance(parameters.getEncryptionAlgorithm()); Cipher cipher = Cipher.getInstance(parameters.getEncryptionAlgorithm());
GCMParameterSpec spec = new GCMParameterSpec(parameters.getEncryptionGCMTagLength() * 8, parameters.getIV()); GCMParameterSpec spec = new GCMParameterSpec(parameters.getEncryptionGCMTagLength() * 8, parameters.getIV());
cipher.init(Cipher.ENCRYPT_MODE, key, spec); if(useIV) {
return cipher.doFinal(bytes); cipher.init(Cipher.ENCRYPT_MODE, key, spec);
}else {
cipher.init(Cipher.ENCRYPT_MODE, key);
}
return new CryptoResult(cipher.doFinal(bytes), cipher.getIV());
}catch(NoSuchAlgorithmException | NoSuchPaddingException | }catch(NoSuchAlgorithmException | NoSuchPaddingException |
InvalidAlgorithmParameterException | InvalidKeyException | BadPaddingException | InvalidAlgorithmParameterException | InvalidKeyException | BadPaddingException |
IllegalBlockSizeException e) { IllegalBlockSizeException e) {
@ -68,10 +94,14 @@ public class Crypto {
} }
} }
public static byte[] decrypt(CryptoParameters parameters, byte[] bytes, SecretKey key) throws CryptoException { public static byte[] encrypt(CryptoParameters parameters, byte[] bytes, SecretKey key) throws CryptoException {
return encryptWithResult(parameters, bytes, key, true).getEncrypted();
}
public static byte[] decrypt(CryptoParameters parameters, byte[] bytes, SecretKey key, byte[] overrideIV) throws CryptoException {
try { try {
Cipher cipher = Cipher.getInstance(parameters.getEncryptionAlgorithm()); Cipher cipher = Cipher.getInstance(parameters.getEncryptionAlgorithm());
GCMParameterSpec spec = new GCMParameterSpec(parameters.getEncryptionGCMTagLength() * 8, parameters.getIV()); GCMParameterSpec spec = new GCMParameterSpec(parameters.getEncryptionGCMTagLength() * 8, overrideIV != null ? overrideIV : parameters.getIV());
cipher.init(Cipher.DECRYPT_MODE, key, spec); cipher.init(Cipher.DECRYPT_MODE, key, spec);
return cipher.doFinal(bytes); return cipher.doFinal(bytes);
}catch(NoSuchAlgorithmException | NoSuchPaddingException | }catch(NoSuchAlgorithmException | NoSuchPaddingException |
@ -81,4 +111,53 @@ public class Crypto {
} }
} }
public static byte[] decrypt(CryptoParameters parameters, byte[] bytes, SecretKey key) throws CryptoException {
return decrypt(parameters, bytes, key, null);
}
@RequiresApi(api = Build.VERSION_CODES.M)
public static BiometricKey createBiometricKey(CryptoParameters parameters) throws CryptoException {
try {
KeyGenerator generator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, KEY_STORE);
String keyID = UUID.randomUUID().toString();
generator.init(new KeyGenParameterSpec.Builder(keyID,
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setKeySize(parameters.getEncryptionAESKeyLength() * 8)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.setUserAuthenticationRequired(true)
.setUserAuthenticationValidityDurationSeconds(60)
.setRandomizedEncryptionRequired(true)
.build());
SecretKey biometricKey = generator.generateKey();
SecretKey key = OTPDatabase.getLoadedKey();
CryptoResult encryptedKey = Crypto.encryptWithResult(parameters, key.getEncoded(), biometricKey, false);
return new BiometricKey(keyID, encryptedKey.getEncrypted(), encryptedKey.getIV());
} catch (NoSuchAlgorithmException | NoSuchProviderException | InvalidAlgorithmParameterException e) {
throw new CryptoException(e);
}
}
public static SecretKey getBiometricKey(BiometricKey key) throws CryptoException {
try {
KeyStore store = KeyStore.getInstance(KEY_STORE);
store.load(null);
return (SecretKey) store.getKey(key.getId(), null);
} catch (KeyStoreException | NoSuchAlgorithmException | UnrecoverableKeyException | CertificateException | IOException e) {
throw new CryptoException(e);
}
}
public static void deleteBiometricKey(BiometricKey key) throws CryptoException {
try {
KeyStore ks = KeyStore.getInstance(KEY_STORE);
ks.load(null);
ks.deleteEntry(key.getId());
} catch (KeyStoreException | CertificateException | IOException | NoSuchAlgorithmException e) {
throw new CryptoException(e);
}
}
} }

View File

@ -0,0 +1,20 @@
package com.cringe_studios.cringe_authenticator.crypto;
public class CryptoResult {
private byte[] encrypted;
private byte[] iv;
public CryptoResult(byte[] encrypted, byte[] iv) {
this.encrypted = encrypted;
this.iv = iv;
}
public byte[] getEncrypted() {
return encrypted;
}
public byte[] getIV() {
return iv;
}
}

View File

@ -3,6 +3,7 @@ package com.cringe_studios.cringe_authenticator.fragment;
import static androidx.biometric.BiometricManager.Authenticators.BIOMETRIC_STRONG; import static androidx.biometric.BiometricManager.Authenticators.BIOMETRIC_STRONG;
import static androidx.biometric.BiometricManager.Authenticators.DEVICE_CREDENTIAL; import static androidx.biometric.BiometricManager.Authenticators.DEVICE_CREDENTIAL;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.util.Log; import android.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
@ -16,10 +17,12 @@ import androidx.annotation.Nullable;
import androidx.biometric.BiometricManager; import androidx.biometric.BiometricManager;
import com.cringe_studios.cringe_authenticator.MainActivity; import com.cringe_studios.cringe_authenticator.MainActivity;
import com.cringe_studios.cringe_authenticator.crypto.BiometricKey;
import com.cringe_studios.cringe_authenticator.crypto.Crypto; import com.cringe_studios.cringe_authenticator.crypto.Crypto;
import com.cringe_studios.cringe_authenticator.crypto.CryptoException; import com.cringe_studios.cringe_authenticator.crypto.CryptoException;
import com.cringe_studios.cringe_authenticator.crypto.CryptoParameters; import com.cringe_studios.cringe_authenticator.crypto.CryptoParameters;
import com.cringe_studios.cringe_authenticator.databinding.FragmentSettingsBinding; import com.cringe_studios.cringe_authenticator.databinding.FragmentSettingsBinding;
import com.cringe_studios.cringe_authenticator.util.BiometricUtil;
import com.cringe_studios.cringe_authenticator.util.DialogUtil; import com.cringe_studios.cringe_authenticator.util.DialogUtil;
import com.cringe_studios.cringe_authenticator.util.FabUtil; import com.cringe_studios.cringe_authenticator.util.FabUtil;
import com.cringe_studios.cringe_authenticator.util.OTPDatabase; import com.cringe_studios.cringe_authenticator.util.OTPDatabase;
@ -28,6 +31,7 @@ import com.cringe_studios.cringe_authenticator.util.SettingsUtil;
import java.util.Arrays; import java.util.Arrays;
import java.util.Locale; import java.util.Locale;
import java.util.Objects;
import javax.crypto.SecretKey; import javax.crypto.SecretKey;
@ -106,9 +110,35 @@ public class SettingsFragment extends NamedFragment {
binding.settingsEnableIntroVideo.setChecked(SettingsUtil.isIntroVideoEnabled(requireContext())); binding.settingsEnableIntroVideo.setChecked(SettingsUtil.isIntroVideoEnabled(requireContext()));
binding.settingsEnableIntroVideo.setOnCheckedChangeListener((view, checked) -> SettingsUtil.setEnableIntroVideo(requireContext(), checked)); binding.settingsEnableIntroVideo.setOnCheckedChangeListener((view, checked) -> SettingsUtil.setEnableIntroVideo(requireContext(), checked));
if(BiometricManager.from(requireContext()).canAuthenticate(BIOMETRIC_STRONG | DEVICE_CREDENTIAL) == BiometricManager.BIOMETRIC_SUCCESS) { if(SettingsUtil.isDatabaseEncrypted(requireContext())
binding.settingsBiometricLock.setChecked(SettingsUtil.isBiometricLock(requireContext())); && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
binding.settingsBiometricLock.setOnCheckedChangeListener((view, checked) -> SettingsUtil.setBiometricLock(requireContext(), checked)); && BiometricManager.from(requireContext()).canAuthenticate(BIOMETRIC_STRONG | DEVICE_CREDENTIAL) == BiometricManager.BIOMETRIC_SUCCESS) {
binding.settingsBiometricLock.setChecked(SettingsUtil.isBiometricEncryption(requireContext()));
binding.settingsBiometricLock.setOnCheckedChangeListener((view, checked) -> {
if(checked) {
OTPDatabase.promptLoadDatabase(requireActivity(), () -> {
Log.i("PROMPT", "§BERIIO");
BiometricUtil.promptBiometricAuth(requireActivity(), () -> {
try {
BiometricKey biometricKey = Crypto.createBiometricKey(SettingsUtil.getCryptoParameters(requireContext()));
SettingsUtil.enableBiometricEncryption(requireContext(), biometricKey);
} catch (CryptoException e) {
e.printStackTrace();
DialogUtil.showErrorDialog(requireContext(), "Failed to enable: " + e);
}
}, () -> view.setChecked(false));
}, null);
}else {
try {
BiometricKey key = SettingsUtil.getBiometricKey(requireContext());
if(key != null) Crypto.deleteBiometricKey(key);
} catch (CryptoException e) {
DialogUtil.showErrorDialog(requireContext(), "Failed to delete key: " + e);
}
SettingsUtil.disableBiometricEncryption(requireContext());
}
});
}else { }else {
binding.settingsBiometricLock.setChecked(false); binding.settingsBiometricLock.setChecked(false);
binding.settingsBiometricLock.setEnabled(false); binding.settingsBiometricLock.setEnabled(false);

View File

@ -18,9 +18,11 @@ import androidx.core.content.ContextCompat;
import com.cringe_studios.cringe_authenticator.MainActivity; import com.cringe_studios.cringe_authenticator.MainActivity;
import com.cringe_studios.cringe_authenticator.R; import com.cringe_studios.cringe_authenticator.R;
import com.cringe_studios.cringe_authenticator.crypto.BiometricKey;
import com.cringe_studios.cringe_authenticator.crypto.Crypto; import com.cringe_studios.cringe_authenticator.crypto.Crypto;
import com.cringe_studios.cringe_authenticator.crypto.CryptoException; import com.cringe_studios.cringe_authenticator.crypto.CryptoException;
import com.cringe_studios.cringe_authenticator.databinding.ActivityUnlockBinding; import com.cringe_studios.cringe_authenticator.databinding.ActivityUnlockBinding;
import com.cringe_studios.cringe_authenticator.util.BiometricUtil;
import com.cringe_studios.cringe_authenticator.util.DialogUtil; import com.cringe_studios.cringe_authenticator.util.DialogUtil;
import com.cringe_studios.cringe_authenticator.util.OTPDatabase; import com.cringe_studios.cringe_authenticator.util.OTPDatabase;
import com.cringe_studios.cringe_authenticator.util.OTPDatabaseException; import com.cringe_studios.cringe_authenticator.util.OTPDatabaseException;
@ -36,6 +38,7 @@ import java.util.concurrent.Executor;
import javax.crypto.KeyGenerator; import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey; import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
public class UnlockActivity extends AppCompatActivity { public class UnlockActivity extends AppCompatActivity {
@ -57,47 +60,20 @@ public class UnlockActivity extends AppCompatActivity {
binding = ActivityUnlockBinding.inflate(getLayoutInflater()); binding = ActivityUnlockBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot()); setContentView(binding.getRoot());
try { if(SettingsUtil.isBiometricEncryption(this) && BiometricUtil.isSupported(this)) {
KeyStore ks = KeyStore.getInstance("AndroidKeyStore"); binding.unlockBiometrics.setOnClickListener(view -> BiometricUtil.promptBiometricAuth(this, this::success, () -> {}));
ks.load(null); BiometricUtil.promptBiometricAuth(this, () -> {
SecretKey key = KeyGenerator.getInstance("AES").generateKey(); BiometricKey biometricKey = SettingsUtil.getBiometricKey(this);
ks.setEntry("amogus", new KeyStore.SecretKeyEntry(key), new KeyStoreParameter.Builder(this).build()); try {
} catch (KeyStoreException | CertificateException | IOException | NoSuchAlgorithmException e) { SecretKey biometricSecretKey = Crypto.getBiometricKey(biometricKey);
throw new RuntimeException(e); byte[] keyBytes = Crypto.decrypt(SettingsUtil.getCryptoParameters(this), biometricKey.getEncryptedKey(), biometricSecretKey, biometricKey.getIV());
} SecretKey key = new SecretKeySpec(keyBytes, "AES");
OTPDatabase.loadDatabase(this, key);
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) {
failure();
}
@Override
public void onAuthenticationSucceeded(@NonNull BiometricPrompt.AuthenticationResult result) {
success(); success();
} catch (CryptoException | OTPDatabaseException e) {
DialogUtil.showErrorDialog(this, "Failed to load database: " + e);
} }
}); }, () -> {});
boolean supportsBiometricAuth = BiometricManager.from(this).canAuthenticate(BIOMETRIC_STRONG | DEVICE_CREDENTIAL) == BiometricManager.BIOMETRIC_SUCCESS;
boolean recentlyUnlocked = savedInstanceState != null && (System.currentTimeMillis() - savedInstanceState.getLong("pauseTime", 0L) < LOCK_TIMEOUT);
if(!recentlyUnlocked && SettingsUtil.isBiometricLock(this) && supportsBiometricAuth) {
BiometricPrompt.PromptInfo info = new BiometricPrompt.PromptInfo.Builder()
.setTitle(getString(R.string.app_name))
.setSubtitle(getString(R.string.biometric_lock_subtitle))
.setAllowedAuthenticators(BIOMETRIC_STRONG | DEVICE_CREDENTIAL)
.build();
//prompt.authenticate(info);
binding.unlockBiometrics.setOnClickListener(view -> {
//prompt.authenticate(info);
});
}else {
success();
}
} }
binding.unlockButton.setOnClickListener(view -> { binding.unlockButton.setOnClickListener(view -> {

View File

@ -0,0 +1,52 @@
package com.cringe_studios.cringe_authenticator.util;
import static androidx.biometric.BiometricManager.Authenticators.BIOMETRIC_STRONG;
import static androidx.biometric.BiometricManager.Authenticators.DEVICE_CREDENTIAL;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.biometric.BiometricManager;
import androidx.biometric.BiometricPrompt;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.FragmentActivity;
import com.cringe_studios.cringe_authenticator.R;
import java.util.concurrent.Executor;
public class BiometricUtil {
public static boolean isSupported(Context context) {
return BiometricManager.from(context).canAuthenticate(BIOMETRIC_STRONG | DEVICE_CREDENTIAL) == BiometricManager.BIOMETRIC_SUCCESS;
}
public static void promptBiometricAuth(FragmentActivity context, Runnable success, Runnable failure) {
if(!isSupported(context)) {
failure.run();
return;
}
Executor executor = ContextCompat.getMainExecutor(context);
BiometricPrompt prompt = new BiometricPrompt(context, executor, new BiometricPrompt.AuthenticationCallback() {
@Override
public void onAuthenticationError(int errorCode, @NonNull CharSequence errString) {
failure.run();
}
@Override
public void onAuthenticationSucceeded(@NonNull BiometricPrompt.AuthenticationResult result) {
success.run();
}
});
BiometricPrompt.PromptInfo info = new BiometricPrompt.PromptInfo.Builder()
.setTitle(context.getString(R.string.app_name))
.setSubtitle(context.getString(R.string.biometric_lock_subtitle))
.setAllowedAuthenticators(BIOMETRIC_STRONG | DEVICE_CREDENTIAL)
.build();
prompt.authenticate(info);
}
}

View File

@ -182,6 +182,10 @@ public class OTPDatabase {
return loadedDatabase; return loadedDatabase;
} }
public static SecretKey getLoadedKey() {
return loadedKey;
}
public static void encrypt(Context ctx, SecretKey key, CryptoParameters parameters) throws OTPDatabaseException, CryptoException { public static void encrypt(Context ctx, SecretKey key, CryptoParameters parameters) throws OTPDatabaseException, CryptoException {
if(!isDatabaseLoaded()) throw new IllegalStateException("Database is not loaded"); if(!isDatabaseLoaded()) throw new IllegalStateException("Database is not loaded");
loadedKey = key; loadedKey = key;

View File

@ -5,6 +5,7 @@ import android.content.SharedPreferences;
import android.util.Base64; import android.util.Base64;
import com.cringe_studios.cringe_authenticator.R; import com.cringe_studios.cringe_authenticator.R;
import com.cringe_studios.cringe_authenticator.crypto.BiometricKey;
import com.cringe_studios.cringe_authenticator.crypto.CryptoParameters; import com.cringe_studios.cringe_authenticator.crypto.CryptoParameters;
import com.google.gson.Gson; import com.google.gson.Gson;
@ -120,6 +121,8 @@ public class SettingsUtil {
ctx.getSharedPreferences(GENERAL_PREFS_NAME, Context.MODE_PRIVATE).edit() ctx.getSharedPreferences(GENERAL_PREFS_NAME, Context.MODE_PRIVATE).edit()
.putBoolean("encryption", false) .putBoolean("encryption", false)
.remove("encryption.parameters") .remove("encryption.parameters")
.remove("encryption.biometric")
.remove("encryption.biometric.key")
.apply(); .apply();
} }
@ -127,10 +130,10 @@ public class SettingsUtil {
return GSON.fromJson(ctx.getSharedPreferences(GENERAL_PREFS_NAME, Context.MODE_PRIVATE).getString("encryption.parameters", "{}"), CryptoParameters.class); return GSON.fromJson(ctx.getSharedPreferences(GENERAL_PREFS_NAME, Context.MODE_PRIVATE).getString("encryption.parameters", "{}"), CryptoParameters.class);
} }
public static void enableBiometricEncryption(Context ctx, byte[] encryptedBiometricKey) { public static void enableBiometricEncryption(Context ctx, BiometricKey biometricKey) {
ctx.getSharedPreferences(GENERAL_PREFS_NAME, Context.MODE_PRIVATE).edit() ctx.getSharedPreferences(GENERAL_PREFS_NAME, Context.MODE_PRIVATE).edit()
.putBoolean("encryption.biometric", true) .putBoolean("encryption.biometric", true)
.putString("encryption.biometric.key", Base64.encodeToString(encryptedBiometricKey, Base64.DEFAULT)) .putString("encryption.biometric.key", GSON.toJson(biometricKey))
.apply(); .apply();
} }
@ -141,21 +144,16 @@ public class SettingsUtil {
.apply(); .apply();
} }
public static byte[] getEncryptedBiometricKey(Context ctx) { public static boolean isBiometricEncryption(Context ctx) {
return ctx.getSharedPreferences(GENERAL_PREFS_NAME, Context.MODE_PRIVATE).getBoolean("encryption.biometric", false);
}
public static BiometricKey getBiometricKey(Context ctx) {
String encoded = ctx.getSharedPreferences(GENERAL_PREFS_NAME, Context.MODE_PRIVATE).getString("encryption.biometric.key", null); String encoded = ctx.getSharedPreferences(GENERAL_PREFS_NAME, Context.MODE_PRIVATE).getString("encryption.biometric.key", null);
if(encoded == null) return null; if(encoded == null) return null;
return Base64.decode(encoded, Base64.DEFAULT); return GSON.fromJson(encoded, BiometricKey.class);
} }
/*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) { public static void setEnableIntroVideo(Context ctx, boolean enableIntroVideo) {
SharedPreferences prefs = ctx.getSharedPreferences(GENERAL_PREFS_NAME, Context.MODE_PRIVATE); SharedPreferences prefs = ctx.getSharedPreferences(GENERAL_PREFS_NAME, Context.MODE_PRIVATE);
prefs.edit().putBoolean("enableIntroVideo", enableIntroVideo).apply(); prefs.edit().putBoolean("enableIntroVideo", enableIntroVideo).apply();

View File

@ -1,6 +1,6 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules. // Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins { plugins {
id 'com.android.application' version '8.1.0' apply false id 'com.android.application' version '8.1.1' apply false
id 'com.android.library' version '8.1.0' apply false id 'com.android.library' version '8.1.1' apply false
id 'com.google.protobuf' version '0.9.3' apply false id 'com.google.protobuf' version '0.9.3' apply false
} }