test_ECC_Curve25519.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. # ===================================================================
  2. #
  3. # Copyright (c) 2024, Helder Eijs <helderijs@gmail.com>
  4. # All rights reserved.
  5. #
  6. # Redistribution and use in source and binary forms, with or without
  7. # modification, are permitted provided that the following conditions
  8. # are met:
  9. #
  10. # 1. Redistributions of source code must retain the above copyright
  11. # notice, this list of conditions and the following disclaimer.
  12. # 2. Redistributions in binary form must reproduce the above copyright
  13. # notice, this list of conditions and the following disclaimer in
  14. # the documentation and/or other materials provided with the
  15. # distribution.
  16. #
  17. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  18. # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  19. # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  20. # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  21. # COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  22. # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  23. # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  24. # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  25. # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  26. # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  27. # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  28. # POSSIBILITY OF SUCH DAMAGE.
  29. # ===================================================================
  30. import unittest
  31. from binascii import unhexlify
  32. from Crypto.SelfTest.st_common import list_test_cases
  33. from Crypto.Math.Numbers import Integer
  34. from Crypto.Hash import SHAKE128
  35. from Crypto.PublicKey import ECC
  36. from Crypto.PublicKey.ECC import EccKey, EccXPoint, _curves
  37. # Test vectors for scalar multiplication using point with X=9 as base
  38. # generated with nickovs' Python-only code https://gist.github.com/nickovs/cc3c22d15f239a2640c185035c06f8a3
  39. # The order is 0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed
  40. # Each tuple is (exponent, X-coordinate)
  41. scalar_base9_test = [
  42. (1, 9),
  43. (2, 0x20d342d51873f1b7d9750c687d1571148f3f5ced1e350b5c5cae469cdd684efb),
  44. (3, 0x1c12bc1a6d57abe645534d91c21bba64f8824e67621c0859c00a03affb713c12),
  45. (4, 0x79ce98b7e0689d7de7d1d074a15b315ffe1805dfcd5d2a230fee85e4550013ef),
  46. (6, 0x26954ccdc99ebf34f8f1dde5e6bb080685fec73640494c28f9fe0bfa8c794531),
  47. (9, 0x192b929197d07748db44600da41bab7499b1c2e6e2f87c6f0e337980668164ba),
  48. (129, 0x7332096a738900085e721103fce2cbf13aee50fef0788ea0d669008eb09ceab7),
  49. (255, 0x1534582fc2b1cea45e8cb776547e209da4fd54a9e473b50c5b8c6b0ae023a9b3),
  50. (256, 0x4300017536976a742ec8747f7505cd6bc80e610d669acab1a1eed36f680d98e8),
  51. (257, 0x6c410611cb484c9016adfb884d37a0e682e075daca1d46f45bb7a4afed10b125),
  52. (0x10101, 0xa679e9d7e043bf76c03362576e2c88abe9093c5d4f6b4a202c64a8397467cf),
  53. (0xAA55CC, 0x2cc02f84c067e3586f4278326689be163e606d69ccae505bb09488e11f295887),
  54. (0x1B29A0E579E0A000567, 0x50c38a72d7bfd7864c8b9083fa123e8d359068e6b491a019a885036e073f6604),
  55. (0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed + 1, 9),
  56. ]
  57. class TestEccPoint_Curve25519(unittest.TestCase):
  58. v1 = 0x09fa78b39b00a72930bcd8039be789a0997830bb99f79aeeb93493715390b4e8
  59. v2 = 0x15210f12786811d3f4b7959d0538ae2c31dbe7106fc03c3efc4cd549c715a493
  60. def test_init(self):
  61. EccXPoint(9, "curve25519")
  62. EccXPoint(2**255 - 19 + 5, "curve25519")
  63. def test_curve_attribute(self):
  64. point = EccXPoint(9, "curve25519")
  65. self.assertEqual(point.curve, "Curve25519")
  66. def test_init_fail(self):
  67. self.assertRaises(ValueError, EccXPoint, 3*(2**255 - 19), "curve25519")
  68. self.assertRaises(ValueError, EccXPoint, 9, "curve25518")
  69. def test_equal_set(self):
  70. point1 = EccXPoint(self.v1, "curve25519")
  71. point2 = EccXPoint(self.v2, "curve25519")
  72. self.assertEqual(point1, point1)
  73. self.assertNotEqual(point1, point2)
  74. point2.set(point1)
  75. self.assertEqual(point1.x, point2.x)
  76. def test_copy(self):
  77. point1 = EccXPoint(self.v1, "curve25519")
  78. point2 = point1.copy()
  79. self.assertEqual(point1.x, point2.x)
  80. def test_pai(self):
  81. point1 = EccXPoint(self.v1, "curve25519")
  82. pai = point1.point_at_infinity()
  83. self.assertTrue(pai.point_at_infinity())
  84. point2 = EccXPoint(None, "curve25519")
  85. self.assertTrue(point2.point_at_infinity())
  86. def test_scalar_multiply(self):
  87. base = EccXPoint(9, "curve25519")
  88. pointH = 0 * base
  89. self.assertTrue(pointH.point_at_infinity())
  90. pointH = 0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed * base
  91. self.assertTrue(pointH.point_at_infinity())
  92. pointH = base * 1
  93. self.assertEqual(pointH.x, 9)
  94. for d, result in scalar_base9_test:
  95. pointH = d * base
  96. self.assertEqual(pointH.x, result)
  97. def test_sizes(self):
  98. point = EccXPoint(9, "curve25519")
  99. self.assertEqual(point.size_in_bits(), 255)
  100. self.assertEqual(point.size_in_bytes(), 32)
  101. class TestEccKey_Curve25519(unittest.TestCase):
  102. def test_private_key(self):
  103. # RFC7748 Section 6.1 - Alice
  104. alice_priv = unhexlify("77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a")
  105. alice_pub = unhexlify("8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a")
  106. alice_pub_x = Integer.from_bytes(alice_pub, byteorder='little')
  107. key = EccKey(curve="Curve25519", seed=alice_priv)
  108. self.assertEqual(key.seed, alice_priv)
  109. self.assertTrue(key.has_private())
  110. self.assertEqual(key.pointQ.x, alice_pub_x)
  111. # RFC7748 Section 6.1 - Bob
  112. bob_priv = unhexlify("5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb")
  113. bob_pub = unhexlify("de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f")
  114. bob_pub_x = Integer.from_bytes(bob_pub, byteorder='little')
  115. key = EccKey(curve="Curve25519", seed=bob_priv)
  116. self.assertEqual(key.seed, bob_priv)
  117. self.assertTrue(key.has_private())
  118. self.assertEqual(key.pointQ.x, bob_pub_x)
  119. # Other names
  120. key = EccKey(curve="curve25519", seed=alice_priv)
  121. # Must not accept d parameter
  122. self.assertRaises(ValueError, EccKey, curve="curve25519", d=1)
  123. def test_public_key(self):
  124. point = EccXPoint(_curves['curve25519'].Gx,
  125. curve='curve25519')
  126. key = EccKey(curve="curve25519", point=point)
  127. self.assertFalse(key.has_private())
  128. self.assertEqual(key.pointQ, point)
  129. def test_public_key_derived(self):
  130. priv_key = EccKey(curve="curve25519", seed=b'H'*32)
  131. pub_key = priv_key.public_key()
  132. self.assertFalse(pub_key.has_private())
  133. self.assertEqual(priv_key.pointQ, pub_key.pointQ)
  134. def test_invalid_seed(self):
  135. self.assertRaises(ValueError, lambda: EccKey(curve="curve25519", seed=b'H' * 31))
  136. def test_equality(self):
  137. private_key = ECC.construct(seed=b'H'*32, curve="Curve25519")
  138. private_key2 = ECC.construct(seed=b'H'*32, curve="curve25519")
  139. private_key3 = ECC.construct(seed=b'C'*32, curve="Curve25519")
  140. public_key = private_key.public_key()
  141. public_key2 = private_key2.public_key()
  142. public_key3 = private_key3.public_key()
  143. self.assertEqual(private_key, private_key2)
  144. self.assertNotEqual(private_key, private_key3)
  145. self.assertEqual(public_key, public_key2)
  146. self.assertNotEqual(public_key, public_key3)
  147. self.assertNotEqual(public_key, private_key)
  148. def test_name_consistency(self):
  149. key = ECC.generate(curve='curve25519')
  150. self.assertIn("curve='Curve25519'", repr(key))
  151. self.assertEqual(key.curve, 'Curve25519')
  152. self.assertEqual(key.public_key().curve, 'Curve25519')
  153. class TestEccModule_Curve25519(unittest.TestCase):
  154. def test_generate(self):
  155. key = ECC.generate(curve="Curve25519")
  156. self.assertTrue(key.has_private())
  157. point = EccXPoint(_curves['Curve25519'].Gx, curve="Curve25519") * key.d
  158. self.assertEqual(key.pointQ, point)
  159. # Always random
  160. key2 = ECC.generate(curve="Curve25519")
  161. self.assertNotEqual(key, key2)
  162. # Other names
  163. ECC.generate(curve="curve25519")
  164. # Random source
  165. key1 = ECC.generate(curve="Curve25519", randfunc=SHAKE128.new().read)
  166. key2 = ECC.generate(curve="Curve25519", randfunc=SHAKE128.new().read)
  167. self.assertEqual(key1, key2)
  168. def test_construct(self):
  169. seed = unhexlify("77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a")
  170. point_hex = unhexlify("8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a")
  171. Px = Integer.from_bytes(point_hex, byteorder='little')
  172. point = EccXPoint(Px, curve="Curve25519")
  173. # Private key only
  174. key = ECC.construct(curve="Curve25519", seed=seed)
  175. self.assertEqual(key.pointQ, point)
  176. self.assertTrue(key.has_private())
  177. # Public key only
  178. key = ECC.construct(curve="Curve25519", point_x=Px)
  179. self.assertEqual(key.pointQ, point)
  180. self.assertFalse(key.has_private())
  181. # Private and public key
  182. key = ECC.construct(curve="Curve25519", seed=seed, point_x=Px)
  183. self.assertEqual(key.pointQ, point)
  184. self.assertTrue(key.has_private())
  185. # Other names
  186. key = ECC.construct(curve="curve25519", seed=seed)
  187. def test_negative_construct(self):
  188. coordG = dict(point_x=_curves['curve25519'].Gx)
  189. self.assertRaises(ValueError, ECC.construct, curve="Curve25519", d=2, **coordG)
  190. self.assertRaises(ValueError, ECC.construct, curve="Curve25519", seed=b'H'*31)
  191. # Verify you cannot construct weak keys (small-order points)
  192. self.assertRaises(ValueError, ECC.construct, curve="Curve25519",
  193. point_x=0)
  194. self.assertRaises(ValueError, ECC.construct, curve="Curve25519",
  195. point_x=1)
  196. self.assertRaises(ValueError, ECC.construct, curve="Curve25519",
  197. point_x=325606250916557431795983626356110631294008115727848805560023387167927233504)
  198. self.assertRaises(ValueError, ECC.construct, curve="Curve25519",
  199. point_x=39382357235489614581723060781553021112529911719440698176882885853963445705823)
  200. p = 2**255 - 19
  201. self.assertRaises(ValueError, ECC.construct, curve="Curve25519",
  202. point_x=p-1)
  203. self.assertRaises(ValueError, ECC.construct, curve="Curve25519",
  204. point_x=p)
  205. self.assertRaises(ValueError, ECC.construct, curve="Curve25519",
  206. point_x=p+1)
  207. self.assertRaises(ValueError, ECC.construct, curve="Curve25519",
  208. point_x=p+325606250916557431795983626356110631294008115727848805560023387167927233504)
  209. self.assertRaises(ValueError, ECC.construct, curve="Curve25519",
  210. point_x=p+39382357235489614581723060781553021112529911719440698176882885853963445705823)
  211. self.assertRaises(ValueError, ECC.construct, curve="Curve25519",
  212. point_x=p*2-1)
  213. self.assertRaises(ValueError, ECC.construct, curve="Curve25519",
  214. point_x=p*2)
  215. self.assertRaises(ValueError, ECC.construct, curve="Curve25519",
  216. point_x=p*2+1)
  217. def get_tests(config={}):
  218. tests = []
  219. tests += list_test_cases(TestEccPoint_Curve25519)
  220. tests += list_test_cases(TestEccKey_Curve25519)
  221. tests += list_test_cases(TestEccModule_Curve25519)
  222. return tests
  223. if __name__ == '__main__':
  224. def suite():
  225. return unittest.TestSuite(get_tests())
  226. unittest.main(defaultTest='suite')