diff --git a/.gitignore b/.gitignore index 71f7189..fea47f9 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ .cxx local.properties sentry.properties +androidacy.properties \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index b694d78..73d9354 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -28,6 +28,7 @@ android { debug { applicationIdSuffix '.debug' debuggable true + // ONLY FOR TESTING SENTRY // minifyEnabled true // shrinkResources true @@ -44,6 +45,10 @@ android { buildConfigField("java.util.List", "ENABLED_REPOS", "java.util.Arrays.asList(\"magisk_alt_repo\", \"androidacy_repo\")",) + // Get the androidacy client ID from the androidacy.properties + Properties properties = new Properties() + properties.load(project.rootProject.file('androidacy.properties').newDataInputStream()) + buildConfigField("String", "ANDROIDACY_CLIENT_ID", properties.getProperty('client_id')) } fdroid { @@ -63,6 +68,11 @@ android { buildConfigField("java.util.List", "ENABLED_REPOS", "java.util.Arrays.asList(\"magisk_alt_repo\")",) + + // Get the androidacy client ID from the androidacy.properties + Properties properties = new Properties() + properties.load(project.rootProject.file('androidacy.properties').newDataInputStream()) + buildConfigField("String", "ANDROIDACY_CLIENT_ID", properties.getProperty('client_id')) } } @@ -154,14 +164,6 @@ configurations { } dependencies { - // Big scary fingerprinting library - // Actually, just used to generate device ID for androidacy (they use it for fraud detection) - implementation "com.fingerprint.android:pro:2.2.1" - implementation "com.github.fingerprintjs:fingerprint-android:2.0.0" - - // If you use Java for you project, add also this line - implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" - // UI implementation 'androidx.appcompat:appcompat:1.5.1' implementation 'androidx.emoji2:emoji2:1.2.0' diff --git a/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyActivity.java b/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyActivity.java index 0ca2b46..e26387b 100644 --- a/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyActivity.java +++ b/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyActivity.java @@ -41,6 +41,7 @@ import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; +import java.security.NoSuchAlgorithmException; import java.util.HashMap; /** @@ -106,7 +107,11 @@ public final class AndroidacyActivity extends FoxActivity { String device_id = uri.getQueryParameter("device_id"); if (device_id == null) { // get from shared preferences - device_id = AndroidacyRepoData.generateDeviceId(); + try { + device_id = AndroidacyRepoData.generateDeviceId(); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } url = url + "&device_id=" + device_id; } boolean allowInstall = intent.getBooleanExtra(Constants.EXTRA_ANDROIDACY_ALLOW_INSTALL, false); diff --git a/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyRepoData.java b/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyRepoData.java index 3c706dc..14443df 100644 --- a/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyRepoData.java +++ b/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyRepoData.java @@ -1,6 +1,5 @@ package com.fox2code.mmm.androidacy; -import android.content.Context; import android.content.SharedPreferences; import android.os.Looper; import android.util.Log; @@ -8,11 +7,6 @@ import android.widget.Toast; import androidx.annotation.NonNull; -import com.fingerprintjs.android.fingerprint.Fingerprinter; -import com.fingerprintjs.android.fingerprint.FingerprinterFactory; -import com.fingerprintjs.android.fpjs_pro.Configuration; -import com.fingerprintjs.android.fpjs_pro.FingerprintJS; -import com.fingerprintjs.android.fpjs_pro.FingerprintJSFactory; import com.fox2code.mmm.BuildConfig; import com.fox2code.mmm.MainApplication; import com.fox2code.mmm.R; @@ -24,6 +18,7 @@ import com.fox2code.mmm.utils.Http; import com.fox2code.mmm.utils.HttpException; import com.fox2code.mmm.utils.PropUtils; import com.google.android.material.dialog.MaterialAlertDialogBuilder; +import com.topjohnwu.superuser.Shell; import org.json.JSONArray; import org.json.JSONException; @@ -31,6 +26,8 @@ import org.json.JSONObject; import java.io.File; import java.io.IOException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; @@ -48,6 +45,9 @@ public final class AndroidacyRepoData extends RepoData { OK_HTTP_URL_BUILDER.build(); } + @SuppressWarnings("unused") + public final String ClientID = BuildConfig.ANDROIDACY_CLIENT_ID; + private final boolean testMode; private final String host; public String token = MainApplication.getINSTANCE().getSharedPreferences("androidacy", 0).getString("pref_androidacy_api_token", null); @@ -84,42 +84,57 @@ public final class AndroidacyRepoData extends RepoData { // Generates a unique device ID. This is used to identify the device in the API for rate // limiting and fraud detection. - public static String generateDeviceId() { + public static String generateDeviceId() throws NoSuchAlgorithmException { // Try to get the device ID from the shared preferences SharedPreferences sharedPreferences = MainApplication.getINSTANCE().getSharedPreferences("androidacy", 0); String deviceIdPref = sharedPreferences.getString("device_id", null); if (deviceIdPref != null) { return deviceIdPref; } else { - Context context = MainApplication.getINSTANCE().getApplicationContext(); - FingerprintJSFactory factory = new FingerprintJSFactory(context); - Configuration.Region region = Configuration.Region.US; - Configuration configuration = new Configuration( - "NiZiHi266YaTLreOIOzc", - region, - region.getEndpointUrl(), - true - ); - - FingerprintJS fpjsClient = factory.createInstance( - configuration - ); - - fpjsClient.getVisitorId(visitorIdResponse -> { - // Use the ID - String visitorId = visitorIdResponse.getVisitorId(); - // Save the ID in the shared preferences - SharedPreferences.Editor editor = sharedPreferences.edit(); - editor.putString("device_id", visitorId); - editor.apply(); - return null; - }); - // return the id - return sharedPreferences.getString("device_id", null); + // AAAA we're fingerprintiiiiing + // Really not that scary - just hashes some device info. We can't even get the info + // we originally hashed, so it's not like we can use it to track you. + String deviceId = null; + // Get ro.serialno if it exists + // First, we need to get an su shell + Shell.Result result = Shell.cmd("getprop ro.serialno").exec(); + // Check if the command was successful + if (result.isSuccess()) { + // Get the output + String output = result.getOut().get(0); + // Check if the output is valid + if (output != null && !output.isEmpty()) { + deviceId = output; + } + } + // Now, get device model, manufacturer, and Android version + String deviceModel = android.os.Build.MODEL; + String deviceManufacturer = android.os.Build.MANUFACTURER; + String androidVersion = android.os.Build.VERSION.RELEASE; + // Append it all together + deviceId += deviceModel + deviceManufacturer + androidVersion; + // Hash it + MessageDigest digest = MessageDigest.getInstance("SHA-256"); + byte[] hash = digest.digest(deviceId.getBytes()); + // Convert it to a hex string + StringBuilder hexString = new StringBuilder(); + for (byte b : hash) { + String hex = Integer.toHexString(0xff & b); + if (hex.length() == 1) { + hexString.append('0'); + } + hexString.append(hex); + } + // Save it to shared preferences + SharedPreferences.Editor editor = sharedPreferences.edit(); + editor.putString("device_id", hexString.toString()); + editor.apply(); + // Return it + return hexString.toString(); } } - public boolean isValidToken(String token) throws IOException { + public boolean isValidToken(String token) throws IOException, NoSuchAlgorithmException { String deviceId = generateDeviceId(); try { Http.doHttpGet("https://" + this.host + "/auth/me?token=" + token + "&device_id=" + deviceId, false); @@ -139,23 +154,8 @@ public final class AndroidacyRepoData extends RepoData { } @Override - protected boolean prepare() { + protected boolean prepare() throws NoSuchAlgorithmException { if (Http.needCaptchaAndroidacy()) return false; - // Check if we have a device ID yet - SharedPreferences sharedPreferences = MainApplication.getINSTANCE().getSharedPreferences("androidacy", 0); - String deviceIdPref = sharedPreferences.getString("device_id", null); - if (deviceIdPref == null) { - // Generate a device ID - generateDeviceId(); - // Loop until we have a device ID - while (sharedPreferences.getString("device_id", null) == null) { - try { - Thread.sleep(100); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - } // Implementation details discussed on telegram // First, ping the server to check if it's alive try { @@ -240,7 +240,7 @@ public final class AndroidacyRepoData extends RepoData { } @Override - protected List populate(JSONObject jsonObject) throws JSONException { + protected List populate(JSONObject jsonObject) throws JSONException, NoSuchAlgorithmException { if (!jsonObject.getString("status").equals("success")) throw new JSONException("Response is not a success!"); String name = jsonObject.optString("name", "Androidacy Modules Repo"); @@ -353,12 +353,12 @@ public final class AndroidacyRepoData extends RepoData { } @Override - public String getUrl() { + public String getUrl() throws NoSuchAlgorithmException { return this.token == null ? this.url : this.url + "?token=" + this.token + "&v=" + BuildConfig.VERSION_CODE + "&c=" + BuildConfig.VERSION_NAME + "&device_id=" + generateDeviceId(); } - private String injectToken(String url) { + private String injectToken(String url) throws NoSuchAlgorithmException { // Do not inject token for non Androidacy urls if (!AndroidacyUtil.isAndroidacyLink(url)) return url; if (this.testMode) { diff --git a/app/src/main/java/com/fox2code/mmm/repo/CustomRepoData.java b/app/src/main/java/com/fox2code/mmm/repo/CustomRepoData.java index 31aca8f..e0448f0 100644 --- a/app/src/main/java/com/fox2code/mmm/repo/CustomRepoData.java +++ b/app/src/main/java/com/fox2code/mmm/repo/CustomRepoData.java @@ -10,6 +10,7 @@ import org.json.JSONObject; import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; +import java.security.NoSuchAlgorithmException; public final class CustomRepoData extends RepoData { boolean loadedExternal; @@ -30,7 +31,7 @@ public final class CustomRepoData extends RepoData { this.id : this.override; } - public void quickPrePopulate() throws IOException, JSONException { + public void quickPrePopulate() throws IOException, JSONException, NoSuchAlgorithmException { JSONObject jsonObject = new JSONObject( new String(Http.doHttpGet(this.getUrl(), false), StandardCharsets.UTF_8)); diff --git a/app/src/main/java/com/fox2code/mmm/repo/RepoData.java b/app/src/main/java/com/fox2code/mmm/repo/RepoData.java index 70b53d9..17ea670 100644 --- a/app/src/main/java/com/fox2code/mmm/repo/RepoData.java +++ b/app/src/main/java/com/fox2code/mmm/repo/RepoData.java @@ -21,6 +21,7 @@ import org.json.JSONObject; import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; +import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; @@ -81,11 +82,11 @@ public class RepoData extends XRepo { } } - protected boolean prepare() { + protected boolean prepare() throws NoSuchAlgorithmException { return true; } - protected List populate(JSONObject jsonObject) throws JSONException { + protected List populate(JSONObject jsonObject) throws JSONException, NoSuchAlgorithmException { List newModules = new ArrayList<>(); synchronized (this.populateLock) { String name = jsonObject.getString("name").trim(); @@ -215,7 +216,7 @@ public class RepoData extends XRepo { .getBoolean("pref_" + this.getPreferenceId() + "_enabled", this.isEnabledByDefault()); } - public String getUrl() { + public String getUrl() throws NoSuchAlgorithmException { return this.url; } diff --git a/app/src/main/java/com/fox2code/mmm/settings/SettingsActivity.java b/app/src/main/java/com/fox2code/mmm/settings/SettingsActivity.java index 903cf7b..ffe374a 100644 --- a/app/src/main/java/com/fox2code/mmm/settings/SettingsActivity.java +++ b/app/src/main/java/com/fox2code/mmm/settings/SettingsActivity.java @@ -67,6 +67,7 @@ import com.topjohnwu.superuser.internal.UiThreadHandler; import org.json.JSONException; import java.io.IOException; +import java.security.NoSuchAlgorithmException; import java.util.HashSet; import java.util.Objects; import java.util.Random; @@ -615,7 +616,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity { boolean valid = false; try { valid = AndroidacyRepoData.getInstance().isValidToken(apiKey); - } catch (IOException ignored) {} + } catch (IOException | NoSuchAlgorithmException ignored) {} // If the key is valid, save it if (valid) { originalApiKeyRef[0] = apiKey; @@ -715,7 +716,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity { public void run() { try { customRepoData.quickPrePopulate(); - } catch (IOException | JSONException e) { + } catch (IOException | JSONException | NoSuchAlgorithmException e) { Log.e(TAG, "Failed to preload repo values", e); } UiThreadHandler.handler.post(() -> updateCustomRepoList(false)); diff --git a/build.gradle b/build.gradle index 6a44e52..5765da3 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,6 @@ buildscript { google() mavenCentral() gradlePluginPortal() - maven { url 'https://maven.fpregistry.io/releases' } maven { url 'https://jitpack.io' } } project.ext.latestAboutLibsRelease = "10.5.0" diff --git a/settings.gradle b/settings.gradle index db25883..5c996dc 100644 --- a/settings.gradle +++ b/settings.gradle @@ -6,7 +6,6 @@ dependencyResolutionManagement { maven { url 'https://jitpack.io' } - maven { url 'https://maven.fpregistry.io/releases' } } } rootProject.name = "MagiskModuleManager"