]> 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)
committerBob Halley <halley@dnspython.org>
Sat, 21 Sep 2013 23:25:36 +0000 (16:25 -0700)
dns/dnssec.py
tests/dnssec.py

index 084f908d3d2309ee11c7660be556e01201db065e..edbe479672081916f46e811af26fdfa96813c016 100644 (file)
@@ -45,6 +45,8 @@ DSANSEC3SHA1 = 6
 RSASHA1NSEC3SHA1 = 7
 RSASHA256 = 8
 RSASHA512 = 10
+ECDSAP256SHA256 = 13
+ECDSAP384SHA384 = 14
 INDIRECT = 252
 PRIVATEDNS = 253
 PRIVATEOID = 254
@@ -60,6 +62,8 @@ _algorithm_by_text = {
     'RSASHA256' : RSASHA256,
     'RSASHA512' : RSASHA512,
     'INDIRECT' : INDIRECT,
+    'ECDSAP256SHA256' : ECDSAP256SHA256,
+    'ECDSAP384SHA384' : ECDSAP384SHA384,
     'PRIVATEDNS' : PRIVATEDNS,
     'PRIVATEOID' : PRIVATEOID,
     }
@@ -153,6 +157,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
 
@@ -161,7 +168,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
@@ -173,6 +183,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
@@ -274,6 +286,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
 
@@ -302,7 +338,7 @@ def _validate_rrsig(rrset, rrsig, keys, origin=None, now=None):
             digest = _make_algorithm_id(rrsig.algorithm) + digest
             padlen = keylen // 8 - len(digest) - 3
             digest = chr(0) + chr(1) + chr(0xFF) * padlen + chr(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
@@ -372,3 +408,21 @@ try:
 except ImportError:
     validate = _need_pycrypto
     validate_rrsig = _need_pycrypto
+
+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 b997b176b12fd5fe29808304a0c8dddec5217c61..f1d3b5294ad89044a0e7047e8840aa86686325c6 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):
 
     def testAbsoluteRSAGood(self):
@@ -143,6 +177,27 @@ class DNSSECValidatorTestCase(unittest.TestCase):
         ds = dns.dnssec.make_ds(abs_example, example_sep_key, 'SHA256')
         self.failUnless(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: