密码学学习笔记 之 knapsack

admin 2024年8月24日23:44:49评论29 views字数 4572阅读15分14秒阅读模式

首发于安全客

ACM中的背包问题是一种组合优化的NP完全问题,而在密码学中,也有对背包问题的应用。

Subset-sum problems and knapsack cryptosystems

密码学中的利用knapsack problem的加密。大意就是,你公开一个序列,然后你把你的消息转为二进制,如果你消息的这一位是1,就从你序列中取出对应位置的值,最后密文就是你所有取出的值的和。

但是这样就产生一个问题,加密是加过去了。那该怎么解密呢?

蛮力攻击的时间复杂度是$O(2^n)$,就算中途相遇也只能降低到$O(2^{\frac{n}{2}})$。太难了。。。

那Alice怎么解决这个问题呢?公钥密码系统如何体现呢?

这就看Alice对序列的选择了。她用的是超递增序列。什么是超递增序列呢?就是后一项≥前一项的两倍,也就意味着,后一项大于前面的所有项之和。

有超递增序列后,给你一个密文,你只需要一项一项对比过来,你就能知道加密者选取的元素是哪几个了。

这不难理解。我们将序列中的每一项放到二进制下就很明显。如果第一项的位长是2,那第二项的位长就至少是3,如果第二项位长是3,那么第三项的位长至少是4。如果你的密文最后是个2位长的,那加密者必定是没有用到第三项,必定是用到了第二项,至于有没有用到第一项,那就看密文减掉第二项之后还有没有剩下来了。【当然,这在每一项只能用一次的大前提下】

preparation

Alice准备加密了,首先她先整了一个超递增序列,$r = (r_1,r_2,,,r_n)$,然后她整了两个大数A和B,其中要求$B>2r_n$ 也就是B大于这个超递增序列之和啦。然后要需要$gcd(A,B)=1$,因为之后要求一个A在B下的逆。

encryption

Alice要加密了,她先用她的A和她的超递增序列生成一个新的序列M,其中$M_i \equiv Ar_i \pmod B$

这个新的序列就是Alice的公钥了,她把这个序列甩给Bob,然后Bob按照之前描述的方式,用这个序列和他的明文生成密文$S = x*M = \sum \limits_{i=1}^{n}x_iM_i$,然后把这传给Alice

decryption

Alice要解密了,她拿到了这个密文S,然后计算

$S’ \equiv A^{-1}S \equiv A^{-1}\sum_{i=1}^{n}x_iM_i\equiv A^{-1}\sum_{i=1}^{n}x_iAr_i\equiv \sum_{i=1}^{n}x_ir_i \pmod B$

所以这个$S’$就是密文在超递增序列下的值了,之后再像之前那样判断一下这个$S’$和序列里面每一项的关系就能解出明文了。【记得从序列大的一端开始判断,如果满足关系别忘了减掉那一项再判断下一项】

伪代码走一波

123456
for i in reverse(r):if s >= i:        m = '1' + m        s -= i    else:        m = '0' + m

table

来个总体流程

密码学学习笔记 之 knapsack

attention

同RSA一样,这里对参数的选择也要慎重,不然会出现许多出人意料的问题。

具体实例可参考BJD3rd-knapsack

这里由于对参数A的不当选择,导致序列中较小的项乘以A后并没有被B模掉,也就导致了参数A的泄露。

并且由于这里的超递增序列的生成规则过于简单,只是不断地整除2,所以攻击者能够利用加密后的序列轻易的计算出B来。

further more

之前提到Alice最开始整的一个无序的序列,由于这玩意儿没有陷门(trapdoor),因此这无法成为一个密码系统。但自从有关LLL的paper发表后,基于背包的密码系统出现了一个大weakness,

这里简短的介绍下Eve怎么去解决这个无序序列的背包问题。不管是序列本身无序,还是超递增序列加密后显得无序的序列。

Eve先构造了一个矩阵$M =\begin{bmatrix}2&0&0&\dots&0&0&m_1 \newline0&2&0&\dots&0&0&m_2 \newline\vdots&\vdots&\vdots&\ddots&\vdots&\vdots&\vdots \newline0&0&0&\dots&2&0&m_{n-1}\newline0&0&0&\dots&0&2&m_{n}\newline1&1&1&\dots&1&1&S\newline\end{bmatrix}$

其中这个$m_1,m_2,m_3…$就是那个无序序列,S就是密文

然后Eve从这个矩阵中,将每一条行向量划分出来,分别为$V_1,V_2,…,V_n,V_{n+1}$

我们现在假设向量$x = (x_1,x_2,x_3,…,x_n)$ 是明文,($x_i = 0$ or $1$)

那么这个格中就会有这么一条向量

$t = \sum_ \limits {i=1}^{n}x_iV_i-V_{n+1}=(2x_1-1,2x_2-1,…,2x_n-1,0)$

这个t,因为$2x_i-1=\pm 1$,所以t的模长是$\sqrt{n}$,

根据Minkowskl’s First Theorem,

即对于任意n维满秩格基,都有 $SVP(L)≤\sqrt n(det(L))^{\frac 1 n}$

显然t是格L中的短向量。

所以,如果Eve知道如何找到lattice中的短向量,那么他就可以完成破解了。

关于找到lattice中短向量的算法我们称之为reduction algorithm,最著名的就是LLL algorithm了,然后它还有变体LLL-BKZ。

variant

再刚过去不久的2020RCTF中出现了一道有关knapsack problem 的变体,这里用的不是子集之和,而是子集之积

123456789101112131415161718192021
from Cryptodome.Util.number import bytes_to_long, getPrimeimport randomimport hashlibsr = random.SystemRandom()p = getPrime(120)num = [sr.randint(1,p-1) for i in range(90)]secret = sr.randint(0, 2**90)r = 1for i in range(90):    if (secret >> i) & 1:        r *= num[i]        r %= pflag = open("flag.txt","rb").read()h = hashlib.sha256(str(secret).encode('utf-8')).digest()print(p)print(num)print(r)print(bytes_to_long(h)^bytes_to_long(flag))

蛮力攻击的时间复杂度是2的90次方,即使是中途相遇攻击的时间复杂度也仍然有2的45次方,于此同时还得考虑2的45次方的空间复杂度。

赛后看了国外大佬hellman的脚本才明白了本题的解法。

这一道题的切入点就在于模数p,通过不断地nc,直到获得的模数p具有p-1 smooth的性质(为了方便后面解离散对数),

这个时候再找到一个p的原根,然后对所有的数据利用pohlig算法解一个离散对数,就是开一个log,这样这个子集积的问题就能重新变回子集和的问题了。

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
from sage.all import *import ast, sys, subprocessimport hashlibfrom random import shufflefrom time import time#不断nc以获取一个具有p-1 smooth性质的模数pwhile True:    data = subprocess.check_output("nc 124.156.133.6 22298 </dev/null", shell=True).splitlines()    p = int(data[0])    nums = ast.literal_eval(data[1].decode())    r = int(data[2])    encflag = int(data[3])    fp1 = factor(p-1)    print("p-1 =", fp1)    if all(d < 2**50 for d, e in fp1):#判断模数是否具有p-1 smooth的性质        break    print("too high factor of p-1")# 解离散对数问题 -> 将子集积问题转化问子集和F = GF(p)g = F.primitive_element() #获取原根ges = []for num in nums:    e = F(num).log(g)#以原根为底数开log,因为循环群中所有的数都能以原根表示    print(num, "->", e)    es.append(e)r = F(r).log(g)nums = esmod = p - 1#利用LLL解决0-1背包问题N = 90nums0 = nums[::]BS = 30itr = 0while True:    itr += 1    print(itr, "BS", BS)    t0 = time()    nums = nums0[::]    shuffle(nums)    h = QQ(1)/2    #  n1 1 0 0 0 0    #  n2 0 1 0 0 0    #  n3 0 0 1 0 0    #  n4 0 0 0 1 0     #   r h h h h h    # mod 0 0 0 0 0    m = matrix(QQ, N+2, N+2)    m.set_column(0, nums + [r, mod])    m.set_row(N, [r] + [h] * (N+1))    m[:N,1:N+1] = identity_matrix(N)    m.set_column(0, 50*m.column(0))    ml = (m*2).change_ring(ZZ).BKZ(block_size=BS)    print("time %.3fs" % (time() - t0))    for irow, row in enumerate(ml):        if not (-1 <= min(row[:-1]) < max(row[:-1]) <= 1):            continue        if row[-1] < 0:            row = -row        print("GOOD", irow)        secret = 0        for i in range(N):            if row[1+i] < 0:                secret |= 1 << nums0.index(nums[i])        print("secret", secret, "=", hex(secret))        h = hashlib.sha256(str(secret).encode('utf-8')).digest()        h = int.from_bytes(h, "big")        flag = encflag ^ h        print(int(flag).to_bytes(100, "big").strip(b"\x00"))        quit()# RCTF{M4th_0f_MuLLLtiplication_2333}

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

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

发表评论

匿名网友 填写信息