Skip to content

Commit d9d2555

Browse files
authored
Open tsig key file (#1271)
* add tsigkeyring.from_file to use key file generate by tsig-keygen * add tsigkeyring.from_file to use key file generate by tsig-keygen
1 parent aa63dd8 commit d9d2555

7 files changed

Lines changed: 108 additions & 2 deletions

File tree

dns/tsigkeyring.py

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,46 @@
1818
"""A place to store TSIG keys."""
1919

2020
import base64
21+
import re
2122
from typing import Any
22-
23+
from pathlib import Path
2324
import dns.name
2425
import dns.tsig
2526

27+
TSIG_KEY_FILE_RE = re.compile(
28+
r"key\s*"
29+
r'(?:"([^"]*)"' # group 1: quoted name
30+
r"|([^{\s]+))" # group 2: unquoted name
31+
r"\s*\{\s*"
32+
r"(?:algorithm\s*" # --- algorithm block (optional) ---
33+
r'(?:"([^"]*)"' # group 3: quoted algo
34+
r"|([^;\s}]+))" # group 4: unquoted algo
35+
r"\s*;\s*)?" # --- end optional block ---
36+
r"secret\s*"
37+
r'(?:"([^"]*)"' # group 5: quoted secret
38+
r"|([^;\s}]+))" # group 6: unquoted secret
39+
r"\s*;?\s*\}?"
40+
)
41+
42+
43+
def from_file(key_file: str) -> dict[dns.name.Name, Any]:
44+
"""Open a tsig key file generate by tsig-keygen
45+
and parse to retrieve key, algo and secret
46+
@rtype: dict"""
47+
48+
content = Path(key_file).read_text().replace("\n", "").strip()
49+
50+
if m := TSIG_KEY_FILE_RE.match(content):
51+
key_name = m.group(1) or m.group(2)
52+
algorithm = m.group(3) or m.group(4)
53+
secret = m.group(5) or m.group(6)
54+
if algorithm:
55+
return from_text({key_name: (algorithm, secret)})
56+
else:
57+
return from_text({key_name: secret})
58+
else:
59+
raise Exception(f"Error while parsing {key_file}")
60+
2661

2762
def from_text(textring: dict[str, Any]) -> dict[dns.name.Name, Any]:
2863
"""Convert a dictionary containing (textual DNS name, base64 secret)

tests/test_tsigkeyring.py

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22

33
import base64
44
import unittest
5-
5+
import os
66
import dns.tsig
77
import dns.tsigkeyring
8+
import pytest
89

910
text_keyring = {"keyname.": ("hmac-sha256.", "NjHwPsMKjdN++dOfE5iAiQ==")}
1011

@@ -18,6 +19,39 @@
1819

1920
old_rich_keyring = {key.name: key.secret}
2021

22+
keyring_file_1 = {
23+
dns.name.from_text("aaa-test"): dns.tsig.Key(
24+
dns.name.from_text("aaa-test"),
25+
"WklIGweRJDBzgYJAfFAt3ln7NJAyz66W56/fpoeepVn5yqs3i3iiXFIS6GXpuFw0TyRfADBcnap8Vvl7TokfYA==",
26+
"hmac-sha512",
27+
)
28+
}
29+
30+
keyring_file_2 = {
31+
dns.name.from_text("b-key"): dns.tsig.Key(
32+
dns.name.from_text("b-key"),
33+
"KykTutSQNZYc+o8rqfMPH7ce2a9SruvKgPAqfn8EER8=",
34+
"hmac-sha256",
35+
)
36+
}
37+
38+
keyring_file_3 = {
39+
dns.name.from_text("example.com"): dns.tsig.Key(
40+
dns.name.from_text("example.com"),
41+
"WklIGweRJDBzgYJAfFAt3ln7NJAyz66W56/fpoeepVn5yqs3i3iiXFIS6GXpuFw0TyRfADBcnap8Vvl7TokfYA==",
42+
).secret
43+
}
44+
45+
keyring_file_4 = {
46+
dns.name.from_text("bh-"): dns.tsig.Key(
47+
dns.name.from_text("bh-"),
48+
"WklIGweRJDBzgYJAfFAt3ln7NJAyz66W56/fpoeepVn5yqs3i3iiXFIS6GXpuFw0TyRfADBcnap8Vvl7TokfYA==",
49+
"hmac-sha512",
50+
)
51+
}
52+
53+
TEST_DIR = os.path.dirname(os.path.abspath(__file__))
54+
2155

2256
class TSIGKeyRingTestCase(unittest.TestCase):
2357
def test_from_text(self):
@@ -56,3 +90,30 @@ def test_old_from_and_to_text(self):
5690
rkeyring = dns.tsigkeyring.from_text(old_text_keyring)
5791
tkeyring = dns.tsigkeyring.to_text(rkeyring)
5892
self.assertEqual(tkeyring, old_text_keyring)
93+
94+
def test_from_file_1(self):
95+
"""test to parse key file 1.key"""
96+
keyring = dns.tsigkeyring.from_file(os.path.join(TEST_DIR, "./tsigkeys/1.key"))
97+
self.assertEqual(keyring, keyring_file_1)
98+
99+
def test_from_file_2(self):
100+
"""test to parse key file 2.key"""
101+
keyring = dns.tsigkeyring.from_file(os.path.join(TEST_DIR, "./tsigkeys/2.key"))
102+
self.assertEqual(keyring, keyring_file_2)
103+
104+
def test_from_file_3(self):
105+
"""test to parse key file 3.key"""
106+
keyring = dns.tsigkeyring.from_file(os.path.join(TEST_DIR, "./tsigkeys/3.key"))
107+
self.assertEqual(keyring, keyring_file_3)
108+
109+
def test_from_file_4(self):
110+
"""test to parse key file 4.key"""
111+
keyring = dns.tsigkeyring.from_file(os.path.join(TEST_DIR, "./tsigkeys/4.key"))
112+
self.assertEqual(keyring, keyring_file_4)
113+
114+
def test_from_file_error(self):
115+
"""test to parse key file 5.key"""
116+
with pytest.raises(Exception):
117+
keyring = dns.tsigkeyring.from_file(
118+
os.path.join(TEST_DIR, "./tsigkeys/5.key")
119+
)

tests/tsigkeys/1.key

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
key "aaa-test" {algorithm hmac-sha512;secret "WklIGweRJDBzgYJAfFAt3ln7NJAyz66W56/fpoeepVn5yqs3i3iiXFIS6GXpuFw0TyRfADBcnap8Vvl7TokfYA==";};

tests/tsigkeys/2.key

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
key"b-key"{algorithm"hmac-sha256";secret"KykTutSQNZYc+o8rqfMPH7ce2a9SruvKgPAqfn8EER8=";};

tests/tsigkeys/3.key

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
key example.com {
2+
secret "WklIGweRJDBzgYJAfFAt3ln7NJAyz66W56/fpoeepVn5yqs3i3iiXFIS6GXpuFw0TyRfADBcnap8Vvl7TokfYA==";
3+
};

tests/tsigkeys/4.key

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
key bh- {
2+
algorithm hmac-sha512;
3+
secret "WklIGweRJDBzgYJAfFAt3ln7NJAyz66W56/fpoeepVn5yqs3i3iiXFIS6GXpuFw0TyRfADBcnap8Vvl7TokfYA==";
4+
};

tests/tsigkeys/5.key

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
key bh

0 commit comments

Comments
 (0)