]> git.ipfire.org Git - thirdparty/dnspython.git/commitdiff
Use PyCryptodome for ECDSA. 422/head
authorBrian Wellington <bwelling@xbill.org>
Tue, 10 Mar 2020 17:58:56 +0000 (10:58 -0700)
committerBrian Wellington <bwelling@xbill.org>
Tue, 10 Mar 2020 17:58:56 +0000 (10:58 -0700)
.travis.yml
dns/dnssec.py
dns/dnssec.pyi
doc/dnssec.rst
doc/installation.rst
setup.py
tests/test_dnssec.py

index 608d02d719da3693a89cbf8b8dd68513221aa6a8..61c9522074ed4c7a4e3d9c2020f997091b915d4b 100644 (file)
@@ -16,6 +16,6 @@ branches:
   except:
     - python3
 install:
- - pip install typing pylint pycryptodome ecdsa idna requests requests-toolbelt
+ - pip install typing pylint pycryptodome idna requests requests-toolbelt
 script:
  - make test
index 7ce11244a9806513ac9b9cf7145bfeb80ab0448f..f2b453c70b03613172e29978bf4a1c210d51839c 100644 (file)
@@ -362,31 +362,20 @@ def _validate_rrsig(rrset, rrsig, keys, origin=None, now=None):
                  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
-            if not _have_ecdsa:
-                raise ImportError('DNSSEC validation for algorithm %u requires edcsa library' % rrsig.algorithm)
-
             keyptr = candidate_key.key
-
             if rrsig.algorithm == ECDSAP256SHA256:
-                curve = ecdsa.curves.NIST256p
-                key_len = 32
-            elif rrsig.algorithm == ECDSAP384SHA384:
-                curve = ecdsa.curves.NIST384p
-                key_len = 48
-
-            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)
-            verifying_key = ecdsa.keys.VerifyingKey.from_public_point(point,
-                                                                      curve)
-            pubkey = ECKeyWrapper(verifying_key, key_len)
-            r = rrsig.signature[:key_len]
-            s = rrsig.signature[key_len:]
-            sig = ecdsa.ecdsa.Signature(number.bytes_to_long(r),
-                                        number.bytes_to_long(s))
+                curve = 'secp256r1'
+                octets = 32
+            else:
+                curve = 'secp384r1'
+                octets = 48
+            ecdsa_x = keyptr[0:octets]
+            ecdsa_y = keyptr[octets:octets * 2]
+            pubkey = CryptoECC.construct(
+                curve = curve,
+                point_x = number.bytes_to_long(ecdsa_x),
+                point_y = number.bytes_to_long(ecdsa_y))
+            sig = rrsig.signature
 
         elif _is_eddsa(rrsig.algorithm) or _is_gost(rrsig.algorithm):
             raise UnsupportedAlgorithm(
@@ -418,13 +407,9 @@ def _validate_rrsig(rrset, rrsig, keys, origin=None, now=None):
                 verifier = pkcs1_15.new(pubkey)
                 # will raise ValueError if verify fails:
                 verifier.verify(hash, sig)
-            elif _is_dsa(rrsig.algorithm):
+            elif _is_dsa(rrsig.algorithm) or _is_ecdsa(rrsig.algorithm):
                 verifier = DSS.new(pubkey, 'fips-186-3')
                 verifier.verify(hash, sig)
-            elif _is_ecdsa(rrsig.algorithm):
-                digest = hash.digest()
-                if not pubkey.verify(digest, sig):
-                    raise ValueError
             else:
                 # Raise here for code clarity; this won't actually ever happen
                 # since if the algorithm is really unknown we'd already have
@@ -495,39 +480,20 @@ 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.PublicKey import ECC as CryptoECC
         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.PublicKey import ECC as CryptoECC
         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
-    except ImportError:
-        _have_ecdsa = False
-    else:
-        _have_ecdsa = True
-
-        class ECKeyWrapper(object):
-
-            def __init__(self, key, key_len):
-                self.key = key
-                self.key_len = key_len
-
-            def verify(self, digest, sig):
-                diglong = number.bytes_to_long(digest)
-                return self.key.pubkey.verifies(diglong, sig)
index 5699b3e15bc9eaecdc456deceec5656165d72425..da02c1510f613c20d088c5d4d9ab74bf3620ffaa 100644 (file)
@@ -3,7 +3,6 @@ from . import rdataset, rrset, exception, name, rdtypes, rdata, node
 import dns.rdtypes.ANY.DS as DS
 import dns.rdtypes.ANY.DNSKEY as DNSKEY
 
-_have_ecdsa : bool
 _have_pycrypto : bool
 
 def validate_rrsig(rrset : Union[Tuple[name.Name, rdataset.Rdataset], rrset.RRset], rrsig : rdata.Rdata, keys : Dict[name.Name, Union[node.Node, rdataset.Rdataset]], origin : Optional[name.Name] = None, now : Optional[int] = None) -> None:
index eee8ea741e8e618d96fc3362e40cfe02bd44b180..079ba2315eb3ea237db09d8e08eab30f21e57bb1 100644 (file)
@@ -6,8 +6,7 @@ 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`` or ``pycryptodomex`` installed.  If you want to do elliptic
-curves, you must also have ``ecdsa`` installed.
+``pycryptodome`` or ``pycryptodomex`` installed.
 
 DNSSEC Functions
 ----------------
@@ -33,4 +32,4 @@ DNSSEC Algorithms
 .. autodata:: dns.dnssec.ECDSAP384SHA384
 .. autodata:: dns.dnssec.INDIRECT
 .. autodata:: dns.dnssec.PRIVATEDNS
-.. autodata:: dns.dnssec.PRIVATEOID
\ No newline at end of file
+.. autodata:: dns.dnssec.PRIVATEOID
index d9d7339eb0f56b32ef369aadb1a366668740acb3..9c619e90bc34c183345b0bfd8d7f7e95a90d6959 100644 (file)
@@ -46,9 +46,6 @@ Optional Modules
 The following modules are optional, but recommended for full functionality.
 
 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.
+able to do low-level DNSSEC RSA, DSA, and ECDSA signature validation.
 
 If ``idna`` is installed, then IDNA 2008 will be available.
index 8279a890a03a1bc0c69fb3cc9df64f486438b8ff..2dac565ef674efcc1fa30049104da0d7314d0281 100755 (executable)
--- a/setup.py
+++ b/setup.py
@@ -75,7 +75,7 @@ direct manipulation of DNS zones, messages, names, and records.""",
     'tests_require': ['typing ; python_version<"3.5"'],
     'extras_require': {
         'IDNA': ['idna>=2.1'],
-        'DNSSEC': ['pycryptodome', 'ecdsa>=0.13'],
+        'DNSSEC': ['pycryptodome>=3.4'],
         },
     'ext_modules': ext_modules if compile_cython else None,
     'zip_safe': False if compile_cython else None,
index 24492103a2b0df9162e2a2a1ea071b8ef1ac11dd..31078c7bd4598720bd53edd6cf5e54ccecbc251e 100644 (file)
@@ -186,28 +186,20 @@ class DNSSECValidatorTestCase(unittest.TestCase):
                                 abs_dsa_keys, None, when2)
         self.assertRaises(dns.dnssec.ValidationFailure, bad)
 
-    @unittest.skipUnless(dns.dnssec._have_ecdsa,
-                         "python ECDSA cannot be imported")
     def testAbsoluteECDSA256Good(self):  # type: () -> None
         dns.dnssec.validate(abs_ecdsa256_soa, abs_ecdsa256_soa_rrsig,
                             abs_ecdsa256_keys, None, when3)
 
-    @unittest.skipUnless(dns.dnssec._have_ecdsa,
-                         "python ECDSA cannot be imported")
     def testAbsoluteECDSA256Bad(self):  # type: () -> None
         def bad():  # type: () -> None
             dns.dnssec.validate(abs_other_ecdsa256_soa, abs_ecdsa256_soa_rrsig,
                                 abs_ecdsa256_keys, None, when3)
         self.assertRaises(dns.dnssec.ValidationFailure, bad)
 
-    @unittest.skipUnless(dns.dnssec._have_ecdsa,
-                         "python ECDSA cannot be imported")
     def testAbsoluteECDSA384Good(self):  # type: () -> None
         dns.dnssec.validate(abs_ecdsa384_soa, abs_ecdsa384_soa_rrsig,
                             abs_ecdsa384_keys, None, when4)
 
-    @unittest.skipUnless(dns.dnssec._have_ecdsa,
-                         "python ECDSA cannot be imported")
     def testAbsoluteECDSA384Bad(self):  # type: () -> None
         def bad():  # type: () -> None
             dns.dnssec.validate(abs_other_ecdsa384_soa, abs_ecdsa384_soa_rrsig,