Merge pull request #1032 from JordanPlayz158/master

Battle.net Authenticator Import Support
This commit is contained in:
Alexander Bakker 2022-12-05 22:00:05 +01:00 committed by GitHub
commit 2c36149a3d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 144 additions and 1 deletions

View File

@ -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>

View File

@ -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);
}
}
}
}

View File

@ -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;

View File

@ -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>

View File

@ -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");

View File

@ -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);

View File

@ -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>