diff options
author | Tomas Krizek <tomas.krizek@nic.cz> | 2018-07-18 14:56:31 +0200 |
---|---|---|
committer | Tomas Krizek <tomas.krizek@nic.cz> | 2018-07-20 16:24:17 +0200 |
commit | c15a34bc0ee818620fdb23a2a2bc0366f23feab0 (patch) | |
tree | 0c7fefb5d52f97b41ca122b12da96beb4e1b5fdf | |
parent | 8d7a84a2dfeffd2d080296898aadb3503cc237e1 (diff) | |
download | dnspython-c15a34bc0ee818620fdb23a2a2bc0366f23feab0.tar.gz |
dns/dnssec: support both pycryptodome and pycryptodomex
-rw-r--r-- | ChangeLog | 3 | ||||
-rw-r--r-- | dns/dnssec.py | 84 | ||||
-rw-r--r-- | doc/dnssec.rst | 4 | ||||
-rw-r--r-- | doc/installation.rst | 4 | ||||
-rwxr-xr-x | setup.py | 2 | ||||
-rw-r--r-- | tests/test_dnssec.py | 26 |
6 files changed, 58 insertions, 65 deletions
@@ -1,7 +1,8 @@ 2017-12-21 Daniel Robbins <drobbins@funtoo.org> * dns/dnssec.py: migrated code from pycrypto (apparently no - longer maintained) to pycryptodome. All tests passing. + longer maintained) to pycryptodome/pycryptodomex. + All tests passing. 2018-07-18 Tomas Krizek <tomas.krizek@nic.cz> diff --git a/dns/dnssec.py b/dns/dnssec.py index b154b10..634c934 100644 --- a/dns/dnssec.py +++ b/dns/dnssec.py @@ -27,8 +27,7 @@ import dns.rdata import dns.rdatatype import dns.rdataclass from ._compat import string_types -from Crypto.Hash import MD5, SHA1, SHA256, SHA384, SHA512 -from Crypto.Signature import pkcs1_15, DSS + class UnsupportedAlgorithm(dns.exception.DNSException): """The DNSSEC algorithm is not supported.""" @@ -327,9 +326,9 @@ def _validate_rrsig(rrset, rrsig, keys, origin=None, now=None): rsa_e = keyptr[0:bytes_] rsa_n = keyptr[bytes_:] try: - pubkey = Crypto.PublicKey.RSA.construct( - (Crypto.Util.number.bytes_to_long(rsa_n), - Crypto.Util.number.bytes_to_long(rsa_e))) + pubkey = CryptoRSA.construct( + (number.bytes_to_long(rsa_n), + number.bytes_to_long(rsa_e))) except ValueError: raise ValidationFailure('invalid public key') sig = rrsig.signature @@ -345,11 +344,11 @@ def _validate_rrsig(rrset, rrsig, keys, origin=None, now=None): dsa_g = keyptr[0:octets] keyptr = keyptr[octets:] dsa_y = keyptr[0:octets] - pubkey = Crypto.PublicKey.DSA.construct( - (Crypto.Util.number.bytes_to_long(dsa_y), - Crypto.Util.number.bytes_to_long(dsa_g), - Crypto.Util.number.bytes_to_long(dsa_p), - Crypto.Util.number.bytes_to_long(dsa_q))) + pubkey = CryptoDSA.construct( + (number.bytes_to_long(dsa_y), + number.bytes_to_long(dsa_g), + number.bytes_to_long(dsa_p), + number.bytes_to_long(dsa_q))) sig = rrsig.signature[1:] elif _is_ecdsa(rrsig.algorithm): # use ecdsa for NIST-384p -- not currently supported by pycryptodome @@ -363,8 +362,8 @@ def _validate_rrsig(rrset, rrsig, keys, origin=None, now=None): curve = ecdsa.curves.NIST384p key_len = 48 - x = Crypto.Util.number.bytes_to_long(keyptr[0:key_len]) - y = Crypto.Util.number.bytes_to_long(keyptr[key_len:key_len * 2]) + x = number.bytes_to_long(keyptr[0:key_len]) + y = number.bytes_to_long(keyptr[key_len:key_len * 2]) if not ecdsa.ecdsa.point_is_valid(curve.generator, x, y): raise ValidationFailure('invalid ECDSA key') point = ecdsa.ellipticcurve.Point(curve.curve, x, y, curve.order) @@ -373,8 +372,8 @@ def _validate_rrsig(rrset, rrsig, keys, origin=None, now=None): pubkey = ECKeyWrapper(verifying_key, key_len) r = rrsig.signature[:key_len] s = rrsig.signature[key_len:] - sig = ecdsa.ecdsa.Signature(Crypto.Util.number.bytes_to_long(r), - Crypto.Util.number.bytes_to_long(s)) + sig = ecdsa.ecdsa.Signature(number.bytes_to_long(r), + number.bytes_to_long(s)) else: raise ValidationFailure('unknown algorithm %u' % rrsig.algorithm) @@ -474,36 +473,47 @@ def _validate(rrset, rrsigset, keys, origin=None, now=None): def _need_pycrypto(*args, **kwargs): - raise NotImplementedError("DNSSEC validation requires pycryptodome") + raise NotImplementedError("DNSSEC validation requires pycryptodome/pycryptodomex") + try: - import Crypto.PublicKey.RSA - import Crypto.PublicKey.DSA - import Crypto.Util.number - validate = _validate - validate_rrsig = _validate_rrsig - _have_pycrypto = True + try: + # test we're using pycryptodome, not pycrypto (which misses SHA1 for example) + from Crypto.Hash import MD5, SHA1, SHA256, SHA384, SHA512 + from Crypto.PublicKey import RSA as CryptoRSA, DSA as CryptoDSA + from Crypto.Signature import pkcs1_15, DSS + from Crypto.Util import number + except ImportError: + from Cryptodome.Hash import MD5, SHA1, SHA256, SHA384, SHA512 + from Cryptodome.PublicKey import RSA as CryptoRSA, DSA as CryptoDSA + from Cryptodome.Signature import pkcs1_15, DSS + from Cryptodome.Util import number except ImportError: validate = _need_pycrypto validate_rrsig = _need_pycrypto _have_pycrypto = False + _have_ecdsa = False +else: + validate = _validate + validate_rrsig = _validate_rrsig + _have_pycrypto = True -try: - import ecdsa - import ecdsa.ecdsa - import ecdsa.ellipticcurve - import ecdsa.keys - _have_ecdsa = True - - class ECKeyWrapper(object): + try: + import ecdsa + import ecdsa.ecdsa + import ecdsa.ellipticcurve + import ecdsa.keys + except ImportError: + _have_ecdsa = False + else: + _have_ecdsa = True - def __init__(self, key, key_len): - self.key = key - self.key_len = key_len + class ECKeyWrapper(object): - def verify(self, digest, sig): - diglong = Crypto.Util.number.bytes_to_long(digest) - return self.key.pubkey.verifies(diglong, sig) + def __init__(self, key, key_len): + self.key = key + self.key_len = key_len -except ImportError: - _have_ecdsa = False + def verify(self, digest, sig): + diglong = number.bytes_to_long(digest) + return self.key.pubkey.verifies(diglong, sig) diff --git a/doc/dnssec.rst b/doc/dnssec.rst index 64f08b3..115f973 100644 --- a/doc/dnssec.rst +++ b/doc/dnssec.rst @@ -6,8 +6,8 @@ DNSSEC Dnspython can do simple DNSSEC signature validation, but currently has no facilities for signing. In order to use DNSSEC functions, you must have -``pycryptodome`` installed. If you want to do elliptic curves, you must also -have ``ecdsa`` installed. +``pycryptodome`` or ``pycryptodomex`` installed. If you want to do elliptic +curves, you must also have ``ecdsa`` installed. DNSSEC Algorithms ----------------- diff --git a/doc/installation.rst b/doc/installation.rst index 7854f3d..e480655 100644 --- a/doc/installation.rst +++ b/doc/installation.rst @@ -45,8 +45,8 @@ Optional Modules The following modules are optional, but recommended for full functionality. -If ``pycryptodome`` is installed, then dnspython will be able to do low-level -DNSSEC RSA and DSA signature validation. +If ``pycryptodome`` / ``pycryptodomex`` is installed, then dnspython will be +able to do low-level DNSSEC RSA and DSA signature validation. If ``ecdsa`` is installed, then Elliptic Curve signature algorithms will be available for low-level DNSSEC signature validation. @@ -62,7 +62,7 @@ direct manipulation of DNS zones, messages, names, and records.""", 'provides': ['dns'], 'extras_require': { 'IDNA': ['idna>=2.1'], - 'DNSSEC': ['pycrypto>=2.6.1', 'ecdsa>=0.13'], + 'DNSSEC': ['pycryptodome', 'ecdsa>=0.13'], }, } diff --git a/tests/test_dnssec.py b/tests/test_dnssec.py index 9fb037e..78b1cdc 100644 --- a/tests/test_dnssec.py +++ b/tests/test_dnssec.py @@ -20,12 +20,6 @@ try: except ImportError: import unittest -try: - import Crypto.Util.number # pylint: disable=unused-import - import_ok = True -except ImportError: - import_ok = False - import dns.dnssec import dns.name import dns.rdata @@ -156,36 +150,28 @@ abs_other_ecdsa384_soa = dns.rrset.from_text('example.', 86400, 'IN', 'SOA', abs_ecdsa384_soa_rrsig = dns.rrset.from_text('example.', 86400, 'IN', 'RRSIG', "SOA 14 1 86400 20130929021229 20130921230729 63571 example. CrnCu34EeeRz0fEhL9PLlwjpBKGYW8QjBjFQTwd+ViVLRAS8tNkcDwQE NhSV89NEjj7ze1a/JcCfcJ+/mZgnvH4NHLNg3Tf6KuLZsgs2I4kKQXEk 37oIHravPEOlGYNI") -@unittest.skipUnless(import_ok, "skipping DNSSEC tests because pycryptodome is not" - " installed") + + +@unittest.skipUnless(dns.dnssec._have_pycrypto, + "Pycryptodome cannot be imported") class DNSSECValidatorTestCase(unittest.TestCase): - @unittest.skipUnless(dns.dnssec._have_pycrypto, - "Pycryptodome cannot be imported") def testAbsoluteRSAGood(self): dns.dnssec.validate(abs_soa, abs_soa_rrsig, abs_keys, None, when) - @unittest.skipUnless(dns.dnssec._have_pycrypto, - "Pycryptodome cannot be imported") def testDuplicateKeytag(self): dns.dnssec.validate(abs_soa, abs_soa_rrsig, abs_keys_duplicate_keytag, None, when) - @unittest.skipUnless(dns.dnssec._have_pycrypto, - "Pycryptodome cannot be imported") def testAbsoluteRSABad(self): def bad(): dns.dnssec.validate(abs_other_soa, abs_soa_rrsig, abs_keys, None, when) self.failUnlessRaises(dns.dnssec.ValidationFailure, bad) - @unittest.skipUnless(dns.dnssec._have_pycrypto, - "Pycryptodome cannot be imported") def testRelativeRSAGood(self): dns.dnssec.validate(rel_soa, rel_soa_rrsig, rel_keys, abs_dnspython_org, when) - @unittest.skipUnless(dns.dnssec._have_pycrypto, - "Pycryptodome cannot be imported") def testRelativeRSABad(self): def bad(): dns.dnssec.validate(rel_other_soa, rel_soa_rrsig, rel_keys, @@ -196,14 +182,10 @@ class DNSSECValidatorTestCase(unittest.TestCase): ds = dns.dnssec.make_ds(abs_dnspython_org, sep_key, 'SHA256') self.failUnless(ds == good_ds) - @unittest.skipUnless(dns.dnssec._have_pycrypto, - "Pycryptodome cannot be imported") def testAbsoluteDSAGood(self): dns.dnssec.validate(abs_dsa_soa, abs_dsa_soa_rrsig, abs_dsa_keys, None, when2) - @unittest.skipUnless(dns.dnssec._have_pycrypto, - "Pycryptodome cannot be imported") def testAbsoluteDSABad(self): def bad(): dns.dnssec.validate(abs_other_dsa_soa, abs_dsa_soa_rrsig, |