cryptopals crypto challenges(set 1)

练习地址:https://cryptopals.com

challenge1-Convert hex to base64

The string:

49276d206b696c6c696e6720796f757220627261696e206c696b65206120706f69736f6e6f7573206d757368726f6f6d

Should produce:

SSdtIGtpbGxpbmcgeW91ciBicmFpbiBsaWtlIGEgcG9pc29ub3VzIG11c2hyb29t

python3:

1
2
3
4
5
6
from Crypto.Util.number import long_to_bytes
s = '49276d206b696c6c696e6720796f757220627261696e206c696b65206120706f69736f6e6f7573206d757368726f6f6d'
l = int(s, 16)
import base64
res = base64.b64encode(long_to_bytes(l)).decode('utf-8')
print(res)

python2:

1
2
3
s = '49276d206b696c6c696e6720796f757220627261696e206c696b65206120706f69736f6e6f7573206d757368726f6f6d'
res = s.decode('hex').encode('base64')
print res

challenge2-Fixed XOR

Write a function that takes two equal-length buffers and produces their XOR combination.

If your function works properly, then when you feed it the string:

1c0111001f010100061a024b53535009181c

… after hex decoding, and when XOR’d against:

686974207468652062756c6c277320657965

… should produce:

746865206b696420646f6e277420706c6179

python3:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from Crypto.Util.number import long_to_bytes, bytes_to_long

a = '1c0111001f010100061a024b53535009181c'
b = '686974207468652062756c6c277320657965'
bytes_a = long_to_bytes(int(a, 16))
bytes_b = long_to_bytes(int(b, 16))

def xor(a, b):
res = bytearray(len(a))
for i in range(len(a)):
res[i] = a[i] ^ b[i]
return res

res = xor(bytes_a, bytes_b)
print(res) # bytearray(b"the kid don\'t play")

hex_res = hex(bytes_to_long(res))[2:]
print(hex_res) # 746865206b696420646f6e277420706c6179

python2:

1
2
3
4
5
6
7
8
9
10
11
12
def xor(b1, b2):
b = bytearray(len(b1))
for i in range(len(b1)):
b[i] = b1[i] ^ b2[i]
return b

b1 = bytearray.fromhex("1c0111001f010100061a024b53535009181c")
b2 = bytearray.fromhex("686974207468652062756c6c277320657965")

b = bytes(xor(b1, b2)) # the kid don’t play

b.encode("hex") # 746865206b696420646f6e277420706c6179

补充:

bytes类型是不可变序列,bytearray是可变的bytes数据类型;

bytes对象只负责以字节(二进制格式)序列来记录数据,以字节为单位进行操作. 例如字节序列b'\x01\x02',其中\x01为一个字节,\x表示十六进制,01表示十六进制数

有三种方式将字符串转换成bytes对象:

  • 如果字符串内容都是ascii字符,可直接在字符串前加b,例如b'abc'
  • 调用bytes()函数,例如bytes('abc', encoding='utf-8')
  • 调用字符串的encode()方法,例如'abc'.encode('utf-8')

bytearray(int) :创建一个int位的全为0的bytearray:

1
2
3
4
5
6
7
8
9
>>> a = bytearray(10)
>>> a
bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
>>> a[0] = 7
>>> a
bytearray(b'\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00')
>>> a[0] = 97
>>> a
bytearray(b'a\x00\x00\x00\x00\x00\x00\x00\x00\x00')

如果for循环作用于bytearray,那么得到的是bytearray字节的10进制ascii码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
>>> a
bytearray(b'a\xff\x00\x00\x00\x00\x00\x00\x00\x00')
>>> for i in a:
... print(i)
...
97
255
0
0
0
0
0
0
0
0

challenge3-Single-byte XOR cipher

The hex encoded string:

1b37373331363f78151b7f2b783431333d78397828372d363c78373e783a393b3736

… has been XOR’d against a single character. Find the key, decrypt the message.

You can do this by hand. But don’t: write code to do it for you.

How? Devise some method for “scoring” a piece of English plaintext. Character frequency is a good metric. Evaluate each output and choose the one with the best score.

要求不让爆破,使用统计分析频率的方法来找出正确的字符…首先写脚本爆破出正确字符,发现还好这道题的字符较短,不然正确的字符串确实不好找:

1
2
3
4
5
6
7
8
9
10
11
12
from Crypto.Util.number import long_to_bytes, bytes_to_long

s = '1b37373331363f78151b7f2b783431333d78397828372d363c78373e783a393b3736'

bytes_s = long_to_bytes(int(s, 16))

for i in range(256):
print(i, end=' ')
for j in bytes_s:
res = i ^ j
print(chr(res), end='')
print('')

得到结果:

字符串短的话可以用上面的方法跑出结果,但是一旦有个很长的字符串,那么要找出正确的结果就比较难了…

在样本比较大时,英文字母中每个字母出现的频率都是不同的,当统计范围足够大时,每个字母或单字出现的频率是比较稳定的;因此,可以字符的频率为指标对异或的结果进行评分,分数最高的便是最优解。详见百度百科

python3:

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
from Crypto.Util.number import long_to_bytes


def cal_score(s):
freq = {
'a': 0.0651738, 'b': 0.0124248, 'c': 0.0217339, 'd': 0.0349835, 'e': 0.1041442, 'f': 0.0197881, 'g': 0.0158610,
'h': 0.0492888, 'i': 0.0558094, 'j': 0.0009033, 'k': 0.0050529, 'l': 0.0331490, 'm': 0.0202124, 'n': 0.0564513,
'o': 0.0596302, 'p': 0.0137645, 'q': 0.0008606, 'r': 0.0497563, 's': 0.0515760, 't': 0.0729357, 'u': 0.0225134,
'v': 0.0082903, 'w': 0.0171272, 'x': 0.0013692, 'y': 0.0145984, 'z': 0.0007836, ' ': 0.1918182
}

score = 0
for i in s.lower():
if chr(i) in freq:
score += freq[chr(i)]
return score


def cal_plain(n, length):
max_score = 0 # 记录最高score
key = None # 记录单字节密文

for i in range(256):
res = bytearray(length) # 定义空可变字节串,记录异或的结果
index = 0

for j in n:
res[index] = i ^ j
index += 1

pscore = cal_score(res) # 计算得到的字节串的score

# 比较score,得到的最高的score的字节串即为正确的明文
if pscore <= max_score:
pass
else:
max_score = pscore
plain = res
key = chr(i)
print(plain, key)


cipher = '1b37373331363f78151b7f2b783431333d78397828372d363c78373e783a393b3736'
n = long_to_bytes(int(cipher, 16))
cal_plain(n, len(n))

challenge4-Detect single-character XOR

One of the 60-character strings in this file has been encrypted by single-character XOR.

Find it.

(Your code from #3 should help.)

用第三道题的方法来做,需要注意的是要处理这些密文中的换行符(不仔细看还真看不出来),将这些密文进行分别计算max_score,再比较这些max_score中最大的

python3:

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
from Crypto.Util.number import long_to_bytes

max_score = 0 # 记录最高score
key = None # 记录单字节密文
plain = None # 记录明文

def cal_score(s):
freq = {
'a': 0.0651738, 'b': 0.0124248, 'c': 0.0217339, 'd': 0.0349835, 'e': 0.1041442, 'f': 0.0197881, 'g': 0.0158610,
'h': 0.0492888, 'i': 0.0558094, 'j': 0.0009033, 'k': 0.0050529, 'l': 0.0331490, 'm': 0.0202124, 'n': 0.0564513,
'o': 0.0596302, 'p': 0.0137645, 'q': 0.0008606, 'r': 0.0497563, 's': 0.0515760, 't': 0.0729357, 'u': 0.0225134,
'v': 0.0082903, 'w': 0.0171272, 'x': 0.0013692, 'y': 0.0145984, 'z': 0.0007836, ' ': 0.1918182
}

score = 0
for i in s.lower():
if chr(i) in freq:
score += freq[chr(i)]
return score


def cal_plain(n, length):
global max_score, key, plain

for i in range(256):
res = bytearray(length) # 定义空可变字节串,记录异或的结果
index = 0

for j in n:
res[index] = i ^ j
index += 1

pscore = cal_score(res) # 计算得到的字节串的score

# 比较score,得到的最高的score的字节串即为正确的明文
if pscore <= max_score:
pass
else:
max_score = pscore
plain = res
key = chr(i)


cipher = open('4.txt', 'r').read().replace('\n', ' ')
cipher = cipher.split(' ')
for c in cipher:
n = long_to_bytes(int(c, 16))
cal_plain(n, len(n))

print(key)
print(plain)

challenge5-Implement repeating-key XOR

Here is the opening stanza of an important work of the English language:

Burning 'em, if you ain't quick and nimble
I go crazy when I hear a cymbal

Encrypt it, under the key “ICE”, using repeating-key XOR.

In repeating-key XOR, you’ll sequentially apply each byte of the key; the first byte of plaintext will be XOR’d against I, the next C, the next E, then I again for the 4th byte, and so on.

It should come out to:

0b3637272a2b2e63622c2e69692a23693a2a3c6324202d623d63343c2a26226324272765272
a282b2f20430a652e2c652a3124333a653e2b2027630c692b20283165286326302e27282f

意思就是将一句英文用ICE进行异或,英文第一个字节用I进行异或,第二个字节用C进行异或,以此类推。可以使用python中的迭代器模块-itertools,使用其中的cycle()方法,结合next()函数可以很轻松的实现该运算

1
2
3
4
5
6
7
8
9
10
11
from Crypto.Util.number import bytes_to_long, long_to_bytes
from itertools import cycle

s = '''Burning 'em, if you ain't quick and nimble
I go crazy when I hear a cymbal'''

key = cycle('ICE')

for i in bytes(s, encoding='utf-8'):
res = i ^ ord(next(key))
print(hex(res)[2:], end='')

challenge6-Break repeating-key XOR

There’s a file here. It’s been base64’d after being encrypted with repeating-key XOR.

Decrypt it.

Here’s how(英文实在看着费劲,直接用下别人翻译好的-_-):

1.首先猜测密钥长度,记为keysize,建议猜测范围为2到40。

2.写一个计算两个字符串的Hamming distance的函数。Hamming distance是两个字符串不同比特的数量。例如,this is a testwokka wokka!!!之间的Hamming distance为37。

3.对于每一个keysize,获得密文文本的第一个长度为keysize的块以及第二个长度为keysize的块,计算这两个块的Hamming distance并除以keysize得到结果记为nomal_distance。

4.有着最小的nomal_distance的keysize很可能就是密钥的长度。可以选择最小的三个nomal_distance所对应的keysize进行接下来的操作。也可以在第3步中使用4个keysize大小的块来计算得到nomal_distance。

5.将密文分为长度为keysize的块。

6.将每个块中的第一个字节组合成一个新的块,每个块中的第二个字节组合成一个新的块,依次类推。

7.使用single-character XOR来处理每个新块。即第一个新块中的每个字节均和密钥的第一个字节异或,第二个新块中的每个字节均和密钥的第二个字节异或,依次类推。由此可得到每个keysize所对应的候选密钥key。

8.分别将每个候选密钥key与密文进行xor_repeat_key操作得到明文文本。计算各个明文文本的评分分数,分数最高的即为我们需要得到的明文文本。

Hamming distance

参考文章所说,当秘钥长度较短时我们可用枚举的方法猜解出秘钥长度,但随着秘钥长度的增加,枚举法是一种效率很低的方法;我们可以使用Hamming distance来猜解秘钥长度

汉明距离其实就是两个等长字符串的二进制比特位的差异,例如:

hamming('1110', '1111') == 1

有一种快速求汉明距离的方法就是将两个等长字符串的字符的二进制进行异或,计算出比特位为1的个数即为汉明距离:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from Crypto.Util.strxor import strxor

a = 'this is a test'
b = 'wokka wokka!!!'

a = a.encode('utf-8')
b = b.encode('utf-8')

sum = 0
res = strxor(a, b)
for i in res:
res = bin(i)
sum += res[2:].count('1')
print(sum)

为什么可以用汉明距离猜解出秘钥长度呢?文章所述:

两个以ascii编码的英文字符的汉明距离是2-3之间,也就是说正常英文字母的平均汉明距离为2-3(每比特),任意字符(非纯字母)的两两汉明距离平均为4。另外我们也容易知道,正确分组的密文与密文的汉明距离等于明文与明文的汉明距离(可以通过按正确密钥长度分组的密文与密文异或等于明文与明文异或证明)。这样,我们可以知道,当我们使用了正确的密钥长度后,两两字母进行计算汉明距离,那么这个值应该是趋于最小。为了增加效率,我们不需要对每一对分组都计算汉明距离,只需取出前几对就可说明问题。当然为了排除偶然误差,结果不应该只取最小的那一个密钥长度,而是酌情多取几组
那么接下来计算nomal_distance:
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
from Crypto.Util.strxor import strxor
import base64


def hamming_distance(a, b):
sum = 0
res = strxor(a, b)
for i in res:
res = bin(i)
sum += res[2:].count('1')

return sum


def normal_distance(cipher, res):
for key_size in range(2, 40):
# 对前面的密文分成4组
c1 = cipher[:key_size]
c2 = cipher[key_size:key_size*2]
c3 = cipher[key_size*2:key_size*3]
c4 = cipher[key_size*3:key_size*4]

# 计算normal_distance
normal = (hamming_distance(c1, c2)+hamming_distance(c2, c3)+hamming_distance(c3, c4))/(key_size*3)
res.append((normal, key_size))


res = [] # 保存key_size及其对应的normal_distance
cipher = open('6.txt', 'r').read()
cipher = base64.b64decode(cipher)
normal_distance(cipher, res)
print(sorted(res)) # 按照列表中元祖的第一个元素从小到大排序(即normal_distance)

得到的前几组结果为:

[(2.0, 2), (2.6666666666666665, 3), (2.793103448275862, 29), (2.8, 5), (2.9444444444444446, 18)......

可推测,正确的秘钥长度可能是2,3,29,5中的一个;剩下的便根据题目的做法写脚本即可,大佬写的脚本如下:

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
import base64
import string

def bxor(a, b): # xor two byte strings of different lengths
if len(a) > len(b):
return bytes([x ^ y for x, y in zip(a[:len(b)], b)])
else:
return bytes([x ^ y for x, y in zip(a, b[:len(a)])])


def hamming_distance(b1, b2):
differing_bits = 0
for byte in bxor(b1, b2):
differing_bits += bin(byte).count("1")
return differing_bits


def break_single_key_xor(text):
key = 0
possible_space=0
max_possible=0
letters = string.ascii_letters.encode('ascii')
for a in range(0, len(text)):
maxpossible = 0
for b in range(0, len(text)):
if(a == b):
continue
c = text[a] ^ text[b]
if c not in letters and c != 0:
continue
maxpossible += 1
if maxpossible>max_possible:
max_possible=maxpossible
possible_space=a
key = text[possible_space]^ 0x20
return chr(key)


text = ''
with open("6.txt","r") as f:
for line in f:
text += line
b = base64.b64decode(text)


normalized_distances = []


for KEYSIZE in range(2, 40):
#我们取其中前6段计算平局汉明距离
b1 = b[: KEYSIZE]
b2 = b[KEYSIZE: KEYSIZE * 2]
b3 = b[KEYSIZE * 2: KEYSIZE * 3]
b4 = b[KEYSIZE * 3: KEYSIZE * 4]
b5 = b[KEYSIZE * 4: KEYSIZE * 5]
b6 = b[KEYSIZE * 5: KEYSIZE * 6]

normalized_distance = float(
hamming_distance(b1, b2) +
hamming_distance(b2, b3) +
hamming_distance(b3, b4) +
hamming_distance(b4, b5) +
hamming_distance(b5, b6)
) / (KEYSIZE * 5)
normalized_distances.append(
(KEYSIZE, normalized_distance)
)
normalized_distances = sorted(normalized_distances,key=lambda x:x[1])


for KEYSIZE,_ in normalized_distances[:5]:
block_bytes = [[] for _ in range(KEYSIZE)]
for i, byte in enumerate(b):
block_bytes[i % KEYSIZE].append(byte)
keys = ''
try:
for bbytes in block_bytes:
keys += break_single_key_xor(bbytes)
key = bytearray(keys * len(b), "utf-8")
plaintext = bxor(b, key)
print("keysize:", KEYSIZE)
print("key is:", keys, "n")
s = bytes.decode(plaintext)
print(s)
except Exception:
continue

De1CTF xorz

题目:

1
2
3
4
5
6
7
8
9
10
11
12
from itertools import *
from data import flag,plain

key=flag.strip("de1ctf{").strip("}")
assert(len(key)<38)
salt="WeAreDe1taTeam"
ki=cycle(key)
si=cycle(salt)
cipher = ''.join([hex(ord(p) ^ ord(next(ki)) ^ ord(next(si)))[2:].zfill(2) for p in plain])
print cipher
# output:
# 49380d773440222d1b421b3060380c3f403c3844791b202651306721135b6229294a3c3222357e766b2f15561b35305e3c3b670e49382c295c6c170553577d3a2b791470406318315d753f03637f2b614a4f2e1c4f21027e227a4122757b446037786a7b0e37635024246d60136f7802543e4d36265c3e035a725c6322700d626b345d1d6464283a016f35714d434124281b607d315f66212d671428026a4f4f79657e34153f3467097e4e135f187a21767f02125b375563517a3742597b6c394e78742c4a725069606576777c314429264f6e330d7530453f22537f5e3034560d22146831456b1b72725f30676d0d5c71617d48753e26667e2f7a334c731c22630a242c7140457a42324629064441036c7e646208630e745531436b7c51743a36674c4f352a5575407b767a5c747176016c0676386e403a2b42356a727a04662b4446375f36265f3f124b724c6e346544706277641025063420016629225b43432428036f29341a2338627c47650b264c477c653a67043e6766152a485c7f33617264780656537e5468143f305f4537722352303c3d4379043d69797e6f3922527b24536e310d653d4c33696c635474637d0326516f745e610d773340306621105a7361654e3e392970687c2e335f3015677d4b3a724a4659767c2f5b7c16055a126820306c14315d6b59224a27311f747f336f4d5974321a22507b22705a226c6d446a37375761423a2b5c29247163046d7e47032244377508300751727126326f117f7a38670c2b23203d4f27046a5c5e1532601126292f577776606f0c6d0126474b2a73737a41316362146e581d7c1228717664091c

1.根据得到的cipher,可以知道plain的长度,即len(cipher)/2,为600

2.根据列表解析式:

cipher = ''.join([hex(ord(p) ^ ord(next(ki)) ^ ord(next(si)))[2:].zfill(2) for p in plain])

且由异或算式:

如果:

A ^ B ^ C = Z

那么:

A ^ B = C ^ Z
...
A ^ B ^ Z = C
...

那么已知密文和si,可以推出plain和key异或的结果:

de.py:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from Crypto.Util.number import long_to_bytes
from itertools import cycle
import base64


cipher = '49380d773440222d1b421b3060380c3f403c3844791b202651306721135b6229294a3c3222357e766b2f15561b35305e3c3b670e49382c295c6c170553577d3a2b791470406318315d753f03637f2b614a4f2e1c4f21027e227a4122757b446037786a7b0e37635024246d60136f7802543e4d36265c3e035a725c6322700d626b345d1d6464283a016f35714d434124281b607d315f66212d671428026a4f4f79657e34153f3467097e4e135f187a21767f02125b375563517a3742597b6c394e78742c4a725069606576777c314429264f6e330d7530453f22537f5e3034560d22146831456b1b72725f30676d0d5c71617d48753e26667e2f7a334c731c22630a242c7140457a42324629064441036c7e646208630e745531436b7c51743a36674c4f352a5575407b767a5c747176016c0676386e403a2b42356a727a04662b4446375f36265f3f124b724c6e346544706277641025063420016629225b43432428036f29341a2338627c47650b264c477c653a67043e6766152a485c7f33617264780656537e5468143f305f4537722352303c3d4379043d69797e6f3922527b24536e310d653d4c33696c635474637d0326516f745e610d773340306621105a7361654e3e392970687c2e335f3015677d4b3a724a4659767c2f5b7c16055a126820306c14315d6b59224a27311f747f336f4d5974321a22507b22705a226c6d446a37375761423a2b5c29247163046d7e47032244377508300751727126326f117f7a38670c2b23203d4f27046a5c5e1532601126292f577776606f0c6d0126474b2a73737a41316362146e581d7c1228717664091c'

salt="WeAreDe1taTeam"
si=cycle(salt)
c = long_to_bytes(int(cipher, 16))
res = '' # 记录key与plain异或的结果
for i in range(600):
res += chr(c[i] ^ ord(next(si)))

res = base64.b64encode(bytes(res, encoding='utf-8'))

由于是repeating-key XOR类型,则可以直接使用challenge6的方法,先根据汉明距离算出key的长度,再根据频率分析得出正确的key。直接改用上面的脚本即可:

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
import base64
import string
from de import *


def bxor(a, b): # xor two byte strings of different lengths
if len(a) > len(b):
return bytes([x ^ y for x, y in zip(a[:len(b)], b)])
else:
return bytes([x ^ y for x, y in zip(a, b[:len(a)])])


def hamming_distance(b1, b2):
differing_bits = 0
for byte in bxor(b1, b2):
differing_bits += bin(byte).count("1")
return differing_bits


def break_single_key_xor(text):
key = 0
possible_space=0
max_possible=0
letters = string.ascii_letters.encode('ascii')
for a in range(0, len(text)):
maxpossible = 0
for b in range(0, len(text)):
if(a == b):
continue
c = text[a] ^ text[b]
if c not in letters and c != 0:
continue
maxpossible += 1
if maxpossible>max_possible:
max_possible=maxpossible
possible_space=a
key = text[possible_space]^ 0x20
return chr(key)


text = res

b = base64.b64decode(text)


normalized_distances = []


for KEYSIZE in range(2, 40):
#我们取其中前6段计算平局汉明距离
b1 = b[: KEYSIZE]
b2 = b[KEYSIZE: KEYSIZE * 2]
b3 = b[KEYSIZE * 2: KEYSIZE * 3]
b4 = b[KEYSIZE * 3: KEYSIZE * 4]
b5 = b[KEYSIZE * 4: KEYSIZE * 5]
b6 = b[KEYSIZE * 5: KEYSIZE * 6]

normalized_distance = float(
hamming_distance(b1, b2) +
hamming_distance(b2, b3) +
hamming_distance(b3, b4) +
hamming_distance(b4, b5) +
hamming_distance(b5, b6)
) / (KEYSIZE * 5)
normalized_distances.append(
(KEYSIZE, normalized_distance)
)
normalized_distances = sorted(normalized_distances,key=lambda x:x[1])


for KEYSIZE,_ in normalized_distances[:5]:
block_bytes = [[] for _ in range(KEYSIZE)]
for i, byte in enumerate(b):
block_bytes[i % KEYSIZE].append(byte)
keys = ''
try:
for bbytes in block_bytes:
keys += break_single_key_xor(bbytes)
key = bytearray(keys * len(b), "utf-8")
plaintext = bxor(b, key)
print("keysize:", KEYSIZE)
print("key is:", keys, "n")
s = bytes.decode(plaintext)
print(s)
except Exception:
continue

challenge7-AES in ECB mode

The Base64-encoded content in this file has been encrypted via AES-128 in ECB mode under the key

"YELLOW SUBMARINE".

Decrypt it. You know the key, after all.

知道秘钥了,使用python的Crypto库即可实现解密:

1
2
3
4
5
6
7
8
9
from Crypto.Cipher import AES
import base64

cipher = open('7.txt', 'r').read().replace('\n', '')
cipher = base64.b64decode(cipher)
secret = "YELLOW SUBMARINE"
s = AES.new(secret, AES.MODE_ECB)
res = s.decrypt(cipher)
print(res)

challenge8-Detect AES in ECB mode

In this file are a bunch of hex-encoded ciphertexts.

One of them has been encrypted with ECB.

Detect it.

Remember that the problem with ECB is that it is stateless and deterministic; the same 16 byte plaintext block will always produce the same 16 byte ciphertext.

电码本模式(Electronic Codebook Book,ECB):将整个明文分成若干段相同长度的段,然后对每一段进行加密;缺点是相同的明文段会被加密成相同的密文块,这为攻击者提供了有关明文模式的信息

那么找出一个密文中,具有许多重复相同块(根据提示为16byte)的密文,即很可能是经ECB模式加密的字符串:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from Crypto.Util.number import long_to_bytes, bytes_to_long


def ecb_detect(c):
dic = []
flag = 0
for i in range(0, len(c), 16):
s = c[i:i+16]
if s not in dic:
dic.append(s)
else:
flag = 1
if flag:
print(hex(bytes_to_long(c))[2:])


cipher_list = open('8.txt', 'r').read().split('\n')
for i in cipher_list:
if i == '':
pass
else:
c = long_to_bytes(int(i, 16))
ecb_detect(c)

密码学真让人头秃!