From b746ca0c6e4973b0fe9d881cc62a03cad4c2b28e Mon Sep 17 00:00:00 2001 From: Alexander Bakker Date: Sat, 15 Aug 2020 17:08:08 +0200 Subject: [PATCH 1/2] Ignore deep link intents if the given URI is null --- .../java/com/beemdevelopment/aegis/ui/MainActivity.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/beemdevelopment/aegis/ui/MainActivity.java b/app/src/main/java/com/beemdevelopment/aegis/ui/MainActivity.java index 9e02876f..bb25f20b 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/ui/MainActivity.java +++ b/app/src/main/java/com/beemdevelopment/aegis/ui/MainActivity.java @@ -440,10 +440,10 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene } Intent intent = getIntent(); - if (Intent.ACTION_VIEW.equals(intent.getAction())) { - Uri uri = intent.getData(); - getIntent().setData(null); - getIntent().setAction(null); + Uri uri = intent.getData(); + if (Intent.ACTION_VIEW.equals(intent.getAction()) && uri != null) { + intent.setData(null); + intent.setAction(null); GoogleAuthInfo info = null; try { From a45c834b9c680a06e5bc9769537f6903884f7379 Mon Sep 17 00:00:00 2001 From: Alexander Bakker Date: Sat, 15 Aug 2020 17:08:36 +0200 Subject: [PATCH 2/2] Add UI tests for deep links --- .../com/beemdevelopment/aegis/AegisTest.java | 66 +++++++++++++++++++ .../com/beemdevelopment/aegis/IntentTest.java | 65 ++++++++++++++++++ .../com/beemdevelopment/aegis/IntroTest.java | 8 +-- .../beemdevelopment/aegis/OverallTest.java | 27 ++------ 4 files changed, 139 insertions(+), 27 deletions(-) create mode 100644 app/src/androidTest/java/com/beemdevelopment/aegis/IntentTest.java diff --git a/app/src/androidTest/java/com/beemdevelopment/aegis/AegisTest.java b/app/src/androidTest/java/com/beemdevelopment/aegis/AegisTest.java index 520b1cf4..d727efba 100644 --- a/app/src/androidTest/java/com/beemdevelopment/aegis/AegisTest.java +++ b/app/src/androidTest/java/com/beemdevelopment/aegis/AegisTest.java @@ -6,11 +6,30 @@ import androidx.test.espresso.UiController; import androidx.test.espresso.ViewAction; import androidx.test.platform.app.InstrumentationRegistry; +import com.beemdevelopment.aegis.crypto.CryptoUtils; +import com.beemdevelopment.aegis.crypto.SCryptParameters; +import com.beemdevelopment.aegis.otp.OtpInfo; +import com.beemdevelopment.aegis.vault.Vault; +import com.beemdevelopment.aegis.vault.VaultEntry; +import com.beemdevelopment.aegis.vault.VaultFileCredentials; import com.beemdevelopment.aegis.vault.VaultManager; +import com.beemdevelopment.aegis.vault.VaultManagerException; +import com.beemdevelopment.aegis.vault.slots.PasswordSlot; +import com.beemdevelopment.aegis.vault.slots.SlotException; import org.hamcrest.Matcher; +import java.lang.reflect.InvocationTargetException; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; + +import javax.crypto.NoSuchPaddingException; +import javax.crypto.SecretKey; + public abstract class AegisTest { + public static final String VAULT_PASSWORD = "test"; + protected AegisApplication getApp() { return (AegisApplication) InstrumentationRegistry.getInstrumentation().getTargetContext().getApplicationContext(); } @@ -19,6 +38,53 @@ public abstract class AegisTest { return getApp().getVaultManager(); } + protected VaultManager initVault() { + PasswordSlot slot = new PasswordSlot(); + byte[] salt = CryptoUtils.generateSalt(); + SCryptParameters scryptParams = new SCryptParameters( + CryptoUtils.CRYPTO_SCRYPT_N, + CryptoUtils.CRYPTO_SCRYPT_r, + CryptoUtils.CRYPTO_SCRYPT_p, + salt + ); + + VaultFileCredentials creds = new VaultFileCredentials(); + try { + SecretKey key = slot.deriveKey(VAULT_PASSWORD.toCharArray(), scryptParams); + slot.setKey(creds.getKey(), CryptoUtils.createEncryptCipher(key)); + } catch (NoSuchAlgorithmException + | InvalidKeyException + | InvalidAlgorithmParameterException + | NoSuchPaddingException + | SlotException e) { + throw new RuntimeException(e); + } + creds.getSlots().add(slot); + + VaultManager vault = getApp().initVaultManager(new Vault(), creds); + try { + vault.save(false); + } catch (VaultManagerException e) { + throw new RuntimeException(e); + } + + getApp().getPreferences().setIntroDone(true); + return vault; + } + + protected static VaultEntry generateEntry(Class type, String name, String issuer) { + byte[] secret = CryptoUtils.generateRandomBytes(20); + + OtpInfo info; + try { + info = type.getConstructor(byte[].class).newInstance(secret); + } catch (IllegalAccessException | InstantiationException | InvocationTargetException | NoSuchMethodException e) { + throw new RuntimeException(e); + } + + return new VaultEntry(info, name, issuer); + } + // source: https://stackoverflow.com/a/30338665 protected static ViewAction clickChildViewWithId(final int id) { return new ViewAction() { diff --git a/app/src/androidTest/java/com/beemdevelopment/aegis/IntentTest.java b/app/src/androidTest/java/com/beemdevelopment/aegis/IntentTest.java new file mode 100644 index 00000000..89ebffd9 --- /dev/null +++ b/app/src/androidTest/java/com/beemdevelopment/aegis/IntentTest.java @@ -0,0 +1,65 @@ +package com.beemdevelopment.aegis; + +import android.content.Intent; +import android.net.Uri; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.LargeTest; +import androidx.test.rule.ActivityTestRule; + +import com.beemdevelopment.aegis.otp.GoogleAuthInfo; +import com.beemdevelopment.aegis.otp.TotpInfo; +import com.beemdevelopment.aegis.ui.MainActivity; +import com.beemdevelopment.aegis.vault.VaultEntry; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.action.ViewActions.click; +import static androidx.test.espresso.matcher.ViewMatchers.withId; +import static junit.framework.TestCase.assertTrue; + +@RunWith(AndroidJUnit4.class) +@LargeTest +public class IntentTest extends AegisTest { + @Before + public void before() { + initVault(); + } + + @Test + public void doDeepLinkIntent() { + VaultEntry entry = generateEntry(TotpInfo.class, "Bob", "Google"); + GoogleAuthInfo info = new GoogleAuthInfo(entry.getInfo(), entry.getName(), entry.getIssuer()); + launch(info.getUri()); + + onView(withId(R.id.action_save)).perform(click()); + + VaultEntry createdEntry = (VaultEntry) getVault().getEntries().toArray()[0]; + assertTrue(createdEntry.equivalates(entry)); + } + + @Test + public void doDeepLinkIntent_Empty() { + launch(null); + } + + @Test + public void doDeepLinkIntent_Bad() { + launch(Uri.parse("otpauth://bad")); + onView(withId(android.R.id.button1)).perform(click()); + } + + @SuppressWarnings("deprecation") + private void launch(Uri uri) { + Intent intent = new Intent(getApp(), MainActivity.class); + intent.setAction(Intent.ACTION_VIEW); + intent.setData(uri); + + // we need to use the deprecated ActivityTestRule class because of https://github.com/android/android-test/issues/143 + ActivityTestRule rule = new ActivityTestRule<>(MainActivity.class); + rule.launchActivity(intent); + } +} diff --git a/app/src/androidTest/java/com/beemdevelopment/aegis/IntroTest.java b/app/src/androidTest/java/com/beemdevelopment/aegis/IntroTest.java index 6e74b999..ecc40163 100644 --- a/app/src/androidTest/java/com/beemdevelopment/aegis/IntroTest.java +++ b/app/src/androidTest/java/com/beemdevelopment/aegis/IntroTest.java @@ -31,8 +31,6 @@ import static org.hamcrest.Matchers.not; @RunWith(AndroidJUnit4.class) @LargeTest public class IntroTest extends AegisTest { - private static final String _password = "test"; - @Rule public final ActivityScenarioRule activityRule = new ActivityScenarioRule<>(IntroActivity.class); @@ -69,10 +67,10 @@ public class IntroTest extends AegisTest { prev.check(matches(not(isDisplayed()))); next.perform(click()); next.perform(click()); - onView(withId(R.id.text_password)).perform(typeText(_password), closeSoftKeyboard()); - onView(withId(R.id.text_password_confirm)).perform(typeText(_password + "1"), closeSoftKeyboard()); + onView(withId(R.id.text_password)).perform(typeText(VAULT_PASSWORD), closeSoftKeyboard()); + onView(withId(R.id.text_password_confirm)).perform(typeText(VAULT_PASSWORD + "1"), closeSoftKeyboard()); next.perform(click()); - onView(withId(R.id.text_password_confirm)).perform(replaceText(_password), closeSoftKeyboard()); + onView(withId(R.id.text_password_confirm)).perform(replaceText(VAULT_PASSWORD), closeSoftKeyboard()); prev.perform(click()); prev.perform(click()); prev.check(matches(not(isDisplayed()))); diff --git a/app/src/androidTest/java/com/beemdevelopment/aegis/OverallTest.java b/app/src/androidTest/java/com/beemdevelopment/aegis/OverallTest.java index a215571a..a95e939a 100644 --- a/app/src/androidTest/java/com/beemdevelopment/aegis/OverallTest.java +++ b/app/src/androidTest/java/com/beemdevelopment/aegis/OverallTest.java @@ -9,10 +9,8 @@ import androidx.test.ext.junit.rules.ActivityScenarioRule; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.LargeTest; -import com.beemdevelopment.aegis.crypto.CryptoUtils; import com.beemdevelopment.aegis.encoding.Base32; import com.beemdevelopment.aegis.otp.HotpInfo; -import com.beemdevelopment.aegis.otp.OtpInfo; import com.beemdevelopment.aegis.otp.SteamInfo; import com.beemdevelopment.aegis.otp.TotpInfo; import com.beemdevelopment.aegis.ui.MainActivity; @@ -24,7 +22,6 @@ import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; -import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -50,7 +47,6 @@ import static org.hamcrest.Matchers.anything; @RunWith(AndroidJUnit4.class) @LargeTest public class OverallTest extends AegisTest { - private static final String _password = "test"; private static final String _groupName = "Test"; @Rule @@ -62,8 +58,8 @@ public class OverallTest extends AegisTest { next.perform(click()); onView(withId(R.id.rb_password)).perform(click()); next.perform(click()); - onView(withId(R.id.text_password)).perform(typeText(_password), closeSoftKeyboard()); - onView(withId(R.id.text_password_confirm)).perform(typeText(_password), closeSoftKeyboard()); + onView(withId(R.id.text_password)).perform(typeText(VAULT_PASSWORD), closeSoftKeyboard()); + onView(withId(R.id.text_password_confirm)).perform(typeText(VAULT_PASSWORD), closeSoftKeyboard()); next.perform(click()); onView(withId(R.id.btnNext)).perform(click()); @@ -125,7 +121,7 @@ public class OverallTest extends AegisTest { openContextualActionModeOverflowMenu(); onView(withText(R.string.lock)).perform(click()); - onView(withId(R.id.text_password)).perform(typeText(_password), closeSoftKeyboard()); + onView(withId(R.id.text_password)).perform(typeText(VAULT_PASSWORD), closeSoftKeyboard()); onView(withId(R.id.button_decrypt)).perform(click()); vault = getVault(); @@ -138,8 +134,8 @@ public class OverallTest extends AegisTest { assertNull(vault.getCredentials()); onView(withId(androidx.preference.R.id.recycler_view)).perform(RecyclerViewActions.actionOnItem(hasDescendant(withText(R.string.pref_encryption_title)), click())); - onView(withId(R.id.text_password)).perform(typeText(_password), closeSoftKeyboard()); - onView(withId(R.id.text_password_confirm)).perform(typeText(_password), closeSoftKeyboard()); + onView(withId(R.id.text_password)).perform(typeText(VAULT_PASSWORD), closeSoftKeyboard()); + onView(withId(R.id.text_password_confirm)).perform(typeText(VAULT_PASSWORD), closeSoftKeyboard()); onView(withId(android.R.id.button1)).perform(click()); assertTrue(vault.isEncryptionEnabled()); @@ -191,17 +187,4 @@ public class OverallTest extends AegisTest { onView(withId(R.id.action_save)).perform(click()); } - - private VaultEntry generateEntry(Class type, String name, String issuer) { - byte[] secret = CryptoUtils.generateRandomBytes(20); - - OtpInfo info; - try { - info = type.getConstructor(byte[].class).newInstance(secret); - } catch (IllegalAccessException | InstantiationException | InvocationTargetException | NoSuchMethodException e) { - throw new RuntimeException(e); - } - - return new VaultEntry(info, name, issuer); - } }