from Crypto.Util.number import bytes_to_long from string import ascii_lowercase from sympy import nextprime from secret import FLAG import numpy as np import random import signal import os
defuniform_sample(n, bound, SecureRandom): return [SecureRandom.randrange(-bound, bound) for _ inrange(n)]
defternary_sample(n, ternaryL, SecureRandom): return [ternaryL[int(_)] for __ inrange(n // 5) for _ in np.base_repr(ord(SecureRandom.choice(ascii_lowercase)), 3)]
n = 137 m = 220 q = nextprime(1337) e_L = [0, 101, 731] R_s= random.SystemRandom() s = np.array(uniform_sample(n, q//2, R_s)) R_e = random.SystemRandom() e = np.array(ternary_sample(m, e_L, R_e)) seed = os.urandom(16) R_A = random R_A.seed(seed) A = np.array([uniform_sample(n, q, R_A) for _ inrange(m)]) b = (A.dot(s) + e) % q print(f"{seed.hex() = }") print(f"{b.tolist() = }") s_ = input("Give me s: ") if s_ == str(s.tolist()): print("Congratulations! You have signed in successfully.") print(FLAG) else: print("Sorry, you cannot sign in.")
题目基于$(m,n)=(220,137)$的LWE:
给出$A,b$,需要我们在60s内解出私钥$s$并提交给靶机,从而拿到flag。
对于这个题目的LWE来讲,能利用的地方主要有:
$(m,n)$不算很大
$e$仅由三个值[0, 101, 731]组成
并且仔细观察一下生成$e$的函数:
1 2
defternary_sample(n, ternaryL, SecureRandom): return [ternaryL[int(_)] for __ inrange(n // 5) for _ in np.base_repr(ord(SecureRandom.choice(ascii_lowercase)), 3)]
########################################################################## part1 get data from Crypto.Util.number import * from pwn import process, context, remote import random
defuniform_sample(n, bound, SecureRandom): return [SecureRandom.randrange(-bound, bound) for _ inrange(n)]
n = 137 m = 220 q = next_prime(1337) R_A = random R_A.seed(SEED) A = Matrix(Zmod(q), ([uniform_sample(n, q, R_A) for _ inrange(m)])).T b = vector(Zmod(q), b)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
########################################################################## part2 block A, b A1 = Matrix(Zmod(q), n, 0) A2 = Matrix(Zmod(q), n, 0) b1 = [] b2 = [] known = m // 5 for i inrange(m // 5): A1 = A1.augment(A[:, 5*i:5*i+1]) A2 = A2.augment(A[:, 5*i+1:5*i+5]) b1.append(b[5*i]) b2 += b[5*i+1:5*i+5] b1 = vector(Zmod(q), b1) b2 = vector(Zmod(q), b2)
########################################################################## part3 convert to (137-44)-LWE X = A12*A11^(-1) Y = (b1 - vector(Zmod(q), [101]*known))*A11^(-1)
########################################################################## part4 *283 - 1 to make error -> (1,0,-1) and get e2 defprimal_attack2(A,b,m,n,p): L = block_matrix( [ [matrix(Zmod(p), A).T.echelon_form().change_ring(ZZ), 0], [matrix.zero(m - n, n).augment(matrix.identity(m - n) * p), 0], [matrix(ZZ, b), 1], ] ) L = L.BKZ(block_size=10) res = L[0] if(res[-1] == 1): e2 = res[:-1] else: e2 = -res[:-1] return e2
nums = m - known e2 = primal_attack2(283*A_.T[:nums], (283*b_-vector(Zmod(q), [1]*len(b_)))[:nums], nums, n-known, q)
1 2 3 4 5 6 7 8 9 10 11 12 13
########################################################################## part5 get s and get flag e = (vector(Zmod(q), [1]*known + e2.list()) + (vector(Zmod(q), [1]*m))) * 731 s = (A1.augment(A2)).solve_left(vector(Zmod(q), b1.list()+b2.list()) - e).list() for i inrange(len(s)): if(s[i] > q // 2): s[i] = int(s[i]) - q
sh.recvuntil(b"Give me s: ") sh.sendline(str(s).encode()) print(sh.recvline()) print(sh.recvline())
defrun(self): self.chall_lst = [randint(0,1) for _ inrange(self.CHALL_NUM)] whilesum(self.chall_lst) == 0orsum(self.chall_lst) == self.CHALL_NUM: self.chall_lst = [randint(0, 1) for _ inrange(self.CHALL_NUM)]
for _ inrange(self.CHALL_NUM): print(f"Now, for the {_} round of PoK:") self._commit() self._challenge(self.chall_lst[_]) ifnot self._verify(): returnFalse returnTrue
J = Ea.isogeny(Sb * phiPb + Tb * phiQb, algorithm="factored").codomain().j_invariant() Sa, Ta = hash_function(J) EA = E0.isogeny(Sa * Pa + Ta * Qa, algorithm="factored").codomain()
s = set() for _ inrange(FAKE_NUM): print("Give me your share: ")
if(1): sh.recvuntil(b"Elliptic Curve defined by y^2 = x^3 + 6*x^2 + ") Eb = sh.recvline().strip().decode().strip(" over Finite Field in i of size 84495767949234467194240606666751^2").split("*x + ") Eb = EllipticCurve(Fp2, [0, 6, 0, eval(Eb[0][1:-1]), eval(Eb[1][1:-1])]) psiPa = convert_2_point(Eb, sh.recvline().strip().decode()[8:]) psiQa = convert_2_point(Eb, sh.recvline().strip().decode()[8:])
###################################################################### part2 get EA J = Ea.isogeny(phiQb, algorithm="factored").codomain().j_invariant() Sa, Ta = hash_function(J) RA = Sa * Pa + Ta * Qa assert RA.weil_pairing(E0(1,2^26*3^18), 2^a) == p-1 EA = E0.isogeny(RA, algorithm="factored").codomain()
deffind_equ_ker(RA): Ej = EllipticCurve(Fp2, [0, 0, 0, 1, 0]) phi1 = E0.isogeny(E0(0, 0), algorithm="factored").codomain().isomorphism_to(Ej)*E0.isogeny(E0(0, 0)) TT = [RA] for j in Ej.automorphisms()[3:]: PHI = phi1.dual()*j for kk in phi1(RA).division_points(2): if(E0.isogeny(PHI(kk), algorithm="factored").codomain().j_invariant() == EA.j_invariant()): if(PHI(kk).order() > 2^(a-2)): TT.append(PHI(kk)) if(E0.isogeny(PHI(kk)+RA, algorithm="factored").codomain().j_invariant() == EA.j_invariant()): if((PHI(kk)+RA).order() > 2^(a-2)): TT.append(PHI(kk)+RA) TT = list(set(TT)) for tt inrange(len(TT)): for kk inrange(tt, len(TT)): for rr inrange(kk, len(TT)): if(TT[tt].weil_pairing(TT[kk], 2^a) != 1and TT[rr].weil_pairing(TT[tt], 2^a) != 1and TT[rr].weil_pairing(TT[kk], 2^a) != 1): print() return TT[kk], TT[tt], TT[rr]
li = find_equ_ker(RA)
###################################################################### part3 ZKP for j inrange(3): ppa,qqa = get_canonical_basis(E0, 3, b)
from Crypto.Util.number import getPrime, bytes_to_long, inverse from Crypto.Random.random import randrange, getrandbits from math import prod from secret import FLAG import os import signal
defLevel(level, primes, ROUND): print(f"This is level {level}!") N = prod(primes) phi = prod([(pp - 1) for pp in primes]) d = inverse(0x10001, phi) m = bytes_to_long(os.urandom(N.bit_length() // 8 - 2)) c = pow(m, 0x10001, N)
idx = int(input("Choose one prime you prefer: ")) assert idx inlist(range(len(primes))), "No such prime" mod = primes.pop(idx) print(f"Here is your prime: {mod}") print(f"{c = }") print(f"{N = }")
if level == 0: a = [getrandbits(496) for _ inrange(ROUND)] b = getrandbits(248) c = [getrandbits(496) for _ inrange(ROUND)] e = b ph1 = [prod([(primes[0] + a[i]), (primes[1] + b)]) for i inrange(ROUND)] ph2 = [prod([(-primes[0] + c[i]), (primes[1] + e)]) for i inrange(ROUND)] else: a = [getrandbits(160) for _ inrange(ROUND)] b = a c = [ai + 1for ai in a] e = c ph1 = [prod([(primes[0] + a[i]), (primes[1] + b[i])]) for i inrange(ROUND)] ph2 = [prod([(primes[0] - c[i]), (primes[1] + e[i])]) for i inrange(ROUND)]
for i inrange(ROUND): x0 = randrange(0, N) x1 = randrange(0, N) print(f"{x0 = }") print(f"{x1 = }") v = int(input("Give me v: ")) m0 = (pow(v - x0, d, mod) + ph1[i]) % mod m1 = (pow(v - x1, d, mod) + ph2[i]) % mod print(f"{m0 = }") print(f"{m1 = }") m_ = int(input("Give me m: ")) if m_ == m: print("Good job!") returnTrue else: print("Try again!") returnFalse
primes1 = [getPrime(512) for _ inrange(3)] primes2 = [getPrime(512) for _ inrange(4)] if Level(0, primes1, 80) and Level(1, primes2, 80): print("This is your flag:", FLAG)
####################################################################### exp from Crypto.Util.number import *
defsol_0(phis, N, c, mod): M = Matrix(ZZ, 82, 82) for i inrange(80): M[i, i] = mod M[-2, i] = -phis[i] M[-1, i] = -2^496 M[-2, -2] = 1 M[-1, -1] = 1
Q = diagonal_matrix(ZZ, [2^15]*80 + [1] + [mod]) M = M * Q M = M.LLL() M = M / Q M = Matrix(ZZ, M) for i in M: if(i[-1] == -1andabs(i[-2])> 2^500): Kinv = int(i[-2]) K = int(inverse(Kinv, mod)) while(len(bin(K)[2:]) < 512): K += mod break elif(i[-1] == 1andabs(i[-2])> 2^500): Kinv = mod-int(i[-2]) K = int(inverse(Kinv, mod)) while(len(bin(K)[2:]) < 512): K += mod break
pq = N // mod Psh.<x> = PolynomialRing(Zmod(pq)) f = K - 2*x if(K % 2 == 0): f -= 1 f = f.monic() res = f.small_roots(X=2^247, beta=0.499,epsilon=0.016) if(res != []): q = gcd(int(f(res[0])),pq) p = (N//mod)//q primes = [p,q,mod] phi = prod([(pp - 1) for pp in primes]) d = int(inverse(0x10001, phi)) m = int(pow(c,d,N)) return m
res = Ideal([f10, f11, f20, f21, f30, f31]).groebner_basis() p = mod-ZZ(res[0].univariate_polynomial()(0)) q = mod-ZZ(res[1].univariate_polynomial()(0)) while(len(bin(p)[2:]) < 512): p += mod while(len(bin(p)[2:]) < 512): q += mod assert isPrime(p) and isPrime(q) s = (N//mod)//q//p primes = [p,q,mod,s] phi = prod([(pp - 1) for pp in primes]) d = int(inverse(0x10001, phi)) m = int(pow(c,d,N)) return m