2 # pylint: disable=import-outside-toplevel
4 """Test key management commands."""
8 from testlib import check, util
9 from testlib.log import log
10 from testlib.feature import Feature
11 from testlib.proc import Tinc
12 from testlib.test import Test
15 def init(ctx: Test) -> Tinc:
16 """Initialize a node."""
29 TEST_DATA = b"foo bar baz"
32 def try_rsa_keys(priv_path: str, pub_path: str) -> None:
33 """Check that RSA key pair works."""
36 import cryptography # type: ignore
37 from cryptography.hazmat.primitives import hashes, serialization # type: ignore
38 from cryptography.hazmat.primitives.asymmetric import padding # type: ignore
40 log.info("cryptography module missing or broken, skipping key checks")
43 version = cryptography.__version__.split(".", maxsplit=2)
44 if not (int(version[0]) >= 3 and int(version[1]) >= 3):
45 log.info("cryptography module is too old, skipping key check")
48 log.info("loading keys from (%s, %s)", priv_path, pub_path)
49 with open(priv_path, "rb") as priv, open(pub_path, "rb") as pub:
51 serialization.load_pem_private_key(priv.read(), password=None),
52 serialization.load_pem_public_key(pub.read()),
56 mgf=padding.MGF1(hashes.SHA256()), salt_length=padding.PSS.MAX_LENGTH
58 s_hash = hashes.SHA256()
60 log.info("signing sample data %s", TEST_DATA)
61 signature = key_pair[0].sign(TEST_DATA, s_pad, s_hash)
63 log.info("verifying signature %s", signature)
64 key_pair[1].verify(signature, TEST_DATA, s_pad, s_hash)
67 def test_rsa(foo: Tinc) -> None:
68 """Test command 'generate-rsa-keys'."""
70 for key_size in "foobar", "512", "16384":
71 log.info("generate %s-bit RSA key", key_size)
72 _, err = foo.cmd("generate-rsa-keys", key_size, code=1)
73 check.is_in("Invalid key size", err)
75 log.info("generate RSA key with too many arguments")
76 _, err = foo.cmd("generate-rsa-keys", "2048", "4096", code=1)
77 check.is_in("Too many arguments", err)
79 rsa_priv = foo.sub("rsa_key.priv")
80 rsa_pub = foo.sub(f"hosts/{foo}")
82 for key_size in "1024", "1025":
83 log.info("generate %s-bit RSA key", key_size)
84 _, err = foo.cmd("generate-rsa-keys", key_size)
85 check.is_in("Generating 1024 bits", err)
86 check.is_in("generating a weak", err)
87 check.is_in("found and disabled", err)
88 try_rsa_keys(rsa_priv, rsa_pub)
90 for key_size in "2048", "2049":
91 log.info("generate %s-bit RSA key", key_size)
93 _, err = foo.cmd("generate-rsa-keys", key_size)
94 check.is_in("Generating 2048 bits", err)
95 check.file_exists(rsa_priv)
96 try_rsa_keys(rsa_priv, rsa_pub)
98 log.info("check that key is present")
99 key = util.read_text(rsa_priv)
100 check.has_prefix(key, "-----BEGIN RSA PRIVATE KEY-----")
103 log.info("remove access to private key")
104 os.chmod(rsa_priv, 0)
105 _, err = foo.cmd("generate-rsa-keys", "1024", code=1)
106 check.is_in("Error opening file", err)
109 def test_rsa_nolegacy(foo: Tinc) -> None:
110 """Test command 'generate-rsa-keys' on a nolegacy build."""
112 log.info("generate RSA key with nolegacy tinc")
113 _, err = foo.cmd("generate-rsa-keys", code=1)
114 check.is_in("Unknown command", err)
117 def test_eddsa(foo: Tinc) -> None:
118 """Test command 'generate-ed25519-keys'."""
120 log.info("generate EC key with too many arguments")
121 _, err = foo.cmd("generate-ed25519-keys", "2048", code=1)
122 check.is_in("Too many arguments", err)
124 log.info("generate and replace EC key")
125 _, err = foo.cmd("generate-ed25519-keys")
126 check.is_in("found and disabled", err)
128 log.info("remove EC key files")
129 ec_priv = foo.sub("ed25519_key.priv")
130 ec_pub = foo.sub(f"hosts/{foo}")
134 log.info("create new EC key files")
135 foo.cmd("generate-ed25519-keys")
136 check.has_prefix(util.read_text(ec_priv), "-----BEGIN ED25519 PRIVATE KEY-----")
137 check.has_prefix(util.read_text(ec_pub), "Ed25519PublicKey")
140 log.info("remove access to EC private key file")
142 _, err = foo.cmd("generate-ed25519-keys", code=1)
143 check.is_in("Error opening file", err)
146 def run_tests(foo: Tinc) -> None:
151 if Feature.LEGACY_PROTOCOL in foo.features:
154 test_rsa_nolegacy(foo)
157 with Test("run tests") as context:
158 run_tests(init(context))