Ho provato un pò lo script (un'altra versione che hai pibblicato ecc_for_collider5.py, non trovo il link ora dal telefono) in python è parecchio veloce ~60 secondi per 16.4M chiavi pubbliche, non mi sono ancora addentrato nei dettagli del funzionamento e relativa spiegazione.
Magari dipende dalla versione di python che uso 2.7, ma ho notato che alcune coordinate non sono 32 byte, la stringa che le rappresenta ha un carattere in più per qualche motivo. Non ho ancora verificato nel dettaglio con che frequenza capita, magari domani riesco a controllare un pò meglio.
Comunque ottimo lavoro, grazie
Non offendiamo, la mia libreria migliore in python impiega meno di 6,5 secondi per generare 16.4 milioni di chiavi pubbliche!
Utilizzando gmpy2. Poi mi sono stufato di python e sono passato a C (50 milioni di chiavi in 1 secondo).
Penso che la versione che hai scaricato tu sia più leggibile, comunque ti allego la più veloce (la 08). Per quanto riguarda la stampa, se stampa a video una L finale, L sta per long. Se ci sono altri problemi fammi sapere.
$ time python2 gen_batches_points08.py
private key: -lambda*(k+m)G
0x2a7fb67883ea214c0a96f75b449fa3cec8e1b0d1f1e65e8aa80c3ef6cffaa1ae
('0x1c98a16eb218925feea48547a41d9d07bbd757d696857b4c2072bd264c827938', '0x49963b55d6a149f0df12d4acd5065e26b3e9d4f7da9c56aab690f44e866afe16')
*******
private key: -lambda*(k-m)G
0x6554827e46f82b9e6c571fdc6ae54b90dfdd38c4a3daf5213f3b4ee41119b8f8
('0x3c8250f180d59f9f1deeb55dbe00deb835de8a774c2fe1e6231b9eb44cdb3148', '0x7fcc26c9d9b58dd0971e87dd475c9d535a393cd5cf2cc62d53d4130cef611b6b')
*******
You have just generated 16 Millions of keys!!!
real 0m6,460s
user 0m6,456s
ecc_for_collider08.py
import gmpy2
from gmpy2 import mpz
#see http://www.secg.org/sec2-v2.pdf at page 9
#Gx=55066263022277343669578718895168534326250603453777594175500187360389116729240
#Gy=32670510020758816978083085130507043184471273380659243275938904335757337482424
#p=115792089237316195423570985008687907853269984665640564039457584007908834671663
#n=115792089237316195423570985008687907852837564279074904382605163141518161494337
Gx=mpz(0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798)
Gy=mpz(0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8)
p=mpz(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F) # Fp
n=mpz(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141) # E(Fp)
#endomorphism
lambd=mpz(0x5363ad4cc05c30e0a5261c028812645a122e22ea20816678df02967c1b23bd72) # (lambda^3 = 1 mod n)
lambd2=mpz(0xac9c52b33fa3cf1f5ad9e3fd77ed9ba4a880b9fc8ec739c2e0cfc810b51283ce) #(lambda^2)
beta=mpz(0x7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee) # (beta^3 = 1 mod p)
beta2=mpz(0x851695d49a83f8ef919bb86153cbcb16630fb68aed0a766a3ec693d68e6afa40) # (beta^2)
############################################################################################
###Field operations
#a must be a number between 1 and p-1; input: a output: 1/a
#see http://www.springer.com/?SGWID=4-102-45-110359-0 page 40 alg. 2.20
def inv(a,p):
u, v = a%p, p
x1, x2 = 1, 0
while u != 1:
#q = v//u
#r = v-q*u
q, r = divmod(v,u)
x = x2-q*x1
v = u
u = r
x2 = x1
x1 = x
return x1%p
#inverse of a batch of x
#input: batch of x : [x1,x2,x3,x4,x5,x6,x7,x8,...,x2048]
#x is known (x of kG)
#output: batch of inverse: 1/(x1-x), 1/(x2-x), 1/(x3-x), ....
#3M for each inverse
def inv_batch(x,batchx,p):
a = (batchx[1]-x) % p
partial= [0]*2049
partial[1]=a
for i in range(2,2049):
a = (a*(batchx[i]-x)) % p #2047M
partial[i]=a
inverse=gmpy2.invert(partial[2048],p) # 1I
#inverse=inv(partial[2048],p) # 1I
batch_inverse=[0]*2049
for i in range(2048,1,-1):
batch_inverse[i-1]=(partial[i-1]*inverse) % p #2047M
inverse=(inverse*(batchx[i]-x)) %p #2047M
batch_inverse[0]=inverse
return batch_inverse
############################################################################################
##Group operations
# https://en.wikipedia.org/wiki/Elliptic_curve_point_multiplication#Point_addition
#'double' addition
#add 2 points P and Q --> P+Q
#add 2 points P and Q --> P-Q
#
#(X1,Y1) + (X2,Y2) --> (X3,Y3) (the inverse of (x2-x1) must be known)
#(X1,Y1) + (X2,-Y2) --> (X4,Y4) (the inverse of (x2-x1) must be known)
#
#input: (X1,Y1), (X2,Y2), the inverse of (X2 - X1): 5 scalars
#output: (X3,Y3), (X4,Y4) : 4 scalars
#4M + 2S
def double_add_P_Q_inv(x1, y1, batchG, batchinvx2x1):
batch=[0]*2048
for i in range(1,2049):
x2,y2 = batchG[i]
invx2x1 = batchinvx2x1[i]
dy = (y2-y1) #% p
a = dy*invx2x1 % p #1M
a2 = a**2 #1S
x3 = (a2 - x1 -x2) % p
y3 = (a*(x1-x3) -y1) % p #1M
dy = p-(y2+y1) #% p # only the sign of y2 changes, "y of -Q = -y of +Q"
a = dy*invx2x1 % p #1M x2 and then the inverse invx2x1 are the same
a2 = a**2 #1S
x4 = (a2 - x1 -x2) % p
y4 = (a*(x1-x4) -y1) % p #1M
batch[i-1]=(x3,y3,x4,y4)
return batch
#https://en.wikibooks.org/wiki/Cryptography/Prime_Curve/Jacobian_Coordinates
#jacobian coordinates
def add_j_j(jax, jay, jaz, jbx, jby, jbz):
u1 = (jax*jbz**2) % p
u2 = (jbx*jaz**2) % p
s1 = (jay*jbz**3) % p
s2 = (jby*jaz**3) % p
h = (u2-u1) % p
r = (s2-s1) % p
jcx = (r**2 -h**3-2*u1*h**2) % p
jcy = (r*(u1*h**2-jcx)-s1*h**3) % p
jcz = (h*jaz*jbz) % p
return jcx, jcy, jcz
def double_j_j(jx, jy, jz):
s = (4*jx*jy**2) % p
m = (3*jx**2) % p
jx2 = (m**2 - 2*s) % p
jy2 = (m*(s-jx2) - 8*jy**4) % p
jz2 = (2*jy*jz) % p
return jx2, jy2, jz2
def mul(k,jx,jy,jz):
jkx, jky, jkz = 0, 0, 1
if (k%2 == 1):
jkx, jky, jkz = jx, jy, jz #only if d is odd
q=k//2
while q>0:
jx, jy, jz = double_j_j(jx,jy,jz)
a=q%2
if (a > jkx):
jkx, jky, jkz = jx, jy, jz #only if d is even
elif (a):
jkx, jky, jkz = add_j_j(jx, jy, jz, jkx, jky, jkz)
q=q//2
return jkx, jky, jkz
############################################################################################
#Coordinates
#from jacobian to affine
def jac_to_aff(jax, jay, jaz, invjaz):
ax, ay = (jax*invjaz**2) % p, (jay*invjaz**3) % p
return ax, ay
gen_batches_points08.py
#!/usr/bin/env python
#this script computes 6x667 batchs of 4097 points (1 single point + 2048 couples of points -> (kG+mG, kG-mg))
#16,4 millions of points
#3 : 1 batch + 5 endomorphism
from ecc_for_collider08 import *
########################################################################
#in this section the script pre-computes 1G, 2G, 3G, ..., 2048G
mG = [(0,0)]*2049
mGx = [0]*2049
mGy = [0]*2049
mGx[1]=Gx
mGy[1]=Gy
mG[1]=(Gx,Gy) #list of multiples of G,
#you have to precompute and store these multiples somewhere
mGx[2]=mpz(0xC6047F9441ED7D6D3045406E95C07CD85C778E4B8CEF3CA7ABAC09B95C709EE5)
mGy[2]=mpz(0x1AE168FEA63DC339A3C58419466CEAEEF7F632653266D0E1236431A950CFE52A)
mG[2]=(mGx[2],mGy[2])
for i in range(3,2049):
jx,jy,jz=add_j_j(Gx, Gy, mpz(1), mGx[i-1], mGy[i-1], mpz(1)) #this is not a efficient way, but it is not important
jzinv = inv(jz,p)
(x,y) = jac_to_aff(jx, jy, jz, jzinv)
mG[i] = (x,y)
mGx[i] = x
mGy[i] = y
###########################################################################
lowest_key=2**55+789079076 #put here the lowest key you want to generate
number_of_batches=667 # #put here how many consecutive batches you want to generate
number_of_keys=4097*6*number_of_batches #this is the number of consecutive keys you are going to generate
#this number is always a multiple of 6*4097=24582, because 24582 is the minimum size
#remember: you generate always 6 batches at once, 1 in (1,2^160) and 5 out of this range
#their name are: main_batch (the main), batch2, batch3, batch_minus
#(these other batches are derived from the main with endomorphism or complement private keys)
id_batch=lowest_key+2048 #id_batch is now the private key of the middle point of the first batch that you want compute
distance_between_successive_batches=4097 #this is the distance from the middle point of the first batch and the middle point
#of the successive batch in (1,2^160)
#4097 means that I'm generating only consecutive batches in (1,2^160), this is the default
start=id_batch #the first key to generate of the the first batch is the lowest key + 2048 -->
#the middle point of the first batch
stop=id_batch + number_of_batches*distance_between_successive_batches #the key of the middle point of the last batch
#k is always the id of the current main batch and the key of the middle point of the current main batch
for k in range(start,stop,distance_between_successive_batches):
jkx,jky,jkz = mul(k,Gx,Gy,mpz(1)) #here we use the slow function mul to generate the middle point
#invjkz = inv(jkz,p)
invjkz=gmpy2.invert(jkz,p) # 1I
(kx,ky) = jac_to_aff(jkx, jky, jkz, invjkz)
kminverse = [0]*2048
kminverse = inv_batch(kx,mGx,p) #you need to compute 2048 inverse, 1 for each couple
#to perform this task you need only the x coordinates of the multiples of G and
#the x coordinate of kG: kx
kminverse = [invjkz]+kminverse
#the element number 0 of the list is the inverse invjkz that you have computed to get kG=(kx,ky)
#the element number 1 of the list is the inverse necessary to perform kG + 1G (and kG-1G)
#---> 1/(x_of_1G - kx)
#the element number 2 of the list is the inverse necessary to perform kG + 2G (and kG-2G)
#---> 1/(x_of_2G - kx)
#...
#the element number m of the list is the inverse necessary to perform kG + mG (and kG-mG)
#---> 1/(x_of_mG - kx)
#then the name: "kminverse", the inverse to get the generic kG+mG
#...
#the element number 2048 of the list is the inverse necessary to perform kG+2048G and kG-2048G
#---> 1/(x_of_2048G - kx)
main_batch=[(0,0,0,0)]*2048
#the element number 0 of the list will be the middle point kG=(kx,ky), you can see it as the couple ((k+0)G, (k-0)G)
#the element number 1 will be the couple (k+1)G, (k-1)G
#the element number 2 will be the couple (k+2)G, (k-2)G
#...
#the element number 2048 will be the couple (k+2048)G, (k-2048)G
#each element of this list is a couple of points -> 4 scalars: x_(k+m)G,y_(k+m)G,x_(k-m)G,y_(k-m)G,
#but the first element is only a fake couple
main_batch = double_add_P_Q_inv(kx,ky,mG,kminverse)
#_the function double_add_P_Q_inv computes kG +/-mG, it uses the list of the inverse and return a list of couple of points
#each element of this list is a couple of points, 4 scalars, x_(k+m)G,y_(k+m)G,x_(k-m)G,y_(k-m)G
#this list has 2049 elements
main_batch = [(kx,ky,kx,ky)]+main_batch #the element number 0 of the list is now the middle point kG=(kx,ky), you can see it as the fake couple (k+0)G, (k-0)G
batch2=[0] * 2049 #this is the batch2 with lambda*kG, ( lambda*(k+1)G, lambda*(k-1)G ), ( lambda*(k+2)G, lambda*(k-2)G ), ....,
# (lambda*(k+2048)G, lambda*(k-2048)G)
#this list actually has only the x-cooordinates of the points, because the y-coordinates are the same of main batch
#each element of this list is a couple of x -> 2 scalars: x_lambda*(k+m)G, x_lambda*(k-m)G
#this list has 2049 elements
batch3=[0] * 2049 #this is the batch3 with lambda^2*kG, ( lambda^2*(k+1)G, lambda^2*(k-1)G ), ( lambda^2*(k+2)G, lambda^2*(k-2)G ), ....,
# (lambda^2*(k+2048)G, lambda^2*(k-2048)G)
#this list actually has only the x-cooordinates of the points, because the y-coordinates are the same of main batch
#each element of this list is a couple of x -> 2 scalars: x_lambda^2*(k+m)G, x_lambda^2*(k-m)G
#this list has 2049 elements
batch_minus=[0] * 2049 #the complement private keys of the other batches
for i in range(0,2049): #endomorphism + the complement private keys
x1,y1,x2,y2=main_batch[i]
batch2[i] = [(x1*beta % p), (x2*beta % p)] #only x coordinates, the first and the third scalar of each element
#of the main batch
#private key: lambda*k mod n, coordinate x: beta*x mod p
batch3[i] = [(x1*beta2 % p), (x2*beta2 % p)] #only x coordinates, the first and the third scalar of each element
#of the main batch
#private key: lambda^2*k mod n, coordinate x: beta^2*x mod p
batch_minus[i]=[(-y1 % p), (-y2 % p)] #only y coordinates, the 2^ and the 4^ scalar of each element of all batches
#k in this case is the id of the last batch (or if you prefer, k is the private key of the middle point of the last batch)
m=2048 #a random number between 0 and 2048, you can pick up a couple of points in the last main batch (or in batch2 / batch3)
(kmx1,kmx2)=batch2[m]
(kmy1,kmy2)=batch_minus[m] #in this case the points chosen are in batch3
#the y are from batch_minus
#(kmx1,kmy1,kmx2,kmy2)=main_batch[m]
print ('private key: -lambda*(k+m)G')
print (hex(-lambd*(k+m)%n)) #private key
print (hex(kmx1), hex(kmy1)) #public key (first and second coordinate)
print ('*******')
print ('private key: -lambda*(k-m)G')
print (hex(-lambd*(k-m)%n)) #private key
print (hex(kmx2), hex(kmy2)) #public key (first and second coordinate)
print ('*******')
print (' ')
a=number_of_keys//1000000
print ('You have just generated ' + str(a) + ' Millions of keys!!!')