Skip to content

Commit

Permalink
Add Proton Pass importer
Browse files Browse the repository at this point in the history
  • Loading branch information
pedrxd committed Jul 24, 2024
1 parent 2050d29 commit 34550ff
Show file tree
Hide file tree
Showing 5 changed files with 171 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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("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("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("TOTP Authenticator", TotpAuthenticatorImporter.class, R.string.importer_help_totp_authenticator, true));
_importers.add(new Definition("WinAuth", WinAuthImporter.class, R.string.importer_help_winauth, false));
Expand Down
Original file line number Diff line number Diff line change
@@ -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());
}
}
}
1 change: 1 addition & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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_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_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_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>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,12 @@ public void testImportBitwardenCsv() throws IOException, DatabaseImporterExcepti
checkImportedBitwardenEntries(entries);
}

@Test
public void testImportProtonPassZip() throws DatabaseImporterException, IOException, OtpInfoException {
List<VaultEntry> entries = importPlain(ProtonPassImporter.class, "proton_pass.zip");
checkImportedProtonPassEntries(entries);
}

@Test
public void testImportFreeOtp() throws IOException, DatabaseImporterException, OtpInfoException {
List<VaultEntry> entries = importPlain(FreeOtpImporter.class, "freeotp.xml");
Expand Down Expand Up @@ -441,6 +447,14 @@ private void checkImportedBitwardenEntries(List<VaultEntry> entries) throws OtpI
}
}

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 {
for (VaultEntry entry : entries) {
checkImportedEntry(entry);
Expand Down
Binary file not shown.

0 comments on commit 34550ff

Please sign in to comment.