cmd_keys.py 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. #!/usr/bin/env python3
  2. # pylint: disable=import-outside-toplevel
  3. """Test key management commands."""
  4. import os
  5. from testlib import check, util
  6. from testlib.log import log
  7. from testlib.feature import Feature
  8. from testlib.proc import Tinc
  9. from testlib.test import Test
  10. def init(ctx: Test) -> Tinc:
  11. """Initialize a node."""
  12. node = ctx.node()
  13. stdin = f"""
  14. init {node}
  15. set Port 0
  16. set Address localhost
  17. set DeviceType dummy
  18. """
  19. node.cmd(stdin=stdin)
  20. return node
  21. TEST_DATA = b"foo bar baz"
  22. def try_rsa_keys(priv_path: str, pub_path: str) -> None:
  23. """Check that RSA key pair works."""
  24. try:
  25. import cryptography # type: ignore
  26. from cryptography.hazmat.primitives import hashes, serialization # type: ignore
  27. from cryptography.hazmat.primitives.asymmetric import padding # type: ignore
  28. except ImportError:
  29. log.info("cryptography module missing or broken, skipping key checks")
  30. return
  31. version = cryptography.__version__.split(".", maxsplit=2)
  32. if not (int(version[0]) >= 3 and int(version[1]) >= 3):
  33. log.info("cryptography module is too old, skipping key check")
  34. return
  35. log.info("loading keys from (%s, %s)", priv_path, pub_path)
  36. with open(priv_path, "rb") as priv, open(pub_path, "rb") as pub:
  37. key_pair = (
  38. serialization.load_pem_private_key(priv.read(), password=None),
  39. serialization.load_pem_public_key(pub.read()),
  40. )
  41. s_pad = padding.PSS(
  42. mgf=padding.MGF1(hashes.SHA256()), salt_length=padding.PSS.MAX_LENGTH
  43. )
  44. s_hash = hashes.SHA256()
  45. log.info("signing sample data %s", TEST_DATA)
  46. signature = key_pair[0].sign(TEST_DATA, s_pad, s_hash)
  47. log.info("verifying signature %s", signature)
  48. key_pair[1].verify(signature, TEST_DATA, s_pad, s_hash)
  49. def test_rsa(foo: Tinc) -> None:
  50. """Test command 'generate-rsa-keys'."""
  51. for key_size in "foobar", "512", "16384":
  52. log.info("generate %s-bit RSA key", key_size)
  53. _, err = foo.cmd("generate-rsa-keys", key_size, code=1)
  54. check.is_in("Invalid key size", err)
  55. log.info("generate RSA key with too many arguments")
  56. _, err = foo.cmd("generate-rsa-keys", "2048", "4096", code=1)
  57. check.is_in("Too many arguments", err)
  58. rsa_priv = foo.sub("rsa_key.priv")
  59. rsa_pub = foo.sub(f"hosts/{foo}")
  60. for key_size in "1024", "1025":
  61. log.info("generate %s-bit RSA key", key_size)
  62. _, err = foo.cmd("generate-rsa-keys", key_size)
  63. check.is_in("Generating 1024 bits", err)
  64. check.is_in("generating a weak", err)
  65. check.is_in("found and disabled", err)
  66. try_rsa_keys(rsa_priv, rsa_pub)
  67. for key_size in "2048", "2049":
  68. log.info("generate %s-bit RSA key", key_size)
  69. os.remove(rsa_priv)
  70. _, err = foo.cmd("generate-rsa-keys", key_size)
  71. check.is_in("Generating 2048 bits", err)
  72. check.file_exists(rsa_priv)
  73. try_rsa_keys(rsa_priv, rsa_pub)
  74. log.info("check that key is present")
  75. key = util.read_text(rsa_priv)
  76. check.has_prefix(key, "-----BEGIN RSA PRIVATE KEY-----")
  77. if os.name != "nt":
  78. log.info("remove access to private key")
  79. os.chmod(rsa_priv, 0)
  80. _, err = foo.cmd("generate-rsa-keys", "1024", code=1)
  81. check.is_in("Error opening file", err)
  82. def test_rsa_nolegacy(foo: Tinc) -> None:
  83. """Test command 'generate-rsa-keys' on a nolegacy build."""
  84. log.info("generate RSA key with nolegacy tinc")
  85. _, err = foo.cmd("generate-rsa-keys", code=1)
  86. check.is_in("Unknown command", err)
  87. def test_eddsa(foo: Tinc) -> None:
  88. """Test command 'generate-ed25519-keys'."""
  89. log.info("generate EC key with too many arguments")
  90. _, err = foo.cmd("generate-ed25519-keys", "2048", code=1)
  91. check.is_in("Too many arguments", err)
  92. log.info("generate and replace EC key")
  93. _, err = foo.cmd("generate-ed25519-keys")
  94. check.is_in("found and disabled", err)
  95. log.info("remove EC key files")
  96. ec_priv = foo.sub("ed25519_key.priv")
  97. ec_pub = foo.sub(f"hosts/{foo}")
  98. os.remove(ec_priv)
  99. os.remove(ec_pub)
  100. log.info("create new EC key files")
  101. foo.cmd("generate-ed25519-keys")
  102. check.has_prefix(util.read_text(ec_priv), "-----BEGIN ED25519 PRIVATE KEY-----")
  103. check.has_prefix(util.read_text(ec_pub), "Ed25519PublicKey")
  104. if os.name != "nt":
  105. log.info("remove access to EC private key file")
  106. os.chmod(ec_priv, 0)
  107. _, err = foo.cmd("generate-ed25519-keys", code=1)
  108. check.is_in("Error opening file", err)
  109. def run_tests(foo: Tinc) -> None:
  110. """Run tests."""
  111. test_eddsa(foo)
  112. if Feature.LEGACY_PROTOCOL in foo.features:
  113. test_rsa(foo)
  114. else:
  115. test_rsa_nolegacy(foo)
  116. with Test("run tests") as context:
  117. run_tests(init(context))