diff options
Diffstat (limited to 'src/mailman_pgp')
| -rw-r--r-- | src/mailman_pgp/testing/pgp.py | 26 | ||||
| -rw-r--r-- | src/mailman_pgp/utils/pgp.py | 32 | ||||
| -rw-r--r-- | src/mailman_pgp/utils/tests/__init__.py | 0 | ||||
| -rw-r--r-- | src/mailman_pgp/utils/tests/test_pgp.py | 86 |
4 files changed, 128 insertions, 16 deletions
diff --git a/src/mailman_pgp/testing/pgp.py b/src/mailman_pgp/testing/pgp.py index 15723bc..89f7a98 100644 --- a/src/mailman_pgp/testing/pgp.py +++ b/src/mailman_pgp/testing/pgp.py @@ -30,23 +30,31 @@ from mailman_pgp.testing.layers import PGPLayer from mailman_pgp.utils.pgp import revoc_from_blob +def load_blob(*path): + return resource_string('mailman_pgp.pgp.tests', + os.path.join('data', *path)) + + def load_message(path): - data = resource_string('mailman_pgp.pgp.tests', - os.path.join('data', 'messages', path)) - return message_from_bytes(data, Message) + """ + :rtype: Message + """ + return message_from_bytes(load_blob('messages', path), Message) def load_key(path): - key, _ = PGPKey.from_blob( - resource_string('mailman_pgp.pgp.tests', - os.path.join('data', 'keys', path))) + """ + :rtype: pgpy.PGPKey + """ + key, _ = PGPKey.from_blob(load_blob('keys', path)) return key def load_revoc(path): - return revoc_from_blob(resource_string('mailman_pgp.pgp.tests', - os.path.join('data', 'revocs', - path))) + """ + :rtype: pgpy.PGPSignature + """ + return revoc_from_blob(load_blob('revocs', path)) def payload_equal(one_msg, other_msg): diff --git a/src/mailman_pgp/utils/pgp.py b/src/mailman_pgp/utils/pgp.py index 416e643..a8f06f2 100644 --- a/src/mailman_pgp/utils/pgp.py +++ b/src/mailman_pgp/utils/pgp.py @@ -81,6 +81,7 @@ def key_from_file(file): @public def revoc_from_blob(blob): """ + Load a key revocation signature from an ASCII-Armored blob. :param blob: :return: @@ -103,27 +104,44 @@ def revoc_from_blob(blob): @public def key_usable(key, flags_required): """ + Check that the `key` has the `flags_required` set of KeyFlags. - :param key: + Checks only non-expired, non-revoked key/subkeys. Validates revocations it + can, so not those made with some other designated revocation key. + + :param key: The key to check. :type key: pgpy.PGPKey - :param flags_required: + :param flags_required: The set of flags required. :type flags_required: set - :return: + :return: Whether the key has the flags_required. :rtype: bool """ if key.is_expired: return False - primary_revocs = (sig for sig in key.self_signatures if - sig.sigtype is SignatureType.KeyRevocation) - for revoc in primary_revocs: + for revoc in key.revocation_signatures: try: verified = key.verify(key, revoc) except PGPError: continue if bool(verified): return False + usage_flags = key.usage_flags() for subkey in key.subkeys.values(): - usage_flags |= subkey.usage_flags() + if subkey.is_expired: + continue + + valid = True + for revoc in subkey.revocation_signatures: + try: + verified = key.verify(subkey, revoc) + except PGPError: + continue + if bool(verified): + valid = False + break + + if valid: + usage_flags |= subkey.usage_flags() return flags_required.issubset(usage_flags) diff --git a/src/mailman_pgp/utils/tests/__init__.py b/src/mailman_pgp/utils/tests/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/src/mailman_pgp/utils/tests/__init__.py diff --git a/src/mailman_pgp/utils/tests/test_pgp.py b/src/mailman_pgp/utils/tests/test_pgp.py new file mode 100644 index 0000000..493a0ca --- /dev/null +++ b/src/mailman_pgp/utils/tests/test_pgp.py @@ -0,0 +1,86 @@ +# 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 <http://www.gnu.org/licenses/>. + +"""""" +import datetime +import time +from unittest import TestCase + +from parameterized import parameterized +from pgpy import PGPKey, PGPUID +from pgpy.constants import (PubKeyAlgorithm, EllipticCurveOID, KeyFlags, + HashAlgorithm, SymmetricKeyAlgorithm, + CompressionAlgorithm) + +from mailman_pgp.testing.layers import PGPLayer +from mailman_pgp.testing.pgp import load_key, load_blob +from mailman_pgp.utils.pgp import revoc_from_blob, key_usable + + +class TestPGPUtils(TestCase): + layer = PGPLayer + + @parameterized.expand([ + (load_blob('revocs', 'rsa_1024.revoc.asc'), + load_key('rsa_1024.pub.asc')), + (load_blob('revocs', 'ecc_secp256k1.revoc.asc'), + load_key('ecc_secp256k1.pub.asc')), + (load_blob('revocs', 'ecc_p256.revoc.asc'), + load_key('ecc_p256.pub.asc')) + ]) + def test_revoc_from_blob_valid(self, blob, key): + revoc = revoc_from_blob(blob) + verifies = key.verify(key, revoc) + self.assertTrue(bool(verifies)) + + @parameterized.expand([ + ('Not an ASCII-Armored blob',), + (load_blob('keys', 'rsa_1024.pub.asc'),), + ]) + def test_revoc_from_blob_invalid(self, blob): + self.assertRaises(ValueError, revoc_from_blob, blob) + + def test_key_usable_expired(self): + key = PGPKey.new(PubKeyAlgorithm.ECDSA, EllipticCurveOID.SECP256K1) + uid = PGPUID.new('Some Name', email='anne@example.org') + key.add_uid(uid, key_expiration=datetime.timedelta(seconds=1), + usage={KeyFlags.Certify, + KeyFlags.Authentication, + KeyFlags.Sign}, + hashes=[HashAlgorithm.SHA256, + HashAlgorithm.SHA512], + ciphers=[SymmetricKeyAlgorithm.AES256], + compression=[CompressionAlgorithm.ZLIB]) + + time.sleep(2) + + self.assertFalse(key_usable(key, set())) + + def test_key_usable_revoked(self): + key = load_key('ecc_p256.priv.asc') + rsig = key.revoke(key) + key |= rsig + + self.assertFalse(key_usable(key, set())) + + def test_key_usable_subkey_revoked(self): + key = load_key('ecc_p256.priv.asc') + sub = next(iter(key.subkeys.values())) + rsig = key.revoke(sub) + sub |= rsig + + self.assertFalse(key_usable(key, {KeyFlags.EncryptCommunications})) |
