背包密码加密
分析一个加密算法的破解首先要比较熟悉它的加密算法以及加密体制,这样才能更好的破解背包加密。
背包问题
01背包问题
在算法的动态规划中有一个比较经典的问题,就是背包问题,先来介绍一下这个问题,以此引入背包加密体制
假定一个背包可以承重W,现在有n个物品,其重量分别为a 1 , a 2 , . . . . , a n a_1,a_2,....,a_n a 1 , a 2 , . . . . , a n ,此时我们想知道装哪些物品可以恰好使得背包装满。并且每个物品只能被装一次。这其实就是解这样一个问题。
x 1 a 1 + x 2 a 2 + . . . . + x n a x = W x_1a_1+x_2a_2+....+x_na_x = W
x 1 a 1 + x 2 a 2 + . . . . + x n a x = W
其中x i x_i x i 只能为0或者1(表示当前物品是否装进背包)。显然我们必须枚举所有的n个物品的组合才能解决这个问题,而复杂度也就是2 n 2^n 2 n (n个物品,每个物品都有装进背包和没装进背包,全排列就有2 n 2^{n} 2 n 种可能,所以暴力破解最坏的情况就是遍历2 n 2^n 2 n 种可能)
完全背包问题 (简单介绍一下)
假定一个背包可以承重W,现在有n个物品,每个物品的重量为a 1 , a 2 , . . . , a n a_1,a_2,...,a_n a 1 , a 2 , . . . , a n ,并且每个物品可以无限次 放入背包中,背包所有物品权重和最大是多少。
注意:01背包问题和完全背包问题的区别就在于,每个物品取的最大次数是1次还是无限次
背包加密
背包问题中由于存在0、1序列,所以我们可以比较好的联想到加密的消息。
在加密时,我们想要加密的明文为x ,那么我们就可以将其表示为n位二进制数 ,然后**分别乘上a i a_i a i **即可得到加密结果。
但是如果这个a i a_{i} a i 没有条件限制的话,就算我们知道a i a_{i} a i 这个密钥也没办法解密。所以为了能够解密我们就对密钥a i a_{i} a i 进行条件的限制。
超递增序列 :我们规定a i a_{i} a i 是一个超递增序列,此时我们知道密钥就可以比较容易的对背包加密后的密文进行解密。
a i > ∑ k = 1 i − 1 a k 即 a i > a 1 + a 2 + . . . . + a i − 1 a_i>\sum_{k=1}^{i-1}a_k\\
即a_i>a_1+a_2+....+a_{i-1}
a i > k = 1 ∑ i − 1 a k 即 a i > a 1 + a 2 + . . . . + a i − 1
例如:[1,2,5,11,20,41]
就是一个超递增序列。
但是这边就存在一个问题,我们用来加密消息的这个密钥a i a_{i} a i 很容易被窃取或者截获,这样就会导致一个密钥的泄露。
Merkle-Hellman背包加密算法
为了解决密钥泄露的问题,R. Merkle
和M. Hellman
在一篇论文中提出了一个非对称背包加密算法。弥补了上面背包加密的密钥泄露问题。(论文会附在后面)
私钥的生成 :其实私钥就是上面的a i a_{i} a i 需要满足超递增序列,还有一个乘数w
公钥的生成 :
公钥的生成我们需要通过模运算,进行生成,所以我们要选定一个模数m ,而这个模数要大于生成私钥的和。也就是模数m需要满足如下 :
m > ∑ i = 1 n a i 即 m > a 1 + a 2 + . . . . + a n m>\sum_{i=1}^{n}a_i\\
即m>a_1+a_2+....+a_n
m > i = 1 ∑ n a i 即 m > a 1 + a 2 + . . . . + a n
然后我们要使用私钥w 作为乘数,所以我们必须确保私钥
g c d ( w , m ) = 1 gcd(w,m)=1
g c d ( w , m ) = 1
生成了以上两个数我们就可以生成公钥 ,公钥生成的算法如下:
b i = w a i m o d m b_i = wa_i~mod~m
b i = w a i m o d m
加密过程 :公钥(b i b_{i} b i ,m),私钥(a i a_{i} a i ,w)
假设我们需要加密的明文是v(已经是字符串转换为整数类型),将其转换为二进制的形式,v i v_i v i 那么我们加密的结果为:
c = ∑ i = 1 n b i v i m o d m 即 c = b 1 v 1 + b 2 v 2 + . . . + b n v n m o d m c = \sum_{i=1}^{n}b_iv_i~mod~ m\\
即c = b_1v_1+b_2v_2+...+b_nv_n~mod~m
c = i = 1 ∑ n b i v i m o d m 即 c = b 1 v 1 + b 2 v 2 + . . . + b n v n m o d m
c = ∑ i = 1 n w a i v i m o d m = w ∑ i = 1 n a i v i m o d m 即 c = w a 1 v 1 + w a 2 v 2 + . . . + w a 3 v 3 m o d m c=\sum_{i=1}^{n}wa_iv_i~mod~m=w\sum_{i=1}^{n}a_iv_i~mod~ m\\
即c = wa_1v_1+wa_2v_2+...+wa_3v_3~mod~m
c = i = 1 ∑ n w a i v i m o d m = w i = 1 ∑ n a i v i m o d m 即 c = w a 1 v 1 + w a 2 v 2 + . . . + w a 3 v 3 m o d m
解密过程 :
首先要计算w关于m的逆元w − 1 w^{-1} w − 1 ,然后通过如下的式子可以得到
∑ i = 1 n a i v i m o d m = ∑ i = 1 n w w − 1 a i v i m o d m = ∑ i = 1 n w − 1 b i v i m o d m = c w − 1 m o d m m > ∑ i = 1 n a i \sum_{i=1}^{n}a_iv_i~mod~m=\sum_{i=1}^{n}ww^{-1}a_iv_i~mod~m=\sum_{i=1}^{n}w^{-1}b_iv_i~mod~m=cw^{-1}~mod~m\\m>\sum_{i=1}^{n}a_i\\
i = 1 ∑ n a i v i m o d m = i = 1 ∑ n w w − 1 a i v i m o d m = i = 1 ∑ n w − 1 b i v i m o d m = c w − 1 m o d m m > i = 1 ∑ n a i
由于m是大于a i a_i a i 的和,所以我们解密后∑ i = 1 n a i v i = ∑ i = 1 n a i v i m o d m \sum_{i=1}^{n}a_iv_i=\sum_{i=1}^{n}a_iv_i~mod~m ∑ i = 1 n a i v i = ∑ i = 1 n a i v i m o d m ,就是这么一个式子。这样我们就可以通过背包问相关的算法解密出v的二进制序列
由于这个序列是超递增序列,我们的解密就可以进行如下的遍历操作,这样可以很快速的求解出最终的结果
记 x = ∑ i = 1 n a i v i , 解密时已知 a i 这个序列 x > a n 说明 v n 为 1 , x < a n 说明 v n 为 0 当 v n 为 1 的时候 , 计算得 x = x − a n 当 v n 为 0 的时候 , 直接进行下一步操作 判断 x > a n − 1 说明 v n − 1 为 1 , x < a n − 1 说明 v n − 1 为 0 以此类推直到 a 1 \begin{array}{l}
记x = \sum_{i=1}^{n}a_iv_i,解密时已知a_i这个序列\\
x >a_n说明v_n为1,x<a_n说明v_n为0\\
当v_n为1的时候,计算得x=x-a_n\\
当v_n为0的时候,直接进行下一步操作\\
判断x >a_{n-1}说明v_{n-1}为1,x<a_{n-1}说明v_{n-1}为0\\
以此类推直到a_1
\end{array}
记 x = ∑ i = 1 n a i v i , 解 密 时 已 知 a i 这 个 序 列 x > a n 说 明 v n 为 1 , x < a n 说 明 v n 为 0 当 v n 为 1 的 时 候 , 计 算 得 x = x − a n 当 v n 为 0 的 时 候 , 直 接 进 行 下 一 步 操 作 判 断 x > a n − 1 说 明 v n − 1 为 1 , x < a n − 1 说 明 v n − 1 为 0 以 此 类 推 直 到 a 1
解密出二进制序列后就可以还原出加密的消息了。但是当加密消息的二进制序列过长的时候我们在最后还原出二进制序列的时候需要的算力就很大,这个解密的时间一定程度上会受到影响。
加解密代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 from Crypto.Util.number import *import randomimport gmpy2flag = b'flag' v = bytes_to_long(flag) v = bin (v)[2 :] m = getPrime(200 ) w = getPrime(18 ) a = [3 , 5 , 15 , 25 , 55 , 127 , 231 , 512 , 991 , 2046 , 4033 , 8161 , 16307 , 32614 , 65391 , 130575 , 261330 , 522531 , 1047888 , 2094999 , 4192536 , 8382741 , 16769048 , 33545618 , 67086288 , 134167557 , 268336325 , 536672015 , 1073358395 , 2147301026 , 4294750090 ] b = [] """ total = 0 for i in range(len(v)): while True: temp = random.randint(2**(i),2**(i+2)) if temp > total: total+=temp a.append(temp) break assert m > sum(a) assert gmpy2.gcd(w,m)==1 """ for i in range (len (v)): b.append(a[i]*w) c = 0 for i in range (len (v)): c += int (v[i],2 ) * b[i] c = c % m print (c)w_ = gmpy2.invert(w,m) c = c*w_ % m print (c)x = 0 for i in range (len (v)): x += int (v[i],2 )*a[i] assert x == c
解密代码实现
由于是递增序列,所以我们可以从最高判断到最低,这样我们就可以,得到m的二进制位
此时我们就可以逐二进制位对其进行解密
1 2 3 4 5 6 7 8 9 10 11 12 import libnuma = [3 , 5 , 15 , 25 , 55 , 127 , 231 , 512 , 991 , 2046 , 4033 , 8161 , 16307 , 32614 , 65391 , 130575 , 261330 , 522531 , 1047888 , 2094999 , 4192536 , 8382741 , 16769048 , 33545618 , 67086288 , 134167557 , 268336325 , 536672015 , 1073358395 , 2147301026 , 4294750090 ] c = 7734243960 m = '' for i in range (len (a)-1 ,-1 ,-1 ): if c>=a[i]: m += '1' c-=a[i] else : m+='0' m = int (m[::-1 ],2 ) print (libnum.n2s(int (m)))
离散对数问题的背包加密
在他们的论文中除了提供了上面的Merkle-Hellman背包加密算法 ,还提供了另一种陷门方案,就是基于离散对数问题的乘法型陷门背包。
因为上面的那一种生成公钥的方式是线性生成的,所以该加密方式容易被攻击。
我们也详细介绍一下这个基于离散对数问题 的背包加密过程。这个加密理论可行 ,但是在公私钥的生成会出现问题,这就使得背包加密体制一般都指前面一种背包加密。
私钥的生成 :
公钥的生成 :
利用私钥给中的a n a_n a n 、g、p,按照如下方式生成公钥b n b_n b n
b n = l o g g ( a i ) m o d p b_n = log_g(a_i)~mod~p
b n = l o g g ( a i ) m o d p
加密过程 :公钥(b n b_n b n )私钥(a n a_n a n ,g,p)
对于给定消息v,将消息v转换成整数,最后转成整数的二进制序列v i v_i v i
接下来我们就可以对消息加密,加密过程如下:
c = ∑ i = 1 n b i v i = ∑ i = 1 n l o g g ( a i ) v i m o d p = l o g g ( ∏ i = 1 n a i v i ) m o d p c = \sum_{i=1}^nb_iv_i=\sum_{i=1}^nlog_g(a_i)v_i~mod~p=log_g(\prod_{i=1}^na_iv_i)~mod~p
c = i = 1 ∑ n b i v i = i = 1 ∑ n l o g g ( a i ) v i m o d p = l o g g ( i = 1 ∏ n a i v i ) m o d p
解密过程 :
解密过程经过如下就可以得到一个门限,如果要还原成明文二进制序列就必须满足p > ∏ i = 1 n a i v i p>\prod_{i=1}^na_iv_i p > ∏ i = 1 n a i v i
m = g c = g l o g g ( ∏ i = 1 n a i v i ) m o d p = ∏ i = 1 n a i v i m o d p m = g^c=g^{log_g(\prod_{i=1}^na_iv_i)}~mod~p =\prod_{i=1}^na_iv_i~mod~p
m = g c = g l o g g ( ∏ i = 1 n a i v i ) m o d p = i = 1 ∏ n a i v i m o d p
由于解密方知道a n a_n a n 这个序列,此时我们就可以遍历a n a_n a n 。此时我们通过判断a n a_n a n 中的每个元素是否能整除m ,从而得到明文的二进制序列。
背包加密相关论文
基础题型
题目1_BaseCTF2024__babypack
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 from Crypto.Util.number import *import randomflag=b'BaseCTF{}' m=bytes_to_long(flag) bin_m=bin (m)[2 :] length=len (bin_m) a=[1 ] sum =1 for i in range (length-1 ): temp=random.randint(2 *sum +1 ,4 *sum ) sum =sum +temp a.append(temp) a=a[::-1 ] c=0 for i in range (length): if bin_m[i]=='1' : c=c+a[i] print ("a=" ,a)print ("c=" ,c)
这题就是基本的Merkle-Hellman背包加密算法
中的解密过程
1 2 3 4 5 6 7 8 9 10 11 12 13 import libnuma = [] c = 2488656295807929935404316556194747314175977860755594014838879551525915558042003735363919054632036359039039831854134957725034750353847782168033537523854288427613513938991943920607437000388885418821419115067060003426834 m = "" for i in range (len (a)): if c >= a[i]: m+='1' c-=a[i] else : m+='0' m = int (m,2 ) print (libnum.n2s(int (m)))
题目2_MoeCTF2022__MiniMiniBackPack
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 from gmpy2 import *from Crypto.Util.number import *import randomfrom FLAG import flagdef gen_key (size ): s = 1000 key = [] for _ in range (size): a = random.randint(s + 1 , 2 * s) assert a > sum (key) key.append(a) s += a return key m = bytes_to_long(flag) L = len (bin (m)[2 :]) key = gen_key(L) c = 0 for i in range (L): c += key[i]**(m&1 ) m >>= 1 print (key)print (c)
1 2 3 4 5 6 7 8 9 10 11 12 import libnuma = c = m = '' for i in range (len (a)-1 ,-1 ,-1 ): if c >= a[i]: m +='1' c -=a[i] else : m +='0' print (libnum.n2s(int (m,2 )))
格攻击(低密度攻击)
攻击介绍
向量角度看背包加密
由于要使用格,我们就需要使用向量角度来进行背包加密的介绍。首先我们以n维向量v ⃗ \vec{v} v 表示明文v的二进制序列。写成向量的形式就如下。
v ⃗ = ( v 1 , v 2 , v 3 , . . . , v n ) , v i ∈ { 0 , 1 } , ( i = 1 , 2 , . . . , n ) \vec{v} = (v_1,v_2,v_3,...,v_n),v_i\in\{0,1\},(i=1,2,...,n)
v = ( v 1 , v 2 , v 3 , . . . , v n ) , v i ∈ { 0 , 1 } , ( i = 1 , 2 , . . . , n )
背包加密的私钥,也就是一开始的这个超递增序列a n a_n a n ,使用a ⃗ \vec{a} a 表示。写成向量的形式就如下图所示:
a ⃗ = ( a 1 , a 2 , a 3 , . . . , a n ) \vec{a} = (a_1,a_2,a_3,...,a_n)
a = ( a 1 , a 2 , a 3 , . . . , a n )
然后生成模数p p p 和乘数w w w ,使用b ⃗ \vec{b} b 表示公钥序列b n b_n b n ,所以公钥序列b n b_n b n 写成向量的形式就是下面的这个式子:
b ⃗ = w a ⃗ m o d p = ( w a 1 , w a 2 , w a 3 , . . . , w a n ) m o d p \vec{b} = w\vec{a}~mod~p = (wa_1,wa_2,wa_3,...,wa_n)~mod~p
b = w a m o d p = ( w a 1 , w a 2 , w a 3 , . . . , w a n ) m o d p
这样我们加密后的密文就可以写成向量的点乘形式,如下式子
c = b ⃗ ⋅ v ⃗ = b 1 v 1 + b 2 v 2 + b 3 v 3 + . . . + b n v n c = \vec{b}·\vec{v}=b_1v_1+b_2v_2+b_3v_3+...+b_nv_n
c = b ⋅ v = b 1 v 1 + b 2 v 2 + b 3 v 3 + . . . + b n v n
格攻击背包加密
由于私钥a n a_n a n 是一个超递增序列,所以这就使得这个加密体制存在一定被破译的分险,并且之后由于格基规约等格技术 的发展,许多背包问题的变种都被证明在实际参数下是不太安全的,这些背包加密被破解的原因基本上是特殊结构 或者低密度 。除了 Okamoto-Tanaka-Uchiyama (OTU) 量子背包密码体制 目前还没有被攻击。
在读论文的时候,有一些概念需要先介绍一下,以便于我们分析背包的复杂度等,最终目的都是帮助我们理解和使用格对背包密码进行攻击。
概念:背包密度d
d = n l o g 2 A , 其中 A = m a x { a 1 , a 2 , . . . , a n } d = \frac{n}{log_2A},其中A = max\{a_1,a_2,...,a_n\}
d = l o g 2 A n , 其 中 A = m a x { a 1 , a 2 , . . . , a n }
对于一个背包密码,我们想要判断它是否能被破解,我们首要的就是看这个背包密度d
当d < 0.9408... d<0.9408... d < 0 . 9 4 0 8 . . . 时,这个背包密码就可以被格基规约攻击。
当d > 1 d>1 d > 1 时,可以使用基于格规约的变体攻击可以成功碰撞
当0.9408... < d < 1 0.9408...<d<1 0 . 9 4 0 8 . . . < d < 1 的时候,并没有比较好的格规约的攻击方式,可能有一些算法可以解特定情况,这种情况被称为困难背包 问题
常规造格
[ 1 0 ⋯ 0 a 1 0 1 ⋯ 0 a 2 ⋮ ⋮ ⋱ ⋮ ⋮ 0 0 ⋯ 1 a n 0 0 ⋯ 0 c ] \begin{bmatrix}
1 & 0 & \cdots & 0 & a_1 \\
0 & 1 & \cdots & 0 & a_2\\
\vdots & \vdots & \ddots & \vdots & \vdots\\
0 & 0 & \cdots & 1 & a_n\\
0&0 & \cdots &0 &c
\end{bmatrix}
⎣ ⎢ ⎢ ⎢ ⎢ ⎢ ⎢ ⎡ 1 0 ⋮ 0 0 0 1 ⋮ 0 0 ⋯ ⋯ ⋱ ⋯ ⋯ 0 0 ⋮ 1 0 a 1 a 2 ⋮ a n c ⎦ ⎥ ⎥ ⎥ ⎥ ⎥ ⎥ ⎤
证明如下 :
其实我们一开始只需要造如下的形式的格即可,这种形式其实就是我们的基于密钥和密文造的最初的格。
[ 1 0 ⋯ 0 a 1 0 1 ⋯ 0 a 2 ⋮ ⋮ ⋱ ⋮ ⋮ 0 0 ⋯ 1 a n 0 0 ⋯ 0 c ] \begin{bmatrix}
1 & 0 & \cdots & 0 & a_1 \\
0 & 1 & \cdots & 0 & a_2\\
\vdots & \vdots & \ddots & \vdots & \vdots\\
0 & 0 & \cdots & 1 & a_n\\
0&0 & \cdots &0 &c
\end{bmatrix}
⎣ ⎢ ⎢ ⎢ ⎢ ⎢ ⎢ ⎡ 1 0 ⋮ 0 0 0 1 ⋮ 0 0 ⋯ ⋯ ⋱ ⋯ ⋯ 0 0 ⋮ 1 0 a 1 a 2 ⋮ a n c ⎦ ⎥ ⎥ ⎥ ⎥ ⎥ ⎥ ⎤
记每一行为B i B_i B i ,例如b 1 = ( 1 , 0 , . . . , 0 , a 1 ) b_1=(1,0,...,0,a_1) b 1 = ( 1 , 0 , . . . , 0 , a 1 ) ,其实向量组B B B 其实就是格L ( b 1 , . . . , b n − 1 ) L(b_1,...,b_{n-1}) L ( b 1 , . . . , b n − 1 ) 的一个基。
而明文序列v = { v 1 , v 2 , . . . , v n } , v i ∈ 1 , 0 v=\{v_1,v_2,...,v_n\},v_i\in {1,0} v = { v 1 , v 2 , . . . , v n } , v i ∈ 1 , 0
由于格是离散子群,运算具有封闭性,所以t ⃗ = v 1 b 1 + b 2 b 2 + . . . + v n b n − b n + 1 \vec{t} = v_1b_1+b_2b_2+...+v_nb_n-b_{n+1} t = v 1 b 1 + b 2 b 2 + . . . + v n b n − b n + 1 ,在格L ( B ) L(B) L ( B ) 上。
即t ⃗ = ( v 1 , v 2 , . . . , v n , 0 ) \vec{t} =(v_1,v_2,...,v_n,0) t = ( v 1 , v 2 , . . . , v n , 0 ) 在格L ( B ) L(B) L ( B ) 上。根据Hermite定理 可以得到t ⃗ \vec{t} t 是L ( B ) L(B) L ( B ) 上最短向量。
至于为什么最后常用造的格最后一行是1 2 \frac{1}{2} 2 1 或者是1 1 1 ,具体的去看格攻击相关论文第二篇吧。
优化造格
对于背包加密,我们可以构造出n + 1 n+1 n + 1 个n + 1 n+1 n + 1 维向量,记这些向量为b i b_i b i ,注意这里的a i a_i a i 没有限制为超递增序列
c = ∑ i = 1 n v i a i b 1 = ( 1 , 0 , . . . , 0 , N a 1 ) b 2 = ( 0 , 1 , . . . , 0 , N a 2 ) . . . . . . b n = ( 0 , 0 , . . . , 1 , N a n ) b n + 1 = ( 1 2 , 1 2 , . . . , 1 2 , N c ) c = \sum_{i=1}^nv_ia_i\\
b_1 = (1,0,...,0,Na_1)\\
b_2 = (0,1,...,0,Na_2)\\
......\\
b_n = (0,0,...,1,Na_n)\\
b_{n+1} = (\frac{1}{2},\frac{1}{2},...,\frac{1}{2},Nc)\\
c = i = 1 ∑ n v i a i b 1 = ( 1 , 0 , . . . , 0 , N a 1 ) b 2 = ( 0 , 1 , . . . , 0 , N a 2 ) . . . . . . b n = ( 0 , 0 , . . . , 1 , N a n ) b n + 1 = ( 2 1 , 2 1 , . . . , 2 1 , N c )
将上面这些向量组合成一个如下的矩阵(左边),还可以乘个2构造如下矩阵(右边):
L ( B ) = [ 1 0 ⋯ 0 N a 1 0 1 ⋯ 0 N a 2 ⋮ ⋮ ⋱ ⋮ ⋮ 0 0 ⋯ 1 N a n 1 2 1 2 ⋯ 1 2 N c ] ↔ [ 2 0 ⋯ 0 N a 1 0 2 ⋯ 0 N a 2 ⋮ ⋮ ⋱ ⋮ ⋮ 0 0 ⋯ 2 N a n 1 1 ⋯ 1 N c ] L(B) =
\begin{bmatrix}
1 & 0 & \cdots & 0 & Na_1 \\
0 & 1 & \cdots & 0 & Na_2\\
\vdots & \vdots & \ddots & \vdots & \vdots\\
0 & 0 & \cdots & 1 & Na_n\\
\frac{1}{2}&\frac{1}{2} & \cdots &\frac{1}{2} &Nc
\end{bmatrix}
\leftrightarrow
\begin{bmatrix}
2 & 0 & \cdots & 0 & Na_1 \\
0 & 2 & \cdots & 0 & Na_2\\
\vdots & \vdots & \ddots & \vdots & \vdots\\
0 & 0 & \cdots & 2 & Na_n\\
1&1 & \cdots &1 &Nc
\end{bmatrix}
L ( B ) = ⎣ ⎢ ⎢ ⎢ ⎢ ⎢ ⎢ ⎡ 1 0 ⋮ 0 2 1 0 1 ⋮ 0 2 1 ⋯ ⋯ ⋱ ⋯ ⋯ 0 0 ⋮ 1 2 1 N a 1 N a 2 ⋮ N a n N c ⎦ ⎥ ⎥ ⎥ ⎥ ⎥ ⎥ ⎤ ↔ ⎣ ⎢ ⎢ ⎢ ⎢ ⎢ ⎢ ⎡ 2 0 ⋮ 0 1 0 2 ⋮ 0 1 ⋯ ⋯ ⋱ ⋯ ⋯ 0 0 ⋮ 2 1 N a 1 N a 2 ⋮ N a n N c ⎦ ⎥ ⎥ ⎥ ⎥ ⎥ ⎥ ⎤
a i a_i a i 和c c c 前面的N是:缩放因子 ,放大最后一列,控制格结构。一般我们造格,N取1就行。如果N取1规约不出来的话我们就可以调整N,此时其选取范围应为N > 1 2 n N>\frac{1}{2}\sqrt{n} N > 2 1 n
如果我们造的格是左边矩阵(存在1 2 \frac{1}{2} 2 1 )我们规约出来的结果,如果满足这样的形式就说明是我们要找的消息二进制序列。具体值需要根据情况进行分析 ,该情况只是分析逐二进制序列加密,下图是论文给出的相应地方
v ′ = ( v 1 ′ , . . . , v 2 ′ , 0 ) ∈ L ( B ) , v i ′ = v i − 1 2 即 v ′ = ( v 1 ′ , . . . , v 2 ′ , 0 ) ∈ L ( B ) , v i ′ ∈ { 1 2 , − 1 2 } v' = (v'_1,...,v'_2,0)\in L(B),v'_i = v_i-\frac{1}{2}
\\即v' = (v'_1,...,v'_2,0)\in L(B),v'_i\in \{\frac{1}{2},-\frac{1}{2}\}
v ′ = ( v 1 ′ , . . . , v 2 ′ , 0 ) ∈ L ( B ) , v i ′ = v i − 2 1 即 v ′ = ( v 1 ′ , . . . , v 2 ′ , 0 ) ∈ L ( B ) , v i ′ ∈ { 2 1 , − 2 1 }
如果我们造的是格是右边的那种矩阵,规约出来的结果就如下
v ′ = ( v 1 ′ , . . . , v 2 ′ , 0 ) ∈ L ( B ) , v i ′ = 2 v i − 1 即 v ′ = ( v 1 ′ , . . . , v 2 ′ , 0 ) ∈ L ( B ) , v i ′ ∈ { 1 , − 1 } v' = (v'_1,...,v'_2,0)\in L(B),v'_i = 2v_i-1\\
\\即v' = (v'_1,...,v'_2,0)\in L(B),v'_i\in \{1,-1\}
v ′ = ( v 1 ′ , . . . , v 2 ′ , 0 ) ∈ L ( B ) , v i ′ = 2 v i − 1 即 v ′ = ( v 1 ′ , . . . , v 2 ′ , 0 ) ∈ L ( B ) , v i ′ ∈ { 1 , − 1 }
例题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 from random import randintfrom Crypto.Util.number import bytes_to_long,long_to_bytes,GCD,inversefrom secret import flagdef bitlength (n ): length=len (bin (bytes_to_long(n))[2 :]) return length def makeKey (n ): length=len (n) privKey = [randint(1 , 65536 **length)] sum = privKey[0 ] for i in range (1 , length): privKey.append(randint(sum *255 + 1 , 65536 **(length + i))) sum += privKey[i] q = 255 *randint(privKey[length-1 ] + 1 , 2 *privKey[length-1 ]) r = randint(1 , q) while GCD(r, q) != 1 : r = randint(1 , q) pubKey = [ r*w % q for w in privKey ] return privKey, q, r, pubKey def encrypt (msg, pubKey ): cipher = 0 i = 0 for bit in msg: cipher += bit*pubKey[i] i += 1 return cipher def decrypt (cipher, privKey, q, r ): d = inverse(r, q) msg = cipher*d % q res = b'' n = len (privKey) for i in range (n - 1 , -1 , -1 ): temp=0 if msg >= privKey[i]: while msg >= privKey[i]: temp=temp+1 msg -= privKey[i] res = bytes ([temp]) + res else : res = bytes ([0 ]) + res return res privKey, q, r, pubKey=makeKey(flag) cipher=encrypt(flag,pubKey) f=open ("pubKey.txt" ,'w' ) f.write(str (pubKey)) f.close() f=open ("cipher.txt" ,'w' ) f.write(str (cipher)) f.close() print (decrypt(encrypt(flag,pubKey),privKey,q,r))assert decrypt(encrypt(flag,pubKey),privKey,q,r)==flag
查看代码我们会比较清楚这个背包加密的过程其实就是Merkle-Hellman背包加密算法 。生成超递增序列作为私钥。
生成私钥 p r i v k e y n 满足 p r i v k e y k > ∑ i = 1 k − 1 生成乘数 r , 模数 p , 公钥 p u b k e y = p r i v k e y ∗ r m o d p 生成私钥privkey_n满足privkey_k >\sum^{k-1}_{i=1}\\
生成乘数r,模数p,公钥pubkey_=privkey*r~mod~p\\
生 成 私 钥 p r i v k e y n 满 足 p r i v k e y k > i = 1 ∑ k − 1 生 成 乘 数 r , 模 数 p , 公 钥 p u b k e y = p r i v k e y ∗ r m o d p
但是在加密的时候有所不同,不同的地方在于,Merkle-Hellman背包加密算法 使用的是二进制序列去乘每个公钥,而我们的这个加密是使用flag
的ASCII 码直接去乘公钥。
c = ∑ i = 1 n o r d ( m i ) ∗ p u b k e y i c = \sum_{i=1}^{n}ord(m_i)*pubkey_i
c = i = 1 ∑ n o r d ( m i ) ∗ p u b k e y i
很显然是经典的背包加密,并且我们已知公钥和密文,所以我们就使用经典的造格,对所造之格进行规约。
[ 1 0 ⋯ 0 p u b k e y 1 0 1 ⋯ 0 p u b k e y 2 ⋮ ⋮ ⋱ ⋮ ⋮ 0 0 ⋯ 1 p u b k e y n 0 0 ⋯ 0 c ] \begin{bmatrix}
1 & 0& \cdots &0 &pubkey_1\\
0 & 1& \cdots&0 &pubkey_2 \\
\vdots &\vdots&\ddots&\vdots&\vdots\\
0 &0&\cdots&1&pubkey_n\\
0 &0&\cdots&0&c\\
\end{bmatrix}
⎣ ⎢ ⎢ ⎢ ⎢ ⎢ ⎢ ⎡ 1 0 ⋮ 0 0 0 1 ⋮ 0 0 ⋯ ⋯ ⋱ ⋯ ⋯ 0 0 ⋮ 1 0 p u b k e y 1 p u b k e y 2 ⋮ p u b k e y n c ⎦ ⎥ ⎥ ⎥ ⎥ ⎥ ⎥ ⎤
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 from tqdm import tqdmfrom Crypto.Util.number import *pubkey = n = len (pubkey) L = Matrix(QQ,n+1 ,n+1 ) for i in range (n): L[i,i] = 1 L[-1 ,i] = 0 L[i,-1 ] = pubkey[i] L[-1 ,-1 ] = c result = L.LLL() for i in result[0 ]: print (chr (abs (int (i))),end='' )
但是如果我们这样造格,规约出来的结果m需要m / / 2 + 1 m//2+1 m / / 2 + 1 ,才能得到最终的明文,
[ 2 0 ⋯ 0 p u b k e y 1 0 2 ⋯ 0 p u b k e y 2 ⋮ ⋮ ⋱ ⋮ ⋮ 0 0 ⋯ 2 p u b k e y n 1 1 ⋯ 1 c ] \begin{bmatrix}
2 & 0& \cdots &0 &pubkey_1\\
0 & 2& \cdots&0 &pubkey_2 \\
\vdots &\vdots&\ddots&\vdots&\vdots\\
0 &0&\cdots&2&pubkey_n\\
1 &1&\cdots&1&c\\
\end{bmatrix}
⎣ ⎢ ⎢ ⎢ ⎢ ⎢ ⎢ ⎡ 2 0 ⋮ 0 1 0 2 ⋮ 0 1 ⋯ ⋯ ⋱ ⋯ ⋯ 0 0 ⋮ 2 1 p u b k e y 1 p u b k e y 2 ⋮ p u b k e y n c ⎦ ⎥ ⎥ ⎥ ⎥ ⎥ ⎥ ⎤
格攻击相关论文
这篇论文是介绍背包攻击的几个方法,其中就包含了格攻击,细读之后发现该论文只是介绍方法,已经怎么造格,并没有具体的证明。
对于格攻击的具体证明,即证明消息的二进制序列构成的向量是那个格中的最短向量。
题目1_
题目2_
题目3_hgame2025_ezBag
[ x 1 x 2 … x n − 1 x n − 1 − 1 − 1 − 1 ] ∗ [ 2 0 … 0 0 l i s t [ 0 ] 1 l i s t [ 1 ] 1 l i s t [ 2 ] 1 l i s t [ 3 ] 1 0 2 … 0 0 l i s t [ 0 ] 2 l i s t [ 1 ] 2 l i s t [ 2 ] 2 l i s t [ 3 ] 2 ⋮ ⋮ ⋱ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ 0 0 … 2 0 l i s t [ 0 ] 63 l i s t [ 1 ] 63 l i s t [ 2 ] 63 l i s t [ 3 ] 63 0 0 … 0 2 l i s t [ 0 ] 64 l i s t [ 1 ] 64 l i s t [ 2 ] 64 l i s t [ 3 ] 64 1 1 … 1 1 b a g [ 0 ] 0 0 0 1 1 … 1 1 0 b a g [ 1 ] 0 0 1 1 … 1 1 0 0 b a g [ 2 ] 0 1 1 … 1 1 0 0 0 b a g [ 3 ] ] \begin{bmatrix}
x_1&x_2&\dots&x_{n-1}&x_{n}&-1&-1&-1&-1
\end{bmatrix}
*
\begin{bmatrix}
2&0&\dots&0&0&list[0]_1&list[1]_1&list[2]_1&list[3]_1\\
0&2&\dots&0&0&list[0]_2&list[1]_2&list[2]_2&list[3]_2\\
\vdots&\vdots&\ddots&\vdots&\vdots&\vdots&\vdots&\vdots&\vdots\\
0&0&\dots&2&0&list[0]_{63}&list[1]_{63}&list[2]_{63}&list[3]_{63}\\
0&0&\dots&0&2&list[0]_{64}&list[1]_{64}&list[2]_{64}&list[3]_{64}\\
1&1&\dots&1&1&bag[0]&0&0&0\\
1&1&\dots&1&1&0&bag[1]&0&0\\
1&1&\dots&1&1&0&0&bag[2]&0\\
1&1&\dots&1&1&0&0&0&bag[3]\\
\end{bmatrix}
[ x 1 x 2 … x n − 1 x n − 1 − 1 − 1 − 1 ] ∗ ⎣ ⎢ ⎢ ⎢ ⎢ ⎢ ⎢ ⎢ ⎢ ⎢ ⎢ ⎢ ⎢ ⎢ ⎢ ⎡ 2 0 ⋮ 0 0 1 1 1 1 0 2 ⋮ 0 0 1 1 1 1 … … ⋱ … … … … … … 0 0 ⋮ 2 0 1 1 1 1 0 0 ⋮ 0 2 1 1 1 1 l i s t [ 0 ] 1 l i s t [ 0 ] 2 ⋮ l i s t [ 0 ] 6 3 l i s t [ 0 ] 6 4 b a g [ 0 ] 0 0 0 l i s t [ 1 ] 1 l i s t [ 1 ] 2 ⋮ l i s t [ 1 ] 6 3 l i s t [ 1 ] 6 4 0 b a g [ 1 ] 0 0 l i s t [ 2 ] 1 l i s t [ 2 ] 2 ⋮ l i s t [ 2 ] 6 3 l i s t [ 2 ] 6 4 0 0 b a g [ 2 ] 0 l i s t [ 3 ] 1 l i s t [ 3 ] 2 ⋮ l i s t [ 3 ] 6 3 l i s t [ 3 ] 6 4 0 0 0 b a g [ 3 ] ⎦ ⎥ ⎥ ⎥ ⎥ ⎥ ⎥ ⎥ ⎥ ⎥ ⎥ ⎥ ⎥ ⎥ ⎥ ⎤
题目4_LitCTF_new_Bag
参数不当