N1CTF2021
:: Contents
Challenge | Tags |
---|---|
checkin | Coppersmith, Math |
n1token1 | HNP, Quadratic Sieve |
n1token2 | Linear Algebra |
checkin
task.py
from Crypto.Util.number import *
from secret import flag
p = getPrime(512)
q = getPrime(512)
n = p*q
x = 2021*p+1120*q
h = (inverse(x,n)+x)%n
e = 65537
c = pow(bytes_to_long(flag), e, n)
print('n =', n)
print('c =', c)
print('h =', h)
print('p0 =', p >> 490)
# n = 124592923216765837982528839202733339713655242872717311800329884147642320435241014134533341888832955643881019336863843062120984698416851559736918389766033534214383285754683751490292848191235308958825702189602212123282858416891155764271492033289942894367802529296453904254165606918649570613530838932164490341793
# c = 119279592136391518960778700178474826421062018379899342254406783670889432182616590099071219538938202395671695005539485982613862823970622126945808954842683496637377151180225469409261800869161467402364879561554585345399947589618235872378329510108345004513054262809629917083343715270605155751457391599728436117833
# h = 115812446451372389307840774747986196103012628652193338630796109042038320397499948364970459686079508388755154855414919871257982157430015224489195284512204803276307238226421244647463550637321174259849701618681565567468929295822889537962306471780258801529979716298619553323655541002084406217484482271693997457806
# p0 = 4055618
Solution
$h=\frac{1}{x}+x$
$\Rightarrow x^2-h\cdot x+1(mod\ n)$
We can get high bits of q through the known p high bits.
$x=2021\cdot p+1120\cdot q=(2021\cdot(2^{490}\cdot p_0+p_1)+1120\cdot(2^{490}\cdot q_0+q_1))$
The unknown part of x is less than 501 bits
small_roots
can solve this with epsilon=0.02
It’s higher than the theoretical bound, may be LLL sometimes do better.
n = 124592923216765837982528839202733339713655242872717311800329884147642320435241014134533341888832955643881019336863843062120984698416851559736918389766033534214383285754683751490292848191235308958825702189602212123282858416891155764271492033289942894367802529296453904254165606918649570613530838932164490341793
c = 119279592136391518960778700178474826421062018379899342254406783670889432182616590099071219538938202395671695005539485982613862823970622126945808954842683496637377151180225469409261800869161467402364879561554585345399947589618235872378329510108345004513054262809629917083343715270605155751457391599728436117833
h = 115812446451372389307840774747986196103012628652193338630796109042038320397499948364970459686079508388755154855414919871257982157430015224489195284512204803276307238226421244647463550637321174259849701618681565567468929295822889537962306471780258801529979716298619553323655541002084406217484482271693997457806
p0 = 4055618
q0 = None
head = n>>(1024-22)
for i in range(2**22):
if (i*p0)>>22 == head:
q0 = i
break
PR.<x> = PolynomialRing(Zmod(n))
g = x+2021*2^490*p0+1120*2^490*q0
f = g^2-h*g+1
root = ZZ(f.small_roots(X=2^500, beta=1, epsilon=0.02)[0])
x0 = root+2021*2^490*p0+1120*2^490*q0
diff = isqrt(x0^2-4*2021*1120*n)
p = (x0+diff)//4042
q = (x0-diff)//2240
phi = (p-1)*(q-1)
d = inverse_mod(0x10001, phi)
m = pow(c, d, n)
print(bytes.fromhex(hex(m)[2:]))
n1token1
task.py
from Crypto.Util.number import *
import random
from secret import flag
def gettoken(c):
X = 0
while ((pow(X, (p-1)//2, p)!=1) or (pow(X, (q-1)//2, q)!=1)):
X = 1
while X.bit_length() < 920:
X *= random.choice(primes)
xp = pow(X, (p + 1)//4, p)
xq = pow(X, (q + 1)//4, q)
xp = random.choice([xp,-xp%p])
xq = random.choice([xq,-xq%q])
x = c * (xp*inverse(q,p)*q + xq*inverse(p,q)*p) % n
return x
def getmyPrime(nbits):
p = getPrime(nbits)
while(p%4==1):
p = getPrime(nbits)
return p
primes = random.sample(sieve_base, 920)
p = getmyPrime(512)
q = getmyPrime(512)
e = 65537
n = p*q
c = pow(bytes_to_long(flag), e, n)
with open("output.txt", "w")as f:
f.write("n = " + str(n) + "\n")
for i in range(920):
f.write("Token #"+str(i+1)+': '+str(gettoken(c))+'\n')
Solution
The token generating as follow,
$tk_i = c\cdot \sqrt{X_i}(mod\ n)$
$\Rightarrow tk_i^2\cdot(c^{-2})=X_i(mod\ n)$ Where $X<2^{940}$
So we can solve this hidden number problem to recover $X_i$.
$\Rightarrow tk_0^2\cdot (tk_i^2)^{-1}=X_0\cdot X_i^{-1}(mod\ n)$
Rewriting the $X_i$ with primes’ exponent, and solve the kernel of exponent matrix in $F_2$ (only care parity)
Finally, we can construct $a^2=b^2(mod\ n)$, it’s probably get the factors of n.
from Crypto.Util.number import *
from functools import reduce
f = open('output.txt', 'r')
f.read(4)
n = int(f.readline())
tk = []
for _ in range(920):
tk_ = f.readline()
st = tk_.index(':')
tk.append(int(tk_[st+1:]))
def solver_hnp(s):
L = matrix(QQ, 101, 101)
L[0, 0] = 2^(940-1024)
for _ in range(100):
L[0, _+1] = s[_]**2%n
L[_+1, _+1] = n
basis = L.LLL()[1]
g = reduce(GCD, basis[1:])
c_ = basis[0]*2^(1024-940)//g
return c_
c_ = solver_hnp(tk[:100])
if (c_*tk[0]**2)%n > 2^940:
c_ = -c_
X = []
for tk_ in tk:
X.append((tk_**2*(c_)%n))
primes = set()
for x in X:
facs = list(factor(x))
for item in facs:
primes.add(item[0])
primes = list(primes)
ch = []
for x in X:
u = [0]*920
facs = list(factor(x))
for item in facs:
u[primes.index(item[0])] = item[1]
ch.append(u)
std = vector(ZZ, ch[0])
Ch = []
for i in range(1, len(ch)):
Ch.append(std-vector(ch[i]))
A = matrix(Zmod(2), Ch)
ker = vector(ZZ, list(A.left_kernel().matrix()[0]))
res = ker*matrix(ZZ, Ch)
a = 1; b = 1
for i in range(len(ker)):
if ker[i]:
a *= tk[0]
b *= tk[i+1]
for i in range(len(res)):
if res[i] > 0:
b *= pow(primes[i], res[i]//2, n)
else:
a *= pow(primes[i], abs(res[i])//2, n)
p = GCD(a+b, n)
q = n//p
Fp = GF(p)
Fq = GF(q)
phi = (p-1)*(q-1)
d = inverse_mod(0x10001, phi)
c2 = inverse_mod(X[0], n)*tk[0]**2%n
m2 = pow(c2, d, n)
mp = Fp(m2).nth_root(2, all=True)
mq = Fq(m2).nth_root(2, all=True)
for i in mp:
for j in mq:
m = crt([int(i), int(j)], [p, q])
print(long_to_bytes(m))
n1token2
task.py
import random
from secret import flag
assert FLAG.startswith('n1ctf{')
assert FLAG.endswith('}')
SECRET = bytes.fromhex(FLAG[6:-1])
assert len(SECRET) == 16
p = 251
e = [1, 20, 113, 149, 219]
y = b''
for x in range(1, p):
coeff = [random.choice(e)] + list(SECRET)
y += bytes([sum(c * pow(x, i, p) for i, c in enumerate(coeff)) % p])
print(f'Token: {y.hex()}')
Solution
We can use the prod idea to solve multiple choices problem
assume $F(x)=\sum_{i=1}\limits^{16}a_ix^i$
So the relation can be write as follow,
$\prod_{i=0}\limits^4(F(x_j)+e_i-y_j)=0$
$a_0+a_1\cdot F(x_j)+a_2\cdot F^2(x_j)+a_3\cdot F^3(x_j)+a_4\cdot F^4(x_j)+a_5\cdot F^5(x_j)=0$
Considering $F^j(x)$ as independent polynomial, there are total 245 unknow coefficients.
With 250 tokens, solving the matrix to recover all coefficient.
token = 'ae47d3533e10b444374ebd35f578103f94c327b5959f4580b0dc4701d089f22c282acfed2626bd9ced919547dacc96c18595468952cbedbcf518b5ec0fbc0437ea8e3bd77c5f01f73b555cc97a382df1ce28b4af7f9a5ab9c47e055044f47463c4f33bebdd4d6965599c71720b5a549a8246cb200dc4e2dccfc4765c0ac266a8a5385172aa47f834db737a3bcc725ae5a1ec8b76d1ca4800da10450b04543377efa5428584efdfdf55282e0e4410ae7848a0217a7d66f2d6c06c846a4755867dbdf61c70646f67debf44839a342e5f64e3abd2c9f5b66656edc8004cea987503793f7e07e3b60af4852e03177cae1e8ee605aab76d3a70df4e1e'
p = 251
e = [1, 20, 113, 149, 219]
A = []
v = []
Z.<x> = PolynomialRing(Zmod(p))
for i in range(0, len(token), 2):
tk = int(token[i:i+2], 16)
a = []
for e_ in e:
a.append(e_-tk)
f = (x+a[0])*(x+a[1])*(x+a[2])*(x+a[3])*(x+a[4])
coeff = (list(f))[::-1]
x0 = i//2+1
u = []
for _ in range(81):
u.append(coeff[0]*x0^_)
for _ in range(65):
u.append(coeff[1]*x0^_)
for _ in range(49):
u.append(coeff[2]*x0^_)
for _ in range(33):
u.append(coeff[3]*x0^_)
for _ in range(17):
u.append(coeff[4]*x0^_)
v.append(-coeff[-1])
A.append(u)
A = matrix(Zmod(p), A)
v = vector(Zmod(p), v)
sol = A.solve_right(v)
secret = bytes(sol[-16:]).hex()
print(secret)