注:本blog参考《祖冲之序列密码算法 第1部分:算法描述》(GB/T 33133_1-2016),源码为本人手搓(ai辅助),如有漏洞请轻点喷 XD
国家标准全文公开系统:https://openstd.samr.gov.cn
算法基本信息 ZUC算法是流密码,加密本质是 $密文 = 明文 \oplus 密钥流$,解密是$密文 \oplus 密钥流$,
可分为ZUC-128、ZUC-256
ZUC-128 密钥长度:128bit iv长度:128bit
ZUC-256 密钥长度:256bit iv长度:根据具体的加密保护场景,可分为:184bit (23字节)、200bit (25字节)或规范性补充中的128bit (16字节)
算法结构 由线性反馈寄存器(LFSR)、比特重组(BR)和非线性函数F组成。 如图:
LFSR 基础信息: 包括16个31比特寄存器单元变量$s_0,s_1,…,s_15$ 俩种模式:初始化模式、工作模式
初始化模式 LFSR接收1个31比特字u的输入,对寄存器单元变$s_0,s_1,…,s_{15}$进行更新,计算如下:
1 2 3 4 5 6 7 8 9 MASK32 = 0 xFFFFFFFF # 模 2 ^32 掩码LFSR_MOD = 0 x7FFFFFFF # 模 2 ^31 - 1 def LFSRWithInitialisationMode(self, u): v = (2 **15 *self.s[15 ]+2 **17 *self.s[13 ]+2 **21 *self.s[10 ]+2 **20 *self.s[4 ]+(1 +2 **8 )*self.s[0 ]) % LFSR_MOD self .s.append((v+u) % LFSR_MOD) if self.s[16 ] == 0 : self .s[16 ] = LFSR_MOD self .s.pop(0 )
工作模式
1 2 3 4 5 6 def LFSRWithWorkMode(self): v = (2 **15 *self.s[15 ]+2 **17 *self.s[13 ]+2 **21 *self.s[10 ]+2 **20 *self.s[4 ]+(1 +2 **8 )*self.s[0 ]) % LFSR_MOD if v == 0 : v = LFSR_MOD self .s.append(v) self .s.pop(0 )
比特重组 BR 输入:LFSR寄存器单元变量$s_0,s_2,s_5,s_7,s_9,s_11,s_14,s_15$ 输出:4个32比特字$X_0、X_1、X_2、X_3$
计算过程: 1.$s_{15}$高16位 || $s_{14}$低16位 -> $X_0$ 2.$s_{11}$低16位 || $s_{9}$高16位拼接->$X_1$ 3.$s_{7}$低16位 || $s_{5}$高16位拼接->$X_2$ 4.$s_{2}$低16位 || $s_{0}$高16位拼接->$X_3$ 注:|| 是指左边和右边直接拼接在一起(注意不要被高位和低位混淆了,是直接拼在一起,例如:第二步中$s_{11}$取的是低16位,但是它拼接后是$X_1$的高16位)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 def BitReconstruction(self): self .X[0 ] = ((self.s[15 ] >> 15 ) << 16 ) | (self.s[14 ] & 0 xffff) self .X[1 ] = ((self.s[11 ] & 0 xffff) << 16 ) | (self.s[9 ] >> 15 ) self .X[2 ] = ((self.s[7 ] & 0 xffff) << 16 ) | (self.s[5 ] >> 15 ) self .X[3 ] = ((self.s[2 ] & 0 xffff) << 16 ) | (self.s[0 ] >> 15 )
密钥装入
$d_i$ 是定义好的16个16bit常量串
1 2 3 4 5 6 7 8 9 k = * 16 iv_ = * 16 # k_i 和 iv_i 均为8比特字节 for i in range(16): k = key iv_ = iv for i in range(16): self.s = (k << 23) | (d << 8) | iv_
S盒的定义 表A.1和表A.2里存的是S盒的具体数值,在最后的代码演示中有放具体数值,这里先看S盒的定义代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 def _S (self, W ): b8 = [0 ] * 4 b8[0 ] = (W >> 24 ) & 0xff b8[1 ] = (W >> 16 ) & 0xff b8[2 ] = (W >> 8 ) & 0xff b8[3 ] = W & 0xff for i in range(4 ): row = b8[i] >> 4 col = b8[i] & 0xf if i b8[i] = S0 [row][col] # i=0 ,2 时使用S0 else: b8[i] = S1 [row][col] # i=1 ,3 时使用S1 return (b8[0 ] << 24 ) | (b8[1 ] << 16 ) | (b8[2 ] << 8 ) | b8[3 ]
非线性函数F 在加密过程中还会用到非线性函数F,函数F的定义如下: 其中:
代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 def rotl32 (x, n ): return ((x << n) | (x >> (32 - n))) & 0xFFFFFFFF def F (self, X0, X1, X2 ): W = ((X0 ^ self .R1) + self .R2) & MASK32 W1 = (self .R1 + X1) & MASK32 W2 = (self .R2 ^ X2) & MASK32 L1_X = ((W1 & 0xffff ) << 16 ) | (W2 >> 16 ) L2_X = ((W2 & 0xffff ) << 16 ) | (W1 >> 16 ) u = self .L1(L1_X) v = self .L2(L2_X) self .R1 = self ._S(u) self .R2 = self ._S(v) return Wdef L1 (self, L1_X ): return L1_X ^ rotl32(L1_X, 2 ) ^ rotl32(L1_X, 10 ) ^ rotl32(L1_X,18 ) ^ rotl32(L1_X,24 )def L2 (self, L2_X ): return L2_X ^ rotl32(L2_X, 8 ) ^ rotl32(L2_X, 14 ) ^ rotl32(L2_X, 22 ) ^ rotl32(L2_X, 30 )
加密流程
将密钥装入到LFSR的寄存器单元变量$s_0,s_1,…,s_15$中,作为LFSR的初态
重复执行下述过程32次 a. $BitReconstruction()$ b. $W = F(X_0,X_1,X_2)$ c. 输出32比特字W d. $LFSRWithInitialisationMode(W >> 1)$前俩步只是初始化步骤,下面开始工作步骤
执行下述三步: a. $BitReconstruction()$ b. $F(X_0,X_1,X_2)$ c. $LFSRWithWorkMode()$
重复计算L次(下面会解释这个L)下述过程: a. $BitReconstruction()$ b. $Z = F(X_0,X_1,X_2)\oplus X_3$ c. 输出32比特密钥字Z d. $LFSRWithWorkMode()$
这里的 L 是指需要产生的密钥流长度 (以32比特字为单位) L 表示实际加密所需的32比特密钥字的个数 ,每执行一次步骤4,就输出一个32比特密钥字 Z,因此循环 L 次后,共输出 L × 32 比特 的密钥流 举例来说,如果需要加密一段160比特的数据,则需要 L = 5(5 × 32 = 160比特)。 L 的值由实际应用场景中的数据长度 决定,不是一个固定常量。
按照ZUC标准,每次循环应输出一个32比特(4字节)的密钥字 Z ,循环次数 L 应为:
代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 def encrypt (self, plaintext ): ciphertext = bytearray () L = (len (plaintext) + 3 ) // 4 for i in range (L): self .BitReconstruction() W = self .F(self .X[0 ], self .X[1 ], self .X[2 ]) Z = W ^ self .X[3 ] self .LFSRWithWorkMode() print (f'Z{i} = {hex (Z)[2 :].zfill(8 )} ' ) for j in range (4 ): idx = i * 4 + j if idx < len (plaintext): ciphertext.append(plaintext[idx] ^ ((Z >> (24 - 8 * j)) & 0xFF )) return bytes (ciphertext)
解密流程 ZUC算法是流密码,也就是说,加密本质是 $密文 = 明文 \oplus 密钥流$,解密就是$密文 \oplus 密钥流$,问题就在密钥流的生成要与加密时一致,所以解密和加密流程是一致的:
1 2 def decrypt (self , ciphertext ): return self .encrypt(ciphertext)
代码 下面只展示ZUC这个类的定义,想要包括有其他常量的请移步Baymax-Fools\ZUC.py
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 class ZUC : def __init__(self , key, iv): # key和iv均为16字节(128比特)的输入 self .s = [0] * 16 # 线性反馈移位寄存器的16个31比特寄存器单元变量 self .X = [0] * 4 # 比特重组输出的4个32比特字 self .R1 = 0 self .R2 = 0 k = [0] * 16 iv_ = [0] * 16 # k_i 和 iv_i 均为8比特字节 for i in range(16): k[i] = key[i] iv_[i] = iv[i] for i in range(16): self .s[i] = (k[i] << 23) | (d[i] << 8) | iv_[i] for _ in range(32): self .BitReconstruction () W = self .F (self .X [0], self .X [1], self .X [2]) self .LFSRWithInitialisationMode (W >> 1) # 用 W 的高 31 位 self .BitReconstruction () self .F (self .X [0], self .X [1], self .X [2]) self .LFSRWithWorkMode () def LFSRWithInitialisationMode (self , u): v = (2**15*self .s[15]+2**17*self .s[13]+2**21*self .s[10]+2**20*self .s[4]+(1+2**8)*self .s[0]) % LFSR_MOD self .s.append((v+u) % LFSR_MOD ) if self .s[16] == 0: self .s[16] = LFSR_MOD self .s.pop(0) def LFSRWithWorkMode (self ): v = (2**15*self .s[15]+2**17*self .s[13]+2**21*self .s[10]+2**20*self .s[4]+(1+2**8)*self .s[0]) % LFSR_MOD if v == 0: v = LFSR_MOD self .s.append(v) self .s.pop(0) def BitReconstruction (self ): # X0 = S15的高16位 | S14的低16位 # 0x7fff8000 是第 30-15 位,左移 1 位正好到 31-16 位 self .X [0] = ((self .s[15] >> 15) << 16) | (self .s[14] & 0xffff) # X1 = S11的低16位 | S9的高16位 # 低16位左移16位到高处,高16位右移15位到低处 self .X [1] = ((self .s[11] & 0xffff) << 16) | (self .s[9] >> 15) # X2 = S7的低16位 | S5的高16位 self .X [2] = ((self .s[7] & 0xffff) << 16) | (self .s[5] >> 15) # X3 = S2的低16位 | S0的高16位 self .X [3] = ((self .s[2] & 0xffff) << 16) | (self .s[0] >> 15) def F (self , X0 , X1 , X2 ): W = ((X0 ^ self .R1 ) + self .R2 ) & MASK32 W1 = (self .R1 + X1 ) & MASK32 W2 = (self .R2 ^ X2 ) & MASK32 L1_X = ((W1 & 0xffff) << 16) | (W2 >> 16) L2_X = ((W2 & 0xffff) << 16) | (W1 >> 16) u = self .L1 (L1_X ) v = self .L2 (L2_X ) self .R1 = self ._ S (u) self .R2 = self ._ S (v) return W def _ S (self , W ): b8 = [0] * 4 b8[0] = (W >> 24) & 0xff b8[1] = (W >> 16) & 0xff b8[2] = (W >> 8) & 0xff b8[3] = W & 0xff for i in range(4): row = b8[i] >> 4 col = b8[i] & 0xf if i % 2 == 0: b8[i] = S0 [row][col] # i=0,2时使用S0 else : b8[i] = S1 [row][col] # i=1,3时使用S1 return (b8[0] << 24) | (b8[1] << 16) | (b8[2] << 8) | b8[3] def L1 (self , L1_X ): return L1_X ^ rotl32(L1_X , 2) ^ rotl32(L1_X , 10) ^ rotl32(L1_X ,18) ^ rotl32(L1_X ,24) def L2 (self , L2_X ): return L2_X ^ rotl32(L2_X , 8) ^ rotl32(L2_X , 14) ^ rotl32(L2_X , 22) ^ rotl32(L2_X , 30) def encrypt(self , plaintext): ciphertext = bytearray() L = (len(plaintext) + 3) // 4 # 向上取整 for i in range(L ): self .BitReconstruction () W = self .F (self .X [0], self .X [1], self .X [2]) Z = W ^ self .X [3] self .LFSRWithWorkMode () print(f'Z {i} = {hex(Z )[2 :].zfill(8 )}') # 每个Z处理4个字节 for j in range(4 ): idx = i * 4 + j if idx < len(plaintext): ciphertext.append(plaintext[idx] ^ ((Z >> (24 - 8 * j)) & 0xFF )) return bytes(ciphertext) def decrypt(self , ciphertext): return self .encrypt(ciphertext) # ZUC 的加密和解密过程相同