From c0eecabccaf4f0644857c0d9dc7415613fad81f1 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Wed, 20 Nov 2013 00:21:48 -0500 Subject: [PATCH 1/6] also match repo urls that have one, two or three extra levels to the path Yes, you really have to do all this craziness to get it to match right! --- AndroidManifest.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index ac8ee383a..b23692682 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -83,6 +83,18 @@ + + + + + + + + + + + + From 240f03d372558b20f57f2ee86e01ea66b93a0611 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Thu, 21 Nov 2013 19:59:44 -0500 Subject: [PATCH 2/6] add "proper" Java decorators that Android ADT insists on adding Everytime I save a java file with Android ADT, it adds @Override decorators and throws errors if a method is called that is not supported in API 4. My setup might be more sensitive since its setup with the official Android style plugin for Eclipse. But the decorators are "correct" Java style, so it would be nice to have them in F-Droid. --- src/org/fdroid/fdroid/AppDetails.java | 11 +++++++++-- src/org/fdroid/fdroid/DB.java | 6 ++++++ src/org/fdroid/fdroid/Downloader.java | 1 + src/org/fdroid/fdroid/FDroid.java | 10 +++++++++- src/org/fdroid/fdroid/FDroidApp.java | 1 + src/org/fdroid/fdroid/ManageRepo.java | 4 ++++ src/org/fdroid/fdroid/UpdateService.java | 1 + .../fdroid/views/AppListFragmentPageAdapter.java | 1 + .../fdroid/views/fragments/AppListFragment.java | 1 + .../fdroid/views/fragments/AvailableAppsFragment.java | 2 ++ .../fdroid/views/fragments/CanUpdateAppsFragment.java | 1 + .../fdroid/views/fragments/InstalledAppsFragment.java | 1 + 12 files changed, 37 insertions(+), 3 deletions(-) diff --git a/src/org/fdroid/fdroid/AppDetails.java b/src/org/fdroid/fdroid/AppDetails.java index 7759bf80e..7f25bff85 100644 --- a/src/org/fdroid/fdroid/AppDetails.java +++ b/src/org/fdroid/fdroid/AppDetails.java @@ -433,14 +433,14 @@ public class AppDetails extends ListActivity { The following is a quick solution to enable both text selection and links. Causes glitches and crashes: java.lang.IndexOutOfBoundsException: setSpan (-1 ... -1) starts before 0 - + class CustomMovementMethod extends LinkMovementMethod { @Override public boolean canSelectArbitrarily () { return true; } } - + if (Utils.hasApi(11)) { tv.setTextIsSelectable(true); tv.setMovementMethod(new CustomMovementMethod()); @@ -598,6 +598,7 @@ public class AppDetails extends ListActivity { ask_alrt.setMessage(getString(R.string.installDowngrade)); ask_alrt.setPositiveButton(getString(R.string.yes), new DialogInterface.OnClickListener() { + @Override public void onClick(DialogInterface dialog, int whichButton) { install(); @@ -605,6 +606,7 @@ public class AppDetails extends ListActivity { }); ask_alrt.setNegativeButton(getString(R.string.no), new DialogInterface.OnClickListener() { + @Override public void onClick(DialogInterface dialog, int whichButton) { return; @@ -807,6 +809,7 @@ public class AppDetails extends ListActivity { ask_alrt.setMessage(getString(R.string.installIncompatible)); ask_alrt.setPositiveButton(getString(R.string.yes), new DialogInterface.OnClickListener() { + @Override public void onClick(DialogInterface dialog, int whichButton) { downloadHandler = new DownloadHandler(app.curApk, @@ -816,6 +819,7 @@ public class AppDetails extends ListActivity { }); ask_alrt.setNegativeButton(getString(R.string.no), new DialogInterface.OnClickListener() { + @Override public void onClick(DialogInterface dialog, int whichButton) { return; @@ -831,6 +835,7 @@ public class AppDetails extends ListActivity { builder.setMessage(R.string.SignatureMismatch).setPositiveButton( getString(R.string.ok), new DialogInterface.OnClickListener() { + @Override public void onClick(DialogInterface dialog, int id) { dialog.cancel(); } @@ -890,6 +895,7 @@ public class AppDetails extends ListActivity { pd.setCancelable(true); pd.setCanceledOnTouchOutside(false); pd.setOnCancelListener(new DialogInterface.OnCancelListener() { + @Override public void onCancel(DialogInterface dialog) { downloadHandler.cancel(); } @@ -897,6 +903,7 @@ public class AppDetails extends ListActivity { pd.setButton(DialogInterface.BUTTON_NEUTRAL, getString(R.string.cancel), new DialogInterface.OnClickListener() { + @Override public void onClick(DialogInterface dialog, int which) { pd.cancel(); } diff --git a/src/org/fdroid/fdroid/DB.java b/src/org/fdroid/fdroid/DB.java index 531c22d04..358cc0540 100644 --- a/src/org/fdroid/fdroid/DB.java +++ b/src/org/fdroid/fdroid/DB.java @@ -19,6 +19,7 @@ package org.fdroid.fdroid; +import android.annotation.SuppressLint; import java.io.File; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -350,6 +351,7 @@ public class DB { } private static class BasicChecker extends CompatibilityChecker { + @Override public boolean isCompatible(Apk apk) { return hasApi(apk.minSdkVersion); } @@ -362,6 +364,7 @@ public class DB { private List cpuAbis; private boolean ignoreTouchscreen; + @SuppressLint("NewApi") public EclairChecker(Context ctx) { SharedPreferences prefs = PreferenceManager @@ -397,6 +400,7 @@ public class DB { return false; } + @Override public boolean isCompatible(Apk apk) { if (!hasApi(apk.minSdkVersion)) return false; @@ -1002,10 +1006,12 @@ public class DB { return (instance == null ? null : instance.toString()); } + @Override public String toString() { return value; } + @Override public Iterator iterator() { SimpleStringSplitter splitter = new SimpleStringSplitter(','); splitter.setString(value); diff --git a/src/org/fdroid/fdroid/Downloader.java b/src/org/fdroid/fdroid/Downloader.java index ca7a5a987..e0cb96ad6 100644 --- a/src/org/fdroid/fdroid/Downloader.java +++ b/src/org/fdroid/fdroid/Downloader.java @@ -95,6 +95,7 @@ public class Downloader extends Thread { return curapk; } + @Override public void run() { InputStream input = null; diff --git a/src/org/fdroid/fdroid/FDroid.java b/src/org/fdroid/fdroid/FDroid.java index 5068adada..63353ffec 100644 --- a/src/org/fdroid/fdroid/FDroid.java +++ b/src/org/fdroid/fdroid/FDroid.java @@ -23,6 +23,7 @@ import android.content.*; import android.content.res.Configuration; import android.support.v4.view.MenuItemCompat; +import android.annotation.TargetApi; import android.app.AlertDialog; import android.app.AlertDialog.Builder; import android.app.NotificationManager; @@ -123,6 +124,7 @@ public class FDroid extends FragmentActivity { manager.repopulateLists(); } + @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); getTabManager().onConfigurationChanged(newConfig); @@ -205,6 +207,7 @@ public class FDroid extends FragmentActivity { alrt.setButton(AlertDialog.BUTTON_NEUTRAL, getString(R.string.about_website), new DialogInterface.OnClickListener() { + @Override public void onClick(DialogInterface dialog, int whichButton) { Uri uri = Uri.parse("https://f-droid.org"); @@ -213,6 +216,7 @@ public class FDroid extends FragmentActivity { }); alrt.setButton(AlertDialog.BUTTON_NEGATIVE, getString(R.string.ok), new DialogInterface.OnClickListener() { + @Override public void onClick(DialogInterface dialog, int whichButton) { } @@ -223,6 +227,7 @@ public class FDroid extends FragmentActivity { return super.onOptionsItemSelected(item); } + @TargetApi(5) @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { @@ -237,6 +242,7 @@ public class FDroid extends FragmentActivity { ask_alrt.setMessage(getString(R.string.repo_alrt)); ask_alrt.setPositiveButton(getString(R.string.yes), new DialogInterface.OnClickListener() { + @Override public void onClick(DialogInterface dialog, int whichButton) { updateRepos(); @@ -244,6 +250,7 @@ public class FDroid extends FragmentActivity { }); ask_alrt.setNegativeButton(getString(R.string.no), new DialogInterface.OnClickListener() { + @Override public void onClick(DialogInterface dialog, int whichButton) { return; @@ -264,7 +271,7 @@ public class FDroid extends FragmentActivity { } else if ((resultCode & PreferencesActivity.RESULT_REFILTER) != 0) { ((FDroidApp) getApplication()).filterApps(); } - + if ((resultCode & PreferencesActivity.RESULT_RESTART) != 0) { ((FDroidApp) getApplication()).reloadTheme(); final Intent intent = getIntent(); @@ -284,6 +291,7 @@ public class FDroid extends FragmentActivity { viewPageAdapter = new AppListFragmentPageAdapter(this); viewPager.setAdapter(viewPageAdapter); viewPager.setOnPageChangeListener( new ViewPager.SimpleOnPageChangeListener() { + @Override public void onPageSelected(int position) { getTabManager().selectTab(position); } diff --git a/src/org/fdroid/fdroid/FDroidApp.java b/src/org/fdroid/fdroid/FDroidApp.java index 9d401f92c..b7dea120d 100644 --- a/src/org/fdroid/fdroid/FDroidApp.java +++ b/src/org/fdroid/fdroid/FDroidApp.java @@ -117,6 +117,7 @@ public class FDroidApp extends Application { .discCache(new UnlimitedDiscCache( new File(StorageUtils.getCacheDirectory(ctx), "icons"), new FileNameGenerator() { + @Override public String generate(String imageUri) { return imageUri.substring( imageUri.lastIndexOf('/') + 1); diff --git a/src/org/fdroid/fdroid/ManageRepo.java b/src/org/fdroid/fdroid/ManageRepo.java index fae936183..4e948b614 100644 --- a/src/org/fdroid/fdroid/ManageRepo.java +++ b/src/org/fdroid/fdroid/ManageRepo.java @@ -202,6 +202,7 @@ public class ManageRepo extends ListActivity { redraw(); } + @Override public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); @@ -332,6 +333,7 @@ public class ManageRepo extends ListActivity { builder.setIcon(android.R.drawable.ic_menu_close_clear_cancel); builder.setMultiChoiceItems(b, null, new DialogInterface.OnMultiChoiceClickListener() { + @Override public void onClick(DialogInterface dialog, int whichButton, boolean isChecked) { if (isChecked) { @@ -343,6 +345,7 @@ public class ManageRepo extends ListActivity { }); builder.setPositiveButton(getString(R.string.ok), new DialogInterface.OnClickListener() { + @Override public void onClick(DialogInterface dialog, int whichButton) { try { @@ -357,6 +360,7 @@ public class ManageRepo extends ListActivity { }); builder.setNegativeButton(getString(R.string.cancel), new DialogInterface.OnClickListener() { + @Override public void onClick(DialogInterface dialog, int whichButton) { return; diff --git a/src/org/fdroid/fdroid/UpdateService.java b/src/org/fdroid/fdroid/UpdateService.java index a7c07787c..5323d0e60 100644 --- a/src/org/fdroid/fdroid/UpdateService.java +++ b/src/org/fdroid/fdroid/UpdateService.java @@ -115,6 +115,7 @@ public class UpdateService extends IntentService implements ProgressListener { return count; } + @Override protected void onHandleIntent(Intent intent) { receiver = intent.getParcelableExtra("receiver"); diff --git a/src/org/fdroid/fdroid/views/AppListFragmentPageAdapter.java b/src/org/fdroid/fdroid/views/AppListFragmentPageAdapter.java index 575839865..cd8946490 100644 --- a/src/org/fdroid/fdroid/views/AppListFragmentPageAdapter.java +++ b/src/org/fdroid/fdroid/views/AppListFragmentPageAdapter.java @@ -38,6 +38,7 @@ public class AppListFragmentPageAdapter extends FragmentPagerAdapter { return 3; } + @Override public String getPageTitle(int i) { switch(i) { case 0: diff --git a/src/org/fdroid/fdroid/views/fragments/AppListFragment.java b/src/org/fdroid/fdroid/views/fragments/AppListFragment.java index b8102f320..5177c4ac9 100644 --- a/src/org/fdroid/fdroid/views/fragments/AppListFragment.java +++ b/src/org/fdroid/fdroid/views/fragments/AppListFragment.java @@ -32,6 +32,7 @@ abstract class AppListFragment extends Fragment implements AdapterView.OnItemCli Preferences.get().unregisterCompactLayoutChangeListener(this); } + @Override public void onAttach(Activity activity) { super.onAttach(activity); try { diff --git a/src/org/fdroid/fdroid/views/fragments/AvailableAppsFragment.java b/src/org/fdroid/fdroid/views/fragments/AvailableAppsFragment.java index f7cd4263d..26f34e363 100644 --- a/src/org/fdroid/fdroid/views/fragments/AvailableAppsFragment.java +++ b/src/org/fdroid/fdroid/views/fragments/AvailableAppsFragment.java @@ -12,6 +12,7 @@ import org.fdroid.fdroid.views.AppListView; public class AvailableAppsFragment extends AppListFragment implements AdapterView.OnItemSelectedListener { + @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { AppListView view = new AppListView(getActivity()); view.setOrientation(LinearLayout.VERTICAL); @@ -40,6 +41,7 @@ public class AvailableAppsFragment extends AppListFragment implements AdapterVie return view; } + @Override public void onItemSelected(AdapterView parent, View view, int pos, long id) { String category = parent.getItemAtPosition(pos).toString(); diff --git a/src/org/fdroid/fdroid/views/fragments/CanUpdateAppsFragment.java b/src/org/fdroid/fdroid/views/fragments/CanUpdateAppsFragment.java index a233f61e7..a390f365c 100644 --- a/src/org/fdroid/fdroid/views/fragments/CanUpdateAppsFragment.java +++ b/src/org/fdroid/fdroid/views/fragments/CanUpdateAppsFragment.java @@ -9,6 +9,7 @@ import org.fdroid.fdroid.views.AppListAdapter; public class CanUpdateAppsFragment extends AppListFragment { + @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return createPlainAppList(); } diff --git a/src/org/fdroid/fdroid/views/fragments/InstalledAppsFragment.java b/src/org/fdroid/fdroid/views/fragments/InstalledAppsFragment.java index 8eb4bf04c..db3a93cfc 100644 --- a/src/org/fdroid/fdroid/views/fragments/InstalledAppsFragment.java +++ b/src/org/fdroid/fdroid/views/fragments/InstalledAppsFragment.java @@ -9,6 +9,7 @@ import org.fdroid.fdroid.views.AppListAdapter; public class InstalledAppsFragment extends AppListFragment { + @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return createPlainAppList(); } From fd28883429d7be469650c4214cce0a72c3d47d98 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Tue, 19 Nov 2013 23:58:29 -0500 Subject: [PATCH 3/6] break out signing key fingerprint calculation into its own method This will be needed for checking an incoming repo URL with a fingerprint included to see if that uri/fingerprint already exists in the database. --- src/org/fdroid/fdroid/ManageRepo.java | 39 ++++++++++++++++----------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/src/org/fdroid/fdroid/ManageRepo.java b/src/org/fdroid/fdroid/ManageRepo.java index 4e948b614..46c45d421 100644 --- a/src/org/fdroid/fdroid/ManageRepo.java +++ b/src/org/fdroid/fdroid/ManageRepo.java @@ -158,22 +158,8 @@ public class ManageRepo extends ListActivity { server_line.put("inuse", R.drawable.btn_check_off); } if (repo.pubkey != null) { - try { - MessageDigest digest = MessageDigest.getInstance("SHA-1"); - digest.update(Hasher.unhex(repo.pubkey)); - byte[] fingerprint = digest.digest(); - Formatter formatter = new Formatter(new StringBuilder()); - formatter.format("%02X", fingerprint[0]); - for (int i = 1; i < fingerprint.length; i++) { - formatter.format(i % 5 == 0 ? " %02X" : ":%02X", - fingerprint[i]); - } - server_line.put("fingerprint", formatter.toString()); - formatter.close(); - } catch (Exception e) { - Log.w("FDroid", "Unable to get certificate fingerprint.\n" - + Log.getStackTraceString(e)); - } + String fingerprint = getRepoFingerprint(repo); + server_line.put("fingerprint", fingerprint); } result.add(server_line); } @@ -244,6 +230,27 @@ public class ManageRepo extends ListActivity { return null; } + protected String getRepoFingerprint(Repo repo) { + String ret = null; + try { + MessageDigest digest = MessageDigest.getInstance("SHA-1"); + digest.update(Hasher.unhex(repo.pubkey)); + byte[] fingerprint = digest.digest(); + Formatter formatter = new Formatter(new StringBuilder()); + formatter.format("%02X", fingerprint[0]); + for (int i = 1; i < fingerprint.length; i++) { + formatter.format(i % 5 == 0 ? " %02X" : ":%02X", + fingerprint[i]); + } + ret = formatter.toString(); + formatter.close(); + } catch (Exception e) { + Log.w("FDroid", "Unable to get certificate fingerprint.\n" + + Log.getStackTraceString(e)); + } + return ret; + } + @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { From 9b485bece37dad4afa71b4767901815343a70404 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Thu, 21 Nov 2013 21:32:44 -0500 Subject: [PATCH 4/6] switch repo key fingerprint to SHA-256 since SHA-1 is considered deprecated * a number of sources have said to avoid SHA-1 in new implementations * nothing currently depends on the SHA-1 fingerprint in the code, it is only used to display on the repo list. * Java 7 requires SHA-256 to be included * keytool -list -v shows the SHA-256 fingerprint --- src/org/fdroid/fdroid/ManageRepo.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/org/fdroid/fdroid/ManageRepo.java b/src/org/fdroid/fdroid/ManageRepo.java index 46c45d421..8b91e440d 100644 --- a/src/org/fdroid/fdroid/ManageRepo.java +++ b/src/org/fdroid/fdroid/ManageRepo.java @@ -233,7 +233,8 @@ public class ManageRepo extends ListActivity { protected String getRepoFingerprint(Repo repo) { String ret = null; try { - MessageDigest digest = MessageDigest.getInstance("SHA-1"); + // keytool -list -v gives you the SHA-256 fingerprint + MessageDigest digest = MessageDigest.getInstance("SHA-256"); digest.update(Hasher.unhex(repo.pubkey)); byte[] fingerprint = digest.digest(); Formatter formatter = new Formatter(new StringBuilder()); From ee21a2724c707efbb3ec7e419772455028cc8a69 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Thu, 21 Nov 2013 21:35:29 -0500 Subject: [PATCH 5/6] generate fingerprint as a hex String, leave formatting for display code When the fingerprint is generated to be stored in the database in the repo table, make it a single String that is a hex number. This is a natural format for working with the fingerprints programmatically. The display formatting can then be handled by the display code, and can freely change without affecting the underlying function of the code. --- src/org/fdroid/fdroid/ManageRepo.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/org/fdroid/fdroid/ManageRepo.java b/src/org/fdroid/fdroid/ManageRepo.java index 8b91e440d..efdf932e4 100644 --- a/src/org/fdroid/fdroid/ManageRepo.java +++ b/src/org/fdroid/fdroid/ManageRepo.java @@ -238,10 +238,8 @@ public class ManageRepo extends ListActivity { digest.update(Hasher.unhex(repo.pubkey)); byte[] fingerprint = digest.digest(); Formatter formatter = new Formatter(new StringBuilder()); - formatter.format("%02X", fingerprint[0]); for (int i = 1; i < fingerprint.length; i++) { - formatter.format(i % 5 == 0 ? " %02X" : ":%02X", - fingerprint[i]); + formatter.format("%02X", fingerprint[i]); } ret = formatter.toString(); formatter.close(); From 95f932f79f10f2ee9c06809c8b41bb2dc49a545d Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Thu, 21 Nov 2013 23:06:09 -0500 Subject: [PATCH 6/6] update database to store repo key fingerprint The stored fingerprint is needed for comparing new, incoming repos that are in the Add Repo dialog. This is to prevent malicious use of the automated adding of repos via QR Codes, NFC, etc. The only other option that I could think of for handling this situation is for the Add Repo dialog to open a socket to the proposed repo to get its pubkey. That seems much less desirable than just storing the fingerprints in the database. --- src/org/fdroid/fdroid/DB.java | 93 +++++++++++++++++++++++---- src/org/fdroid/fdroid/ManageRepo.java | 34 ++-------- 2 files changed, 88 insertions(+), 39 deletions(-) diff --git a/src/org/fdroid/fdroid/DB.java b/src/org/fdroid/fdroid/DB.java index 358cc0540..3b950bd5d 100644 --- a/src/org/fdroid/fdroid/DB.java +++ b/src/org/fdroid/fdroid/DB.java @@ -21,10 +21,12 @@ package org.fdroid.fdroid; import android.annotation.SuppressLint; import java.io.File; +import java.security.MessageDigest; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Date; +import java.util.Formatter; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -434,7 +436,8 @@ public class DB { private static final String CREATE_TABLE_REPO = "create table " + TABLE_REPO + " (id integer primary key, address text not null, " + "name text, description text, inuse integer not null, " - + "priority integer not null, pubkey text, lastetag text);"; + + "priority integer not null, pubkey text, fingerprint text, " + + "lastetag text);"; public static class Repo { public int id; @@ -444,10 +447,11 @@ public class DB { public boolean inuse; public int priority; public String pubkey; // null for an unsigned repo + public String fingerprint; // always null for an unsigned repo public String lastetag; // last etag we updated from, null forces update } - private final int DBVersion = 28; + private final int DBVersion = 29; private static void createAppApk(SQLiteDatabase db) { db.execSQL(CREATE_TABLE_APP); @@ -457,6 +461,26 @@ public class DB { db.execSQL("create index apk_id on " + TABLE_APK + " (id);"); } + public static String calcFingerprint(String pubkey) { + String ret = null; + try { + // keytool -list -v gives you the SHA-256 fingerprint + MessageDigest digest = MessageDigest.getInstance("SHA-256"); + digest.update(Hasher.unhex(pubkey)); + byte[] fingerprint = digest.digest(); + Formatter formatter = new Formatter(new StringBuilder()); + for (int i = 1; i < fingerprint.length; i++) { + formatter.format("%02X", fingerprint[i]); + } + ret = formatter.toString(); + formatter.close(); + } catch (Exception e) { + Log.w("FDroid", "Unable to get certificate fingerprint.\n" + + Log.getStackTraceString(e)); + } + return ret; + } + public void resetTransient(SQLiteDatabase db) { mContext.getSharedPreferences("FDroid", Context.MODE_PRIVATE).edit() .putBoolean("triedEmptyUpdate", false).commit(); @@ -491,8 +515,10 @@ public class DB { mContext.getString(R.string.default_repo_name)); values.put("description", mContext.getString(R.string.default_repo_description)); - values.put("pubkey", - mContext.getString(R.string.default_repo_pubkey)); + String pubkey = mContext.getString(R.string.default_repo_pubkey); + String fingerprint = DB.calcFingerprint(pubkey); + values.put("pubkey", pubkey); + values.put("fingerprint", fingerprint); values.put("inuse", 1); values.put("priority", 10); values.put("lastetag", (String) null); @@ -505,8 +531,9 @@ public class DB { mContext.getString(R.string.default_repo_name2)); values.put("description", mContext.getString(R.string.default_repo_description2)); - values.put("pubkey", - mContext.getString(R.string.default_repo_pubkey)); + // default #2 is /archive which has the same key as /repo + values.put("pubkey", pubkey); + values.put("fingerprint", fingerprint); values.put("inuse", 0); values.put("priority", 20); values.put("lastetag", (String) null); @@ -568,6 +595,28 @@ public class DB { mContext.getString(R.string.default_repo_address2) }); } + if (oldVersion < 29) { + if (!columnExists(db, TABLE_REPO, "fingerprint")) + db.execSQL("alter table " + TABLE_REPO + " add column fingerprint text"); + List oldrepos = new ArrayList(); + Cursor c = db.query(TABLE_REPO, + new String[] { "address", "pubkey" }, + null, null, null, null, null); + c.moveToFirst(); + while (!c.isAfterLast()) { + Repo repo = new Repo(); + repo.address = c.getString(0); + repo.pubkey = c.getString(1); + oldrepos.add(repo); + c.moveToNext(); + } + c.close(); + for (Repo repo : oldrepos) { + ContentValues values = new ContentValues(); + values.put("fingerprint", DB.calcFingerprint(repo.pubkey)); + db.update(TABLE_REPO, values, "address = ?", new String[] { repo.address }); + } + } } } @@ -1245,7 +1294,8 @@ public class DB { Cursor c = null; try { c = db.query(TABLE_REPO, new String[] { "address", "name", - "description", "inuse", "priority", "pubkey", "lastetag" }, + "description", "inuse", "priority", "pubkey", "fingerprint", + "lastetag" }, "id = ?", new String[] { Integer.toString(id) }, null, null, null); if (!c.moveToFirst()) return null; @@ -1257,7 +1307,8 @@ public class DB { repo.inuse = (c.getInt(3) == 1); repo.priority = c.getInt(4); repo.pubkey = c.getString(5); - repo.lastetag = c.getString(6); + repo.fingerprint = c.getString(6); + repo.lastetag = c.getString(7); return repo; } finally { if (c != null) @@ -1271,7 +1322,8 @@ public class DB { Cursor c = null; try { c = db.query(TABLE_REPO, new String[] { "id", "address", "name", - "description", "inuse", "priority", "pubkey", "lastetag" }, + "description", "inuse", "priority", "pubkey", "fingerprint", + "lastetag" }, null, null, null, null, "priority"); c.moveToFirst(); while (!c.isAfterLast()) { @@ -1283,7 +1335,8 @@ public class DB { repo.inuse = (c.getInt(4) == 1); repo.priority = c.getInt(5); repo.pubkey = c.getString(6); - repo.lastetag = c.getString(7); + repo.fingerprint = c.getString(7); + repo.lastetag = c.getString(8); repos.add(repo); c.moveToNext(); } @@ -1316,6 +1369,12 @@ public class DB { values.put("inuse", repo.inuse); values.put("priority", repo.priority); values.put("pubkey", repo.pubkey); + if (repo.pubkey != null && repo.fingerprint == null) { + // we got a new pubkey, so calc the fingerprint + values.put("fingerprint", DB.calcFingerprint(repo.pubkey)); + } else { + values.put("fingerprint", repo.fingerprint); + } values.put("lastetag", (String) null); db.update(TABLE_REPO, values, "address = ?", new String[] { repo.address }); @@ -1329,7 +1388,8 @@ public class DB { } public void addRepo(String address, String name, String description, - int priority, String pubkey, boolean inuse) { + int priority, String pubkey, String fingerprint, boolean inuse) + throws SecurityException { ContentValues values = new ContentValues(); values.put("address", address); values.put("name", name); @@ -1337,6 +1397,17 @@ public class DB { values.put("inuse", inuse ? 1 : 0); values.put("priority", priority); values.put("pubkey", pubkey); + String calcedFingerprint = DB.calcFingerprint(pubkey); + if (fingerprint == null) { + fingerprint = calcedFingerprint; + } else { + fingerprint = fingerprint.toUpperCase(); + if (!fingerprint.equals(calcedFingerprint)) { + throw new SecurityException("Given fingerprint does not match calculated one! (" + + fingerprint + " != " + calcedFingerprint); + } + } + values.put("fingerprint", fingerprint); values.put("lastetag", (String) null); db.insert(TABLE_REPO, null, values); } diff --git a/src/org/fdroid/fdroid/ManageRepo.java b/src/org/fdroid/fdroid/ManageRepo.java index efdf932e4..60ad9c656 100644 --- a/src/org/fdroid/fdroid/ManageRepo.java +++ b/src/org/fdroid/fdroid/ManageRepo.java @@ -19,10 +19,8 @@ package org.fdroid.fdroid; -import java.security.MessageDigest; import java.util.ArrayList; import java.util.Date; -import java.util.Formatter; import java.util.HashMap; import java.util.List; import java.util.Locale; @@ -157,9 +155,8 @@ public class ManageRepo extends ListActivity { } else { server_line.put("inuse", R.drawable.btn_check_off); } - if (repo.pubkey != null) { - String fingerprint = getRepoFingerprint(repo); - server_line.put("fingerprint", fingerprint); + if (repo.fingerprint != null) { + server_line.put("fingerprint", repo.fingerprint); } result.add(server_line); } @@ -202,10 +199,10 @@ public class ManageRepo extends ListActivity { return true; } - protected void addRepo(String repoUri) { + protected void addRepo(String repoUri, String fingerprint) { try { DB db = DB.getDB(); - db.addRepo(repoUri, null, null, 10, null, true); + db.addRepo(repoUri, null, null, 10, null, fingerprint, true); } finally { DB.releaseDB(); } @@ -230,26 +227,6 @@ public class ManageRepo extends ListActivity { return null; } - protected String getRepoFingerprint(Repo repo) { - String ret = null; - try { - // keytool -list -v gives you the SHA-256 fingerprint - MessageDigest digest = MessageDigest.getInstance("SHA-256"); - digest.update(Hasher.unhex(repo.pubkey)); - byte[] fingerprint = digest.digest(); - Formatter formatter = new Formatter(new StringBuilder()); - for (int i = 1; i < fingerprint.length; i++) { - formatter.format("%02X", fingerprint[i]); - } - ret = formatter.toString(); - formatter.close(); - } catch (Exception e) { - Log.w("FDroid", "Unable to get certificate fingerprint.\n" - + Log.getStackTraceString(e)); - } - return ret; - } - @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { @@ -275,7 +252,8 @@ public class ManageRepo extends ListActivity { new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { - addRepo(uriEditText.getText().toString()); + addRepo(uriEditText.getText().toString(), + fingerprintEditText.getText().toString()); changed = true; redraw(); }