diff options
author | Bob Halley <halley@dnspython.org> | 2013-09-21 16:25:36 -0700 |
---|---|---|
committer | Bob Halley <halley@dnspython.org> | 2013-09-21 16:25:36 -0700 |
commit | a9c362b858bcc2ee2370bc3d0a41f9a649262138 (patch) | |
tree | 6362b87e41e2052dd1ae79c7ee9ecb099a40c1bd | |
parent | 5797467e6da346666381687971707b47ac9e933d (diff) | |
download | dnspython-a9c362b858bcc2ee2370bc3d0a41f9a649262138.tar.gz |
Preliminary Elliptic Curve DNSSEC Validation (requires ecdsa module)
-rw-r--r-- | dns/dnssec.py | 58 | ||||
-rw-r--r-- | tests/dnssec.py | 55 |
2 files changed, 111 insertions, 2 deletions
diff --git a/dns/dnssec.py b/dns/dnssec.py index 084f908..edbe479 100644 --- a/dns/dnssec.py +++ b/dns/dnssec.py @@ -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 diff --git a/tests/dnssec.py b/tests/dnssec.py index b997b17..f1d3b52 100644 --- a/tests/dnssec.py +++ b/tests/dnssec.py @@ -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: |