September 12, 2022

AES & PRGs构建块密码

给现代密码学讲课准备的文档……

The AES Block Cipher

概述

AES 密钥长度(bit) 分组长度(bit) 加密轮数
AES-128 128 128 10
AES-192 192 128 12
AES-256 256 128 14

以下均以AES-128说明

16字节 -> 4*4矩阵

1.轮密钥加
2.AES encrypt
3.重复2若干轮
4.Last AES encrypt

详述

轮密钥加(AddRoundKey)

明文与轮密钥异或

AES encrypt

1
2
3
4
SubBytes(state);            //字节替换
ShiftRows(state); //行移位
MixColumns(state); //列混合
AddRoundKey(state,&key[4]); //轮密钥加
字节替换

AES定义了一个S盒和一个逆S盒(16*16的表)。
字节替换是将矩阵中每个字符在S盒中的映射进行替换,即一个简单的查表操作。

映射方法:把该字节的高4位作为行值,低4位作为列值,取出S盒中对应的行的元素作为输出。
解密时从逆S盒取出元素。

state = aes_sbox[state >> 4][state & 0x0F];

行移位

第i行循环左移i字节。

列混合

每一列乘以一个固定的矩阵,得到新的一列密文

其中,矩阵元素的乘法和加法都是定义在基于上的二元运算,并不是通常意义上的乘法和加法。

矩阵中的第j列(0≤j≤3)的列混合可以表示为下图所示:

逆运算只需要乘原矩阵的逆矩阵即可。

轮密钥加

与轮密钥异或

Last AES encrypt

进行字节替换、行移位、轮密钥加(不必进行列混合)

重复若干轮

1
2
3
4
5
6
7
8
9
//aes_encrypt()
{
AddRoundKey(state,&key[0]);

for(int i = 0; i < 9; i++)
SubBytes(state); ShiftRows(state); MixColumns(state); AddRoundKey(state,&key[4 * i + 4]);

SubBytes(state); ShiftRows(state); AddRoundKey(state,&key[40]);
}

应用

代码量 or 速度?

总而言之,AES总能在代码量和性能之间找到一个平衡点。

硬件支持

AES指令集

使用128bit的寄存器(对应AES加密块的位数) -> 14倍加速

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/*
xmm1 = state //result
xmm2 = round key
*/
m = _mm_xor_si128 (m, k[0]);
m = _mm_aesenc_si128 (m, k[1]);
m = _mm_aesenc_si128 (m, k[2]);
m = _mm_aesenc_si128 (m, k[3]);
m = _mm_aesenc_si128 (m, k[4]);
m = _mm_aesenc_si128 (m, k[5]);
m = _mm_aesenc_si128 (m, k[6]);
m = _mm_aesenc_si128 (m, k[7]);
m = _mm_aesenc_si128 (m, k[8]);
m = _mm_aesenc_si128 (m, k[9]);
m = _mm_aesenclast_si128(m, k[10]);

安全性

还原密钥

AES的密钥只有126位,对这126bit进行穷举。
显然这不会伤害AES的安全性。

对AES-256的相关密钥攻击

AES-256的密钥扩展设计上有弱点,针对此可以进行相关密钥攻击。
如果有大约个AES的输入输出对,但有4个相关联的密钥(指相异位数很短,比如密钥之间只相差1bit)得出时,可以在时间内恢复出密钥。
显然这仍不足以伤害AES的安全性。

Block Ciphers From PRGs

我们能否从一个PRG构建一个PRF?

构建PRF

PRG:伪随机数发生器(输出与真随机序列不可区分)
PRF:伪随机函数

是一个安全的PRG
定义


输入为1bit的值,输出为0或1任一个值。
输入为0时,用左边的输出(),输入为1用右边的输出。

如果G是一个安全的PRG,那么F是一个安全的PRF。
显然这是一句废话

扩展PRG


得到一个 2bit 的 PRF:

如何证明是一个安全的PRG?
如何证明这个四元组输出与随机序列不可区分?

  1. 我们知道发生器G是安全的,因此第一层的输出与随机序列不可区分。换句话说,我们可以用真随机字符串来替换第一层。因此,我们从密钥空间中随机选取来替代容易知道这两个分布是安全的,即没有有效的攻击可以区分这两个分布。如果存在这样的攻击,就说明G是不安全的,那么与PRG的定义矛盾。
  2. 现在我们要对左边做同样的事情。换句话说,继续用真随机替换左下角两个伪随机输出。这跟第1步一样,因为G是安全的,所以我们可以进行这样的替换。
  3. 最后再对右边进行一次真随机的替换,我们就得到了一个真随机的四元组输出。

再次扩展


可以扩展3次,就可以扩展n次。

GGM PRF

GGM是这个PRF的3位发明者的首字母缩写


:

容易证明:G是一个安全的PRG F在上是一个安全的PRF。

不过此函数在实际应用中并不多,因为它的速度太慢了。产生 bit输出需要运行发生器n次。

能否从一个安全的PRG构建一个安全的分组密码 or 能否从一个安全的PRG构建一个安全的PRP?
PRP:伪随机置换

PRG PRF Luby-Rackoff(3轮Feistel) PRP

引用到的数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
static const BYTE aes_sbox[16][16] = {
{0x63,0x7C,0x77,0x7B,0xF2,0x6B,0x6F,0xC5,0x30,0x01,0x67,0x2B,0xFE,0xD7,0xAB,0x76},
{0xCA,0x82,0xC9,0x7D,0xFA,0x59,0x47,0xF0,0xAD,0xD4,0xA2,0xAF,0x9C,0xA4,0x72,0xC0},
{0xB7,0xFD,0x93,0x26,0x36,0x3F,0xF7,0xCC,0x34,0xA5,0xE5,0xF1,0x71,0xD8,0x31,0x15},
{0x04,0xC7,0x23,0xC3,0x18,0x96,0x05,0x9A,0x07,0x12,0x80,0xE2,0xEB,0x27,0xB2,0x75},
{0x09,0x83,0x2C,0x1A,0x1B,0x6E,0x5A,0xA0,0x52,0x3B,0xD6,0xB3,0x29,0xE3,0x2F,0x84},
{0x53,0xD1,0x00,0xED,0x20,0xFC,0xB1,0x5B,0x6A,0xCB,0xBE,0x39,0x4A,0x4C,0x58,0xCF},
{0xD0,0xEF,0xAA,0xFB,0x43,0x4D,0x33,0x85,0x45,0xF9,0x02,0x7F,0x50,0x3C,0x9F,0xA8},
{0x51,0xA3,0x40,0x8F,0x92,0x9D,0x38,0xF5,0xBC,0xB6,0xDA,0x21,0x10,0xFF,0xF3,0xD2},
{0xCD,0x0C,0x13,0xEC,0x5F,0x97,0x44,0x17,0xC4,0xA7,0x7E,0x3D,0x64,0x5D,0x19,0x73},
{0x60,0x81,0x4F,0xDC,0x22,0x2A,0x90,0x88,0x46,0xEE,0xB8,0x14,0xDE,0x5E,0x0B,0xDB},
{0xE0,0x32,0x3A,0x0A,0x49,0x06,0x24,0x5C,0xC2,0xD3,0xAC,0x62,0x91,0x95,0xE4,0x79},
{0xE7,0xC8,0x37,0x6D,0x8D,0xD5,0x4E,0xA9,0x6C,0x56,0xF4,0xEA,0x65,0x7A,0xAE,0x08},
{0xBA,0x78,0x25,0x2E,0x1C,0xA6,0xB4,0xC6,0xE8,0xDD,0x74,0x1F,0x4B,0xBD,0x8B,0x8A},
{0x70,0x3E,0xB5,0x66,0x48,0x03,0xF6,0x0E,0x61,0x35,0x57,0xB9,0x86,0xC1,0x1D,0x9E},
{0xE1,0xF8,0x98,0x11,0x69,0xD9,0x8E,0x94,0x9B,0x1E,0x87,0xE9,0xCE,0x55,0x28,0xDF},
{0x8C,0xA1,0x89,0x0D,0xBF,0xE6,0x42,0x68,0x41,0x99,0x2D,0x0F,0xB0,0x54,0xBB,0x16}
};

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
static const BYTE aes_invsbox[16][16] = {
{0x52,0x09,0x6A,0xD5,0x30,0x36,0xA5,0x38,0xBF,0x40,0xA3,0x9E,0x81,0xF3,0xD7,0xFB},
{0x7C,0xE3,0x39,0x82,0x9B,0x2F,0xFF,0x87,0x34,0x8E,0x43,0x44,0xC4,0xDE,0xE9,0xCB},
{0x54,0x7B,0x94,0x32,0xA6,0xC2,0x23,0x3D,0xEE,0x4C,0x95,0x0B,0x42,0xFA,0xC3,0x4E},
{0x08,0x2E,0xA1,0x66,0x28,0xD9,0x24,0xB2,0x76,0x5B,0xA2,0x49,0x6D,0x8B,0xD1,0x25},
{0x72,0xF8,0xF6,0x64,0x86,0x68,0x98,0x16,0xD4,0xA4,0x5C,0xCC,0x5D,0x65,0xB6,0x92},
{0x6C,0x70,0x48,0x50,0xFD,0xED,0xB9,0xDA,0x5E,0x15,0x46,0x57,0xA7,0x8D,0x9D,0x84},
{0x90,0xD8,0xAB,0x00,0x8C,0xBC,0xD3,0x0A,0xF7,0xE4,0x58,0x05,0xB8,0xB3,0x45,0x06},
{0xD0,0x2C,0x1E,0x8F,0xCA,0x3F,0x0F,0x02,0xC1,0xAF,0xBD,0x03,0x01,0x13,0x8A,0x6B},
{0x3A,0x91,0x11,0x41,0x4F,0x67,0xDC,0xEA,0x97,0xF2,0xCF,0xCE,0xF0,0xB4,0xE6,0x73},
{0x96,0xAC,0x74,0x22,0xE7,0xAD,0x35,0x85,0xE2,0xF9,0x37,0xE8,0x1C,0x75,0xDF,0x6E},
{0x47,0xF1,0x1A,0x71,0x1D,0x29,0xC5,0x89,0x6F,0xB7,0x62,0x0E,0xAA,0x18,0xBE,0x1B},
{0xFC,0x56,0x3E,0x4B,0xC6,0xD2,0x79,0x20,0x9A,0xDB,0xC0,0xFE,0x78,0xCD,0x5A,0xF4},
{0x1F,0xDD,0xA8,0x33,0x88,0x07,0xC7,0x31,0xB1,0x12,0x10,0x59,0x27,0x80,0xEC,0x5F},
{0x60,0x51,0x7F,0xA9,0x19,0xB5,0x4A,0x0D,0x2D,0xE5,0x7A,0x9F,0x93,0xC9,0x9C,0xEF},
{0xA0,0xE0,0x3B,0x4D,0xAE,0x2A,0xF5,0xB0,0xC8,0xEB,0xBB,0x3C,0x83,0x53,0x99,0x61},
{0x17,0x2B,0x04,0x7E,0xBA,0x77,0xD6,0x26,0xE1,0x69,0x14,0x63,0x55,0x21,0x0C,0x7D}
};

作业题

作业题跟正文无关

题目地址

题目描述

1、 AES加密模式为CBC,初始化矢量即IV为零,填充为01-00)。此外,相应的密钥在身份证件上的机器可读区域(MRZ)等表格中,它与欧洲的电子护照一起使用时并不十分完整。
2、 目标是找到以下base64编码消息的明文:
9MgYwmuPrjiecPMx61O6zIuy3MtIXQQ0E59T3xB6u0Gyf1gYs2i3K9Jxaa0zj4gTMazJuApwd6+jdyeI5iGHvhQyDHGVlAuYTgJrbFDrfB22Fpil2NfNnWFBTXyf7SDI
3、 对于加密,已生成并应用基于基本访问控制(BAC)协议的密钥KENC。对于解密,已经发送了以下字符,从中可以导出KENC(这些字符的编码类型在[1]中描述): 12345678 <8 <<< 1110182 <111116?<<<<<<<<<<<<<<< 4
4、 在传输过程中丢失了并且突出显示了一个’’?’’。可以在[2]的帮助下恢复它。
为了能够在之后计算密钥KENC,可以找到应用编码的概述
[3],[4]中的协议和[5]中的一个例子。
5、 在解密之前解码base64代码。

解题步骤

求未知数字

![](/img/code/pht/cry1.png width=100% />

按照给出的步骤手搓脚本即可求出未知数

计算key

Kseed

对数字和校验位sha1后取[:16]即可。

Ka Kb

Kseed+c然后sha1,ka是[:16],kb是[16:32]

key

对ka和kb奇偶校验即可

解密

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

def check(ka):
k=[]
a=bin(int(ka,16))[2:]
for i in range(0,len(a),8):
if(a[i:i+7].count('1')%2==0):
k.append(a[i:i+7]+'1')
else:
k.append(a[i:i+7]+'0')
knew=hex(int(''.join(k),2))
return knew[2:]

x1 = [1,1,1,1,1,6]
x2 = [7,3,1,7,3,1]
x = 0
for i in range(len(a)):
x += x1[i]*x2[i]
x %= 10

passport = '12345678<8<<<1110182<1111167<<<<<<<<<<<<<<<4'
no = passport[:10]
birth = passport[13:20]
arrive = passport[21:28]
mrz = no+birth+arrive
h_mrz = sha1(mrz.encode()).hexdigest()

k_seed = h_mrz[:32]
c = '00000001'
d = k_seed + c
h_d = sha1(bytes.fromhex(d)).hexdigest()

ka = check(h_d[:16])
kb = check(h_d[16:32])
key = ka+kb

cipher = '9MgYwmuPrjiecPMx61O6zIuy3MtIXQQ0E59T3xB6u0Gyf1gYs2i3K9Jxaa0zj4gTMazJuApwd6+jdyeI5iGHvhQyDHGVlAuYTgJrbFDrfB22Fpil2NfNnWFBTXyf7SDI'
cipher = base64.b64decode(cipher)

aes = AES.new(bytes.fromhex(key),AES.MODE_CBC,bytes.fromhex('0'*32))
result = aes.decrypt(cipher).decode()
print(result)
# Herzlichen Glueckwunsch. Sie haben die Nuss geknackt. Das Codewort lautet: Kryptographie!

感想

感觉算是……解决了现实生活中的一个问题?hhhhhhhhhhhhhh

关于本文

本文作者 云之君, 许可由 CC BY-NC 4.0.