TEA加密算法及newstarctf2024茶里茶气
TEA是一种对称加密算法,使用相同的密钥进行加密解密,也是一种块加密算法(加密数据时以64位(8字节)为一组),TEA算法的密钥长度为128位(16字节)
TEA采用与DES算法类似的Feistel结构(采用模加减代替异或xor),迭代的每次循环使用加法和移位操作,对明文和密钥进行扩散和混乱,实现明文的非线性变换
目录
TEA加密算法
1. 数据准备
TEA算法处理的每个数据块长度为64位,分成两个32位的部分,记作:
- V0: 数据块的前32位
- V1: 数据块的后32位
2. 密钥准备
TEA使用128位的密钥,将密钥分为4个32位的子密钥:
- K0: 密钥的前32位
- K1: 密钥的中32位
- K2: 密钥的后32位
- K3: 密钥的最后32位
3. 加密算法
TEA的加密过程使用了一种简单的Feistel结构,分为64轮(即32轮加法和移位操作)。每一轮都使用密钥来扰动数据。
加密算法步骤:
- 初始化:设定输入数据块
(V0, V1),和密钥(K0, K1, K2, K3)。 - 循环加密:
- 每一轮都会根据以下公式进行操作:
V0 = V0 + ((V1 << 4) + K0) ^ (V1 + sum) ^ ((V1 >> 5) + K1)V1 = V1 + ((V0 << 4) + K2) ^ (V0 + sum) ^ ((V0 >> 5) + K3)
- 其中
sum是一个不断更新的常量,用于增强加密的复杂性。sum的更新方式为:sum = sum + deltadelta = 0x9e3779b9(一个常用的常数,称为黄金分割常数)
- 每一轮都会根据以下公式进行操作:
- 重复步骤2进行64轮(32轮加法和移位操作)。
- 输出密文:64轮之后,输出加密后的
(V0, V1),即为加密结果。
4. 解密算法
TEA的解密过程与加密过程类似,只是顺序相反。解密的核心是将加密公式中的操作进行反转,但由于TEA是基于Feistel结构,解密过程和加密过程使用相同的密钥和步骤,只是sum的更新方向是相反的。
解密算法步骤:
- 初始化:输入密文
(V0, V1)和密钥(K0, K1, K2, K3)。 - 循环解密:
- 每一轮使用反向的公式进行解密:
V1 = V1 - ((V0 << 4) + K2) ^ (V0 + sum) ^ ((V0 >> 5) + K3)V0 = V0 - ((V1 << 4) + K0) ^ (V1 + sum) ^ ((V1 >> 5) + K1)
sum的更新方向是相反的:sum = sum - deltadelta = 0x9e3779b9
- 每一轮使用反向的公式进行解密:
- 重复步骤2进行64轮(32轮加法和移位操作)。
- 输出明文:64轮之后,输出解密后的
(V0, V1),即为原始的明文数据。
茶里茶气
下载解压得到一个python文件

解读代码:
assert len( flag ) == 25
第四行:确保flag的长度为25
a = ""
for i in flag:
a += hex(ord(i))[2:]
l = int(a,16).bit_length()
print("l =" , l )
第六-八行:首先初始化了一个空字符串a,然后先得到flag中每一个字符i的ASCII码(ord()函数用于返回一个字符对应的Unicode码值)后转换为十六进制(e.g.0x41),并取第二位后的数据(即将0x去掉),最后将每一个i拼接到一起赋值给a
第九行:将a从十六进制转换为十进制整数(int( , ))后,该十进制转换为二进制形式且用最少二进制位数存储在l中
v0 = int(a,16)>>(l//2)
v1 = int(a,16)-(v0<<(l//2))
p = getPrime(l//2+10)
第十二行:将a从十六进制转换为十进制整数后右移(>>表示将数据整体右移,位数固定,空出的高位补0,高位是左边)l//2位(//表示向下取整的整除运算,e.g.5//2=2,-5//2=-3),并将得到的新数据赋值给v0
第十三行:将a从十六进制转换为十进制整数与v0右移(<<表示将数据整体左移,位数固定,空出的低位补0,低位是右边)l//2位后得到的新数据的差并赋值给v1
并可以推知v0是a转换为十进制整数后的左半部分,v1是a转换为十进制整数后的右半部分,所以可以推出a的值
a=hex((v0<<(l//2))+v1)[2:]
第十四行:获得一个(l//2+10)位数的质数并赋值给p
assert v1 < p and v0 < p and derta < p and v3 < p and v4 < p and v5 < p and v6 < p
第二十二行:确保v1 < p,v0 < p,derta < p,v3 < p,v4 < p,v5 < p,v6 < p
for i in range(32):
v1 += (v0+v2) ^ ( 8*v0 + v3 ) ^ ( (v0>>7) + v4 ) ; v1 %= p
v0 += (v1+v2) ^ ( 8*v1 + v5 ) ^ ( (v1>>7) + v6 ) ; v0 %= p
v2 += derta ; v2 %= p
第二十四行:将v0与v2相加的和与8*v0与v3的和异或,再将异或的值与v0右移7位与v4的和异或运算得到的结果与v1的初始值相加后的值赋给v1,然后将此时v1的值对p取模后的值赋给v1
第二十五行:与二十四行类似
第二十六行:将v2的值与derta的值相加后赋值给v2,然后将新得到的v2的值对p取模后得到的值赋给v2
可以先求出v2的值,再通过逆循环解密
for i in range(32):
v2+=derta;v2%=p
for i in range(32):
v2-=derta;v2%=p
v0-=(v1+v2)^(8*v1+v5)^((v1>>7)+v6);v0%=p
v1-=(v0+v2)^(8*v0+v3)^((v0>>7)+v4);v1%=p
最后定义flag为一个空字符串
并通过循环输出flag完整的值
flag=""
for i in range(0,len(a),2):
flag+=chr(int(a[i:i+2],16))
range(0,len(a),2)表示从0开始到a的长度为止,步长为2(步长指的是每次索引值增加的量,因为在十六进制中,每两个字符对应一个字节,一个字节对应8个二进制位(bit),每四个二进制位可以用一个十六进制数表示,所以两个十六进制字符能对应一个字节,一个字符通常占用一个字节,所以设置步长为二)
int(a[i:i+2],16)表示将a字符串中的第i个字符和第i+1个字符从十六进制转换为十进制整数,然后通过chr()将十进制整数转换为字符,最后将这些字符拼在一起就得到了flag字符串
以下是完整的代码:
from Crypto.Util.number import *
v2 = 0
derta = 462861781278454071588539315363
v3 = 489552116384728571199414424951
v4 = 469728069391226765421086670817
v5 = 564098252372959621721124077407
v6 = 335640247620454039831329381071
l = 199
p = 446302455051275584229157195942211
v0 = 190997821330413928409069858571234
v1 = 137340509740671759939138452113480
for i in range(32):
v2+=derta;v2%=p
for i in range(32):
v2-=derta;v2%=p
v0-=(v1+v2)^(8*v1+v5)^((v1>>7)+v6);v0%=p
v1-=(v0+v2)^(8*v0+v3)^((v0>>7)+v4);v1%=p
a=hex((v0<<(l//2))+v1)[2:]
flag=""
for i in range(0,len(a),2):
flag+=chr(int(a[i:i+2],16))
print(flag)
运行后得到flag
更多推荐

所有评论(0)