chiark / gitweb /
Replace getsig.java with a pure python implementation
authorDaniel Martí <mvdan@mvdan.cc>
Sat, 30 Aug 2014 02:53:55 +0000 (22:53 -0400)
committerCiaran Gultnieks <ciaran@ciarang.com>
Fri, 7 Nov 2014 09:20:14 +0000 (09:20 +0000)
Special thanks to deki for helping out with the certificate encodings:
https://gitlab.com/snippets/1842

fixes #5 https://gitlab.com/fdroid/fdroidserver/issues/5

MANIFEST.in
fdroidserver/update.py
jenkins-build
setup.py
tests/run-tests

index 29dd42e47f386c4153b8c658e2d29a11dd5ae0b8..468d24ee74f0c9325bd9316d6abaab6288af2470 100644 (file)
@@ -24,9 +24,6 @@ include examples/config.py
 include examples/fdroid-icon.png
 include examples/makebs.config.py
 include examples/opensc-fdroid.cfg
-include fdroidserver/getsig/run.sh
-include fdroidserver/getsig/make.sh
-include fdroidserver/getsig/getsig.java
 include tests/run-tests
 include tests/urzip.apk
 include wp-fdroid/AndroidManifest.xml
index e161764d78479ff9e2f5bab11c262eb47f5fe5cd..c618fc787ed5c693808e04e050defd24c9b2b2f5 100644 (file)
@@ -29,6 +29,11 @@ import pickle
 from xml.dom.minidom import Document
 from optparse import OptionParser
 import time
+from pyasn1.error import PyAsn1Error
+from pyasn1.codec.der import decoder, encoder
+from pyasn1_modules import rfc2315
+from hashlib import md5
+
 from PIL import Image
 import logging
 
@@ -322,6 +327,52 @@ def resize_all_icons(repodirs):
                 resize_icon(iconpath, density)
 
 
+cert_path_regex = re.compile(r'^META-INF/.*\.RSA$')
+
+
+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.
+
+    :param apkpath: path to the apk
+    :returns: A string containing the md5 of the signature of the apk or None
+              if an error occurred.
+    """
+
+    cert = None
+
+    with zipfile.ZipFile(apkpath, 'r') as apk:
+
+        certs = [n for n in apk.namelist() if cert_path_regex.match(n)]
+
+        if len(certs) < 1:
+            logging.error("Found no signing certificates on %s" % apkpath)
+            return None
+        if len(certs) > 1:
+            logging.error("Found multiple signing certificates on %s" % apkpath)
+            return None
+
+        cert = apk.read(certs[0])
+
+    content = decoder.decode(cert, asn1Spec=rfc2315.ContentInfo())[0]
+    if content.getComponentByName('contentType') != rfc2315.signedData:
+        logging.error("Unexpected format.")
+        return None
+
+    content = decoder.decode(content.getComponentByName('content'),
+                             asn1Spec=rfc2315.SignedData())[0]
+    try:
+        certificates = content.getComponentByName('certificates')
+    except PyAsn1Error:
+        logging.error("Certificates not found.")
+        return None
+
+    cert_encoded = encoder.encode(certificates)[4:]
+
+    return md5(cert_encoded.encode('hex')).hexdigest()
+
+
 def scan_apks(apps, apkcache, repodir, knownapks):
     """Scan the apks in the given repo directory.
 
@@ -476,18 +527,8 @@ def scan_apks(apps, apkcache, repodir, knownapks):
                 sys.exit(1)
 
             # Get the signature (or md5 of, to be precise)...
-            getsig_dir = os.path.join(os.path.dirname(__file__), 'getsig')
-            if not os.path.exists(getsig_dir + "/getsig.class"):
-                logging.critical("getsig.class not found. To fix: cd '%s' && ./make.sh" % getsig_dir)
-                sys.exit(1)
-            p = FDroidPopen(['java', '-cp', os.path.join(os.path.dirname(__file__), 'getsig'),
-                             'getsig', os.path.join(os.getcwd(), apkfile)])
-            thisinfo['sig'] = None
-            for line in p.output.splitlines():
-                if line.startswith('Result:'):
-                    thisinfo['sig'] = line[7:].strip()
-                    break
-            if p.returncode != 0 or not thisinfo['sig']:
+            thisinfo['sig'] = getsig(os.path.join(os.getcwd(), apkfile))
+            if not thisinfo['sig']:
                 logging.critical("Failed to get apk signature")
                 sys.exit(1)
 
index 4069d820d19df8c5251cf6ba3b1a66510f466e9e..6e50b663e420bd9e362035c40ce2e2ab077dc3a8 100755 (executable)
@@ -38,11 +38,6 @@ fi
 
 export PATH=/usr/lib/jvm/java-7-openjdk-amd64/bin:$PATH
 
-#------------------------------------------------------------------------------#
-# run local build
-cd $WORKSPACE/fdroidserver/getsig
-./make.sh
-
 
 #------------------------------------------------------------------------------#
 # run local tests
index 82936c398a1c3c899369e295fa0257bb8c28aadd..669cfaee60174696d59d2485f29cb4ab9aa7810d 100644 (file)
--- a/setup.py
+++ b/setup.py
@@ -1,14 +1,8 @@
 #!/usr/bin/env python2
 
 from setuptools import setup
-import os
-import subprocess
 import sys
 
-if not os.path.exists('fdroidserver/getsig/getsig.class'):
-    subprocess.check_output('cd fdroidserver/getsig && javac getsig.java',
-                            shell=True)
-
 setup(name='fdroidserver',
       version='0.2.1',
       description='F-Droid Server Tools',
@@ -25,8 +19,6 @@ setup(name='fdroidserver',
                   'examples/makebs.config.py',
                   'examples/opensc-fdroid.cfg',
                   'examples/fdroid-icon.png']),
-          ('fdroidserver/getsig',
-              ['fdroidserver/getsig/getsig.class']),
           ],
       install_requires=[
           'mwclient',
@@ -34,6 +26,8 @@ setup(name='fdroidserver',
           'Pillow',
           'python-magic',
           'apache-libcloud >= 0.14.1',
+          'pyasn1',
+          'pyasn1-modules',
           ],
       classifiers=[
           'Development Status :: 3 - Alpha',
index 90bfb873988f3e924a6916fa5dfd4397f0ea98d1..1f0e7709e7355f770f496577fb7678403d44e527 100755 (executable)
@@ -101,8 +101,6 @@ $python setup.py sdist
 REPOROOT=`create_test_dir`
 cd $REPOROOT
 tar xzf `ls -1 $WORKSPACE/dist/fdroidserver-*.tar.gz | sort -n | tail -1`
-cd $REPOROOT/fdroidserver-*/fdroidserver/getsig
-./make.sh
 cd $REPOROOT
 ./fdroidserver-*/fdroid init
 copy_apks_into_repo $REPOROOT