Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- from itertools import product
- n=2
- m=10
- nb=12
- m2=m//2
- nq=nb*m2
- q=random_prime(2^nq,lbound=2^(nq-1))
- B=2
- R=ZZ["x"]
- K=CyclotomicField(2*n,"x")
- f=R.gen()**n+1
- Rq=R.change_ring(IntegerModRing(q))
- Rqf=Rq.quotient_ring(Rq.ideal(f))
- def roundFromK(a):
- return R([round(c) for c in a.list()])
- def roundFromVecK(v):
- return vector([roundFromK(vi) for vi in v])
- def roundCoef(p):
- res=[]
- for c in p.change_ring(ZZ).list():
- if c<q/2:
- res.append(c)
- else:
- res.append(c-q)
- return ZZ['x'](res)
- def roundCoefVec(v):
- return vector(map(roundCoef,v))
- def prodScal(a,b):
- return sum([a[i]*b[i] for i in range(len(a))])
- def randVecRq(l):
- return vector(Rq,[Rq.random_element(degree=n-1) for i in range(l)])
- def randVecRSmall(l):
- return vector(R,[R.random_element(x=-B,y=B+1,degree=n-1) for i in range(l)])
- def randomMatRSmall(l):
- return Matrix(R,[[R.random_element(x=-B,y=B+1,degree=n-1) for i in range(l)] for j in range(l)])
- def genKeySample():
- c=randVecRq(m//2)
- X=randomMatRSmall(m//2)
- b=vector(ZZ,[2^(i*nb) for i in range(m2)])
- a=vector(Rq,list(c)+list((b+c*X) % f))
- return a,X
- def binary(u):
- dig=[u[i].lift().digits(base=2^nb,padto=m//2) for i in range(n)]
- return vector(R,[R([dig[i][j] for i in range(n)]) for j in range(m//2)])
- def sample(a,X,u,fullSize=False):
- """Return small s such that a.s=u [q] where a=[c | b+c.X]
- With fullSize=True, s is full size modulo q"""
- if fullSize:
- s=randVecRq(m)
- s[0]=Rq((u-prodScal(s[1:],a[1:]) % f)*a[0].inverse_mod(f)) % f
- s=roundCoefVec([Rq(si) for si in s])
- else:
- c=a[0:m//2]
- sp1=vector(R,[R.random_element(x=-2^nb,y=2^nb,degree=n-1) for i in range(m//2)]) # randVecRSmall(m//2)
- b=vector(ZZ,[2^(i*nb) for i in range(m2)])
- s2=binary((u-prodScal(c,sp1)) % f)
- s1=sp1-X*s2 % f
- s=vector(list(s1)+list(s2))
- return s
- def testSample():
- R=ZZ["x"]
- K=CyclotomicField(2*n,"x")
- f=R.gen()**n+1
- Rq=R.change_ring(IntegerModRing(q))
- a,X=genKeySample()
- u=Rq.random_element(degree=n-1)
- print u
- s=sample(a,X,u)
- print "u=",u,"\na=",a,"\ns=",s,"\na.s=",prodScal(a,s) % f
- class KeySample():
- def __init__(self):
- self.a,self.X=genKeySample()
- def sampleVec(self,u,fullSize):
- return sample(self.a,self.X,u,fullSize)
- def sampleMat(self,U,fullSize):
- return Matrix([self.sampleVec(u % f,fullSize) for u in U]).transpose()
- def sampleMatApprox(self,U,fullSize=False):
- E=randVecRSmall(m)
- return self.sampleMat(U+E,fullSize)
- def toR(x):
- return ZZ['x'](map(ZZ,x.list()))
- def toVecR(v):
- return vector(ZZ['x'],map(toR,v.list()))
- def toMatR(M):
- return Matrix([toVecR(v*v.denominator()) for v in M])
- # attack without the safeGuards
- def genParams(kappa=3,safeGuards=False):
- if not n.is_power_of(2):
- raise ValueError
- print "n=",n
- print "m=",m
- print "q=",q
- print "size of q=",q.nbits()
- r=2*m+2
- nv=r+1 # we need 2*m+3 encodings Cij
- nu=r+2 # total number of encodings
- print "r=",r
- while True:
- print "Generating the Aij matrices"
- A0=randVecRq(m)
- Aij=[[KeySample() for j in range(kappa)] for i in range(kappa)]
- print "Generating the secret exponents"
- s=randVecRSmall(kappa)
- t=[s]+[randVecRSmall(kappa) for i in range(nv)]
- print "Generating encodings Cij"
- Cij=[[[] for j in range(kappa)] for i in range(kappa)]
- for (i,j,l) in product(range(kappa),range(kappa),range(nu)):
- Cij[i][j].append(Aij[i][j].sampleMatApprox( \
- t[l][(j-i+kappa)%kappa] * (Aij[i][j+1].a if j<kappa-1 else A0)))
- print "Attack first step"
- # to adapt the indices to arbitrary kappa, it's easiest to consider
- # the second row and the last row (of index 1 and k=kappa-1
- # respectively); on the second row, t_2 appears third, and on the last
- # row, t_kappa appears in the last but one position, hence the
- # following choice.
- k = kappa-1
- l0 = 1 # we work with constant indices for t_3,...,t_kappa
- A10 = Aij[1][0] # row 2
- Ak0 = Aij[k][0] # row kappa
- # t_kappa t_1
- C1L = [Cij[1][0][l0] * Cij[1][1][l] for l in range(nu)]
- # t_2 t_3*...*t_{kappa-1}
- C1R = [Cij[1][2][l] * prod([Cij[1][j][l0] for j in range(3,kappa)]) \
- for l in range(nu)]
- # t_2 t_3*...*t_{kappa}
- CkL = [Cij[k][0][l] * prod([Cij[k][j][l0] for j in range(1,kappa-1)]) \
- for l in range(nu)]
- # t_1
- CkR = [Cij[k][k][l] for l in range(nu)]
- W=Matrix(K,[[roundCoef(( \
- (A10.a*C1L[i]*C1R[j]-Ak0.a*CkL[j]*CkR[i]) % f)[0]) \
- for j in range(1,1+r)] for i in range(nv)]) # nv=r+1
- W2=Matrix(K,[[roundCoef(( \
- (A10.a*C1L[i]*C1R[j]-Ak0.a*CkL[j]*CkR[i]) % f)[0]) \
- for j in range(1,1+r)] for i in [0]+range(2,nu)]) #nu=nv+1=r+2
- print " Kernel computation"
- a=W.kernel().matrix()[0] #len(a)=nv=r+1
- b=W2.kernel().matrix()[0] #len(b)=nv=r+1
- gc=gcd(a.denominator(),b.denominator())
- print " gc=",gc
- if gc!=1:
- print " gc<>1: we restart the attack"
- continue
- a=toVecR(a*a.denominator())
- b=toVecR(b*b.denominator())
- a=vector(a.list()+[0])
- b=vector([b[0]]+[0]+b.list()[1:])
- print " Bezout computation"
- gc,ca,cb=xgcd(ZZ(a[0]),ZZ(b[0]))
- u=ca*a+cb*b # we have u[0]=1 len(u)=nu=nv+1=r+2
- ps=prodScal([ti[0] for ti in t],u) % f # ps should be 0.
- print " Check scalar product:",ps
- if ps==0: break
- print "Attack second step"
- # we compute the difference between the first line and the last line
- A00 = Aij[0][0]
- # t1
- C0L = [Cij[0][0][l] for l in range(nu)]
- # t2 t3*...*t_{kappa}
- C0R = [Cij[0][1][l] * prod([Cij[0][j][l0] for j in range(2,kappa)]) \
- for l in range(nu)]
- Omega=sum([vector([roundCoef(((A00.a*C0L[j]*C0R[l]-Ak0.a*CkL[l]*CkR[j]) % f)[0]) for l in range(1,m+1)])*(-u[j]) % f for j in range(1,len(u))])
- # equivalent of D00, the private key for user 1.
- D0Lp=prodScal([-ui for ui in u[1:]],C0L[1:])
- # this product is computed from public encodings.
- C0Rp =Matrix(K,[[roundCoef(C0R[j][i][0]) for j in range(1,m+1)] for i in range(m)])
- print " C0Rp.rank()=",C0Rp.rank()
- print " Computing approximate error vector"
- E=roundFromVecK(C0Rp.solve_left(Omega))
- print "true secret:"
- print A00.a*prod([Cij[0][i][0] for i in range(kappa)]) % f
- print "computed secret:"
- print (A00.a*D0Lp-E)*prod([Cij[0][i][0] for i in range(1,kappa)]) % f
- def genParamsSafeguards(kappa=3):
- if not n.is_power_of(2):
- raise ValueError
- print "n=",n
- print "m=",m
- print "q=",q
- s=randVecRSmall(kappa)
- print "s=",s
- print "Generating graded encoding parameters with safeguards"
- A0=randVecRq(m)
- Aij=[[KeySample() for j in range(kappa)] for i in range(kappa)]
- # for some reason this sometimes does not work
- #Rij=[[random_matrix(Rqf,m,m,algorithm='echelonizable',rank=m) if j>0 else \
- # identity_matrix(Rqf, m) for j in range(kappa)] for i in range(kappa)]
- Rij=[[random_matrix(Rqf,m,m) if j>0 else \
- identity_matrix(Rqf, m) for j in range(kappa)] for i in range(kappa)]
- Rijinv=[[Rij[i][j]^(-1) for j in range(kappa)] for i in range(kappa)]
- # lift from Rqf to Rq
- Rij=[[Rij[i][j].apply_map(lift) for j in range(kappa)] \
- for i in range(kappa)]
- Rijinv=[[Rijinv[i][j].apply_map(lift) for j in range(kappa)] \
- for i in range(kappa)]
- r=2*m+2
- print "r=",r
- nenc=m+r # total number of encodings
- print "nenc=",nenc
- print "Generating Diffie-Hellman encodings with safeguards"
- t=[s]+[randVecRSmall(kappa) for i in range(1,nenc)]
- Cij=[[[] for j in range(kappa)] for i in range(kappa)]
- for (i,j,l) in product(range(kappa),range(kappa),range(nenc)):
- v=t[l][(j-i+kappa)%kappa] * (Aij[i][j+1].a if j<kappa-1 else A0)
- fullSize=(j==0)
- C= Aij[i][j].sampleMatApprox(v,fullSize)
- Cij[i][j].append(Rij[i][j] * C * (Rijinv[i][j+1] if j<kappa-1 else Rijinv[i][0]) % f)
- # This is step one of the attack without safeguards.
- print "Phase 1: finding a linear relation between secret exponent and other exponent"
- k = kappa-1
- l0 = 1
- A10 = Aij[1][0]
- Ak0 = Aij[k][0]
- # t_kappa t_1
- C1L = [Cij[1][0][l0] * Cij[1][1][l] % f for l in range(nenc)]
- # t_2 t_3*...*t_{kappa-1}
- C1R = [Cij[1][2][l] * prod([Cij[1][j][l0] for j in range(3,kappa)]) % f \
- for l in range(nenc)]
- # t_2 t_3*...*t_{kappa}
- CkL = [Cij[k][0][l] * prod([Cij[k][j][l0] for j in range(1,kappa-1)]) % f \
- for l in range(nenc)]
- # t_1
- CkR = [Cij[k][k][l] for l in range(nenc)]
- print " Finding relations between discrete logs"
- for nk in range(r+2,nenc+1): # we initially work with nk vectors in total
- W=Matrix(K,[[roundCoef(( \
- (A10.a*C1L[i]*C1R[j]-Ak0.a*CkL[j]*CkR[i]) % f)[0]) \
- for j in range(1,1+r)] for i in [0]+range(1,nk-1)])
- W2=Matrix(K,[[roundCoef(( \
- (A10.a*C1L[i]*C1R[j]-Ak0.a*CkL[j]*CkR[i]) % f)[0]) \
- for j in range(1,1+r)] for i in [0]+range(2,nk)])
- a=W.kernel().matrix()[0] # len(a)=nk-1
- b=W2.kernel().matrix()[0] # len(b)=nk-1
- gc=gcd(a.denominator(),b.denominator())
- print " nk=",nk,"gc=",gc
- if gc==1:
- break
- assert gc==1
- a=toVecR(a*a.denominator())
- b=toVecR(b*b.denominator())
- # we now work with vectors of size nenc=r+m=3m+2
- a=vector(a.list()+[0]+[0 for i in range(nenc-nk)])
- b=vector([b[0]]+[0]+b.list()[1:]+[0 for i in range(nenc-nk)])
- print " gcd computation"
- gc,ca,cb=xgcd(ZZ(a[0]),ZZ(b[0]))
- u=ca*a+cb*b # we have u[0]=1 len(u)=nenc
- ps=prodScal([t[l][0] for l in range(nenc)],u) % f
- print " Check scalar product:",ps
- assert ps==0
- print "Phase 2: expressing the encoding of s_kappa on first row as a linear combination of the others."
- A00 = Aij[0][0]
- # t1
- C0L = [Cij[0][0][l] for l in range(nenc)]
- # t2*...*t_{kappa_1} t_{kappa}
- C0R = [prod([Cij[0][j][l0] for j in range(1,kappa-1)]) * Cij[0][kappa-1][l] % f \
- for l in range(nenc)]
- # t_kappa
- C1L = [Cij[1][0][l] for l in range(nenc)]
- # t_1 t_2*...*t_{kappa-1}
- C1R = [Cij[1][1][l] * prod([Cij[1][j][l0] for j in range(2,kappa)]) % f \
- for l in range(nenc)]
- W=Matrix(K,[[roundCoef(( \
- (A00.a*C0L[j]*C0R[i]-A10.a*C1L[i]*C1R[j]) % f)[0]) \
- for j in range(1,1+r)] for i in range(nenc)])
- vker=W.kernel().matrix()[0]
- # same as the attack without safeguards
- print "Phase 3: computing Omega and computing the error"
- A00 = Aij[0][0]
- # t1
- C0L = [Cij[0][0][l] for l in range(nenc)]
- # t2*...*t_{kappa_1} t_{kappa}
- C0R = [prod([Cij[0][j][0] for j in range(1,kappa-1)]) * Cij[0][kappa-1][l] % f \
- for l in range(nenc)]
- # t2*...*t_{kappa-1} t_{kappa}
- CkL = [prod([Cij[k][j][0] for j in range(kappa-2)]) * Cij[k][kappa-2][l] % f \
- for l in range(nenc)]
- # t_1
- CkR = [Cij[k][k][l] for l in range(nenc)]
- Omega=vector(K,sum([vector([roundCoef(((A00.a*C0L[j]*C0R[l]-Ak0.a*CkL[l]*CkR[j]) % f)[0]) \
- for l in range(1,nenc)])*(-u[j]) % f for j in range(1,len(u))]))
- # estimated error
- EC=roundFromK(prodScal(Omega,(-vker)[1:]))
- # equivalent of D00, the private key for user 1.
- D0Lp=prodScal([-ui for ui in u[1:]],C0L[1:]) %f
- print "true secret:"
- print A00.a*prod([Cij[0][i][0] for i in range(kappa)]) % f
- print "Computing the first component of the secret:"
- print (A00.a*D0Lp*prod([Cij[0][i][0] for i in range(1,kappa)]) % f)[0]-EC
Advertisement
Add Comment
Please, Sign In to add comment