2022 强网杯 Crypto

Lattice

from sage.modules.free_module_integer import IntegerLattice
from Crypto.Cipher import AES
from base64 import b64encode
from hashlib import *
from secret import flag
import signal

n = 75
m = 150
r = 10
N = 126633165554229521438977290762059361297987250739820462036000284719563379254544315991201997343356439034674007770120263341747898897565056619503383631412169301973302667340133958109

def gen(n, m, r, N):
    t1 = [ZZ.random_element(-2^15, 2^15) for _ in range(n*m)]
    t2 = [ZZ.random_element(N) for _ in range(r*n)]
    B = matrix(ZZ, n, m, t1)        # B为75*150的矩阵
    L = IntegerLattice(B)
    A = matrix(ZZ, r, n, t2)        # A为10*75的矩阵
    C = (A * B) % N                 # C为10*150的矩阵
    return L, C

def pad(s):
    return s + (16 - len(s) % 16) * b"\x00"

signal.alarm(60)
token = input("team token:").strip().encode()
L, C = gen(n, m, r, N)
print(C)
key = sha256(str(L.reduced_basis[0]).encode()).digest()
aes = AES.new(key, AES.MODE_ECB)
ct = b64encode(aes.encrypt(pad(flag))).decode()
print(ct)

题目生成了一个元素在 间的矩阵 ,和 上的矩阵 。然后给了一个两矩阵相乘再模 的结果 ,即 ,需要我们恢复出原来格的最短向量。

比赛时尝试过构造 ,跑LLL出来的结果很差, BKZ 的话一晚上啥也没出来。。 赛后只找到 Nu1L 队的 wp ,but 也只有个 exp ,一句解释都没,像我这样的菜鸡分析起来就十分吃力了,但聊胜于无嘛,其他几个队连个 wp 都不放呜呜呜。

exp 中构造了一个 维的方阵 如下:

可以看到 左上角为一个 维的单位阵,左下角为零阵,右上角为 的转置数乘 ,右下角为一个 维的单位阵数乘 ,这里乘不乘 得到的结果都是一样的,但神奇的是不乘的话 LLL 耗时会长一些。 跑一遍 LLL 后,取结果的左上角 矩阵,记为 ,取 的核记为 ,最后 跑一遍 BKZ 后的最短向量即为所求。

这里涉及到矩阵的核的概念,核也叫矩阵的零空间,比如 的核是方程 的所有解 的集合。

那么就不难看出有 ,即 至于 为什么左边要拼接一个单位阵,回忆你已经遗忘的线性代数,这种操作是不是似曾相识?没错说的就是矩阵的求逆,求逆矩阵除了伴随矩阵法,还有一种方法就是初等变换法。 例如如下一个矩阵求逆

在右边补上一个单位阵,得到

此时右边的矩阵 即为所求 究其原理,在进行初等行变换的时候,右边的矩阵“记录”下了我们的操作,可以表示为

回到题目中来,有

至于右上角为何是一个 的零阵,我也不知道,但 exp 既然这么断言,姑且就这么认为先,那么我们就推测

即有

得到 ,结合上面 ,推测。。。推测不出来了,然后结合队里大手子的分析如下

记题目中给出的两个矩阵为 ,有 若有 ,则 互为左右零空间。 因此可以通过构造格 ,使得对格 进行格基规约后可以得到一组基 满足 ,即 的基就是 在模 下的一个左零空间。 接下来对 求解其零空间 就得到了 所在的那个空间上了,这里从有限域化为整数域,即 ,然后因为 中所求的行向量是一个短向量,且矩阵 的行向量是 的行向量的线性组合,因此对 进行格基规约算法就可以把 的短向量给恢复出来。

至此已经有明悟的感觉,但仍是有少许不解,消化一段时间吧。

文章作者weyung
文章链接https://weyung.cc/posts/09630dba/
许可协议CC BY-NC-SA 4.0
上一篇

高数下笔记

下一篇

密码学论文记