Tags: lattice crypto bbs linearization
Rating: 5.0
The PRNG is a B.B.S.-like generator, with update function F(v)=v2+b over Z/pZ.
We're given 2/3 of higher-order bits of the first two outputs, and the FLAG is Xor-ed with the next five outputs.
Denote the unknown parts as x1, x2, we have
{v0=2n−kw0+x0v1=2n−kw1+x1v1≡v20+b(modp)
Expand and rearrange, we get
2w0×x0+(w20−w1+b)×1−p×k=x1−x20,
(this k is not that k, don't mind)
Here we hide the variable x1 in the right term, and see it as a new variable. Then we can solve it with LLL algorithm over 3-rank basis.
Write it in matrix form: (x01−k)T×(102w001w20−w1+b00p)=(x0,1,x1−x20)
Next we need to adjust the scale to make the norm satisfy the Minkowski's bound (also make sure the second element is 1)
Here I choose A=p/X, B=p, C=p/X2, then
norm<√3pdet=p4/X3
Since X≈p1/3, it does.
After we found x0, it's easy to get x1. Then the PRNG is broken.
exp.sage :
p = 86160765871200393116432211865381287556448879131923154695356172713106176601077
b = 71198163834256441900788553646474983932569411761091772746766420811695841423780
m = 88219145192729480056743197897921789558305761774733086829638493717397473234815
w0 = 401052873479535541023317092941219339820731562526505
w1 = 994046339364774179650447057905749575131331863844814
nbits = 256
d = 2
k = ceil(nbits * (d / (d + 1)))
w0 <<= (nbits - k)
w1 <<= (nbits - k)
AA = p >> (nbits-k)
BB = p
CC = p >> (2*(nbits-k))
M = Matrix(ZZ, [
[AA, 0, CC*2*w0],
[0, BB, CC*(w0^2-w1+b)],
[0, 0, CC*p]
])
b1 = M.LLL()[0]
if b1[0] < 0:
b1 *= -1
assert b1[1] == BB
x0 = ZZ(b1[0] / AA)
# x0 = 1319495720667326863431558
x1 = ZZ(b1[2] / CC) + x0^2
# x1 = 1811790233058988426434106
v0 = w0 + x0
v1 = w1 + x1
assert v1 == (v0^2 + b) % p
task.sage :
from Crypto.Util.number import*
from flag import flag
nbits = 256
p = random_prime(1 << nbits)
Fp = Zmod(p)
P.<v> = PolynomialRing(Fp)
b = randrange(p)
d = 2
F = v^2 + b
v0 = randrange(p)
v1 = F(v0)
k = ceil(nbits * (d / (d + 1)))
w0 = (v0 >> (nbits - k))
w1 = (v1 >> (nbits - k))
# encrypt
m = bytes_to_long(flag)
v = v1
for i in range(5):
v = F(v)
m ^^= int(v)
print(f"p = {p}")
print(f"b = {b}")
print(f"m = {m}")
print(f"w0 = {w0}")
print(f"w1 = {w1}")