]> git.ipfire.org Git - thirdparty/dnspython.git/commitdiff
Preliminary Elliptic Curve DNSSEC Validation (requires ecdsa module)
authorBob Halley <halley@dnspython.org>
Sat, 21 Sep 2013 23:25:36 +0000 (16:25 -0700)
committerPetr Viktorin <pviktori@redhat.com>
Tue, 26 May 2015 14:06:29 +0000 (16:06 +0200)
dns/dnssec.py
tests/test_dnssec.py

index 85108329348401b7c21fb45972e0a8cfa2309c0c..b9bc8744b2ea644fc71bf59b6be4b4c8182a43b5 100644 (file)
@@ -43,6 +43,8 @@ DSANSEC3SHA1 = 6
 RSASHA1NSEC3SHA1 = 7
 RSASHA256 = 8
 RSASHA512 = 10
+ECDSAP256SHA256 = 13
+ECDSAP384SHA384 = 14
 INDIRECT = 252
 PRIVATEDNS = 253
 PRIVATEOID = 254
@@ -58,6 +60,8 @@ _algorithm_by_text = {
     'RSASHA256' : RSASHA256,
     'RSASHA512' : RSASHA512,
     'INDIRECT' : INDIRECT,
+    'ECDSAP256SHA256' : ECDSAP256SHA256,
+    'ECDSAP384SHA384' : ECDSAP384SHA384,
     'PRIVATEDNS' : PRIVATEDNS,
     'PRIVATEOID' : PRIVATEOID,
     }
@@ -151,6 +155,9 @@ def _is_rsa(algorithm):
 def _is_dsa(algorithm):
     return algorithm in (DSA, DSANSEC3SHA1)
 
+def _is_ecdsa(algorithm):
+    return _have_ecdsa and (algorithm in (ECDSAP256SHA256, ECDSAP384SHA384))
+
 def _is_md5(algorithm):
     return algorithm == RSAMD5
 
@@ -159,7 +166,10 @@ def _is_sha1(algorithm):
                          DSANSEC3SHA1, RSASHA1NSEC3SHA1)
 
 def _is_sha256(algorithm):
-    return algorithm == RSASHA256
+    return algorithm in (RSASHA256, ECDSAP256SHA256)
+
+def _is_sha384(algorithm):
+    return algorithm == ECDSAP384SHA384
 
 def _is_sha512(algorithm):
     return algorithm == RSASHA512
@@ -171,6 +181,8 @@ def _make_hash(algorithm):
         return dns.hash.get('SHA1')()
     if _is_sha256(algorithm):
         return dns.hash.get('SHA256')()
+    if _is_sha384(algorithm):
+        return dns.hash.get('SHA384')()
     if _is_sha512(algorithm):
         return dns.hash.get('SHA512')()
     raise ValidationFailure('unknown hash for algorithm %u' % algorithm)
@@ -272,6 +284,30 @@ def _validate_rrsig(rrset, rrsig, keys, origin=None, now=None):
             (dsa_r, dsa_s) = struct.unpack('!20s20s', rrsig.signature[1:])
             sig = (Crypto.Util.number.bytes_to_long(dsa_r),
                    Crypto.Util.number.bytes_to_long(dsa_s))
+        elif _is_ecdsa(rrsig.algorithm):
+            if rrsig.algorithm == ECDSAP256SHA256:
+                curve = ecdsa.curves.NIST256p
+                key_len = 32
+                digest_len = 32
+            elif rrsig.algorithm == ECDSAP384SHA384:
+                curve = ecdsa.curves.NIST384p
+                key_len = 48
+                digest_len = 48
+            else:
+                # shouldn't happen
+                raise ValidationFailure('unknown ECDSA curve')
+            keyptr = candidate_key.key
+            x = Crypto.Util.number.bytes_to_long(keyptr[0:key_len])
+            y = Crypto.Util.number.bytes_to_long(keyptr[key_len:key_len * 2])
+            assert ecdsa.ecdsa.point_is_valid(curve.generator, x, y)
+            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(Crypto.Util.number.bytes_to_long(r),
+                                        Crypto.Util.number.bytes_to_long(s))
         else:
             raise ValidationFailure('unknown algorithm %u' % rrsig.algorithm)
 
@@ -301,7 +337,7 @@ def _validate_rrsig(rrset, rrsig, keys, origin=None, now=None):
             padlen = keylen // 8 - len(digest) - 3
             digest = bytes([0]) + bytes([1]) + bytes([0xFF]) * padlen + \
                      bytes([0]) + digest
-        elif _is_dsa(rrsig.algorithm):
+        elif _is_dsa(rrsig.algorithm) or _is_ecdsa(rrsig.algorithm):
             pass
         else:
             # Raise here for code clarity; this won't actually ever happen
@@ -373,3 +409,21 @@ except ImportError:
     validate = _need_pycrypto
     validate_rrsig = _need_pycrypto
     _have_pycrypto = False
+
+try:
+    import ecdsa
+    import ecdsa.ecdsa
+    import ecdsa.ellipticcurve
+    import ecdsa.keys
+    _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 = Crypto.Util.number.bytes_to_long(digest)
+            return self.key.pubkey.verifies(diglong, sig)
+
+except ImportError:
+    _have_ecdsa = False
index 98c47888dea434817779e74bcd516febf0baa738..1d13041c3b428454dc5737eb78e0c25134270d04 100644 (file)
@@ -97,6 +97,40 @@ example_ds_sha1 = dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.DS,
 example_ds_sha256 = dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.DS,
                                         '18673 3 2 eb8344cbbf07c9d3d3d6c81d10c76653e28d8611a65e639ef8f716e4e4e5d913')
 
+when3 = 1379801800
+
+abs_ecdsa256_keys = { abs_example :
+                      dns.rrset.from_text('example.', 86400, 'IN', 'DNSKEY',
+                                          "256 3 13 +3ss1sCpdARVA61DJigEsL/8quo2a8MszKtn2gkkfxgzFs8S2UHtpb4N fY+XFmNW+JK6MsCkI3jHYN8eEQUgMw==",
+                                          "257 3 13 eJCEVH7AS3wnoaQpaNlAXH0W8wxymtT9P6P3qjN2ZCV641ED8pF7wZ5V yWfOpgTs6oaZevbJgehl/GaRPUgVyQ==")
+                 }
+
+abs_ecdsa256_soa = dns.rrset.from_text('example.', 86400, 'IN', 'SOA',
+                                       'ns1.example. hostmaster.example. 4 10800 3600 604800 86400')
+
+abs_other_ecdsa256_soa = dns.rrset.from_text('example.', 86400, 'IN', 'SOA',
+                                             'ns1.example. hostmaster.example. 2 10800 3600 604800 86401')
+
+abs_ecdsa256_soa_rrsig = dns.rrset.from_text('example.', 86400, 'IN', 'RRSIG',
+                                             "SOA 13 1 86400 20130921221753 20130921221638 7460 example. Sm09SOGz1ULB5D/duwdE2Zpn8bWbVBM77H6N1wPkc42LevvVO+kZEjpq 2nq4GOMJcih52667GIAbMrwmU5P2MQ==")
+
+when4 = 1379804850
+
+abs_ecdsa384_keys = { abs_example :
+                      dns.rrset.from_text('example.', 86400, 'IN', 'DNSKEY',
+                                          "256 3 14 1bG8qWviKNXQX3BIuG6/T5jrP1FISiLW/8qGF6BsM9DQtWYhhZUA3Owr OAEiyHAhQwjkN2kTvWiAYoPN80Ii+5ff9/atzY4F9W50P4l75Dj9PYrL HN/hLUgWMNVc9pvA",
+                                          "257 3 14 mSub2n0KRt6u2FaD5XJ3oQu0R4XvB/9vUJcyW6+oo0y+KzfQeTdkf1ro ZMVKoyWXW9zUKBYGJpMUIdbAxzrYi7f5HyZ3yDpBFz1hw9+o3CX+gtgb +RyhHfJDwwFXBid9")
+                 }
+
+abs_ecdsa384_soa = dns.rrset.from_text('example.', 86400, 'IN', 'SOA',
+                                       'ns1.example. hostmaster.example. 2 10800 3600 604800 86400')
+
+abs_other_ecdsa384_soa = dns.rrset.from_text('example.', 86400, 'IN', 'SOA',
+                                             'ns1.example. hostmaster.example. 2 10800 3600 604800 86401')
+
+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")
+
 class DNSSECValidatorTestCase(unittest.TestCase):
 
     @unittest.skipIf(not dns.dnssec._have_pycrypto,
@@ -157,6 +191,27 @@ class DNSSECValidatorTestCase(unittest.TestCase):
         ds = dns.dnssec.make_ds(abs_example, example_sep_key, 'SHA256')
         self.assertTrue(ds == example_ds_sha256)
 
+    def testAbsoluteECDSA256Good(self):
+        dns.dnssec.validate(abs_ecdsa256_soa, abs_ecdsa256_soa_rrsig,
+                            abs_ecdsa256_keys, None, when3)
+
+    def testAbsoluteECDSA256Bad(self):
+        def bad():
+            dns.dnssec.validate(abs_other_ecdsa256_soa, abs_ecdsa256_soa_rrsig,
+                                abs_ecdsa256_keys, None, when3)
+        self.failUnlessRaises(dns.dnssec.ValidationFailure, bad)
+
+    def testAbsoluteECDSA384Good(self):
+        dns.dnssec.validate(abs_ecdsa384_soa, abs_ecdsa384_soa_rrsig,
+                            abs_ecdsa384_keys, None, when4)
+
+    def testAbsoluteECDSA384Bad(self):
+        def bad():
+            dns.dnssec.validate(abs_other_ecdsa384_soa, abs_ecdsa384_soa_rrsig,
+                                abs_ecdsa384_keys, None, when4)
+        self.failUnlessRaises(dns.dnssec.ValidationFailure, bad)
+
+
 if __name__ == '__main__':
     import_ok = False
     try: