Cryptohack刷题记录(三) Mathematics部分 Lattice WriteUp


backpack Cryptography没出


Mathematics

Lattices

1. Vectors

基本的向量于标量之间的运算

题目:

v = (2,6,3), w = (1,0,0) and u = (7,7,2), calculate 3*(2*v - w) ∙ 2*u.

直接使用sage计算

sage: v = vector([2,6,3])
sage: w = vector([1,0,0])
sage: u = vector([7,7,2])
sage: 3*(2*v-w)*2*u
702

flag是702


2. Size and Basis

一组向量 v 1 , v 2 . . . v k ∈ V v_1,v_2...v_k\in V v1,v2...vkV是线性无关的,当且仅当
a 1 v 1 + a 2 v 2 + . . . + a k v k = 0 a_1v_1+a_2v_2+...+a_kv_k=0 a1v1+a2v2+...+akvk=0
仅在 a 1 = a 2 = . . . = a k = 0 a_1=a_2=...=a_k=0 a1=a2=...=ak=0时成立

基中的元素个数向量空间的维数

向量的大小,定义为||v||,是自己和自己的内积== ∣ ∣ v ∣ ∣ 2 = v ⋅ v ||v||^2=v\cdot v v2=vv==

一组正交基(orthogonal) v 1 , v 2 , . . . , v n ∈ V v_1,v_2,...,v_n\in V v1,v2,...,vnV,任意两个元素的内积为0== v i ⋅ v j = 0 , i ≠ j v_i\cdot v_j=0,i≠j vivj=0,i=j==

一组标准正交基(orthonormal),是对于所有 i ∈ [ 1 , n ] i\in[1,n] i[1,n]都满足满足== ∣ ∣ v i ∣ ∣ = 1 ||v_i||=1 vi=1==的正交基

题目:

计算 v = ( 4 , 6 , 2 , 5 ) v=(4,6,2,5) v=(4,6,2,5)的大小(size)

∣ ∣ v ∣ ∣ = v ⋅ v = ( 4 , 6 , 2 , 5 ) ⋅ ( 4 , 6 , 2 , 5 ) = 81 = 9 ||v||=\sqrt{v\cdot v}=\sqrt{(4,6,2,5)\cdot(4,6,2,5)}=\sqrt{81}=9 v=vv =(4,6,2,5)(4,6,2,5) =81 =9

flag就是9


3. Gram Schmidt

施密特算法

可以将向量组 v 1 , v 2 . . . v k ∈ V v_1,v_2...v_k\in V v1,v2...vkV正交化为 u 1 , u 2 , . . . , u k ∈ V u_1,u_2,...,u_k\in V u1,u2,...,ukV

u 1 = v 1 u_1=v_1 u1=v1
Loop i = 2 , 3 , . . . , n i=2,3,...,n i=2,3,...,n
Compute μ i j = v i ⋅ u j ∣ ∣ u j ∣ ∣ ( 1 ≤ j < i ) \mu_{ij}=\frac{v_i·u_j}{||u_j||}(1\le j<i) μij=ujviuj(1j<i)

​ Set u i = ∑ 1 ≤ j < i v i − μ i j ∗ u j u_i=\sum_{1\le j<i}v_i-\mu_{ij}*u_j ui=1j<iviμijuj

End Loop

题目:

v1 = (4,1,3,-1), v2 = (2,1,-3,4), v3 = (1,0,-2,7), v4 = (6, 2, 9, -5)

使用Gram Schmidt算法计算标准基

写了半天手搓线性代数,全是bug,不写了

寄了

一晚上全学线代了,

连个施密特都写不出来

Sage有内置的Gram Schmidt

sage: v0 = vector([4,1,3,-1])
sage: v1 = vector([2,1,-3,4])
sage: v2 = vector([1,0,-2,7])
sage: v3 = vector([6,2,9,-5])
sage: M = Matrix([v0,v1,v2,v3])
sage: M.gram_schmidt()
(
[         4          1          3         -1]
[     70/27      31/27      -23/9     104/27]
[  -287/397   -405/397    799/397    844/397]
[-1456/4023    273/298  1729/8046   455/4023],

[       1        0        0        0]
[   -4/27        1        0        0]
[    -1/3  468/397        1        0]
[   58/27 -659/794 439/4023        1]
)
sage: M
[ 4  1  3 -1]
[ 2  1 -3  4]
[ 1  0 -2  7]
[ 6  2  9 -5]

flag就是0.91611(四舍五入)

我也贴一个别人的solution,实现了题目所给的算法,作为学习参考

import numpy as np
v = [
    np.array([4,1,3,-1]), 
    np.array([2,1,-3,4]), 
    np.array([1,0,-2,7]), 
    np.array([6,2,9,-5]),
]

"""
u1 = v1
Loop i = 2,3...,n
   Compute μij = vi ∙ uj / ||uj||2, 1 ≤ j < i.
   Set ui = vi - μij * uj (Sum over j for 1 ≤ j < i)
End Loop
"""
u = [v[0]]
for vi in v[1:]:
    mi = [np.dot(vi, uj) / np.dot(uj, uj) for uj in u]
    u += [vi - sum([mij * uj for (mij, uj) in zip(mi,u)])]

print(round(u[3][1], 5))

4. What’s Lattice?

格的基本空间F

给定一组线性无关的向量 v 1 , v 2 , . . . , v n ∈ R m v_1,v_2,...,v_n\in R^m v1,v2,...,vnRm,则由这组向量生成的格 L L L 是由 v 1 , v 2 , . . . , v n v_1,v_2,...,v_n v1,v2,...,vn和对应整系数的组合
L = { a 1 ∗ v 1 + a 2 ∗ v 2 + . . . + a k ∗ v k : a 1 , a 2 , . . . , a n ∈ Z } L=\{a_1*v_1+a_2*v_2+...+a_k*v_k:a_1,a_2,...,a_n\in Z\} L={a1v1+a2v2+...+akvk:a1,a2,...,anZ}
L的基,可以是任意一组生成L的线性无关的向量。基的选择不唯一

The choice of basis is far from unique

在这里插入图片描述

由一组基向量和与基向量相乘的数,可以到达任何一点。一组基向量定义了一块基本空间(fundamental domain)
F ( v 1 , . . . , v n ) = { t 1 v 1 + t 2 v 2 + . . . + t n v n : 0 ≤ t i < 1 } F(v_1,...,v_n)=\{t_1v_1+t_2v_2+...+t_nv_n:0\le t_i<1\} F(v1,...,vn)={t1v1+t2v2+...+tnvn:0ti<1}
对于二维向量,基向量组成的基本空间就是 v 1 ⃗ \vec{v_1} v1 u 1 ⃗ \vec{u_1} u1 组成的平行四边形

我们可以通过基向量来计算基本空间。1题目:

已知 v 1 = ( 6 , 2 , − 3 ) , v 2 = ( 5 , 1 , 4 ) , v 3 = ( 2 , 7 , 1 ) v_1 = (6, 2, -3), v_2 = (5, 1, 4), v_3 = (2, 7, 1) v1=(6,2,3),v2=(5,1,4),v3=(2,7,1),计算基本空间的体积(the volume of the fundamental domain)
V o l ( F ) = ∣ d e t ( A ) ∣ = ∣ ∣ 6 2 − 3 5 1 4 2 7 1 ∣ ∣ Vol(F)=|det(A)|=\left|\left| \begin{matrix} 6 & 2 & -3\\ 5 & 1 & 4\\ 2 & 7 & 1 \end{matrix} \right|\right| Vol(F)=det(A)=652217341
使用sage计算

sage: v = vector
sage: v1 = v([6,2,-3])
sage: v2 = v([5,1,4])
sage: v3 = v([2,7,1])
sage: A = matrix([v1,v2,v3])
sage: A
[ 6  2 -3]
[ 5  1  4]
[ 2  7  1]
sage: det(A)
-255

flag就是255


5. Gaussian Reduction

如果你仔细观察,“格”开始出现在密码学中的每一个角落。 有时它们操纵一个加密系统,破坏了(生成)不够安全的参数。 最著名的例子是 Coppersmith 对 RSA 加密的攻击。

格也可用于构建加密协议,其安全性基于两个基本的“难”问题:

  1. The Shortest Vector Problem (SVP)

给定的基向量和格,找到格 L L L中的长度最短非零向量。换言之,找到一个非零向量 v ∈ L v\in L vL使 ∣ ∣ v ∣ ∣ ||v|| v 最小

  1. The Closest Vector Problem (CVP)

给定的基向量、格,和一个不在格上的目标向量,找到距离目标向量最近的格向量。

给定一个不在格 L L L中的向量 w ∈ R m w\in R^m wRm,找到向量 v ∈ L v\in L vL使 ∣ ∣ v − w ∣ ∣ ||v-w|| vw 最小

对于一般的格,SVP问题十分困难,但对于相对简单的情况,我们有非常有效的算法可以计算出问题的解或者近似解。当格的维数小于等于4时,可以在多项式时间内计算出确定解,对于更高维,只能求出近似解。

高斯发明了一种算法来找到给定任意基的二维格 L L L 的最佳基。算法的输出 v1 是 L L L 中最短的非零向量,由此解决了二维的SVP问题。

高斯的算法大致通过从另一个基向量中减去一个基向量的倍数来达到目标,直到不再可能使它们变得更小。这适用于二维,因此可以很好地可视化。
在这里插入图片描述

一句一句的翻译已经很累了,不想再敲公式了,直接放图吧

脚本也不想写了

直接交互式命令行吧

import numpy as np
ar = np.array
v = ar([846835985, 9834798552],dtype='i8')
u = ar([87502093, 123094980],dtype='i8')
v1,v2 = u,v
>>> if siz2(v2) < siz2(v1):
	print('swap')
	v1,v2 = v2,v1

>>> m = int(v1.dot(v2)/v1.dot(v1));m
56
>>> v2 = v2 - m*v1;v2
array([-4053281223,  2941479672], dtype=int64)
>>> if siz2(v2) < siz2(v1):
	print('swap')
	v1,v2 = v2,v1
	
>>> m = int(v1.dot(v2)/v1.dot(v1));m
0
>>> v1
array([ 87502093, 123094980], dtype=int64)
>>> v2
array([-4053281223,  2941479672], dtype=int64)
>>> v1.dot(v2)
7410790865146821

flag即7410790865146821


6. Find the Lattice

这种题在各类比赛里比较常见了,可以参考一下这个
https://xz.aliyun.com/t/7163
题目

from Crypto.Util.number import getPrime, inverse, bytes_to_long
import random
import math

FLAG = b'crypto{?????????????????????}'
def gen_key():
    q = getPrime(512)
    upper_bound = int(math.sqrt(q // 2))
    lower_bound = int(math.sqrt(q // 4))
    f = random.randint(2, upper_bound)
    while True:
        g = random.randint(lower_bound, upper_bound)
        if math.gcd(f, g) == 1:
            break
    h = (inverse(f, q)*g) % q
    return (q, h), (f, g)

def encrypt(q, h, m):
    assert m < int(math.sqrt(q // 2))
    r = random.randint(2, int(math.sqrt(q // 2)))
    e = (r*h + m) % q
    return e

def decrypt(q, h, f, g, e):
    a = (f*e) % q
    m = (a*inverse(f, g)) % g
    return m


public, private = gen_key()
q, h = public
f, g = private

m = bytes_to_long(FLAG)
e = encrypt(q, h, m)

print(f'Public key: {(q,h)}')
print(f'Encrypted Flag: {e}')

已知的值有

q,h,e

其中
h ≡ f − 1 g m o d    p e ≡ ( r h + m ) m o d    p h\equiv f^{-1}g\mod p\\ e\equiv (rh+m)\mod p hf1gmodpe(rh+m)modp

q 512位
f,g 低于256位
h,e 512位
r 256位
m为flag,大小约为231bit

要求得m,需要获得函数decrypt的参数,还剩fg是未知的

而,我们唯一知道的关于fg的关系式,只有
f ⋅ h ≡ g m o d    p f\cdot h\equiv g\mod p fhgmodp
这里需要使用上一题的格的思想
我们可以构造一个由下面这个矩阵M中的两个行向量(1,h),(0,q)所张成的lattice:
M = [ 1 h 0 q ] M= \left[ \begin{matrix} 1&h\\ 0&q \end{matrix} \right] M=[10hq]
首先,向量(f,g)是在这个格上的,即存在系数 a a a, b b b 使 a ( 1 , h ) + b ( 0 , q ) = ( f , p ) a(1,h)+b(0,q)=(f,p) a(1,h)+b(0,q)=(f,p)
f ⋅ h ≡ g m o d    q f ⋅ h = g + u ⋅ q f ⋅ h − u ⋅ q = g f\cdot h\equiv g\mod q\\ f·h=g+u·q\\ f·h-u·q=g\\ fhgmodqfh=g+uqfhuq=g
a , b = f , − u a,b=f,-u a,b=f,u满足条件

向量(f,g)可以由两组基向量M的某种整系数线性组合(f, -u)来表示,因此向量(f,g)就在这个lattice上。

对于两个基底向量,

(1,h) : 512位

(0,q) : 512位

而向量 (f,g) 的长度 : 大约256位

相比于基底向量,是非常小的。

很大概率上,这个(f, g)就是这个lattice的最短向量,(Gaussian heurstic)。

上一题我没写程序,现在补回来…

# sage
# GaussLatticeReduction
def Gauss(v1, v2):
    while True:
        if v2.norm() < v1.norm():
            v1, v2 = v2, v1
        m = round( v1*v2 / v1.norm()^2 )
        if m == 0:
            return (v1, v2)
        v2 = v2 - m*v1
sage: v = vector([1,h])
sage: u = vector([0,q])
sage: Gauss(u,v)
((47251817614431369468151088301948722761694622606220578981561236563325808178756, 43997957885147078115851147456370880089696256470389782348293341937915504254589),
(-67269010250212717075432182693043963184097648880165008621907831284647116025901, 99012763459529858809608436133564630524350106000242070336818304053467942269178))

输出的第一个向量即所求的(f,g)

>>> len(bin(f)[2:])
255
>>> len(bin(g)[2:])
255

长度也满足

现在直接运行decrypt函数即可得到flag

q, h = (7638232120454925879231554234011842347641017888219021175304217358715878636183252433454896490677496516149889316745664606749499241420160898019203925115292257, 2163268902194560093843693572170199707501787797497998463462129592239973581462651622978282637513865274199374452805292639586264791317439029535926401109074800)
e = 5605696495253720664142881956908624307570671858477482119657436163663663844731169035682344974286379049123733356009125671924280312532755241162267269123486523
f,g = 47251817614431369468151088301948722761694622606220578981561236563325808178756, 43997957885147078115851147456370880089696256470389782348293341937915504254589
print(l2b(decrypt(q,h,f,g,e)))

crypto{}


7. Backpack Cryptography

背包加密算法

参考密码学——公钥密码体系之背包算法1_摆渡沧桑-CSDN博客

这是一种早期的公钥算法,背包算法的安全性起源于背包难题,即子集和问题,他是一个NP完全问题,但后来发现该算法并不安全,但是它证明了如何将NP完全问题用于公开秘钥密码学

在这里插入图片描述
对于易解的背包问题,可以选择超递增序列,那么相应的背包问题容易求解。
超递增序列:它的每一项都大于它之前所有项之和,例如{1,3,6,13,27,52}是一个超递增序列,而{1,3,4,9,15,25}则不是。

非超递增序列的背包是困难的问题,它们没有快速算法

背包加密算法在于,公钥是一个非超递增序列背包,私钥是超递增序列和将两者互相转换的 r r r

背包问题的解密及破解_Taotaoboke的博客-CSDN博客_超递增序列背包问题

破译的基本思想是不必找出正确的模数 q q q 和乘数 r r r(即陷门信息)只需要找到出任意模数 k ′ k' k 和乘数 t ′ t' t,然后用 k ′ k' k t ′ t' t 对公开的背包向量 b b b 求出新的超递增向量即可。

方式是通过构造一个格,用LLL直接日。
记已知的公钥数组为 p k pk pk ,加密后的结果为 e n c enc enc,则可以构造:
A = ( 2 0 0 ⋯ 0 p k [ 0 ] 0 2 0 ⋯ 0 p k [ 1 ] 0 0 2 ⋯ 0 p k [ 2 ] ⋮ ⋮ ⋮ ⋱ ⋮ ⋮ 0 0 0 … 2 p k [ − 1 ] 1 1 1 … 1 e n c ) A=\begin{pmatrix} 2 & 0 & 0 & \cdots & 0 & pk[0] \\ 0 & 2 & 0 & \cdots & 0 & pk[1] \\ 0 & 0 & 2 & \cdots & 0 & pk[2] \\ \vdots & \vdots & \vdots & \ddots & \vdots & \vdots \\ 0 & 0 & 0 & \dots & 2 & pk[-1] \\ 1 & 1 & 1 & \dots & 1 & enc \\ \end{pmatrix} A=20001020010020100021pk[0]pk[1]pk[2]pk[1]enc
计算A.LLL(),在每一行中寻找满足条件的。

from output import *
from Crypto.Util.number import isPrime, bytes_to_long, inverse, long_to_bytes

nbit = len(pk)
# N = nextprime(gmpy2.iroot(nbit,2)[0]//2)

A = [[0] * (nbit+1) for _ in range(nbit+1)]
for i in range(nbit):
    A[i][i] = 2
    A[i][-1] = pk[i]# *N
    A[-1][i] = 1

A[-1][-1] = int(enc)# *N

A = Matrix(ZZ,A)
r = A.LLL()
for i in r:
    if len(set(i[:-1])) == 2:
        F = i

print(long_to_bytes(int(''.join(str(i) for i in [1 if i == -1 else 0 for i in F][::-1]),2)))

# crypto{my_kn4ps4ck_1s_l1ghtw31ght}

有更多的格的构造方法,能够求解更大密度的背包问题,详细可以参考2020密码数学挑战赛第三题

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值