aboutsummaryrefslogtreecommitdiff
path: root/src/mailman_pgp/pgp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mailman_pgp/pgp')
-rw-r--r--src/mailman_pgp/pgp/__init__.py60
-rw-r--r--src/mailman_pgp/pgp/keygen.py28
-rw-r--r--src/mailman_pgp/pgp/tests/test_keygen.py38
3 files changed, 79 insertions, 47 deletions
diff --git a/src/mailman_pgp/pgp/__init__.py b/src/mailman_pgp/pgp/__init__.py
index 31b61b3..b41c8a1 100644
--- a/src/mailman_pgp/pgp/__init__.py
+++ b/src/mailman_pgp/pgp/__init__.py
@@ -24,25 +24,29 @@ 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
+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', 'key_type', 'key_length',
- 'subkey_type', 'subkey_length']
+KEYPAIR_CONFIG_VARIABLES = ['autogenerate', 'primary_key', 'sub_key']
-# The main key needs to support signing.
-KEYPAIR_KEY_TYPE_VALID = ['RSA', 'DSA', 'ECDSA']
-# The subkey needs to support encryption.
-KEYPAIR_SUBKEY_TYPE_VALID = ['RSA', 'ECDH']
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
@@ -56,7 +60,7 @@ class PGP:
Load [keypairs] and [keydirs] config sections. Expand paths in them.
"""
# Get all the [keypairs] config variables.
- self.keypair_config = dict(
+ self._keypair_config = dict(
(k, config.get('keypairs', k)) for k in
KEYPAIR_CONFIG_VARIABLES)
@@ -66,25 +70,41 @@ class PGP:
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.
- key_type = self.keypair_config['key_type'].upper()
- if key_type not in KEYPAIR_KEY_TYPE_VALID:
- raise ValueError('Invalid key_type. {}'.format(key_type))
- self.keypair_config['key_type'] = KEYPAIR_TYPE_MAP[key_type]
- self.keypair_config['key_length'] = int(
- self.keypair_config['key_length'])
+ 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]))
- subkey_type = self.keypair_config['subkey_type'].upper()
- if subkey_type not in KEYPAIR_SUBKEY_TYPE_VALID:
- raise ValueError('Invalid subkey_type. {}'.format(subkey_type))
- self.keypair_config['subkey_type'] = KEYPAIR_TYPE_MAP[subkey_type]
- self.keypair_config['subkey_length'] = int(
- self.keypair_config['subkey_length'])
+ 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():
diff --git a/src/mailman_pgp/pgp/keygen.py b/src/mailman_pgp/pgp/keygen.py
index b750e28..684b81a 100644
--- a/src/mailman_pgp/pgp/keygen.py
+++ b/src/mailman_pgp/pgp/keygen.py
@@ -29,36 +29,42 @@ from pgpy.constants import (
class ListKeyGenerator(mp.Process):
"""A multiprocessing list key generator."""
- def __init__(self, keypair_config, display_name, posting_address,
+ def __init__(self, primary_args, subkey_args, display_name,
+ posting_address,
request_address, key_path):
super().__init__(
target=self.generate,
- args=(keypair_config, display_name, posting_address,
+ args=(primary_args, subkey_args, display_name, posting_address,
request_address, key_path),
daemon=True)
- def generate(self, keypair_config, display_name, posting_address,
+ def generate(self, primary_args, subkey_args, display_name,
+ posting_address,
request_address, key_path):
"""
Generate the list keypair and save it.
- :param keypair_config:
+ :param primary_args:
+ :param subkey_args:
:param display_name:
:param posting_address:
:param request_address:
:param key_path:
"""
- key = self._create(keypair_config, display_name, posting_address,
+ key = self._create(primary_args, subkey_args, display_name,
+ posting_address,
request_address)
with Lock(key_path + '.lock'):
self._save(key, key_path)
- def _create(self, config, display_name, posting_address, request_address):
+ def _create(self, primary_args, subkey_args, display_name, posting_address,
+ request_address):
"""
Generate the list `PGPKey` keypair, with posting and request UIDs.
Use a Sign+Certify main key and Encrypt subkey.
- :param config:
+ :param primary_args:
+ :param subkey_args:
:param display_name:
:param posting_address:
:param request_address:
@@ -79,9 +85,7 @@ class ListKeyGenerator(mp.Process):
)
# Generate the Sign + Certify primary key.
- key_type = config['key_type']
- key_length = config['key_length']
- key = PGPKey.new(key_type, key_length)
+ key = PGPKey.new(*primary_args)
key_params = dict(usage={KeyFlags.Sign, KeyFlags.Certify},
**common_params)
# Generate the posting + request uids.
@@ -89,9 +93,7 @@ class ListKeyGenerator(mp.Process):
request_uid = PGPUID.new(display_name,
email=request_address)
# Generate the Encrypt subkey.
- subkey_type = config['subkey_type']
- subkey_length = config['subkey_length']
- subkey = PGPKey.new(subkey_type, subkey_length)
+ subkey = PGPKey.new(*subkey_args)
subkey_params = dict(
usage={KeyFlags.EncryptCommunications,
KeyFlags.EncryptStorage},
diff --git a/src/mailman_pgp/pgp/tests/test_keygen.py b/src/mailman_pgp/pgp/tests/test_keygen.py
index dab6801..bbd0c84 100644
--- a/src/mailman_pgp/pgp/tests/test_keygen.py
+++ b/src/mailman_pgp/pgp/tests/test_keygen.py
@@ -15,32 +15,42 @@
# You should have received a copy of the GNU General Public License along with
# this program. If not, see <http://www.gnu.org/licenses/>.
+"""Test the out-of-process key generator."""
from os.path import exists, isfile, join
from tempfile import TemporaryDirectory
from unittest import TestCase
+from parameterized import parameterized
from pgpy import PGPKey
-from pgpy.constants import PubKeyAlgorithm
+from pgpy.constants import PubKeyAlgorithm, EllipticCurveOID
from mailman_pgp.pgp.keygen import ListKeyGenerator
-class TesKeygen(TestCase):
+class TestKeygen(TestCase):
def setUp(self):
- self.keypair_config = {
- 'key_type': PubKeyAlgorithm.RSAEncryptOrSign,
- 'key_length': 1024,
- 'subkey_type': PubKeyAlgorithm.RSAEncryptOrSign,
- 'subkey_length': 1024
- }
self.display_name = 'Display Name'
self.posting_address = 'posting@address.com'
self.request_address = 'posting-request@address.com'
- def test_generate(self):
+ @parameterized.expand([
+ # RSA + RSA
+ (PubKeyAlgorithm.RSAEncryptOrSign, 1024,
+ PubKeyAlgorithm.RSAEncryptOrSign, 1024),
+ # ECDSA + ECDH
+ (PubKeyAlgorithm.ECDSA, EllipticCurveOID.SECP256K1,
+ PubKeyAlgorithm.ECDH, EllipticCurveOID.SECP256K1),
+ # DSA + ECDH
+ (PubKeyAlgorithm.DSA, 1024,
+ PubKeyAlgorithm.ECDH, EllipticCurveOID.SECP256K1)
+ ])
+ def test_generate(self, primary_key_type, primary_key_size, sub_key_type,
+ sub_key_size):
with TemporaryDirectory() as temp_dir:
key_path = join(temp_dir, 'key.asc')
- keygen = ListKeyGenerator(self.keypair_config, self.display_name,
+ keygen = ListKeyGenerator((primary_key_type, primary_key_size),
+ (sub_key_type, sub_key_size),
+ self.display_name,
self.posting_address,
self.request_address, key_path)
keygen.start()
@@ -50,18 +60,18 @@ class TesKeygen(TestCase):
key, _ = PGPKey.from_file(key_path)
self.assertEqual(key.key_algorithm,
- self.keypair_config['key_type'])
+ primary_key_type)
self.assertEqual(key.key_size,
- self.keypair_config['key_length'])
+ primary_key_size)
subs = key.subkeys
self.assertEqual(len(subs), 1)
keyid, sub = subs.popitem()
self.assertEqual(sub.key_algorithm,
- self.keypair_config['subkey_type'])
+ sub_key_type)
self.assertEqual(sub.key_size,
- self.keypair_config['subkey_length'])
+ sub_key_size)
uids = key.userids
self.assertEqual(len(uids), 2)