您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 

450 行
15 KiB

  1. import os
  2. import sys
  3. import shutil
  4. import subprocess
  5. import pytest
  6. from binascii import unhexlify
  7. try:
  8. import unittest2 as unittest
  9. except ImportError:
  10. import unittest
  11. from .curves import (
  12. NIST192p,
  13. NIST224p,
  14. NIST256p,
  15. NIST384p,
  16. NIST521p,
  17. BRAINPOOLP160r1,
  18. SECP112r2,
  19. SECP128r1,
  20. )
  21. from .curves import curves
  22. from .ecdh import (
  23. ECDH,
  24. InvalidCurveError,
  25. InvalidSharedSecretError,
  26. NoKeyError,
  27. NoCurveError,
  28. )
  29. from .keys import SigningKey, VerifyingKey
  30. from .ellipticcurve import CurveEdTw
  31. if "--fast" in sys.argv: # pragma: no cover
  32. curves = [SECP112r2, SECP128r1]
  33. @pytest.mark.parametrize(
  34. "vcurve",
  35. curves,
  36. ids=[curve.name for curve in curves],
  37. )
  38. def test_ecdh_each(vcurve):
  39. if isinstance(vcurve.curve, CurveEdTw):
  40. pytest.skip("ECDH is not supported for Edwards curves")
  41. ecdh1 = ECDH(curve=vcurve)
  42. ecdh2 = ECDH(curve=vcurve)
  43. ecdh2.generate_private_key()
  44. ecdh1.load_received_public_key(ecdh2.get_public_key())
  45. ecdh2.load_received_public_key(ecdh1.generate_private_key())
  46. secret1 = ecdh1.generate_sharedsecret_bytes()
  47. secret2 = ecdh2.generate_sharedsecret_bytes()
  48. assert secret1 == secret2
  49. def test_ecdh_both_keys_present():
  50. key1 = SigningKey.generate(BRAINPOOLP160r1)
  51. key2 = SigningKey.generate(BRAINPOOLP160r1)
  52. ecdh1 = ECDH(BRAINPOOLP160r1, key1, key2.verifying_key)
  53. ecdh2 = ECDH(private_key=key2, public_key=key1.verifying_key)
  54. secret1 = ecdh1.generate_sharedsecret_bytes()
  55. secret2 = ecdh2.generate_sharedsecret_bytes()
  56. assert secret1 == secret2
  57. def test_ecdh_no_public_key():
  58. ecdh1 = ECDH(curve=NIST192p)
  59. with pytest.raises(NoKeyError):
  60. ecdh1.generate_sharedsecret_bytes()
  61. ecdh1.generate_private_key()
  62. with pytest.raises(NoKeyError):
  63. ecdh1.generate_sharedsecret_bytes()
  64. class TestECDH(unittest.TestCase):
  65. def test_load_key_from_wrong_curve(self):
  66. ecdh1 = ECDH()
  67. ecdh1.set_curve(NIST192p)
  68. key1 = SigningKey.generate(BRAINPOOLP160r1)
  69. with self.assertRaises(InvalidCurveError) as e:
  70. ecdh1.load_private_key(key1)
  71. self.assertIn("Curve mismatch", str(e.exception))
  72. def test_generate_without_curve(self):
  73. ecdh1 = ECDH()
  74. with self.assertRaises(NoCurveError) as e:
  75. ecdh1.generate_private_key()
  76. self.assertIn("Curve must be set", str(e.exception))
  77. def test_load_bytes_without_curve_set(self):
  78. ecdh1 = ECDH()
  79. with self.assertRaises(NoCurveError) as e:
  80. ecdh1.load_private_key_bytes(b"\x01" * 32)
  81. self.assertIn("Curve must be set", str(e.exception))
  82. def test_set_curve_from_received_public_key(self):
  83. ecdh1 = ECDH()
  84. key1 = SigningKey.generate(BRAINPOOLP160r1)
  85. ecdh1.load_received_public_key(key1.verifying_key)
  86. self.assertEqual(ecdh1.curve, BRAINPOOLP160r1)
  87. def test_ecdh_wrong_public_key_curve():
  88. ecdh1 = ECDH(curve=NIST192p)
  89. ecdh1.generate_private_key()
  90. ecdh2 = ECDH(curve=NIST256p)
  91. ecdh2.generate_private_key()
  92. with pytest.raises(InvalidCurveError):
  93. ecdh1.load_received_public_key(ecdh2.get_public_key())
  94. with pytest.raises(InvalidCurveError):
  95. ecdh2.load_received_public_key(ecdh1.get_public_key())
  96. ecdh1.public_key = ecdh2.get_public_key()
  97. ecdh2.public_key = ecdh1.get_public_key()
  98. with pytest.raises(InvalidCurveError):
  99. ecdh1.generate_sharedsecret_bytes()
  100. with pytest.raises(InvalidCurveError):
  101. ecdh2.generate_sharedsecret_bytes()
  102. def test_ecdh_invalid_shared_secret_curve():
  103. ecdh1 = ECDH(curve=NIST256p)
  104. ecdh1.generate_private_key()
  105. ecdh1.load_received_public_key(
  106. SigningKey.generate(NIST256p).get_verifying_key()
  107. )
  108. ecdh1.private_key.privkey.secret_multiplier = ecdh1.private_key.curve.order
  109. with pytest.raises(InvalidSharedSecretError):
  110. ecdh1.generate_sharedsecret_bytes()
  111. # https://github.com/scogliani/ecc-test-vectors/blob/master/ecdh_kat/secp192r1.txt
  112. # https://github.com/scogliani/ecc-test-vectors/blob/master/ecdh_kat/secp256r1.txt
  113. # https://github.com/coruus/nist-testvectors/blob/master/csrc.nist.gov/groups/STM/cavp/documents/components/ecccdhtestvectors/KAS_ECC_CDH_PrimitiveTest.txt
  114. @pytest.mark.parametrize(
  115. "curve,privatekey,pubkey,secret",
  116. [
  117. pytest.param(
  118. NIST192p,
  119. "f17d3fea367b74d340851ca4270dcb24c271f445bed9d527",
  120. "42ea6dd9969dd2a61fea1aac7f8e98edcc896c6e55857cc0"
  121. "dfbe5d7c61fac88b11811bde328e8a0d12bf01a9d204b523",
  122. "803d8ab2e5b6e6fca715737c3a82f7ce3c783124f6d51cd0",
  123. id="NIST192p-1",
  124. ),
  125. pytest.param(
  126. NIST192p,
  127. "56e853349d96fe4c442448dacb7cf92bb7a95dcf574a9bd5",
  128. "deb5712fa027ac8d2f22c455ccb73a91e17b6512b5e030e7"
  129. "7e2690a02cc9b28708431a29fb54b87b1f0c14e011ac2125",
  130. "c208847568b98835d7312cef1f97f7aa298283152313c29d",
  131. id="NIST192p-2",
  132. ),
  133. pytest.param(
  134. NIST192p,
  135. "c6ef61fe12e80bf56f2d3f7d0bb757394519906d55500949",
  136. "4edaa8efc5a0f40f843663ec5815e7762dddc008e663c20f"
  137. "0a9f8dc67a3e60ef6d64b522185d03df1fc0adfd42478279",
  138. "87229107047a3b611920d6e3b2c0c89bea4f49412260b8dd",
  139. id="NIST192p-3",
  140. ),
  141. pytest.param(
  142. NIST192p,
  143. "e6747b9c23ba7044f38ff7e62c35e4038920f5a0163d3cda",
  144. "8887c276edeed3e9e866b46d58d895c73fbd80b63e382e88"
  145. "04c5097ba6645e16206cfb70f7052655947dd44a17f1f9d5",
  146. "eec0bed8fc55e1feddc82158fd6dc0d48a4d796aaf47d46c",
  147. id="NIST192p-4",
  148. ),
  149. pytest.param(
  150. NIST192p,
  151. "beabedd0154a1afcfc85d52181c10f5eb47adc51f655047d",
  152. "0d045f30254adc1fcefa8a5b1f31bf4e739dd327cd18d594"
  153. "542c314e41427c08278a08ce8d7305f3b5b849c72d8aff73",
  154. "716e743b1b37a2cd8479f0a3d5a74c10ba2599be18d7e2f4",
  155. id="NIST192p-5",
  156. ),
  157. pytest.param(
  158. NIST192p,
  159. "cf70354226667321d6e2baf40999e2fd74c7a0f793fa8699",
  160. "fb35ca20d2e96665c51b98e8f6eb3d79113508d8bccd4516"
  161. "368eec0d5bfb847721df6aaff0e5d48c444f74bf9cd8a5a7",
  162. "f67053b934459985a315cb017bf0302891798d45d0e19508",
  163. id="NIST192p-6",
  164. ),
  165. pytest.param(
  166. NIST224p,
  167. "8346a60fc6f293ca5a0d2af68ba71d1dd389e5e40837942df3e43cbd",
  168. "af33cd0629bc7e996320a3f40368f74de8704fa37b8fab69abaae280"
  169. "882092ccbba7930f419a8a4f9bb16978bbc3838729992559a6f2e2d7",
  170. "7d96f9a3bd3c05cf5cc37feb8b9d5209d5c2597464dec3e9983743e8",
  171. id="NIST224p",
  172. ),
  173. pytest.param(
  174. NIST256p,
  175. "7d7dc5f71eb29ddaf80d6214632eeae03d9058af1fb6d22ed80badb62bc1a534",
  176. "700c48f77f56584c5cc632ca65640db91b6bacce3a4df6b42ce7cc838833d287"
  177. "db71e509e3fd9b060ddb20ba5c51dcc5948d46fbf640dfe0441782cab85fa4ac",
  178. "46fc62106420ff012e54a434fbdd2d25ccc5852060561e68040dd7778997bd7b",
  179. id="NIST256p-1",
  180. ),
  181. pytest.param(
  182. NIST256p,
  183. "38f65d6dce47676044d58ce5139582d568f64bb16098d179dbab07741dd5caf5",
  184. "809f04289c64348c01515eb03d5ce7ac1a8cb9498f5caa50197e58d43a86a7ae"
  185. "b29d84e811197f25eba8f5194092cb6ff440e26d4421011372461f579271cda3",
  186. "057d636096cb80b67a8c038c890e887d1adfa4195e9b3ce241c8a778c59cda67",
  187. id="NIST256p-2",
  188. ),
  189. pytest.param(
  190. NIST256p,
  191. "1accfaf1b97712b85a6f54b148985a1bdc4c9bec0bd258cad4b3d603f49f32c8",
  192. "a2339c12d4a03c33546de533268b4ad667debf458b464d77443636440ee7fec3"
  193. "ef48a3ab26e20220bcda2c1851076839dae88eae962869a497bf73cb66faf536",
  194. "2d457b78b4614132477618a5b077965ec90730a8c81a1c75d6d4ec68005d67ec",
  195. id="NIST256p-3",
  196. ),
  197. pytest.param(
  198. NIST256p,
  199. "207c43a79bfee03db6f4b944f53d2fb76cc49ef1c9c4d34d51b6c65c4db6932d",
  200. "df3989b9fa55495719b3cf46dccd28b5153f7808191dd518eff0c3cff2b705ed"
  201. "422294ff46003429d739a33206c8752552c8ba54a270defc06e221e0feaf6ac4",
  202. "96441259534b80f6aee3d287a6bb17b5094dd4277d9e294f8fe73e48bf2a0024",
  203. id="NIST256p-4",
  204. ),
  205. pytest.param(
  206. NIST256p,
  207. "59137e38152350b195c9718d39673d519838055ad908dd4757152fd8255c09bf",
  208. "41192d2813e79561e6a1d6f53c8bc1a433a199c835e141b05a74a97b0faeb922"
  209. "1af98cc45e98a7e041b01cf35f462b7562281351c8ebf3ffa02e33a0722a1328",
  210. "19d44c8d63e8e8dd12c22a87b8cd4ece27acdde04dbf47f7f27537a6999a8e62",
  211. id="NIST256p-5",
  212. ),
  213. pytest.param(
  214. NIST256p,
  215. "f5f8e0174610a661277979b58ce5c90fee6c9b3bb346a90a7196255e40b132ef",
  216. "33e82092a0f1fb38f5649d5867fba28b503172b7035574bf8e5b7100a3052792"
  217. "f2cf6b601e0a05945e335550bf648d782f46186c772c0f20d3cd0d6b8ca14b2f",
  218. "664e45d5bba4ac931cd65d52017e4be9b19a515f669bea4703542a2c525cd3d3",
  219. id="NIST256p-6",
  220. ),
  221. pytest.param(
  222. NIST384p,
  223. "3cc3122a68f0d95027ad38c067916ba0eb8c38894d22e1b1"
  224. "5618b6818a661774ad463b205da88cf699ab4d43c9cf98a1",
  225. "a7c76b970c3b5fe8b05d2838ae04ab47697b9eaf52e76459"
  226. "2efda27fe7513272734466b400091adbf2d68c58e0c50066"
  227. "ac68f19f2e1cb879aed43a9969b91a0839c4c38a49749b66"
  228. "1efedf243451915ed0905a32b060992b468c64766fc8437a",
  229. "5f9d29dc5e31a163060356213669c8ce132e22f57c9a04f4"
  230. "0ba7fcead493b457e5621e766c40a2e3d4d6a04b25e533f1",
  231. id="NIST384p",
  232. ),
  233. pytest.param(
  234. NIST521p,
  235. "017eecc07ab4b329068fba65e56a1f8890aa935e57134ae0ffcce802735151f4ea"
  236. "c6564f6ee9974c5e6887a1fefee5743ae2241bfeb95d5ce31ddcb6f9edb4d6fc47",
  237. "00685a48e86c79f0f0875f7bc18d25eb5fc8c0b07e5da4f4370f3a949034085433"
  238. "4b1e1b87fa395464c60626124a4e70d0f785601d37c09870ebf176666877a2046d"
  239. "01ba52c56fc8776d9e8f5db4f0cc27636d0b741bbe05400697942e80b739884a83"
  240. "bde99e0f6716939e632bc8986fa18dccd443a348b6c3e522497955a4f3c302f676",
  241. "005fc70477c3e63bc3954bd0df3ea0d1f41ee21746ed95fc5e1fdf90930d5e1366"
  242. "72d72cc770742d1711c3c3a4c334a0ad9759436a4d3c5bf6e74b9578fac148c831",
  243. id="NIST521p",
  244. ),
  245. ],
  246. )
  247. def test_ecdh_NIST(curve, privatekey, pubkey, secret):
  248. ecdh = ECDH(curve=curve)
  249. ecdh.load_private_key_bytes(unhexlify(privatekey))
  250. ecdh.load_received_public_key_bytes(unhexlify(pubkey))
  251. sharedsecret = ecdh.generate_sharedsecret_bytes()
  252. assert sharedsecret == unhexlify(secret)
  253. pem_local_private_key = (
  254. "-----BEGIN EC PRIVATE KEY-----\n"
  255. "MF8CAQEEGF7IQgvW75JSqULpiQQ8op9WH6Uldw6xxaAKBggqhkjOPQMBAaE0AzIA\n"
  256. "BLiBd9CE7xf15FY5QIAoNg+fWbSk1yZOYtoGUdzkejWkxbRc9RWTQjqLVXucIJnz\n"
  257. "bA==\n"
  258. "-----END EC PRIVATE KEY-----\n"
  259. )
  260. der_local_private_key = (
  261. "305f02010104185ec8420bd6ef9252a942e989043ca29f561fa525770eb1c5a00a06082a864"
  262. "8ce3d030101a13403320004b88177d084ef17f5e45639408028360f9f59b4a4d7264e62da06"
  263. "51dce47a35a4c5b45cf51593423a8b557b9c2099f36c"
  264. )
  265. pem_remote_public_key = (
  266. "-----BEGIN PUBLIC KEY-----\n"
  267. "MEkwEwYHKoZIzj0CAQYIKoZIzj0DAQEDMgAEuIF30ITvF/XkVjlAgCg2D59ZtKTX\n"
  268. "Jk5i2gZR3OR6NaTFtFz1FZNCOotVe5wgmfNs\n"
  269. "-----END PUBLIC KEY-----\n"
  270. )
  271. der_remote_public_key = (
  272. "3049301306072a8648ce3d020106082a8648ce3d03010103320004b88177d084ef17f5e4563"
  273. "9408028360f9f59b4a4d7264e62da0651dce47a35a4c5b45cf51593423a8b557b9c2099f36c"
  274. )
  275. gshared_secret = "8f457e34982478d1c34b9cd2d0c15911b72dd60d869e2cea"
  276. def test_ecdh_pem():
  277. ecdh = ECDH()
  278. ecdh.load_private_key_pem(pem_local_private_key)
  279. ecdh.load_received_public_key_pem(pem_remote_public_key)
  280. sharedsecret = ecdh.generate_sharedsecret_bytes()
  281. assert sharedsecret == unhexlify(gshared_secret)
  282. def test_ecdh_der():
  283. ecdh = ECDH()
  284. ecdh.load_private_key_der(unhexlify(der_local_private_key))
  285. ecdh.load_received_public_key_der(unhexlify(der_remote_public_key))
  286. sharedsecret = ecdh.generate_sharedsecret_bytes()
  287. assert sharedsecret == unhexlify(gshared_secret)
  288. # Exception classes used by run_openssl.
  289. class RunOpenSslError(Exception):
  290. pass
  291. def run_openssl(cmd):
  292. OPENSSL = "openssl"
  293. p = subprocess.Popen(
  294. [OPENSSL] + cmd.split(),
  295. stdout=subprocess.PIPE,
  296. stderr=subprocess.STDOUT,
  297. )
  298. stdout, ignored = p.communicate()
  299. if p.returncode != 0:
  300. raise RunOpenSslError(
  301. "cmd '%s %s' failed: rc=%s, stdout/err was %s"
  302. % (OPENSSL, cmd, p.returncode, stdout)
  303. )
  304. return stdout.decode()
  305. OPENSSL_SUPPORTED_CURVES = set(
  306. c.split(":")[0].strip()
  307. for c in run_openssl("ecparam -list_curves").split("\n")
  308. )
  309. @pytest.mark.slow
  310. @pytest.mark.parametrize(
  311. "vcurve",
  312. curves,
  313. ids=[curve.name for curve in curves],
  314. )
  315. def test_ecdh_with_openssl(vcurve):
  316. if isinstance(vcurve.curve, CurveEdTw):
  317. pytest.skip("Edwards curves are not supported for ECDH")
  318. assert vcurve.openssl_name
  319. if vcurve.openssl_name not in OPENSSL_SUPPORTED_CURVES:
  320. pytest.skip("system openssl does not support " + vcurve.openssl_name)
  321. try:
  322. hlp = run_openssl("pkeyutl -help")
  323. if hlp.find("-derive") == 0: # pragma: no cover
  324. pytest.skip("system openssl does not support `pkeyutl -derive`")
  325. except RunOpenSslError: # pragma: no cover
  326. pytest.skip("system openssl could not be executed")
  327. if os.path.isdir("t"): # pragma: no branch
  328. shutil.rmtree("t")
  329. os.mkdir("t")
  330. run_openssl(
  331. "ecparam -name %s -genkey -out t/privkey1.pem" % vcurve.openssl_name
  332. )
  333. run_openssl(
  334. "ecparam -name %s -genkey -out t/privkey2.pem" % vcurve.openssl_name
  335. )
  336. run_openssl("ec -in t/privkey1.pem -pubout -out t/pubkey1.pem")
  337. ecdh1 = ECDH(curve=vcurve)
  338. ecdh2 = ECDH(curve=vcurve)
  339. with open("t/privkey1.pem") as e:
  340. key = e.read()
  341. ecdh1.load_private_key_pem(key)
  342. with open("t/privkey2.pem") as e:
  343. key = e.read()
  344. ecdh2.load_private_key_pem(key)
  345. with open("t/pubkey1.pem") as e:
  346. key = e.read()
  347. vk1 = VerifyingKey.from_pem(key)
  348. assert vk1.to_string() == ecdh1.get_public_key().to_string()
  349. vk2 = ecdh2.get_public_key()
  350. with open("t/pubkey2.pem", "wb") as e:
  351. e.write(vk2.to_pem())
  352. ecdh1.load_received_public_key(vk2)
  353. ecdh2.load_received_public_key(vk1)
  354. secret1 = ecdh1.generate_sharedsecret_bytes()
  355. secret2 = ecdh2.generate_sharedsecret_bytes()
  356. assert secret1 == secret2
  357. run_openssl(
  358. "pkeyutl -derive -inkey t/privkey1.pem -peerkey t/pubkey2.pem -out t/secret1"
  359. )
  360. run_openssl(
  361. "pkeyutl -derive -inkey t/privkey2.pem -peerkey t/pubkey1.pem -out t/secret2"
  362. )
  363. with open("t/secret1", "rb") as e:
  364. ssl_secret1 = e.read()
  365. with open("t/secret1", "rb") as e:
  366. ssl_secret2 = e.read()
  367. assert len(ssl_secret1) == vk1.curve.verifying_key_length // 2
  368. assert len(secret1) == vk1.curve.verifying_key_length // 2
  369. assert ssl_secret1 == ssl_secret2
  370. assert secret1 == ssl_secret1