test_crypt.py 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399
  1. import os
  2. import shutil
  3. import tempfile
  4. import salt.utils.files
  5. from salt import crypt
  6. from salt.ext import six
  7. from tests.support.helpers import slowTest
  8. from tests.support.mock import MagicMock, MockCall, mock_open, patch
  9. from tests.support.unit import TestCase, skipIf
  10. try:
  11. import M2Crypto
  12. HAS_M2 = True
  13. except ImportError:
  14. HAS_M2 = False
  15. try:
  16. from Cryptodome.PublicKey import RSA
  17. HAS_PYCRYPTO_RSA = True
  18. except ImportError:
  19. HAS_PYCRYPTO_RSA = False
  20. if not HAS_PYCRYPTO_RSA:
  21. try:
  22. from Crypto.PublicKey import RSA
  23. HAS_PYCRYPTO_RSA = True
  24. except ImportError:
  25. HAS_PYCRYPTO_RSA = False
  26. PRIVKEY_DATA = (
  27. "-----BEGIN RSA PRIVATE KEY-----\n"
  28. "MIIEpAIBAAKCAQEA75GR6ZTv5JOv90Vq8tKhKC7YQnhDIo2hM0HVziTEk5R4UQBW\n"
  29. "a0CKytFMbTONY2msEDwX9iA0x7F5Lgj0X8eD4ZMsYqLzqjWMekLC8bjhxc+EuPo9\n"
  30. "Dygu3mJ2VgRC7XhlFpmdo5NN8J2E7B/CNB3R4hOcMMZNZdi0xLtFoTfwU61UPfFX\n"
  31. "14mV2laqLbvDEfQLJhUTDeFFV8EN5Z4H1ttLP3sMXJvc3EvM0JiDVj4l1TWFUHHz\n"
  32. "eFgCA1Im0lv8i7PFrgW7nyMfK9uDSsUmIp7k6ai4tVzwkTmV5PsriP1ju88Lo3MB\n"
  33. "4/sUmDv/JmlZ9YyzTO3Po8Uz3Aeq9HJWyBWHAQIDAQABAoIBAGOzBzBYZUWRGOgl\n"
  34. "IY8QjTT12dY/ymC05GM6gMobjxuD7FZ5d32HDLu/QrknfS3kKlFPUQGDAbQhbbb0\n"
  35. "zw6VL5NO9mfOPO2W/3FaG1sRgBQcerWonoSSSn8OJwVBHMFLG3a+U1Zh1UvPoiPK\n"
  36. "S734swIM+zFpNYivGPvOm/muF/waFf8tF/47t1cwt/JGXYQnkG/P7z0vp47Irpsb\n"
  37. "Yjw7vPe4BnbY6SppSxscW3KoV7GtJLFKIxAXbxsuJMF/rYe3O3w2VKJ1Sug1VDJl\n"
  38. "/GytwAkSUer84WwP2b07Wn4c5pCnmLslMgXCLkENgi1NnJMhYVOnckxGDZk54hqP\n"
  39. "9RbLnkkCgYEA/yKuWEvgdzYRYkqpzB0l9ka7Y00CV4Dha9Of6GjQi9i4VCJ/UFVr\n"
  40. "UlhTo5y0ZzpcDAPcoZf5CFZsD90a/BpQ3YTtdln2MMCL/Kr3QFmetkmDrt+3wYnX\n"
  41. "sKESfsa2nZdOATRpl1antpwyD4RzsAeOPwBiACj4fkq5iZJBSI0bxrMCgYEA8GFi\n"
  42. "qAjgKh81/Uai6KWTOW2kX02LEMVRrnZLQ9VPPLGid4KZDDk1/dEfxjjkcyOxX1Ux\n"
  43. "Klu4W8ZEdZyzPcJrfk7PdopfGOfrhWzkREK9C40H7ou/1jUecq/STPfSOmxh3Y+D\n"
  44. "ifMNO6z4sQAHx8VaHaxVsJ7SGR/spr0pkZL+NXsCgYEA84rIgBKWB1W+TGRXJzdf\n"
  45. "yHIGaCjXpm2pQMN3LmP3RrcuZWm0vBt94dHcrR5l+u/zc6iwEDTAjJvqdU4rdyEr\n"
  46. "tfkwr7v6TNlQB3WvpWanIPyVzfVSNFX/ZWSsAgZvxYjr9ixw6vzWBXOeOb/Gqu7b\n"
  47. "cvpLkjmJ0wxDhbXtyXKhZA8CgYBZyvcQb+hUs732M4mtQBSD0kohc5TsGdlOQ1AQ\n"
  48. "McFcmbpnzDghkclyW8jzwdLMk9uxEeDAwuxWE/UEvhlSi6qdzxC+Zifp5NBc0fVe\n"
  49. "7lMx2mfJGxj5CnSqQLVdHQHB4zSXkAGB6XHbBd0MOUeuvzDPfs2voVQ4IG3FR0oc\n"
  50. "3/znuwKBgQChZGH3McQcxmLA28aUwOVbWssfXKdDCsiJO+PEXXlL0maO3SbnFn+Q\n"
  51. "Tyf8oHI5cdP7AbwDSx9bUfRPjg9dKKmATBFr2bn216pjGxK0OjYOCntFTVr0psRB\n"
  52. "CrKg52Qrq71/2l4V2NLQZU40Dr1bN9V+Ftd9L0pvpCAEAWpIbLXGDw==\n"
  53. "-----END RSA PRIVATE KEY-----"
  54. )
  55. PUBKEY_DATA = (
  56. "-----BEGIN PUBLIC KEY-----\n"
  57. "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA75GR6ZTv5JOv90Vq8tKh\n"
  58. "KC7YQnhDIo2hM0HVziTEk5R4UQBWa0CKytFMbTONY2msEDwX9iA0x7F5Lgj0X8eD\n"
  59. "4ZMsYqLzqjWMekLC8bjhxc+EuPo9Dygu3mJ2VgRC7XhlFpmdo5NN8J2E7B/CNB3R\n"
  60. "4hOcMMZNZdi0xLtFoTfwU61UPfFX14mV2laqLbvDEfQLJhUTDeFFV8EN5Z4H1ttL\n"
  61. "P3sMXJvc3EvM0JiDVj4l1TWFUHHzeFgCA1Im0lv8i7PFrgW7nyMfK9uDSsUmIp7k\n"
  62. "6ai4tVzwkTmV5PsriP1ju88Lo3MB4/sUmDv/JmlZ9YyzTO3Po8Uz3Aeq9HJWyBWH\n"
  63. "AQIDAQAB\n"
  64. "-----END PUBLIC KEY-----"
  65. )
  66. MSG = b"It's me, Mario"
  67. SIG = (
  68. b"\x07\xf3\xb1\xe7\xdb\x06\xf4_\xe2\xdc\xcb!F\xfb\xbex{W\x1d\xe4E"
  69. b"\xd3\r\xc5\x90\xca(\x05\x1d\x99\x8b\x1aug\x9f\x95>\x94\x7f\xe3+"
  70. b"\x12\xfa\x9c\xd4\xb8\x02]\x0e\xa5\xa3LL\xc3\xa2\x8f+\x83Z\x1b\x17"
  71. b'\xbfT\xd3\xc7\xfd\x0b\xf4\xd7J\xfe^\x86q"I\xa3x\xbc\xd3$\xe9M<\xe1'
  72. b"\x07\xad\xf2_\x9f\xfa\xf7g(~\xd8\xf5\xe7\xda-\xa3Ko\xfc.\x99\xcf"
  73. b"\x9b\xb9\xc1U\x97\x82'\xcb\xc6\x08\xaa\xa0\xe4\xd0\xc1+\xfc\x86"
  74. b'\r\xe4y\xb1#\xd3\x1dS\x96D28\xc4\xd5\r\xd4\x98\x1a44"\xd7\xc2\xb4'
  75. b"]\xa7\x0f\xa7Db\x85G\x8c\xd6\x94!\x8af1O\xf6g\xd7\x03\xfd\xb3\xbc"
  76. b"\xce\x9f\xe7\x015\xb8\x1d]AHK\xa0\x14m\xda=O\xa7\xde\xf2\xff\x9b"
  77. b"\x8e\x83\xc8j\x11\x1a\x98\x85\xde\xc5\x91\x07\x84!\x12^4\xcb\xa8"
  78. b"\x98\x8a\x8a&#\xb9(#?\x80\x15\x9eW\xb5\x12\xd1\x95S\xf2<G\xeb\xf1"
  79. b"\x14H\xb2\xc4>\xc3A\xed\x86x~\xcfU\xd5Q\xfe~\x10\xd2\x9b"
  80. )
  81. @skipIf(not HAS_PYCRYPTO_RSA, "pycrypto >= 2.6 is not available")
  82. @skipIf(HAS_M2, "m2crypto is used by salt.crypt if installed")
  83. class CryptTestCase(TestCase):
  84. @slowTest
  85. def test_gen_keys(self):
  86. open_priv_wb = MockCall("/keydir{}keyname.pem".format(os.sep), "wb+")
  87. open_pub_wb = MockCall("/keydir{}keyname.pub".format(os.sep), "wb+")
  88. with patch.multiple(
  89. os,
  90. umask=MagicMock(),
  91. chmod=MagicMock(),
  92. access=MagicMock(return_value=True),
  93. ):
  94. with patch("salt.utils.files.fopen", mock_open()) as m_open, patch(
  95. "os.path.isfile", return_value=True
  96. ):
  97. result = crypt.gen_keys("/keydir", "keyname", 2048)
  98. assert result == "/keydir{}keyname.pem".format(os.sep), result
  99. assert open_priv_wb not in m_open.calls
  100. assert open_pub_wb not in m_open.calls
  101. with patch("salt.utils.files.fopen", mock_open()) as m_open, patch(
  102. "os.path.isfile", return_value=False
  103. ):
  104. crypt.gen_keys("/keydir", "keyname", 2048)
  105. assert open_priv_wb in m_open.calls
  106. assert open_pub_wb in m_open.calls
  107. @patch("os.umask", MagicMock())
  108. @patch("os.chmod", MagicMock())
  109. @patch("os.chown", MagicMock(), create=True)
  110. @patch("os.access", MagicMock(return_value=True))
  111. @slowTest
  112. def test_gen_keys_with_passphrase(self):
  113. key_path = os.path.join(os.sep, "keydir")
  114. open_priv_wb = MockCall(os.path.join(key_path, "keyname.pem"), "wb+")
  115. open_pub_wb = MockCall(os.path.join(key_path, "keyname.pub"), "wb+")
  116. with patch("salt.utils.files.fopen", mock_open()) as m_open, patch(
  117. "os.path.isfile", return_value=True
  118. ):
  119. self.assertEqual(
  120. crypt.gen_keys(key_path, "keyname", 2048, passphrase="password"),
  121. os.path.join(key_path, "keyname.pem"),
  122. )
  123. result = crypt.gen_keys(key_path, "keyname", 2048, passphrase="password")
  124. assert result == os.path.join(key_path, "keyname.pem"), result
  125. assert open_priv_wb not in m_open.calls
  126. assert open_pub_wb not in m_open.calls
  127. with patch("salt.utils.files.fopen", mock_open()) as m_open, patch(
  128. "os.path.isfile", return_value=False
  129. ):
  130. crypt.gen_keys(key_path, "keyname", 2048)
  131. assert open_priv_wb in m_open.calls
  132. assert open_pub_wb in m_open.calls
  133. def test_sign_message(self):
  134. key = RSA.importKey(PRIVKEY_DATA)
  135. with patch("salt.crypt.get_rsa_key", return_value=key):
  136. self.assertEqual(SIG, salt.crypt.sign_message("/keydir/keyname.pem", MSG))
  137. def test_sign_message_with_passphrase(self):
  138. key = RSA.importKey(PRIVKEY_DATA)
  139. with patch("salt.crypt.get_rsa_key", return_value=key):
  140. self.assertEqual(
  141. SIG,
  142. crypt.sign_message("/keydir/keyname.pem", MSG, passphrase="password"),
  143. )
  144. def test_verify_signature(self):
  145. with patch("salt.utils.files.fopen", mock_open(read_data=PUBKEY_DATA)):
  146. self.assertTrue(crypt.verify_signature("/keydir/keyname.pub", MSG, SIG))
  147. @skipIf(not HAS_M2, "m2crypto is not available")
  148. class M2CryptTestCase(TestCase):
  149. @patch("os.umask", MagicMock())
  150. @patch("os.chmod", MagicMock())
  151. @patch("os.access", MagicMock(return_value=True))
  152. @slowTest
  153. def test_gen_keys(self):
  154. with patch("M2Crypto.RSA.RSA.save_pem", MagicMock()) as save_pem:
  155. with patch("M2Crypto.RSA.RSA.save_pub_key", MagicMock()) as save_pub:
  156. with patch("os.path.isfile", return_value=True):
  157. self.assertEqual(
  158. crypt.gen_keys("/keydir", "keyname", 2048),
  159. "/keydir{}keyname.pem".format(os.sep),
  160. )
  161. save_pem.assert_not_called()
  162. save_pub.assert_not_called()
  163. with patch("os.path.isfile", return_value=False):
  164. self.assertEqual(
  165. crypt.gen_keys("/keydir", "keyname", 2048),
  166. "/keydir{}keyname.pem".format(os.sep),
  167. )
  168. save_pem.assert_called_once_with(
  169. "/keydir{}keyname.pem".format(os.sep), cipher=None
  170. )
  171. save_pub.assert_called_once_with(
  172. "/keydir{}keyname.pub".format(os.sep)
  173. )
  174. @patch("os.umask", MagicMock())
  175. @patch("os.chmod", MagicMock())
  176. @patch("os.chown", MagicMock())
  177. @patch("os.access", MagicMock(return_value=True))
  178. @slowTest
  179. def test_gen_keys_with_passphrase(self):
  180. with patch("M2Crypto.RSA.RSA.save_pem", MagicMock()) as save_pem:
  181. with patch("M2Crypto.RSA.RSA.save_pub_key", MagicMock()) as save_pub:
  182. with patch("os.path.isfile", return_value=True):
  183. self.assertEqual(
  184. crypt.gen_keys(
  185. "/keydir", "keyname", 2048, passphrase="password"
  186. ),
  187. "/keydir{}keyname.pem".format(os.sep),
  188. )
  189. save_pem.assert_not_called()
  190. save_pub.assert_not_called()
  191. with patch("os.path.isfile", return_value=False):
  192. self.assertEqual(
  193. crypt.gen_keys(
  194. "/keydir", "keyname", 2048, passphrase="password"
  195. ),
  196. "/keydir{}keyname.pem".format(os.sep),
  197. )
  198. callback = save_pem.call_args[1]["callback"]
  199. save_pem.assert_called_once_with(
  200. "/keydir{}keyname.pem".format(os.sep),
  201. cipher="des_ede3_cbc",
  202. callback=callback,
  203. )
  204. self.assertEqual(callback(None), b"password")
  205. save_pub.assert_called_once_with(
  206. "/keydir{}keyname.pub".format(os.sep)
  207. )
  208. def test_sign_message(self):
  209. key = M2Crypto.RSA.load_key_string(six.b(PRIVKEY_DATA))
  210. with patch("salt.crypt.get_rsa_key", return_value=key):
  211. self.assertEqual(SIG, salt.crypt.sign_message("/keydir/keyname.pem", MSG))
  212. def test_sign_message_with_passphrase(self):
  213. key = M2Crypto.RSA.load_key_string(six.b(PRIVKEY_DATA))
  214. with patch("salt.crypt.get_rsa_key", return_value=key):
  215. self.assertEqual(
  216. SIG,
  217. crypt.sign_message("/keydir/keyname.pem", MSG, passphrase="password"),
  218. )
  219. def test_verify_signature(self):
  220. with patch("salt.utils.files.fopen", mock_open(read_data=six.b(PUBKEY_DATA))):
  221. self.assertTrue(crypt.verify_signature("/keydir/keyname.pub", MSG, SIG))
  222. def test_encrypt_decrypt_bin(self):
  223. priv_key = M2Crypto.RSA.load_key_string(six.b(PRIVKEY_DATA))
  224. pub_key = M2Crypto.RSA.load_pub_key_bio(
  225. M2Crypto.BIO.MemoryBuffer(six.b(PUBKEY_DATA))
  226. )
  227. encrypted = salt.crypt.private_encrypt(priv_key, b"salt")
  228. decrypted = salt.crypt.public_decrypt(pub_key, encrypted)
  229. self.assertEqual(b"salt", decrypted)
  230. class TestBadCryptodomePubKey(TestCase):
  231. """
  232. Test that we can load public keys exported by pycrpytodome<=3.4.6
  233. """
  234. TEST_KEY = (
  235. "-----BEGIN RSA PUBLIC KEY-----\n"
  236. "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzLtFhsvfbFDFaUgulSEX\n"
  237. "Gl12XriL1DT78Ef2/u8HHaSMmPie37BLWas/zaHwI6066bIyYQJ/nUCahTaoHM7L\n"
  238. "GlWc0wOU6zyfpihCRQHil05Y6F+olFBoZuYbFPvtp7/hJx/D7I/0n2o/c7M5i3Y2\n"
  239. "3sBxAYNooIQHXHUmPQW6C9iu95ylZDW8JQzYy/EI4vCC8yQMdTK8jK1FQV0Sbwny\n"
  240. "qcMxSyAWDoFbnhh2P2TnO8HOWuUOaXR8ZHOJzVcDl+a6ew+medW090x3K5O1f80D\n"
  241. "+WjgnG6b2HG7VQpOCfM2GALD/FrxicPilvZ38X1aLhJuwjmVE4LAAv8DVNJXohaO\n"
  242. "WQIDAQAB\n"
  243. "-----END RSA PUBLIC KEY-----\n"
  244. )
  245. def setUp(self):
  246. self.test_dir = tempfile.mkdtemp()
  247. self.key_path = os.path.join(self.test_dir, "cryptodom-3.4.6.pub")
  248. with salt.utils.files.fopen(self.key_path, "wb") as fd:
  249. fd.write(self.TEST_KEY.encode())
  250. def tearDown(self):
  251. shutil.rmtree(self.test_dir)
  252. @skipIf(not HAS_M2, "Skip when m2crypto is not installed")
  253. def test_m2_bad_key(self):
  254. """
  255. Load public key with an invalid header using m2crypto and validate it
  256. """
  257. key = salt.crypt.get_rsa_pub_key(self.key_path)
  258. assert key.check_key() == 1
  259. @skipIf(HAS_M2, "Skip when m2crypto is installed")
  260. def test_crypto_bad_key(self):
  261. """
  262. Load public key with an invalid header and validate it without m2crypto
  263. """
  264. key = salt.crypt.get_rsa_pub_key(self.key_path)
  265. assert key.can_encrypt()
  266. class TestM2CryptoRegression47124(TestCase):
  267. SIGNATURE = (
  268. b"w\xac\xfe18o\xeb\xfb\x14+\x9e\xd1\xb7\x7fe}\xec\xd6\xe1P\x9e\xab"
  269. b"\xb5\x07\xe0\xc1\xfd\xda#\x04Z\x8d\x7f\x0b\x1f}:~\xb2s\x860u\x02N"
  270. b'\xd4q"\xb7\x86*\x8f\x1f\xd0\x9d\x11\x92\xc5~\xa68\xac>\x12H\xc2%y,'
  271. b"\xe6\xceU\x1e\xa3?\x0c,\xf0u\xbb\xd0[g_\xdd\x8b\xb0\x95:Y\x18\xa5*"
  272. b"\x99\xfd\xf3K\x92\x92 ({\xd1\xff\xd9F\xc8\xd6K\x86e\xf9\xa8\xad\xb0z"
  273. b"\xe3\x9dD\xf5k\x8b_<\xe7\xe7\xec\xf3\"'\xd5\xd2M\xb4\xce\x1a\xe3$"
  274. b"\x9c\x81\xad\xf9\x11\xf6\xf5>)\xc7\xdd\x03&\xf7\x86@ks\xa6\x05\xc2"
  275. b"\xd0\xbd\x1a7\xfc\xde\xe6\xb0\xad!\x12#\xc86Y\xea\xc5\xe3\xe2\xb3"
  276. b"\xc9\xaf\xfa\x0c\xf2?\xbf\x93w\x18\x9e\x0b\xa2a\x10:M\x05\x89\xe2W.Q"
  277. b"\xe8;yGT\xb1\xf2\xc6A\xd2\xc4\xbeN\xb3\xcfS\xaf\x03f\xe2\xb4)\xe7\xf6"
  278. b'\xdbs\xd0Z}8\xa4\xd2\x1fW*\xe6\x1c"\x8b\xd0\x18w\xb9\x7f\x9e\x96\xa3'
  279. b"\xd9v\xf7\x833\x8e\x01"
  280. )
  281. @skipIf(not HAS_M2, "Skip when m2crypto is not installed")
  282. def test_m2crypto_verify_bytes(self):
  283. message = salt.utils.stringutils.to_unicode("meh")
  284. with patch("salt.utils.files.fopen", mock_open(read_data=six.b(PUBKEY_DATA))):
  285. salt.crypt.verify_signature("/keydir/keyname.pub", message, self.SIGNATURE)
  286. @skipIf(not HAS_M2, "Skip when m2crypto is not installed")
  287. def test_m2crypto_verify_unicode(self):
  288. message = salt.utils.stringutils.to_bytes("meh")
  289. with patch("salt.utils.files.fopen", mock_open(read_data=six.b(PUBKEY_DATA))):
  290. salt.crypt.verify_signature("/keydir/keyname.pub", message, self.SIGNATURE)
  291. @skipIf(not HAS_M2, "Skip when m2crypto is not installed")
  292. def test_m2crypto_sign_bytes(self):
  293. message = salt.utils.stringutils.to_unicode("meh")
  294. key = M2Crypto.RSA.load_key_string(six.b(PRIVKEY_DATA))
  295. with patch("salt.crypt.get_rsa_key", return_value=key):
  296. signature = salt.crypt.sign_message(
  297. "/keydir/keyname.pem", message, passphrase="password"
  298. )
  299. self.assertEqual(signature, self.SIGNATURE)
  300. @skipIf(not HAS_M2, "Skip when m2crypto is not installed")
  301. def test_m2crypto_sign_unicode(self):
  302. message = salt.utils.stringutils.to_bytes("meh")
  303. key = M2Crypto.RSA.load_key_string(six.b(PRIVKEY_DATA))
  304. with patch("salt.crypt.get_rsa_key", return_value=key):
  305. signature = salt.crypt.sign_message(
  306. "/keydir/keyname.pem", message, passphrase="password"
  307. )
  308. self.assertEqual(signature, self.SIGNATURE)
  309. @skipIf(
  310. not HAS_M2 and not HAS_PYCRYPTO_RSA,
  311. "No crypto library found. Install either M2Crypto or Cryptodome to run this test",
  312. )
  313. class TestCrypt(TestCase):
  314. def test_pwdata_decrypt(self):
  315. key_string = """-----BEGIN RSA PRIVATE KEY-----
  316. MIIEpQIBAAKCAQEAzhBRyyHa7b63RLE71uKMKgrpulcAJjaIaN68ltXcCvy4w9pi
  317. Kj+4I3Qp6RvUaHOEmymqyjOMjQc6iwpe0scCFqh3nUk5YYaLZ3WAW0htQVlnesgB
  318. ZiBg9PBeTQY/LzqtudL6RCng/AX+fbnCsddlIysRxnUoNVMvz0gAmCY2mnTDjcTt
  319. pyxuk2T0AHSHNCKCalm75L1bWDFF+UzFemf536tBfBUGRWR6jWTij85vvCntxHS/
  320. HdknaTJ50E7XGVzwBJpCyV4Y2VXuW/3KrCNTqXw+jTmEw0vlcshfDg/vb3IxsUSK
  321. 5KuHalKq/nUIc+F4QCJOl+A10goGdIfYC1/67QIDAQABAoIBAAOP+qoFWtCTZH22
  322. hq9PWVb8u0+yY1lFxhPyDdaZueUiu1r/coUCdv996Z+TEJgBr0AzdzVpsLtbbaKr
  323. ujnwoNOdc/vvISPTfKN8P4zUcrcXgZd4z7VhR+vUH/0652q8m/ZDdHorMy2IOP8Z
  324. cAk9DQ2PmA4TRm+tkX0G5KO8vWLsK921aRMWdsKJyQ0lYxl7M8JWupFsCJFr/U+8
  325. dAVtwnUiS7RnhBABZ1cfNTHYhXVAh4d+a9y/gZ00a66OGqPxiXfhjjDUZ6fGvWKN
  326. FlhKWEg6YqIx/H4aNXkLI5Rzzhdx/c2ukNm7+X2veRcAW7bcTwk8wxJxciEP5pBi
  327. 1el9VE0CgYEA/lbzdE2M4yRBvTfYYC6BqZcn+BqtrAUc2h3fEy+p7lwlet0af1id
  328. gWpYpOJyLc0AUfR616/m2y3PwEH/nMKDSTuU7o/qKNtlHW0nQcnhDCjTUydS3+J/
  329. JM3dhfgVqi03rjqNcgHA2eOEwcu/OBZtiaC0wqKbuRZRtfGffyoO3ssCgYEAz2iw
  330. wqu/NkA+MdQIxz/a3Is7gGwoFu6h7O+XU2uN8Y2++jSBw9AzzWj31YCvyjuJPAE+
  331. gxHm6yOnNoLVn423NtibHejhabzHNIK6UImH99bSTKabsxfF2BX6v982BimU1jwc
  332. bYykzws37oN/poPb5FTpEiAUrsd2bAMn/1S43icCgYEAulHkY0z0aumCpyUkA8HO
  333. BvjOtPiGRcAxFLBRXPLL3+vtIQachLHcIJRRf+jLkDXfiCo7W4pm6iWzTbqLkMEG
  334. AD3/qowPFAM1Hct6uL01efzmYsIp+g0o60NMhvnolRQu+Bm4yM30AyqjdHzYBjSX
  335. 5fyuru8EeSCal1j8aOHcpuUCgYEAhGhDH6Pg59NPYSQJjpm3MMA59hwV473n5Yh2
  336. xKyO6zwgRT6r8MPDrkhqnwQONT6Yt5PbwnT1Q/t4zhXsJnWkFwFk1U1MSeJYEa+7
  337. HZsPECs2CfT6xPRSO0ac00y+AmUdPT8WruDwfbSdukh8f2MCR9vlBsswKPvxH7dM
  338. G3aMplUCgYEAmMFgB/6Ox4OsQPPC6g4G+Ezytkc4iVkMEcjiVWzEsYATITjq3weO
  339. /XDGBYJoBhYwWPi9oBufFc/2pNtWy1FKKXPuVyXQATdA0mfEPbtsHjMFQNZbeKnm
  340. 0na/SysSDCK3P+9ijlbjqLjMmPEmhJxGWTJ7khnTTkfre7/w9ZxJxi8=
  341. -----END RSA PRIVATE KEY-----"""
  342. pwdata = b"""\
  343. V\x80+b\xca\x06M\xb6\x12\xc6\xe8\xf2\xb5\xbb\xd8m\xc0\x97\x9a\xeb\xb9q\x19\xc3\
  344. \xcdi\xb84\x90\xaf\x12kT\xe2@u\xd6\xe8T\x89\xa3\xc7\xb2Y\xd1N\x00\xa9\xc0"\xbe\
  345. \xed\xb1\xc3\xb7^\xbf\xbd\x8b\x13\xd3/L\x1b\xa1`\xe2\xea\x03\x98\x82\xf3uS&|\
  346. \xe5\xd8J\xce\xfc\x97\x8d\x0b\x949\xc0\xbd^\xef\xc6\xfd\xce\xbb\x1e\xd0"(m\xe1\
  347. \x95\xfb\xc8/\x07\x93\xb8\xda\x8f\x99\xfe\xdc\xd5\xcb\xdb\xb2\xf11M\xdbD\xcf\
  348. \x95\x13p\r\xa4\x1c{\xd5\xdb\xc7\xe5\xaf\x95F\x97\xa9\x00p~\xb5\xec\xa4.\xd0\
  349. \xa4\xb4\xf4f\xcds,Y/\xa1:WF\xb8\xc7\x07\xaa\x0b<\'~\x1b$D9\xd4\x8d\xf0x\xc5\
  350. \xee\xa8:\xe6\x00\x10\xc5i\x11\xc7]C8\x05l\x8b\x9b\xc3\x83e\xf7y\xadi:0\xb4R\
  351. \x1a(\x04&yL8\x19s\n\x11\x81\xfd?\xfb2\x80Ll\xa1\xdc\xc9\xb6P\xca\x8d\'\x11\xc1\
  352. \x07\xa5\xa1\x058\xc7\xce\xbeb\x92\xbf\x0bL\xec\xdf\xc3M\x83\xfb$\xec\xd5\xf9\
  353. """
  354. self.assertEqual("1234", salt.crypt.pwdata_decrypt(key_string, pwdata))