diff --git a/app/src/main/java/org/fdroid/fdroid/Utils.java b/app/src/main/java/org/fdroid/fdroid/Utils.java index b9b101d48..34623bd1c 100644 --- a/app/src/main/java/org/fdroid/fdroid/Utils.java +++ b/app/src/main/java/org/fdroid/fdroid/Utils.java @@ -33,10 +33,6 @@ import android.os.Build; import android.os.Handler; import android.os.Looper; import android.os.StatFs; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.RequiresApi; -import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; import android.text.Editable; import android.text.Html; import android.text.SpannableStringBuilder; @@ -52,6 +48,10 @@ import android.view.View; import android.view.ViewTreeObserver; import android.widget.ImageView; import android.widget.Toast; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.RequiresApi; +import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; import com.nostra13.universalimageloader.core.DisplayImageOptions; import com.nostra13.universalimageloader.core.ImageLoader; import com.nostra13.universalimageloader.core.assist.ImageScaleType; @@ -406,10 +406,26 @@ public final class Utils { return ret; } + /** * Get the fingerprint used to represent an APK signing key in F-Droid. * This is a custom fingerprint algorithm that was kind of accidentally * created, but is still in use. + * + * @see #getPackageSig(PackageInfo) + * @see org.fdroid.fdroid.data.Apk#sig + */ + public static String getsig(byte[] rawCertBytes) { + return Utils.hashBytes(toHexString(rawCertBytes).getBytes(), "md5"); + } + + /** + * Get the fingerprint used to represent an APK signing key in F-Droid. + * This is a custom fingerprint algorithm that was kind of accidentally + * created, but is still in use. + * + * @see #getsig(byte[]) + * @see org.fdroid.fdroid.data.Apk#sig */ public static String getPackageSig(PackageInfo info) { if (info == null || info.signatures == null || info.signatures.length < 1) { @@ -556,7 +572,7 @@ public final class Utils { } byte[] mdbytes = md.digest(); - return toHexString(mdbytes).toLowerCase(Locale.ENGLISH); + return toHexString(mdbytes); } catch (IOException e) { String message = e.getMessage(); if (message.contains("read failed: EIO (I/O error)")) { @@ -576,7 +592,7 @@ public final class Utils { * Computes the base 16 representation of the byte array argument. * * @param bytes an array of bytes. - * @return the bytes represented as a string of hexadecimal digits. + * @return the bytes represented as a string of lowercase hexadecimal digits. * @see source */ public static String toHexString(byte[] bytes) { @@ -589,7 +605,7 @@ public final class Utils { return new String(hexChars); } - private static final char[] HEX_LOOKUP_ARRAY = "0123456789ABCDEF".toCharArray(); + private static final char[] HEX_LOOKUP_ARRAY = "0123456789abcdef".toCharArray(); public static int parseInt(String str, int fallback) { if (str == null || str.length() == 0) { diff --git a/app/src/main/java/org/fdroid/fdroid/data/App.java b/app/src/main/java/org/fdroid/fdroid/data/App.java index 9e69bd899..3ba04b38f 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/App.java +++ b/app/src/main/java/org/fdroid/fdroid/data/App.java @@ -949,24 +949,7 @@ public class App extends ValueObject implements Comparable, Parcelable { } apkJar.close(); - /* - * I don't fully understand the loop used here. I've copied it verbatim - * from getsig.java bundled with FDroidServer. I *believe* it is taking - * the raw byte encoding of the certificate & converting it to a byte - * array of the hex representation of the original certificate byte - * array. This is then MD5 sum'd. It's a really bad way to be doing this - * if I'm right... If I'm not right, I really don't know! see lines - * 67->75 in getsig.java bundled with Fdroidserver - */ - final byte[] fdroidSig = new byte[rawCertBytes.length * 2]; - for (int j = 0; j < rawCertBytes.length; j++) { - byte v = rawCertBytes[j]; - int d = (v >> 4) & 0xF; - fdroidSig[j * 2] = (byte) (d >= 10 ? ('a' + d - 10) : ('0' + d)); - d = v & 0xF; - fdroidSig[j * 2 + 1] = (byte) (d >= 10 ? ('a' + d - 10) : ('0' + d)); - } - apk.sig = Utils.hashBytes(fdroidSig, "md5"); + apk.sig = Utils.getsig(rawCertBytes); } /** diff --git a/app/src/test/java/org/fdroid/fdroid/UtilsTest.java b/app/src/test/java/org/fdroid/fdroid/UtilsTest.java index 1c6453d63..518d63dba 100644 --- a/app/src/test/java/org/fdroid/fdroid/UtilsTest.java +++ b/app/src/test/java/org/fdroid/fdroid/UtilsTest.java @@ -2,9 +2,9 @@ package org.fdroid.fdroid; import android.content.Context; - +import android.content.pm.PackageInfo; +import android.content.pm.Signature; import androidx.test.core.app.ApplicationProvider; - import org.fdroid.fdroid.views.AppDetailsRecyclerViewAdapter; import org.junit.Test; import org.junit.runner.RunWith; @@ -12,6 +12,7 @@ import org.robolectric.RobolectricTestRunner; import java.io.File; import java.util.Date; +import java.util.Random; import java.util.TimeZone; import static org.junit.Assert.assertEquals; @@ -215,4 +216,40 @@ public class UtilsTest { } } } + + /** + * Test the replacement for the ancient fingerprint algorithm. + * + * @see org.fdroid.fdroid.data.Apk#sig + */ + @Test + public void testGetsig() { + /* + * I don't fully understand the loop used here. I've copied it verbatim + * from getsig.java bundled with FDroidServer. I *believe* it is taking + * the raw byte encoding of the certificate & converting it to a byte + * array of the hex representation of the original certificate byte + * array. This is then MD5 sum'd. It's a really bad way to be doing this + * if I'm right... If I'm not right, I really don't know! see lines + * 67->75 in getsig.java bundled with Fdroidserver + */ + for (int length : new int[]{256, 345, 1233, 4032, 12092}) { + byte[] rawCertBytes = new byte[length]; + new Random().nextBytes(rawCertBytes); + final byte[] fdroidSig = new byte[rawCertBytes.length * 2]; + for (int j = 0; j < rawCertBytes.length; j++) { + byte v = rawCertBytes[j]; + int d = (v >> 4) & 0xF; + fdroidSig[j * 2] = (byte) (d >= 10 ? ('a' + d - 10) : ('0' + d)); + d = v & 0xF; + fdroidSig[j * 2 + 1] = (byte) (d >= 10 ? ('a' + d - 10) : ('0' + d)); + } + String sig = Utils.hashBytes(fdroidSig, "md5"); + assertEquals(sig, Utils.getsig(rawCertBytes)); + + PackageInfo packageInfo = new PackageInfo(); + packageInfo.signatures = new Signature[]{new Signature(rawCertBytes)}; + assertEquals(sig, Utils.getPackageSig(packageInfo)); + } + } }