├── a ├── d ├── shape ├── rigid ├── l ├── p ├── x0 ├── x1 ├── y0 ├── y1 ├── run.sh ├── LICENSE ├── README.md ├── .gitignore ├── findCurve.sage └── verify.sage /a: -------------------------------------------------------------------------------- 1 | 168700 2 | -------------------------------------------------------------------------------- /d: -------------------------------------------------------------------------------- 1 | 168696 2 | -------------------------------------------------------------------------------- /shape: -------------------------------------------------------------------------------- 1 | tedwards 2 | -------------------------------------------------------------------------------- /rigid: -------------------------------------------------------------------------------- 1 | fully rigid 2 | -------------------------------------------------------------------------------- /l: -------------------------------------------------------------------------------- 1 | 2736030358979909402780800718157159386076813972158567259200215660948447373041 2 | -------------------------------------------------------------------------------- /p: -------------------------------------------------------------------------------- 1 | 21888242871839275222246405745257275088548364400416034343698204186575808495617 2 | -------------------------------------------------------------------------------- /x0: -------------------------------------------------------------------------------- 1 | 995203441582195749578291179787384436505546430278305826713579947235728471134 2 | -------------------------------------------------------------------------------- /x1: -------------------------------------------------------------------------------- 1 | 5299619240641551281634865583518297030282874472190772894086521144482721001553 2 | -------------------------------------------------------------------------------- /y0: -------------------------------------------------------------------------------- 1 | 5472060717959818805561601436314318772137091100104008585924551046643952123905 2 | -------------------------------------------------------------------------------- /y1: -------------------------------------------------------------------------------- 1 | 16950150798460657717958625567821834550301663161624707787222815936182638968203 2 | -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | sage verify.sage . 3 | grep -Rn '.' verify-* |grep '^verify-.*:1:' |sed 's/:1:/ = /' 4 | 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 The Zcash developers 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | baby\_Jubjub supporting evidence 2 | -------------------------- 3 | 4 | This repository contains supporting evidence that the twisted Edwards curve 5 | 168700.x^2 + y^2 = 1 + 168696.x^2.y^2 of rational points over 6 | GF(21888242871839275222246405745257275088548364400416034343698204186575808495617), 7 | also called babyJubJub based upone ["Jubjub"](https://z.cash/technology/jubjub.html), 8 | satisfies the [SafeCurves criteria](https://safecurves.cr.yp.to/index.html). 9 | 10 | The script ``verify.sage`` is based on 11 | [this script from the SafeCurves site](https://safecurves.cr.yp.to/verify.html), 12 | modified by [Daira Hopwood](https://github.com/daira) 13 | 14 | * to support twisted Edwards curves; 15 | * to generate a file 'primes' containing the primes needed for primality proofs, 16 | if it is not already present; 17 | * to change the directory in which Pocklington proof files are generated 18 | (``proof/`` rather than ``../../../proof``), and to create that directory 19 | if it does not exist. 20 | 21 | Prerequisites: 22 | 23 | * apt-get install sagemath 24 | * pip install sortedcontainers 25 | 26 | Run ``sage verify.sage .``, or ``./run.sh`` to also print out the results. 27 | 28 | You can generate the curve by running `sage findCurve.sage 168698` 29 | This is the lowest A=168698 of a montgomary curve that statifies the 30 | critieria defined in [ref7748](https://tools.ietf.org/html/rfc7748) 31 | 32 | Note that the "rigidity" criterion cannot be checked automatically. 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | 49 | # Translations 50 | *.mo 51 | *.pot 52 | 53 | # Django stuff: 54 | *.log 55 | local_settings.py 56 | 57 | # Flask stuff: 58 | instance/ 59 | .webassets-cache 60 | 61 | # Scrapy stuff: 62 | .scrapy 63 | 64 | # Sphinx documentation 65 | docs/_build/ 66 | 67 | # PyBuilder 68 | target/ 69 | 70 | # Jupyter Notebook 71 | .ipynb_checkpoints 72 | 73 | # pyenv 74 | .python-version 75 | 76 | # celery beat schedule file 77 | celerybeat-schedule 78 | 79 | # SageMath parsed files 80 | *.sage.py 81 | 82 | # dotenv 83 | .env 84 | 85 | # virtualenv 86 | .venv 87 | venv/ 88 | ENV/ 89 | 90 | # Spyder project settings 91 | .spyderproject 92 | .spyproject 93 | 94 | # Rope project settings 95 | .ropeproject 96 | 97 | # mkdocs documentation 98 | /site 99 | 100 | # mypy 101 | .mypy_cache/ 102 | 103 | -------------------------------------------------------------------------------- /findCurve.sage: -------------------------------------------------------------------------------- 1 | #https://www.heise.de/netze/rfc/rfcs/rfc7748.shtml 2 | import sys 3 | import pdb 4 | 5 | def findCurve(prime, curveCofactor, twistCofactor, _A): 6 | F = GF(prime) 7 | A = _A 8 | while A < _A + 100000: 9 | print A 10 | if (A-2.) % 4 != 0: 11 | A+=1. 12 | continue 13 | try: 14 | E = EllipticCurve(F, [0, A, 0, 1, 0]) 15 | except: 16 | A+=1. 17 | continue 18 | groupOrder = E.order() 19 | if (groupOrder % curveCofactor != 0 or not is_prime(groupOrder // curveCofactor)): 20 | A+=1 21 | continue 22 | 23 | twistOrder = 2*(prime+1)-groupOrder 24 | if (twistOrder % twistCofactor != 0 or 25 | not is_prime(twistOrder // twistCofactor)): 26 | A+=1 27 | continue 28 | return A, E 29 | 30 | def find1Mod4(prime, curveCofactor, twistCofactor, A): 31 | assert((prime % 4) == 1) 32 | return findCurve(prime, curveCofactor, twistCofactor, A) 33 | 34 | def findGenPoint(prime, A, EC, N): 35 | F = GF(prime) 36 | for uInt in range(1, 1e3): 37 | u = F(uInt) 38 | v2 = u^3 + A*u^2 + u 39 | if not v2.is_square(): 40 | continue 41 | v = v2.sqrt() 42 | 43 | point = EC(u, v) 44 | pointOrder = point.order() 45 | if pointOrder == N: 46 | return point 47 | 48 | def mont_to_ted(u, v , r): 49 | x = Mod(u / v, r) 50 | y = Mod((u-1)/(u+1), r) 51 | return(x, y) 52 | 53 | def ted_to_mont(x, y , r): 54 | u = Mod((1 + y )/ ( 1 - y) , r) 55 | v = Mod((1 + y ) / ( (1 - y) * x) , r ) 56 | return(u,v) 57 | 58 | def isOnEd(x,y,r,a,d): 59 | return Mod(Mod(a,r)*(x**2),r) + Mod(y**2 , r) - 1 - Mod(d,r)*(Mod(x**2,r))*(Mod(y**2,r)) == 0 60 | 61 | prime = 21888242871839275222246405745257275088548364400416034343698204186575808495617 62 | Fr = GF(prime) 63 | h = 8 # cofactor 64 | 65 | A = int(sys.argv[1]) 66 | A, EC = find1Mod4(prime, h, 4, A) 67 | 68 | # A = 170214 another candidate 69 | B = 1 70 | a = A + 2 / B 71 | d = A - 2 / B 72 | 73 | print "a " , a , "d " , d , sqrt(d) 74 | # check we have a safe twist 75 | assert(not d.is_square()) 76 | assert(a*d*(a-d)!=0) 77 | 78 | s = factor(EC.order()) 79 | print ("l : " , s) 80 | N = h * s # order of the curve 81 | print (factor(EC.quadratic_twist().order())) 82 | 83 | # get generator point 84 | u_gen, v_gen, w_gen = findGenPoint(prime, A, EC, N) 85 | # find that generator point on the edwards curve 86 | gen_x, gen_y = mont_to_ted(u_gen, v_gen, prime) 87 | # make sure the generator point is on the twisted edwards curve 88 | assert(isOnEd(gen_x, gen_y, prime, a , d)) 89 | # go back to montgomery 90 | u , v = ted_to_mont(gen_x, gen_y, prime) 91 | # confirm we are back where we started from 92 | assert (u == u_gen) 93 | assert (v == v_gen) 94 | 95 | # get base point on montgorery curve by multiplying the generator point by h 96 | base_x , base_y, base_z = h*EC(u_gen, v_gen) 97 | # find the same points on twisted edwards curve 98 | base_x , base_y = mont_to_ted(base_x , base_y, prime) 99 | 100 | # the generator is on the twisted edwards curve 101 | assert(isOnEd(base_x,base_y, prime , a , d)) 102 | 103 | print ("generator :" , gen_x, gen_y) 104 | print ("base :", base_x, base_y) 105 | -------------------------------------------------------------------------------- /verify.sage: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | from errno import ENOENT, EEXIST 4 | from sortedcontainers import SortedSet 5 | 6 | 7 | def readfile(fn): 8 | fd = open(fn,'r') 9 | r = fd.read() 10 | fd.close() 11 | return r 12 | 13 | def writefile(fn,s): 14 | fd = open(fn,'w') 15 | fd.write(s) 16 | fd.close() 17 | 18 | def expand2(n): 19 | s = "" 20 | 21 | while n != 0: 22 | j = 16 23 | while 2**j < abs(n): j += 1 24 | if 2**j - abs(n) > abs(n) - 2**(j-1): j -= 1 25 | 26 | if abs(abs(n) - 2**j) > 2**(j - 10): 27 | if n > 0: 28 | if s != "": s += " + " 29 | s += str(n) 30 | else: 31 | s += " - " + str(-n) 32 | n = 0 33 | elif n > 0: 34 | if s != "": s += " + " 35 | s += "2^" + str(j) 36 | n -= 2**j 37 | else: 38 | s += " - 2^" + str(j) 39 | n += 2**j 40 | 41 | return s 42 | 43 | def requirement(fn,istrue): 44 | writefile(fn,str(istrue) + '\n') 45 | return istrue 46 | 47 | def verify(): 48 | try: 49 | os.mkdir('proof') 50 | except OSError as e: 51 | if e.errno != EEXIST: raise 52 | 53 | try: 54 | s = set(map(Integer, readfile('primes').split())) 55 | except IOError, e: 56 | if e.errno != ENOENT: raise 57 | s = set() 58 | 59 | needtofactor = SortedSet() 60 | V = SortedSet() # distinct verified primes 61 | verify_primes(V, s, needtofactor) 62 | verify_pass(V, needtofactor) 63 | 64 | old = V 65 | needtofactor.update(V) 66 | while len(needtofactor) > len(old): 67 | k = len(needtofactor) - len(old) 68 | sys.stdout.write('Factoring %d integer%s' % (k, '' if k == 1 else 's')) 69 | sys.stdout.flush() 70 | for x in needtofactor: 71 | if x not in old: 72 | for (y, z) in factor(x): 73 | s.add(y) 74 | sys.stdout.write('.') 75 | sys.stdout.flush() 76 | 77 | print('') 78 | 79 | old = needtofactor.copy() 80 | verify_primes(V, s, needtofactor) 81 | 82 | writefile('primes', '\n'.join(map(str, s)) + '\n') 83 | writefile('verify-primes', '
\n' + 84 | ''.join(('2\n' if v == 2 else 85 | '%s\n' % (v,v)) for v in V) + 86 | '\n') 87 | 88 | verify_pass(V, needtofactor) 89 | 90 | 91 | def verify_primes(V, s, needtofactor): 92 | for n in sorted(s): 93 | if not n.is_prime() or n in V: continue 94 | needtofactor.add(n-1) 95 | if n == 2: 96 | V.add(n) 97 | continue 98 | for trybase in primes(2,10000): 99 | base = Integers(n)(trybase) 100 | if base^(n-1) != 1: continue 101 | proof = 'Primality proof for n = %s:\n' % n 102 | proof += 'Take b = %s.\n' % base 103 | proof += '
b^(n-1) mod n = 1.\n' 104 | f = factor(1) 105 | for v in reversed(V): 106 | if f.prod()^2 <= n: 107 | if n % v == 1: 108 | u = base^((n-1)/v)-1 109 | if u.is_unit(): 110 | if v == 2: 111 | proof += '
2 is prime.\n' 112 | else: 113 | proof += '
%s is prime.\n' % (v,v)
114 | proof += '
b^((n-1)/%s)-1 mod n = %s, which is a unit, inverse %s.\n' % (v,u,1/u)
115 | f *= factor(v)^(n-1).valuation(v)
116 | if f.prod()^2 <= n: continue
117 | if n % f.prod() != 1: continue
118 | proof += '
(%s) divides n-1.\n' % f 119 | proof += '
(%s)^2 > n.\n' % f 120 | proof += "
n is prime by Pocklington's theorem.\n"
121 | proof += '\n'
122 | writefile('proof/%s.html' % n,proof)
123 | V.add(n)
124 | break
125 |
126 |
127 | def verify_pass(V, needtofactor):
128 | p = Integer(readfile('p'))
129 | k = GF(p)
130 | kz.
= %s\n' % expand2(l))
167 |
168 | writefile('hex-p',hex(p) + '\n')
169 | writefile('hex-l',hex(l) + '\n')
170 | writefile('hex-x0',hex(x0) + '\n')
171 | writefile('hex-x1',hex(x1) + '\n')
172 | writefile('hex-y0',hex(y0) + '\n')
173 | writefile('hex-y1',hex(y1) + '\n')
174 |
175 | gcdlpis1 = gcd(l,p) == 1
176 | safetransfer &= requirement('verify-gcdlp1',gcdlpis1)
177 |
178 | writefile('verify-movsafe','Unverified\n')
179 | writefile('verify-embeddingdegree','Unverified\n')
180 | if gcdlpis1 and l.is_prime():
181 | u = Integers(l)(p)
182 | d = l-1
183 | needtofactor.add(d)
184 | for v in V:
185 | while d % v == 0: d /= v
186 | if d == 1:
187 | d = l-1
188 | for v in V:
189 | while d % v == 0:
190 | if u^(d/v) != 1: break
191 | d /= v
192 | safetransfer &= requirement('verify-movsafe',(l-1)/d <= 100)
193 | writefile('verify-embeddingdegree','%s
= (l-1)/%s\n' % (d,(l-1)/d))
194 |
195 | t = p+1-l*round((p+1)/l)
196 | if l^2 > 16*p:
197 | writefile('verify-trace','%s\n' % t)
198 | f = factor(1)
199 | d = (p+1-t)/l
200 | needtofactor.add(d)
201 | for v in V:
202 | while d % v == 0:
203 | d //= v
204 | f *= factor(v)
205 | writefile('verify-cofactor','%s\n' % f)
206 | else:
207 | writefile('verify-trace','Unverified\n')
208 | writefile('verify-cofactor','Unverified\n')
209 |
210 | D = t^2-4*p
211 | needtofactor.add(D)
212 | for v in V:
213 | while D % v^2 == 0: D /= v^2
214 | if prod([v for v in V if D % v == 0]) != -D:
215 | writefile('verify-disc','Unverified\n')
216 | writefile('verify-discisbig','Unverified\n')
217 | safedisc = False
218 | else:
219 | f = -prod([factor(v) for v in V if D % v == 0])
220 | if D % 4 != 1:
221 | D *= 4
222 | f = factor(4) * f
223 | Dbits = (log(-D)/log(2)).numerical_approx()
224 | writefile('verify-disc','%s
= %s
≈ -2^%.1f\n' % (D,f,Dbits))
225 | safedisc &= requirement('verify-discisbig',D < -2^100)
226 |
227 | pi4 = 0.78539816339744830961566084581987572105
228 | rho = log(pi4*l)/log(4)
229 | writefile('verify-rho','%.1f\n' % rho)
230 | saferho &= requirement('verify-rhoabove100',rho.numerical_approx() >= 100)
231 |
232 | twistl = 'Unverified'
233 | d = p+1+t
234 | needtofactor.add(d)
235 | for v in V:
236 | while d % v == 0: d /= v
237 | if d == 1:
238 | d = p+1+t
239 | for v in V:
240 | if d % v == 0:
241 | if twistl == 'Unverified' or v > twistl: twistl = v
242 |
243 | writefile('verify-twistl','%s\n' % twistl)
244 | writefile('verify-twistembeddingdegree','Unverified\n')
245 | writefile('verify-twistmovsafe','Unverified\n')
246 | if twistl == 'Unverified':
247 | writefile('hex-twistl','Unverified\n')
248 | writefile('expand2-twistl','Unverified\n')
249 | writefile('verify-twistcofactor','Unverified\n')
250 | writefile('verify-gcdtwistlp1','Unverified\n')
251 | writefile('verify-twistrho','Unverified\n')
252 | safetwist = False
253 | else:
254 | writefile('hex-twistl',hex(twistl) + '\n')
255 | writefile('expand2-twistl','
= %s\n' % expand2(twistl))
256 | f = factor(1)
257 | d = (p+1+t)/twistl
258 | needtofactor.add(d)
259 | for v in V:
260 | while d % v == 0:
261 | d //= v
262 | f *= factor(v)
263 | writefile('verify-twistcofactor','%s\n' % f)
264 | gcdtwistlpis1 = gcd(twistl,p) == 1
265 | safetwist &= requirement('verify-gcdtwistlp1',gcdtwistlpis1)
266 |
267 | movsafe = 'Unverified'
268 | embeddingdegree = 'Unverified'
269 | if gcdtwistlpis1 and twistl.is_prime():
270 | u = Integers(twistl)(p)
271 | d = twistl-1
272 | needtofactor.add(d)
273 | for v in V:
274 | while d % v == 0: d /= v
275 | if d == 1:
276 | d = twistl-1
277 | for v in V:
278 | while d % v == 0:
279 | if u^(d/v) != 1: break
280 | d /= v
281 | safetwist &= requirement('verify-twistmovsafe',(twistl-1)/d <= 100)
282 | writefile('verify-twistembeddingdegree',"%s
= (l'-1)/%s\n" % (d,(twistl-1)/d))
283 |
284 | rho = log(pi4*twistl)/log(4)
285 | writefile('verify-twistrho','%.1f\n' % rho)
286 | safetwist &= requirement('verify-twistrhoabove100',rho.numerical_approx() >= 100)
287 |
288 | precomp = 0
289 | joint = l
290 | needtofactor.add(p+1-t)
291 | needtofactor.add(p+1+t)
292 | for v in V:
293 | d1 = p+1-t
294 | d2 = p+1+t
295 | while d1 % v == 0 or d2 % v == 0:
296 | if d1 % v == 0: d1 //= v
297 | if d2 % v == 0: d2 //= v
298 | # best case for attack: cyclic; each power is usable
299 | # also assume that kangaroo is as efficient as rho
300 | if v + sqrt(pi4*joint/v) < sqrt(pi4*joint):
301 | precomp += v
302 | joint /= v
303 |
304 | rho = log(precomp + sqrt(pi4 * joint))/log(2)
305 | writefile('verify-jointrho','%.1f\n' % rho)
306 | safetwist &= requirement('verify-jointrhoabove100',rho.numerical_approx() >= 100)
307 |
308 |
309 | x0 = k(x0)
310 | y0 = k(y0)
311 | x1 = k(x1)
312 | y1 = k(y1)
313 |
314 | if shape in ('edwards', 'tedwards'):
315 | d = Integer(readfile('d'))
316 | a = 1
317 | if shape == 'tedwards':
318 | a = Integer(readfile('a'))
319 |
320 | writefile('verify-shape','Twisted Edwards\n')
321 | writefile('verify-equation','%sx^2+y^2 = 1%+dx^2y^2\n' % (a, d))
322 | if a == 1:
323 | writefile('verify-shape','Edwards\n')
324 | writefile('verify-equation','x^2+y^2 = 1%+dx^2y^2\n' % d)
325 |
326 | a = k(a)
327 | d = k(d)
328 | elliptic = a*d*(a-d)
329 | level0 = a*x0^2+y0^2-1-d*x0^2*y0^2
330 | level1 = a*x1^2+y1^2-1-d*x1^2*y1^2
331 |
332 | if shape == 'montgomery':
333 | writefile('verify-shape','Montgomery\n')
334 | A = Integer(readfile('A'))
335 | B = Integer(readfile('B'))
336 | equation = '%sy^2 = x^3