summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBob Halley <halley@dnspython.org>2013-09-21 16:25:36 -0700
committerBob Halley <halley@dnspython.org>2013-09-21 16:25:36 -0700
commita9c362b858bcc2ee2370bc3d0a41f9a649262138 (patch)
tree6362b87e41e2052dd1ae79c7ee9ecb099a40c1bd
parent5797467e6da346666381687971707b47ac9e933d (diff)
downloaddnspython-a9c362b858bcc2ee2370bc3d0a41f9a649262138.tar.gz
Preliminary Elliptic Curve DNSSEC Validation (requires ecdsa module)
-rw-r--r--dns/dnssec.py58
-rw-r--r--tests/dnssec.py55
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: