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
local.properties
sentry.properties
androidacy.properties

@ -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<String>",
"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<String>",
"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'

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

@ -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<RepoModule> populate(JSONObject jsonObject) throws JSONException {
protected List<RepoModule> 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) {

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

@ -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<RepoModule> populate(JSONObject jsonObject) throws JSONException {
protected List<RepoModule> populate(JSONObject jsonObject) throws JSONException, NoSuchAlgorithmException {
List<RepoModule> 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;
}

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

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

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

Loading…
Cancel
Save