Add management of Unique App Identifier (UAID). This help identify app

install threw other google play client (Yalp, Aurora,...)
Fix #7
Thanks @stom79 for working on most of what's in Utils.java
This commit is contained in:
Schoumi 2018-08-31 15:35:42 +02:00
parent 416a3b4583
commit 1e65884d4e
6 changed files with 133 additions and 14 deletions

View File

@ -0,0 +1,82 @@
package org.eu.exodus_privacy.exodusprivacy;
import android.annotation.SuppressLint;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
public class Utils {
@SuppressLint("PackageManagerGetSignatures")
public static String getCertificateSHA1Fingerprint(PackageManager pm, String packageName) {
int flags = PackageManager.GET_SIGNATURES;
PackageInfo packageInfo = null;
try {
packageInfo = pm.getPackageInfo(packageName, flags);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
assert packageInfo != null;
Signature[] signatures = packageInfo.signatures;
StringBuilder builder = new StringBuilder();
builder.append(packageName);
for(Signature signature: signatures) {
InputStream input = new ByteArrayInputStream(signature.toByteArray());
CertificateFactory cf = null;
try {
cf = CertificateFactory.getInstance("X509");
} catch (CertificateException e) {
e.printStackTrace();
}
try {
assert cf != null;
X509Certificate c = (X509Certificate) cf.generateCertificate(input);
try {
MessageDigest md = MessageDigest.getInstance("SHA1");
byte[] publicKey = md.digest(c.getEncoded());
builder.append(' ');
builder.append(byte2HexFormatted(publicKey).toUpperCase());
} catch (NoSuchAlgorithmException e1) {
e1.printStackTrace();
}
} catch (CertificateException e) {
e.printStackTrace();
}
}
String hexString = null;
try {
MessageDigest md = MessageDigest.getInstance("SHA1");
byte[] publicKey = md.digest(builder.toString().getBytes());
hexString = byte2HexFormatted(publicKey);
} catch (NoSuchAlgorithmException e1) {
e1.printStackTrace();
}
assert hexString != null;
return hexString.toUpperCase();
}
private static String byte2HexFormatted(byte[] arr) {
StringBuilder str = new StringBuilder(arr.length * 2);
for (byte anArr : arr) {
String h = Integer.toHexString(anArr);
int l = h.length();
if (l == 1) h = "0" + h;
if (l > 2) h = h.substring(l - 2, l);
str.append(h.toUpperCase());
}
return str.toString();
}
}

View File

@ -28,10 +28,12 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import org.eu.exodus_privacy.exodusprivacy.R;
import org.eu.exodus_privacy.exodusprivacy.Utils;
import org.eu.exodus_privacy.exodusprivacy.databinding.AppItemBinding;
import org.eu.exodus_privacy.exodusprivacy.manager.DatabaseManager;
import org.eu.exodus_privacy.exodusprivacy.objects.Report;
import org.eu.exodus_privacy.exodusprivacy.objects.Tracker;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@ -48,6 +50,7 @@ public class ApplicationListAdapter extends RecyclerView.Adapter {
private String filter = "";
private final int HIDDEN_APP = 0;
private final int DISPLAYED_APP = 1;
private Context context;
private Comparator<PackageInfo> alphaPackageComparator = new Comparator<PackageInfo>() {
@ -59,8 +62,9 @@ public class ApplicationListAdapter extends RecyclerView.Adapter {
}
};
public ApplicationListAdapter(PackageManager manager, OnAppClickListener listener) {
public ApplicationListAdapter(Context context, PackageManager manager, OnAppClickListener listener) {
onAppClickListener = listener;
this.context = context;
setPackageManager(manager);
}
@ -75,7 +79,11 @@ public class ApplicationListAdapter extends RecyclerView.Adapter {
List<PackageInfo> toRemove = new ArrayList<>();
for (PackageInfo pkg : packages) {
if (!gStore.equals(packageManager.getInstallerPackageName(pkg.packageName))) {
toRemove.add(pkg);
String auid = Utils.getCertificateSHA1Fingerprint(packageManager,pkg.packageName);
String appuid = DatabaseManager.getInstance(context).getAUID(pkg.packageName);
if(!auid.equalsIgnoreCase(appuid)) {
toRemove.add(pkg);
}
}
}
packages.removeAll(toRemove);

View File

@ -24,6 +24,7 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.databinding.DataBindingUtil;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v7.widget.LinearLayoutManager;
@ -64,7 +65,7 @@ public class AppListFragment extends Fragment {
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
applistBinding = DataBindingUtil.inflate(inflater,R.layout.applist,container,false);
return applistBinding.getRoot();
}
@ -87,7 +88,7 @@ public class AppListFragment extends Fragment {
}
applistBinding.noPackageManager.setVisibility(View.GONE);
applistBinding.noAppFound.setVisibility(View.GONE);
adapter = new ApplicationListAdapter(packageManager, onAppClickListener);
adapter = new ApplicationListAdapter(getActivity().getApplicationContext(),packageManager, onAppClickListener);
if(adapter.getItemCount() == 0) {
applistBinding.noAppFound.setVisibility(View.VISIBLE);
} else {

View File

@ -44,13 +44,13 @@ public class DatabaseManager extends SQLiteOpenHelper {
public static DatabaseManager getInstance(Context context) {
if(instance == null)
instance = new DatabaseManager(context,"Exodus.db",null,1);
instance = new DatabaseManager(context,"Exodus.db",null,2);
return instance;
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("Create Table if not exists applications (id INTEGER primary key autoincrement, package TEXT, name TEXT, creator TEXT);");
db.execSQL("Create Table if not exists applications (id INTEGER primary key autoincrement, package TEXT, name TEXT, creator TEXT, auid TEXT);");
db.execSQL("Create Table if not exists reports (id INTEGER primary key, creation INTEGER, updateat INTEGER, downloads TEXT, version TEXT, version_code INTEGER, app_id INTEGER, foreign key(app_id) references applications(id));");
db.execSQL("Create Table if not exists trackers (id INTEGER primary key, name TEXT, creation_date INTEGER, code_signature TEXT, network_signature TEXT, website TEXT, description TEXT);");
@ -60,6 +60,9 @@ public class DatabaseManager extends SQLiteOpenHelper {
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// do nothing
if(newVersion >= 2) {
db.execSQL("Alter Table applications add column auid TEXT");
}
}
private boolean existReport(SQLiteDatabase db, long reportId) {
@ -119,6 +122,7 @@ public class DatabaseManager extends SQLiteOpenHelper {
values.put("package", application.packageName);
values.put("name",application.name);
values.put("creator",application.creator);
values.put("auid",application.auid);
if(!existApplication(db, application.packageName)) {
db.insert("applications", null, values);
@ -309,4 +313,18 @@ public class DatabaseManager extends SQLiteOpenHelper {
insertOrUpdateTracker(db,tracker);
}
}
public String getAUID(String packageName) {
String where = "package = ?";
String[] whereArgs = {packageName};
String[] columns = {"auid"};
Cursor cursor = getReadableDatabase().query("applications",columns,where,whereArgs,null,null,null,null);
String uaid="";
if(cursor.moveToFirst())
{
uaid = cursor.getString(0);
}
cursor.close();
return uaid;
}
}

View File

@ -42,10 +42,14 @@ import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Random;
import java.util.TimeZone;
import java.util.concurrent.Semaphore;
@ -218,13 +222,14 @@ public class NetworkManager {
if(object != null) {
List<String> handles = new ArrayList<>();
Map<String,String> handles = new HashMap<>();
try {
JSONArray applications = object.getJSONArray("applications");
for(int i = 0; i<applications.length(); i++) {
JSONObject app = applications.getJSONObject(i);
String handle = app.getString("handle");
handles.add(handle);
String auid = app.getString("app_uid");
handles.put(handle,auid);
}
} catch (JSONException e) {
mes.listener.onError(mes.context.getString(R.string.json_error));
@ -234,29 +239,30 @@ public class NetworkManager {
mes.listener.onSuccess();
}
private void getReports(Message mes, List<String> handles) {
private void getReports(Message mes, Map<String,String> handles) {
ArrayList<String> packages = mes.args.getStringArrayList("packages");
if(packages == null)
return;
packages.retainAll(handles);
packages.retainAll(handles.keySet());
// Add some random packages to avoid tracking
Random rand = new Random(Thread.currentThread().getId());
int alea = rand.nextInt(120) % 10 + 11;
List<String> handleList = new ArrayList<>(handles.keySet());
for(int i = 0 ; i < alea; i++) {
int val = rand.nextInt(handles.size());
packages.add(handles.get(val));
int val = rand.nextInt(handleList.size());
packages.add(handleList.get(val));
}
for(int i = 0; i < packages.size(); i++) {
mes.listener.onProgress(R.string.parse_application,i+1,packages.size());
getReport(mes,packages.get(i));
getReport(mes,packages.get(i),handles.get(packages.get(i)));
}
}
private void getReport(Message mes, String handle) {
private void getReport(Message mes, String handle, String auid) {
URL url;
try {
url = new URL(apiUrl+"search/"+handle);
@ -272,6 +278,7 @@ public class NetworkManager {
ArrayList<String> packages = mes.args.getStringArrayList("packages");
if(packages != null && packages.contains(handle)) {
Application app = parseApplication(application, handle);
app.auid = auid;
DatabaseManager.getInstance(mes.context).insertOrUpdateApplication(app);
}
} catch (JSONException e) {
@ -285,6 +292,7 @@ public class NetworkManager {
application.packageName = packageName;
application.creator = object.getString("creator");
application.name = object.getString("name");
//parse Report
application.reports = new HashSet<>();
JSONArray reports = object.getJSONArray("reports");

View File

@ -26,6 +26,7 @@ public class Application {
public String name;
public String creator;
public Set<Report> reports;
public String auid;
@Override
public boolean equals(Object o) {
@ -35,6 +36,7 @@ public class Application {
Application that = (Application) o;
if (id != that.id) return false;
if (!auid.equals(that.auid)) return false;
return packageName.equals(that.packageName);
}