mirror of
https://github.com/beemdevelopment/Aegis.git
synced 2025-04-20 13:59:14 +00:00
Battle.net Importing Support
Fixed issue caused by supplying the VaultEntry with the Base32 encoded string rather than the raw secret Added blizzard package to manifest so Aegis is allowed to query whether the app is installed Fixed VaultEntry to be more inline with other entries Removed the unnecessary encoding of the secret as it is used as is without encoding and changed the way the TotpInfo object is supplied with the relevant information. Credits to alexbakker (https://github.com/beemdevelopment/Aegis/pull/1032#pullrequestreview-1203477313)
This commit is contained in:
parent
f8d89d5754
commit
f3731c23a3
7 changed files with 144 additions and 1 deletions
|
@ -149,6 +149,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -33,6 +33,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));
|
||||
|
@ -102,6 +103,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;
|
||||
|
|
|
@ -441,6 +441,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