From d96f5ff660969e9c0e68eeb06f9f6a71ae6c57ba Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Wed, 30 Jan 2019 16:48:35 +0100 Subject: [PATCH] support APK Signature V2 when apksigner is installed This was done with much help from @uniqx. This is the first level of supporting APK Signatures v1, v2, and v3. This is enough to include APKs with any combo of v1/v2/v3 signatures. For this to work at all, apksigner and androguard 3.3.3+ must be installed. closes #399 --- fdroidserver/common.py | 43 ++++++++++++++++++-------- fdroidserver/update.py | 31 +++++++++---------- tests/common.TestCase | 1 + tests/repo/v1.v2.sig_1020.apk | Bin 0 -> 13493 bytes tests/update.TestCase | 56 ++++++++++++++++++++++++++++++++-- tests/v2.only.sig_2.apk | Bin 0 -> 12086 bytes 6 files changed, 98 insertions(+), 33 deletions(-) create mode 100644 tests/repo/v1.v2.sig_1020.apk create mode 100644 tests/v2.only.sig_2.apk diff --git a/fdroidserver/common.py b/fdroidserver/common.py index 701a9e03..c362049e 100644 --- a/fdroidserver/common.py +++ b/fdroidserver/common.py @@ -2516,28 +2516,45 @@ def signer_fingerprint(cert_encoded): return hashlib.sha256(cert_encoded).hexdigest() +def get_first_signer_certificate(apkpath): + """Get the first signing certificate from the APK, DER-encoded""" + certs = None + cert_encoded = None + with zipfile.ZipFile(apkpath, 'r') as apk: + cert_files = [n for n in apk.namelist() if SIGNATURE_BLOCK_FILE_REGEX.match(n)] + if len(cert_files) > 1: + logging.error(_("Found multiple JAR Signature Block Files in {path}").format(path=apkpath)) + return None + elif len(cert_files) == 1: + cert_encoded = get_certificate(apk.read(cert_files[0])) + + if cert_encoded is None: + apkobject = _get_androguard_APK(apkpath) + certs = apkobject.get_certificates_der_v2() + if len(certs) > 0: + logging.info(_('Using APK v2 Signature')) + cert_encoded = certs[0] + + if not cert_encoded: + logging.error(_("No signing certificates found in {path}").format(path=apkpath)) + return None + return cert_encoded + + def apk_signer_fingerprint(apk_path): """Obtain sha256 signing-key fingerprint for APK. Extracts hexadecimal sha256 signing-key fingerprint string for a given APK. - :param apkpath: path to APK + :param apk_path: path to APK :returns: signature fingerprint """ - with zipfile.ZipFile(apk_path, 'r') as apk: - certs = [n for n in apk.namelist() if SIGNATURE_BLOCK_FILE_REGEX.match(n)] - - if len(certs) < 1: - logging.error("Found no signing certificates on %s" % apk_path) - return None - if len(certs) > 1: - logging.error("Found multiple signing certificates on %s" % apk_path) - return None - - cert_encoded = get_certificate(apk.read(certs[0])) - return signer_fingerprint(cert_encoded) + cert_encoded = get_first_signer_certificate(apk_path) + if not cert_encoded: + return None + return signer_fingerprint(cert_encoded) def apk_signer_fingerprint_short(apk_path): diff --git a/fdroidserver/update.py b/fdroidserver/update.py index 42be7e5c..a0f9a5f2 100644 --- a/fdroidserver/update.py +++ b/fdroidserver/update.py @@ -414,29 +414,26 @@ def resize_all_icons(repodirs): def getsig(apkpath): - """ Get the signing certificate of an apk. To get the same md5 has that - Android gets, we encode the .RSA certificate in a specific format and pass - it hex-encoded to the md5 digest algorithm. + """Get the unique ID for the signing certificate of an APK. + + This uses a strange algorithm that was devised at the very + beginning of F-Droid. Since it is only used for checking + signature compatibility, it does not matter much that it uses MD5. + + To get the same MD5 has that fdroidclient gets, we encode the .RSA + certificate in a specific format and pass it hex-encoded to the + md5 digest algorithm. This is not the same as the standard X.509 + certificate fingerprint. :param apkpath: path to the apk :returns: A string containing the md5 of the signature of the apk or None if an error occurred. + """ - with zipfile.ZipFile(apkpath, 'r') as apk: - certs = [n for n in apk.namelist() if common.SIGNATURE_BLOCK_FILE_REGEX.match(n)] - - if len(certs) < 1: - logging.error(_("No signing certificates found in {path}").format(path=apkpath)) - return None - if len(certs) > 1: - logging.error(_("Found multiple signing certificates in {path}").format(path=apkpath)) - return None - - cert = apk.read(certs[0]) - - cert_encoded = common.get_certificate(cert) - + cert_encoded = common.get_first_signer_certificate(apkpath) + if not cert_encoded: + return None return hashlib.md5(hexlify(cert_encoded)).hexdigest() # nosec just used as ID for signing key diff --git a/tests/common.TestCase b/tests/common.TestCase index 6c5ba647..b976376f 100755 --- a/tests/common.TestCase +++ b/tests/common.TestCase @@ -161,6 +161,7 @@ class CommonTest(unittest.TestCase): testfiles = [] testfiles.append(os.path.join(self.basedir, 'urzip-release.apk')) testfiles.append(os.path.join(self.basedir, 'urzip-release-unsigned.apk')) + testfiles.append(os.path.join(self.basedir, 'v2.only.sig_2.apk')) for apkfile in testfiles: debuggable = fdroidserver.common.is_apk_and_debuggable(apkfile) self.assertFalse(debuggable, diff --git a/tests/repo/v1.v2.sig_1020.apk b/tests/repo/v1.v2.sig_1020.apk new file mode 100644 index 0000000000000000000000000000000000000000..006ff76473c2fddef98660cbef680a05e4db62da GIT binary patch literal 13493 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 Ct>tn6 literal 0 HcmV?d00001 diff --git a/tests/update.TestCase b/tests/update.TestCase index 80870e02..2ba207b8 100755 --- a/tests/update.TestCase +++ b/tests/update.TestCase @@ -14,6 +14,7 @@ import sys import tempfile import unittest import yaml +import zipfile from binascii import unhexlify from distutils.version import LooseVersion @@ -233,6 +234,45 @@ class UpdateTest(unittest.TestCase): self.assertEqual(sig, 'e0ecb5fc2d63088e4a07ae410a127722', "python sig should be: " + str(sig)) + def test_getsig(self): + # config needed to use jarsigner and keytool + config = dict() + fdroidserver.common.fill_config_defaults(config) + fdroidserver.update.config = config + + sig = fdroidserver.update.getsig('urzip-release-unsigned.apk') + self.assertIsNone(sig) + + good_fingerprint = 'b4964fd759edaa54e65bb476d0276880' + + apkpath = 'urzip-release.apk' # v1 only + sig = fdroidserver.update.getsig(apkpath) + self.assertEqual(good_fingerprint, sig, + 'python sig was: ' + str(sig)) + + apkpath = 'repo/v1.v2.sig_1020.apk' + sig = fdroidserver.update.getsig(apkpath) + self.assertEqual(good_fingerprint, sig, + 'python sig was: ' + str(sig)) + # check that v1 and v2 have the same certificate + import hashlib + from binascii import hexlify + from androguard.core.bytecodes.apk import APK + apkobject = APK(apkpath) + cert_encoded = apkobject.get_certificates_der_v2()[0] + self.assertEqual(good_fingerprint, sig, + hashlib.md5(hexlify(cert_encoded)).hexdigest()) # nosec just used as ID for signing key + + filename = 'v2.only.sig_2.apk' + with zipfile.ZipFile(filename) as z: + self.assertTrue('META-INF/MANIFEST.MF' in z.namelist(), 'META-INF/MANIFEST.MF required') + for f in z.namelist(): + # ensure there are no v1 signature files + self.assertIsNone(fdroidserver.common.SIGNATURE_BLOCK_FILE_REGEX.match(f)) + sig = fdroidserver.update.getsig(filename) + self.assertEqual(good_fingerprint, sig, + "python sig was: " + str(sig)) + def testScanApksAndObbs(self): os.chdir(os.path.join(localmodule, 'tests')) if os.path.basename(os.getcwd()) != 'tests': @@ -254,7 +294,7 @@ class UpdateTest(unittest.TestCase): apps = fdroidserver.metadata.read_metadata(xref=True) knownapks = fdroidserver.common.KnownApks() apks, cachechanged = fdroidserver.update.process_apks({}, 'repo', knownapks, False) - self.assertEqual(len(apks), 16) + self.assertEqual(len(apks), 17) apk = apks[1] self.assertEqual(apk['packageName'], 'com.politedroid') self.assertEqual(apk['versionCode'], 3) @@ -321,7 +361,7 @@ class UpdateTest(unittest.TestCase): fdroidserver.update.options.clean = False read_from_json = fdroidserver.update.get_cache() - self.assertEqual(18, len(read_from_json)) + self.assertEqual(19, len(read_from_json)) for f in glob.glob('repo/*.apk'): self.assertTrue(os.path.basename(f) in read_from_json) @@ -363,6 +403,16 @@ class UpdateTest(unittest.TestCase): else: continue + apk_info = fdroidserver.update.scan_apk('repo/v1.v2.sig_1020.apk') + self.assertIsNone(apk_info.get('maxSdkVersion')) + self.assertEqual(apk_info.get('versionName'), 'v1+2') + self.assertEqual(apk_info.get('versionCode'), 1020) + + apk_info = fdroidserver.update.scan_apk('v2.only.sig_2.apk') + self.assertIsNone(apk_info.get('maxSdkVersion')) + self.assertEqual(apk_info.get('versionName'), 'v2-only') + self.assertEqual(apk_info.get('versionCode'), 2) + apk_info = fdroidserver.update.scan_apk('repo/souch.smsbypass_9.apk') self.assertIsNone(apk_info.get('maxSdkVersion')) self.assertEqual(apk_info.get('versionName'), '0.9') @@ -623,7 +673,7 @@ class UpdateTest(unittest.TestCase): knownapks = fdroidserver.common.KnownApks() apks, cachechanged = fdroidserver.update.process_apks({}, 'repo', knownapks, False) fdroidserver.update.translate_per_build_anti_features(apps, apks) - self.assertEqual(len(apks), 16) + self.assertEqual(len(apks), 17) foundtest = False for apk in apks: if apk['packageName'] == 'com.politedroid' and apk['versionCode'] == 3: diff --git a/tests/v2.only.sig_2.apk b/tests/v2.only.sig_2.apk new file mode 100644 index 0000000000000000000000000000000000000000..0b1804d318ee5a6e2c3236f9c6ea1ef274aae6ff GIT binary patch literal 12086 zcmeHtWmH^Cv*_S%!JVK10?gp<1a}A!Tn2{$1{vHTf#5-d1PBCzI|PDDfFQwxTL=&c z?skXdoKMcX_r4$Zt#yCAQ)|^!SM_vt@9NrBd)ICaWh7((9MJE70I2pA3)OJG{Q&?# z2Stvf_)mn7mw=6>1@=FIYZt?G;S7`d$%v*=CP1Vzix z9v#f(Dqr1Qr3tl-lJ5M7E@LN6!+H;FmGRoFOu=NX%PSh{RsjAUcB6Y*R$bP4 z;?WNd%WCYn@WOkiL$G8$T>j=Kb@cd1xE#$D`2fY`&m)f?t)CBHPPy(rj6$7tsD!w+ ziTyky$}SO|LoL6tk=h-_^yQ?UyYlC7(1{m1XNe3%WIbixac~=uy?CX0(HcTF;VAF@ zaYDHCXuxqTn?FsP&+1ssFQx9N5}_n2A5eTGEeDlkGAhY76wN0yY+;D*d&_ct>#}(~ z)|W|1#e�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