diff --git a/.idea/modules.xml b/.idea/modules.xml index 194f1e0..a24d3b2 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -2,7 +2,7 @@ - + diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 35eb1dd..94a25f7 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/TLSMetric.iml b/TLSMetric.iml deleted file mode 100644 index 18fa739..0000000 --- a/TLSMetric.iml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/app.iml b/app/app.iml index abc6f77..a49a45d 100644 --- a/app/app.iml +++ b/app/app.iml @@ -1,5 +1,5 @@ - + @@ -63,17 +63,9 @@ - + - - - - - - - - @@ -82,31 +74,61 @@ + + + + + + + + - - - - - - + + + + + + + + + + + + + + - - - + - + + + + + + + + + + + + + + + + + + + - - - - - + + + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 7ef9581..2845142 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,34 +1,29 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 23 - buildToolsVersion '25.0.0' - + compileSdkVersion 25 + buildToolsVersion "25.0.0" defaultConfig { applicationId "org.secuso.privacyfriendlytlsmetric" minSdkVersion 17 - targetSdkVersion 23 - versionCode 11 - versionName "1.1 - PFE" - + targetSdkVersion 25 + versionCode 1 + versionName "1.0" + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } - buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } - sourceSets { - main { - jni.srcDirs = ['libs'] - } - } } dependencies { - compile fileTree(include: ['*.jar'], dir: 'libs') + compile fileTree(dir: 'libs', include: ['*.jar']) + androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { + exclude group: 'com.android.support', module: 'support-annotations' + }) + compile 'com.android.support:appcompat-v7:25.0.0' testCompile 'junit:junit:4.12' - compile 'com.android.support:appcompat-v7:23.1.0' - compile 'com.android.support:design:23.1.0' } diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 066855c..5984673 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -1,6 +1,6 @@ # Add project specific ProGuard rules here. # By default, the flags in this file are appended to flags specified -# in E:\Programme\Android\sdk/tools/proguard/proguard-android.txt +# in C:\Users\fs\AppData\Local\Android\Sdk/tools/proguard/proguard-android.txt # You can edit the include path and order by changing the proguardFiles # directive in build.gradle. # diff --git a/app/src/androidTest/java/org/secuso/privacyfriendlytlsmetric/ExampleInstrumentedTest.java b/app/src/androidTest/java/org/secuso/privacyfriendlytlsmetric/ExampleInstrumentedTest.java new file mode 100644 index 0000000..d44bbb2 --- /dev/null +++ b/app/src/androidTest/java/org/secuso/privacyfriendlytlsmetric/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package org.secuso.privacyfriendlytlsmetric; + +import android.content.Context; +import android.support.test.InstrumentationRegistry; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumentation test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() throws Exception { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getTargetContext(); + + assertEquals("org.secuso.privacyfriendlytlsmetric", appContext.getPackageName()); + } +} diff --git a/app/src/main/java/org/secuso/privacyfriendlytlsmetric/Activities/EvidenceActivity.java b/app/src/main/java/org/secuso/privacyfriendlytlsmetric/Activities/EvidenceActivity.java new file mode 100644 index 0000000..67329bd --- /dev/null +++ b/app/src/main/java/org/secuso/privacyfriendlytlsmetric/Activities/EvidenceActivity.java @@ -0,0 +1,244 @@ +/* + TLSMetric + - Copyright (2015, 2016) Felix Tsala Schiller + + ################################################################### + + This file is part of TLSMetric. + + TLSMetric is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + TLSMetric is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with TLSMetric. If not, see . + + Diese Datei ist Teil von TLSMetric. + + TLSMetric ist Freie Software: Sie können es unter den Bedingungen + der GNU General Public License, wie von der Free Software Foundation, + Version 3 der Lizenz oder (nach Ihrer Wahl) jeder späteren + veröffentlichten Version, weiterverbreiten und/oder modifizieren. + + TLSMetric wird in der Hoffnung, dass es nützlich sein wird, aber + OHNE JEDE GEWÄHRLEISTUNG, bereitgestellt; sogar ohne die implizite + Gewährleistung der MARKTFÄHIGKEIT oder EIGNUNG FÜR EINEN BESTIMMTEN ZWECK. + Siehe die GNU General Public License für weitere Details. + + Sie sollten eine Kopie der GNU General Public License zusammen mit diesem + Programm erhalten haben. Wenn nicht, siehe . + */ + +package org.secuso.privacyfriendlytlsmetric.Activities; + +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.Toolbar; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.ImageView; +import android.widget.ListView; +import android.widget.TextView; +import android.widget.Toast; + +import java.util.ArrayList; + +import org.secuso.privacyfriendlytlsmetric.Assistant.Const; +import org.secuso.privacyfriendlytlsmetric.Assistant.ContextSingleton; +import org.secuso.privacyfriendlytlsmetric.ConnectionAnalysis.Report; +import org.secuso.privacyfriendlytlsmetric.ConnectionAnalysis.Evidence; +import org.secuso.privacyfriendlytlsmetric.ConnectionAnalysis.PackageInformation; +import org.secuso.privacyfriendlytlsmetric.R; + +/** + * Lists a Report of each detected connection. Most critical if several reports exist. + */ +public class EvidenceActivity extends AppCompatActivity{ + + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_evidence); + ContextSingleton.setContext(this); + Evidence.newWarnings = 0; + + //Toolbar + Toolbar toolbar = (Toolbar) findViewById(R.id.evidence_toolbar); + setSupportActionBar(toolbar); + //toolbar.setLogo(R.mipmap.icon); + toolbar.setLogoDescription(R.string.app_name); + + + //EvidenceList + final ListView listview = (ListView) findViewById(android.R.id.list); + + final EvidenceAdapter adapter; + if(Evidence.mEvidence != null){ + adapter = new EvidenceAdapter(this, copyArrayList(Evidence.getSortedEvidence())); + } else { + if(Const.IS_DEBUG) Log.e(Const.LOG_TAG, "Evidence list not existing or empty!"); + adapter = new EvidenceAdapter(this, new ArrayList()); + } + + listview.setAdapter(adapter); + + listview.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView parent, final View view, + int position, long id) { + final Report ann = (Report) parent.getItemAtPosition(position); + view.animate().setDuration(500).alpha((float)0.5) + .withEndAction(new Runnable() { + @Override + public void run() { + if (ann.filter.severity != -1) { + Evidence.setSortedEvidenceDetail(ann.srcPort); + Intent intent = new Intent(ContextSingleton.getContext(), EvidenceDetailActivity.class); + startActivity(intent); + } else { + Toast toast = Toast.makeText(ContextSingleton.getContext(), "No detail availiable for this connection", Toast.LENGTH_LONG); + toast.show(); + } + } + }); + } + + }); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.menu_tlsmetric, menu); + return true; + } + + //menu + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.action_settings: + return true; + + case R.id.action_back: + Intent intent = new Intent(ContextSingleton.getContext(), MainActivity.class); + startActivity(intent); + return true; + + case R.id.action_refresh: + Evidence.disposeInactiveEvidence(); + Evidence.updateConnections(); + ListView listview = (ListView) findViewById(android.R.id.list); + EvidenceAdapter adapter = new EvidenceAdapter(ContextSingleton.getContext(), + copyArrayList(Evidence.getSortedEvidence())); + listview.setAdapter(adapter); + adapter.notifyDataSetChanged(); + return true; + + default: + return super.onOptionsItemSelected(item); + } + } + + //Customised Adapter class for display of Evidence Reports + private class EvidenceAdapter extends ArrayAdapter { + + private Report[] anns; + private final Context context; + + public EvidenceAdapter(Context context, ArrayList AnnList) { + super(context, R.layout.evidence_list_entry, AnnList); + this.context = context; + this.anns = new Report[AnnList.size()]; + for(int i = 0; i < AnnList.size(); i++){ + this.anns[i] = AnnList.get(i); + } + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + LayoutInflater inflater = (LayoutInflater) context + .getSystemService(Context.LAYOUT_INFLATER_SERVICE); + + View rowView = inflater.inflate(R.layout.evidence_list_entry, parent, false); + + //if unknown app (-1) try again to get pid by sourcePort; + Report ann = anns[position]; + if(ann.pid == -1 && ann.uid == -1){ + ann.pid = Evidence.getPidByPort(ann.srcPort); + ann.uid = Evidence.getUidByPort(ann.srcPort); + if(Const.IS_DEBUG)Log.d(Const.LOG_TAG, "Rescan of pid and uid. srcPort: " + + ann.srcPort + " new pid: " + ann.pid + + " new uid: " + ann.uid); + } + + PackageInformation pi = Evidence.getPackageInformation(ann.pid, ann.uid); + //First Line Text + TextView firstLine = (TextView) rowView.findViewById(R.id.firstLine); + String first = pi.packageName; + firstLine.setText(first); + + //second Line Text + TextView secondLine = (TextView) rowView.findViewById(R.id.secondLine); + String second = "Host: " + ann.url; + secondLine.setText(second); + + //App icon + ImageView imageView = (ImageView) rowView.findViewById(R.id.icon); + imageView.setImageDrawable(pi.icon); + + //Status icon + ImageView imageStatusView = (ImageView) rowView.findViewById(R.id.statusIcon); + int severity = ann.filter.severity; + if(severity == 3){ + imageStatusView.setImageResource(R.mipmap.icon_warn_red); + } else if (severity == 2){ + imageStatusView.setImageResource(R.mipmap.icon_warn_orange); + } else if (severity == 1) { + imageStatusView.setImageResource(R.mipmap.icon_warn_orange); + } else if (severity == 0){ + imageStatusView.setImageResource(R.mipmap.icon_ok); + } else if (severity == -1) { + imageStatusView.setImageResource(R.mipmap.icon_quest); + } + + //Status Text + TextView statusLine = (TextView) rowView.findViewById(R.id.statusLine); + String status = "Level :" + severity; + statusLine.setText(status); + return rowView; + } + + } + + @Override + public void onDestroy(){ + super.onDestroy(); + } + + public ArrayList copyArrayList(ArrayList anns){ + ArrayList copy = new ArrayList<>(); + for(Report ann: anns){ + copy.add(ann); + } + return copy; + } + + +} \ No newline at end of file diff --git a/app/src/main/java/org/secuso/privacyfriendlytlsmetric/Activities/EvidenceDetailActivity.java b/app/src/main/java/org/secuso/privacyfriendlytlsmetric/Activities/EvidenceDetailActivity.java new file mode 100644 index 0000000..6f53c40 --- /dev/null +++ b/app/src/main/java/org/secuso/privacyfriendlytlsmetric/Activities/EvidenceDetailActivity.java @@ -0,0 +1,248 @@ +/* + TLSMetric + - Copyright (2015, 2016) Felix Tsala Schiller + + ################################################################### + + This file is part of TLSMetric. + + TLSMetric is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + TLSMetric is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with TLSMetric. If not, see . + + Diese Datei ist Teil von TLSMetric. + + TLSMetric ist Freie Software: Sie können es unter den Bedingungen + der GNU General Public License, wie von der Free Software Foundation, + Version 3 der Lizenz oder (nach Ihrer Wahl) jeder späteren + veröffentlichten Version, weiterverbreiten und/oder modifizieren. + + TLSMetric wird in der Hoffnung, dass es nützlich sein wird, aber + OHNE JEDE GEWÄHRLEISTUNG, bereitgestellt; sogar ohne die implizite + Gewährleistung der MARKTFÄHIGKEIT oder EIGNUNG FÜR EINEN BESTIMMTEN ZWECK. + Siehe die GNU General Public License für weitere Details. + + Sie sollten eine Kopie der GNU General Public License zusammen mit diesem + Programm erhalten haben. Wenn nicht, siehe . + */ + +package org.secuso.privacyfriendlytlsmetric.Activities; + +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.Toolbar; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.ImageView; +import android.widget.ListView; +import android.widget.TextView; +import android.widget.Toast; + +import java.util.ArrayList; + +import org.secuso.privacyfriendlytlsmetric.Assistant.Const; +import org.secuso.privacyfriendlytlsmetric.Assistant.ContextSingleton; +import org.secuso.privacyfriendlytlsmetric.ConnectionAnalysis.Report; +import org.secuso.privacyfriendlytlsmetric.ConnectionAnalysis.Evidence; +import org.secuso.privacyfriendlytlsmetric.ConnectionAnalysis.PackageInformation; +import org.secuso.privacyfriendlytlsmetric.R; + +/** + * Evidence Detail Panel. List all reports of a connection, invoked by Evidence Panel (EvidenceActivity) + */ +public class EvidenceDetailActivity extends AppCompatActivity{ + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_evidence); + ContextSingleton.setContext(this); + + //Toolbar + Toolbar toolbar = (Toolbar) findViewById(R.id.evidence_toolbar); + setSupportActionBar(toolbar); + //toolbar.setLogo(R.mipmap.icon); + toolbar.setLogoDescription(R.string.app_name); + + + //EvidenceList + final ListView listview = (ListView) findViewById(android.R.id.list); + + final DetailAdapter adapter; + if(Evidence.mEvidence != null){ + adapter = new DetailAdapter(this, copyArrayList(Evidence.mEvidenceDetail)); + } else { + if(Const.IS_DEBUG) Log.e(Const.LOG_TAG, "Evidence list not existing or empty!"); + adapter = new DetailAdapter(this, new ArrayList()); + Toast.makeText(EvidenceDetailActivity.this, "No connections availiable.", Toast.LENGTH_SHORT).show(); + } + + listview.setAdapter(adapter); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.menu_tlsmetric, menu); + return true; + } + + //menu + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.action_settings: + return true; + + case R.id.action_back: + Intent intent = new Intent(ContextSingleton.getContext(), EvidenceActivity.class); + startActivity(intent); + return true; + + case R.id.action_refresh: + Evidence.disposeInactiveEvidence(); + Evidence.updateConnections(); + ListView listview = (ListView) findViewById(android.R.id.list); + DetailAdapter adapter = (DetailAdapter)listview.getAdapter(); + adapter.notifyDataSetChanged(); + return true; + + default: + return super.onOptionsItemSelected(item); + } + } + + + + private class DetailAdapter extends ArrayAdapter { + + private final Report[] anns; + private final Context context; + + public DetailAdapter(Context context, ArrayList AnnList) { + super(context, R.layout.evidence_detail_entry, AnnList); + this.context = context; + this.anns = new Report[AnnList.size()]; + for(int i = 0; i < AnnList.size(); i++){ + this.anns[i] = AnnList.get(i); + } + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + LayoutInflater inflater = (LayoutInflater) context + .getSystemService(Context.LAYOUT_INFLATER_SERVICE); + + View rowView = inflater.inflate(R.layout.evidence_detail_entry, parent, false); + + PackageInformation pi = Evidence.getPackageInformation(anns[position].pid, anns[position].uid); + //First Line Text + TextView firstLine = (TextView) rowView.findViewById(R.id.firstLine); + String first = pi.packageName; + firstLine.setText(first); + + //Detail Text Field + TextView detail = (TextView) rowView.findViewById(R.id.detail); + String detailText = generateDetail(anns[position]); + detail.setText(detailText); + + //App icon + ImageView imageView = (ImageView) rowView.findViewById(R.id.icon); + imageView.setImageDrawable(pi.icon); + + //Status icon + ImageView imageStatusView = (ImageView) rowView.findViewById(R.id.statusIcon); + int severity = anns[position].filter.severity; + if(severity == 3){ + imageStatusView.setImageResource(R.mipmap.icon_warn_red); + } else if (severity == 2){ + imageStatusView.setImageResource(R.mipmap.icon_warn_orange); + } else if (severity == 1) { + imageStatusView.setImageResource(R.mipmap.icon_warn_orange); + } else if (severity == 0){ + imageStatusView.setImageResource(R.mipmap.icon_ok); + } else if (severity == -1) { + imageStatusView.setImageResource(R.mipmap.icon_quest); + } + + return rowView; + } + + } + + //Generate detail information based ob the report. + private String generateDetail(Report ann) { + String detail = ann.filter.description; + detail += " \n Severity: " + ann.filter.severity ; + switch (ann.filter.severity){ + case -1: + detail += " - connection information."; + break; + case 0: + detail += " - secure connection."; + break; + case 1: + detail += " - minor warning."; + break; + case 2: + detail += " - major warning."; + break; + case 3: + detail += " - unencrypted connection."; + break; + default: + break; + } + detail += " \n Protocol: " + ann.filter.protocol; + detail += " \n Time: " + ann.timestamp.toString(); + detail += " \n Target Host IP: " + ann.dstAddr.getHostAddress(); + detail += " \n Target Hostname: " + ann.dstAddr.getHostName(); + detail += " \n Source Port: " + ann.srcPort; + detail += " \n Destination Port: " + ann.srcPort; + if(ann.pid == -1){ + detail += " \n App process ID: UNKNOWN"; + } else { + detail += " \n App process ID (PID) : " + ann.pid; + } + if(ann.uid == -1){ + detail += " \n App user ID (UID) : UNKNOWN"; + } else { + detail += " \n App process ID: " + ann.uid; + } + return detail; + } + + @Override + public void onDestroy(){ + super.onDestroy(); + } + + //Poor excuse for a copy constructor + //TODO: Make real copy constructors + public ArrayList copyArrayList(ArrayList anns){ + ArrayList copy = new ArrayList<>(); + for(Report ann: anns){ + copy.add(ann); + } + return copy; + } + + +} \ No newline at end of file diff --git a/app/src/main/java/org/secuso/privacyfriendlytlsmetric/Activities/MainActivity.java b/app/src/main/java/org/secuso/privacyfriendlytlsmetric/Activities/MainActivity.java new file mode 100644 index 0000000..833170e --- /dev/null +++ b/app/src/main/java/org/secuso/privacyfriendlytlsmetric/Activities/MainActivity.java @@ -0,0 +1,173 @@ +/* + TLSMetric + - Copyright (2015, 2016) Felix Tsala Schiller + + ################################################################### + + This file is part of TLSMetric. + + TLSMetric is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + TLSMetric is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with TLSMetric. If not, see . + + Diese Datei ist Teil von TLSMetric. + + TLSMetric ist Freie Software: Sie können es unter den Bedingungen + der GNU General Public License, wie von der Free Software Foundation, + Version 3 der Lizenz oder (nach Ihrer Wahl) jeder späteren + veröffentlichten Version, weiterverbreiten und/oder modifizieren. + + TLSMetric wird in der Hoffnung, dass es nützlich sein wird, aber + OHNE JEDE GEWÄHRLEISTUNG, bereitgestellt; sogar ohne die implizite + Gewährleistung der MARKTFÄHIGKEIT oder EIGNUNG FÜR EINEN BESTIMMTEN ZWECK. + Siehe die GNU General Public License für weitere Details. + + Sie sollten eine Kopie der GNU General Public License zusammen mit diesem + Programm erhalten haben. Wenn nicht, siehe . + */ + +package org.secuso.privacyfriendlytlsmetric.Activities; + +import android.content.Intent; +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.util.Log; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.widget.Button; +import android.widget.TextView; +import android.widget.Toast; + +import org.secuso.privacyfriendlytlsmetric.Assistant.Const; +import org.secuso.privacyfriendlytlsmetric.Assistant.ContextSingleton; +import org.secuso.privacyfriendlytlsmetric.Assistant.ToolBox; +import org.secuso.privacyfriendlytlsmetric.R; + + +/** + * Activity of the Main Panel. Start and stop everything from here. + */ +public class MainActivity extends AppCompatActivity { + + private boolean serviceIsRunning; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + //Fill the Singleton + ContextSingleton.setContext(this); + + //TODO: Change Sudo tests + // Test for Root Acces and Logging + CheckDependencies.checkSu(); + + serviceIsRunning = ToolBox.isAnalyzerServiceRunning(); + + final Button startStop = (Button) findViewById(R.id.startStop); + startStop.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + TextView infoText = (TextView) findViewById(R.id.infoText); + if(!serviceIsRunning) { + startStop.setBackground(getResources().getDrawable(R.drawable.power_working)); + serviceIsRunning = true; + infoText.setText(R.string.info_starting); + if(Const.IS_DEBUG) Log.d(Const.LOG_TAG, "begin start sequence."); + DumpHandler.start(); + infoText.setText(R.string.info_waiting); + DumpHandler.startAnalyzerService(); + infoText.setText(R.string.info_running); + startStop.setBackground(getResources().getDrawable(R.drawable.power_on)); + minimizeActivity(); + } else { + startStop.setBackground(getResources().getDrawable(R.drawable.power_working)); + serviceIsRunning = false; + if(Const.IS_DEBUG) Log.d(Const.LOG_TAG, "begin stop sequence."); + infoText.setText(R.string.info_stopping); + DumpHandler.stopAnalyzerService(); + DumpHandler.stop(); + infoText.setText(R.string.info_handle); + startStop.setBackground(getResources().getDrawable(R.drawable.power_off)); + } + } + }); + + Button gotoEvidence = (Button) findViewById(R.id.gotoEvidence); + gotoEvidence.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + if(serviceIsRunning){ + Intent intent = new Intent(ContextSingleton.getContext(), EvidenceActivity.class); + startActivity(intent); + } else { + Toast toast = Toast.makeText(ContextSingleton.getContext(), "TLS Metric service is not yet running.", Toast.LENGTH_LONG); + toast.show(); + } + + } + }); + + + if(serviceIsRunning){ + startStop.setBackground(getResources().getDrawable(R.drawable.power_on)); + } else { + startStop.setBackground(getResources().getDrawable(R.drawable.power_off)); + } + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.menu_tlsmetric, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + // Handle action bar item clicks here. The action bar will + // automatically handle clicks on the Home/Up button, so long + // as you specify a parent activity in AndroidManifest.xml. + int id = item.getItemId(); + + //noinspection SimplifiableIfStatement + if (id == R.id.action_settings) { + return true; + } + + return super.onOptionsItemSelected(item); + } + + @Override + public void onDestroy(){ + super.onDestroy(); + + } + + // Initiate VPN Capture Service + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + if (resultCode == RESULT_OK) { + VpnCaptureService.start(this); + } + } + + //Call this to minimize the activity + private void minimizeActivity(){ + Intent startMain = new Intent(Intent.ACTION_MAIN); + startMain.addCategory(Intent.CATEGORY_HOME); + startMain.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + startActivity(startMain); + } + +} diff --git a/app/src/main/java/org/secuso/privacyfriendlytlsmetric/Assistant/Const.java b/app/src/main/java/org/secuso/privacyfriendlytlsmetric/Assistant/Const.java new file mode 100644 index 0000000..b11d7a6 --- /dev/null +++ b/app/src/main/java/org/secuso/privacyfriendlytlsmetric/Assistant/Const.java @@ -0,0 +1,61 @@ +/* + TLSMetric + - Copyright (2015, 2016) Felix Tsala Schiller + + ################################################################### + + This file is part of TLSMetric. + + TLSMetric is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + TLSMetric is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with TLSMetric. If not, see . + + Diese Datei ist Teil von TLSMetric. + + TLSMetric ist Freie Software: Sie können es unter den Bedingungen + der GNU General Public License, wie von der Free Software Foundation, + Version 3 der Lizenz oder (nach Ihrer Wahl) jeder späteren + veröffentlichten Version, weiterverbreiten und/oder modifizieren. + + TLSMetric wird in der Hoffnung, dass es nützlich sein wird, aber + OHNE JEDE GEWÄHRLEISTUNG, bereitgestellt; sogar ohne die implizite + Gewährleistung der MARKTFÄHIGKEIT oder EIGNUNG FÜR EINEN BESTIMMTEN ZWECK. + Siehe die GNU General Public License für weitere Details. + + Sie sollten eine Kopie der GNU General Public License zusammen mit diesem + Programm erhalten haben. Wenn nicht, siehe . + */ + +package org.secuso.privacyfriendlytlsmetric.Assistant; + +/** + * Storing constant values for app + */ + +public interface Const { + + boolean IS_DEBUG = true; + + int CHANNEL_TIMEOUT_UDP = 10000; + int CHANNEL_TIMEOUT_TCP = 3800; + + String LOG_TAG = "TLSMetric"; + String FILE_IF_LIST = "iflist"; + + //File info for AnalyzerService + String FILE_TCPDUMP = "tcpdump"; + String FILE_DUMP = "dump.pcap"; + String FILE_FILTER = "filter.ini"; + String PARAMS = "-w"; + Object FILE_RESOLVE_PID = "resolve"; + int ANNOUNCEMENT_TIMEOUT = 1; +} diff --git a/app/src/main/java/org/secuso/privacyfriendlytlsmetric/Assistant/ContextSingleton.java b/app/src/main/java/org/secuso/privacyfriendlytlsmetric/Assistant/ContextSingleton.java new file mode 100644 index 0000000..1782e91 --- /dev/null +++ b/app/src/main/java/org/secuso/privacyfriendlytlsmetric/Assistant/ContextSingleton.java @@ -0,0 +1,63 @@ +/* + TLSMetric + - Copyright (2015, 2016) Felix Tsala Schiller + + ################################################################### + + This file is part of TLSMetric. + + TLSMetric is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + TLSMetric is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with TLSMetric. If not, see . + + Diese Datei ist Teil von TLSMetric. + + TLSMetric ist Freie Software: Sie können es unter den Bedingungen + der GNU General Public License, wie von der Free Software Foundation, + Version 3 der Lizenz oder (nach Ihrer Wahl) jeder späteren + veröffentlichten Version, weiterverbreiten und/oder modifizieren. + + TLSMetric wird in der Hoffnung, dass es nützlich sein wird, aber + OHNE JEDE GEWÄHRLEISTUNG, bereitgestellt; sogar ohne die implizite + Gewährleistung der MARKTFÄHIGKEIT oder EIGNUNG FÜR EINEN BESTIMMTEN ZWECK. + Siehe die GNU General Public License für weitere Details. + + Sie sollten eine Kopie der GNU General Public License zusammen mit diesem + Programm erhalten haben. Wenn nicht, siehe . + */ + +package org.secuso.privacyfriendlytlsmetric.Assistant; + + +import android.app.Activity; +import android.content.Context; + +/** + * Singleton which holds the context of the current/last activity. + */ + +public class ContextSingleton { + + private static Activity gContext; + + public static void setContext( Activity activity) { + gContext = activity; + } + + public static Activity getActivity() { + return gContext; + } + + public static Context getContext() { + return gContext; + } +} diff --git a/app/src/main/java/org/secuso/privacyfriendlytlsmetric/Assistant/ExecuteCommand.java b/app/src/main/java/org/secuso/privacyfriendlytlsmetric/Assistant/ExecuteCommand.java new file mode 100644 index 0000000..23c9370 --- /dev/null +++ b/app/src/main/java/org/secuso/privacyfriendlytlsmetric/Assistant/ExecuteCommand.java @@ -0,0 +1,125 @@ +/* + TLSMetric + - Copyright (2015, 2016) Felix Tsala Schiller + + ################################################################### + + This file is part of TLSMetric. + + TLSMetric is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + TLSMetric is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with TLSMetric. If not, see . + + Diese Datei ist Teil von TLSMetric. + + TLSMetric ist Freie Software: Sie können es unter den Bedingungen + der GNU General Public License, wie von der Free Software Foundation, + Version 3 der Lizenz oder (nach Ihrer Wahl) jeder späteren + veröffentlichten Version, weiterverbreiten und/oder modifizieren. + + TLSMetric wird in der Hoffnung, dass es nützlich sein wird, aber + OHNE JEDE GEWÄHRLEISTUNG, bereitgestellt; sogar ohne die implizite + Gewährleistung der MARKTFÄHIGKEIT oder EIGNUNG FÜR EINEN BESTIMMTEN ZWECK. + Siehe die GNU General Public License für weitere Details. + + Sie sollten eine Kopie der GNU General Public License zusammen mit diesem + Programm erhalten haben. Wenn nicht, siehe . + */ + +package org.secuso.privacyfriendlytlsmetric.Assistant; + +import android.util.Log; + +import java.io.ByteArrayOutputStream; +import java.io.Closeable; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStream; + +/* + * Handles the execution of shell commands. + */ +public class ExecuteCommand extends Thread { + + //Execute user commands. + public static void user(String string) { + if (Const.IS_DEBUG) Log.d(Const.LOG_TAG, "Executing as user: " + string); + try { + Process user = Runtime.getRuntime().exec(string); + try { + user.waitFor(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + + //Execute user commands and get the result. + public static String userForResult(String string) { + if (Const.IS_DEBUG) Log.d(Const.LOG_TAG, "Executing for result as user: " + string); + String res = ""; + DataOutputStream outputStream = null; + InputStream response = null; + try { + Process user = Runtime.getRuntime().exec(string); + + outputStream = new DataOutputStream(user.getOutputStream()); + response = user.getInputStream(); + + outputStream.writeBytes("exit\n"); + outputStream.flush(); + try { + user.waitFor(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + res = readFully(response); + } catch (IOException e) { + e.printStackTrace(); + } finally { + closeSilently(outputStream, response); + } + return res; + } + + //Read the command output and return an utf8 string. + public static String readFully(InputStream is) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + int length = 0; + while ((length = is.read(buffer)) != -1) { + baos.write(buffer, 0, length); + } + return baos.toString("UTF-8"); + } + + //Closes a variety of closable objects. + public static void closeSilently(Object... xs) { + // Note: on Android API levels prior to 19 Socket does not implement Closeable + for (Object x : xs) { + if (x != null) { + try { + if (x instanceof Closeable) { + ((Closeable) x).close(); + } else { + Log.d(Const.LOG_TAG, "cannot close: " + x); + throw new RuntimeException("cannot close " + x); + } + } catch (Throwable e) { + e.printStackTrace(); + } + } + } + } +} diff --git a/app/src/main/java/org/secuso/privacyfriendlytlsmetric/Assistant/ToolBox.java b/app/src/main/java/org/secuso/privacyfriendlytlsmetric/Assistant/ToolBox.java new file mode 100644 index 0000000..0b0cccd --- /dev/null +++ b/app/src/main/java/org/secuso/privacyfriendlytlsmetric/Assistant/ToolBox.java @@ -0,0 +1,139 @@ +/* + TLSMetric + - Copyright (2015, 2016) Felix Tsala Schiller + + ################################################################### + + This file is part of TLSMetric. + + TLSMetric is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + TLSMetric is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with TLSMetric. If not, see . + + Diese Datei ist Teil von TLSMetric. + + TLSMetric ist Freie Software: Sie können es unter den Bedingungen + der GNU General Public License, wie von der Free Software Foundation, + Version 3 der Lizenz oder (nach Ihrer Wahl) jeder späteren + veröffentlichten Version, weiterverbreiten und/oder modifizieren. + + TLSMetric wird in der Hoffnung, dass es nützlich sein wird, aber + OHNE JEDE GEWÄHRLEISTUNG, bereitgestellt; sogar ohne die implizite + Gewährleistung der MARKTFÄHIGKEIT oder EIGNUNG FÜR EINEN BESTIMMTEN ZWECK. + Siehe die GNU General Public License für weitere Details. + + Sie sollten eine Kopie der GNU General Public License zusammen mit diesem + Programm erhalten haben. Wenn nicht, siehe . + */ + +package org.secuso.privacyfriendlytlsmetric.Assistant; + +import android.app.ActivityManager; +import android.content.Context; +import android.util.Log; + +import java.io.File; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.util.Enumeration; + +/** + * All the litte helpers, used by more than one layer + */ +public class ToolBox{ + + private static final char[] hexCode = "0123456789ABCDEF".toCharArray(); + + //Convert byte[] to HexString + public static String printHexBinary(byte[] data) { + StringBuilder r = new StringBuilder(data.length * 2); + for (byte b : data) { + r.append(hexCode[(b >> 4) & 0xF]); + r.append(hexCode[(b & 0xF)]); + } + return r.toString(); + } + + //Convert HexString to byte[] + public static byte[] hexStringToByteArray(String s) { + byte[] b = new byte[s.length() / 2]; + for (int i = 0; i < b.length; i++) { + int index = i * 2; + int v = Integer.parseInt(s.substring(index, index + 2), 16); + b[i] = (byte) v; + } + return b; + } + + //Convert byte[] to wireshark import-string. + public static String printExportHexString(byte[] data) { + String hexString = printHexBinary(data); + String export = "000000 "; + for (int i = 0; i + 1 < hexString.length(); i += 2) { + export += " " + hexString.substring(i, i + 2); + } + export += " ......"; + return export; + } + + //Returns active network interfaces + public String getIfs(Context context){ + //read from command: netcfg | grep UP + + String filePath = context.getFilesDir().getAbsolutePath() + File.separator + Const.FILE_IF_LIST; + if(Const.IS_DEBUG)Log.d(Const.LOG_TAG, "Try to get active interfaces to" + filePath); + ExecuteCommand.user("rm " + filePath); + ExecuteCommand.user("netcfg | grep UP -> " + filePath); + String result = ExecuteCommand.userForResult("cat " + filePath); + return result; + } + + //Char to value + private int hexToBin(char ch) { + if ('0' <= ch && ch <= '9') return ch - '0'; + if ('A' <= ch && ch <= 'F') return ch - 'A' + 10; + if ('a' <= ch && ch <= 'f') return ch - 'a' + 10; + return -1; + } + + //Lookup local IP address + public static InetAddress getLocalAddress(){ + try { + for (Enumeration en = NetworkInterface.getNetworkInterfaces(); + en.hasMoreElements();) { + NetworkInterface intf = en.nextElement(); + for (Enumeration enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();) { + InetAddress inetAddress = enumIpAddr.nextElement(); + if (!inetAddress.isLoopbackAddress()) { + return inetAddress; + } + } + } + } catch (Exception e) { + Log.e(Const.LOG_TAG, "Error while obtaining local address"); + e.printStackTrace(); + } + return null; + } + + //Test if service is active. + public static boolean isAnalyzerServiceRunning() { + ActivityManager manager = (ActivityManager)ContextSingleton.getContext().getSystemService(Context.ACTIVITY_SERVICE); + for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) { + if (AnalyserService.class.getName().equals(service.service.getClassName())) { + return true; + } + } + return false; + } + +} diff --git a/app/src/main/java/org/secuso/privacyfriendlytlsmetric/ConnectionAnalysis/Evidence.java b/app/src/main/java/org/secuso/privacyfriendlytlsmetric/ConnectionAnalysis/Evidence.java new file mode 100644 index 0000000..fc5200e --- /dev/null +++ b/app/src/main/java/org/secuso/privacyfriendlytlsmetric/ConnectionAnalysis/Evidence.java @@ -0,0 +1,530 @@ +/* + TLSMetric + - Copyright (2015, 2016) Felix Tsala Schiller + + ################################################################### + + This file is part of TLSMetric. + + TLSMetric is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + TLSMetric is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with TLSMetric. If not, see . + + Diese Datei ist Teil von TLSMetric. + + TLSMetric ist Freie Software: Sie können es unter den Bedingungen + der GNU General Public License, wie von der Free Software Foundation, + Version 3 der Lizenz oder (nach Ihrer Wahl) jeder späteren + veröffentlichten Version, weiterverbreiten und/oder modifizieren. + + TLSMetric wird in der Hoffnung, dass es nützlich sein wird, aber + OHNE JEDE GEWÄHRLEISTUNG, bereitgestellt; sogar ohne die implizite + Gewährleistung der MARKTFÄHIGKEIT oder EIGNUNG FÜR EINEN BESTIMMTEN ZWECK. + Siehe die GNU General Public License für weitere Details. + + Sie sollten eine Kopie der GNU General Public License zusammen mit diesem + Programm erhalten haben. Wenn nicht, siehe . + */ + + +package org.secuso.privacyfriendlytlsmetric.ConnectionAnalysis; + +import android.app.ActivityManager; +import android.content.Context; +import android.content.pm.PackageManager; +import android.util.Log; + +import com.voytechs.jnetstream.codec.Header; +import com.voytechs.jnetstream.codec.Packet; +import com.voytechs.jnetstream.primitive.address.Address; + + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.nio.BufferOverflowException; +import java.nio.BufferUnderflowException; +import java.nio.ByteBuffer; +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Set; + +import org.secuso.privacyfriendlytlsmetric.Assistant.Const; +import org.secuso.privacyfriendlytlsmetric.Assistant.ContextSingleton; +import org.secuso.privacyfriendlytlsmetric.Assistant.ExecuteCommand; +import org.secuso.privacyfriendlytlsmetric.Assistant.ToolBox; +import org.secuso.privacyfriendlytlsmetric.ConnectionAnalysis.Filter.Empty; +import org.secuso.privacyfriendlytlsmetric.ConnectionAnalysis.Filter.Filter; +import org.secuso.privacyfriendlytlsmetric.R; + +/** + * Class for generating connection information (Evidence Reports) from packets, which has been + * detected by the Analyser Service. + */ +public class Evidence { + //public Members + public static ArrayList mEvidence; + public static ArrayList mEvidenceDetail; + public static HashMap> mEvidenceDetailMap; + public static HashMap mPacketInfoMap; + public static int newWarnings; + + //private Members + private static HashMap mPortPidMap = new HashMap<>(); + private static HashMap mUidPidMap = new HashMap<>(); + private static HashMap mPortUidMap = new HashMap<>(); + + + public Evidence(){ + mEvidence = new ArrayList<>(); + mEvidenceDetailMap = new HashMap<>(); + mPacketInfoMap = new HashMap<>(); + updateConnections(); + newWarnings = 0; + } + + + // Update currently active connection + // TODO: UID/PID detection needs improvement. Unknown Apps exist + public static void updateConnections(){ + updatePortPidMap(); + Set ports = mPortPidMap.keySet(); + for(int i =0; i< mEvidence.size(); i++){ + int con = mEvidence.get(i).srcPort; + if (ports.contains(con)){ + ports.remove(con); + } + } + for (int port: ports) { + Report ann = new Report(); + ann.filter = new Empty(Filter.Protocol.UNKNOWN,-1,"SrcPort: " + port + "No data."); + ann.touch(); + ann.srcPort = port; + ann.pid = getPidByPort(port); + updatePackageInformationData(ann.pid, ann.uid); + //TODO: parse url from /proc/net/tcp + ann.url = "unknown"; + addEvidenceEntry(ann); + } + } + + // Filter triggered? -> Generate Report + public boolean processPacket(Packet pkt) { + Filter filter = scanPacket(pkt); + if (filter != null) { + if (Const.IS_DEBUG) Log.d(Const.LOG_TAG, "Filter triggered: " + filter.protocol); + Report ann = generateReport(pkt, filter); + addEvidenceEntry(ann); + return true; + } else { + return false; + } + + } + + // Sort a Reports in the Lists + private static void addEvidenceEntry(Report ann){ + + boolean updated = false; + + //Check and update existing connections with lesser filter severity (unknown (-1) or ok (0)) + for(int i =0; i< mEvidence.size(); i++){ + if(mEvidence.get(i).srcPort == ann.srcPort){ + updated = true; + if(mEvidence.get(i).filter.severity < ann.filter.severity){ + if(Const.IS_DEBUG)Log.d(Const.LOG_TAG, "Replacing connection to " + ann.url + " in evidence list. Higher warning state."); + mEvidence.set(i, ann); + //Set notification count +1 + if(ann.filter.severity > 0){ newWarnings++;} + } + } + } + + //Add found filters if connection not yet exist + if(!updated){ + if(Const.IS_DEBUG)Log.d(Const.LOG_TAG, "Adding connection " + ann.url + "to evidence list."); + mEvidence.add(ann); + //Set notification count +1 + if(ann.filter.severity > 0){ newWarnings++;} + } + + //Add found filters to detail list, if triggered filter not already exist. + if(mEvidenceDetailMap.containsKey(ann.srcPort)){ + ArrayList detailList = mEvidenceDetailMap.get(ann.srcPort); + boolean hasFilter = false; + for(Report exAnn : detailList){ + if(exAnn.filter.getClass() == ann.filter.getClass()){ + exAnn.touch(); + hasFilter = true; + } + } + if(!hasFilter){ + detailList.add(ann); + } + } else { + ArrayList newList = new ArrayList<>(); + newList.add(ann); + mEvidenceDetailMap.put(ann.srcPort, newList); + } + } + + //Scan a packet for TCP payload and initiate identification + //TODO: Move alle detection methods in new module when new filter system is designed + private Filter scanPacket(Packet pkt) { + + if (pkt.hasHeader("TCP") && pkt.hasDataHeader()) { + byte[] b = pkt.getDataValue(); + //if(Const.IS_DEBUG)Log.d(Const.LOG_TAG, b.length + " Bytes data found"); + if (b.length > 0){ + ByteBuffer bb = ByteBuffer.allocate(b.length); + bb.put(b); + + byte[] identChunk; + if (b.length >= 12){ + identChunk = new byte[20]; + } else { + identChunk = new byte[b.length]; + } + + bb.position(0); + try { + bb.get(identChunk); + }catch (BufferOverflowException |BufferUnderflowException e) { + Log.e(Const.LOG_TAG, "Could not read identChunk from TCP packet."); + } + return Identifyer.indent(identChunk); + } else return null; + } else { + return null; + } + } + + // generate an evidence report. + public static Report generateReport(Packet pkt, Filter filter) { + Report ann = new Report(); + ann.filter = filter; + ann.touch(); + fillConnectionData(ann, pkt); + ann.uid = getUidByPort(ann.srcPort); + ann.pid = getPidByPort(ann.srcPort); + updatePackageInformationData(ann.pid, ann.uid); + return ann; + } + + //Extract connection details from packet + private static void fillConnectionData(Report ann, Packet pkt) { + Header ipHeader; + Header transportHeader; + ann.timestamp = new Timestamp(System.currentTimeMillis()); + //Read ip and transport Header + if (pkt.hasHeader("IPv4")) { + ipHeader = pkt.getHeader("IPv4"); + } else if (pkt.hasHeader("IPv6")){ + ipHeader = pkt.getHeader("IPv6"); + } else { + ipHeader = null; + } + + if (pkt.hasHeader("TCP") && ipHeader != null) { + transportHeader = pkt.getHeader("TCP"); + }else if (pkt.hasHeader("UDP") && ipHeader != null ) { + transportHeader = pkt.getHeader("UDP"); + } else { + transportHeader = null; + } + + if(ipHeader != null && transportHeader != null) { + try { + //Get ports and addresses from header + Address address = (Address)ipHeader.getValue("daddr"); + InetAddress remoteaddress = InetAddress.getByAddress(address.toByteArray()); + if(getPortMap().containsKey((int) transportHeader.getValue("sport"))){ + ann.dstAddr = remoteaddress; + ann.dstPort = (int) transportHeader.getValue("dport"); + ann.srcPort = (int) transportHeader.getValue("sport"); + } else { + address = (Address)ipHeader.getValue("saddr"); + ann.dstAddr = InetAddress.getByAddress(address.toByteArray()); + ann.srcPort = (int) transportHeader.getValue("dport"); + ann.dstPort = (int) transportHeader.getValue("sport"); + } + } catch (UnknownHostException e) { + e.printStackTrace(); + } + if(ann.dstAddr != null)ann.url = ann.dstAddr.getHostName(); + } + } + + //Updates the PackageInformation hash map with new entries. + private static void updatePackageInformationData(int pid, int uid) { + if (pid >= 0 && !mPacketInfoMap.containsKey(pid)){ + PackageManager pm = ContextSingleton.getContext().getPackageManager(); + ActivityManager am = (ActivityManager) ContextSingleton.getContext().getSystemService(Context.ACTIVITY_SERVICE); + PackageInformation pi = generateDummy(); + pi.pid = pid; + pi.uid = uid; + + List pids = am.getRunningAppProcesses(); + for (int i = 0; i < pids.size(); i++) { + ActivityManager.RunningAppProcessInfo info = pids.get(i); + if((info.pid == pid || info.uid == uid )&& !mPacketInfoMap.containsKey(pid)){ + try { + String[] list = info.pkgList; + if(Const.IS_DEBUG)Log.d(Const.LOG_TAG, "Processing packet information of: " + list[0]); + pi.packageName = list[0]; + pi.icon = pm.getApplicationIcon(pi.packageName); + mPacketInfoMap.put(pid, pi); + mPacketInfoMap.put(uid, pi); + } catch (PackageManager.NameNotFoundException e) { + if(Const.IS_DEBUG)Log.e(Const.LOG_TAG, "Icon and/or package name not found. Using TLSMetric icon for unknown app."); + } + + } + } + } + } + + //Remove all reports of inactive connection + public static void disposeInactiveEvidence(){ + for (int i = 0; i < mEvidence.size(); i++){ + if(!Evidence.mPortUidMap.containsKey(mEvidence.get(i).srcPort)){ + mEvidenceDetailMap.remove(mEvidence.get(i).srcPort); + mEvidence.remove(i); + } + } + } + + public static int getPidByPort(int port) { + if(!mPortPidMap.containsKey(port)){ + updatePortPidMap(); + if(mPortPidMap.containsKey(port)){ + return mPortPidMap.get(port); + } else{ + return -1; + } + } else { + return mPortPidMap.get(port); + + } + } + + public static int getUidByPort(int port) { + if(!mPortUidMap.containsKey(port)){ + updatePortUidMap(); + if(mPortUidMap.containsKey(port)){ + return mPortUidMap.get(port); + } else{ + return -1; + } + } else { + return mPortPidMap.get(port); + + } + } + + private static void updatePortUidMap(){ + mPortUidMap = getPortMap(); + } + + //match pid and uid, accessiable by port + private static void updatePortPidMap() { + updateUidPidMap(); + updatePortUidMap(); + Set ports = mPortUidMap.keySet(); + for (int port :ports){ + if(!mPortPidMap.containsKey(port) && mUidPidMap.containsKey(mPortUidMap.get(port))){ + mPortPidMap.put(port, mUidPidMap.get(mPortUidMap.get(port))); + if(Const.IS_DEBUG)Log.d(Const.LOG_TAG,"mapping port to pid: " + port + " ->" + mUidPidMap.get(mPortUidMap.get(port))); + } + } + } + + //match pids + public static void updateUidPidMap(){ + ActivityManager am = (ActivityManager) ContextSingleton.getContext().getSystemService(Context.ACTIVITY_SERVICE); + List infos = am.getRunningAppProcesses(); + for (ActivityManager.RunningAppProcessInfo info : infos) { + if(!mUidPidMap.containsKey(info.uid)){ + mUidPidMap.put(info.uid, info.pid); + if(Const.IS_DEBUG)Log.d(Const.LOG_TAG, "Adding uid/pid: " + info.uid + " -> " + info.pid); + } + } + } + + //parse net output and scan for new conenctions, sort by port + public static HashMap getPortMap() { + HashMap result = new HashMap<>(); + String commandTcp4 = "cat /proc/net/tcp"; + String commandTcp6 = "cat /proc/net/tcp6"; + + parseNetOutput(ExecuteCommand.userForResult(commandTcp4), result); + parseNetOutput(ExecuteCommand.userForResult(commandTcp6), result); + + return result; + } + + //Parse output from /proc/net/tcp + public static void parseNetOutput(String readIn, HashMap hashMap) { + String[] splitLines; + String[] splitTabs; + + splitLines = readIn.split("\\n"); + for (int i = 1; i < splitLines.length; i++) { + splitLines[i] = splitLines[i].trim(); + while (splitLines[i].contains(" ")) { + splitLines[i] = splitLines[i].replace(" ", " "); + } + splitTabs = splitLines[i].split("\\s"); + int pos = splitTabs[1].indexOf(":"); + String port = splitTabs[1].substring(pos + 1, pos + 5); + + ByteBuffer bb = ByteBuffer.allocate(4); + bb.position(2); + bb.put(ToolBox.hexStringToByteArray(port)); + bb.position(0); + int srcPort = bb.getInt(); + int uid = Integer.parseInt(splitTabs[7]); + hashMap.put(srcPort, uid); + if (Const.IS_DEBUG)Log.d(Const.LOG_TAG,"port to uid:" + srcPort + " -> " + uid); + } + } + + // request PacketInformation + public static PackageInformation getPackageInformation(int pid,int uid) { + if(mPacketInfoMap.containsKey(pid)){ + return mPacketInfoMap.get(pid); + } else if(mPacketInfoMap.containsKey(uid)) { + return mPacketInfoMap.get(uid); + }else { + updatePackageInformationData(pid, uid); + if(mPacketInfoMap.containsKey(pid)){ + return mPacketInfoMap.get(pid); + } else if(mPacketInfoMap.containsKey(uid)) { + return mPacketInfoMap.get(uid); + }else { + return generateDummy(); + } + } + + } + + //Just a BubbleSort - order ArrayList in place by by severity, DESC + private static void sortAnnList(ArrayList annList){ + int range = annList.size() - 1; + while(range > 1){ + for(int i = 0; i < range; i ++){ + if(annList.get(i).filter.severity < annList.get(i + 1).filter.severity){ + Report tmpAnn = annList.get(i); + annList.set(i, annList.get(i + 1)); + annList.set(i + 1, tmpAnn); + } + } + range --; + } + } + + //Sort the report list and return it report + //TODO: deep copy + public static ArrayList getSortedEvidence(){ + sortAnnList(mEvidence); + return mEvidence; + } + + //Sort the report detail list and return it report + //TODO: deep copy + public static void setSortedEvidenceDetail(int key){ + sortAnnList(mEvidenceDetailMap.get(key)); + mEvidenceDetail = mEvidenceDetailMap.get(key); + } + + //No UID/PID match? Go dummy! + private static PackageInformation generateDummy() { + PackageInformation pi = new PackageInformation(); + pi.icon = ContextSingleton.getContext().getResources().getDrawable(R.mipmap.unknown_app); + pi.packageName = "Unknown App"; + pi.pid = -1; + pi.uid = -1; + return pi; + } + + // Get highest severity level in list + public static int getMaxSeverity(){ + int severity = -1; + for(Report ann : mEvidence){ + if(ann.filter.severity > severity){ + severity = ann.filter.severity; + } + } + return severity; + } + + //For further use: + //Example method for parsing /proc/pid/output + /* public static HashMap generatePortPidMap(){ + if(Const.IS_DEBUG)Log.d(Const.LOG_TAG, "Generating Port-to-Pid Map."); + HashMap portPidMap = new HashMap<>(); + HashMap portUidMap = getPortMap(); + + Set set = portUidMap.keySet(); + for (int key : set) { + int uid = portUidMap.get(key); + if(mUidPidMap.containsKey(uid)){ + portPidMap.put(key, mUidPidMap.get(uid)); + if(Const.IS_DEBUG)Log.d(Const.LOG_TAG, "PortPidMap matched uid " + uid + + "->" + key + ", " + mUidPidMap.get(uid) ); + } else if(uid == 0){ + portPidMap.put(key, 0); + if(Const.IS_DEBUG)Log.d(Const.LOG_TAG, "Root uid " + uid + + ": " + key + ", " + 0 ); + } else { + portPidMap.put(key, -1); + if(Const.IS_DEBUG)Log.d(Const.LOG_TAG, "Could not match by uid " + uid + + ": " + key + ", " + -1 ); + } + } + return portPidMap; + }*/ + + /* + public static HashMap getPidMap(){ + HashMap result = new HashMap<>(); + int[] pids = getPids(); + + String[] split; + for (int pid : pids) { + String command = "cat /proc/" + pid + "/status"; + String readIn = ExecuteCommand.userForResult(command); + int pos = readIn.indexOf("Uid:"); + try { + readIn = readIn.substring(pos, pos + 20); + } catch (StringIndexOutOfBoundsException e){ + Log.e(Const.LOG_TAG, "Readin of uid of process " + pid + " failed, StringIndexOutOfBounds."); + } + + split = readIn.split("\\t"); + if(split.length > 1) { + try { + int uid = Integer.parseInt(split[1]); + Log.d(Const.LOG_TAG, "pid to uid: " + pid + "->" + uid); + result.put(uid, pid); + } catch (NumberFormatException e) { + Log.e(Const.LOG_TAG, "Parsing of UID failed! " + split[1] + " Pid: " + pid); + result.put(-1, pid); + } + } + } + + return result; + }*/ + +} diff --git a/app/src/main/java/org/secuso/privacyfriendlytlsmetric/ConnectionAnalysis/Filter/Empty.java b/app/src/main/java/org/secuso/privacyfriendlytlsmetric/ConnectionAnalysis/Filter/Empty.java new file mode 100644 index 0000000..4a3a45d --- /dev/null +++ b/app/src/main/java/org/secuso/privacyfriendlytlsmetric/ConnectionAnalysis/Filter/Empty.java @@ -0,0 +1,47 @@ +/* + TLSMetric + - Copyright (2015, 2016) Felix Tsala Schiller + + ################################################################### + + This file is part of TLSMetric. + + TLSMetric is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + TLSMetric is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with TLSMetric. If not, see . + + Diese Datei ist Teil von TLSMetric. + + TLSMetric ist Freie Software: Sie können es unter den Bedingungen + der GNU General Public License, wie von der Free Software Foundation, + Version 3 der Lizenz oder (nach Ihrer Wahl) jeder späteren + veröffentlichten Version, weiterverbreiten und/oder modifizieren. + + TLSMetric wird in der Hoffnung, dass es nützlich sein wird, aber + OHNE JEDE GEWÄHRLEISTUNG, bereitgestellt; sogar ohne die implizite + Gewährleistung der MARKTFÄHIGKEIT oder EIGNUNG FÜR EINEN BESTIMMTEN ZWECK. + Siehe die GNU General Public License für weitere Details. + + Sie sollten eine Kopie der GNU General Public License zusammen mit diesem + Programm erhalten haben. Wenn nicht, siehe . + */ + +package org.secuso.privacyfriendlytlsmetric.ConnectionAnalysis.Filter; + +/** + * Dummy protocol Filter. + */ +public class Empty extends Filter{ + public Empty(Protocol protocol, int severity, String description) { + super(protocol, severity, description); + } +} diff --git a/app/src/main/java/org/secuso/privacyfriendlytlsmetric/ConnectionAnalysis/Filter/Filter.java b/app/src/main/java/org/secuso/privacyfriendlytlsmetric/ConnectionAnalysis/Filter/Filter.java new file mode 100644 index 0000000..0a532bb --- /dev/null +++ b/app/src/main/java/org/secuso/privacyfriendlytlsmetric/ConnectionAnalysis/Filter/Filter.java @@ -0,0 +1,67 @@ +/* + TLSMetric + - Copyright (2015, 2016) Felix Tsala Schiller + + ################################################################### + + This file is part of TLSMetric. + + TLSMetric is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + TLSMetric is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with TLSMetric. If not, see . + + Diese Datei ist Teil von TLSMetric. + + TLSMetric ist Freie Software: Sie können es unter den Bedingungen + der GNU General Public License, wie von der Free Software Foundation, + Version 3 der Lizenz oder (nach Ihrer Wahl) jeder späteren + veröffentlichten Version, weiterverbreiten und/oder modifizieren. + + TLSMetric wird in der Hoffnung, dass es nützlich sein wird, aber + OHNE JEDE GEWÄHRLEISTUNG, bereitgestellt; sogar ohne die implizite + Gewährleistung der MARKTFÄHIGKEIT oder EIGNUNG FÜR EINEN BESTIMMTEN ZWECK. + Siehe die GNU General Public License für weitere Details. + + Sie sollten eine Kopie der GNU General Public License zusammen mit diesem + Programm erhalten haben. Wenn nicht, siehe . + */ + +package org.secuso.privacyfriendlytlsmetric.ConnectionAnalysis.Filter; + +/** + * Interface for protocol filter + */ +public abstract class Filter { + public Protocol protocol; + public int severity = 3; + public String description; + public boolean checkCypher; + + public Filter(Protocol protocol, int severity, String description) { + this.protocol = protocol; + this.severity = severity; + this.description = description; + } + + public enum Protocol { + UNKNOWN, + HTTP, + SSL1, + SSL2, + SSL3, + TLS10, + TLS11, + TLS12, + + } + +} diff --git a/app/src/main/java/org/secuso/privacyfriendlytlsmetric/ConnectionAnalysis/Filter/Http.java b/app/src/main/java/org/secuso/privacyfriendlytlsmetric/ConnectionAnalysis/Filter/Http.java new file mode 100644 index 0000000..8a0c256 --- /dev/null +++ b/app/src/main/java/org/secuso/privacyfriendlytlsmetric/ConnectionAnalysis/Filter/Http.java @@ -0,0 +1,49 @@ +/* + TLSMetric + - Copyright (2015, 2016) Felix Tsala Schiller + + ################################################################### + + This file is part of TLSMetric. + + TLSMetric is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + TLSMetric is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with TLSMetric. If not, see . + + Diese Datei ist Teil von TLSMetric. + + TLSMetric ist Freie Software: Sie können es unter den Bedingungen + der GNU General Public License, wie von der Free Software Foundation, + Version 3 der Lizenz oder (nach Ihrer Wahl) jeder späteren + veröffentlichten Version, weiterverbreiten und/oder modifizieren. + + TLSMetric wird in der Hoffnung, dass es nützlich sein wird, aber + OHNE JEDE GEWÄHRLEISTUNG, bereitgestellt; sogar ohne die implizite + Gewährleistung der MARKTFÄHIGKEIT oder EIGNUNG FÜR EINEN BESTIMMTEN ZWECK. + Siehe die GNU General Public License für weitere Details. + + Sie sollten eine Kopie der GNU General Public License zusammen mit diesem + Programm erhalten haben. Wenn nicht, siehe . + */ + +package org.secuso.privacyfriendlytlsmetric.ConnectionAnalysis.Filter; + +/** + * HTTP protocol filter. Example for unencrypted traffic. + */ +public class Http extends Filter { + + public Http(Protocol protocol, int severity, String description) { + super(protocol, severity, description); + checkCypher = false; + } +} diff --git a/app/src/main/java/org/secuso/privacyfriendlytlsmetric/ConnectionAnalysis/Filter/Tls.java b/app/src/main/java/org/secuso/privacyfriendlytlsmetric/ConnectionAnalysis/Filter/Tls.java new file mode 100644 index 0000000..9325f98 --- /dev/null +++ b/app/src/main/java/org/secuso/privacyfriendlytlsmetric/ConnectionAnalysis/Filter/Tls.java @@ -0,0 +1,61 @@ +/* + TLSMetric + - Copyright (2015, 2016) Felix Tsala Schiller + + ################################################################### + + This file is part of TLSMetric. + + TLSMetric is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + TLSMetric is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with TLSMetric. If not, see . + + Diese Datei ist Teil von TLSMetric. + + TLSMetric ist Freie Software: Sie können es unter den Bedingungen + der GNU General Public License, wie von der Free Software Foundation, + Version 3 der Lizenz oder (nach Ihrer Wahl) jeder späteren + veröffentlichten Version, weiterverbreiten und/oder modifizieren. + + TLSMetric wird in der Hoffnung, dass es nützlich sein wird, aber + OHNE JEDE GEWÄHRLEISTUNG, bereitgestellt; sogar ohne die implizite + Gewährleistung der MARKTFÄHIGKEIT oder EIGNUNG FÜR EINEN BESTIMMTEN ZWECK. + Siehe die GNU General Public License für weitere Details. + + Sie sollten eine Kopie der GNU General Public License zusammen mit diesem + Programm erhalten haben. Wenn nicht, siehe . + */ + +package org.secuso.privacyfriendlytlsmetric.ConnectionAnalysis.Filter; + +/** + * Protocol filter for all TLS versions. Can hold a message type identifier. + */ +public class Tls extends Filter { + + public TlsProtocol mSubProtocol; + public int mVersion; + + public Tls(Protocol protocol, int severity, String description, TlsProtocol subProtocol, int version) { + super(protocol, severity, description); + checkCypher = true; + mSubProtocol = subProtocol; + mVersion = version; + } + + public enum TlsProtocol { + HANDSHAKE, + CHANGE_CYPHER, + ALERT, + APP_DATA + } +} diff --git a/app/src/main/java/org/secuso/privacyfriendlytlsmetric/ConnectionAnalysis/Identifyer.java b/app/src/main/java/org/secuso/privacyfriendlytlsmetric/ConnectionAnalysis/Identifyer.java new file mode 100644 index 0000000..9252c2b --- /dev/null +++ b/app/src/main/java/org/secuso/privacyfriendlytlsmetric/ConnectionAnalysis/Identifyer.java @@ -0,0 +1,144 @@ +/* + TLSMetric + - Copyright (2015, 2016) Felix Tsala Schiller + + ################################################################### + + This file is part of TLSMetric. + + TLSMetric is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + TLSMetric is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with TLSMetric. If not, see . + + Diese Datei ist Teil von TLSMetric. + + TLSMetric ist Freie Software: Sie können es unter den Bedingungen + der GNU General Public License, wie von der Free Software Foundation, + Version 3 der Lizenz oder (nach Ihrer Wahl) jeder späteren + veröffentlichten Version, weiterverbreiten und/oder modifizieren. + + TLSMetric wird in der Hoffnung, dass es nützlich sein wird, aber + OHNE JEDE GEWÄHRLEISTUNG, bereitgestellt; sogar ohne die implizite + Gewährleistung der MARKTFÄHIGKEIT oder EIGNUNG FÜR EINEN BESTIMMTEN ZWECK. + Siehe die GNU General Public License für weitere Details. + + Sie sollten eine Kopie der GNU General Public License zusammen mit diesem + Programm erhalten haben. Wenn nicht, siehe . + */ + +package org.secuso.privacyfriendlytlsmetric.ConnectionAnalysis; + +import android.util.Log; + +import java.util.ArrayDeque; +import java.util.Arrays; +import java.util.Deque; + +import org.secuso.privacyfriendlytlsmetric.Assistant.Const; +import org.secuso.privacyfriendlytlsmetric.Assistant.ContextSingleton; +import org.secuso.privacyfriendlytlsmetric.Assistant.ToolBox; +import org.secuso.privacyfriendlytlsmetric.ConnectionAnalysis.Filter.Filter; +import org.secuso.privacyfriendlytlsmetric.ConnectionAnalysis.Filter.Http; +import org.secuso.privacyfriendlytlsmetric.ConnectionAnalysis.Filter.Tls; +import org.secuso.privacyfriendlytlsmetric.R; + + +/** + * Hardcoded for protocol filters. + * Protocols are identified by their unique header signature: ident byte[] and offset. + * + * TODO: Move identifiers to parsable xml + */ +public class Identifyer { + + + final static byte[] sHTTP = new byte[]{(byte) 0x48, (byte) 0x54, (byte) 0x54, (byte) 0x50}; + final static byte[] sSSL3 = new byte[]{(byte) 0x03, (byte) 0x00}; + final static byte[] sTLS10 = new byte[]{(byte) 0x03, (byte) 0x01}; + final static byte[] sTLS11 = new byte[]{(byte) 0x03, (byte) 0x02}; + final static byte[] sTLS12 = new byte[]{(byte) 0x03, (byte) 0x03}; + + //search for protocol match in first tcp payload bytes + public static Filter indent(byte[] ident) { + Filter filter = null; + + if (searchByteArray(ident, sHTTP) == 0) filter = new Http(Filter.Protocol.HTTP, 3, ContextSingleton.getContext().getResources().getString(R.string.ALERT_HTTP)); + else if (searchByteArray(ident, sSSL3) == 1 && fillSubProto(ident) != null) + filter = new Tls(Filter.Protocol.SSL3, 1, + ContextSingleton.getContext().getResources().getString(R.string.ALERT_SSL_3), + fillSubProto(ident), 10); + else if (searchByteArray(ident, sTLS10) == 1 && fillSubProto(ident) != null) + filter = new Tls(Filter.Protocol.TLS10, 0, + ContextSingleton.getContext().getResources().getString(R.string.ALERT_TLS_10), + fillSubProto(ident), 10); + else if (searchByteArray(ident, sTLS11) == 1 && fillSubProto(ident) != null) + filter = new Tls(Filter.Protocol.TLS11, 0, + ContextSingleton.getContext().getResources().getString(R.string.ALERT_TLS_11), + fillSubProto(ident), 11); + else if (searchByteArray(ident, sTLS12) == 1 && fillSubProto(ident) != null) + filter = new Tls(Filter.Protocol.TLS12, 0, + ContextSingleton.getContext().getResources().getString(R.string.ALERT_TLS_12), + fillSubProto(ident), 12); + return filter; + } + + // TLS message type identifier + private static Tls.TlsProtocol fillSubProto(byte[] ident) { + + switch (ident[0]) { + case (byte) 0x16: + return Tls.TlsProtocol.HANDSHAKE; + case (byte) 0x15: + return Tls.TlsProtocol.ALERT; + case (byte) 0x17: + return Tls.TlsProtocol.APP_DATA; + case (byte) 0x14: + return Tls.TlsProtocol.CHANGE_CYPHER; + default: + return null; + } + + } + + //Search for byte array in given byte array. + public static int searchByteArray(byte[] input, byte[] searchedFor) { + //convert byte[] to Byte[] + Byte[] searchedForB = new Byte[searchedFor.length]; + for (int x = 0; x < searchedFor.length; x++) { + searchedForB[x] = searchedFor[x]; + } + + int idx = -1; + //search: + Deque q = new ArrayDeque<>(input.length); + for (int i = 0; i < input.length; i++) { + if (q.size() == searchedForB.length) { + //here I can check + Byte[] cur = q.toArray(new Byte[]{}); + if (Arrays.equals(cur, searchedForB)) { + //found! + idx = i - searchedForB.length; + break; + } else { + //not found + q.pop(); + q.addLast(input[i]); + } + } else { + q.addLast(input[i]); + } + } + if (Const.IS_DEBUG && idx != -1) + Log.d(Const.LOG_TAG, ToolBox.printHexBinary(searchedFor) + " found at position " + idx); + return idx; + } +} diff --git a/app/src/main/java/org/secuso/privacyfriendlytlsmetric/ConnectionAnalysis/PackageInformation.java b/app/src/main/java/org/secuso/privacyfriendlytlsmetric/ConnectionAnalysis/PackageInformation.java new file mode 100644 index 0000000..559ea77 --- /dev/null +++ b/app/src/main/java/org/secuso/privacyfriendlytlsmetric/ConnectionAnalysis/PackageInformation.java @@ -0,0 +1,50 @@ +/* + TLSMetric + - Copyright (2015, 2016) Felix Tsala Schiller + + ################################################################### + + This file is part of TLSMetric. + + TLSMetric is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + TLSMetric is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with TLSMetric. If not, see . + + Diese Datei ist Teil von TLSMetric. + + TLSMetric ist Freie Software: Sie können es unter den Bedingungen + der GNU General Public License, wie von der Free Software Foundation, + Version 3 der Lizenz oder (nach Ihrer Wahl) jeder späteren + veröffentlichten Version, weiterverbreiten und/oder modifizieren. + + TLSMetric wird in der Hoffnung, dass es nützlich sein wird, aber + OHNE JEDE GEWÄHRLEISTUNG, bereitgestellt; sogar ohne die implizite + Gewährleistung der MARKTFÄHIGKEIT oder EIGNUNG FÜR EINEN BESTIMMTEN ZWECK. + Siehe die GNU General Public License für weitere Details. + + Sie sollten eine Kopie der GNU General Public License zusammen mit diesem + Programm erhalten haben. Wenn nicht, siehe . + */ + +package org.secuso.privacyfriendlytlsmetric.ConnectionAnalysis; + +import android.graphics.drawable.Drawable; + +/** + * Detail of an application + */ +public class PackageInformation { + public int pid; + public int uid; + public Drawable icon; + public String packageName; +} diff --git a/app/src/main/java/org/secuso/privacyfriendlytlsmetric/ConnectionAnalysis/PassiveService.java b/app/src/main/java/org/secuso/privacyfriendlytlsmetric/ConnectionAnalysis/PassiveService.java new file mode 100644 index 0000000..d1f0c41 --- /dev/null +++ b/app/src/main/java/org/secuso/privacyfriendlytlsmetric/ConnectionAnalysis/PassiveService.java @@ -0,0 +1,246 @@ +/* + TLSMetric + - Copyright (2015, 2016) Felix Tsala Schiller + + ################################################################### + + This file is part of TLSMetric. + + TLSMetric is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + TLSMetric is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with TLSMetric. If not, see . + + Diese Datei ist Teil von TLSMetric. + + TLSMetric ist Freie Software: Sie können es unter den Bedingungen + der GNU General Public License, wie von der Free Software Foundation, + Version 3 der Lizenz oder (nach Ihrer Wahl) jeder späteren + veröffentlichten Version, weiterverbreiten und/oder modifizieren. + + TLSMetric wird in der Hoffnung, dass es nützlich sein wird, aber + OHNE JEDE GEWÄHRLEISTUNG, bereitgestellt; sogar ohne die implizite + Gewährleistung der MARKTFÄHIGKEIT oder EIGNUNG FÜR EINEN BESTIMMTEN ZWECK. + Siehe die GNU General Public License für weitere Details. + + Sie sollten eine Kopie der GNU General Public License zusammen mit diesem + Programm erhalten haben. Wenn nicht, siehe . + */ + +package org.secuso.privacyfriendlytlsmetric.ConnectionAnalysis; + +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.app.Service; +import android.app.TaskStackBuilder; +import android.content.Context; +import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.os.Binder; +import android.os.IBinder; +import android.support.v4.app.NotificationCompat; +import android.util.Log; +import android.widget.Toast; + + +import org.secuso.privacyfriendlytlsmetric.Activities.EvidenceActivity; +import org.secuso.privacyfriendlytlsmetric.Activities.MainActivity; +import org.secuso.privacyfriendlytlsmetric.Assistant.Const; +import org.secuso.privacyfriendlytlsmetric.R; + + +/** + * Connection Analyzer Service. Identifies active connections on the device and invokes data + * gatheriung and report compilation off available information. + * + */ +public class PassiveService extends Service { + + public static boolean mInterrupt; + private Thread mThread; + private boolean isVpn; + private Evidence mEvidence = new Evidence(); + + private int mNotificationCount; + NotificationCompat.Builder mBuilder = + new NotificationCompat.Builder(this) + .setSmallIcon(R.mipmap.icon) + .setContentTitle("TLSMetric") + .setContentText("Packet analyzer service is running."); + + //private Bitmap mQuest; + private Bitmap mOk; + private Bitmap mWarnOrange; + private Bitmap mWarnRed; + + + @Override + public void onCreate() { + mInterrupt = false; + isVpn = false; + mNotificationCount = 0; + loadNotificationBitmaps(); + + showAppNotification(); + + if(isVpn){ + //VPN branch : Not implemented yet + Log.i(Const.LOG_TAG,"VPN core not yet implemented"); + } + } + + //Icons for Notification manager. Must be converted to bitmaps. + private void loadNotificationBitmaps() { + //mQuest = BitmapFactory.decodeResource(getResources(), R.mipmap.icon_quest, mBitmapOptions); + mOk = BitmapFactory.decodeResource(getResources(), R.mipmap.icon_ok); + mWarnOrange = BitmapFactory.decodeResource(getResources(), R.mipmap.icon_warn_orange); + mWarnRed = BitmapFactory.decodeResource(getResources(), R.mipmap.icon_warn_red); + + + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + Log.i("LocalService", "Received start id " + startId + ": " + intent); + showAppNotification(); + // Stop the previous session by interrupting the thread. + if (mThread != null) { + mThread.interrupt(); + } + + //Connection analyzer working thread + mThread = new Thread(new Runnable() { + @Override + public void run() { + try { + while (!mInterrupt) { + + //TODO: Check for Changes else sleep 500ms + checkForNotifications(); + } + } catch (Error e) { + e.printStackTrace(); + } + if(mInterrupt)mThread.interrupt(); + stopSelf(); + } + }, "AnalyzerThreadRunnable"); + + //start the service + mThread.start(); + return START_NOT_STICKY; + } + + @Override + public void onDestroy() { + showNoNotification(); + //TODO: Stop whatever service is doing + Toast.makeText(this, "TLSMetric service stopped", Toast.LENGTH_SHORT).show(); + } + + @Override + public IBinder onBind(Intent intent) { + return new AnalyzerBinder(); + } + + /* + * Returns the dumped Packet or null. Null means no packet is availiable and thread will sleep. + * If the dumpfile is empty a new initialization attempt will be made. + */ + + + private void checkForNotifications(){ + if(Evidence.newWarnings != mNotificationCount) { + mNotificationCount = Evidence.newWarnings; + if (mNotificationCount > 0) { + showWarningNotification(); + } else { + showAppNotification(); + } + } + } + + //BG notification. Standard Android version. + private void showAppNotification(){ + mBuilder.setSmallIcon(R.mipmap.icon); + mBuilder.setLargeIcon(mOk); + Intent resultIntent = new Intent(this, MainActivity.class); + + // The stack builder object will contain an artificial back stack for the + // started Activity. + // This ensures that navigating backward from the Activity leads out of + // your application to the Home screen. + TaskStackBuilder stackBuilder = TaskStackBuilder.create(this); + + // Adds the back stack for the Intent (but not the Intent itself) + stackBuilder.addParentStack(MainActivity.class); + + // Adds the Intent that starts the Activity to the top of the stack + stackBuilder.addNextIntent(resultIntent); + PendingIntent resultPendingIntent = + stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT); + mBuilder.setContentIntent(resultPendingIntent); + NotificationManager mNotificationManager = + (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + + // mId allows you to update the notification later on. + mNotificationManager.notify(Const.LOG_TAG, 1, mBuilder.build()); + } + + //Computes the need and severity of a notification. + private void showWarningNotification(){ + //Set corresponding icon + if(Evidence.getMaxSeverity() > 2){ + mBuilder.setSmallIcon(R.mipmap.icon_warn_red); + mBuilder.setLargeIcon(mWarnRed); + } else { + mBuilder.setSmallIcon(R.mipmap.icon_warn_orange); + mBuilder.setLargeIcon(mWarnOrange); + } + mBuilder.setContentText(mNotificationCount + " new warnings encountered."); + + // Creates an explicit intent for an Activity in your app + Intent resultIntent = new Intent(this, EvidenceActivity.class); + + TaskStackBuilder stackBuilder = TaskStackBuilder.create(this); + + // Adds the back stack for the Intent (but not the Intent itself) + stackBuilder.addParentStack(EvidenceActivity.class); + + // Adds the Intent that starts the Activity to the top of the stack + stackBuilder.addNextIntent(resultIntent); + PendingIntent resultPendingIntent = + stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT); + mBuilder.setContentIntent(resultPendingIntent); + NotificationManager mNotificationManager = + (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + + // mId allows you to update the notification later on. + mNotificationManager.notify(Const.LOG_TAG, 1, mBuilder.build()); + } + + //Revoke notifications + private void showNoNotification(){ + NotificationManager mNotificationManager = + (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + mNotificationManager.cancelAll(); + } + + //For future IPC implementations + public class AnalyzerBinder extends Binder { + PassiveService getService() { + return PassiveService.this; + } + } + + +} diff --git a/app/src/main/java/org/secuso/privacyfriendlytlsmetric/ConnectionAnalysis/Report.java b/app/src/main/java/org/secuso/privacyfriendlytlsmetric/ConnectionAnalysis/Report.java new file mode 100644 index 0000000..8514a79 --- /dev/null +++ b/app/src/main/java/org/secuso/privacyfriendlytlsmetric/ConnectionAnalysis/Report.java @@ -0,0 +1,67 @@ +/* + TLSMetric + - Copyright (2015, 2016) Felix Tsala Schiller + + ################################################################### + + This file is part of TLSMetric. + + TLSMetric is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + TLSMetric is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with TLSMetric. If not, see . + + Diese Datei ist Teil von TLSMetric. + + TLSMetric ist Freie Software: Sie können es unter den Bedingungen + der GNU General Public License, wie von der Free Software Foundation, + Version 3 der Lizenz oder (nach Ihrer Wahl) jeder späteren + veröffentlichten Version, weiterverbreiten und/oder modifizieren. + + TLSMetric wird in der Hoffnung, dass es nützlich sein wird, aber + OHNE JEDE GEWÄHRLEISTUNG, bereitgestellt; sogar ohne die implizite + Gewährleistung der MARKTFÄHIGKEIT oder EIGNUNG FÜR EINEN BESTIMMTEN ZWECK. + Siehe die GNU General Public License für weitere Details. + + Sie sollten eine Kopie der GNU General Public License zusammen mit diesem + Programm erhalten haben. Wenn nicht, siehe . + */ + +package org.secuso.privacyfriendlytlsmetric.ConnectionAnalysis; + + +import java.net.InetAddress; +import java.sql.Timestamp; + +import org.secuso.privacyfriendlytlsmetric.ConnectionAnalysis.Filter.Filter; + +/** + * Evidence report + */ +public class Report { + + public InetAddress dstAddr; + public String url; + public int srcPort; + public int dstPort; + public Timestamp timestamp; + + public int pid; + public int uid; + + public Filter filter; + + //Set current timestamp + public void touch(){ + timestamp = new Timestamp(System.currentTimeMillis()); + } + +} diff --git a/app/src/main/res/menu/menu_tlsmetric.xml b/app/src/main/res/menu/menu_tlsmetric.xml deleted file mode 100644 index 70dd931..0000000 --- a/app/src/main/res/menu/menu_tlsmetric.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..cde69bc Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..c133a0c Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..bfa42f0 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xhdpi/icon.png b/app/src/main/res/mipmap-xhdpi/icon.png deleted file mode 100644 index a3dc06f..0000000 Binary files a/app/src/main/res/mipmap-xhdpi/icon.png and /dev/null differ diff --git a/app/src/main/res/mipmap-xhdpi/icon_ok.png b/app/src/main/res/mipmap-xhdpi/icon_ok.png deleted file mode 100644 index 91878ef..0000000 Binary files a/app/src/main/res/mipmap-xhdpi/icon_ok.png and /dev/null differ diff --git a/app/src/main/res/mipmap-xhdpi/icon_quest.png b/app/src/main/res/mipmap-xhdpi/icon_quest.png deleted file mode 100644 index 6429b51..0000000 Binary files a/app/src/main/res/mipmap-xhdpi/icon_quest.png and /dev/null differ diff --git a/app/src/main/res/mipmap-xhdpi/icon_warn_orange.png b/app/src/main/res/mipmap-xhdpi/icon_warn_orange.png deleted file mode 100644 index 38adf7c..0000000 Binary files a/app/src/main/res/mipmap-xhdpi/icon_warn_orange.png and /dev/null differ diff --git a/app/src/main/res/mipmap-xhdpi/icon_warn_red.png b/app/src/main/res/mipmap-xhdpi/icon_warn_red.png deleted file mode 100644 index 8906b5c..0000000 Binary files a/app/src/main/res/mipmap-xhdpi/icon_warn_red.png and /dev/null differ diff --git a/app/src/main/res/mipmap-xhdpi/unknown_app.png b/app/src/main/res/mipmap-xhdpi/unknown_app.png deleted file mode 100644 index 21db9fb..0000000 Binary files a/app/src/main/res/mipmap-xhdpi/unknown_app.png and /dev/null differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..324e72c Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..aee44e1 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/icon.png b/app/src/main/res/mipmap-xxxhdpi/icon.png deleted file mode 100644 index 6ffbdbc..0000000 Binary files a/app/src/main/res/mipmap-xxxhdpi/icon.png and /dev/null differ diff --git a/app/src/main/res/mipmap-xxxhdpi/icon_ok.png b/app/src/main/res/mipmap-xxxhdpi/icon_ok.png deleted file mode 100644 index 385803f..0000000 Binary files a/app/src/main/res/mipmap-xxxhdpi/icon_ok.png and /dev/null differ diff --git a/app/src/main/res/mipmap-xxxhdpi/icon_quest.png b/app/src/main/res/mipmap-xxxhdpi/icon_quest.png deleted file mode 100644 index ba1bbce..0000000 Binary files a/app/src/main/res/mipmap-xxxhdpi/icon_quest.png and /dev/null differ diff --git a/app/src/main/res/mipmap-xxxhdpi/icon_warn_orange.png b/app/src/main/res/mipmap-xxxhdpi/icon_warn_orange.png deleted file mode 100644 index 097502b..0000000 Binary files a/app/src/main/res/mipmap-xxxhdpi/icon_warn_orange.png and /dev/null differ diff --git a/app/src/main/res/mipmap-xxxhdpi/icon_warn_red.png b/app/src/main/res/mipmap-xxxhdpi/icon_warn_red.png deleted file mode 100644 index 1ba7c4d..0000000 Binary files a/app/src/main/res/mipmap-xxxhdpi/icon_warn_red.png and /dev/null differ diff --git a/app/src/main/res/mipmap-xxxhdpi/unknown_app.png b/app/src/main/res/mipmap-xxxhdpi/unknown_app.png deleted file mode 100644 index 0e4ee4d..0000000 Binary files a/app/src/main/res/mipmap-xxxhdpi/unknown_app.png and /dev/null differ diff --git a/app/src/main/res/raw/filter.ini b/app/src/main/res/raw/filter.ini deleted file mode 100644 index 9caa814..0000000 --- a/app/src/main/res/raw/filter.ini +++ /dev/null @@ -1,20 +0,0 @@ -############ TLS METRIC FILTER ################### -# 1) #: Comment line -# 2) Filter Types: IS_PRESENT, CONTAINS -# 2) syntax: -# FilterType dependent: -# Filter Type: IS_PRESENT: -# -- FilterType, protocol, severity, description -# Filter Type: CONTAINS: -# -- FilterType, protocol, value_as_hex_string, severity, description -# 3) Options: -# -- FilterType - ENUM: IS_PRESENT, CONTAINS -# -- protocol - String: related protocol as in JNETSTREAM, examples: TCP, UDP, IPv4, ... -# -- value_as_hex_string - String: a hexString to search for in the given protocol or payload -# -- severity - numeral from 0 to 3, zero is green, 3 is red -# -- description - string with filter explanation -# -- End with a comma as line ender -# EXAMPLE: -################################################## -IS_PRESENT,http,3,Unencrypted HTTP traffic detected!, -CONTAINS,tls,1,ABCD,Server warning: Server supports weak cypher ABCD!, \ No newline at end of file diff --git a/app/src/main/res/raw/resolveport b/app/src/main/res/raw/resolveport deleted file mode 100644 index 0c7149d..0000000 --- a/app/src/main/res/raw/resolveport +++ /dev/null @@ -1,9 +0,0 @@ -#! /bin/bash -port= -hex_port=$(echo "obase=16; $port" | bc ) - -inode=$(cat /proc/net/tcp | grep ":$hex_port" | awk '{print $10}') - -for i in $(ps axo pid); do - ls -l /proc/$i/fd 2> /dev/null | grep -q ":\[$inode\]" && echo $i -done \ No newline at end of file diff --git a/app/src/main/res/raw/tcpdump b/app/src/main/res/raw/tcpdump deleted file mode 100644 index ced58bd..0000000 Binary files a/app/src/main/res/raw/tcpdump and /dev/null differ diff --git a/app/src/main/res/values-v21/styles.xml b/app/src/main/res/values-v21/styles.xml deleted file mode 100644 index 65d0c39..0000000 --- a/app/src/main/res/values-v21/styles.xml +++ /dev/null @@ -1,8 +0,0 @@ -> - - diff --git a/app/src/main/res/values-w820dp/dimens.xml b/app/src/main/res/values-w820dp/dimens.xml deleted file mode 100644 index 63fc816..0000000 --- a/app/src/main/res/values-w820dp/dimens.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - 64dp - diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index acfa1ef..b4026eb 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,46 +1,3 @@ - - Press power button to start TLSMetric - Starting network dump - Waiting for connection data - TLS Metric running - Stopping TLS Metric - - - Severity: - ShiftyActivity - Back to Main - refresh view - Connections - - - TLSMetric - TLS Metric with TCPDump. - Stop TCPDump. - inspect connections - - - Unnencrypted HTTP connection detected! - Encrypted Connection - TLS 1.0 - Outdated encryption warning - SSL3 - Encrypted Connection - TLS 1.2 - Encrypted Connection - TLS 1.1 - settings - - - Lorem ipsum dolor sit amet, consectetur adipiscing elit. - Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec - consectetur ante hendrerit. Donec et mollis dolor. Praesent et diam eget libero egestas - mattis sit amet vitae augue. Nam tincidunt congue enim, ut porta lorem lacinia consectetur. - Donec ut libero sed arcu vehicula ultricies a non tortor. Lorem ipsum dolor sit amet, - consectetur adipiscing elit. Aenean ut gravida lorem. Ut turpis felis, pulvinar a semper - sed, adipiscing id dolor. Pellentesque auctor nisi id magna consequat sagittis. Curabitur - dapibus enim sit amet elit pharetra tincidunt feugiat nisl imperdiet. Ut convallis libero - in urna ultrices accumsan. Donec sed odio eros. Donec viverra mi quis quam pulvinar at - malesuada arcu rhoncus. Cum sociis natoque penatibus et magnis dis parturient montes, - nascetur ridiculus mus. In rutrum accumsan ultricies. Mauris vitae nisi at sem facilisis - semper ac in est. - Detail View - - + Privacy Friendly TLSMetric diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index cd3297e..5885930 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -7,13 +7,5 @@ @color/colorPrimaryDark @color/colorAccent - -