ed25519_exts_ref.py 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. #!/usr/bin/python
  2. # Copyright 2014-2017, The Tor Project, Inc
  3. # See LICENSE for licensing information
  4. """
  5. Reference implementations for the ed25519 tweaks that Tor uses.
  6. Includes self-tester and test vector generator.
  7. """
  8. import slow_ed25519
  9. from slow_ed25519 import *
  10. import os
  11. import random
  12. import slownacl_curve25519
  13. import unittest
  14. import binascii
  15. import textwrap
  16. #define a synonym that doesn't look like 1
  17. ell = l
  18. # This replaces expmod above and makes it go a lot faster.
  19. slow_ed25519.expmod = pow
  20. def curve25519ToEd25519(c, sign):
  21. u = decodeint(c)
  22. y = ((u - 1) * inv(u + 1)) % q
  23. x = xrecover(y)
  24. if x & 1 != sign: x = q-x
  25. return encodepoint([x,y])
  26. def blindESK(esk, param):
  27. h = H("Derive temporary signing key" + param)
  28. mult = 2**(b-2) + sum(2**i * bit(h,i) for i in range(3,b-2))
  29. s = decodeint(esk[:32])
  30. s_prime = (s * mult) % ell
  31. k = esk[32:]
  32. assert(len(k) == 32)
  33. k_prime = H("Derive temporary signing key hash input" + k)[:32]
  34. return encodeint(s_prime) + k_prime
  35. def blindPK(pk, param):
  36. h = H("Derive temporary signing key" + param)
  37. mult = 2**(b-2) + sum(2**i * bit(h,i) for i in range(3,b-2))
  38. P = decodepoint(pk)
  39. return encodepoint(scalarmult(P, mult))
  40. def expandSK(sk):
  41. h = H(sk)
  42. a = 2**(b-2) + sum(2**i * bit(h,i) for i in range(3,b-2))
  43. k = ''.join([h[i] for i in range(b/8,b/4)])
  44. assert len(k) == 32
  45. return encodeint(a)+k
  46. def publickeyFromESK(h):
  47. a = decodeint(h[:32])
  48. A = scalarmult(B,a)
  49. return encodepoint(A)
  50. def signatureWithESK(m,h,pk):
  51. a = decodeint(h[:32])
  52. r = Hint(''.join([h[i] for i in range(b/8,b/4)]) + m)
  53. R = scalarmult(B,r)
  54. S = (r + Hint(encodepoint(R) + pk + m) * a) % l
  55. return encodepoint(R) + encodeint(S)
  56. def newSK():
  57. return os.urandom(32)
  58. def random_scalar(entropy_f): # 0..L-1 inclusive
  59. # reduce the bias to a safe level by generating 256 extra bits
  60. oversized = int(binascii.hexlify(entropy_f(32+32)), 16)
  61. return oversized % ell
  62. # ------------------------------------------------------------
  63. MSG = "This is extremely silly. But it is also incredibly serious business!"
  64. class SelfTest(unittest.TestCase):
  65. def _testSignatures(self, esk, pk):
  66. sig = signatureWithESK(MSG, esk, pk)
  67. checkvalid(sig, MSG, pk)
  68. bad = False
  69. try:
  70. checkvalid(sig, MSG*2, pk)
  71. bad = True
  72. except Exception:
  73. pass
  74. self.failIf(bad)
  75. def testExpand(self):
  76. sk = newSK()
  77. pk = publickey(sk)
  78. esk = expandSK(sk)
  79. sig1 = signature(MSG, sk, pk)
  80. sig2 = signatureWithESK(MSG, esk, pk)
  81. self.assertEquals(sig1, sig2)
  82. def testSignatures(self):
  83. sk = newSK()
  84. esk = expandSK(sk)
  85. pk = publickeyFromESK(esk)
  86. pk2 = publickey(sk)
  87. self.assertEquals(pk, pk2)
  88. self._testSignatures(esk, pk)
  89. def testDerivation(self):
  90. priv = slownacl_curve25519.Private()
  91. pub = priv.get_public()
  92. ed_pub0 = publickeyFromESK(priv.private)
  93. sign = (ord(ed_pub0[31]) & 255) >> 7
  94. ed_pub1 = curve25519ToEd25519(pub.public, sign)
  95. self.assertEquals(ed_pub0, ed_pub1)
  96. def testBlinding(self):
  97. sk = newSK()
  98. esk = expandSK(sk)
  99. pk = publickeyFromESK(esk)
  100. param = os.urandom(32)
  101. besk = blindESK(esk, param)
  102. bpk = blindPK(pk, param)
  103. bpk2 = publickeyFromESK(besk)
  104. self.assertEquals(bpk, bpk2)
  105. self._testSignatures(besk, bpk)
  106. def testIdentity(self):
  107. # Base point:
  108. # B is the unique point (x, 4/5) \in E for which x is positive
  109. By = 4 * inv(5)
  110. Bx = xrecover(By)
  111. B = [Bx % q,By % q]
  112. # Get identity E by doing: E = l*B, where l is the group order
  113. identity = scalarmult(B, ell)
  114. # Get identity E by doing: E = l*A, where A is a random point
  115. sk = newSK()
  116. pk = decodepoint(publickey(sk))
  117. identity2 = scalarmult(pk, ell)
  118. # Check that identities match
  119. assert(identity == identity2)
  120. # Check that identity is the point (0,1)
  121. assert(identity == [0L,1L])
  122. # Check identity element: a*E = E, where a is a random scalar
  123. scalar = random_scalar(os.urandom)
  124. result = scalarmult(identity, scalar)
  125. assert(result == identity == identity2)
  126. # ------------------------------------------------------------
  127. # From pprint.pprint([ binascii.b2a_hex(os.urandom(32)) for _ in xrange(8) ])
  128. RAND_INPUTS = [
  129. '26c76712d89d906e6672dafa614c42e5cb1caac8c6568e4d2493087db51f0d36',
  130. 'fba7a5366b5cb98c2667a18783f5cf8f4f8d1a2ce939ad22a6e685edde85128d',
  131. '67e3aa7a14fac8445d15e45e38a523481a69ae35513c9e4143eb1c2196729a0e',
  132. 'd51385942033a76dc17f089a59e6a5a7fe80d9c526ae8ddd8c3a506b99d3d0a6',
  133. '5c8eac469bb3f1b85bc7cd893f52dc42a9ab66f1b02b5ce6a68e9b175d3bb433',
  134. 'eda433d483059b6d1ff8b7cfbd0fe406bfb23722c8f3c8252629284573b61b86',
  135. '4377c40431c30883c5fbd9bc92ae48d1ed8a47b81d13806beac5351739b5533d',
  136. 'c6bbcce615839756aed2cc78b1de13884dd3618f48367a17597a16c1cd7a290b']
  137. # From pprint.pprint([ binascii.b2a_hex(os.urandom(32)) for _ in xrange(8) ])
  138. BLINDING_PARAMS = [
  139. '54a513898b471d1d448a2f3c55c1de2c0ef718c447b04497eeb999ed32027823',
  140. '831e9b5325b5d31b7ae6197e9c7a7baf2ec361e08248bce055908971047a2347',
  141. 'ac78a1d46faf3bfbbdc5af5f053dc6dc9023ed78236bec1760dadfd0b2603760',
  142. 'f9c84dc0ac31571507993df94da1b3d28684a12ad14e67d0a068aba5c53019fc',
  143. 'b1fe79d1dec9bc108df69f6612c72812755751f21ecc5af99663b30be8b9081f',
  144. '81f1512b63ab5fb5c1711a4ec83d379c420574aedffa8c3368e1c3989a3a0084',
  145. '97f45142597c473a4b0e9a12d64561133ad9e1155fe5a9807fe6af8a93557818',
  146. '3f44f6a5a92cde816635dfc12ade70539871078d2ff097278be2a555c9859cd0']
  147. PREFIX = "ED25519_"
  148. def writeArray(name, array):
  149. print "static const char *{prefix}{name}[] = {{".format(
  150. prefix=PREFIX,name=name)
  151. for a in array:
  152. h = binascii.b2a_hex(a)
  153. if len(h) > 70:
  154. h1 = h[:70]
  155. h2 = h[70:]
  156. print ' "{0}"\n "{1}",'.format(h1,h2)
  157. else:
  158. print ' "{0}",'.format(h)
  159. print "};\n"
  160. def comment(text, initial="/**"):
  161. print initial
  162. print textwrap.fill(text,initial_indent=" * ",subsequent_indent=" * ")
  163. print " */"
  164. def makeTestVectors():
  165. comment("""Test vectors for our ed25519 implementation and related
  166. functions. These were automatically generated by the
  167. ed25519_exts_ref.py script.""", initial="/*")
  168. comment("""Secret key seeds used as inputs for the ed25519 test vectors.
  169. Randomly generated. """)
  170. secretKeys = [ binascii.a2b_hex(r) for r in RAND_INPUTS ]
  171. writeArray("SECRET_KEYS", secretKeys)
  172. comment("""Secret ed25519 keys after expansion from seeds. This is how Tor
  173. represents them internally.""")
  174. expandedSecretKeys = [ expandSK(sk) for sk in secretKeys ]
  175. writeArray("EXPANDED_SECRET_KEYS", expandedSecretKeys)
  176. comment("""Public keys derived from the above secret keys""")
  177. publicKeys = [ publickey(sk) for sk in secretKeys ]
  178. writeArray("PUBLIC_KEYS", publicKeys)
  179. comment("""The curve25519 public keys from which the ed25519 keys can be
  180. derived. Used to test our 'derive ed25519 from curve25519'
  181. code.""")
  182. writeArray("CURVE25519_PUBLIC_KEYS",
  183. (slownacl_curve25519.smult_curve25519_base(sk[:32])
  184. for sk in expandedSecretKeys))
  185. comment("""Parameters used for key blinding tests. Randomly generated.""")
  186. blindingParams = [ binascii.a2b_hex(r) for r in BLINDING_PARAMS ]
  187. writeArray("BLINDING_PARAMS", blindingParams)
  188. comment("""Blinded secret keys for testing key blinding. The nth blinded
  189. key corresponds to the nth secret key blidned with the nth
  190. blinding parameter.""")
  191. writeArray("BLINDED_SECRET_KEYS",
  192. (blindESK(expandSK(sk), bp)
  193. for sk,bp in zip(secretKeys,blindingParams)))
  194. comment("""Blinded public keys for testing key blinding. The nth blinded
  195. key corresponds to the nth public key blidned with the nth
  196. blinding parameter.""")
  197. writeArray("BLINDED_PUBLIC_KEYS",
  198. (blindPK(pk, bp) for pk,bp in zip(publicKeys,blindingParams)))
  199. comment("""Signatures of the public keys, made with their corresponding
  200. secret keys.""")
  201. writeArray("SELF_SIGNATURES",
  202. (signature(pk, sk, pk) for pk,sk in zip(publicKeys,secretKeys)))
  203. if __name__ == '__main__':
  204. import sys
  205. if len(sys.argv) == 1 or sys.argv[1] not in ("SelfTest", "MakeVectors"):
  206. print "You should specify one of 'SelfTest' or 'MakeVectors'"
  207. sys.exit(1)
  208. if sys.argv[1] == 'SelfTest':
  209. unittest.main()
  210. else:
  211. makeTestVectors()