Add Proton Pass importer

This commit is contained in:
Pedro Ruiz 2024-07-22 22:47:28 +02:00
parent 2050d29236
commit 34550fff35
5 changed files with 171 additions and 0 deletions

View file

@ -44,6 +44,7 @@ public abstract class DatabaseImporter {
_importers.add(new Definition("Google Authenticator", GoogleAuthImporter.class, R.string.importer_help_google_authenticator, true)); _importers.add(new Definition("Google Authenticator", GoogleAuthImporter.class, R.string.importer_help_google_authenticator, true));
_importers.add(new Definition("Microsoft Authenticator", MicrosoftAuthImporter.class, R.string.importer_help_microsoft_authenticator, true)); _importers.add(new Definition("Microsoft Authenticator", MicrosoftAuthImporter.class, R.string.importer_help_microsoft_authenticator, true));
_importers.add(new Definition("Plain text", GoogleAuthUriImporter.class, R.string.importer_help_plain_text, false)); _importers.add(new Definition("Plain text", GoogleAuthUriImporter.class, R.string.importer_help_plain_text, false));
_importers.add(new Definition("Proton Pass", ProtonPassImporter.class, R.string.importer_help_proton_pass, true));
_importers.add(new Definition("Steam", SteamImporter.class, R.string.importer_help_steam, true)); _importers.add(new Definition("Steam", SteamImporter.class, R.string.importer_help_steam, true));
_importers.add(new Definition("TOTP Authenticator", TotpAuthenticatorImporter.class, R.string.importer_help_totp_authenticator, true)); _importers.add(new Definition("TOTP Authenticator", TotpAuthenticatorImporter.class, R.string.importer_help_totp_authenticator, true));
_importers.add(new Definition("WinAuth", WinAuthImporter.class, R.string.importer_help_winauth, false)); _importers.add(new Definition("WinAuth", WinAuthImporter.class, R.string.importer_help_winauth, false));

View file

@ -0,0 +1,155 @@
package com.beemdevelopment.aegis.importers;
import android.content.Context;
import android.net.Uri;
import com.beemdevelopment.aegis.otp.GoogleAuthInfo;
import com.beemdevelopment.aegis.otp.GoogleAuthInfoException;
import com.beemdevelopment.aegis.vault.VaultEntry;
import com.beemdevelopment.aegis.vault.VaultGroup;
import com.topjohnwu.superuser.io.SuFile;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONTokener;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Iterator;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
public class ProtonPassImporter extends DatabaseImporter {
public ProtonPassImporter(Context context) {
super(context);
}
@Override
protected SuFile getAppPath() {
throw new UnsupportedOperationException();
}
@Override
protected State read(InputStream stream, boolean isInternal) throws DatabaseImporterException {
// Unzip
ZipInputStream zis = new ZipInputStream(stream);
// Read file from zip
ZipEntry zipEntry;
try {
while((zipEntry = zis.getNextEntry()) != null)
{
if(!zipEntry.getName().equals("Proton Pass/data.json"))
{
continue;
}
// Read file
BufferedReader br = new BufferedReader(new InputStreamReader(zis));
StringBuilder json = new StringBuilder();
String line;
while((line = br.readLine()) != null){
json.append(line);
}
br.close();
// Parse JSON
JSONTokener tokener = new JSONTokener(json.toString());
JSONObject jsonObject = new JSONObject(tokener);
return new State(jsonObject);
}
}catch (IOException | JSONException e)
{
throw new DatabaseImporterException(e);
}
//Json not found
throw new DatabaseImporterException("Invalid proton zip file");
}
public static class State extends DatabaseImporter.State {
private JSONObject _jsonObject;
private State(JSONObject jsonObject)
{
super(false);
_jsonObject = jsonObject;
}
public Result convert() throws DatabaseImporterException {
Result result = new Result();
try {
JSONObject vaults = this._jsonObject.getJSONObject("vaults");
Iterator<String> keys = vaults.keys();
// Iterate over vaults
while (keys.hasNext())
{
JSONObject vault = vaults.getJSONObject(keys.next());
JSONArray items = vault.getJSONArray("items");
//Create a new group
VaultGroup group = new VaultGroup(vault.getString("name"));
result.addGroup(group);
// Iterate over items on the vault
for(int j = 0; j < items.length(); j++)
{
JSONObject item = items.getJSONObject(j);
try{
VaultEntry entry = this.fromItem(item);
if(entry == null)
{
continue;
}
entry.addGroup(group.getUUID());
result.addEntry(entry);
}catch (JSONException | GoogleAuthInfoException e)
{
result.addError(new DatabaseImporterEntryException(e, "Can't import " + item.getString("itemId")));
}
}
}
return result;
}catch (JSONException e)
{
throw new DatabaseImporterException(e);
}
}
public VaultEntry fromItem(JSONObject item) throws JSONException, GoogleAuthInfoException {
JSONObject data = item.getJSONObject("data");
JSONObject metadata = data.getJSONObject("metadata");
JSONObject content = data.getJSONObject("content");
//Only login items
if(!data.getString("type").equals("login"))
{
return null;
}
String uri = content.getString("totpUri");
if(uri.isEmpty())
{
return null;
}
Uri toptURI = Uri.parse(content.getString("totpUri"));
GoogleAuthInfo entry = GoogleAuthInfo.parseUri(toptURI);
return new VaultEntry(entry.getOtpInfo(), metadata.getString("name"), entry.getIssuer());
}
}
}

View file

@ -506,6 +506,7 @@
<string name="importer_help_authy">Supply a copy of <b>/data/data/com.authy.authy/shared_prefs/com.authy.storage.tokens.authenticator.xml</b>, located in the internal storage directory of Authy.</string> <string name="importer_help_authy">Supply a copy of <b>/data/data/com.authy.authy/shared_prefs/com.authy.storage.tokens.authenticator.xml</b>, located in the internal storage directory of Authy.</string>
<string name="importer_help_andotp">Supply an andOTP export/backup file.</string> <string name="importer_help_andotp">Supply an andOTP export/backup file.</string>
<string name="importer_help_bitwarden">Supply a Bitwarden export/backup file. Encrypted files are not supported.</string> <string name="importer_help_bitwarden">Supply a Bitwarden export/backup file. Encrypted files are not supported.</string>
<string name="importer_help_proton_pass">Supply a Proton pass export/backup zip. Encrypted files are not supported.</string>
<string name="importer_help_battle_net_authenticator">Supply a copy of <b>/data/data/com.blizzard.messenger/shared_prefs/com.blizzard.messenger.authenticator_preferences.xml</b>, located in the internal storage directory of Battle.net Authenticator.</string> <string name="importer_help_battle_net_authenticator">Supply a copy of <b>/data/data/com.blizzard.messenger/shared_prefs/com.blizzard.messenger.authenticator_preferences.xml</b>, located in the internal storage directory of Battle.net Authenticator.</string>
<string name="importer_help_duo">Supply a copy of <b>/data/data/com.duosecurity.duomobile/files/duokit/accounts.json</b>, located in the internal storage directory of DUO.</string> <string name="importer_help_duo">Supply a copy of <b>/data/data/com.duosecurity.duomobile/files/duokit/accounts.json</b>, located in the internal storage directory of DUO.</string>
<string name="importer_help_freeotp">Supply a copy of <b>/data/data/org.fedorahosted.freeotp/shared_prefs/tokens.xml</b>, located in the internal storage directory of FreeOTP (1.x).</string> <string name="importer_help_freeotp">Supply a copy of <b>/data/data/org.fedorahosted.freeotp/shared_prefs/tokens.xml</b>, located in the internal storage directory of FreeOTP (1.x).</string>

View file

@ -209,6 +209,12 @@ public class DatabaseImporterTest {
checkImportedBitwardenEntries(entries); checkImportedBitwardenEntries(entries);
} }
@Test
public void testImportProtonPassZip() throws DatabaseImporterException, IOException, OtpInfoException {
List<VaultEntry> entries = importPlain(ProtonPassImporter.class, "proton_pass.zip");
checkImportedProtonPassEntries(entries);
}
@Test @Test
public void testImportFreeOtp() throws IOException, DatabaseImporterException, OtpInfoException { public void testImportFreeOtp() throws IOException, DatabaseImporterException, OtpInfoException {
List<VaultEntry> entries = importPlain(FreeOtpImporter.class, "freeotp.xml"); List<VaultEntry> entries = importPlain(FreeOtpImporter.class, "freeotp.xml");
@ -441,6 +447,14 @@ public class DatabaseImporterTest {
} }
} }
private void checkImportedProtonPassEntries(List<VaultEntry> entries) throws OtpInfoException {
for (VaultEntry entry : entries)
{
entry.getGroups().clear();
checkImportedEntry(entry);
}
}
private void checkImportedEntries(List<VaultEntry> entries) throws OtpInfoException { private void checkImportedEntries(List<VaultEntry> entries) throws OtpInfoException {
for (VaultEntry entry : entries) { for (VaultEntry entry : entries) {
checkImportedEntry(entry); checkImportedEntry(entry);