前言
用来记录我的格密码学习,参考资料是NSS工坊和一些blog
NTRU
1.入门题
task.py
import gmpy2from secret import flagfrom Crypto.Util.number import *
f = bytes_to_long(flag)p = getPrime(512)g = getPrime(128)h = gmpy2.invert(f+20192020202120222023, p) * g % p
print('h =', h)print('p =', p)
想要flag就要求出f,f=f+20192020202120222023,最后减去这个数就好了,那么已知的式子就变成了
在本质的计算中,他是拿向量(h,1)(p,0)去做线性组合,如果两组是x_1$$x_2,那么就变成了 ,而当就是我们想要的结果
exp.sage
from sage.all import*from Crypto.Util.number import *
h =p =
zsm=Matrix(ZZ,[[1,h],[0,p]])f,g=zsm.LLL()[0]print(long_to_bytes(abs(f)-20192020202120222023))
与此题相似的还有LitCTF2025的baby,不过那个需要配平一下
2.HNCTF 2022 WEEK2
task.py
from Crypto.Util.number import *from hashlib import *
p = getPrime(2048)f = getPrime(1024)g = getPrime(768)h = pow(f,-1,p)*g%pverify = sha256(bytes.fromhex(hex(f+g)[2:])).hexdigest()print(f'verify = {verify}')print(f'p = {p}')print(f'h = {h}')print('NSSCTF{' + md5(bytes.fromhex(hex(f+g)[2:])).hexdigest() + '}')
你会发现和上一题差不多的感觉()
exp.sage
from sage.all import*from Crypto.Util.number import *from hashlib import *verify = ""p =h =
zsm=Matrix(ZZ,[[1,h],[0,p]])f,g=zsm.LLL()[0]if sha256(bytes.fromhex(hex(abs(f)+abs(g))[2:])).hexdigest() == verify: flag = 'NSSCTF{' + md5(bytes.fromhex(hex(f+g)[2:])).hexdigest() + '}' print(flag)
3.不知道哪来的
task.py
from Crypto.Util.number import *
p = getPrime(1024)
f = getPrime(400)g = getPrime(512)r = getPrime(400)
h = inverse(f, p) * g % p
m = b'******'m = bytes_to_long(m)
c = (r*h + m) % p
print(f'p = {p}')print(f'h = {h}')print(f'c = {c}')
先把h代入进去,然后化简
要求m就要知道fcpg,cp已知,求fg
exp.sage
from sage.all import*from Crypto.Util.number import *
p =h =c =
zsm=Matrix(ZZ,[[1,h],[0,p]])f,g=zsm.LLL()[0]f,g=abs(f),abs(g)m=((f*c%p)*inverse(f,g))%gprint(long_to_bytes(m))
4.深育杯2021
task.py
from Crypto.Util.number import *import gmpy2from flag import flag
def encrypt(plaintext): p = getStrongPrime(3072) m = bytes_to_long(plaintext) r = getRandomNBitInteger(1024) while True: f = getRandomNBitInteger(1024) g = getStrongPrime(768) h = gmpy2.invert(f, p) * g % p c = (r * h + m * f) % p return (h, p, c)
h, p, c = encrypt(flag)with open("cipher.txt", "w") as f: f.write("h = " + str(h) + "\n") f.write("p = " + str(p) + "\n") f.write("c = " + str(c) + "\n")
格和前面构造的一样,主要是c的化简不一样,这里就省略了
NSS工坊题目
P3
task.py
from Crypto.Util.number import *import random
flag = b'******'m = bytes_to_long(flag)
a = getPrime(1024)b = getPrime(1536)
p = getPrime(512)q = getPrime(512)r = random.randint(2**14, 2**15)assert ((p-r) * a + q) % b < 50
c = pow(m, 65537, p*q)
print(f'c = {c}')print(f'a = {a}')print(f'b = {b}')
xr都是很小的数,可以爆破,值得注意的是,先爆破r再爆破x会快一点xd
exp.sage
from sage.all import*from Crypto.Util.number import *
c =a =b =e=65537zsm=Matrix(ZZ,[[a,1],[b,0]])xq,pr=zsm.LLL()[0]xq,pr=abs(xq),abs(pr)
for r in range(2**14,2**15): for x in range(50): p=pr+r q=x+xq n=p*q d=inverse(e,(p-1)*(q-1)) m=pow(c,d,n) flag=long_to_bytes(m) if b'NSSCTF{' in flag: print(flag) break
P4
task.py
from Crypto.Util.number import *from gmpy2 import *
flag = b'******'flag = bytes_to_long(flag)
p = getPrime(1024)r = getPrime(175)a = inverse(r, p)a = (a*flag) % p
print(f'a = {a}')print(f'p = {p}')
exp.sage
from sage.all import*from Crypto.Util.number import *
a =p =e=65537zsm=Matrix(ZZ,[[a,2**170],[p,0]])flag,R=zsm.LLL()[0]flag,R=abs(flag),abs(R)
print(long_to_bytes(flag))
P5
task.py
from Crypto.Util.number import *from gmpy2 import *
flag = b'******'m = bytes_to_long(flag)
assert m.bit_length() == 351p = getPrime(1024)b = getPrime(1024)c = getPrime(400)
a = (b*m + c) % p
print(f'a = {a}')print(f'b = {b}')print(f'p = {p}')
exp.sage
from sage.all import*from Crypto.Util.number import *
a =b =p =
zsm=Matrix(ZZ,[[a,0,2**351],[-b,1,0],[p,0,0]])c,m,x=zsm.LLL()[0]c,m,x=abs(c),abs(m),abs(x)
print(long_to_bytes(m))
P6
task.py
from Crypto.Util.number import *
flag = b'******'flag = bytes_to_long(flag)d = getPrime(400)
for i in range(4): p = getPrime(512) q = getPrime(512) n = p * q e = inverse(d, (p-1)*(q-1)) c = pow(flag, e, n) print(f'e{i} =', e) print(f'n{i} =', n) print(f'c{i} =', c)
原型应该是NUSTCTF 2022 新生赛
的一个论文题,IJCSI-9-2-1-311-314
具体实现方法是把最大的n开根号赋值给M,然后还有,然后这里假设了phi可以写作N-s
,使式子变成了
exp.sage
from Crypto.Util.number import *from sage.all import*
M = isqrt(n0)
L = Matrix(ZZ, [[M, e0, e1, e2, e3], [0,-n0, 0, 0, 0], [0, 0,-n1, 0, 0], [0, 0, 0,-n2, 0], [0, 0, 0, 0,-n3]])
d = abs(L.LLL()[0][0]) // M
m = power_mod(c0, d, n0)
print(long_to_bytes(m))
P7
task.py
from Crypto.Util.number import *
flag = b'******'flag = bytes_to_long(flag)
p = getPrime(512)q = getPrime(512)n = p * qc = pow(flag, 65537, n)print(f'n =', n)print(f'c =', c)for i in range(2): d = getPrime(350) e = inverse(d, (p-1)*(q-1)) print(f'e{i} =', e)
这个其实就是维纳拓展攻击,从一元变成了二元,直接上脚本了
exp.sage
from Crypto.Util.number import *n =c =e0 =e1 =a = 5/14D = diagonal_matrix(ZZ, [n, int(n^(1/2)), int(n^(1+a)), 1])M = Matrix(ZZ, [[1, -n, 0, n^2], [0, e0, -e0, -e0*n], [0, 0, e1, -e1*n], [0, 0, 0, e0*e1]])*DL = M.LLL()t = vector(ZZ, L[0])x = t * M^(-1)
x * M = tphi = int(x[1]/x[0]*e0)
d = inverse_mod(65537, phi)m = power_mod(c, d, n)print(long_to_bytes(m))
P8
task.py
from Crypto.Util.number import *
flag = b'******'m = bytes_to_long(flag)
p = getPrime(512)s = [getPrime(32) for i in range(3)]a = [getPrime(512) for i in range(3)]
c = (a[0]*s[0]**2*s[1]**2 + a[1]*s[0]*s[2]**2 + a[2]*s[1]*s[2]) % p
flag = m*s[0]*s[1]*s[2]print(f'c = {c}')print(f'flag = {flag}')print(f'a = {a}')print(f'p = {p}')
目的是求s,要对这个式子变形,想办法使未知量在一侧
发现直接这样打出不来,配平一下
exp.sage
from Crypto.Util.number import *
c =flag =a = []p =
ia = inverse_mod(a[2], p)
L = Matrix(ZZ, [[a[0]*ia%p, 0, 0, 1], [a[1]*ia%p, 0, 1, 0], [-c*ia%p, 1, 0, 0 ], [p, 0, 0, 0]]) * diagonal_matrix(ZZ, [1, 2^32, 2^128, 2^64])
v = L.LLL()[0]s0s1 = isqrt(abs(v[0]))s1s2 = abs(v[3]) >> 64s1 = gcd(s0s1, s1s2)s0 = s0s1 // s1s2 = s1s2 // s1
flag = flag // s0 // s1 // s2print(long_to_bytes(flag))
HNP问题
形式比较固定,一般长这样,我们一般会有多组kAB,所以我们可以依靠这个去建格。
这种式子经常出现在DSA签名中
这就是一个非常标准的HNP了,建一个格
对应脚本
import json
t = 40
# Load dataf = open("data", "r")(q, Hm_s, r_s, s_s) = json.load(f)
# Calculate A & BA = []B = []for r, s, Hm in zip(r_s, s_s, Hm_s): A.append( ZZ( (inverse_mod(s, q)*r) % q ) ) B.append( ZZ( (inverse_mod(s, q)*Hm) % q ) )
# Construct LatticeK = 2^122 # ki < 2^122X = q * identity_matrix(QQ, t) # t * tZ = matrix(QQ, [0] * t + [K/q] + [0]).transpose() # t+1 columnZ2 = matrix(QQ, [0] * (t+1) + [K]).transpose() # t+2 column
Y = block_matrix([[X],[matrix(QQ, A)], [matrix(QQ, B)]]) # (t+2) * tY = block_matrix([[Y, Z, Z2]])
# Find short vectorY = Y.LLL()
# checkk0 = ZZ(Y[1, 0] % q)x = ZZ(Y[1, -2] / (K/q) % q)assert(k0 == (A[0]*x + B[0]) % q)print(x)
/*** 复制并使用代码请注明引用出处哦~* Lazzaro @ https://lazzzaro.github.io*/
当然现在LCG的题目里面也有HNP了,比如LCG已知state高位求seed/LCG未知a,b求seed,这种题目在0xGame
中是出现过的
1.2023闽盾杯
task.py
from random import randbytesfrom hashlib import sha256from secret import FLAG
prime_q =prime_p = 2 * prime_q + 1generator = 2
def generate_keys(prime_p:int, prime_q: int, generator: int): private_key = int(randbytes(48).hex(), 16) public_key = pow(generator, private_key, prime_p) return private_key, public_key
def signature(m: str, private_key: int): ephemeral_key = pow(int.from_bytes(m.encode(), "big"), -1, prime_q) value_r = pow(generator, ephemeral_key, prime_p) % prime_q hash_value = sha256(m.encode()).hexdigest() value_s = pow(ephemeral_key, -1, prime_q) * (int(hash_value, 16) + private_key * value_r) % prime_q return hash_value, value_r, value_s
def verification(message_hash: str, value_r: int, value_s: int, public_key: int): message_hash = int(message_hash, 16) inverse_s = pow(value_s, -1, prime_q) u1 = message_hash * inverse_s % prime_q u2 = value_r * inverse_s % prime_q value_v = (pow(generator, u1, prime_p) * pow(public_key, u2, prime_p) % prime_p) % prime_q return value_v == value_r
private_key, public_key = generate_keys(prime_p, prime_q, generator)print(f"prime_p = {prime_p}")print(f"prime_q = {prime_q}")print(f"generator = {generator}")print(f"public_key = {public_key}")hash_value, value_r, value_s = signature(FLAG, private_key)assert verification(hash_value, value_r, value_s, public_key)print("FLAG= *******************************")print(f"Here is your gift = {hash_value}")print(f"value_r = {value_r}")print(f"value_s = {value_s}")
先看看代码,可以看见他把flag加密完带进去当ephemeral_key了,按照上面的式子来说就是
这边配平是因为只打格出不来,就改改,能跑出来就行()
exp.sage
import gmpy2from Crypto.Util.number import *
p =q =g = 2pb =h = ''r =s =
H = int(h,16)inv = gmpy2.invert(H,q)A = -r*invB = s*inv
M = [[q,0,0], [A,1/2^384,0], [B,0,2^320]]
Ge = Matrix(M)
for i in Ge.LLL(): if i[-1] == 2^320: m = i[0] print(long_to_bytes(int(m)))
2.BabyHNP
task.py
from secret import flagfrom random import randintimport libnumimport osassert len(flag) == 44
def padding(f): return f + os.urandom(64 - 1 - len(f))
n = 5m = libnum.s2n(padding(flag))q = libnum.generate_prime(512)A = [randint(1, q) for i in range(n)]B = [A[i] * m % q for i in range(n)]b = [B[i] % 2**128 for i in range(n)]
print('q = %s' % q)print('A = %s' % A)print('b = %s' % b)
这个就和上面的格一模一样了,直接构建就行了
exp.sage
from gmpy2 import *from Crypto.Util.number import *
p =B = [, , , , ]R = [, , , , ]
n = len(R)
M = Matrix(QQ,n+2,n+2)inv = invert(2 ** 128,p)
for i in range(n): M[i,i] = p M[-2,i] = B[i] * inv M[-1,i] = -R[i] * inv
t = 1 / 2^128K = 2^384
M[-2,-2] = tM[-1,-1] = K
L = M.LLL()
x = L[1][-2] // tm = x % pprint(int(m).bit_length())print(long_to_bytes(int(m)))
看了好多师傅的博客,感觉HNP都可以建成这个格,主要难的还是化简构造,得自己多练练,找到这种建格去打的感觉才行
HSP&HSSP
HSP在我的认知里就是背包的升级版?或者说背包就是一种HSP,而HSSP是一种正交格,这个真的不会,看Tover神的博客吧,我只会套脚本
LWE
这种问题的特点在于有一个噪音e(一般比较小),比如原式子是,加入噪音之后变成了,在未知x和e的情况下,我们就可以通过建格去先求出e,然后解方程求出x,构建这个格就行了
SUSCTF2022
task.py
import numpy as npfrom secret import flag
def gravity(n,d=0.25): A=np.zeros([n,n]) for i in range(n): for j in range(n): A[i,j]=d/n*(d**2+((i-j)/n)**2)**(-1.5) return A
n=len(flag)A=gravity(n)x=np.array(list(flag))b=A@xnp.savetxt('b.txt',b)
b已经给出,那么可以知道矩阵的维度,那么就可以恢复出A,再按照上面的格去打出e就行了
exp.sage
import numpy as np
def gravity(n,d=0.25): A=np.zeros([n,n]) for i in range(n): for j in range(n): A[i,j]=d/n*(d**2+((i-j)/n)**2)**(-1.5) return A
b = []for i in open('b.txt','r').readlines(): b.append(float(i.strip()))
n = 85A = gravity(85)
t = 10^21for i in range(len(b)): b[i] = -b[i] * t
for i in range(n): for j in range(n): A[i,j] = A[i,j] * t
M = Matrix(ZZ,n+1,n+1)
for i in range(n): M[-1,i] = b[i] for j in range(n): M[i,j] = A[i,j]
M[-1,-1] = 1
e = M.LLL()[0]flag = M.solve_left(e)
print(bytes(flag[:-1]))
常用脚本
这里直接搬la佬的了
#脚本1-小规模#Sagefrom sage.modules.free_module_integer import IntegerLattice
row =column =prime =
ma =res =
W = matrix(ZZ, ma)cc = vector(ZZ, res)
# Babai's Nearest Plane algorithmdef Babai_closest_vector(M, G, target): small = target for _ in range(5): for i in reversed(range(M.nrows())): c = ((small * G[i]) / (G[i] * G[i])).round() small -= M[i] * c return target - small
A1 = matrix.identity(column)Ap = matrix.identity(row) * primeB = block_matrix([[Ap], [W]])lattice = IntegerLattice(B, lll_reduce=True)print("LLL done")gram = lattice.reduced_basis.gram_schmidt()[0]target = vector(ZZ, res)re = Babai_closest_vector(lattice.reduced_basis, gram, target)print("Closest Vector: {}".format(re))
R = IntegerModRing(prime)M = Matrix(R, ma)M = M.transpose()
ingredients = M.solve_right(re)print("Ingredients: {}".format(ingredients))
m = ''for i in range(len(ingredients)): m += chr(ingredients[i])print(m)
#脚本2-大规模#Sagefrom sage.modules.free_module_integer import IntegerLatticefrom random import randintimport sysfrom itertools import starmapfrom operator import mul
# Babai's Nearest Plane algorithm# from: http://mslc.ctf.su/wp/plaidctf-2016-sexec-crypto-300/def Babai_closest_vector(M, G, target): small = target for _ in range(1): for i in reversed(range(M.nrows())): c = ((small * G[i]) / (G[i] * G[i])).round() small -= M[i] * c return target - small
m =n =q =
A_values =b_values =
A = matrix(ZZ, m + n, m)for i in range(m): A[i, i] = qfor x in range(m): for y in range(n): A[m + y, x] = A_values[x][y]lattice = IntegerLattice(A, lll_reduce=True)print("LLL done")gram = lattice.reduced_basis.gram_schmidt()[0]target = vector(ZZ, b_values)res = Babai_closest_vector(lattice.reduced_basis, gram, target)print("Closest Vector: {}".format(res))
R = IntegerModRing(q)M = Matrix(R, A_values)ingredients = M.solve_right(res)
print("Ingredients: {}".format(ingredients))
for row, b in zip(A_values, b_values): effect = sum(starmap(mul, zip(map(int, ingredients), row))) % q assert(abs(b - effect) < 2 ** 37)
print("ok")