cryptopals crypto challenges(set 2)

continue.

challenge9-Implement PKCS#7 padding

分组密码将固定大小的块(通常为8或16字节)的明文转换为密文。但我们加密大小不规则的消息,几乎从不想改变一个块;

我们考虑不规则大小的消息的一种方法是通过填充,创建一个偶数倍块大小的明文。最流行的填充方案称为PKCS#7。

So: pad any block to a specific block length, by appending the number of bytes of padding to the end of the block. For instance,

"YELLOW SUBMARINE"

… padded to 20 bytes would be:

"YELLOW SUBMARINE\x04\x04\x04\x04"

PKCS#7是在最后一个块中填充,填充长度为使文本长度能够达到块长度整数倍所增加的字节长度,填充内容为填充长度的字节重复

此题在填充前字符长度为16,填充到20,那么需要填充的长度为4,填充的内容为\x04

1
2
3
4
5
6
7
8
9
10
def padding(s, length):
psize = length - len(s) # 填充长度
pad = [r'\x' + hex(psize)[2:].zfill(2)] * psize # 填充内容
return s + ''.join(pad)


s = "YELLOW SUBMARINE"
length = 20
padding_string = padding(s, length)
print(padding_string)

challenge10-Implement CBC mode

CBC mode is a block cipher mode that allows us to encrypt irregularly-sized messages, despite the fact that a block cipher natively only transforms individual blocks.

In CBC mode, each ciphertext block is added to the next plaintext block before the next call to the cipher core.

The first plaintext block, which has no associated previous ciphertext block, is added to a “fake 0th ciphertext block” called the initialization vector, or IV.

Implement CBC mode by hand by taking the ECB function you wrote earlier, making it encrypt instead of decrypt (verify this by decrypting whatever you encrypt to test), and using your XOR function from the previous exercise to combine them.

The file here is intelligible (somewhat) when CBC decrypted against “YELLOW SUBMARINE” with an IV of all ASCII 0 (\x00\x00\x00 &c)

CBC模式加解密如下图所示:

整个加密的过程简单说来就是:

1.首先将明文分组(常见的以16字节为一组),位数不足的使用字符填充
2.生成一个随机的初始化向量(IV)和一个密钥
3.将IV和第一组明文异或
4.用密钥对3中xor后产生的密文加密
5.用4中产生的密文对第二组明文进行xor操作
6.用密钥对5中产生的密文加密
7.重复4-7,到最后一组明文
8.将IV和加密后的密文拼接在一起,得到最终的密文

解密过程:

1.从密文中提取出IV,然后将密文分组
2.使用密钥对第一组的密文解密,然后和IV进行xor得到明文
3.使用密钥对第二组密文解密,然后和2中的密文xor得到明文
4.重复2-3,直到最后一组密文

该模式加密和解密有个相似的地方,就是都使用前一块的密文来产生或解密后一块密文

根据题目提示的向量iv为16个0x00,编写解密脚本:

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
from Crypto.Cipher import AES
import base64

def padding(s, length):
psize = length - len(s) # 填充长度
pad = [r'\x' + hex(psize)[2:].zfill(2)] * psize # 填充内容
return s + bytes(''.join(pad), encoding='utf-8')


# 调用AES的ECB模式(AES默认的模式)进行解密
def ecb_decrypt(secret, cipher):
s = AES.new(secret, AES.MODE_ECB)
res = s.decrypt(cipher)
return res


def cbc_decrypt(cipher, key, iv):
for i in range(len(cipher)):
plain = bytearray(16) # 存储明文信息
block = cipher[i*16:i*16+16] # 分组
if block == b'':
pass
else:
block = padding(block, 16) # 填充
c = ecb_decrypt(key, block) # AES解密

for j in range(16):
plain[j] = c[j] ^ iv[j]

print(plain.decode('utf-8'), end='')

# 将填充后的密文作为下次异或的iv向量
iv = block


cipher = open('10.txt', 'r').read().replace('\n', '')
cipher = base64.b64decode(cipher)

key = "YELLOW SUBMARINE"
iv = bytearray(16)

print(cbc_decrypt(cipher, key, iv))