test_pycrypto.py 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. # -*- coding: utf-8 -*-
  2. from __future__ import absolute_import, print_function, unicode_literals
  3. import re
  4. import salt.utils.platform
  5. import salt.utils.pycrypto
  6. from salt.exceptions import SaltInvocationError
  7. from tests.support.mock import patch
  8. from tests.support.unit import TestCase, skipIf
  9. class PycryptoTestCase(TestCase):
  10. """
  11. TestCase for salt.utils.pycrypto module
  12. """
  13. passwd = "test_password"
  14. expecteds = {
  15. "sha512": {
  16. "hashed": "$6$rounds=65601$goodsalt$lZFhiN5M8RTLd9WKDin50H4lF4F8HGMIdwvKs.nTG7f8F0Y4P447Zb9/E8SkUWjY.K10QT3NuHZNDgc/P/NjT1",
  17. "salt": "rounds=65601$goodsalt",
  18. "badsalt": "badsalt",
  19. },
  20. "sha256": {
  21. "hashed": "$5$rounds=53501$goodsalt$W.uoco0wMfGLDOlsbW52E6raFS1Nhj0McfUTj2vORt7",
  22. "salt": "rounds=53501$goodsalt",
  23. "badsalt": "badsalt",
  24. },
  25. "blowfish": {
  26. "hashed": "$2b$10$goodsaltgoodsaltgoodsObFfGrJwfV.13QddrZIh2w1ccESmvj8K",
  27. "salt": "10$goodsaltgoodsaltgoodsa",
  28. "badsalt": "badsaltbadsaltbadsaltb",
  29. },
  30. "md5": {
  31. "hashed": "$1$goodsalt$4XQMx4a4e1MpBB8xzz.TQ0",
  32. "salt": "goodsalt",
  33. "badsalt": "badsalt",
  34. },
  35. "crypt": {"hashed": "goVHulDpuGA7w", "salt": "go", "badsalt": "ba"},
  36. }
  37. invalid_salt = "thissaltistoolong" * 10
  38. @skipIf(not salt.utils.pycrypto.HAS_CRYPT, "crypt not available")
  39. def test_gen_hash_crypt(self):
  40. """
  41. Test gen_hash with crypt library
  42. """
  43. methods = salt.utils.pycrypto.methods
  44. for algorithm in methods:
  45. expected = self.expecteds[algorithm]
  46. ret = salt.utils.pycrypto.gen_hash(
  47. crypt_salt=expected["salt"], password=self.passwd, algorithm=algorithm,
  48. )
  49. self.assertEqual(ret, expected["hashed"])
  50. ret = salt.utils.pycrypto.gen_hash(
  51. crypt_salt=expected["badsalt"],
  52. password=self.passwd,
  53. algorithm=algorithm,
  54. )
  55. self.assertNotEqual(ret, expected["hashed"])
  56. ret = salt.utils.pycrypto.gen_hash(
  57. crypt_salt=None, password=self.passwd, algorithm=algorithm
  58. )
  59. self.assertNotEqual(ret, expected["hashed"])
  60. # Assert it works without arguments passed
  61. self.assertIsNotNone(salt.utils.pycrypto.gen_hash())
  62. # Assert it works without algorithm passed
  63. default_algorithm = salt.utils.pycrypto.crypt.methods[0].name.lower()
  64. expected = self.expecteds[default_algorithm]
  65. ret = salt.utils.pycrypto.gen_hash(
  66. crypt_salt=expected["salt"], password=self.passwd,
  67. )
  68. self.assertEqual(ret, expected["hashed"])
  69. @skipIf(not salt.utils.pycrypto.HAS_PASSLIB, "passlib not available")
  70. @patch("salt.utils.pycrypto.methods", {})
  71. @patch("salt.utils.pycrypto.HAS_CRYPT", False)
  72. def test_gen_hash_passlib(self):
  73. """
  74. Test gen_hash with passlib
  75. """
  76. methods = salt.utils.pycrypto.known_methods
  77. for algorithm in methods:
  78. expected = self.expecteds[algorithm]
  79. ret = salt.utils.pycrypto.gen_hash(
  80. crypt_salt=expected["salt"], password=self.passwd, algorithm=algorithm,
  81. )
  82. self.assertEqual(ret, expected["hashed"])
  83. ret = salt.utils.pycrypto.gen_hash(
  84. crypt_salt=expected["badsalt"],
  85. password=self.passwd,
  86. algorithm=algorithm,
  87. )
  88. self.assertNotEqual(ret, expected["hashed"])
  89. ret = salt.utils.pycrypto.gen_hash(
  90. crypt_salt=None, password=self.passwd, algorithm=algorithm
  91. )
  92. self.assertNotEqual(ret, expected["hashed"])
  93. # Assert it works without arguments passed
  94. self.assertIsNotNone(salt.utils.pycrypto.gen_hash())
  95. # Assert it works without algorithm passed
  96. default_algorithm = salt.utils.pycrypto.known_methods[0]
  97. expected = self.expecteds[default_algorithm]
  98. if default_algorithm in self.expecteds:
  99. ret = salt.utils.pycrypto.gen_hash(
  100. crypt_salt=expected["salt"], password=self.passwd
  101. )
  102. self.assertEqual(ret, expected["hashed"])
  103. @patch("salt.utils.pycrypto.HAS_CRYPT", False)
  104. @patch("salt.utils.pycrypto.HAS_PASSLIB", False)
  105. def test_gen_hash_no_lib(self):
  106. """
  107. test gen_hash with no crypt library available
  108. """
  109. with self.assertRaises(SaltInvocationError):
  110. salt.utils.pycrypto.gen_hash()
  111. @patch("salt.utils.pycrypto.HAS_CRYPT", True)
  112. @patch("salt.utils.pycrypto.methods", {"crypt": None})
  113. @patch("salt.utils.pycrypto.HAS_PASSLIB", True)
  114. def test_gen_hash_selection(self):
  115. """
  116. verify the hash backend selection works correctly
  117. """
  118. with patch("salt.utils.pycrypto._gen_hash_crypt", autospec=True) as gh_crypt:
  119. with patch(
  120. "salt.utils.pycrypto._gen_hash_passlib", autospec=True
  121. ) as gh_passlib:
  122. with self.assertRaises(SaltInvocationError):
  123. salt.utils.pycrypto.gen_hash(algorithm="doesntexist")
  124. salt.utils.pycrypto.gen_hash(algorithm="crypt")
  125. gh_crypt.assert_called_once()
  126. gh_passlib.assert_not_called()
  127. gh_crypt.reset_mock()
  128. salt.utils.pycrypto.gen_hash(algorithm="sha512")
  129. gh_crypt.assert_not_called()
  130. gh_passlib.assert_called_once()
  131. def test_gen_hash_crypt_warning(self):
  132. """
  133. Verify that a bad crypt salt triggers a warning
  134. """
  135. with patch("salt.utils.pycrypto.log", autospec=True) as log:
  136. try:
  137. salt.utils.pycrypto.gen_hash(
  138. crypt_salt="toolong", password=self.passwd, algorithm="crypt"
  139. )
  140. except Exception: # pylint: disable=broad-except
  141. pass
  142. log.warning.assert_called_with("Hash salt is too long for 'crypt' hash.")
  143. def test_secure_password(self):
  144. """
  145. test secure_password
  146. """
  147. ret = salt.utils.pycrypto.secure_password()
  148. check = re.compile(r"[!@#$%^&*()_=+]")
  149. self.assertIsNone(check.search(ret))
  150. self.assertTrue(ret)