2 # Copyright 2014-2019, The Tor Project, Inc
3 # See LICENSE for licensing information
6 Reference implementations for the ed25519 tweaks that Tor uses.
8 Includes self-tester and test vector generator.
11 # Future imports for Python 2.7, mandatory in 3.0
12 from __future__
import division
13 from __future__
import print_function
14 from __future__
import unicode_literals
17 from slow_ed25519
import *
21 import slownacl_curve25519
26 #define a synonym that doesn't look like 1
29 # This replaces expmod above and makes it go a lot faster.
30 slow_ed25519
.expmod
= pow
32 def curve25519ToEd25519(c
, sign
):
34 y
= ((u
- 1) * inv(u
+ 1)) % q
36 if x
& 1 != sign
: x
= q
-x
37 return encodepoint([x
,y
])
39 def blindESK(esk
, param
):
40 mult
= 2**(b
-2) + sum(2**i
* bit(param
,i
) for i
in range(3,b
-2))
41 s
= decodeint(esk
[:32])
42 s_prime
= (s
* mult
) % ell
45 k_prime
= H(b
"Derive temporary signing key hash input" + k
)[:32]
46 return encodeint(s_prime
) + k_prime
48 def blindPK(pk
, param
):
49 mult
= 2**(b
-2) + sum(2**i
* bit(param
,i
) for i
in range(3,b
-2))
51 return encodepoint(scalarmult(P
, mult
))
55 a
= 2**(b
-2) + sum(2**i
* bit(h
,i
) for i
in range(3,b
-2))
56 k
= bytes(h
[i
] for i
in range(b
//8,b
//4))
60 def publickeyFromESK(h
):
65 def signatureWithESK(m
,h
,pk
):
67 r
= Hint(bytes([h
[i
] for i
in range(b
//8,b
//4)]) + m
)
69 S
= (r
+ Hint(encodepoint(R
) + pk
+ m
) * a
) % l
70 return encodepoint(R
) + encodeint(S
)
75 def random_scalar(entropy_f
): # 0..L-1 inclusive
76 # reduce the bias to a safe level by generating 256 extra bits
77 oversized
= int(binascii
.hexlify(entropy_f(32+32)), 16)
78 return oversized
% ell
80 # ------------------------------------------------------------
82 MSG
= "This is extremely silly. But it is also incredibly serious business!"
84 class SelfTest(unittest
.TestCase
):
86 def _testSignatures(self
, esk
, pk
):
87 sig
= signatureWithESK(MSG
, esk
, pk
)
88 checkvalid(sig
, MSG
, pk
)
91 checkvalid(sig
, MSG
*2, pk
)
102 sig1
= signature(MSG
, sk
, pk
)
103 sig2
= signatureWithESK(MSG
, esk
, pk
)
104 self
.assertEquals(sig1
, sig2
)
106 def testSignatures(self
):
109 pk
= publickeyFromESK(esk
)
111 self
.assertEquals(pk
, pk2
)
113 self
._testSignatures
(esk
, pk
)
115 def testDerivation(self
):
116 priv
= slownacl_curve25519
.Private()
117 pub
= priv
.get_public()
119 ed_pub0
= publickeyFromESK(priv
.private
)
120 sign
= (ord(ed_pub0
[31]) & 255) >> 7
121 ed_pub1
= curve25519ToEd25519(pub
.public
, sign
)
123 self
.assertEquals(ed_pub0
, ed_pub1
)
125 def testBlinding(self
):
128 pk
= publickeyFromESK(esk
)
129 param
= os
.urandom(32)
130 besk
= blindESK(esk
, param
)
131 bpk
= blindPK(pk
, param
)
132 bpk2
= publickeyFromESK(besk
)
133 self
.assertEquals(bpk
, bpk2
)
135 self
._testSignatures
(besk
, bpk
)
137 def testIdentity(self
):
139 # B is the unique point (x, 4/5) \in E for which x is positive
144 # Get identity E by doing: E = l*B, where l is the group order
145 identity
= scalarmult(B
, ell
)
147 # Get identity E by doing: E = l*A, where A is a random point
149 pk
= decodepoint(publickey(sk
))
150 identity2
= scalarmult(pk
, ell
)
152 # Check that identities match
153 assert(identity
== identity2
)
154 # Check that identity is the point (0,1)
155 assert(identity
== [0,1])
157 # Check identity element: a*E = E, where a is a random scalar
158 scalar
= random_scalar(os
.urandom
)
159 result
= scalarmult(identity
, scalar
)
160 assert(result
== identity
== identity2
)
162 # ------------------------------------------------------------
164 # From pprint.pprint([ binascii.b2a_hex(os.urandom(32)) for _ in xrange(8) ])
166 '26c76712d89d906e6672dafa614c42e5cb1caac8c6568e4d2493087db51f0d36',
167 'fba7a5366b5cb98c2667a18783f5cf8f4f8d1a2ce939ad22a6e685edde85128d',
168 '67e3aa7a14fac8445d15e45e38a523481a69ae35513c9e4143eb1c2196729a0e',
169 'd51385942033a76dc17f089a59e6a5a7fe80d9c526ae8ddd8c3a506b99d3d0a6',
170 '5c8eac469bb3f1b85bc7cd893f52dc42a9ab66f1b02b5ce6a68e9b175d3bb433',
171 'eda433d483059b6d1ff8b7cfbd0fe406bfb23722c8f3c8252629284573b61b86',
172 '4377c40431c30883c5fbd9bc92ae48d1ed8a47b81d13806beac5351739b5533d',
173 'c6bbcce615839756aed2cc78b1de13884dd3618f48367a17597a16c1cd7a290b',
174 'c6bbcce615839756aed2cc78b1de13884dd3618f48367a17597a16c1cd7a290b',
175 'c6bbcce615839756aed2cc78b1de13884dd3618f48367a17597a16c1cd7a290b']
177 # From pprint.pprint([ binascii.b2a_hex(os.urandom(32)) for _ in xrange(8) ])
179 '54a513898b471d1d448a2f3c55c1de2c0ef718c447b04497eeb999ed32027823',
180 '831e9b5325b5d31b7ae6197e9c7a7baf2ec361e08248bce055908971047a2347',
181 'ac78a1d46faf3bfbbdc5af5f053dc6dc9023ed78236bec1760dadfd0b2603760',
182 'f9c84dc0ac31571507993df94da1b3d28684a12ad14e67d0a068aba5c53019fc',
183 'b1fe79d1dec9bc108df69f6612c72812755751f21ecc5af99663b30be8b9081f',
184 '81f1512b63ab5fb5c1711a4ec83d379c420574aedffa8c3368e1c3989a3a0084',
185 '97f45142597c473a4b0e9a12d64561133ad9e1155fe5a9807fe6af8a93557818',
186 '3f44f6a5a92cde816635dfc12ade70539871078d2ff097278be2a555c9859cd0',
187 '0000000000000000000000000000000000000000000000000000000000000000',
188 '1111111111111111111111111111111111111111111111111111111111111111']
192 def writeArray(name
, array
):
193 print("static const char *{prefix}{name}[] = {{".format(
194 prefix
=PREFIX
,name
=name
))
196 h
= binascii
.b2a_hex(a
)
200 print(' "{0}"\n "{1}",'.format(h1
.decode('utf-8'),h2
.decode('utf-8')))
202 print(' "{0}",'.format(h
.decode('utf-8')))
205 def comment(text
, initial
="/**"):
207 print(textwrap
.fill(text
,initial_indent
=" * ",subsequent_indent
=" * "))
210 def makeTestVectors():
211 comment("""Test vectors for our ed25519 implementation and related
212 functions. These were automatically generated by the
213 ed25519_exts_ref.py script.""", initial
="/*")
216 comment("""Secret key seeds used as inputs for the ed25519 test vectors.
217 Randomly generated. """)
218 secretKeys
= [ binascii
.a2b_hex(r
) for r
in RAND_INPUTS
]
219 writeArray("SECRET_KEYS", secretKeys
)
221 comment("""Secret ed25519 keys after expansion from seeds. This is how Tor
222 represents them internally.""")
223 expandedSecretKeys
= [ expandSK(sk
) for sk
in secretKeys
]
224 writeArray("EXPANDED_SECRET_KEYS", expandedSecretKeys
)
226 comment("""Public keys derived from the above secret keys""")
227 publicKeys
= [ publickey(sk
) for sk
in secretKeys
]
228 writeArray("PUBLIC_KEYS", publicKeys
)
230 comment("""The curve25519 public keys from which the ed25519 keys can be
231 derived. Used to test our 'derive ed25519 from curve25519'
233 writeArray("CURVE25519_PUBLIC_KEYS",
234 (slownacl_curve25519
.smult_curve25519_base(sk
[:32])
235 for sk
in expandedSecretKeys
))
237 comment("""Parameters used for key blinding tests. Randomly generated.""")
238 blindingParams
= [ binascii
.a2b_hex(r
) for r
in BLINDING_PARAMS
]
239 writeArray("BLINDING_PARAMS", blindingParams
)
241 comment("""Blinded secret keys for testing key blinding. The nth blinded
242 key corresponds to the nth secret key blidned with the nth
243 blinding parameter.""")
244 writeArray("BLINDED_SECRET_KEYS",
245 (blindESK(expandSK(sk
), bp
)
246 for sk
,bp
in zip(secretKeys
,blindingParams
)))
248 comment("""Blinded public keys for testing key blinding. The nth blinded
249 key corresponds to the nth public key blidned with the nth
250 blinding parameter.""")
251 writeArray("BLINDED_PUBLIC_KEYS",
252 (blindPK(pk
, bp
) for pk
,bp
in zip(publicKeys
,blindingParams
)))
254 comment("""Signatures of the public keys, made with their corresponding
256 writeArray("SELF_SIGNATURES",
257 (signature(pk
, sk
, pk
) for pk
,sk
in zip(publicKeys
,secretKeys
)))
261 if __name__
== '__main__':
263 if len(sys
.argv
) == 1 or sys
.argv
[1] not in ("SelfTest", "MakeVectors"):
264 print("You should specify one of 'SelfTest' or 'MakeVectors'")
266 if sys
.argv
[1] == 'SelfTest':