From 3e2bb5b0b381446a54aa28d12a024676bee566e6 Mon Sep 17 00:00:00 2001 From: Alexander Bakker Date: Sun, 6 Aug 2017 21:45:27 +0200 Subject: [PATCH] Support plain text databases --- app/build.gradle | 2 +- .../impy/aegis/CustomAuthenticatedSlide.java | 50 ++++++++++-- .../impy/aegis/CustomAuthenticationSlide.java | 56 ++++++++++++-- .../java/me/impy/aegis/IntroActivity.java | 57 ++++++++++---- .../main/java/me/impy/aegis/MainActivity.java | 2 + .../me/impy/aegis/db/DatabaseManager.java | 10 ++- app/src/main/res/layout/activity_auth.xml | 1 - .../res/layout/fingerprint_dialog_backup.xml | 76 ------------------- .../layout/fingerprint_dialog_container.xml | 63 --------------- .../res/layout/fingerprint_dialog_content.xml | 56 -------------- .../layout/fragment_authentication_slide.xml | 11 +-- app/src/main/res/values/strings.xml | 36 +++------ 12 files changed, 160 insertions(+), 260 deletions(-) delete mode 100644 app/src/main/res/layout/fingerprint_dialog_backup.xml delete mode 100644 app/src/main/res/layout/fingerprint_dialog_container.xml delete mode 100644 app/src/main/res/layout/fingerprint_dialog_content.xml diff --git a/app/build.gradle b/app/build.gradle index 2827efc5..1569e20c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -30,13 +30,13 @@ android { dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) compile 'com.android.support:recyclerview-v7:25.0.0' + compile 'com.android.support:recyclerview-v7:25.0.0' compile 'com.android.support:appcompat-v7:25.0.0' compile 'com.android.support:design:25.0.0' compile 'agency.tango.android:material-intro-screen:0.0.3' compile 'com.amulyakhare:com.amulyakhare.textdrawable:1.0.1' compile 'me.dm7.barcodescanner:zxing:1.9' compile 'com.android.support:cardview-v7:25.0.0' - compile 'com.android.support:recyclerview-v7:25.0.0' compile 'com.android.support:support-v4:25.0.0' compile 'com.yarolegovich:lovely-dialog:1.0.4' compile 'com.mattprecious.swirl:swirl:1.0.0' diff --git a/app/src/main/java/me/impy/aegis/CustomAuthenticatedSlide.java b/app/src/main/java/me/impy/aegis/CustomAuthenticatedSlide.java index 6132a5e3..ba34968b 100644 --- a/app/src/main/java/me/impy/aegis/CustomAuthenticatedSlide.java +++ b/app/src/main/java/me/impy/aegis/CustomAuthenticatedSlide.java @@ -1,5 +1,6 @@ package me.impy.aegis; +import android.content.Intent; import android.os.Bundle; import android.text.Editable; import android.view.LayoutInflater; @@ -22,6 +23,7 @@ import me.impy.aegis.crypto.slots.PasswordSlot; import me.impy.aegis.crypto.slots.Slot; public class CustomAuthenticatedSlide extends SlideFragment { + private int cryptType; private EditText textPassword; private EditText textPasswordConfirm; @@ -33,6 +35,29 @@ public class CustomAuthenticatedSlide extends SlideFragment { return view; } + @Override + public void setUserVisibleHint(boolean isVisibleToUser) { + super.setUserVisibleHint(isVisibleToUser); + + if (!isVisibleToUser) { + return; + } + + Intent intent = getActivity().getIntent(); + cryptType = intent.getIntExtra("cryptType", 1337); + + switch(cryptType) { + case CustomAuthenticationSlide.CRYPT_TYPE_NONE: + break; + case CustomAuthenticationSlide.CRYPT_TYPE_PASS: + break; + case CustomAuthenticationSlide.CRYPT_TYPE_FINGER: + break; + default: + throw new RuntimeException(); + } + } + @Override public int backgroundColor() { return R.color.colorHeaderSuccess; @@ -45,12 +70,21 @@ public class CustomAuthenticatedSlide extends SlideFragment { @Override public boolean canMoveFurther() { - char[] password = getEditTextChars(textPassword); - char[] passwordConfirm = getEditTextChars(textPasswordConfirm); - boolean equal = password.length != 0 && Arrays.equals(password, passwordConfirm); - CryptoUtils.zero(password); - CryptoUtils.zero(passwordConfirm); - return equal; + switch(cryptType) { + case CustomAuthenticationSlide.CRYPT_TYPE_NONE: + return true; + case CustomAuthenticationSlide.CRYPT_TYPE_PASS: + char[] password = getEditTextChars(textPassword); + char[] passwordConfirm = getEditTextChars(textPasswordConfirm); + boolean equal = password.length != 0 && Arrays.equals(password, passwordConfirm); + CryptoUtils.zero(password); + CryptoUtils.zero(passwordConfirm); + return equal; + case CustomAuthenticationSlide.CRYPT_TYPE_FINGER: + return false; + default: + throw new RuntimeException(); + } } @Override @@ -58,6 +92,10 @@ public class CustomAuthenticatedSlide extends SlideFragment { return "Passwords should be equal and non-empty"; } + public int getCryptType() { + return cryptType; + } + public Cipher getCipher(PasswordSlot slot, int mode) throws InvalidKeySpecException, NoSuchAlgorithmException, InvalidKeyException, NoSuchPaddingException { diff --git a/app/src/main/java/me/impy/aegis/CustomAuthenticationSlide.java b/app/src/main/java/me/impy/aegis/CustomAuthenticationSlide.java index e9515886..f6c9e03e 100644 --- a/app/src/main/java/me/impy/aegis/CustomAuthenticationSlide.java +++ b/app/src/main/java/me/impy/aegis/CustomAuthenticationSlide.java @@ -1,35 +1,77 @@ package me.impy.aegis; +import android.Manifest; import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.hardware.fingerprint.FingerprintManager; +import android.os.Build; import android.os.Bundle; +import android.support.annotation.IdRes; +import android.support.v4.app.ActivityCompat; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.RadioButton; import android.widget.RadioGroup; +import android.widget.TextView; import android.widget.Toast; import agency.tango.materialintroscreen.SlideFragment; public class CustomAuthenticationSlide extends SlideFragment { + public static final int CRYPT_TYPE_NONE = 0; + public static final int CRYPT_TYPE_PASS = 1; + public static final int CRYPT_TYPE_FINGER = 2; + private RadioGroup buttonGroup; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { final View view = inflater.inflate(R.layout.fragment_authentication_slide, container, false); - buttonGroup = (RadioGroup) view.findViewById(R.id.rg_authenticationMethod); + final Context context = getContext(); - RadioButton button = (RadioButton) view.findViewById(R.id.rb_fingerprint); - button.setOnClickListener(new View.OnClickListener() { + buttonGroup = (RadioGroup) view.findViewById(R.id.rg_authenticationMethod); + buttonGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() { @Override - public void onClick(View v) { - if (canMoveFurther()) { - buttonGroup.clearCheck(); - Toast.makeText(CustomAuthenticationSlide.this.getActivity(), "Fingerprint is not supported yet", Toast.LENGTH_SHORT).show(); + public void onCheckedChanged(RadioGroup group, @IdRes int checkedId) { + if (checkedId == -1) { + return; } + + int id; + switch (checkedId) { + case R.id.rb_none: + id = CRYPT_TYPE_NONE; + break; + case R.id.rb_password: + id = CRYPT_TYPE_PASS; + break; + case R.id.rb_fingerprint: + id = CRYPT_TYPE_FINGER; + // TODO: remove this + group.clearCheck(); + Toast.makeText(context, "Fingerprint is not supported yet", Toast.LENGTH_SHORT).show(); + break; + default: + throw new RuntimeException(); + } + Intent intent = getActivity().getIntent(); + intent.putExtra("cryptType", id); } }); + // only show the fingerprint option if the api version is new enough, permission is granted and a scanner is found + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + FingerprintManager fingerprintManager = (FingerprintManager) context.getSystemService(Context.FINGERPRINT_SERVICE); + if (ActivityCompat.checkSelfPermission(context, Manifest.permission.USE_FINGERPRINT) == PackageManager.PERMISSION_GRANTED && fingerprintManager.isHardwareDetected()) { + RadioButton button = (RadioButton) view.findViewById(R.id.rb_fingerprint); + TextView text = (TextView) view.findViewById(R.id.text_rb_fingerprint); + button.setVisibility(View.VISIBLE); + text.setVisibility(View.VISIBLE); + } + } + return view; } diff --git a/app/src/main/java/me/impy/aegis/IntroActivity.java b/app/src/main/java/me/impy/aegis/IntroActivity.java index c0a08a87..a9b3f54b 100644 --- a/app/src/main/java/me/impy/aegis/IntroActivity.java +++ b/app/src/main/java/me/impy/aegis/IntroActivity.java @@ -32,6 +32,7 @@ public class IntroActivity extends MaterialIntroActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); + hideBackButton(); addSlide(new SlideFragmentBuilder() .backgroundColor(R.color.colorPrimary) @@ -84,24 +85,48 @@ public class IntroActivity extends MaterialIntroActivity { Database database = new Database(); DatabaseFile databaseFile = new DatabaseFile(); - MasterKey masterKey; + int cryptType = authenticatedSlide.getCryptType(); + + // generate the master key + MasterKey masterKey = null; + if (cryptType != CustomAuthenticationSlide.CRYPT_TYPE_NONE) { + try { + masterKey = MasterKey.generate(); + } catch (Exception e) { + setException(e); + return; + } + } + + if (cryptType != CustomAuthenticationSlide.CRYPT_TYPE_NONE) { + try { + // encrypt the master key with a key derived from the user's password + // and add it to the list of slots + SlotCollection slots = databaseFile.getSlots(); + PasswordSlot slot = new PasswordSlot(); + Cipher cipher = authenticatedSlide.getCipher(slot, Cipher.ENCRYPT_MODE); + masterKey.encryptSlot(slot, cipher); + slots.add(slot); + } catch (Exception e) { + setException(e); + return; + } + } + + if (cryptType != CustomAuthenticationSlide.CRYPT_TYPE_FINGER) { + // TODO + } + + // finally, save the database try { - // generate the master key - masterKey = MasterKey.generate(); - - // encrypt the master key with a key derived from the user's password - // and add it to the list of slots - SlotCollection slots = databaseFile.getSlots(); - PasswordSlot slot = new PasswordSlot(); - Cipher cipher = authenticatedSlide.getCipher(slot, Cipher.ENCRYPT_MODE); - masterKey.encryptSlot(slot, cipher); - slots.add(slot); - - // finally, save the database byte[] bytes = database.serialize(); - CryptResult result = masterKey.encrypt(bytes); - databaseFile.setContent(result.Data); - databaseFile.setCryptParameters(result.Parameters); + if (cryptType == CustomAuthenticationSlide.CRYPT_TYPE_NONE) { + databaseFile.setContent(bytes); + } else { + CryptResult result = masterKey.encrypt(bytes); + databaseFile.setContent(result.Data); + databaseFile.setCryptParameters(result.Parameters); + } databaseFile.save(getApplicationContext()); } catch (Exception e) { setException(e); diff --git a/app/src/main/java/me/impy/aegis/MainActivity.java b/app/src/main/java/me/impy/aegis/MainActivity.java index d7518612..3871ab36 100644 --- a/app/src/main/java/me/impy/aegis/MainActivity.java +++ b/app/src/main/java/me/impy/aegis/MainActivity.java @@ -84,6 +84,8 @@ public class MainActivity extends AppCompatActivity { Intent intent = new Intent(this, AuthActivity.class); intent.putExtra("slots", db.getFile().getSlots()); startActivityForResult(intent, CODE_DECRYPT); + } else { + loadKeyProfiles(); } } diff --git a/app/src/main/java/me/impy/aegis/db/DatabaseManager.java b/app/src/main/java/me/impy/aegis/db/DatabaseManager.java index 981fd5f1..e3818267 100644 --- a/app/src/main/java/me/impy/aegis/db/DatabaseManager.java +++ b/app/src/main/java/me/impy/aegis/db/DatabaseManager.java @@ -40,9 +40,13 @@ public class DatabaseManager { public void save() throws Exception { assertDecrypted(); byte[] bytes = _db.serialize(); - CryptResult result = _key.encrypt(bytes); - _file.setContent(result.Data); - _file.setCryptParameters(result.Parameters); + if (!_file.isEncrypted()) { + _file.setContent(bytes); + } else { + CryptResult result = _key.encrypt(bytes); + _file.setContent(result.Data); + _file.setCryptParameters(result.Parameters); + } _file.save(_context); } diff --git a/app/src/main/res/layout/activity_auth.xml b/app/src/main/res/layout/activity_auth.xml index 8dcd4e08..39bba448 100644 --- a/app/src/main/res/layout/activity_auth.xml +++ b/app/src/main/res/layout/activity_auth.xml @@ -18,7 +18,6 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="24sp" - android:textColor="@color/primary_text_inverted" android:id="@+id/textView2" /> - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/fingerprint_dialog_container.xml b/app/src/main/res/layout/fingerprint_dialog_container.xml deleted file mode 100644 index 53c8e9af..00000000 --- a/app/src/main/res/layout/fingerprint_dialog_container.xml +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - - - - - - - - - - -