Signed-off-by: Denis 'GNUtoo' Carikli <[email protected]> --- .../gen_key_migration_script/extract_certs.py | 150 ++++++++++++++++++ 1 file changed, 150 insertions(+) create mode 100755 images/gen_key_migration_script/extract_certs.py
diff --git a/images/gen_key_migration_script/extract_certs.py b/images/gen_key_migration_script/extract_certs.py new file mode 100755 index 0000000..a08d3e1 --- /dev/null +++ b/images/gen_key_migration_script/extract_certs.py @@ -0,0 +1,150 @@ +#!/usr/bin/env python3 +# Copyright (C) 2020 Denis 'GNUtoo' Carikli <[email protected]> +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +import os +import re +import sh +import sys + +# Etree isn't subject to any data leak vulnerabilities: +# +---------------------------+------------+-----------------------------------+ +# | Issue | Etree | Issue description | +# +---------------------------+------------+-----------------------------------+ +# | billion laughs | Vulnerable | DDOS by huge CPU and memory usage | +# +---------------------------+------------+-----------------------------------+ +# | quadratic blowup | Vulnerable | DDOS by huge CPU and memory usage | +# +---------------------------+------------+-----------------------------------+ +# | external entity expansion | Safe (1) | Data leak | +# +---------------------------+------------+-----------------------------------+ +# | DTD retrieval | Safe | Data leak | +# +---------------------------+------------+-----------------------------------+ +# | decompression bomb | Safe | DDOS by huge amount of CPU | +# +---------------------------+------------+-----------------------------------+ +# (1) xml.etree.ElementTree doesn’t expand external entities and raises a +# ParserError when an entity occurs. +# +# Other XML parsers like sax, minidom, pulldom, xmlrpc have similar security +# properties: they are not vulnerable (anymore) to data leaks but they are still +# vulnerable to DDOS attacks. +# Reference: https://docs.python.org/3.8/library/xml.html#xml-vulnerabilities +import xml.etree.ElementTree + +import jinja2 + +def usage(progname): + print ('Usage:') + print ('\t{} <path/to/packages.xml> <path/to/out/dir/>'.format(progname)) + sys.exit(1) + +def find_keys(results, root): + # Error if there is no keyset-settings or keys + keyset_settings = root.findall('keyset-settings') + assert (len(keyset_settings) == 1) + + keys = keyset_settings[0].findall('keys') + assert (len(keys) == 1) + + public_key_entries = keys[0].findall('public-key') + assert (len(public_key_entries) >= 1) + + for public_key in public_key_entries: + identifier = public_key.get('identifier') + value = public_key.get('value') + results[identifier] = { + 'PEM-pubkey' : value, + 'packages' : [], + } + + return results + +def parse_packages_xml_file(results, path): + xml_file = open(path, 'r') + tree = xml.etree.ElementTree.parse(xml_file) + root = tree.getroot() + + find_keys(results, root) + + # elm.iter(<tag>) iterates over the elm element and all elements below it + for package in root.iter('package'): + package_name = package.get('name') + proper_signing_keyset = package.findall('proper-signing-keyset') + assert(len(proper_signing_keyset) == 1) + + identifier = proper_signing_keyset[0].get('identifier') + + results[identifier]['packages'].append(package_name) + + for cert in package.iter('cert'): + if 'key' in cert.keys(): + results[identifier]['DER-cert'] = cert.get('key') + + xml_file.close() + +def write_pem_cert(der_cert, path): + openssl = sh.Command('openssl') + data = bytes.fromhex(der_cert) + + output = openssl('x509', + '-inform', 'DER', + '-outform', 'PEM', + '-out', path, _in=data) + +def get_filename(id, data): + if 'android' in packages: + return 'platform.x509.pem' + elif 'com.cyanogenmod.trebuchet' in packages: + return 'releasekey.x509.pem' + elif 'com.android.contacts' in packages: + return 'shared.x509.pem' + elif 'com.android.providers.media' in packages: + return 'media.x509.pem' + elif len (data['packages']) == 1: + return str(packages[0]) + '.x509.pem' + else: + return str(id) + '.x509.pem' + +if __name__ == '__main__': + # The packages.xml file has the certificate keys encoded twice: + # - Once in PEM format in the value= in: + # <keyset-settings version="1"> + # <keys> + # <public-key identifier="1" value="..." /> + # - Once in DER format in key= in: + # <package name="..." [...]> + # <sigs [...]> + # <cert [...] key="..." /> + if len(sys.argv) != 3: + usage(sys.argv[0]) + + packages_xml_path = sys.argv[1] + out_dir_path = sys.argv[2] + + results = {} + parse_packages_xml_file(results, packages_xml_path) + + for id, data in results.items(): + packages = data['packages'] + + base_path = out_dir_path + os.sep + get_filename(id, data) + + if len(packages) > 1: + file = open(base_path + '.txt', 'w') + file.write('Packages:' + os.linesep) + for package in packages: + file.write("- " + package + os.linesep) + file.close() + + write_pem_cert(data['DER-cert'], base_path) -- 2.28.0 _______________________________________________ Replicant mailing list [email protected] https://lists.osuosl.org/mailman/listinfo/replicant
