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