diff --git a/app/build.gradle b/app/build.gradle index 381914ce..6d142035 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -94,6 +94,7 @@ dependencies { def libsuVersion = '2.5.1' implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'com.google.protobuf:protobuf-javalite:3.8.0' + implementation 'net.lingala.zip4j:zip4j:2.5.2' implementation 'com.amulyakhare:com.amulyakhare.textdrawable:1.0.1' implementation 'androidx.appcompat:appcompat:1.1.0' implementation "androidx.biometric:biometric:1.0.1" @@ -118,7 +119,6 @@ dependencies { implementation("com.github.bumptech.glide:recyclerview-integration:4.9.0") { transitive = false } - implementation 'net.zetetic:android-database-sqlcipher:4.3.0' annotationProcessor 'androidx.annotation:annotation:1.1.0' annotationProcessor 'com.github.bumptech.glide:compiler:4.10.0' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.5.2' diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index fd2f258d..caf2b409 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -17,6 +17,5 @@ #} -keep class com.beemdevelopment.aegis.importers.** { *; } --keep class net.sqlcipher.** { *; } -keep class * extends com.google.protobuf.GeneratedMessageLite { *; } diff --git a/app/src/main/java/com/beemdevelopment/aegis/importers/AuthenticatorPlusImporter.java b/app/src/main/java/com/beemdevelopment/aegis/importers/AuthenticatorPlusImporter.java index cda66529..14b125a5 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/importers/AuthenticatorPlusImporter.java +++ b/app/src/main/java/com/beemdevelopment/aegis/importers/AuthenticatorPlusImporter.java @@ -1,24 +1,19 @@ package com.beemdevelopment.aegis.importers; import android.content.Context; -import android.database.Cursor; -import com.beemdevelopment.aegis.encoding.Base32; -import com.beemdevelopment.aegis.encoding.EncodingException; -import com.beemdevelopment.aegis.otp.HotpInfo; -import com.beemdevelopment.aegis.otp.OtpInfo; -import com.beemdevelopment.aegis.otp.OtpInfoException; -import com.beemdevelopment.aegis.otp.TotpInfo; import com.beemdevelopment.aegis.ui.Dialogs; -import com.beemdevelopment.aegis.vault.VaultEntry; + +import net.lingala.zip4j.io.inputstream.ZipInputStream; +import net.lingala.zip4j.model.LocalFileHeader; import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileNotFoundException; import java.io.IOException; -import java.io.InputStream; -import java.util.List; public class AuthenticatorPlusImporter extends DatabaseImporter { - private static final String _pkgName = "com.mufri.authenticatorplus"; + private static final String FILENAME = "Accounts.txt"; public AuthenticatorPlusImporter(Context context) { super(context); @@ -26,7 +21,7 @@ public class AuthenticatorPlusImporter extends DatabaseImporter { @Override protected String getAppPkgName() { - return _pkgName; + throw new UnsupportedOperationException(); } @Override @@ -54,123 +49,25 @@ public class AuthenticatorPlusImporter extends DatabaseImporter { @Override public void decrypt(Context context, DecryptListener listener) { Dialogs.showPasswordInputDialog(context, password -> { - try { - // recreate InputStream from saved byte array - InputStream stream = new ByteArrayInputStream(_data); - - SqlImporterHelper helper = new SqlImporterHelper(context, password, true); - List entries = helper.read(Entry.class, stream, "accounts"); - - DecryptedState state = new DecryptedState(entries); - listener.onStateDecrypted(state); - } catch (DatabaseImporterException e) { + try (ByteArrayInputStream inStream = new ByteArrayInputStream(_data); + ZipInputStream zipStream = new ZipInputStream(inStream, password)) { + LocalFileHeader header; + while ((header = zipStream.getNextEntry()) != null) { + File file = new File(header.getFileName()); + if (file.getName().equals(FILENAME)) { + FileReader reader = new FileReader(zipStream); + GoogleAuthUriImporter importer = new GoogleAuthUriImporter(context); + GoogleAuthUriImporter.State state = importer.read(reader); + listener.onStateDecrypted(state); + return; + } + } + + throw new FileNotFoundException(FILENAME); + } catch (IOException | DatabaseImporterException e) { listener.onError(e); } }); } } - - public static class DecryptedState extends DatabaseImporter.State { - private List _entries; - - private DecryptedState(List entries) { - super(false); - _entries = entries; - } - - private static VaultEntry convertEntry(Entry entry) throws DatabaseImporterEntryException { - try { - String secretString = entry.getSecret().replaceAll("\\s", ""); - byte[] secret = Base32.decode(secretString); - - OtpInfo info; - switch (entry.getType()) { - case 0: - info = new TotpInfo(secret); - break; - case 1: - info = new HotpInfo(secret, entry.getCounter()); - break; - default: - throw new DatabaseImporterException("unsupported otp type: " + entry.getType()); - } - - String name = entry.getEmail(); - String[] parts = name.split(":"); - if (parts.length == 2) { - name = parts[1]; - } - - // Authenticator Plus saves all entries to the "All Accounts" category by default - // If an entry is in this category, we can consider it as uncategorized - String group = entry.getGroup(); - if (group.equals("All Accounts")) { - return new VaultEntry(info, name, entry.getIssuer()); - } - - return new VaultEntry(info, name, entry.getIssuer(), entry.getGroup()); - } catch (EncodingException | OtpInfoException | DatabaseImporterException e) { - throw new DatabaseImporterEntryException(e, entry.toString()); - } - } - - @Override - public Result convert() { - Result result = new Result(); - - for (Entry sqlEntry : _entries) { - try { - VaultEntry entry = convertEntry(sqlEntry); - result.addEntry(entry); - } catch (DatabaseImporterEntryException e) { - result.addError(e); - } - } - - return result; - } - } - - private static class Entry extends SqlImporterHelper.Entry { - private int _type; - private String _secret; - private String _email; - private String _issuer; - private String _group; - private long _counter; - - public Entry(Cursor cursor) { - super(cursor); - _type = SqlImporterHelper.getInt(cursor, "type"); - _secret = SqlImporterHelper.getString(cursor, "secret"); - _email = SqlImporterHelper.getString(cursor, "email", ""); - _issuer = SqlImporterHelper.getString(cursor, "issuer", ""); - _group = SqlImporterHelper.getString(cursor, "category", ""); - _counter = SqlImporterHelper.getLong(cursor, "counter"); - } - - public int getType() { - return _type; - } - - public String getSecret() { - return _secret; - } - - public String getEmail() { - return _email; - } - - public String getIssuer() { - return _issuer; - } - - public String getGroup() { - return _group; - } - - public long getCounter() { - return _counter; - } - } } diff --git a/app/src/main/java/com/beemdevelopment/aegis/importers/SqlImporterHelper.java b/app/src/main/java/com/beemdevelopment/aegis/importers/SqlImporterHelper.java index 8e395626..f7cd62a8 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/importers/SqlImporterHelper.java +++ b/app/src/main/java/com/beemdevelopment/aegis/importers/SqlImporterHelper.java @@ -2,13 +2,11 @@ package com.beemdevelopment.aegis.importers; import android.content.Context; import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteException; import com.topjohnwu.superuser.ShellUtils; -import net.sqlcipher.database.SQLiteDatabase; -import net.sqlcipher.database.SQLiteDatabaseHook; -import net.sqlcipher.database.SQLiteException; - import java.io.File; import java.io.FileOutputStream; import java.io.IOException; @@ -21,18 +19,9 @@ import static android.database.sqlite.SQLiteDatabase.OPEN_READONLY; public class SqlImporterHelper { private Context _context; - private char[] _password; - private boolean _compatibilityMode; public SqlImporterHelper(Context context) { _context = context; - _password = null; - } - - public SqlImporterHelper(Context context, char[] password, boolean compatibilityMode) { - _context = context; - _password = password; - _compatibilityMode = compatibilityMode; } public List read(Class type, InputStream inStream, String table) throws DatabaseImporterException { @@ -48,22 +37,7 @@ public class SqlImporterHelper { throw new DatabaseImporterException(e); } - SQLiteDatabase.loadLibs(_context); - SQLiteDatabaseHook compatibilityHook = new SQLiteDatabaseHook() { - @Override - public void preKey(SQLiteDatabase database) { - - } - - @Override - public void postKey(SQLiteDatabase database) { - if (_compatibilityMode) { - database.compileStatement("PRAGMA cipher_compatibility = 3;"); - } - } - }; - - try (SQLiteDatabase db = SQLiteDatabase.openDatabase(file.getAbsolutePath(), _password, null, OPEN_READONLY, compatibilityHook)) { + try (SQLiteDatabase db = SQLiteDatabase.openDatabase(file.getAbsolutePath(), null, OPEN_READONLY)) { try (Cursor cursor = db.rawQuery(String.format("SELECT * FROM %s", table), null)) { List entries = new ArrayList<>();