always use fingerprint hashes in lowercase
* Utils.getBinaryHash() converts it to lowercase() * Utils.getPackageSig() outputs lowercase * fdroidserver outputs lowercase for all hash entries
This commit is contained in:
parent
018e3221a7
commit
2975d4c09f
@ -33,10 +33,6 @@ import android.os.Build;
|
|||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.os.StatFs;
|
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.Editable;
|
||||||
import android.text.Html;
|
import android.text.Html;
|
||||||
import android.text.SpannableStringBuilder;
|
import android.text.SpannableStringBuilder;
|
||||||
@ -52,6 +48,10 @@ import android.view.View;
|
|||||||
import android.view.ViewTreeObserver;
|
import android.view.ViewTreeObserver;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.Toast;
|
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.DisplayImageOptions;
|
||||||
import com.nostra13.universalimageloader.core.ImageLoader;
|
import com.nostra13.universalimageloader.core.ImageLoader;
|
||||||
import com.nostra13.universalimageloader.core.assist.ImageScaleType;
|
import com.nostra13.universalimageloader.core.assist.ImageScaleType;
|
||||||
@ -406,10 +406,26 @@ public final class Utils {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the fingerprint used to represent an APK signing key in F-Droid.
|
* Get the fingerprint used to represent an APK signing key in F-Droid.
|
||||||
* This is a custom fingerprint algorithm that was kind of accidentally
|
* This is a custom fingerprint algorithm that was kind of accidentally
|
||||||
* created, but is still in use.
|
* 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) {
|
public static String getPackageSig(PackageInfo info) {
|
||||||
if (info == null || info.signatures == null || info.signatures.length < 1) {
|
if (info == null || info.signatures == null || info.signatures.length < 1) {
|
||||||
@ -556,7 +572,7 @@ public final class Utils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
byte[] mdbytes = md.digest();
|
byte[] mdbytes = md.digest();
|
||||||
return toHexString(mdbytes).toLowerCase(Locale.ENGLISH);
|
return toHexString(mdbytes);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
String message = e.getMessage();
|
String message = e.getMessage();
|
||||||
if (message.contains("read failed: EIO (I/O error)")) {
|
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.
|
* Computes the base 16 representation of the byte array argument.
|
||||||
*
|
*
|
||||||
* @param bytes an array of bytes.
|
* @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 <a href="https://stackoverflow.com/a/9855338">source</a>
|
* @see <a href="https://stackoverflow.com/a/9855338">source</a>
|
||||||
*/
|
*/
|
||||||
public static String toHexString(byte[] bytes) {
|
public static String toHexString(byte[] bytes) {
|
||||||
@ -589,7 +605,7 @@ public final class Utils {
|
|||||||
return new String(hexChars);
|
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) {
|
public static int parseInt(String str, int fallback) {
|
||||||
if (str == null || str.length() == 0) {
|
if (str == null || str.length() == 0) {
|
||||||
|
@ -949,24 +949,7 @@ public class App extends ValueObject implements Comparable<App>, Parcelable {
|
|||||||
}
|
}
|
||||||
apkJar.close();
|
apkJar.close();
|
||||||
|
|
||||||
/*
|
apk.sig = Utils.getsig(rawCertBytes);
|
||||||
* 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");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2,9 +2,9 @@
|
|||||||
package org.fdroid.fdroid;
|
package org.fdroid.fdroid;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.pm.PackageInfo;
|
||||||
|
import android.content.pm.Signature;
|
||||||
import androidx.test.core.app.ApplicationProvider;
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
|
|
||||||
import org.fdroid.fdroid.views.AppDetailsRecyclerViewAdapter;
|
import org.fdroid.fdroid.views.AppDetailsRecyclerViewAdapter;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
@ -12,6 +12,7 @@ import org.robolectric.RobolectricTestRunner;
|
|||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.Random;
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user