Home | History | Annotate | Line # | Download | only in tools
      1 # Copyright (C) Internet Systems Consortium, Inc. ("ISC")
      2 #
      3 # SPDX-License-Identifier: MPL-2.0
      4 #
      5 # This Source Code Form is subject to the terms of the Mozilla Public
      6 # License, v. 2.0.  If a copy of the MPL was not distributed with this
      7 # file, you can obtain one at https://mozilla.org/MPL/2.0/.
      8 #
      9 # See the COPYRIGHT file distributed with this work for additional
     10 # information regarding copyright ownership.
     11 
     12 import os
     13 import subprocess
     14 
     15 import pytest
     16 
     17 import isctest
     18 from isctest.hypothesis.strategies import dns_names
     19 
     20 from hypothesis import strategies, given, settings
     21 
     22 pytest.importorskip("dns.dnssectypes")
     23 from dns.dnssectypes import NSEC3Hash
     24 import dns.dnssec
     25 
     26 NSEC3HASH = os.environ.get("NSEC3HASH")
     27 
     28 
     29 # test cases from RFC 5155, Appendix A
     30 @pytest.mark.parametrize(
     31     "domain,nsec3hash",
     32     [
     33         ("*.w.example", "R53BQ7CC2UVMUBFU5OCMM6PERS9TK9EN"),
     34         (
     35             "2t7b4g4vsa5smi47k61mv5bv1a22bojr.example",
     36             "KOHAR7MBB8DC2CE8A9QVL8HON4K53UHI",
     37         ),
     38         ("a.example", "35MTHGPGCU1QG68FAB165KLNSNK3DPVL"),
     39         ("ai.example", "GJEQE526PLBF1G8MKLP59ENFD789NJGI"),
     40         ("example", "0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM"),
     41         ("ns1.example", "2T7B4G4VSA5SMI47K61MV5BV1A22BOJR"),
     42         ("ns2.example", "Q04JKCEVQVMU85R014C7DKBA38O0JI5R"),
     43         ("w.example", "K8UDEMVP1J2F7EG6JEBPS17VP3N8I58H"),
     44         ("x.w.example", "B4UM86EGHHDS6NEA196SMVMLO4ORS995"),
     45         ("x.y.w.example", "2VPTU5TIMAMQTTGL4LUU9KG21E0AOR3S"),
     46         ("xx.example", "T644EBQK9BIBCNA874GIVR6JOJ62MLHV"),
     47         ("y.w.example", "JI6NEOAEPV8B5O6K4EV33ABHA8HT9FGC"),
     48     ],
     49 )
     50 def test_nsec3_hashes(domain, nsec3hash):
     51     salt = "aabbccdd"
     52     algorithm = "1"
     53     iterations = "12"
     54 
     55     cmd = isctest.run.cmd([NSEC3HASH, salt, algorithm, iterations, domain])
     56     assert nsec3hash in cmd.out
     57 
     58     flags = "0"
     59     cmd = isctest.run.cmd([NSEC3HASH, "-r", algorithm, flags, iterations, salt, domain])
     60     assert nsec3hash in cmd.out
     61 
     62 
     63 @pytest.mark.parametrize(
     64     "salt_emptiness_args",
     65     [
     66         [""],
     67         ["-"],
     68         ["--", ""],
     69         ["--", "-"],
     70     ],
     71 )
     72 def test_nsec3_empty_salt(salt_emptiness_args):
     73     algorithm = "1"
     74     iterations = "0"
     75     domain = "com"
     76 
     77     cmd = isctest.run.cmd(
     78         [NSEC3HASH] + salt_emptiness_args + [algorithm, iterations, domain]
     79     )
     80     assert "CK0POJMG874LJREF7EFN8430QVIT8BSM" in cmd.out
     81     assert "salt=-" in cmd.out
     82 
     83 
     84 @pytest.mark.parametrize(
     85     "salt_emptiness_arg",
     86     [
     87         "",
     88         "-",
     89     ],
     90 )
     91 def test_nsec3_empty_salt_r(salt_emptiness_arg):
     92     algorithm = "1"
     93     flags = "1"
     94     iterations = "0"
     95     domain = "com"
     96 
     97     cmd = isctest.run.cmd(
     98         [
     99             NSEC3HASH,
    100             "-r",
    101             algorithm,
    102             flags,
    103             iterations,
    104             salt_emptiness_arg,
    105             domain,
    106         ]
    107     )
    108     assert " - CK0POJMG874LJREF7EFN8430QVIT8BSM" in cmd.out
    109 
    110 
    111 @pytest.mark.parametrize(
    112     "args",
    113     [
    114         [""],  # missing arg
    115         ["two", "names"],  # extra arg
    116     ],
    117 )
    118 def test_nsec3_missing_args(args):
    119     with pytest.raises(subprocess.CalledProcessError):
    120         isctest.run.cmd([NSEC3HASH, "00", "1", "0"] + args)
    121 
    122 
    123 def test_nsec3_bad_option():
    124     with pytest.raises(subprocess.CalledProcessError):
    125         isctest.run.cmd([NSEC3HASH, "-?"])
    126 
    127 
    128 @given(
    129     domain=dns_names(),
    130     it=strategies.integers(min_value=0, max_value=65535),
    131     salt_bytes=strategies.binary(min_size=0, max_size=255),
    132 )
    133 def test_nsec3hash_acceptable_values(domain, it, salt_bytes) -> None:
    134     if not salt_bytes:
    135         salt_text = "-"
    136     else:
    137         salt_text = salt_bytes.hex()
    138     # calculate the hash using dnspython:
    139     hash1 = dns.dnssec.nsec3_hash(
    140         domain, salt=salt_bytes, iterations=it, algorithm=NSEC3Hash.SHA1
    141     )
    142 
    143     # calculate the hash using nsec3hash:
    144     cmd = isctest.run.cmd([NSEC3HASH, salt_text, "1", str(it), str(domain)])
    145     hash2 = cmd.out.partition(" ")[0]
    146 
    147     assert hash1 == hash2
    148 
    149 
    150 @settings(max_examples=5)
    151 @given(
    152     domain=dns_names(),
    153     it=strategies.integers(min_value=0, max_value=65535),
    154     salt_bytes=strategies.binary(min_size=256),
    155 )
    156 def test_nsec3hash_salt_too_long(domain, it, salt_bytes) -> None:
    157     salt_text = salt_bytes.hex()
    158     with pytest.raises(subprocess.CalledProcessError):
    159         isctest.run.cmd([NSEC3HASH, salt_text, "1", str(it), str(domain)])
    160 
    161 
    162 @settings(max_examples=5)
    163 @given(
    164     domain=dns_names(),
    165     it=strategies.integers(min_value=65536),
    166     salt_bytes=strategies.binary(min_size=0, max_size=255),
    167 )
    168 def test_nsec3hash_too_many_iterations(domain, it, salt_bytes) -> None:
    169     salt_text = salt_bytes.hex()
    170     with pytest.raises(subprocess.CalledProcessError):
    171         isctest.run.cmd([NSEC3HASH, salt_text, "1", str(it), str(domain)])
    172