熵密杯2024复现

1.puzzle1

题目

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
def encrypt_message(message, key):
# 添加前缀
message_with_prefix = MSG_PREFIX + message
message_bytes = message_with_prefix.encode('utf-8')
message_len = len(message_bytes)
num_blocks = message_len // 16 + 1
blocks = [message_bytes[i * 16:(i + 1) * 16] for i in range(num_blocks)]

# 进行0填充
blocks[-1] = blocks[-1].ljust(16, b'\x00')

encrypted_blocks = []

k = key

# 加密每个分组
for block in blocks:
block_int = int.from_bytes(block, byteorder='big')
encrypted_block_int = Mod(block_int * k, MODULUS)
encrypted_blocks.append(encrypted_block_int)
k += 1 # 密钥自增1

# 将加密后的分组连接成最终的密文
encrypted_message = b''.join(
int(block_int).to_bytes(32, byteorder='big') for block_int in encrypted_blocks
)

return encrypted_message

一个简单的块加密,16个字节为一块,加密函数是 k的生成很简单,给一个初始的initial_key,然后每次加1,求出一开始的就行

每个message都带有前缀

CryptoCup message: 一共是18字节

那么也就是CryptoCup messag为第一个分块,但是分块加密之后,一块是32字节

然后我们又知道密文,取前32个字节,就能得到密钥了

exp

1
2
3
4
5
6
7
#exp
enc = encrypted_message
prefix = enc[:32].hex()
message_bytes = MSG_PREFIX.encode('utf-8')[:16].hex()
recover_key = int(prefix,16) * mod_inverse(int(message_bytes,16),MODULUS) % MODULUS
print(recover_key)
print(decrypt_message(enc,recover_key))

2.puzzle2

题目

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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
import binascii
from gmssl import sm3


# 读取HMAC key文件
def read_hmac_key(file_path):
with open(file_path, 'rb') as f:
hmac_key = f.read().strip()
return hmac_key


# 生成token
def generate_token(hmac_key, counter):
# 如果HMAC_KEY长度不足32字节,则在末尾补0,超过64字节则截断
if len(hmac_key) < 32:
hmac_key = hmac_key.ljust(32, b'\x00')
elif len(hmac_key) > 32:
hmac_key = hmac_key[:32]

# 将计数器转换为字节表示
counter_bytes = counter.to_bytes((counter.bit_length() + 7) // 8, 'big')
# print("counter_bytes:", binascii.hexlify(counter_bytes))

tobe_hashed = bytearray(hmac_key + counter_bytes)

# print("tobe_hashed:", binascii.hexlify(tobe_hashed))

# 使用SM3算法计算哈希值
sm3_hash = sm3.sm3_hash(tobe_hashed)

# 将SM3的哈希值转换为十六进制字符串作为token
token = sm3_hash

return token


current_counter = 0


def verify_token(hmac_key, counter, token):
# 生成token
generated_token = generate_token(hmac_key, counter)
global current_counter
# 比较生成的token和输入的token是否相同
if generated_token == token:
if counter & 0xFFFFFFFF > current_counter:
current_counter = counter & 0xFFFFFFFF
print("current_counter: ", hex(current_counter))
return "Success"
else:
return "Error: counter must be increasing"
else:
return "Error: token not match"


# 假设HMAC key文件路径
hmac_key_file = 'hmac_key.txt'
# 假设计数器值
counter = 0x12345678

# 读取HMAC key
hmac_key = read_hmac_key(hmac_key_file)

# 生成token
token = generate_token(hmac_key, counter)
print("Generated token:", token)
print(verify_token(hmac_key, counter, token))

虽然说是HMAC吧,但是他这里实现的其实就是MAC

SM3算法也是基于MD结构的,和MD5的填充基本一致,只有一处不同

那就是SM3算法填充长度是大端序,MD5是小端序

SM3 填充过程

对于消息 "abc" (长度3字节 = 24位):

  1. 原始消息: 0x61 0x62 0x63
  2. 添加 0x80: 0x61 0x62 0x63 0x80
  3. 填充0x00直到长度56字节: [0x61 0x62 0x63 0x80] + [0x00]*52
  4. 添加64位长度(大端序): 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x18

MD5 填充过程

对于同样的消息 "abc":

  1. 原始消息: 0x61 0x62 0x63
  2. 添加 0x80: 0x61 0x62 0x63 0x80
  3. 填充0x00直到长度56字节: [0x61 0x62 0x63 0x80] + [0x00]*52
  4. 添加64位长度(小端序): 0x18 0x00 0x00 0x00 0x00 0x00 0x00 0x00

SM3的压缩算法更加复杂,有8个寄存器,不过由于是大端序,倒是少了转换的步骤

我们大体了解一下就行,压缩函数是CF(IV,block)

IV是上一个block算出来的SM3值,然后初值IV是给定的,这都和MD5类似,那么照搬MD5的长度拓展攻击就行了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def SM3_len_ex_ak(num_block,IV,plaintext):
Vtemp=IV
a=(len(plaintext)*4+1 ) % 512
#print(a)
k=0
B=[]
if a<=448:
k=448-a
elif a>448:
k=512-a+448
#print(k)
m=plaintext+"8"+"0"*int((k+1)/4-1)+zero_fill(str(hex(len(plaintext)*4+num_block*512))[2:],16) #填充
#print(m)
block_len=int((len(plaintext)*4 + k + 65) / 512)
#print(block_len)
for i in range(0,block_len):
B.append(m[128*i:128*i+128]) #分组
#print("B:",B)
for i in range(0,block_len):
Vtemp=CF(Vtemp,B[i])

return Vtemp

题目要求给定一个counter+hash

然后输出一个更大的counter+hash

我们只要在后面加上一个,这样一定是最大的

exp

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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
def zero_fill(a,n):
if len(a)<n:
a="0"*(n-len(a))+a
return a
def cycle_shift_left( B, n):
n=n%32
return ((B << n) ^ (B >> (32 - n)))%(2**32)

def T(j):
if j>=0 and j<=15:
return int("79cc4519",16)
elif j>=16 and j<=63:
return int("7a879d8a",16)

def FF(X,Y,Z,j):
if j>=0 and j<=15:
return X^Y^Z
elif j>=16 and j<=63:
return (X&Y)|(X&Z)|(Y&Z)
def GG(X,Y,Z,j):
if j >= 0 and j <= 15:
return X ^ Y ^ Z
elif j >= 16 and j <= 63:
return (X & Y) | (~X & Z)

def P0(x):
return x^(cycle_shift_left(x,9))^cycle_shift_left(x,17)
def P1(x):
return x^(cycle_shift_left(x,15))^cycle_shift_left(x,23)

def Message_extension(a): #a的数一定要满足512bit,不够要补零!! ,承接的是字符串
W1 = [] # W0-15
W2=[] # W' 0-63
#print("a消息扩展的a:",a)
for i in range(int(len(a) / 8)):
W1.append(int(a[8 * i:8 * i + 8],16))
#print("W1的前16个",a[8 * i:8 * i + 8])
for j in range(16,68):
temp=P1(W1[j-16] ^ W1[j-9] ^ cycle_shift_left(W1[j-3],15)) ^cycle_shift_left(W1[j-13],7)^W1[j-6]
#print("消息扩展:",hex(temp))
W1.append(temp)

for j in range(0,64):
W2.append(W1[j]^W1[j+4])

W1.append(W2)
return W1

def CF(V,Bi): #V是字符串
Bi=zero_fill(Bi,128)
W=[]
W=Message_extension(Bi) #消息扩展完的消息字
#print("W:",W)
A=int(V[0:8],16)
#print("A:", hex(A))
B = int(V[8:16], 16)
C = int(V[16:24], 16)
D = int(V[24:32], 16)
E = int(V[32:40], 16)
F = int(V[40:48], 16)
G = int(V[48:56], 16)
H = int(V[56:64], 16)
for j in range(0,64):
temp=(cycle_shift_left(A,12) + E +cycle_shift_left(T(j),j)) %(2**32)
SS1=cycle_shift_left(temp,7)
SS2=SS1 ^ cycle_shift_left(A,12)
TT1=(FF(A,B,C,j) +D +SS2 +W[-1][j] ) %(2**32)
TT2=(GG(E,F,G,j)+H+SS1+W[j])%(2**32)
D=C
C=cycle_shift_left(B,9)
B=A
A=TT1
H=G
G=cycle_shift_left(F,19)
F=E
E=P0(TT2)
#print("B:", hex(B))
t1=zero_fill(hex(A^int(V[0:8],16))[2:],8)
t2 = zero_fill(hex(B ^ int(V[8:16], 16))[2:], 8)
t3 = zero_fill(hex(C ^ int(V[16:24], 16))[2:], 8)
t4 = zero_fill(hex(D ^ int(V[24:32], 16))[2:], 8)
t5 = zero_fill(hex(E ^ int(V[32:40], 16))[2:], 8)
t6 = zero_fill(hex(F ^ int(V[40:48], 16))[2:], 8)
t7 = zero_fill(hex(G ^ int(V[48:56], 16))[2:], 8)
t8 = zero_fill(hex(H ^ int(V[56:64], 16))[2:], 8)
t=t1+t2+t3+t4+t5+t6+t7+t8
return t

def SM3(plaintext):
Vtemp=IV
a=(len(plaintext)*4+1 ) % 512
#print(a)
k=0
B=[]
if a<=448:
k=448-a
elif a>448:
k=512-a+448
#print(k)
m=plaintext+"8"+"0"*int((k+1)/4-1)+zero_fill(str(hex(len(plaintext)*4))[2:],16)
#print(m)
block_len=int((len(plaintext)*4 + k + 65) / 512)
#print(block_len)
for i in range(0,block_len):
B.append(m[128*i:128*i+128]) #分组
#print("B:",B)
for i in range(0,block_len):
Vtemp=CF(Vtemp,B[i])

return Vtemp

def SM3_len_ex_ak(num_block,IV,plaintext):
Vtemp=IV
a=(len(plaintext)*4+1 ) % 512
#print(a)
k=0
B=[]
if a<=448:
k=448-a
elif a>448:
k=512-a+448
#print(k)
m=plaintext+"8"+"0"*int((k+1)/4-1)+zero_fill(str(hex(len(plaintext)*4+num_block*512))[2:],16)
#print(m)
block_len=int((len(plaintext)*4 + k + 65) / 512)
#print(block_len)
for i in range(0,block_len):
B.append(m[128*i:128*i+128]) #分组
#print("B:",B)
for i in range(0,block_len):
Vtemp=CF(Vtemp,B[i])

return Vtemp

IV="7380166f4914b2b9172442d7da8a0600a96f30bc163138aae38dee4db0fb0e4e"


#############################################################################
IV2="f81b58b75c469707402e44054d4d45dcacb0294daba39501f5353e20b6fcfe2a"
num_block=1
counter = "fde43312"
New_Counter = hex(int((bin(int(counter,16))[2:].zfill(32) + "1") + "0"*(448 - 32*8 - 1 - 4*8) + bin(36*8)[2:].zfill(64) , 2))[2:] + "ffffffff"

print(New_Counter)
print(SM3_len_ex_ak(1,IV2,"FFFFFFFF"))

3.puzzle3

题目

一道lwe,先看看加密

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 设置参数
n = 16 # 向量长度
q = 251 # 模数

# 生成随机噪声向量e
e = sp.Matrix(sp.randMatrix(n, 1, min=0, max=1)) # 噪声向量

# 生成随机n维私钥向量s和n*n矩阵A
s = sp.Matrix(sp.randMatrix(n, 1, min=0, max=q - 1)) # 私钥向量
Temp = sp.Matrix(sp.randMatrix(n, n, min=0, max=q - 1)) # 中间变量矩阵Temp
A = Temp.inv_mod(q) # 计算矩阵Temp在模 q 下的逆矩阵作为A

# 计算n维公钥向量b
b = (A * s + e) % q # 公钥向量b = A * s + e

def encrypt(message, A, b):
m_bin = bin(message)[2:].zfill(n) # 将消息转换为16比特的二进制字符串
m = sp.Matrix([int(bit) for bit in m_bin]) # 转换为SymPy矩阵
x = sp.Matrix(sp.randMatrix(n, n, min=0, max=q // (n * 4))) # 随机产生一个n*n的矩阵x
e1 = sp.Matrix(sp.randMatrix(n, 1, min=0, max=1)) # 随机产生一个n维噪声向量e
c1 = (x * A) % q # 密文部分c1 = x * A
c2 = (x * b + e1 + m * (q // 2)) % q # 密文部分c2 = x * b + e1 + m * q/2
return c1, c2

这里A,s,e都是随机的 那么 我们看看怎么解密的

1
2
3
4
5
6
7
# 解密函数
def decrypt(c1, c2, s):
m_dec = (c2 - c1 * s) % q
m_rec = m_dec.applyfunc(lambda x: round(2 * x / q) % 2) # 还原消息
m_bin = ''.join([str(bit) for bit in m_rec]) # 将SymPy矩阵转换为二进制字符串
m_rec_int = int(m_bin, 2) # 将二进制字符串转换为整数
return m_rec_int

由于e,e1都很小,如果我们有私钥s

那么 当然题目不会给我们私钥,我们只能得到公钥A,b

但是,A是可逆的。。

所以根本不用s

exp:

1
2
3
4
5
6
x = (c1 * A.inv_mod(q))%q
m_dec = (c2 - x*b) % q
m_rec = m_dec.applyfunc(lambda x: round(2 * x / q) % 2)
m_bin = ''.join([str(bit) for bit in m_rec]) # 将SymPy矩阵转换为二进制字符串
m_rec_int = int(m_bin, 2) # 将二进制字符串转换为整数
print(m_rec_int)

4.协同签名flag1

题目:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 加密函数
void encrypt(unsigned char* password, unsigned int key, unsigned char* ciphertext) {
unsigned char roundKeys[16 * ROUND] = {}; //

// 生成轮密钥
derive_round_key(key, roundKeys, 16 * ROUND);

// 初始状态为16字节的口令
unsigned char state[16]; // 初始状态为16字节的密码
memcpy(state, password, 16); // 初始状态为密码的初始值

// 迭代加密过程
for (int round = 0; round < ROUND; round++)
{
reverseBits(state);
sBoxTransform(state);
leftShiftBytes(state);
addRoundKey(state, roundKeys, round);
}

memcpy(ciphertext, state, 16);
}

先看加密函数,分为四步

reverseBits,sBoxTransform

leftShiftBytes,addRoundKey

看着是类似AES,不过不太一样

addRoundKey不用逆向了,然后看leftShiftBytes

1
2
3
4
5
6
7
8
9
10
11
12
13
void leftShiftBytes(unsigned char* state) {
unsigned char temp[16];
for (int i = 0; i < 16; i += 4) {
temp[i + 0] = state[i + 2] >> 5 | (state[i + 1] << 3);
temp[i + 1] = state[i + 3] >> 5 | (state[i + 2] << 3);
temp[i + 2] = state[i + 0] >> 5 | (state[i + 3] << 3);
temp[i + 3] = state[i + 1] >> 5 | (state[i + 0] << 3);
}
for (int i = 0; i < 16; i++)
{
state[i] = temp[i];
}
}

s0‘是s1的后5位+s2的前3位

s1’是s2后5位+s3前3位

s2‘是s3后5位+s0前3位

s3’是s0后5位+s1前3位

那么很显然拼起来就行了

s0就是s2‘后3位+s3’前5位

回复一个,其他轮换对称就行了

1
2
3
4
5
6
7
8
9
10
11
12
13
void RightShiftBytes(unsigned char* state) {
unsigned char temp[16];
for (int i = 0; i < 16; i += 4) {
temp[i + 0] = state[i + 2] << 5 | (state[i + 3] >> 3);
temp[i + 1] = state[i + 3] << 5 | (state[i + 0] >> 3);
temp[i + 2] = state[i + 0] << 5 | (state[i + 1] >> 3);
temp[i + 3] = state[i + 1] << 5 | (state[i + 2] >> 3);
}
for (int i = 0; i < 16; i++)
{
state[i] = temp[i];
}
}

然后再看sBoxTransform

1
2
3
4
5
6
7
void sBoxTransform(unsigned char* state) {
for (int i = 0; i < 16; i++) {
int lo = sBox[state[i] & 0xF];
int hi = sBox[state[i] >> 4];
state[i] = (hi << 4) | lo;
}
}

sBox可以理解成把一个16位的数映射到16位的数,那我们求出逆映射就行了 那么显然 只需令

1
2
3
for (int i = 0; i < 16; i++) {
sBoxInv[sBox[i]] = i;
}

就能造出inv盒子

然后照抄

1
2
3
4
5
6
7
8
9
10
11
void sBoxTransformInv(unsigned char* state) {
int sBoxInv[16];
for (int i = 0; i < 16; i++) {
sBoxInv[sBox[i]] = i;
}
for (int i = 0; i < 16; i++) {
int lo = sBoxInv[state[i] & 0xF];
int hi = sBoxInv[state[i] >> 4];
state[i] = (hi << 4) | lo;
}
}

然后reverseBit也不用逆,自己就是,

关键是不知道key,不过这是块密码,我们已知明文“pwd:”四个字节,刚好就是一个block,那么就可以用这个来求key

爆破,倒着爆破比较好,因为随机生成的密钥一般来说都比较大

exp:

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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <openssl/sha.h>
#include <chrono>
#define ROUND 16
#include <omp.h>
//S-Box 16x16
int sBox[16] =
{
2, 10, 4, 12,
1, 3, 9, 14,
7, 11, 8, 6,
5, 0, 15, 13
};


// 将十六进制字符串转换为 unsigned char 数组
void hex_to_bytes(const char* hex_str, unsigned char* bytes, size_t bytes_len) {
size_t hex_len = strlen(hex_str);
if (hex_len % 2 != 0 || hex_len / 2 > bytes_len) {
fprintf(stderr, "Invalid hex string length.\n");
return;
}

for (size_t i = 0; i < hex_len / 2; i++) {
sscanf(hex_str + 2 * i, "%2hhx", &bytes[i]);
}
}


// 派生轮密钥
void derive_round_key(unsigned int key, unsigned char* round_key, int length) {

unsigned int tmp = key;
for (int i = 0; i < length / 16; i++)
{
memcpy(round_key + i * 16, &tmp, 4); tmp++;
memcpy(round_key + i * 16 + 4, &tmp, 4); tmp++;
memcpy(round_key + i * 16 + 8, &tmp, 4); tmp++;
memcpy(round_key + i * 16 + 12, &tmp, 4); tmp++;
}
}


// 比特逆序
void reverseBits(unsigned char* state) {
unsigned char temp[16];
for (int i = 0; i < 16; i++) {
unsigned char byte = 0;
for (int j = 0; j < 8; j++) {
byte |= ((state[i] >> j) & 1) << (7 - j);
}
temp[15 - i] = byte;
}
for (int i = 0; i < 16; i++) {
state[i] = temp[i];
}
}


void sBoxTransform(unsigned char* state) {
for (int i = 0; i < 16; i++) {
int lo = sBox[state[i] & 0xF];
int hi = sBox[state[i] >> 4];
state[i] = (hi << 4) | lo;
}
}
void sBoxTransformInv(unsigned char* state) {
int sBoxInv[16];
for (int i = 0; i < 16; i++) {
sBoxInv[sBox[i]] = i;
}
for (int i = 0; i < 16; i++) {
int lo = sBoxInv[state[i] & 0xF];
int hi = sBoxInv[state[i] >> 4];
state[i] = (hi << 4) | lo;
}
}

void leftShiftBytes(unsigned char* state) {
unsigned char temp[16];
for (int i = 0; i < 16; i += 4) {
temp[i + 0] = state[i + 2] >> 5 | (state[i + 1] << 3);
temp[i + 1] = state[i + 3] >> 5 | (state[i + 2] << 3);
temp[i + 2] = state[i + 0] >> 5 | (state[i + 3] << 3);
temp[i + 3] = state[i + 1] >> 5 | (state[i + 0] << 3);
}
for (int i = 0; i < 16; i++)
{
state[i] = temp[i];
}
}
void RightShiftBytes(unsigned char* state) {
unsigned char temp[16];
for (int i = 0; i < 16; i += 4) {
temp[i + 0] = state[i + 2] << 5 | (state[i + 3] >> 3);
temp[i + 1] = state[i + 3] << 5 | (state[i + 0] >> 3);
temp[i + 2] = state[i + 0] << 5 | (state[i + 1] >> 3);
temp[i + 3] = state[i + 1] << 5 | (state[i + 2] >> 3);
}
for (int i = 0; i < 16; i++)
{
state[i] = temp[i];
}
}
void printState(unsigned char* state) {
for (int i = 0; i < 16; i++) {
printf("%02X ", state[i]);
}
printf("\n");
}
void VerifyShift(unsigned char* state)
{
printf("Verify:\n");
printState(state);
leftShiftBytes(state);
printState(state);
RightShiftBytes(state);
printState(state);
}
void VerifySbox(unsigned char* state)
{
printf("Verify:\n");
printState(state);
sBoxTransform(state);
printState(state);
sBoxTransformInv(state);
printState(state);
}
// 轮密钥加
void addRoundKey(unsigned char* state, unsigned char* roundKey, unsigned int round) {
for (int i = 0; i < 16; i++) {
for (int j = 0; j < 8; j++) {
state[i] ^= ((roundKey[i + round * 16] >> j) & 1) << j;
}
}
}

// 加密函数
void encrypt(unsigned char* password, unsigned int key, unsigned char* ciphertext) {
unsigned char roundKeys[16 * ROUND] = {}; //

// 生成轮密钥
derive_round_key(key, roundKeys, 16 * ROUND);

// 初始状态为16字节的口令
unsigned char state[16]; // 初始状态为16字节的密码
memcpy(state, password, 16); // 初始状态为密码的初始值

// 迭代加密过程
for (int round = 0; round < ROUND; round++)
{
reverseBits(state);
sBoxTransform(state);
leftShiftBytes(state);
addRoundKey(state, roundKeys, round);
}

memcpy(ciphertext, state, 16);
}
void decrypt(unsigned char* ciphertext, unsigned int key, unsigned char* decrypted) {
unsigned char roundKeys[16 * ROUND] = {}; //

// 生成轮密钥
derive_round_key(key, roundKeys, 16 * ROUND);

unsigned char state[16];
memcpy(state, ciphertext, 16);

for (int round = ROUND - 1; round >= 0; round--) {
addRoundKey(state, roundKeys, round);
RightShiftBytes(state);
sBoxTransformInv(state);
reverseBits(state);
}

memcpy(decrypted, state, 16);
}
void main() {
unsigned char ciphertext[16];
// 16字节的状态
hex_to_bytes("99F2980AAB4BE8640D8F322147CBA409", ciphertext, 16);
unsigned int key;
unsigned char decrypted[16];
int found = 0;
unsigned int foundkey = 0xFAB7C4D9;
auto start = std::chrono::high_resolution_clock::now();
//#pragma omp parallel for shared(found)
// for (key = 0xFFFFFFFF; key >0; key--) {
// if (found) continue; // 如果其他线程已经找到,跳过
//
// unsigned char decrypted[16];
// decrypt(ciphertext, key, decrypted);
//
// if (decrypted[0] == 'p' && decrypted[1] == 'w' && decrypted[2] == 'd' && decrypted[3] == ':') {
//#pragma omp critical
// {
// found = 1;
// foundkey = key;
// printf("Found key: %08X (Thread %d)\n", key, omp_get_thread_num());
// }
// }
// }

printf("Final key: %08X\n", foundkey);
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
decrypt(ciphertext, foundkey, decrypted);
printf("Decryption time: %ld microseconds\n", duration.count());
// 输出解密后的结果
printf("Decrypted password:\n");
printf("%s", decrypted);
}

5.flag2

先看客户端

​ // 客户端通过用户口令、消息摘要和用户私钥d1,计算客户端协同签名值 p1x, p1y, q1x, q1y, r1, s1

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
const submit = () => {
isform.value.validate((valid: boolean) => {
if (valid) {

loading.value = true;
let smPassword = ref("");
smPassword.value = sm3(form.value.password);
// 客户端通过用户口令、消息摘要和用户私钥d1,计算客户端协同签名值 p1x, p1y, q1x, q1y, r1, s1
var { str_e, str_p1x, str_p1y, str_q1x, str_q1y, str_r1, str_s1, errMessage } = clientSign1(smPassword.value, form.value.msgdigest);
if (errMessage) {
ElMessage.error(errMessage)
loading.value = false;
return
}
let data = {
q1x: str_q1x,
q1y: str_q1y,
e: str_e,
r1: str_r1,
s1: str_s1,
p1x: str_p1x,
p1y: str_p1y
}
// 客户端将 e, p1x, p1y, q1x, q1y, r1, s1发送给服务端
// 服务端用服务端私钥d2计算服务端协同签名值 s2, s3, r 发送给客户端
sign_param_send(data).then((res: any) => {
// 客户端通过s2, s3, r,计算协同签名值 s
let str_s: any = clientSign2(smPassword.value, res.s2, res.s3, res.r);
if (str_s.errMessage) {
ElMessage.error(errMessage)
loading.value = false;
return
}
ElMessage.success("协同签名成功");
signature_send({ client_sign: str_s }).then((res: any) => {
qmz.value = str_s;
loading.value = false;
}).then((err: any) => {
loading.value = false;
})
}).catch((err: any) => {
loading.value = false;
})
}
})
}

一堆东西扰乱视线了,只保留逻辑部分,报错信息什么的都删了

1
2
3
4
5
6
7
8
9
10
11
let smPassword = ref("");
smPassword.value = sm3(form.value.password);
// 客户端通过用户口令、消息摘要和用户私钥d1,计算客户端协同签名值 p1x, p1y, q1x, q1y, r1, s1
var { str_e, str_p1x, str_p1y, str_q1x, str_q1y, str_r1, str_s1, errMessage } = clientSign1(smPassword.value, form.value.msgdigest);
// 客户端将 e, p1x, p1y, q1x, q1y, r1, s1发送给服务端
// 服务端用服务端私钥d2计算服务端协同签名值 s2, s3, r 发送给客户端
sign_param_send(data).then((res: any) => {
// 客户端通过s2, s3, r,计算协同签名值 s
let str_s: any = clientSign2(smPassword.value, res.s2, res.s3, res.r);
ElMessage.success("协同签名成功");
signature_send({ client_sign: str_s })

用户要求提交

1
2
3
4
const form = ref({
password: "",
msgdigest: "",
})

password就是私钥d1,msgdigest就是e

然后看client1

椭圆曲线单独拿出来,这是标准的sm2参数

1
2
3
4
5
6
7
8
9
10
const sm2: any = new elliptic.curve.short({
p: 'FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF',
a: 'FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC',
b: '28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93',
n: 'FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123',
g: [
'32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7',
'BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0'
]
} as any);

然后也是删了一些没用的逻辑

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
const clientSign1: any = (str_d1: any, str_e: any) => {
let d1 = new BN(str_d1, 16);
let e = new BN(str_e, 16);

let n = new BN(sm2.n.toString(16), 16);
let G = sm2.g;

// generate random k1
const randomBytes = cryptoRandomStringAsync({ length: 64 });
k1.value = new BN(randomBytes as any, 16);
k1.value = k1.value.mod(n);

// d1 = d1 mod n
d1 = d1.mod(n);

//P1 = ((d1)^(-1)) * G
let tmp1 = d1.invm(n);
let P1 = G.mul(tmp1);

//Q1 = k1*G = (x, y)
let Q1 = G.mul(k1.value);
let x = new BN(Q1.getX().toString(16), 16);

//r1 = x mod n
let r1 = x.mod(n);

//s1 = k1^(-1) * (e + d1^(-1) * r1) mod n
tmp1 = d1.invm(n);
let tmp2 = tmp1.mul(r1).mod(n);
let tmp3 = tmp2.add(e).mod(n);
tmp1 = k1.value.invm(n);
let s1 = tmp1.mul(tmp3).mod(n);

str_e = e.toString(16);
return { str_e, str_p1x, str_p1y, str_q1x, str_q1y, str_r1, str_s1 }
}

随机生成一个

然后我们构造两个点 然后令

接着把这些数发给服务器,然后服务器用d2得到s2,s3,r

1
2
3
4
5
6
7
8
9
const clientSign2 = (str_d1: any, str_s2: any, str_s3: any, str_r: any) => {
//s = d1*k1*s2 + d1*s3 -r mod n
let tmp1 = d1.mul(k1.value).mod(n);
let tmp2 = tmp1.mul(s2).mod(n);
let tmp3 = d1.mul(s3).mod(n);
tmp1 = tmp2.add(tmp3).mod(n);
let s = tmp1.sub(r).mod(n);
return s;
}

然后用d1,s2,s3,r计算 然后我们来看服务端(删了没用的代码),其实只看注释就行了

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
// 协同签名服务端签名算法
Result server(char* str_e, char* str_p1x, char* str_p1y, char* str_q1x, char* str_q1y, char* str_r1, char* str_s1) {
group = EC_GROUP_new_curve_GFp(p, a, b, bn_ctx);
generator = EC_POINT_new(group);
x="32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7"
y="BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0"
generator=(x,y) //生成元,阶数为n
// d2 = ds (server key)
// generate random k2
k3 = k2

G = EC_POINT_new(group);
P = EC_POINT_new(group);
P1 = EC_POINT_new(group);
Q1 = EC_POINT_new(group);
TMP = EC_POINT_new(group);
// if r1=0 or s1=0, error

// set P1 = (p1x, p1y)

// set Q1 = (q1x, q1y)

//u1 = e * (s1^(-1)) mod n, u2 = r1 * (s1^(-1)) mod n

//u1*G + u2*P1 = (x', y')

//verify r1 = x' mod n
//k2*G + k3*Q1 = (x1, y1)
//r=(e+x1) mod n

//s2 = d2 * k3 mod n, s3 = d2 * (r+k2) mod n

// flag2 的格式如下:flag2{xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx},大括号中的内容为 16 进制格式(字母小写)的 d2。
print_flag2(d2);

rv = 0;
BN_CTX_free(bn_ctx);

return rv;
}

计算了这么几个量 计算 验证是否 为什么要验证这个呢,回顾一下 然后令

注意到 那其实x'就是r1

然后再计算 然后 把等式汇总一下,太多了 $$

$$ 把变量捋一下

e, p1x, p1y, q1x, q1y, r1, s1,s2,s3,r 这些都已知

d1,k1,k2=k3,d2不知道

为了求d2,就得求k3,也就是k2,注意到,所以我们直接有 那么 很简单,前面都没什么用

exp:

1
2
3
4
5
6
7
from Crypto.Util.number import *
s2=0xB9D5131ADE5BD8B11B1D68271184F633D0B236FD680746C3B79B584DEBC4DD8E
s3=0xD0DEF51E325FE11A780625A3C8E398060B7F7DBF0E4D91182B0AE6DDF02A3413
r=0xF7A7B5ABE1312F7A2799A27897D7EA57736FA5D00CF81287332D3727677E0EFA
n=0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123
d = inverse(r,n)*(s3-s2)%n
print(hex(d)[2:])