密码学学习笔记 之 HNP

admin 2024年8月24日23:57:22评论90 views字数 7182阅读23分56秒阅读模式

浅尝Lattice 之 HNP

首发于安全客

前言

  格密码是一类备受关注的抗量子计算攻击的公钥密码体制。而格理论也使许多现代公钥密码RSA、DSA等体系受到影响。这篇文章主要从两道CTF题目来学习格密码中的HNP(Hidden number problem)。

Lattice

  首先谈谈个人对Lattice的理解叭。个人觉得,Latiice就是由若干线性无关的向量组成的一个向量空间,在这个空间中,向量彼此之间可以进行相应的加、减运算。向量也可以乘以某个系数,但这个系数仅限于整数,因而形成了布满整个空间的格点。在格中的计算问题主要包括两种,即SVP(the Shortest Vector Problem of lattice)和CVP(the Closest Vector Problem),然后个人认为,CVP可以给Latiice加上一个维度后变成SVP,继而可以用LLL算法来进行规约从而找到最短向量。

XCTF2020-高校战役-NHP

题目附件

题目信息

题目用的是DSA公钥密码签名系统。

题目提供签名函数:用户以用户名注册,服务端返回签名,并提供所用临时密钥的bit长度

我们需要以admin的身份登陆来获取flag,但是服务端不会给admin签名

解题流程

根据题目流程,显然,我们要利用临时密钥的bit长度来获取私钥,从而获得admin的签名

其中,我们知道的信息全局公钥p, q, g,服务端公钥y , 每轮签名使用的r, s, 以及我们可控的H(x),x即为用户名,Hash函数这里用的是sha256

step1-公式转化

由DSA签名中各参数的关系

$r \equiv g^k \pmod{q}$$s \equiv k^{-1}(H(m) + xr) \pmod{q}$

可得每轮临时密钥与签名、明文的关系

$k_i \equiv s_i^{-1}r_i \cdot x + s_i^{-1}H(m) \pmod{q} $$k_i \equiv A_i x + B_i \pmod{q} $$k_i = A_i x + B_i + l_i q$

其中$k_i$就是每次使用的临时密钥

化简后的式子中的$A_i,B_i$均可由已知信息计算

step2-构造Lattice

对于上式中的$k_i$,我们仅仅知道它的bit_ength,bit_ength泄露了什么信息呢?

当我们知道一个数的bit_ength时,我们能确定这个数的大小范围,

比如一个数的bit_ength是500时,我们能确定这个数大小落在$(2^{499}-1,2^{500}-1]$

所以我们知道这个数的MSB位为2^499

这等价于,我们知道了临时密钥的一个大概的值,我们设其为$K$

然后我们构造Lattice

$M =\begin{bmatrix}q & & & & & & \newline& q & & & & & \newline& &\ddots& & & & \newline& & & q & & & \newlineA_1&A_2&\dots & A_t&K/q& & \newlineB_1&B_2&\dots & B_t& & K & \newline\end{bmatrix}$

然后这里就会存在一个向量 $v =\begin{bmatrix}l_1 & l_2 & \cdots & l_t & x & 1\end{bmatrix}$

使得

$vM =\begin{bmatrix}l_1 & l_2 & \cdots & l_t & x & 1\end{bmatrix}\begin{bmatrix}q & & & & & & \newline& q & & & & & \newline& &\ddots& & & & \newline& & & q & & & \newlineA_1&A_2&\dots & A_t&K/q& & \newlineB_1&B_2&\dots & B_t& & K & \newline\end{bmatrix} = \begin{bmatrix}k_1 &k_2 &\cdots &k_t &Kx/q &K\end{bmatrix}= v_k$

其中向量$v$中的x即为我们的私钥,

step3-LLL

解决格密码的问题LLL算法的运用总是必不可少的,可是这里我们该如何利用LLL算法去找到向量$v_k$呢?

如果我们的$v_k$的长度在格中很小,我们利用LLL就很可能将其找出。所以,我们需要与服务端交互,然后收集当$k_i$的bit_length比较小的情况时的相关数据。比如:我们知道q的bit_length为128,那我们可以找bit_legnth为122的$k_i$,然后我们还需要一定的数据量,这样能提高利用LLL算法找到这个短向量的概率,并且,上述格中$K/q, K$的构造也是为了让$v_k$中的每一项的长度都差不多,这样也有利于找到$v_k$,参考这一篇文章中的密码学学习笔记 之 HNP

参考祥哥博客的这篇出题文章,另外感谢祥哥的解惑。

NPUCTF2020-babyLCG

题目附件可以在BUUOJ下载

题目流程

  1. 初始化一个LCG加密类,用到随机参数a, b, m, seed,其中a, b, m,均在附件给出
  2. 生成20个128位的随机数,但是只给出每个数的高64位
  3. 再生成三个随机数,用AES加密加密flag,key由前两个随机数组成,分别取第一个随机数和第二个随机数的高64位拼起来,iv由第三个随机数组成

解题思路

从题目流程来看,我们目的只有一个,恢复seed。

step1-公式转化

LCG生成随机数的公式为:$s_{i+1} = a*s_i+b\pmod{m}$

但是这一题,我们只知道$s_1$ 到$s_{20}$的高64位,所以我们将$s_i$分为h、l高低位两部分,其中$h_i$已知。所以有

$(h_{2}+l_{2}) \equiv a*(h_1 + l_1 )+b\pmod{m}$

$l_{2} \equiv a * l _ 1+a * h _ 1+b-h _ {2}\pmod{m}$

$l_{2} \equiv A _ 1 * l _ 1+B _ 1\pmod{m}$ 【$设A _1 = a; B _ 1 = a * h _ 1+b-h _ {2}$】

$l _ {3} \equiv a * l _ {2}+a * h _ {2}+b-h _ {3}\pmod{m}$

$l _ {3} \equiv a * A_1 * l _ {1}+a * B_1+a * h_{2}+b-h _ {3}\pmod{m}$

$l _ {3} \equiv A _ {2} * l _ {1}+B _ {2}\pmod{m}$【$设A _ {2} = a * A _ 1; B _ {2} = a * B _ 1+a * h _ {2}+b-h _ {3}$】

这里,我们通过公式的变形,可以将原来式子$s _ {i+1} = a*s _ i + b \pmod{m}$

中$s_{i+1}和s_{i}$的关系转变为$l_{i+1}和l_{i}$的关系。当然,原系数a、b的意义也发生了对应转变。

这里给出生成新A 和B 的脚本

1234567891011121314
b=153582801876235638173762045261195852087a=107763262682494809191803026213015101802m=226649634126248141841388712969771891297h = [0,7800489346663478448,11267068470666042741,5820429484185778982,6151953690371151688,548598048162918265,1586400863715808041,7464677042285115264,4702115170280353188,5123967912274624410,8517471683845309964,2106353633794059980,11042210261466318284,4280340333946566776,6859855443227901284,3149000387344084971,7055653494757088867,5378774397517873605,8265548624197463024,2898083382910841577,4927088585601943730]for i in range(len(h)):h[i] <<= 64A = [1]B = [0]for i in range(1, len(h)-1):A.append(a*A[i-1] % m)B.append((a*B[i-1]+a*h[i]+b-h[i+1]) % m)print(A[1:])print(B[1:])

step2-构造Lattice

现在我们二十条与 $l_1$ 相关的方程组了。即

$l_{i+1} \equiv A_i*l_1+B_i\pmod{m}$

$l_{i+1} = A_i*l_1+B_i+k_im$

且对于 $l_i$ 我们真的一无所知么?我们其实知道 $l_i$ 是小于2^64的,即 $l$ 最大为64bit。这样与前面一道题就很类似了。

于是我们构造Lattice

$M =\begin{bmatrix}m & & & & & \newline& m & & & & \newline& &\ddots& & & \newline& & & m & & \newlineA_1&A_2&\dots & A_{19}&1& \newlineB_1&B_2&\dots & B_{19}&0& 2^{64}\newline\end{bmatrix}$

然后这里就会存在一个向量 $v =\begin{bmatrix}k_1 & k_2 & \cdots & k_{19} & l_1 & 1\end{bmatrix}$ 使得

$vM =\begin{bmatrix}k_1 & k_2 & \cdots & k_{19} & l_1 & 1\end{bmatrix}\begin{bmatrix}m & & & & & \newline& m & & & & \newline& &\ddots& & & \newline& & & m & & \newlineA_1&A_2&\dots & A_{19}&1& \newlineB_1&B_2&\dots & B_{19}&0& 2^{64} \newline\end{bmatrix} = \begin{bmatrix}l_2 &l_3 &\cdots &l_{20} &l_1 &2^{64}\end{bmatrix}= v_l$

其中$l_1$即为$s_1$的低位,拼上其高位,在利用a, b, m就能恢复seed了

step3-LLL

  这里我们的$v_l$向量每一位都只有约64bit,显然,它是整个格中比较短的向量了,且我们一共有19组数据,那么直接对这个Lattice M运用LLL算法就可以找到$v_l$了。(格中参数$2^{64}$的选取道理与上面一致)

完整exp:

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
'''#sageb=153582801876235638173762045261195852087a=107763262682494809191803026213015101802m=226649634126248141841388712969771891297h = [0,7800489346663478448,11267068470666042741,5820429484185778982,6151953690371151688,548598048162918265,1586400863715808041,7464677042285115264,4702115170280353188,5123967912274624410,8517471683845309964,2106353633794059980,11042210261466318284,4280340333946566776,6859855443227901284,3149000387344084971,7055653494757088867,5378774397517873605,8265548624197463024,2898083382910841577,4927088585601943730]for i in range(len(h)):h[i] <<= 64A = [1]B = [0]for i in range(1, len(h)-1):A.append(a*A[i-1] % m)B.append((a*B[i-1]+a*h[i]+b-h[i+1]) % m)A = A[1:]B = B[1:]M = matrix(ZZ, 21, 21)for i in range(19):    M[i, i] = m    M[19, i] = A[i]    M[20, i] = B[i]    M[i, 19] = M[i, 20] = 0M[19, 19] =  1M[20, 20] = 2^64M[19, 20]= 0#print(B)vl = M.LLL()[0]l1 = vl[-2]h1 = h[1]s1 = l1+h1#s1 = a*seed+b %mseed = ((s1 - b)*inverse_mod(a,m))%mprint(seed)'''#pythonfrom Crypto.Util.number import *from Crypto.Cipher import AESclass LCG:def __init__(self, bit_length):b = 153582801876235638173762045261195852087a = 107763262682494809191803026213015101802m = 226649634126248141841388712969771891297seed = 73991202721494681711496408225724067994self._key = {'a':a, 'b':b, 'm':m}self._state = seeddef next(self):self._state = (self._key['a'] * self._state + self._key['b']) % self._key['m']return self._statedef export_key(self):return self._keydef gen_lcg():    rand_iter = LCG(128)    key = rand_iter.export_key()    f = open("key", "w")    f.write(str(key))    return rand_iterdef leak_data(rand_iter):    f = open("old", "w")    for i in range(20):        f.write(str(rand_iter.next() >> 64) + "\n")    return rand_iterdef encrypt(rand_iter):    f = open("ct", "wb")    key = rand_iter.next() >> 64    key = (key << 64) + (rand_iter.next() >> 64)    key = long_to_bytes(key).ljust(16, b'\x00')    iv = long_to_bytes(rand_iter.next()).ljust(16, b'\x00')    cipher = AES.new(key, AES.MODE_CBC, iv)    pt = flag + (16 - len(flag) % 16) * chr(16 - len(flag) % 16)    ct = cipher.encrypt(pt.encode())    f.write(ct)def decrypt(rand_iter):    with open("ct", "rb") as f:    flag = f.read()    key = rand_iter.next() >> 64    key = (key << 64) + (rand_iter.next() >> 64)    key = long_to_bytes(key).ljust(16, b'\x00')    iv = long_to_bytes(rand_iter.next()).ljust(16, b'\x00')    cipher = AES.new(key, AES.MODE_CBC, iv)    ct = cipher.decrypt(flag)    print(ct)def main():    rand_iter = gen_lcg()    rand_iter = leak_data(rand_iter)    decrypt(rand_iter)if __name__ == "__main__":    main()

总结

  从这两题我们可以发现,解决HNP问题,一般我们需要多组数据,然后每一组就像方程组中的每一条方程,我们根据这些方程组构造一个Lattice,也可以认为是一个矩阵,就好像在用矩阵解决线代中的 XA=B 的问题,这个B中的每一项是我们获得的MSB这样子的比较模糊的信息(上面两题我们拿到的都是未知量的bit_length,也相当于MSB)。然后这个B向量的长度(范式)需要相对与格中其他向量要短,然后我们就可以利用LLL算法找到这个B,进而根据我们的构造,确定X向量中我们需要的一个特定的值。也就是方程组的解。

  需要再次说明的是,这个矩阵所代表的方程组并非像真正的线代中的XA=B问题中的方程组——是多元的。我们这里的方程组是一元的。如果正常解方程的话,之所以这么多条方程都算不出解,就是因为我们得到信息是模糊的,并非是准确的。故我们需要用到格理论,和一个超好用的LLL算法。

转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可联系QQ 643713081,也可以邮件至 [email protected] - source:Van1sh的小屋

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年8月24日23:57:22
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   密码学学习笔记 之 HNPhttps://cn-sec.com/archives/3093409.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息