# Copyright (C) 2017 Jan Jancar # # This file is a part of the Mailman PGP plugin. # # 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 . """""" from glob import glob from os import makedirs from os.path import join from mailman.config import config as mailman_config from mailman.utilities.string import expand from pgpy import PGPKeyring from pgpy.constants import PubKeyAlgorithm, EllipticCurveOID from public import public from mailman_pgp.config import config KEYDIR_CONFIG_PATHS = ['list_keydir', 'user_keydir', 'archive_keydir'] KEYPAIR_CONFIG_VARIABLES = ['autogenerate', 'primary_key', 'sub_key'] KEYPAIR_TYPE_MAP = { 'RSA': PubKeyAlgorithm.RSAEncryptOrSign, 'DSA': PubKeyAlgorithm.DSA, 'ECDSA': PubKeyAlgorithm.ECDSA, 'ECDH': PubKeyAlgorithm.ECDH } ECC_OID_MAP = { 'nistp256': EllipticCurveOID.NIST_P256, 'nistp384': EllipticCurveOID.NIST_P384, 'nistp521': EllipticCurveOID.NIST_P521, 'brainpoolP256r1': EllipticCurveOID.Brainpool_P256, 'brainpoolP384r1': EllipticCurveOID.Brainpool_P384, 'brainpoolP512r1': EllipticCurveOID.Brainpool_P512, 'secp256k1': EllipticCurveOID.SECP256K1 } @public class PGP: def __init__(self): self._load_config() self._validate_config() def _load_config(self): """ Load [keypairs] and [keydirs] config sections. Expand paths in them. """ # Get all the [keypairs] config variables. self._keypair_config = dict( (k, config.get('keypairs', k)) for k in KEYPAIR_CONFIG_VARIABLES) # Get and expand all [keydirs] config paths against Mailman's paths. self.keydir_config = dict( (k, expand(config.get('keydirs', k), None, mailman_config.paths)) for k in KEYDIR_CONFIG_PATHS) def _parse_key_directive(self, value): key_type, key_length = value.split(':') key_type = key_type.upper() key_length = key_length.lower() if key_type not in KEYPAIR_TYPE_MAP: raise ValueError('Invalid key type: {}.'.format(key_type)) out_type = KEYPAIR_TYPE_MAP[key_type] if key_type in ('ECDSA', 'ECDH'): if key_length not in ECC_OID_MAP: raise ValueError('Invalid key length: {}.'.format(key_length)) out_length = ECC_OID_MAP[key_length] else: out_length = int(key_length) return (out_type, out_length) def _validate_config(self): """ Validate [keypairs] and [keydirs] config sections. And create keydirs if necessary. """ # Validate keypair config. self.primary_key_args = self._parse_key_directive( self._keypair_config['primary_key']) if not self.primary_key_args[0].can_sign: raise ValueError( 'Invalid primary key type: {}.'.format( self.primary_key_args[0])) self.sub_key_args = self._parse_key_directive( self._keypair_config['sub_key']) if not self.sub_key_args[0].can_encrypt: raise ValueError( 'Invalid sub key type: {}.'.format(self.sub_key_args[0])) # Make sure the keydir paths are directories and exist. for keydir in self.keydir_config.values(): # TODO set a strict mode here makedirs(keydir, exist_ok=True) def _keyring(self, keydir): directory = self.keydir_config[keydir] return PGPKeyring(*glob(join(directory, '*.asc'))) @property def list_keyring(self): return self._keyring('list_keydir') @property def user_keyring(self): return self._keyring('user_keydir') @property def archive_keyring(self): return self._keyring('archive_keydir')