| #!/usr/bin/env python3 |
| # |
| # Copyright (C) 2023 Savoir-faire Linux Inc. |
| # |
| # Author: Xavier Jouslin de Noray <xavier.jouslindenoray@savoirfairelinux.com> |
| # |
| # This program is free software: you can redistribute it and/or modify |
| # it under the terms of the GNU 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 General Public License for more details. |
| # |
| # You should have received a copy of the GNU General Public License |
| # along with this program. If not, see <http://www.gnu.org/licenses/>. |
| # |
| # Creates packaging targets for a distribution and architecture. |
| # This helps reduce the length of the top Makefile. |
| # |
| import os |
| import shutil |
| from OpenSSL import crypto |
| from typing import Dict |
| from zipfile import ZipFile |
| from msgpack import packb |
| from utils import read |
| |
| from jplManipulation import JPLStructure |
| from certificate import Certificate |
| |
| |
| class PluginCertificate(Certificate): |
| plugin: JPLStructure |
| |
| def __init__(self, key: crypto.PKey, cert: crypto.X509, plugin: JPLStructure, issuer_path: str = None): |
| super().__init__(key, cert, issuer_path) |
| self.plugin = plugin |
| self.__temp_dir = './plugin/' |
| |
| @staticmethod |
| def load(cert: Certificate, plugin_path: str) -> 'PluginCertificate': |
| plugin = JPLStructure([plugin_path], is_merge=False) |
| return PluginCertificate(cert.cert[0], cert.cert[1], plugin, cert.issuer_path) |
| |
| def sign(self) -> Dict[str, Dict[str, bytes]]: |
| signatures = {} |
| if not os.path.exists(self.__temp_dir): |
| os.mkdir(self.__temp_dir) |
| for path in self.plugin.paths: |
| signatures[path] = {} |
| with ZipFile(path, 'r') as zip_file: |
| zip_file.extractall(self.__temp_dir) |
| for root, _, files in os.walk(self.__temp_dir): |
| for file in files: |
| if not root.endswith('/'): |
| root += '/' |
| abs_path = root + file |
| signatures[path][root.split(self.__temp_dir)[-1] + file] = super().sign(abs_path)[0] |
| shutil.rmtree(self.__temp_dir) |
| return signatures |
| |
| def save(self, path_2_file: str, signatures: Dict[str, Dict[str, bytes]]) -> None: |
| for path in self.plugin.paths: |
| plugin_signatures = packb(signatures[path]) |
| with ZipFile(path, 'a') as file: |
| file.writestr('signatures', plugin_signatures) |
| file.writestr( |
| f'{self._cert.get_subject().CN}.crt', |
| crypto.dump_certificate(crypto.FILETYPE_PEM, self._cert) if self.issuer_path is None else read(f'{self.issuer_path}.crt', 'rb') |
| ) |
| with ZipFile(path, 'r') as zip_file: |
| zip_file.extractall(self.__temp_dir) |
| sign = super().sign(self.__temp_dir + 'signatures') |
| with ZipFile(path, 'a') as file: |
| file.writestr('signatures.sig', sign[0]) |
| shutil.copy(path, f'{path_2_file}.jpl') |
| shutil.rmtree(self.__temp_dir) |