mirror of
https://github.com/beemdevelopment/Aegis.git
synced 2025-04-20 05:49:13 +00:00
Merge pull request #1032 from JordanPlayz158/master
Battle.net Authenticator Import Support
This commit is contained in:
commit
2c36149a3d
7 changed files with 144 additions and 1 deletions
|
@ -154,6 +154,7 @@
|
|||
<package android:name="com.valvesoftware.android.steam.community" />
|
||||
<package android:name="com.authenticator.authservice2" />
|
||||
<package android:name="com.duosecurity.duomobile" />
|
||||
<package android:name="com.blizzard.bma" />
|
||||
</queries>
|
||||
|
||||
</manifest>
|
||||
|
|
|
@ -0,0 +1,116 @@
|
|||
package com.beemdevelopment.aegis.importers;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.util.Xml;
|
||||
|
||||
import com.beemdevelopment.aegis.encoding.EncodingException;
|
||||
import com.beemdevelopment.aegis.encoding.Hex;
|
||||
import com.beemdevelopment.aegis.otp.OtpInfo;
|
||||
import com.beemdevelopment.aegis.otp.OtpInfoException;
|
||||
import com.beemdevelopment.aegis.otp.TotpInfo;
|
||||
import com.beemdevelopment.aegis.util.PreferenceParser;
|
||||
import com.beemdevelopment.aegis.vault.VaultEntry;
|
||||
import com.topjohnwu.superuser.io.SuFile;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class BattleNetImporter extends DatabaseImporter {
|
||||
private static final String _pkgName = "com.blizzard.bma";
|
||||
private static final String _subPath = "shared_prefs/com.blizzard.bma.AUTH_STORE.xml";
|
||||
|
||||
private static final byte[] _key;
|
||||
|
||||
public BattleNetImporter(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
static {
|
||||
try {
|
||||
_key = Hex.decode("398e27fc50276a656065b0e525f4c06c04c61075286b8e7aeda59da9813b5dd6c80d2fb38068773fa59ba47c17ca6c6479015c1d5b8b8f6b9a");
|
||||
} catch (EncodingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SuFile getAppPath() throws DatabaseImporterException, PackageManager.NameNotFoundException {
|
||||
return getAppPath(_pkgName, _subPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected State read(InputStream stream, boolean isInternal) throws DatabaseImporterException {
|
||||
try {
|
||||
XmlPullParser parser = Xml.newPullParser();
|
||||
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
|
||||
parser.setInput(stream, null);
|
||||
parser.nextTag();
|
||||
|
||||
List<String> entries = new ArrayList<>();
|
||||
for (PreferenceParser.XmlEntry entry : PreferenceParser.parse(parser)) {
|
||||
if (entry.Name.equals("com.blizzard.bma.AUTH_STORE.HASH")) {
|
||||
entries.add(entry.Value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return new BattleNetImporter.State(entries);
|
||||
} catch (XmlPullParserException | IOException e) {
|
||||
throw new DatabaseImporterException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static class State extends DatabaseImporter.State {
|
||||
private final List<String> _entries;
|
||||
|
||||
public State(List<String> entries) {
|
||||
super(false);
|
||||
_entries = entries;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result convert() {
|
||||
Result result = new Result();
|
||||
|
||||
for (String str : _entries) {
|
||||
try {
|
||||
VaultEntry entry = convertEntry(str);
|
||||
result.addEntry(entry);
|
||||
} catch (DatabaseImporterEntryException e) {
|
||||
result.addError(e);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static VaultEntry convertEntry(String hashString) throws DatabaseImporterEntryException {
|
||||
try {
|
||||
byte[] hash = Hex.decode(hashString);
|
||||
if (hash.length != _key.length) {
|
||||
throw new DatabaseImporterEntryException(String.format("Unexpected hash length: %d", hash.length), hashString);
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < hash.length; i++) {
|
||||
char c = (char) (hash[i] ^ _key[i]);
|
||||
sb.append(c);
|
||||
}
|
||||
|
||||
final int secretLen = 40;
|
||||
byte[] secret = Hex.decode(sb.substring(0, secretLen));
|
||||
String serial = sb.substring(secretLen);
|
||||
|
||||
OtpInfo info = new TotpInfo(secret, OtpInfo.DEFAULT_ALGORITHM, 8, TotpInfo.DEFAULT_PERIOD);
|
||||
return new VaultEntry(info, serial, "Battle.net");
|
||||
} catch (OtpInfoException | EncodingException e) {
|
||||
throw new DatabaseImporterEntryException(e, hashString);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -34,6 +34,7 @@ public abstract class DatabaseImporter {
|
|||
_importers.add(new Definition("andOTP", AndOtpImporter.class, R.string.importer_help_andotp, false));
|
||||
_importers.add(new Definition("Authenticator Plus", AuthenticatorPlusImporter.class, R.string.importer_help_authenticator_plus, false));
|
||||
_importers.add(new Definition("Authy", AuthyImporter.class, R.string.importer_help_authy, true));
|
||||
_importers.add(new Definition("Battle.net Authenticator", BattleNetImporter.class, R.string.importer_help_battle_net_authenticator, true));
|
||||
_importers.add(new Definition("Bitwarden", BitwardenImporter.class, R.string.importer_help_bitwarden, false));
|
||||
_importers.add(new Definition("Duo", DuoImporter.class, R.string.importer_help_duo, true));
|
||||
_importers.add(new Definition("FreeOTP", FreeOtpImporter.class, R.string.importer_help_freeotp, true));
|
||||
|
@ -105,6 +106,13 @@ public abstract class DatabaseImporter {
|
|||
private final @StringRes int _help;
|
||||
private final boolean _supportsDirect;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param name The name of the Authenticator the importer handles.
|
||||
* @param type The class which does the importing.
|
||||
* @param help The string that explains the type of file needed (and optionally where it can be obtained).
|
||||
* @param supportsDirect Whether the importer can directly import the entries from the app's internal storage using root access.
|
||||
*/
|
||||
public Definition(String name, Class<? extends DatabaseImporter> type, @StringRes int help, boolean supportsDirect) {
|
||||
_name = name;
|
||||
_type = type;
|
||||
|
|
|
@ -456,6 +456,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_battle_net_authenticator">Supply a copy of <b>/data/data/com.blizzard.bma/shared_prefs/com.blizzard.bma.AUTH_STORE.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.</string>
|
||||
<string name="importer_help_freeotp_plus">Supply a FreeOTP+ export file.</string>
|
||||
|
|
|
@ -158,6 +158,15 @@ public class DatabaseImporterTest {
|
|||
checkImportedAuthyEntries(entries);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testImportBattleNetXml() throws DatabaseImporterException, IOException, OtpInfoException {
|
||||
List<VaultEntry> entries = importPlain(BattleNetImporter.class, "battle_net_authenticator.xml");
|
||||
|
||||
for (VaultEntry entry : entries) {
|
||||
checkImportedEntry(entry);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testImportBitwardenJson() throws IOException, DatabaseImporterException, OtpInfoException {
|
||||
List<VaultEntry> entries = importPlain(BitwardenImporter.class, "bitwarden.json");
|
||||
|
|
|
@ -25,7 +25,8 @@ public class VaultEntries {
|
|||
new VaultEntry(new HotpInfo(Base32.decode("YOOMIXWS5GN6RTBPUFFWKTW5M4"), "SHA1", 6, 1), "James", "Issuu"),
|
||||
new VaultEntry(new HotpInfo(Base32.decode("KUVJJOM753IHTNDSZVCNKL7GII"), "SHA256", 7, 50), "Benjamin", "Air Canada"),
|
||||
new VaultEntry(new HotpInfo(Base32.decode("5VAML3X35THCEBVRLV24CGBKOY"), "SHA512", 8, 10300), "Mason", "WWE"),
|
||||
new VaultEntry(new SteamInfo(Base32.decode("JRZCL47CMXVOQMNPZR2F7J4RGI"), "SHA1", 5, 30), "Sophia", "Boeing")
|
||||
new VaultEntry(new SteamInfo(Base32.decode("JRZCL47CMXVOQMNPZR2F7J4RGI"), "SHA1", 5, 30), "Sophia", "Boeing"),
|
||||
new VaultEntry(new TotpInfo(Base32.decode("BMGRXPGFARQQF4GMT25JATL2VYLAHDBI"), "SHA1", 8, 30), "US-2211-2050-3346", "Battle.net")
|
||||
);
|
||||
} catch (OtpInfoException | EncodingException e) {
|
||||
throw new RuntimeException(e);
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
|
||||
<map>
|
||||
<int name="com.blizzard.bma.AUTH_STORE_HASH_VERSION" value="20600015" />
|
||||
<string name="com.blizzard.bma.AUTH_STORE.HASH">09ec179861450806035080d113c5f05e62f67316110eec1bd495a9cdb65a3cb3f93b1f80b80b4507f0c8894e25fb5d494b31692d76b8bc5fac</string>
|
||||
<long name="com.blizzard.bma.AUTH_STORE.CLOCK_OFFSET" value="193" />
|
||||
<long name="com.blizzard.bma.AUTH_STORE.LAST_MODIFIED" value="1668980752290" />
|
||||
</map>
|
Loading…
Add table
Reference in a new issue