Add client ID as per androidacy spec

Signed-off-by: androidacy-user <opensource@androidacy.com>
pull/230/head
androidacy-user 1 year ago
parent 0339dd7525
commit 4fa978b78c

1
.gitignore vendored

@ -9,3 +9,4 @@
.cxx .cxx
local.properties local.properties
sentry.properties sentry.properties
androidacy.properties

@ -28,6 +28,7 @@ android {
debug { debug {
applicationIdSuffix '.debug' applicationIdSuffix '.debug'
debuggable true debuggable true
// ONLY FOR TESTING SENTRY // ONLY FOR TESTING SENTRY
// minifyEnabled true // minifyEnabled true
// shrinkResources true // shrinkResources true
@ -44,6 +45,10 @@ android {
buildConfigField("java.util.List<String>", buildConfigField("java.util.List<String>",
"ENABLED_REPOS", "ENABLED_REPOS",
"java.util.Arrays.asList(\"magisk_alt_repo\", \"androidacy_repo\")",) "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 { fdroid {
@ -63,6 +68,11 @@ android {
buildConfigField("java.util.List<String>", buildConfigField("java.util.List<String>",
"ENABLED_REPOS", "ENABLED_REPOS",
"java.util.Arrays.asList(\"magisk_alt_repo\")",) "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 { 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 // UI
implementation 'androidx.appcompat:appcompat:1.5.1' implementation 'androidx.appcompat:appcompat:1.5.1'
implementation 'androidx.emoji2:emoji2:1.2.0' implementation 'androidx.emoji2:emoji2:1.2.0'

@ -41,6 +41,7 @@ import java.io.ByteArrayInputStream;
import java.io.File; import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap; import java.util.HashMap;
/** /**
@ -106,7 +107,11 @@ public final class AndroidacyActivity extends FoxActivity {
String device_id = uri.getQueryParameter("device_id"); String device_id = uri.getQueryParameter("device_id");
if (device_id == null) { if (device_id == null) {
// get from shared preferences // get from shared preferences
device_id = AndroidacyRepoData.generateDeviceId(); try {
device_id = AndroidacyRepoData.generateDeviceId();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
url = url + "&device_id=" + device_id; url = url + "&device_id=" + device_id;
} }
boolean allowInstall = intent.getBooleanExtra(Constants.EXTRA_ANDROIDACY_ALLOW_INSTALL, false); boolean allowInstall = intent.getBooleanExtra(Constants.EXTRA_ANDROIDACY_ALLOW_INSTALL, false);

@ -1,6 +1,5 @@
package com.fox2code.mmm.androidacy; package com.fox2code.mmm.androidacy;
import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.os.Looper; import android.os.Looper;
import android.util.Log; import android.util.Log;
@ -8,11 +7,6 @@ import android.widget.Toast;
import androidx.annotation.NonNull; 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.BuildConfig;
import com.fox2code.mmm.MainApplication; import com.fox2code.mmm.MainApplication;
import com.fox2code.mmm.R; 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.HttpException;
import com.fox2code.mmm.utils.PropUtils; import com.fox2code.mmm.utils.PropUtils;
import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.topjohnwu.superuser.Shell;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONException; import org.json.JSONException;
@ -31,6 +26,8 @@ import org.json.JSONObject;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
@ -48,6 +45,9 @@ public final class AndroidacyRepoData extends RepoData {
OK_HTTP_URL_BUILDER.build(); OK_HTTP_URL_BUILDER.build();
} }
@SuppressWarnings("unused")
public final String ClientID = BuildConfig.ANDROIDACY_CLIENT_ID;
private final boolean testMode; private final boolean testMode;
private final String host; private final String host;
public String token = MainApplication.getINSTANCE().getSharedPreferences("androidacy", 0).getString("pref_androidacy_api_token", null); 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 // Generates a unique device ID. This is used to identify the device in the API for rate
// limiting and fraud detection. // limiting and fraud detection.
public static String generateDeviceId() { public static String generateDeviceId() throws NoSuchAlgorithmException {
// Try to get the device ID from the shared preferences // Try to get the device ID from the shared preferences
SharedPreferences sharedPreferences = MainApplication.getINSTANCE().getSharedPreferences("androidacy", 0); SharedPreferences sharedPreferences = MainApplication.getINSTANCE().getSharedPreferences("androidacy", 0);
String deviceIdPref = sharedPreferences.getString("device_id", null); String deviceIdPref = sharedPreferences.getString("device_id", null);
if (deviceIdPref != null) { if (deviceIdPref != null) {
return deviceIdPref; return deviceIdPref;
} else { } else {
Context context = MainApplication.getINSTANCE().getApplicationContext(); // AAAA we're fingerprintiiiiing
FingerprintJSFactory factory = new FingerprintJSFactory(context); // Really not that scary - just hashes some device info. We can't even get the info
Configuration.Region region = Configuration.Region.US; // we originally hashed, so it's not like we can use it to track you.
Configuration configuration = new Configuration( String deviceId = null;
"NiZiHi266YaTLreOIOzc", // Get ro.serialno if it exists
region, // First, we need to get an su shell
region.getEndpointUrl(), Shell.Result result = Shell.cmd("getprop ro.serialno").exec();
true // Check if the command was successful
); if (result.isSuccess()) {
// Get the output
FingerprintJS fpjsClient = factory.createInstance( String output = result.getOut().get(0);
configuration // Check if the output is valid
); if (output != null && !output.isEmpty()) {
deviceId = output;
fpjsClient.getVisitorId(visitorIdResponse -> { }
// Use the ID }
String visitorId = visitorIdResponse.getVisitorId(); // Now, get device model, manufacturer, and Android version
// Save the ID in the shared preferences String deviceModel = android.os.Build.MODEL;
SharedPreferences.Editor editor = sharedPreferences.edit(); String deviceManufacturer = android.os.Build.MANUFACTURER;
editor.putString("device_id", visitorId); String androidVersion = android.os.Build.VERSION.RELEASE;
editor.apply(); // Append it all together
return null; deviceId += deviceModel + deviceManufacturer + androidVersion;
}); // Hash it
// return the id MessageDigest digest = MessageDigest.getInstance("SHA-256");
return sharedPreferences.getString("device_id", null); 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(); String deviceId = generateDeviceId();
try { try {
Http.doHttpGet("https://" + this.host + "/auth/me?token=" + token + "&device_id=" + deviceId, false); Http.doHttpGet("https://" + this.host + "/auth/me?token=" + token + "&device_id=" + deviceId, false);
@ -139,23 +154,8 @@ public final class AndroidacyRepoData extends RepoData {
} }
@Override @Override
protected boolean prepare() { protected boolean prepare() throws NoSuchAlgorithmException {
if (Http.needCaptchaAndroidacy()) return false; 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 // Implementation details discussed on telegram
// First, ping the server to check if it's alive // First, ping the server to check if it's alive
try { try {
@ -240,7 +240,7 @@ public final class AndroidacyRepoData extends RepoData {
} }
@Override @Override
protected List<RepoModule> populate(JSONObject jsonObject) throws JSONException { protected List<RepoModule> populate(JSONObject jsonObject) throws JSONException, NoSuchAlgorithmException {
if (!jsonObject.getString("status").equals("success")) if (!jsonObject.getString("status").equals("success"))
throw new JSONException("Response is not a success!"); throw new JSONException("Response is not a success!");
String name = jsonObject.optString("name", "Androidacy Modules Repo"); String name = jsonObject.optString("name", "Androidacy Modules Repo");
@ -353,12 +353,12 @@ public final class AndroidacyRepoData extends RepoData {
} }
@Override @Override
public String getUrl() { public String getUrl() throws NoSuchAlgorithmException {
return this.token == null ? this.url : return this.token == null ? this.url :
this.url + "?token=" + this.token + "&v=" + BuildConfig.VERSION_CODE + "&c=" + BuildConfig.VERSION_NAME + "&device_id=" + generateDeviceId(); 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 // Do not inject token for non Androidacy urls
if (!AndroidacyUtil.isAndroidacyLink(url)) return url; if (!AndroidacyUtil.isAndroidacyLink(url)) return url;
if (this.testMode) { if (this.testMode) {

@ -10,6 +10,7 @@ import org.json.JSONObject;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
public final class CustomRepoData extends RepoData { public final class CustomRepoData extends RepoData {
boolean loadedExternal; boolean loadedExternal;
@ -30,7 +31,7 @@ public final class CustomRepoData extends RepoData {
this.id : this.override; this.id : this.override;
} }
public void quickPrePopulate() throws IOException, JSONException { public void quickPrePopulate() throws IOException, JSONException, NoSuchAlgorithmException {
JSONObject jsonObject = new JSONObject( JSONObject jsonObject = new JSONObject(
new String(Http.doHttpGet(this.getUrl(), new String(Http.doHttpGet(this.getUrl(),
false), StandardCharsets.UTF_8)); false), StandardCharsets.UTF_8));

@ -21,6 +21,7 @@ import org.json.JSONObject;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
@ -81,11 +82,11 @@ public class RepoData extends XRepo {
} }
} }
protected boolean prepare() { protected boolean prepare() throws NoSuchAlgorithmException {
return true; return true;
} }
protected List<RepoModule> populate(JSONObject jsonObject) throws JSONException { protected List<RepoModule> populate(JSONObject jsonObject) throws JSONException, NoSuchAlgorithmException {
List<RepoModule> newModules = new ArrayList<>(); List<RepoModule> newModules = new ArrayList<>();
synchronized (this.populateLock) { synchronized (this.populateLock) {
String name = jsonObject.getString("name").trim(); String name = jsonObject.getString("name").trim();
@ -215,7 +216,7 @@ public class RepoData extends XRepo {
.getBoolean("pref_" + this.getPreferenceId() + "_enabled", this.isEnabledByDefault()); .getBoolean("pref_" + this.getPreferenceId() + "_enabled", this.isEnabledByDefault());
} }
public String getUrl() { public String getUrl() throws NoSuchAlgorithmException {
return this.url; return this.url;
} }

@ -67,6 +67,7 @@ import com.topjohnwu.superuser.internal.UiThreadHandler;
import org.json.JSONException; import org.json.JSONException;
import java.io.IOException; import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.util.HashSet; import java.util.HashSet;
import java.util.Objects; import java.util.Objects;
import java.util.Random; import java.util.Random;
@ -615,7 +616,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity {
boolean valid = false; boolean valid = false;
try { try {
valid = AndroidacyRepoData.getInstance().isValidToken(apiKey); valid = AndroidacyRepoData.getInstance().isValidToken(apiKey);
} catch (IOException ignored) {} } catch (IOException | NoSuchAlgorithmException ignored) {}
// If the key is valid, save it // If the key is valid, save it
if (valid) { if (valid) {
originalApiKeyRef[0] = apiKey; originalApiKeyRef[0] = apiKey;
@ -715,7 +716,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity {
public void run() { public void run() {
try { try {
customRepoData.quickPrePopulate(); customRepoData.quickPrePopulate();
} catch (IOException | JSONException e) { } catch (IOException | JSONException | NoSuchAlgorithmException e) {
Log.e(TAG, "Failed to preload repo values", e); Log.e(TAG, "Failed to preload repo values", e);
} }
UiThreadHandler.handler.post(() -> updateCustomRepoList(false)); UiThreadHandler.handler.post(() -> updateCustomRepoList(false));

@ -4,7 +4,6 @@ buildscript {
google() google()
mavenCentral() mavenCentral()
gradlePluginPortal() gradlePluginPortal()
maven { url 'https://maven.fpregistry.io/releases' }
maven { url 'https://jitpack.io' } maven { url 'https://jitpack.io' }
} }
project.ext.latestAboutLibsRelease = "10.5.0" project.ext.latestAboutLibsRelease = "10.5.0"

@ -6,7 +6,6 @@ dependencyResolutionManagement {
maven { maven {
url 'https://jitpack.io' url 'https://jitpack.io'
} }
maven { url 'https://maven.fpregistry.io/releases' }
} }
} }
rootProject.name = "MagiskModuleManager" rootProject.name = "MagiskModuleManager"

Loading…
Cancel
Save