re-add sig field for use in swap v1 compatibility checks
With index-v2, "signer" will be the only field used, since "sig" is deprecated and should be removed. "sig" needs to be kept only for generating the index-v1.jar in nearby/swap repos so that it remains compatible with older clients. This code base should no longer use "sig" for anything besides writing it out to index-v1.json.
This commit is contained in:
parent
e3550171aa
commit
9ab226b3fb
|
@ -1,5 +1,8 @@
|
|||
package org.fdroid.fdroid;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import org.fdroid.fdroid.data.Apk;
|
||||
import org.fdroid.fdroid.data.App;
|
||||
import org.fdroid.index.v2.FileV1;
|
||||
|
@ -9,38 +12,18 @@ import java.io.FileOutputStream;
|
|||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Collections;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
public class TestUtils {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static final String TAG = "TestUtils"; // NOPMD
|
||||
|
||||
/**
|
||||
* This is the F-Droid signature used to sign the AdAway binaries used for the multiRepo.*.jar
|
||||
* repos used by some tests.
|
||||
* These signers are used to sign the Bitcoin Wallet in f-droid.org/repo.
|
||||
*/
|
||||
private static final String FDROID_CERT = "3082033c30820224a00302010202044e9c4ba6300d06092a864886f70d01010505003060310b300906035504061302554b310c300a060355040813034f5247310c300a060355040713034f524731133011060355040a130a6664726f69642e6f7267310f300d060355040b13064644726f6964310f300d060355040313064644726f6964301e170d3131313031373135333731305a170d3339303330343135333731305a3060310b300906035504061302554b310c300a060355040813034f5247310c300a060355040713034f524731133011060355040a130a6664726f69642e6f7267310f300d060355040b13064644726f6964310f300d060355040313064644726f696430820122300d06092a864886f70d01010105000382010f003082010a0282010100981b0aac96f1c66be3c21e773327ee8c4d3b18c75c548243f4cfedbe8ef0d3c6cc1b3b7b094ddd39cdf71d034ef2cd2d1e7bdca458801b04a531cbe7106a3575151375cb32177b017f81cc508f981a1809d0a417c6f3d59ddfa876c3d91874b1d59e08eaf757da13fb82f7e6f7340abc56f0ab672f02e957d446585931388b1affb6f43a16efc7f060df9c8da17c86899b19495114cc5939decd521e172b48e68c6ec03bc58776acd6a52fd61fd839d2a404df25ae79c2ccec2d9a07c9a1751c341e5e9b706b8e713bec2149e16f5ca15a1d6fe67d52ebb210995ee03d9416118fa9434f65ffe6d43dddfe3e2b0c54b94ea8e5a1031ed41856cd369da41dc6790203010001300d06092a864886f70d0101050500038201010080951aa68b5a2c7ac464b66078afd4826df96e2c10b612a441036e43aa923bfa55f26c61b5d94c2132877a3801c2394328f70b322f6308dbea6ed4f0f4897d73d13af9498277f60685239acd8922275544334d295b07245ef0ec924e1c35e8004d8d268d97c957078149cc5635f8977ce432a56278a03664a45a6be51319b0b5f3e27b2372ae859215e3f3d0f5c8b86d1a42f742abe4d224870d419600966e46d83ce41df04e315353f334378f0f994732a6c05d351b1bea66efc62471762d0f752d379966e8293fc5fe4150665427b0f3fb3a1b64c3b75128abadc02c3efa44c06e2d22ba8f1c3f4b782ac2da0d56307173093fde31215d26ab05714a12d696"; // NOCHECKSTYLE LineLength
|
||||
private static final String UPSTREAM_CERT = "308204e1308202c9a0030201020204483450fa300d06092a864886f70d01010b050030213110300e060355040b1307462d44726f6964310d300b06035504031304736f7661301e170d3136303832333133333131365a170d3434303130393133333131365a30213110300e060355040b1307462d44726f6964310d300b06035504031304736f766130820222300d06092a864886f70d01010105000382020f003082020a0282020100dfdcd120f3ab224999dddf4ea33ea588d295e4d7130bef48c143e9d76e5c0e0e9e5d45e64208e35feebc79a83f08939dd6a343b7d1e2179930a105a1249ccd36d88ff3feffc6e4dc53dae0163a7876dd45ecc1ddb0adf5099aa56c1a84b52affcd45d0711ffa4de864f35ac0333ebe61ea8673eeda35a88f6af678cc4d0f80b089338ac8f2a8279a64195c611d19445cab3fd1a020afed9bd739bb95142fb2c00a8f847db5ef3325c814f8eb741bacf86ed3907bfe6e4564d2de5895df0c263824e0b75407589bae2d3a4666c13b92102d8781a8ee9bb4a5a1a78c4a9c21efdaf5584da42e84418b28f5a81d0456a3dc5b420991801e6b21e38c99bbe018a5b2d690894a114bc860d35601416aa4dc52216aff8a288d4775cddf8b72d45fd2f87303a8e9c0d67e442530be28eaf139894337266e0b33d57f949256ab32083bcc545bc18a83c9ab8247c12aea037e2b68dee31c734cb1f04f241d3b94caa3a2b258ffaf8e6eae9fbbe029a934dc0a0859c5f120334812693a1c09352340a39f2a678dbc1afa2a978bfee43afefcb7e224a58af2f3d647e5745db59061236b8af6fcfd93b3602f9e456978534f3a7851e800071bf56da80401c81d91c45f82568373af0576b1cc5eef9b85654124b6319770be3cdba3fbebe3715e8918fb6c8966624f3d0e815effac3d2ee06dd34ab9c693218b2c7c06ba99d6b74d4f17b8c3cb0203010001a321301f301d0603551d0e04160414d62bee9f3798509546acc62eb1de14b08b954d4f300d06092a864886f70d01010b05000382020100743f7c5692085895f9d1fffad390fb4202c15f123ed094df259185960fd6dadf66cb19851070f180297bba4e6996a4434616573b375cfee94fee73a4505a7ec29136b7e6c22e6436290e3686fe4379d4e3140ec6a08e70cfd3ed5b634a5eb5136efaaabf5f38e0432d3d79568a556970b8cfba2972f5d23a3856d8a981b9e9bbbbb88f35e708bde9cbc5f681cbd974085b9da28911296fe2579fa64bbe9fa0b93475a7a8db051080b0c5fade0d1c018e7858cd4cbe95145b0620e2f632cbe0f8af9cbf22e2fdaa72245ae31b0877b07181cc69dd2df74454251d8de58d25e76354abe7eb690f22e59b08795a8f2c98c578e0599503d9085927634072c82c9f82abd50fd12b8fd1a9d1954eb5cc0b4cfb5796b5aaec0356643b4a65a368442d92ef94edd3ac6a2b7fe3571b8cf9f462729228aab023ef9183f73792f5379633ccac51079177d604c6bc1873ada6f07d8da6d68c897e88a5fa5d63fdb8df820f46090e0716e7562dd3c140ba279a65b996f60addb0abe29d4bf2f5abe89480771d492307b926d91f02f341b2148502903c43d40f3c6c86a811d060711f0698b384acdcc0add44eb54e42962d3d041accc715afd49407715adc09350cb55e8d9281a3b0b6b5fcd91726eede9b7c8b13afdebb2c2b377629595f1096ba62fb14946dbac5f3c5f0b4e5b712e7acc7dcf6c46cdc5e6d6dfdeee55a0c92c2d70f080ac6"; // NOCHECKSTYLE LineLength
|
||||
|
||||
public static final String FDROID_SIG;
|
||||
public static final String UPSTREAM_SIG;
|
||||
|
||||
static {
|
||||
// Some code requires the full certificate (e.g. when we mock PackageInfo to give to the
|
||||
// installed app provider), while others requires the hashed certificate (e.g. inserting
|
||||
// into the apk provider directly, without the need to mock anything).
|
||||
try {
|
||||
FDROID_SIG = new Hasher("MD5", FDROID_CERT.getBytes()).getHash();
|
||||
UPSTREAM_SIG = new Hasher("MD5", UPSTREAM_CERT.getBytes()).getHash();
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
public static final String FDROID_SIGNER = "bcb4b93636bb10c7ddaf61aa9604ff795dfdb05fad4d9412b335682f0b612e32";
|
||||
public static final String UPSTREAM_SIGNER = "58dcd8a0edf2a590683ba022d22a8dca5659aabf4728741a5c07af738d53db38";
|
||||
|
||||
public static Apk getApk(int versionCode) {
|
||||
return getApk(versionCode, "signature", null);
|
||||
|
|
|
@ -1,10 +1,19 @@
|
|||
|
||||
package org.fdroid.fdroid;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static vendored.org.apache.commons.codec.digest.MessageDigestAlgorithms.SHA_256;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.text.format.DateUtils;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
|
||||
import org.fdroid.fdroid.views.AppDetailsRecyclerViewAdapter;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
@ -14,15 +23,6 @@ import org.robolectric.RobolectricTestRunner;
|
|||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static vendored.org.apache.commons.codec.digest.MessageDigestAlgorithms.SHA_256;
|
||||
|
||||
/**
|
||||
* @see <a href="https://gitlab.com/fdroid/fdroidclient/-/merge_requests/1089#note_822501322">forced to vendor Apache Commons Codec</a>
|
||||
*/
|
||||
|
|
|
@ -37,10 +37,10 @@ public class SuggestedVersionTest {
|
|||
public void singleRepoSingleSig() {
|
||||
App singleApp = TestUtils.getApp();
|
||||
singleApp.installedVersionCode = 1;
|
||||
singleApp.installedSigner = TestUtils.FDROID_SIG;
|
||||
Apk apk1 = TestUtils.getApk(1, TestUtils.FDROID_SIG, Apk.RELEASE_CHANNEL_STABLE);
|
||||
Apk apk2 = TestUtils.getApk(2, TestUtils.FDROID_SIG, Apk.RELEASE_CHANNEL_STABLE);
|
||||
Apk apk3 = TestUtils.getApk(3, TestUtils.FDROID_SIG, Apk.RELEASE_CHANNEL_BETA);
|
||||
singleApp.installedSigner = TestUtils.FDROID_SIGNER;
|
||||
Apk apk1 = TestUtils.getApk(1, TestUtils.FDROID_SIGNER, Apk.RELEASE_CHANNEL_STABLE);
|
||||
Apk apk2 = TestUtils.getApk(2, TestUtils.FDROID_SIGNER, Apk.RELEASE_CHANNEL_STABLE);
|
||||
Apk apk3 = TestUtils.getApk(3, TestUtils.FDROID_SIGNER, Apk.RELEASE_CHANNEL_BETA);
|
||||
List<Apk> apks = new ArrayList<>();
|
||||
apks.add(apk3);
|
||||
apks.add(apk2);
|
||||
|
@ -57,11 +57,11 @@ public class SuggestedVersionTest {
|
|||
App singleApp = TestUtils.getApp();
|
||||
singleApp.installedVersionCode = 0;
|
||||
|
||||
Apk apk1 = TestUtils.getApk(1, TestUtils.FDROID_SIG, Apk.RELEASE_CHANNEL_STABLE);
|
||||
Apk apk2 = TestUtils.getApk(2, TestUtils.FDROID_SIG, Apk.RELEASE_CHANNEL_STABLE);
|
||||
Apk apk3 = TestUtils.getApk(3, TestUtils.FDROID_SIG, Apk.RELEASE_CHANNEL_STABLE);
|
||||
Apk apk4 = TestUtils.getApk(4, TestUtils.UPSTREAM_SIG, Apk.RELEASE_CHANNEL_STABLE);
|
||||
Apk apk5 = TestUtils.getApk(5, TestUtils.UPSTREAM_SIG, Apk.RELEASE_CHANNEL_BETA);
|
||||
Apk apk1 = TestUtils.getApk(1, TestUtils.FDROID_SIGNER, Apk.RELEASE_CHANNEL_STABLE);
|
||||
Apk apk2 = TestUtils.getApk(2, TestUtils.FDROID_SIGNER, Apk.RELEASE_CHANNEL_STABLE);
|
||||
Apk apk3 = TestUtils.getApk(3, TestUtils.FDROID_SIGNER, Apk.RELEASE_CHANNEL_STABLE);
|
||||
Apk apk4 = TestUtils.getApk(4, TestUtils.UPSTREAM_SIGNER, Apk.RELEASE_CHANNEL_STABLE);
|
||||
Apk apk5 = TestUtils.getApk(5, TestUtils.UPSTREAM_SIGNER, Apk.RELEASE_CHANNEL_BETA);
|
||||
List<Apk> apks = new ArrayList<>();
|
||||
apks.add(apk5);
|
||||
apks.add(apk4);
|
||||
|
@ -75,13 +75,13 @@ public class SuggestedVersionTest {
|
|||
|
||||
// Now install v1 with the f-droid signature. In response, we should only suggest
|
||||
// apps with that sig in the future. That is, version 4 from upstream is not considered.
|
||||
singleApp.installedSigner = TestUtils.FDROID_SIG;
|
||||
singleApp.installedSigner = TestUtils.FDROID_SIGNER;
|
||||
singleApp.installedVersionCode = 1;
|
||||
assertSuggested(singleApp, apks, 3, Apk.RELEASE_CHANNEL_STABLE);
|
||||
|
||||
// This adds the "suggestedVersionCode" version of the app, but signed by f-droid.
|
||||
Apk apk4f = TestUtils.getApk(4, TestUtils.FDROID_SIG, Apk.RELEASE_CHANNEL_STABLE);
|
||||
Apk apk5f = TestUtils.getApk(5, TestUtils.FDROID_SIG, Apk.RELEASE_CHANNEL_BETA);
|
||||
Apk apk4f = TestUtils.getApk(4, TestUtils.FDROID_SIGNER, Apk.RELEASE_CHANNEL_STABLE);
|
||||
Apk apk5f = TestUtils.getApk(5, TestUtils.FDROID_SIGNER, Apk.RELEASE_CHANNEL_BETA);
|
||||
apks.clear();
|
||||
apks.add(apk5);
|
||||
apks.add(apk5f);
|
||||
|
@ -106,10 +106,10 @@ public class SuggestedVersionTest {
|
|||
public void testIncompatibleWithBeta() {
|
||||
App singleApp = TestUtils.getApp();
|
||||
singleApp.installedVersionCode = 1;
|
||||
singleApp.installedSigner = TestUtils.FDROID_SIG;
|
||||
Apk apk1 = TestUtils.getApk(1, TestUtils.FDROID_SIG, Apk.RELEASE_CHANNEL_STABLE);
|
||||
Apk apk2 = TestUtils.getApk(2, TestUtils.FDROID_SIG, Apk.RELEASE_CHANNEL_STABLE);
|
||||
Apk apk3 = TestUtils.getApk(3, TestUtils.FDROID_SIG, Apk.RELEASE_CHANNEL_STABLE);
|
||||
singleApp.installedSigner = TestUtils.FDROID_SIGNER;
|
||||
Apk apk1 = TestUtils.getApk(1, TestUtils.FDROID_SIGNER, Apk.RELEASE_CHANNEL_STABLE);
|
||||
Apk apk2 = TestUtils.getApk(2, TestUtils.FDROID_SIGNER, Apk.RELEASE_CHANNEL_STABLE);
|
||||
Apk apk3 = TestUtils.getApk(3, TestUtils.FDROID_SIGNER, Apk.RELEASE_CHANNEL_STABLE);
|
||||
apk3.compatible = false;
|
||||
List<Apk> apks = new ArrayList<>();
|
||||
apks.add(apk3);
|
||||
|
|
|
@ -25,6 +25,22 @@ public object IndexUtils {
|
|||
return sha256(signerBytes).toHex()
|
||||
}
|
||||
|
||||
/**
|
||||
* 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. It is now here only for backwards compatibility. It should
|
||||
* only ever be used for writing the `sig` value out to
|
||||
* `index-v1.json`.
|
||||
*
|
||||
* @see getPackageSigner
|
||||
* @see org.fdroid.fdroid.Utils.getPackageSigner
|
||||
* @see org.fdroid.fdroid.data.Apk
|
||||
*/
|
||||
@Deprecated("Only here for backwards compatibility when writing out index-v1.json")
|
||||
public fun getsig(signerBytes: ByteArray): String {
|
||||
return md5(signerBytes.toHex().encodeToByteArray()).toHex()
|
||||
}
|
||||
|
||||
internal fun String.decodeHex(): ByteArray {
|
||||
check(length % 2 == 0) { "Must have an even length" }
|
||||
return chunked(2)
|
||||
|
@ -45,4 +61,15 @@ public object IndexUtils {
|
|||
messageDigest.update(bytes)
|
||||
return messageDigest.digest()
|
||||
}
|
||||
|
||||
@Deprecated("Only here for backwards compatibility when writing out index-v1.json")
|
||||
internal fun md5(bytes: ByteArray): ByteArray {
|
||||
val messageDigest: MessageDigest = try {
|
||||
MessageDigest.getInstance("MD5")
|
||||
} catch (e: NoSuchAlgorithmException) {
|
||||
throw AssertionError(e)
|
||||
}
|
||||
messageDigest.update(bytes)
|
||||
return messageDigest.digest()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import kotlinx.serialization.json.encodeToStream
|
|||
import org.fdroid.index.IndexCreator
|
||||
import org.fdroid.index.IndexParser
|
||||
import org.fdroid.index.IndexUtils.getPackageSigner
|
||||
import org.fdroid.index.IndexUtils.getsig
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
|
||||
|
@ -82,6 +83,7 @@ public class IndexV1Creator(
|
|||
val apk = copyApkToRepo(packageInfo)
|
||||
val hash = hashFile(apk)
|
||||
val apkName = apk.name
|
||||
val sig = getsig(packageInfo.signatures[0].toByteArray())
|
||||
val signer = getPackageSigner(packageInfo.signatures[0].toByteArray())
|
||||
return PackageV1(
|
||||
packageName = packageInfo.packageName,
|
||||
|
@ -92,7 +94,7 @@ public class IndexV1Creator(
|
|||
apkName = apkName,
|
||||
hash = hash,
|
||||
hashType = "sha256",
|
||||
sig = signer, // should be some custom MD5/hex thing, but it works without...
|
||||
sig = sig,
|
||||
signer = signer,
|
||||
size = File(packageInfo.applicationInfo.publicSourceDir).length(),
|
||||
minSdkVersion = if (SDK_INT >= 24) packageInfo.applicationInfo.minSdkVersion else null,
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
package org.fdroid.index
|
||||
|
||||
import org.fdroid.index.IndexUtils.getsig
|
||||
import org.fdroid.index.IndexUtils.md5
|
||||
import org.fdroid.index.IndexUtils.toHex
|
||||
import org.junit.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
internal class IndexUtilsTest {
|
||||
/**
|
||||
* Test the replacement for the ancient fingerprint algorithm.
|
||||
*
|
||||
* @see org.fdroid.fdroid.data.Apk.sig
|
||||
* @see org.fdroid.fdroid.Utils.getsig
|
||||
*/
|
||||
@Deprecated("Only here for backwards compatibility when writing out index-v1.json")
|
||||
@Test
|
||||
fun 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 (length in intArrayOf(256, 345, 1233, 4032, 12092)) {
|
||||
val rawCertBytes = ByteArray(length)
|
||||
java.util.Random().nextBytes(rawCertBytes)
|
||||
val fdroidSig = ByteArray(rawCertBytes.size * 2)
|
||||
for (j in rawCertBytes.indices) {
|
||||
val v = rawCertBytes[j].toInt()
|
||||
var d = ((v shr 4) and 0x000F).toByte() // Java: int d = (v >> 4) & 0xF;
|
||||
fdroidSig[j * 2] = (if (d >= 10) 'a'.code + d - 10 else '0'.code + d).toByte()
|
||||
d = (v and 0x000F).toByte() // Java: d = v & 0xF
|
||||
fdroidSig[j * 2 + 1] = (if (d >= 10) 'a'.code + d - 10 else '0'.code + d).toByte()
|
||||
}
|
||||
val sig = md5(fdroidSig).toHex()
|
||||
assertEquals(sig, getsig(rawCertBytes))
|
||||
assertEquals(sig, md5(rawCertBytes.toHex().toByteArray()).toHex())
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue