From aa1b9e6696dc9181ca898a50743023126b4756a2 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Fri, 25 Apr 2014 21:34:30 -0400 Subject: [PATCH 1/7] allow incoming repos via Intent when on ManageRepo view Since before, incoming repo Intents where handled in the Fragment's onCreate(), an Intent that was received while the Fragment was visible was just ignored. Activities have onNewIntent() for that, but Fragments don't so the repo Intent handling had to be moved to the ManageRepo Activity. That makes for a more direct relationship anyway, since ManageRepo is what is configured as receiving all those Intents in AndroidManifest.xml. --- src/org/fdroid/fdroid/ManageRepo.java | 90 +++++++++++++++++++ .../views/fragments/RepoListFragment.java | 87 +++--------------- 2 files changed, 100 insertions(+), 77 deletions(-) diff --git a/src/org/fdroid/fdroid/ManageRepo.java b/src/org/fdroid/fdroid/ManageRepo.java index 665f8bd6c..733f0e063 100644 --- a/src/org/fdroid/fdroid/ManageRepo.java +++ b/src/org/fdroid/fdroid/ManageRepo.java @@ -20,17 +20,25 @@ package org.fdroid.fdroid; import android.app.Activity; +import android.content.Context; import android.content.Intent; +import android.net.Uri; +import android.net.wifi.WifiInfo; +import android.net.wifi.WifiManager; import android.os.Bundle; import android.support.v4.app.FragmentActivity; import android.support.v4.app.NavUtils; +import android.text.TextUtils; import android.util.Log; import android.view.MenuItem; import android.widget.LinearLayout; +import android.widget.Toast; + import org.fdroid.fdroid.compat.ActionBarCompat; import org.fdroid.fdroid.views.fragments.RepoListFragment; +import java.util.Locale; public class ManageRepo extends FragmentActivity { /** @@ -68,6 +76,18 @@ public class ManageRepo extends FragmentActivity { ActionBarCompat.create(this).setDisplayHomeAsUpEnabled(true); } + @Override + protected void onResume() { + super.onResume(); + /* let's see if someone is trying to send us a new repo */ + addRepoFromIntent(getIntent()); + } + + @Override + protected void onNewIntent(Intent intent) { + addRepoFromIntent(intent); + } + @Override public void finish() { Intent ret = new Intent(); @@ -99,4 +119,74 @@ public class ManageRepo extends FragmentActivity { } return super.onOptionsItemSelected(item); } + + private void addRepoFromIntent(Intent intent) { + /* an URL from a click, NFC, QRCode scan, etc */ + Uri uri = intent.getData(); + if (uri != null) { + // scheme and host should only ever be pure ASCII aka Locale.ENGLISH + String scheme = intent.getScheme(); + String host = uri.getHost(); + if (scheme == null || host == null) { + String msg = String.format(getString(R.string.malformed_repo_uri), uri); + Toast.makeText(this, msg, Toast.LENGTH_LONG).show(); + return; + } + if (scheme.equals("FDROIDREPO") || scheme.equals("FDROIDREPOS")) { + /* + * QRCodes are more efficient in all upper case, so QR URIs are + * encoded in all upper case, then forced to lower case. + * Checking if the special F-Droid scheme being all is upper + * case means it should be downcased. + */ + uri = Uri.parse(uri.toString().toLowerCase(Locale.ENGLISH)); + } else if (uri.getPath().startsWith("/FDROID/REPO")) { + /* + * some QR scanners chop off the fdroidrepo:// and just try + * http://, then the incoming URI does not get downcased + * properly, and the query string is stripped off. So just + * downcase the path, and carry on to get something working. + */ + uri = Uri.parse(uri.toString().toLowerCase(Locale.ENGLISH)); + } + // make scheme and host lowercase so they're readable in dialogs + scheme = scheme.toLowerCase(Locale.ENGLISH); + host = host.toLowerCase(Locale.ENGLISH); + String fingerprint = uri.getQueryParameter("fingerprint"); + Log.i("RepoListFragment", "onCreate " + fingerprint); + if (scheme.equals("fdroidrepos") || scheme.equals("fdroidrepo") + || scheme.equals("https") || scheme.equals("http")) { + + /* sanitize and format for function and readability */ + String uriString = uri.toString() + .replaceAll("\\?.*$", "") // remove the whole query + .replaceAll("/*$", "") // remove all trailing slashes + .replace(uri.getHost(), host) // downcase host name + .replace(intent.getScheme(), scheme) // downcase scheme + .replace("fdroidrepo", "http"); // proper repo address + listFragment.importRepo(uriString, fingerprint); + + // if this is a local repo, check we're on the same wifi + String uriBssid = uri.getQueryParameter("bssid"); + if (!TextUtils.isEmpty(uriBssid)) { + if (uri.getPort() != 8888 + && !host.matches("[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+")) { + Log.i("ManageRepo", "URI is not local repo: " + uri); + return; + } + WifiManager wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE); + WifiInfo wifiInfo = wifiManager.getConnectionInfo(); + String bssid = wifiInfo.getBSSID().toLowerCase(Locale.ENGLISH); + uriBssid = Uri.decode(uriBssid).toLowerCase(Locale.ENGLISH); + if (!bssid.equals(uriBssid)) { + String msg = String.format(getString(R.string.not_on_same_wifi), + uri.getQueryParameter("ssid")); + Toast.makeText(this, msg, Toast.LENGTH_LONG).show(); + } + // TODO we should help the user to the right thing here, + // instead of just showing a message! + } + } + } + } } diff --git a/src/org/fdroid/fdroid/views/fragments/RepoListFragment.java b/src/org/fdroid/fdroid/views/fragments/RepoListFragment.java index fa43cb82b..7f04bd64f 100644 --- a/src/org/fdroid/fdroid/views/fragments/RepoListFragment.java +++ b/src/org/fdroid/fdroid/views/fragments/RepoListFragment.java @@ -63,8 +63,6 @@ public class RepoListFragment extends ListFragment private final int UPDATE_REPOS = 2; private final int SCAN_FOR_REPOS = 3; - private WifiManager wifiManager; - public boolean hasChanged() { return changed; } @@ -85,13 +83,11 @@ public class RepoListFragment extends ListFragment @Override public void onLoadFinished(Loader cursorLoader, Cursor cursor) { - Log.i("FDroid", "Repo cursor loaded."); repoAdapter.swapCursor(cursor); } @Override public void onLoaderReset(Loader cursorLoader) { - Log.i("FDroid", "Repo cursor reset."); repoAdapter.swapCursor(null); } @@ -186,79 +182,6 @@ public class RepoListFragment extends ListFragment super.onCreate(savedInstanceState); setHasOptionsMenu(true); - - /* let's see if someone is trying to send us a new repo */ - Intent intent = getActivity().getIntent(); - /* an URL from a click, NFC, QRCode scan, etc */ - Uri uri = intent.getData(); - if (uri != null) { - // scheme and host should only ever be pure ASCII aka Locale.ENGLISH - String scheme = intent.getScheme(); - String host = uri.getHost(); - if (scheme == null || host == null) { - String msg = String.format(getString(R.string.malformed_repo_uri), uri); - Toast.makeText(getActivity(), msg, Toast.LENGTH_LONG).show(); - return; - } - if (scheme.equals("FDROIDREPO") || scheme.equals("FDROIDREPOS")) { - /* - * QRCodes are more efficient in all upper case, so QR URIs are - * encoded in all upper case, then forced to lower case. - * Checking if the special F-Droid scheme being all is upper - * case means it should be downcased. - */ - uri = Uri.parse(uri.toString().toLowerCase(Locale.ENGLISH)); - } else if (uri.getPath().startsWith("/FDROID/REPO")) { - /* - * some QR scanners chop off the fdroidrepo:// and just try - * http://, then the incoming URI does not get downcased - * properly, and the query string is stripped off. So just - * downcase the path, and carry on to get something working. - */ - uri = Uri.parse(uri.toString().toLowerCase(Locale.ENGLISH)); - } - // make scheme and host lowercase so they're readable in dialogs - scheme = scheme.toLowerCase(Locale.ENGLISH); - host = host.toLowerCase(Locale.ENGLISH); - String fingerprint = uri.getQueryParameter("fingerprint"); - if (scheme.equals("fdroidrepos") || scheme.equals("fdroidrepo") - || scheme.equals("https") || scheme.equals("http")) { - - isImportingRepo = true; - - /* sanitize and format for function and readability */ - String uriString = uri.toString() - .replaceAll("\\?.*$", "") // remove the whole query - .replaceAll("/*$", "") // remove all trailing slashes - .replace(uri.getHost(), host) // downcase host name - .replace(intent.getScheme(), scheme) // downcase scheme - .replace("fdroidrepo", "http"); // proper repo address - showAddRepo(uriString, fingerprint); - - // if this is a local repo, check we're on the same wifi - String uriBssid = uri.getQueryParameter("bssid"); - if (!TextUtils.isEmpty(uriBssid)) { - if (uri.getPort() != 8888 - && !host.matches("[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+")) { - Log.i("ManageRepo", "URI is not local repo: " + uri); - return; - } - Activity a = getActivity(); - if (wifiManager == null) - wifiManager = (WifiManager) a.getSystemService(Context.WIFI_SERVICE); - WifiInfo wifiInfo = wifiManager.getConnectionInfo(); - String bssid = wifiInfo.getBSSID().toLowerCase(Locale.ENGLISH); - uriBssid = Uri.decode(uriBssid).toLowerCase(Locale.ENGLISH); - if (!bssid.equals(uriBssid)) { - String msg = String.format(getString(R.string.not_on_same_wifi), - uri.getQueryParameter("ssid")); - Toast.makeText(a, msg, Toast.LENGTH_LONG).show(); - } - // TODO we should help the user to the right thing here, - // instead of just showing a message! - } - } - } } @Override @@ -375,6 +298,11 @@ public class RepoListFragment extends ListFragment nsdHelper.discoverServices(); } + public void importRepo(String uri, String fingerprint) { + isImportingRepo = true; + showAddRepo(uri, fingerprint); + } + private void showAddRepo() { showAddRepo(getNewRepoUri(), null); } @@ -385,6 +313,11 @@ public class RepoListFragment extends ListFragment final EditText uriEditText = (EditText) view.findViewById(R.id.edit_uri); final EditText fingerprintEditText = (EditText) view.findViewById(R.id.edit_fingerprint); + /* + * If the "add new repo" dialog is launched by an action outside of + * FDroid, i.e. a URL, then check to see if any existing repos match, + * and change the action accordingly. + */ final Repo repo = (newAddress != null && isImportingRepo) ? RepoProvider.Helper.findByAddress(getActivity(), newAddress) : null; From 649bfa10b77b3a353a26bffad60c5ba6ea7a02a2 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Fri, 25 Apr 2014 22:46:24 -0400 Subject: [PATCH 2/7] fix silly bug in signing key fingerprint calculation for display This was causing the first byte of the signature to be chopped off, so therefore it would not validate since the fingerprint of the cert from the net connection had the right fingerprint, but it was compared to the stored, truncated version. This also means that the database version needs to be bumped to trigger an upgrade so that the bad 62 char fingerprints are removed from the database. --- src/org/fdroid/fdroid/Utils.java | 4 ++-- src/org/fdroid/fdroid/data/DBHelper.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/org/fdroid/fdroid/Utils.java b/src/org/fdroid/fdroid/Utils.java index 62409bd15..8d146823a 100644 --- a/src/org/fdroid/fdroid/Utils.java +++ b/src/org/fdroid/fdroid/Utils.java @@ -178,7 +178,7 @@ public final class Utils { // return a fingerprint formatted for display public static String formatFingerprint(String fingerprint) { - if (fingerprint.length() != 62) // SHA-256 is 62 hex chars + if (fingerprint.length() != 64) // SHA-256 is 64 hex chars return "BAD FINGERPRINT"; String displayFP = fingerprint.substring(0, 2); for (int i = 2; i < fingerprint.length(); i = i + 2) @@ -218,7 +218,7 @@ public final class Utils { digest.update(key); byte[] fingerprint = digest.digest(); Formatter formatter = new Formatter(new StringBuilder()); - for (int i = 1; i < fingerprint.length; i++) { + for (int i = 0; i < fingerprint.length; i++) { formatter.format("%02X", fingerprint[i]); } ret = formatter.toString(); diff --git a/src/org/fdroid/fdroid/data/DBHelper.java b/src/org/fdroid/fdroid/data/DBHelper.java index 49cfda93a..4d81bb4b4 100644 --- a/src/org/fdroid/fdroid/data/DBHelper.java +++ b/src/org/fdroid/fdroid/data/DBHelper.java @@ -96,7 +96,7 @@ public class DBHelper extends SQLiteOpenHelper { + "versionName TEXT NOT NULL " + " );"; - private static final int DB_VERSION = 43; + private static final int DB_VERSION = 44; private Context context; @@ -322,7 +322,7 @@ public class DBHelper extends SQLiteOpenHelper { * calculate its fingerprint and save it to the database. */ private void addFingerprintToRepo(SQLiteDatabase db, int oldVersion) { - if (oldVersion < 29) { + if (oldVersion < 44) { if (!columnExists(db, TABLE_REPO, "fingerprint")) db.execSQL("alter table " + TABLE_REPO + " add column fingerprint text"); List oldrepos = new ArrayList(); From a0970d07204e0ab97df63f782e1ff49cc94659fe Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Fri, 25 Apr 2014 23:53:56 -0400 Subject: [PATCH 3/7] when adding a repo with fingerprint, make sure to store the pubkey The logic here is crufty, so I slapped a flag in there to make sure that the pubkey gets stored when someone configures a repo and includes the fingerprint. When the fingerprint is set, it will first download the index.jar and verify it against that fingerprint. The logic for storing the pubkey permanently happens later in the XML parsing, so there needs to be a flag to signal to store the pubkey in this case. Before the flow was always index.xml -> get pubkey -> index.jar. Really, there should no longer be support for unsigned repos, then all of this stuff can be dramatically simplified. fixes #2924 https://dev.guardianproject.info/issues/2924 refs #2960 https://dev.guardianproject.info/issues/2960 --- src/org/fdroid/fdroid/updater/RepoUpdater.java | 12 +++++++++--- src/org/fdroid/fdroid/updater/SignedRepoUpdater.java | 1 + 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/org/fdroid/fdroid/updater/RepoUpdater.java b/src/org/fdroid/fdroid/updater/RepoUpdater.java index d961e26fb..486798154 100644 --- a/src/org/fdroid/fdroid/updater/RepoUpdater.java +++ b/src/org/fdroid/fdroid/updater/RepoUpdater.java @@ -43,6 +43,7 @@ abstract public class RepoUpdater { protected final Repo repo; private List apps = new ArrayList(); private List apks = new ArrayList(); + protected boolean usePubkeyInJar = false; protected boolean hasChanged = false; protected ProgressListener progressListener; @@ -230,9 +231,13 @@ abstract public class RepoUpdater { values.put(RepoProvider.DataColumns.LAST_ETAG, etag); } - // We read an unsigned index, but that indicates that - // a signed version is now available... - if (handler.getPubKey() != null && repo.pubkey == null) { + /* + * We read an unsigned index that indicates that a signed version + * is available. Or we received a repo config that included the + * fingerprint, so we need to save the pubkey now. + */ + if (handler.getPubKey() != null && + (repo.pubkey == null || usePubkeyInJar)) { // TODO: Spend the time *now* going to get the etag of the signed // repo, so that we can prevent downloading it next time. Otherwise // next time we update, we have to download the signed index @@ -241,6 +246,7 @@ abstract public class RepoUpdater { Log.d("FDroid", "Public key found - switching to signed repo for future updates"); values.put(RepoProvider.DataColumns.PUBLIC_KEY, handler.getPubKey()); + usePubkeyInJar = false; } if (handler.getVersion() != -1 && handler.getVersion() != repo.version) { diff --git a/src/org/fdroid/fdroid/updater/SignedRepoUpdater.java b/src/org/fdroid/fdroid/updater/SignedRepoUpdater.java index 9b5ef8b1e..f7ac7e805 100644 --- a/src/org/fdroid/fdroid/updater/SignedRepoUpdater.java +++ b/src/org/fdroid/fdroid/updater/SignedRepoUpdater.java @@ -31,6 +31,7 @@ public class SignedRepoUpdater extends RepoUpdater { String certdata = Hasher.hex(cert); if (repo.pubkey == null && repo.fingerprint.equals(Utils.calcFingerprint(cert))) { repo.pubkey = certdata; + usePubkeyInJar = true; } if (repo.pubkey != null && repo.pubkey.equals(certdata)) { match = true; From cce393de096c3b07093e54622b85cb40899c67ef Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Fri, 25 Apr 2014 23:57:42 -0400 Subject: [PATCH 4/7] "unverified" repo state for repos w/ fingerprints but not yet pubkeys If a repo was configured with a fingerprint, but it has not yet updated and gotten the pubkey from the index.jar, then it will be in an "unverified" state, i.e. the signing key fingerprint is stored, but it has not yet been used to check against the pubkey in the index.jar --- res/values/colors.xml | 1 + res/values/strings.xml | 1 + src/org/fdroid/fdroid/data/Repo.java | 9 ++++++++- src/org/fdroid/fdroid/views/RepoAdapter.java | 8 +++++++- 4 files changed, 17 insertions(+), 2 deletions(-) diff --git a/res/values/colors.xml b/res/values/colors.xml index 7cd1486ca..d2777848b 100644 --- a/res/values/colors.xml +++ b/res/values/colors.xml @@ -2,4 +2,5 @@ #ffcccccc #ffCC0000 + #ff999999 \ No newline at end of file diff --git a/res/values/strings.xml b/res/values/strings.xml index b068d0ae9..e8e837990 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -176,6 +176,7 @@ Show icons at regular size Theme Unsigned + Unverified URL Number of apps Fingerprint of Repo Signing Key (SHA-256) diff --git a/src/org/fdroid/fdroid/data/Repo.java b/src/org/fdroid/fdroid/data/Repo.java index 76001ca1d..64d6b3f78 100644 --- a/src/org/fdroid/fdroid/data/Repo.java +++ b/src/org/fdroid/fdroid/data/Repo.java @@ -2,6 +2,7 @@ package org.fdroid.fdroid.data; import android.content.ContentValues; import android.database.Cursor; +import android.text.TextUtils; import android.util.Log; import org.fdroid.fdroid.Utils; @@ -78,7 +79,13 @@ public class Repo extends ValueObject { } public boolean isSigned() { - return this.pubkey != null && this.pubkey.length() > 0; + return !TextUtils.isEmpty(this.pubkey); + } + + // this happens when a repo is configed with a fingerprint, but the client + // has not connected to it yet to download its pubkey + public boolean isSignedButUnverified() { + return TextUtils.isEmpty(this.pubkey) && !TextUtils.isEmpty(this.fingerprint); } public boolean hasBeenUpdated() { diff --git a/src/org/fdroid/fdroid/views/RepoAdapter.java b/src/org/fdroid/fdroid/views/RepoAdapter.java index 307f325de..aa816bda6 100644 --- a/src/org/fdroid/fdroid/views/RepoAdapter.java +++ b/src/org/fdroid/fdroid/views/RepoAdapter.java @@ -97,10 +97,16 @@ public class RepoAdapter extends CursorAdapter { // If we set the signed view to GONE instead of INVISIBLE, then the // height of each list item varies. - View signedView = view.findViewById(R.id.repo_unsigned); + TextView signedView = (TextView) view.findViewById(R.id.repo_unsigned); if (repo.isSigned()) { signedView.setVisibility(View.INVISIBLE); + } else if (repo.isSignedButUnverified()) { + signedView.setText(R.string.unverified); + signedView.setTextColor(view.getResources().getColor(R.color.unverified)); + signedView.setVisibility(View.VISIBLE); } else { + signedView.setText(R.string.unsigned); + signedView.setTextColor(view.getResources().getColor(R.color.unsigned)); signedView.setVisibility(View.VISIBLE); } } From 4dc2c60863dde06fa9880165c9ca9c889e7525ae Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Sat, 26 Apr 2014 16:12:34 -0400 Subject: [PATCH 5/7] add more checks on fingerprints fed to Utils.formatFingerprint() This is the gateway to the user, so this should present trusted info! --- src/org/fdroid/fdroid/Utils.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/org/fdroid/fdroid/Utils.java b/src/org/fdroid/fdroid/Utils.java index 8d146823a..20b64e07a 100644 --- a/src/org/fdroid/fdroid/Utils.java +++ b/src/org/fdroid/fdroid/Utils.java @@ -178,7 +178,9 @@ public final class Utils { // return a fingerprint formatted for display public static String formatFingerprint(String fingerprint) { - if (fingerprint.length() != 64) // SHA-256 is 64 hex chars + if (TextUtils.isEmpty(fingerprint) + || fingerprint.length() != 64 // SHA-256 is 64 hex chars + || fingerprint.matches(".*[^0-9a-fA-F].*")) // its a hex string return "BAD FINGERPRINT"; String displayFP = fingerprint.substring(0, 2); for (int i = 2; i < fingerprint.length(); i = i + 2) From 1018cad3b422bd3b86cfdda56ec435943260fc88 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Sat, 26 Apr 2014 16:14:59 -0400 Subject: [PATCH 6/7] Utils tests: formatFingerprint() and calcFingerprint(String) Prevent another stupid bug like what is fixed by commit 5ff177cd1884ed61def491a813363f96c5de628a refs #2959 https://dev.guardianproject.info/issues/2959 --- test/src/org/fdroid/fdroid/UtilsTest.java | 129 ++++++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 test/src/org/fdroid/fdroid/UtilsTest.java diff --git a/test/src/org/fdroid/fdroid/UtilsTest.java b/test/src/org/fdroid/fdroid/UtilsTest.java new file mode 100644 index 000000000..5b0a1a08d --- /dev/null +++ b/test/src/org/fdroid/fdroid/UtilsTest.java @@ -0,0 +1,129 @@ + +package org.fdroid.fdroid; + +import android.test.AndroidTestCase; + +public class UtilsTest extends AndroidTestCase { + + String fdroidFingerprint = "43238D512C1E5EB2D6569F4A3AFBF5523418B82E0A3ED1552770ABB9A9C9CCAB"; + String fdroidPubkey = "3082035e30820246a00302010202044c49cd00300d06092a864886f70d01010505003071310b300906035504061302554b3110300e06035504081307556e6b6e6f776e3111300f0603550407130857657468657262793110300e060355040a1307556e6b6e6f776e3110300e060355040b1307556e6b6e6f776e311930170603550403131043696172616e2047756c746e69656b73301e170d3130303732333137313032345a170d3337313230383137313032345a3071310b300906035504061302554b3110300e06035504081307556e6b6e6f776e3111300f0603550407130857657468657262793110300e060355040a1307556e6b6e6f776e3110300e060355040b1307556e6b6e6f776e311930170603550403131043696172616e2047756c746e69656b7330820122300d06092a864886f70d01010105000382010f003082010a028201010096d075e47c014e7822c89fd67f795d23203e2a8843f53ba4e6b1bf5f2fd0e225938267cfcae7fbf4fe596346afbaf4070fdb91f66fbcdf2348a3d92430502824f80517b156fab00809bdc8e631bfa9afd42d9045ab5fd6d28d9e140afc1300917b19b7c6c4df4a494cf1f7cb4a63c80d734265d735af9e4f09455f427aa65a53563f87b336ca2c19d244fcbba617ba0b19e56ed34afe0b253ab91e2fdb1271f1b9e3c3232027ed8862a112f0706e234cf236914b939bcf959821ecb2a6c18057e070de3428046d94b175e1d89bd795e535499a091f5bc65a79d539a8d43891ec504058acb28c08393b5718b57600a211e803f4a634e5c57f25b9b8c4422c6fd90203010001300d06092a864886f70d0101050500038201010008e4ef699e9807677ff56753da73efb2390d5ae2c17e4db691d5df7a7b60fc071ae509c5414be7d5da74df2811e83d3668c4a0b1abc84b9fa7d96b4cdf30bba68517ad2a93e233b042972ac0553a4801c9ebe07bf57ebe9a3b3d6d663965260e50f3b8f46db0531761e60340a2bddc3426098397fda54044a17e5244549f9869b460ca5e6e216b6f6a2db0580b480ca2afe6ec6b46eedacfa4aa45038809ece0c5978653d6c85f678e7f5a2156d1bedd8117751e64a4b0dcd140f3040b021821a8d93aed8d01ba36db6c82372211fed714d9a32607038cdfd565bd529ffc637212aaa2c224ef22b603eccefb5bf1e085c191d4b24fe742b17ab3f55d4e6f05ef"; + + String gpRepoFingerprint = "59050C8155DCA377F23D5A15B77D3713400CDBD8B42FBFBE0E3F38096E68CECE"; + String gpRepoPubkey = "308203c5308202ada00302010202047b7cf549300d06092a864886f70d01010b0500308192310b30090603550406130255533111300f060355040813084e657720596f726b3111300f060355040713084e657720596f726b311d301b060355040a131454686520477561726469616e2050726f6a656374311f301d060355040b1316477561726469616e20462d44726f6964204275696c64311d301b06035504031314677561726469616e70726f6a6563742e696e666f301e170d3132313032393130323530305a170d3430303331363130323530305a308192310b30090603550406130255533111300f060355040813084e657720596f726b3111300f060355040713084e657720596f726b311d301b060355040a131454686520477561726469616e2050726f6a656374311f301d060355040b1316477561726469616e20462d44726f6964204275696c64311d301b06035504031314677561726469616e70726f6a6563742e696e666f30820122300d06092a864886f70d01010105000382010f003082010a0282010100b7f1f635fa3fce1a8042aaa960c2dc557e4ad2c082e5787488cba587fd26207cf59507919fc4dcebda5c8c0959d14146d0445593aa6c29dc639570b71712451fd5c231b0c9f5f0bec380503a1c2a3bc00048bc5db682915afa54d1ecf67b45e1e05c0934b3037a33d3a565899131f27a72c03a5de93df17a2376cc3107f03ee9d124c474dfab30d4053e8f39f292e2dcb6cc131bce12a0c5fc307985195d256bf1d7a2703d67c14bf18ed6b772bb847370b20335810e337c064fef7e2795a524c664a853cd46accb8494f865164dabfb698fa8318236432758bc40d52db00d5ce07fe2210dc06cd95298b4f09e6c9b7b7af61c1d62ea43ea36a2331e7b2d4e250203010001a321301f301d0603551d0e0416041404d763e981cf3a295b94a790d8536a783097232b300d06092a864886f70d01010b05000382010100654e6484ff032c54fed1d96d3c8e731302be9dbd7bb4fe635f2dac05b69f3ecbb5acb7c9fe405e2a066567a8f5c2beb8b199b5a4d5bb1b435cf02df026d4fb4edd9d8849078f085b00950083052d57467d65c6eebd98f037cff9b148d621cf8819c4f7dc1459bf8fc5c7d76f901495a7caf35d1e5c106e1d50610c4920c3c1b50adcfbd4ad83ce7353cdea7d856bba0419c224f89a2f3ebc203d20eb6247711ad2b55fd4737936dc42ced7a047cbbd24012079204a2883b6d55d5d5b66d9fd82fb51fca9a5db5fad9af8564cb380ff30ae8263dbbf01b46e01313f53279673daa3f893380285646b244359203e7eecde94ae141b7dfa8e6499bb8e7e0b25ab85"; + + String gpTest0Fingerprint = "C4DC0B2AB5AB58F0CDBF97FF903CF12415F468D90B11877803BC172D31012B2E"; + String gpTest0Pubkey = "308204f3308202dba003020102020436aac0dc300d06092a864886f70d01010b0500302a3110300e060355040b1307462d44726f6964311630140603550403130d70616c6174736368696e6b656e301e170d3133313130353232353534325a170d3133313130363232353534325a302a3110300e060355040b1307462d44726f6964311630140603550403130d70616c6174736368696e6b656e30820222300d06092a864886f70d01010105000382020f003082020a0282020100b1f3cd3db9207f80e9d854159d40a15344bfcc377fba61983d1ac843e52e2fc1a81d96325174328f77dbe382b2b239567d50ad2e1fea13f1272b0370693acd03b9aef3e5a908118065f21193735552c123a9f59f99c2822b7bba7082c72649e17666ac70d332f1c7cf20830373c86f11d2f80a2aa0307c3b526b8769b69371555540f246ca892db4b51226788bb3b869284254266f3ccb1d7b5b08a2cf398f53877b09da0f1cc922ecc928c477660979d07998b29678feaea9b5c93d3a12f89f695eeda766280df22b688e1da15d979845a81c81f9d1252e2e5fd415df2eb0f28cb339a9d9bc13ec1a059333ca766a0982464f8d9a67397f066b3926aa5ac6f2216962da5705d2b9723353ac3b670f5ab4d365cde4e5d0375ca52e7e8c151dd90eda0025be09feae4c94c59608243b45f0527ad8d46e0a0bc97ac27870af53c0550706502ecfa56a30d7442012e6115ada79243481b759319def848199df423c9664574d8d8a7f8949e9f3549e8695fa0b02eab1dc8e30c6432d159076ceb91b709bd848f4e5d74a0880c1ead0534b1f8a354edd05a6d7b44f9a566f9e419bab6834ff2f2d2a54c797b407ccb2d4648247e69b2b85186f9ebd087879a580be281b73f46975e5c94b5a935adf019d6d56992742ebb23865f94a14ed17fc7fb0fbea43eb686760298ae98b197ac8da2ec0b61be240b6f2a574208da9e0fd9e14d90203010001a321301f301d0603551d0e04160414282e3362786f92645dd7809905166e473bbfc722300d06092a864886f70d01010b05000382020100295efaa7d0e985b408a7c6f2891cae1fa7b6338774eee624edd838c0fbaadc755d140ed6007b91e662434010659a4a5597709e23828a1a5e9846b4369ee8fcef10b85fc64db7726aee8c8d93753d4828250323ebdb768ed9958f4c2c61eb48d2329a0196a47898662ed9418e5ba223c4c1e285e94bfe0f5d5b4813b9d0b6b49d304a79879698d320e1ff5e36be441f1dcda5715d4644825d669b15de2765d285253231fbe052360426fe976af404381909043cfe8e7a537275dc75f367235eb0fc357884ea36f00cdb21fbc75ca2ac9c53adc202456e40d0e950af09c4f5de3d876f43fda7880be4800ff2635f681c19a5b8f1cd68319e78f5ff8e29f5225db849f03d473926aa2d492df970cbcba266211003e7c84f5852ea089b62679acd6243e56b18384596443c379effa1419027345bb929a46193c5c1f6274c83f14a8720189ab178945ef56a99fb16ac8aedb6d8b9ec10bd1f6935b189aa9470947d909bf8d0630e8b189696a79e9560631fa79cc22cddc17594c2c86e03fa7102811154d10aa2ff631732c491969b8a356632eabcf22596db4eb011cfaf426ec972967e2c5f426fa1f3e9d8c1d90fbb7665660d02456d9b7c1fa6bb68b0d53c29c6ef4e7c81f91c1819f754a53a03124a36b539cde945287c5be8817244c1548c17ff671f729545dc9155c94f01ceb620333f10000acbeba866cb242155daa76a5169"; + + String gpTest1Fingerprint = "C63AED1AC79D37C7B0474472AC6EFA6C3AB2B11A767A4F42CF360FA5496E3C50"; + String gpTest1Pubkey = "3082039a30820282020900aa6887be1ec84bde300d06092a864886f70d010105050030818e310b30090603550406130255533111300f06035504080c084e657720596f726b311e301c060355040a0c15477561726469616e2050726f6a65637420546573743122302006035504030c19746573742e677561726469616e70726f6a6563742e696e666f3128302606092a864886f70d01090116197465737440677561726469616e70726f6a6563742e696e666f301e170d3134303332383230343132365a170d3431303831323230343132365a30818e310b30090603550406130255533111300f06035504080c084e657720596f726b311e301c060355040a0c15477561726469616e2050726f6a65637420546573743122302006035504030c19746573742e677561726469616e70726f6a6563742e696e666f3128302606092a864886f70d01090116197465737440677561726469616e70726f6a6563742e696e666f30820122300d06092a864886f70d01010105000382010f003082010a02820101009f4895a4a160d14e9de49dd61ac9434715c2aea25a9de75f0361e3f9bd77306cff7a8f508f9a9edc31dfb5b3aa2571e22b1711c08f0616892fa4efdf94321ec93211486b314bcf27385f670492683a0e50f5a022ede2bfc00c69b14e8c8678f313d6d280feb9c53445f087fa9d12a31392ca63d75351587e3cd2337fbf95fd7c2a9322883d74f18680165a697d4a1a4fa3bd835bd45f00561447350af4ec6b6740c0ae7950ff53c386a2efc43a280e4270912d20eb464761799fdbbae50dd0df01f9b25673499029a2e869203e7d63e7ca98826dabf856c965f472de691ddc77f6ed8db468684baf76f7f1cdf7fc3a07109ad8aea8e332a807bedbb8143bbe230203010001300d06092a864886f70d010105050003820101005284015baba5eb092a3c681634b46b9f59a0dbb651c89ca65af730bfeb22726e048194cbd54fb4242f5ec8e514e26dd8887cbcb431f3f2eb224780b6a2204e614d705aed4bd66e153c216d35e1dc1e38e226566af74bb229a2416ea6ffb388d6f64a68386332f34f50d48b630541e2871030bd27d90a1688f46bff4e9707059cd22e56820a4a3d01f9a91b442f6adf0776d9f73533a2dcd7214305491414dbc7c734166cd833e227f9bd8a82b3d464c662c71a07703fb14de0564cad1d3851e35cc9a04ce36fde2abf8d8d9dec07752e535f35aabc3632d6d2106086477e346efebb0d4bec7afc461d7ab7f96200c2dadb2da41d09342aa2fa9ab94ab92d2053"; + + // this pair has one digit missing from the pubkey + String pubkeyShortByOneFingerprint = "C63AED1AC79D37C7B0474472AC6EFA6C3AB2B11A767A4F42CF360FA5496E3C50"; + String pubkeyShortByOnePubkey = "3082039a30820282020900aa6887be1ec84bde300d06092a86488f70d010105050030818e310b30090603550406130255533111300f06035504080c084e657720596f726b311e301c060355040a0c15477561726469616e2050726f6a65637420546573743122302006035504030c19746573742e677561726469616e70726f6a6563742e696e666f3128302606092a864886f70d01090116197465737440677561726469616e70726f6a6563742e696e666f301e170d3134303332383230343132365a170d3431303831323230343132365a30818e310b30090603550406130255533111300f06035504080c084e657720596f726b311e301c060355040a0c15477561726469616e2050726f6a65637420546573743122302006035504030c19746573742e677561726469616e70726f6a6563742e696e666f3128302606092a864886f70d01090116197465737440677561726469616e70726f6a6563742e696e666f30820122300d06092a864886f70d01010105000382010f003082010a02820101009f4895a4a160d14e9de49dd61ac9434715c2aea25a9de75f0361e3f9bd77306cff7a8f508f9a9edc31dfb5b3aa2571e22b1711c08f0616892fa4efdf94321ec93211486b314bcf27385f670492683a0e50f5a022ede2bfc00c69b14e8c8678f313d6d280feb9c53445f087fa9d12a31392ca63d75351587e3cd2337fbf95fd7c2a9322883d74f18680165a697d4a1a4fa3bd835bd45f00561447350af4ec6b6740c0ae7950ff53c386a2efc43a280e4270912d20eb464761799fdbbae50dd0df01f9b25673499029a2e869203e7d63e7ca98826dabf856c965f472de691ddc77f6ed8db468684baf76f7f1cdf7fc3a07109ad8aea8e332a807bedbb8143bbe230203010001300d06092a864886f70d010105050003820101005284015baba5eb092a3c681634b46b9f59a0dbb651c89ca65af730bfeb22726e048194cbd54fb4242f5ec8e514e26dd8887cbcb431f3f2eb224780b6a2204e614d705aed4bd66e153c216d35e1dc1e38e226566af74bb229a2416ea6ffb388d6f64a68386332f34f50d48b630541e2871030bd27d90a1688f46bff4e9707059cd22e56820a4a3d01f9a91b442f6adf0776d9f73533a2dcd7214305491414dbc7c734166cd833e227f9bd8a82b3d464c662c71a07703fb14de0564cad1d3851e35cc9a04ce36fde2abf8d8d9dec07752e535f35aabc3632d6d2106086477e346efebb0d4bec7afc461d7ab7f96200c2dadb2da41d09342aa2fa9ab94ab92d2053"; + + // this pair has one digit missing from the fingerprint + String fingerprintShortByOneFingerprint = "C63AED1AC79D37C7B047442AC6EFA6C3AB2B11A767A4F42CF360FA5496E3C50"; + String fingerprintShortByOnePubkey = "3082039a30820282020900aa6887be1ec84bde300d06092a864886f70d010105050030818e310b30090603550406130255533111300f06035504080c084e657720596f726b311e301c060355040a0c15477561726469616e2050726f6a65637420546573743122302006035504030c19746573742e677561726469616e70726f6a6563742e696e666f3128302606092a864886f70d01090116197465737440677561726469616e70726f6a6563742e696e666f301e170d3134303332383230343132365a170d3431303831323230343132365a30818e310b30090603550406130255533111300f06035504080c084e657720596f726b311e301c060355040a0c15477561726469616e2050726f6a65637420546573743122302006035504030c19746573742e677561726469616e70726f6a6563742e696e666f3128302606092a864886f70d01090116197465737440677561726469616e70726f6a6563742e696e666f30820122300d06092a864886f70d01010105000382010f003082010a02820101009f4895a4a160d14e9de49dd61ac9434715c2aea25a9de75f0361e3f9bd77306cff7a8f508f9a9edc31dfb5b3aa2571e22b1711c08f0616892fa4efdf94321ec93211486b314bcf27385f670492683a0e50f5a022ede2bfc00c69b14e8c8678f313d6d280feb9c53445f087fa9d12a31392ca63d75351587e3cd2337fbf95fd7c2a9322883d74f18680165a697d4a1a4fa3bd835bd45f00561447350af4ec6b6740c0ae7950ff53c386a2efc43a280e4270912d20eb464761799fdbbae50dd0df01f9b25673499029a2e869203e7d63e7ca98826dabf856c965f472de691ddc77f6ed8db468684baf76f7f1cdf7fc3a07109ad8aea8e332a807bedbb8143bbe230203010001300d06092a864886f70d010105050003820101005284015baba5eb092a3c681634b46b9f59a0dbb651c89ca65af730bfeb22726e048194cbd54fb4242f5ec8e514e26dd8887cbcb431f3f2eb224780b6a2204e614d705aed4bd66e153c216d35e1dc1e38e226566af74bb229a2416ea6ffb388d6f64a68386332f34f50d48b630541e2871030bd27d90a1688f46bff4e9707059cd22e56820a4a3d01f9a91b442f6adf0776d9f73533a2dcd7214305491414dbc7c734166cd833e227f9bd8a82b3d464c662c71a07703fb14de0564cad1d3851e35cc9a04ce36fde2abf8d8d9dec07752e535f35aabc3632d6d2106086477e346efebb0d4bec7afc461d7ab7f96200c2dadb2da41d09342aa2fa9ab94ab92d2053"; + + // this pair has one digit added to the pubkey + String pubkeyLongByOneFingerprint = "59050C8155DCA377F23D5A15B77D3713400CDBD8B42FBFBE0E3F38096E68CECE"; + String pubkeyLongByOnePubkey = "308203c5308202ada00302010202047b7cf5493000d06092a864886f70d01010b0500308192310b30090603550406130255533111300f060355040813084e657720596f726b3111300f060355040713084e657720596f726b311d301b060355040a131454686520477561726469616e2050726f6a656374311f301d060355040b1316477561726469616e20462d44726f6964204275696c64311d301b06035504031314677561726469616e70726f6a6563742e696e666f301e170d3132313032393130323530305a170d3430303331363130323530305a308192310b30090603550406130255533111300f060355040813084e657720596f726b3111300f060355040713084e657720596f726b311d301b060355040a131454686520477561726469616e2050726f6a656374311f301d060355040b1316477561726469616e20462d44726f6964204275696c64311d301b06035504031314677561726469616e70726f6a6563742e696e666f30820122300d06092a864886f70d01010105000382010f003082010a0282010100b7f1f635fa3fce1a8042aaa960c2dc557e4ad2c082e5787488cba587fd26207cf59507919fc4dcebda5c8c0959d14146d0445593aa6c29dc639570b71712451fd5c231b0c9f5f0bec380503a1c2a3bc00048bc5db682915afa54d1ecf67b45e1e05c0934b3037a33d3a565899131f27a72c03a5de93df17a2376cc3107f03ee9d124c474dfab30d4053e8f39f292e2dcb6cc131bce12a0c5fc307985195d256bf1d7a2703d67c14bf18ed6b772bb847370b20335810e337c064fef7e2795a524c664a853cd46accb8494f865164dabfb698fa8318236432758bc40d52db00d5ce07fe2210dc06cd95298b4f09e6c9b7b7af61c1d62ea43ea36a2331e7b2d4e250203010001a321301f301d0603551d0e0416041404d763e981cf3a295b94a790d8536a783097232b300d06092a864886f70d01010b05000382010100654e6484ff032c54fed1d96d3c8e731302be9dbd7bb4fe635f2dac05b69f3ecbb5acb7c9fe405e2a066567a8f5c2beb8b199b5a4d5bb1b435cf02df026d4fb4edd9d8849078f085b00950083052d57467d65c6eebd98f037cff9b148d621cf8819c4f7dc1459bf8fc5c7d76f901495a7caf35d1e5c106e1d50610c4920c3c1b50adcfbd4ad83ce7353cdea7d856bba0419c224f89a2f3ebc203d20eb6247711ad2b55fd4737936dc42ced7a047cbbd24012079204a2883b6d55d5d5b66d9fd82fb51fca9a5db5fad9af8564cb380ff30ae8263dbbf01b46e01313f53279673daa3f893380285646b244359203e7eecde94ae141b7dfa8e6499bb8e7e0b25ab85"; + + // this pair has one digit added to the fingerprint + String fingerprintLongByOneFingerprint = "59050C8155DCA377F23D5A15B77D37134000CDBD8B42FBFBE0E3F38096E68CECE"; + String fingerprintLongByOnePubkey = "308203c5308202ada00302010202047b7cf549300d06092a864886f70d01010b0500308192310b30090603550406130255533111300f060355040813084e657720596f726b3111300f060355040713084e657720596f726b311d301b060355040a131454686520477561726469616e2050726f6a656374311f301d060355040b1316477561726469616e20462d44726f6964204275696c64311d301b06035504031314677561726469616e70726f6a6563742e696e666f301e170d3132313032393130323530305a170d3430303331363130323530305a308192310b30090603550406130255533111300f060355040813084e657720596f726b3111300f060355040713084e657720596f726b311d301b060355040a131454686520477561726469616e2050726f6a656374311f301d060355040b1316477561726469616e20462d44726f6964204275696c64311d301b06035504031314677561726469616e70726f6a6563742e696e666f30820122300d06092a864886f70d01010105000382010f003082010a0282010100b7f1f635fa3fce1a8042aaa960c2dc557e4ad2c082e5787488cba587fd26207cf59507919fc4dcebda5c8c0959d14146d0445593aa6c29dc639570b71712451fd5c231b0c9f5f0bec380503a1c2a3bc00048bc5db682915afa54d1ecf67b45e1e05c0934b3037a33d3a565899131f27a72c03a5de93df17a2376cc3107f03ee9d124c474dfab30d4053e8f39f292e2dcb6cc131bce12a0c5fc307985195d256bf1d7a2703d67c14bf18ed6b772bb847370b20335810e337c064fef7e2795a524c664a853cd46accb8494f865164dabfb698fa8318236432758bc40d52db00d5ce07fe2210dc06cd95298b4f09e6c9b7b7af61c1d62ea43ea36a2331e7b2d4e250203010001a321301f301d0603551d0e0416041404d763e981cf3a295b94a790d8536a783097232b300d06092a864886f70d01010b05000382010100654e6484ff032c54fed1d96d3c8e731302be9dbd7bb4fe635f2dac05b69f3ecbb5acb7c9fe405e2a066567a8f5c2beb8b199b5a4d5bb1b435cf02df026d4fb4edd9d8849078f085b00950083052d57467d65c6eebd98f037cff9b148d621cf8819c4f7dc1459bf8fc5c7d76f901495a7caf35d1e5c106e1d50610c4920c3c1b50adcfbd4ad83ce7353cdea7d856bba0419c224f89a2f3ebc203d20eb6247711ad2b55fd4737936dc42ced7a047cbbd24012079204a2883b6d55d5d5b66d9fd82fb51fca9a5db5fad9af8564cb380ff30ae8263dbbf01b46e01313f53279673daa3f893380285646b244359203e7eecde94ae141b7dfa8e6499bb8e7e0b25ab85"; + + public void testFormatFingerprint() { + String badResult = Utils.formatFingerprint(""); + // real fingerprints + String formatted = null; + formatted = Utils.formatFingerprint(fdroidFingerprint); + assertFalse(formatted.equals(badResult)); + assertTrue(formatted.matches("[A-Z0-9][A-Z0-9] [A-Z0-9 ]+")); + formatted = Utils.formatFingerprint(gpRepoFingerprint); + assertFalse(formatted.equals(badResult)); + assertTrue(formatted.matches("[A-Z0-9][A-Z0-9] [A-Z0-9 ]+")); + formatted = Utils.formatFingerprint(gpTest1Fingerprint); + assertFalse(formatted.equals(badResult)); + assertTrue(formatted.matches("[A-Z0-9][A-Z0-9] [A-Z0-9 ]+")); + // random garbage + assertEquals( + badResult, + Utils.formatFingerprint("234k2lk3jljwlk4j2lk3jlkmqwekljrlkj34lk2jlk2j34lkjl2k3j4lk2j34lja")); + assertEquals( + badResult, + Utils.formatFingerprint("g000000000000000000000000000000000000000000000000000000000000000")); + assertEquals( + badResult, + Utils.formatFingerprint("98273498723948728934789237489273p1928731982731982739182739817238")); + // too short + assertEquals( + badResult, + Utils.formatFingerprint("C63AED1AC79D37C7B0474472AC6EFA6C3AB2B11A767A4F42CF360FA5496E3C5")); + assertEquals( + badResult, + Utils.formatFingerprint("C63AED1")); + assertEquals( + badResult, + Utils.formatFingerprint("f")); + assertEquals( + badResult, + Utils.formatFingerprint("")); + assertEquals( + badResult, + Utils.formatFingerprint(null)); + // real digits but too long + assertEquals( + badResult, + Utils.formatFingerprint("43238D512C1E5EB2D6569F4A3AFBF5523418B82E0A3ED1552770ABB9A9C9CCAB43238D512C1E5EB2D6569F4A3AFBF5523418B82E0A3ED1552770ABB9A9C9CCAB")); + assertEquals( + badResult, + Utils.formatFingerprint("C63AED1AC79D37C7B0474472AC6EFA6C3AB2B11A767A4F42CF360FA5496E3C50F")); + assertEquals( + badResult, + Utils.formatFingerprint("3082035e30820246a00302010202044c49cd00300d06092a864886f70d01010505003071310b300906035504061302554b3110300e06035504081307556e6b6e6f776e3111300f0603550407130857657468657262793110300e060355040a1307556e6b6e6f776e3110300e060355040b1307556e6b6e6f776e311930170603550403131043696172616e2047756c746e69656b73301e170d3130303732333137313032345a170d3337313230383137313032345a3071310b300906035504061302554b3110300e06035504081307556e6b6e6f776e3111300f0603550407130857657468657262793110300e060355040a1307556e6b6e6f776e3110300e060355040b1307556e6b6e6f776e311930170603550403131043696172616e2047756c746e69656b7330820122300d06092a864886f70d01010105000382010f003082010a028201010096d075e47c014e7822c89fd67f795d23203e2a8843f53ba4e6b1bf5f2fd0e225938267cfcae7fbf4fe596346afbaf4070fdb91f66fbcdf2348a3d92430502824f80517b156fab00809bdc8e631bfa9afd42d9045ab5fd6d28d9e140afc1300917b19b7c6c4df4a494cf1f7cb4a63c80d734265d735af9e4f09455f427aa65a53563f87b336ca2c19d244fcbba617ba0b19e56ed34afe0b253ab91e2fdb1271f1b9e3c3232027ed8862a112f0706e234cf236914b939bcf959821ecb2a6c18057e070de3428046d94b175e1d89bd795e535499a091f5bc65a79d539a8d43891ec504058acb28c08393b5718b57600a211e803f4a634e5c57f25b9b8c4422c6fd90203010001300d06092a864886f70d0101050500038201010008e4ef699e9807677ff56753da73efb2390d5ae2c17e4db691d5df7a7b60fc071ae509c5414be7d5da74df2811e83d3668c4a0b1abc84b9fa7d96b4cdf30bba68517ad2a93e233b042972ac0553a4801c9ebe07bf57ebe9a3b3d6d663965260e50f3b8f46db0531761e60340a2bddc3426098397fda54044a17e5244549f9869b460ca5e6e216b6f6a2db0580b480ca2afe6ec6b46eedacfa4aa45038809ece0c5978653d6c85f678e7f5a2156d1bedd8117751e64a4b0dcd140f3040b021821a8d93aed8d01ba36db6c82372211fed714d9a32607038cdfd565bd529ffc637212aaa2c224ef22b603eccefb5bf1e085c191d4b24fe742b17ab3f55d4e6f05ef")); + } + + public void testCalcFingerprintString() { + // these should pass + assertEquals(fdroidFingerprint, Utils.calcFingerprint(fdroidPubkey)); + assertEquals(gpRepoFingerprint, Utils.calcFingerprint(gpRepoPubkey)); + assertEquals(gpTest0Fingerprint, Utils.calcFingerprint(gpTest0Pubkey)); + assertEquals(gpTest1Fingerprint, Utils.calcFingerprint(gpTest1Pubkey)); + + // these should fail + assertFalse(gpRepoFingerprint.equals( + Utils.calcFingerprint(fdroidPubkey))); + assertFalse(gpTest0Fingerprint.equals( + Utils.calcFingerprint(fdroidPubkey))); + assertFalse(gpTest1Fingerprint.equals( + Utils.calcFingerprint(fdroidPubkey))); + assertFalse(fdroidFingerprint.equals( + Utils.calcFingerprint(gpRepoPubkey))); + assertFalse(gpTest0Fingerprint.equals( + Utils.calcFingerprint(gpRepoPubkey))); + assertFalse(gpTest1Fingerprint.equals( + Utils.calcFingerprint(gpRepoPubkey))); + + assertFalse(fingerprintShortByOneFingerprint.equals( + Utils.calcFingerprint(fingerprintShortByOnePubkey))); + assertFalse(fingerprintLongByOneFingerprint.equals( + Utils.calcFingerprint(fingerprintLongByOnePubkey))); + try { + assertFalse(pubkeyShortByOneFingerprint.equals( + Utils.calcFingerprint(pubkeyShortByOnePubkey))); + } catch (ArrayIndexOutOfBoundsException e) { + assertTrue(true); // we should get this Exception! + } + try { + assertFalse(pubkeyLongByOneFingerprint.equals( + Utils.calcFingerprint(pubkeyLongByOnePubkey))); + } catch (ArrayIndexOutOfBoundsException e) { + assertTrue(true); // we should get this Exception! + } + } + + public void testCalcFingerprintCertificate() { + // TODO write tests that work with a Certificate + } +} From 527f649fc28d5c5637c1584372028181550e366e Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Fri, 25 Apr 2014 11:49:43 -0400 Subject: [PATCH 7/7] move getSharingUri() to Utils for easier reuse This method will also be used by the local repo for swapping. --- src/org/fdroid/fdroid/Utils.java | 28 ++++++++++++++++--- .../fdroid/views/RepoDetailsActivity.java | 24 ++-------------- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/org/fdroid/fdroid/Utils.java b/src/org/fdroid/fdroid/Utils.java index 20b64e07a..5131a8e3d 100644 --- a/src/org/fdroid/fdroid/Utils.java +++ b/src/org/fdroid/fdroid/Utils.java @@ -19,23 +19,27 @@ package org.fdroid.fdroid; import android.content.Context; - -import android.content.pm.PackageInfo; +import android.net.Uri; +import android.net.wifi.WifiInfo; +import android.net.wifi.WifiManager; import android.text.TextUtils; import android.util.DisplayMetrics; import android.util.Log; + import com.nostra13.universalimageloader.utils.StorageUtils; +import org.fdroid.fdroid.data.Repo; + import java.io.Closeable; import java.io.File; import java.io.FileReader; -import java.io.InputStream; import java.io.IOException; +import java.io.InputStream; import java.io.OutputStream; +import java.security.MessageDigest; import java.security.cert.Certificate; import java.security.cert.CertificateEncodingException; import java.text.SimpleDateFormat; -import java.security.MessageDigest; import java.util.*; public final class Utils { @@ -188,6 +192,22 @@ public final class Utils { return displayFP; } + public static Uri getSharingUri(Context context, Repo repo) { + Uri uri = Uri.parse(repo.address.replaceFirst("http", "fdroidrepo")); + Uri.Builder b = uri.buildUpon(); + b.appendQueryParameter("fingerprint", repo.fingerprint); + WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); + WifiInfo wifiInfo = wifiManager.getConnectionInfo(); + String ssid = wifiInfo.getSSID().replaceAll("^\"(.*)\"$", "$1"); + String bssid = wifiInfo.getBSSID(); + if (!TextUtils.isEmpty(bssid)) { + b.appendQueryParameter("bssid", Uri.encode(bssid)); + if (!TextUtils.isEmpty(ssid)) + b.appendQueryParameter("ssid", Uri.encode(ssid)); + } + return b.build(); + } + public static File getApkCacheDir(Context context) { File apkCacheDir = new File( StorageUtils.getCacheDirectory(context, true), "apks"); diff --git a/src/org/fdroid/fdroid/views/RepoDetailsActivity.java b/src/org/fdroid/fdroid/views/RepoDetailsActivity.java index 822975b01..f0daf679d 100644 --- a/src/org/fdroid/fdroid/views/RepoDetailsActivity.java +++ b/src/org/fdroid/fdroid/views/RepoDetailsActivity.java @@ -4,8 +4,6 @@ package org.fdroid.fdroid.views; import android.annotation.TargetApi; import android.content.Intent; import android.net.Uri; -import android.net.wifi.WifiInfo; -import android.net.wifi.WifiManager; import android.nfc.NdefMessage; import android.nfc.NdefRecord; import android.nfc.NfcAdapter; @@ -13,12 +11,12 @@ import android.os.Build; import android.os.Bundle; import android.os.Parcelable; import android.support.v4.app.FragmentActivity; -import android.text.TextUtils; import android.util.Log; import android.widget.LinearLayout; import android.widget.Toast; import org.fdroid.fdroid.FDroidApp; +import org.fdroid.fdroid.Utils; import org.fdroid.fdroid.compat.ActionBarCompat; import org.fdroid.fdroid.data.Repo; import org.fdroid.fdroid.data.RepoProvider; @@ -27,7 +25,6 @@ import org.fdroid.fdroid.views.fragments.RepoDetailsFragment; public class RepoDetailsActivity extends FragmentActivity { public static final String TAG = "RepoDetailsActivity"; - private WifiManager wifiManager; private Repo repo; static final String MIME_TYPE = "application/vnd.org.fdroid.fdroid.repo"; @@ -67,8 +64,6 @@ public class RepoDetailsActivity extends FragmentActivity { ActionBarCompat.create(this).setDisplayHomeAsUpEnabled(true); setTitle(repo.getName()); - - wifiManager = (WifiManager) getSystemService(WIFI_SERVICE); } @TargetApi(14) @@ -80,7 +75,7 @@ public class RepoDetailsActivity extends FragmentActivity { return; } nfcAdapter.setNdefPushMessage(new NdefMessage(new NdefRecord[] { - NdefRecord.createUri(getSharingUri()), + NdefRecord.createUri(Utils.getSharingUri(this, repo)), }), this); findViewById(android.R.id.content).post(new Runnable() { @Override @@ -128,19 +123,4 @@ public class RepoDetailsActivity extends FragmentActivity { finish(); } } - - protected Uri getSharingUri() { - Uri uri = Uri.parse(repo.address.replaceFirst("http", "fdroidrepo")); - Uri.Builder b = uri.buildUpon(); - b.appendQueryParameter("fingerprint", repo.fingerprint); - WifiInfo wifiInfo = wifiManager.getConnectionInfo(); - String ssid = wifiInfo.getSSID().replaceAll("^\"(.*)\"$", "$1"); - String bssid = wifiInfo.getBSSID(); - if (!TextUtils.isEmpty(bssid)) { - b.appendQueryParameter("bssid", Uri.encode(bssid)); - if (!TextUtils.isEmpty(ssid)) - b.appendQueryParameter("ssid", Uri.encode(ssid)); - } - return b.build(); - } }