Skip to content

Commit bc600de

Browse files
authored
Merge pull request #1691 from michaelschattgen/feature/proton-importer
Add support for Proton Authenticator exports
2 parents aa48776 + da9adf6 commit bc600de

File tree

5 files changed

+136
-0
lines changed

5 files changed

+136
-0
lines changed

app/src/main/java/com/beemdevelopment/aegis/importers/DatabaseImporter.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ public abstract class DatabaseImporter {
4444
_importers.add(new Definition("Google Authenticator", GoogleAuthImporter.class, R.string.importer_help_google_authenticator, true));
4545
_importers.add(new Definition("Microsoft Authenticator", MicrosoftAuthImporter.class, R.string.importer_help_microsoft_authenticator, true));
4646
_importers.add(new Definition("Plain text", GoogleAuthUriImporter.class, R.string.importer_help_plain_text, false));
47+
_importers.add(new Definition("Proton Authenticator", ProtonAuthenticatorImporter.class, R.string.importer_help_proton_authenticator, false));
4748
_importers.add(new Definition("Steam", SteamImporter.class, R.string.importer_help_steam, true));
4849
_importers.add(new Definition("Stratum (Authenticator Pro)", StratumImporter.class, R.string.importer_help_stratum, true));
4950
_importers.add(new Definition("TOTP Authenticator", TotpAuthenticatorImporter.class, R.string.importer_help_totp_authenticator, true));
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
package com.beemdevelopment.aegis.importers;
2+
3+
import static java.nio.charset.StandardCharsets.UTF_8;
4+
5+
import android.content.Context;
6+
import android.net.Uri;
7+
8+
import androidx.annotation.NonNull;
9+
10+
import com.beemdevelopment.aegis.otp.GoogleAuthInfo;
11+
import com.beemdevelopment.aegis.otp.GoogleAuthInfoException;
12+
import com.beemdevelopment.aegis.otp.OtpInfo;
13+
import com.beemdevelopment.aegis.util.IOUtils;
14+
import com.beemdevelopment.aegis.vault.VaultEntry;
15+
import com.topjohnwu.superuser.io.SuFile;
16+
17+
import org.json.JSONArray;
18+
import org.json.JSONException;
19+
import org.json.JSONObject;
20+
21+
import java.io.IOException;
22+
import java.io.InputStream;
23+
24+
public class ProtonAuthenticatorImporter extends DatabaseImporter {
25+
26+
public ProtonAuthenticatorImporter(Context context) {
27+
super(context);
28+
}
29+
30+
@Override
31+
protected SuFile getAppPath() {
32+
throw new UnsupportedOperationException();
33+
}
34+
35+
@Override
36+
protected @NonNull State read(@NonNull InputStream stream, boolean isInternal) throws DatabaseImporterException {
37+
try {
38+
String contents = new String(IOUtils.readAll(stream), UTF_8);
39+
JSONObject json = new JSONObject(contents);
40+
41+
return new DecryptedState(json);
42+
} catch (JSONException | IOException e) {
43+
throw new DatabaseImporterException(e);
44+
}
45+
}
46+
47+
public static class DecryptedState extends DatabaseImporter.State {
48+
private final JSONObject _json;
49+
50+
public DecryptedState(@NonNull JSONObject json) {
51+
super(false);
52+
_json = json;
53+
}
54+
55+
@Override
56+
public @NonNull Result convert() throws DatabaseImporterException {
57+
Result result = new Result();
58+
59+
try {
60+
JSONArray entries = _json.getJSONArray("entries");
61+
for (int i = 0; i < entries.length(); i++) {
62+
JSONObject entry = entries.getJSONObject(i);
63+
try {
64+
result.addEntry(convertEntry(entry));
65+
} catch (DatabaseImporterEntryException e) {
66+
result.addError(e);
67+
}
68+
}
69+
} catch (JSONException e) {
70+
throw new DatabaseImporterException(e);
71+
}
72+
73+
return result;
74+
}
75+
76+
private static @NonNull VaultEntry convertEntry(@NonNull JSONObject entry) throws DatabaseImporterEntryException {
77+
try {
78+
JSONObject content = entry.getJSONObject("content");
79+
String name = content.getString("name");
80+
String uriString = content.getString("uri");
81+
82+
Uri uri = Uri.parse(uriString);
83+
try {
84+
GoogleAuthInfo info = GoogleAuthInfo.parseUri(uri);
85+
OtpInfo otp = info.getOtpInfo();
86+
87+
return new VaultEntry(otp, name, info.getIssuer());
88+
} catch (GoogleAuthInfoException e) {
89+
throw new DatabaseImporterEntryException(e, uriString);
90+
}
91+
} catch (JSONException e) {
92+
throw new DatabaseImporterEntryException(e, entry.toString());
93+
}
94+
}
95+
}
96+
}

app/src/main/res/values/strings.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -565,6 +565,7 @@
565565
<string name="importer_help_google_authenticator"><b>Only database files from Google Authenticator v5.10 and prior are supported</b>.\n\nSupply a copy of <b>/data/data/com.google.android.apps.authenticator2/databases/databases</b>, located in the internal storage directory of Google Authenticator.</string>
566566
<string name="importer_help_microsoft_authenticator">Supply a copy of <b>/data/data/com.azure.authenticator/databases/PhoneFactor</b>, located in the internal storage directory of Microsoft Authenticator.</string>
567567
<string name="importer_help_plain_text">Supply a plain text file with a Google Authenticator URI on each line.</string>
568+
<string name="importer_help_proton_authenticator">Supply a Proton Authenticator export file (.json) obtained through <b>Settings -> Export</b>.</string>
568569
<string name="importer_help_steam"><b>Steam v3.0 and newer are not supported</b>. Supply a copy of <b>/data/data/com.valvesoftware.android.steam.community/files/Steamguard-*.json</b>, located in the internal storage directory of Steam.</string>
569570
<string name="importer_help_stratum">Supply a Stratum export file obtained through <b>Settings -> Back up -> Back up to encrypted file (recommended)</b>.</string>
570571
<string name="importer_help_totp_authenticator">Supply a TOTP Authenticator export file.</string>

app/src/test/java/com/beemdevelopment/aegis/importers/DatabaseImporterTest.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,12 @@ public void testImportTwoFASAuthenticatorSchema4Encrypted() throws DatabaseImpor
390390
checkImportedEntries(entries);
391391
}
392392

393+
@Test
394+
public void testImportProtonAuthenticator() throws IOException, DatabaseImporterException, OtpInfoException {
395+
List<VaultEntry> entries = importPlain(ProtonAuthenticatorImporter.class, "proton_authenticator.json");
396+
checkImportedEntries(entries);
397+
}
398+
393399
private List<VaultEntry> importPlain(Class<? extends DatabaseImporter> type, String resName)
394400
throws IOException, DatabaseImporterException {
395401
return importPlain(type, resName, false);
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"version": 1,
3+
"entries": [
4+
{
5+
"id": "a19e0019-47e3-4d3b-af63-6dad0cb49e31",
6+
"content": {
7+
"uri": "otpauth://totp/Deno:Mason?secret=4SJHB4GSD43FZBAI7C2HLRJGPQ&issuer=Deno&algorithm=SHA1&digits=6&period=30",
8+
"entry_type": "Totp",
9+
"name": "Mason"
10+
},
11+
"note": null
12+
},
13+
{
14+
"id": "4603c576-5d93-4c7f-8c26-bed6dc4493fc",
15+
"content": {
16+
"uri": "otpauth://totp/SPDX:James?secret=5OM4WOOGPLQEF6UGN3CPEOOLWU&issuer=SPDX&algorithm=SHA256&digits=7&period=20",
17+
"entry_type": "Totp",
18+
"name": "James"
19+
},
20+
"note": null
21+
},
22+
{
23+
"id": "4fae25ae-16ed-4f15-a887-3bbb50b34d5e",
24+
"content": {
25+
"uri": "otpauth://totp/Airbnb:Elijah?secret=7ELGJSGXNCCTV3O6LKJWYFV2RA&issuer=Airbnb&algorithm=SHA512&digits=8&period=50",
26+
"entry_type": "Totp",
27+
"name": "Elijah"
28+
},
29+
"note": null
30+
}
31+
]
32+
}

0 commit comments

Comments
 (0)