From de9419ce4c893bc9706d73605b40758ff457743f Mon Sep 17 00:00:00 2001 From: Peter Thomassen Date: Mon, 18 Jan 2021 17:59:06 +0100 Subject: Ensure that DS digest length is consistent with digest type --- dns/rdtypes/dsbase.py | 18 ++++++++++++++++++ tests/test_dnssec.py | 30 ++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/dns/rdtypes/dsbase.py b/dns/rdtypes/dsbase.py index 38c9548..d125db2 100644 --- a/dns/rdtypes/dsbase.py +++ b/dns/rdtypes/dsbase.py @@ -24,6 +24,15 @@ import dns.rdata import dns.rdatatype +# Digest types registry: https://www.iana.org/assignments/ds-rr-types/ds-rr-types.xhtml +_digest_length_by_type = { + 1: 20, # SHA-1, RFC 3658 Sec. 2.4 + 2: 32, # SHA-256, RFC 4509 Sec. 2.2 + 3: 32, # GOST R 34.11-94, RFC 5933 Sec. 4 in conjunction with RFC 4490 Sec. 2.1 + 4: 48, # SHA-384, RFC 6605 Sec. 2 +} + + @dns.immutable.immutable class DSBase(dns.rdata.Rdata): @@ -39,6 +48,15 @@ class DSBase(dns.rdata.Rdata): self.digest_type = self._as_uint8(digest_type) self.digest = self._as_bytes(digest) + try: + if self.digest_type == 0: # reserved, RFC 3658 Sec. 2.4 + raise ValueError('digest type 0 is reserved') + expected_length = _digest_length_by_type[self.digest_type] + except KeyError: + raise ValueError('unknown digest type') + if len(self.digest) != expected_length: + raise ValueError('digest length inconsistent with digest type') + def to_text(self, origin=None, relativize=True, **kw): kw = kw.copy() chunksize = kw.pop('chunksize', 128) diff --git a/tests/test_dnssec.py b/tests/test_dnssec.py index ea82d7b..6ea51dc 100644 --- a/tests/test_dnssec.py +++ b/tests/test_dnssec.py @@ -497,5 +497,35 @@ class DNSSECMakeDSTestCase(unittest.TestCase): with self.assertRaises(dns.dnssec.UnsupportedAlgorithm): ds = dns.dnssec.make_ds(abs_example, example_sep_key, algorithm) + def testInvalidDigestType(self): # type: () -> None + digest_type_errors = { + 0: 'digest type 0 is reserved', + 5: 'unknown digest type', + } + for digest_type, msg in digest_type_errors.items(): + with self.assertRaises(dns.exception.SyntaxError) as cm: + dns.rdata.from_text(dns.rdataclass.IN, + dns.rdatatype.DS, + f'18673 3 {digest_type} 71b71d4f3e11bbd71b4eff12cde69f7f9215bbe7') + self.assertEqual(msg, str(cm.exception)) + + def testInvalidDigestLength(self): # type: () -> None + test_records = [] + for rdata in [example_ds_sha1, example_ds_sha256, example_ds_sha384]: + flags, digest = rdata.to_text().rsplit(' ', 1) + + # Make sure the construction is working + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.DS, f'{flags} {digest}') + + test_records.append(f'{flags} {digest[:len(digest)//2]}') # too short digest + test_records.append(f'{flags} {digest*2}') # too long digest + + for record in test_records: + with self.assertRaises(dns.exception.SyntaxError) as cm: + dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.DS, record) + + self.assertEqual('digest length inconsistent with digest type', str(cm.exception)) + + if __name__ == '__main__': unittest.main() -- cgit v1.2.1