diff --git a/crypto/Claude_Shannon/crack.sage b/crypto/Claude_Shannon/crack.sage new file mode 100644 index 0000000..ba7ecb9 --- /dev/null +++ b/crypto/Claude_Shannon/crack.sage @@ -0,0 +1,195 @@ +""" +前置知识(建议要看,非常自信可以不看) +https://zhuanlan.zhihu.com/p/448198789 + +信息论的一些基础 +可以结合这份ppt来看 +https://web.xidian.edu.cn/jwliu/files/20190521_003040.pdf + +同时b站上的国防科技大学-信息论与编码基础也可以学习一下 +https://www.bilibili.com/video/BV1pJ411U7G8?p=61 + + +读题可知,我们需要获取一个7bit的有效信息,每次返回的信息可以看作1bit,共15bit。 +而其中1-2次的说谎我们可以视为1-2bit的传输错误。 +因此我们需要一种最少可以纠正2bit的编码,也就是循环冗余校验 + +根据题目描述,我们需要构造一个(15,7)循环码,也就是15bit的编码数,7bit的有效信息数 +""" +# 首先定义伽罗瓦域 +R. = PolynomialRing(GF(2)) + +""" +然后寻找生成元g +根据定理,(n,k)循环码的生成元g一定是x^n+1的n-k次因子 +(ppt中说是x^n-1,这里因为是GF(2),所以+1和-1是等价的) +带入计算得知,g应当为x^15+1的8次因子 +""" +ff = x^15+1 +g = factor(ff) +# (x + 1) * (x^2 + x + 1) * (x^4 + x + 1) * (x^4 + x^3 + 1) * (x^4 + x^3 + x^2 + x + 1) +factors = [] +for i in range(len(g)): + factors.append(g[i][0]) + +possible_g = [] + +# x + 2 * y + 4 * z == 8 +# root = [(8,0,0),(6,1,0),(4,2,0),(4,0,1),(2,3,0),(2,1,1),(0,4,0),(0,2,1),(0,0,2)] + + +possible_g.append(factors[0]^8) + +possible_g.append(factors[0]^6 * factors[1]) + +possible_g.append(factors[0]^4 * factors[1]^2) + +possible_g.append(factors[0]^4 * factors[2]) +possible_g.append(factors[0]^4 * factors[3]) +possible_g.append(factors[0]^4 * factors[4]) + +possible_g.append(factors[0]^2 * factors[1]^3) + +possible_g.append(factors[0]^2 * factors[1] * factors[2]) +possible_g.append(factors[0]^2 * factors[1] * factors[3]) +possible_g.append(factors[0]^2 * factors[1] * factors[4]) + +possible_g.append(factors[1]^4) + +possible_g.append(factors[1]^2 * factors[2]) +possible_g.append(factors[1]^2 * factors[3]) +possible_g.append(factors[1]^2 * factors[4]) + +possible_g.append(factors[2] * factors[2]) +possible_g.append(factors[2] * factors[3]) +possible_g.append(factors[2] * factors[4]) +possible_g.append(factors[3] * factors[3]) +possible_g.append(factors[3] * factors[4]) +possible_g.append(factors[4] * factors[4]) + +""" +至此我们已经找出所有的生成元了,但是不要忘了还有纠错的要求。 +纠正2bit的要求则是: 假设用d表示码组的最小汉明距离,纠正错误时,设可纠正t位的错误,则d >= 2t+1 +(也就是保证任一点A的错误状态不落到其余点B,并且A的错误状态离A更近) +带入公式则是d >= 5 +""" + +# 定义函数 n2p,将二进制数转换为多项式的形式 +def n2p(a): + a = bin(a)[2:] + p = 0 + for i in range(len(a)): + if a[len(a) - i - 1] == '1': + p += x^i + return p + +# 定义函数enc,将7bit的信息编码成循环码 +def enc(i): + m = n2p(i) + c = (x^8) * m + (((x^8) * m) % g) + return "".join([str(int(i in c.exponents())) for i in range(15)])[::-1] + +# 定义函数dif,计算ab之间的汉明距离 +def dif(a, b): + cnt = 0 + for i in range(len(a)): + if a[i] != b[i]: + cnt += 1 + return cnt + +""" +通过构造校验表来检查所有的生成元是否满足条件 +""" +for g in possible_g: + # 创建一个列表 dic,存储不同输入值的编码 + dic = [] + + for i in range(2**7): + dic.append(enc(i)) + + minHD = 15 + for i in dic: + for j in dic: + if i == j: + continue + if dif(i,j) <= minHD: + minHD = dif(i,j) + + #if minHD >= 5: + #print(g) + # x^8 + x^7 + x^6 + x^4 + 1 + # x^8 + x^4 + x^2 + x + 1 + + +# 下面两个随便挑一个作为生成元就好了 +# g = x^8 + x^7 + x^6 + x^4 + 1 +g = x^8 + x^4 + x^2 + x + 1 +dic = [] + +for i in range(2^7): + dic.append(enc(i)) + +""" +构造问题这个地方有点巧妙。 +我们可以发现系统码是一个对称互补的码,也就是说0和127,1和126的系统码每一位都相反。 + +那么对于所有系统码来说0和1就是平均分布的。(因为成对来看,30个bit中必有15bit的0和15bit的1) +对于系统码的任意一位为0的概率就是0.5,为1的概率也是0.5 + +因此我们可以把questions[i]和j[i]等价来看, +我们可以找出所有令j[i]==1的信息码情况,共64种,把这些进行or运算。 +把这个问题发送出去,如果返回true,那么证明j[i]就是1。 +以此类推,我们就可以得到一串15bit的返回值。 + +但是因为存在最多2bit的错误,我们需要跟码表进行对比,找出汉明距离小于等于2的系统码。 +然后这个系统码对应的信息码就是正确的信息码 +""" + +# 定义字符串模板 part,用于后面构造问题 +part = "( C0 == {} and C1 == {} and C2 == {} and C3 == {} and C4 == {} and C5 == {} and C6 == {} ) " + +# 创建问题列表 questions,用于存储构造的问题模板 +questions = [] +for i in range(15): + question = "" + nums = [j[:7] for j in dic if j[i] == '1'] + for j in range(64): + n = nums[j] + question += part.format(n[0], n[1], n[2], n[3], n[4], n[5], n[6]) + "or " + questions.append(question[:-4]) + + + +# 导入 pwn 模块,用于与远程或本地进程进行交互 +from pwn import * + +# 设置日志级别为 debug,以便在执行过程中输出详细日志信息 +#context.log_level = 'debug' + +r = process(['python3', 'main_local.py']) +#r = remote("localhost", "10011") + +for i in range(1): + msg = '' + # 逐个发送问题并接收答案 + for j in range(15): + r.sendlineafter(b"Shannon:", questions[j].encode()) + r.recvuntil(b"answers:") + if b'True' in r.recvuntil(b"!"): + msg += '1' + else: + msg += '0' + # 查找与收到的答案最接近的编码,这里答案肯定只会有一个 + for j in range(128): + if (dif(msg, dic[j]) <= 2): + break + ans = "" + for k in dic[j][:7]: + ans += k + " " + # 发送解码后的答案 + r.sendlineafter(b"chests:", ans.encode()) + +flag = r.recvall() +print(flag) +r.close() +