From 7c34dd96f4b94b02bfb683e00827dfaa5ee7a149 Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Mon, 3 Apr 2017 09:23:06 -0300 Subject: [PATCH] Reduce code duplication by re-using methods for extracting and verifying certificate --- fdroidserver/common.py | 24 ++++++++++++++++++++++++ fdroidserver/index.py | 20 +------------------- fdroidserver/update.py | 28 +++------------------------- 3 files changed, 28 insertions(+), 44 deletions(-) diff --git a/fdroidserver/common.py b/fdroidserver/common.py index 85bd48e1..42918ed7 100644 --- a/fdroidserver/common.py +++ b/fdroidserver/common.py @@ -42,6 +42,10 @@ from distutils.version import LooseVersion from queue import Queue from zipfile import ZipFile +from pyasn1.codec.der import decoder, encoder +from pyasn1_modules import rfc2315 +from pyasn1.error import PyAsn1Error + import fdroidserver.metadata from .asynchronousfilereader import AsynchronousFileReader @@ -2221,6 +2225,26 @@ def get_cert_fingerprint(pubkey): return " ".join(ret) +def get_certificate(certificate_file): + """ + Extracts a certificate from the given file. + :param certificate_file: file bytes (as string) representing the certificate + :return: A binary representation of the certificate's public key, or None in case of error + """ + content = decoder.decode(certificate_file, asn1Spec=rfc2315.ContentInfo())[0] + if content.getComponentByName('contentType') != rfc2315.signedData: + return None + content = decoder.decode(content.getComponentByName('content'), + asn1Spec=rfc2315.SignedData())[0] + try: + certificates = content.getComponentByName('certificates') + cert = certificates[0].getComponentByName('certificate') + except PyAsn1Error: + logging.error("Certificates not found.") + return None + return encoder.encode(cert) + + def write_to_config(thisconfig, key, value=None, config_file=None): '''write a key/value to the local config.py diff --git a/fdroidserver/index.py b/fdroidserver/index.py index 4cf9d4b7..1421acb8 100644 --- a/fdroidserver/index.py +++ b/fdroidserver/index.py @@ -36,8 +36,6 @@ from datetime import datetime from xml.dom.minidom import Document import requests -from pyasn1.codec.der import decoder, encoder -from pyasn1_modules import rfc2315 from fdroidserver import metadata, signindex, common from fdroidserver.common import FDroidPopen, FDroidPopenBytes @@ -621,23 +619,7 @@ def get_public_key_from_jar(jar): raise VerificationException("Found multiple signing certificates for repository.") # extract public key from certificate - public_key = get_public_key_from_certificate(jar.read(certs[0])) + public_key = common.get_certificate(jar.read(certs[0])) public_key_fingerprint = common.get_cert_fingerprint(public_key).replace(' ', '') return public_key, public_key_fingerprint - - -def get_public_key_from_certificate(certificate_file): - """ - Extracts a public key from the given certificate. - :param certificate_file: file bytes (as string) representing the certificate - :return: A binary representation of the certificate's public key - """ - content = decoder.decode(certificate_file, asn1Spec=rfc2315.ContentInfo())[0] - if content.getComponentByName('contentType') != rfc2315.signedData: - raise VerificationException("Unexpected certificate format.") - content = decoder.decode(content.getComponentByName('content'), - asn1Spec=rfc2315.SignedData())[0] - certificates = content.getComponentByName('certificates') - cert = certificates[0].getComponentByName('certificate') - return encoder.encode(cert) diff --git a/fdroidserver/update.py b/fdroidserver/update.py index 94f181dd..92075d8c 100644 --- a/fdroidserver/update.py +++ b/fdroidserver/update.py @@ -34,9 +34,6 @@ from datetime import datetime, timedelta from argparse import ArgumentParser import collections -from pyasn1.error import PyAsn1Error -from pyasn1.codec.der import decoder, encoder -from pyasn1_modules import rfc2315 from binascii import hexlify from PIL import Image @@ -45,7 +42,7 @@ import logging from . import common from . import index from . import metadata -from .common import FDroidPopen, SdkToolsPopen +from .common import SdkToolsPopen METADATA_VERSION = 18 @@ -389,17 +386,11 @@ def getsig(apkpath): if an error occurred. """ - cert = None - # verify the jar signature is correct - args = [config['jarsigner'], '-verify', apkpath] - p = FDroidPopen(args) - if p.returncode != 0: - logging.critical(apkpath + " has a bad signature!") + if not common.verify_apk_signature(apkpath): return None with zipfile.ZipFile(apkpath, 'r') as apk: - certs = [n for n in apk.namelist() if common.CERT_PATH_REGEX.match(n)] if len(certs) < 1: @@ -411,20 +402,7 @@ def getsig(apkpath): 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:] + cert_encoded = common.get_certificate(cert) return hashlib.md5(hexlify(cert_encoded)).hexdigest()