SM4算法


SM4算法

作者:lowlyli

时间:2021-12-5

内容:SM4是国密里面针对无线标准进行的分组密码,和DES加密很像。它采用的方法是能够很好抵抗差值攻击的仿射函数逆映射复合法。有时间分析一下如何破解吧。

SM4算法介绍

2012年3月,国家密码管理局正式公布了包含SM4分组密码算法在内的《祖冲之序列密码算法》等6项密码行业标准。与DES和AES算法类似,SM4算法是一种分组密码算法。其分组长度为128bit,密钥长度也为128bit。加密算法与密钥扩展算法均采用32轮非线性迭代结构,以字(32位)为单位进行加密运算,每一次迭代运算均为一轮变换函数F。SM4算法加/解密算法的结构相同,只是使用轮密钥相反,其中解密轮密钥是加密轮密钥的逆序。

SMS4分组加密算法是中国无线标准中使用的分组加密算法,在2012年已经被国家商用密码管理局确定为国家密码行业标准,标准编号GM/T 0002-2012并且改名为SM4算法,与SM2椭圆曲线公钥密码算法,SM3密码杂凑算法共同作为国家密码的行业标准,在我国密码行业中有着极其重要的位置。

SM4有很高的灵活性,所采用的S盒可以灵活地被替换,以应对突发性的安全威胁。算法的32轮迭代采用串行处理,这与AES中每轮使用代换和混淆并行地处理整个分组有很大不同。

S盒是一种利用非线性变换构造的分组密码的一个组件,主要是为了实现分组密码过程中的混淆的特性和设计的。SMS4算法中的S盒在设计之初完全按照欧美分组密码的设计标准进行,它采用的方法是能够很好抵抗差值攻击的仿射函数逆映射复合法。

参考官方网站:密码行业标准化: http://www.gmbz.org.cn/main/bzlb.html

202112051241833.png

SM4算法原理

本算法是一个分组算法。该算法的分组长度为128比特,密钥长度为128比特。加密算法与密钥扩展算法都采用32轮非线性迭代结构。解密算法与加密算法的结构相同,只是轮密钥的使用顺序相反,解密轮密钥是加密轮密钥的逆序。

术语说明

字与字节

用$Z_2^e$表示e-比特的向量集,$Z_2^{32}$中的元素称为字,$Z_2^8$中的元素称为字节。

S盒

S盒为固定的8比特输入8比特输出的置换,记为$Sbox(X)$

基本运算

在本算法中采用了以下基本运算:

  • $⊕$ 32比特异或
  • $<<<i$ 32比特循环左移$i$位

密钥及密钥参量

加密密钥长度为 128 比特, 表示为, 其中 为 字。

轮密钥表示为 , 其中 为字。轮密钥由加密密钥生成。

系统参数为

固定参数, 用于密钥扩展算法,

其中 为字。

轮函数F

本算法采用非线性迭代结构, 以字为单位进行加密运算, 称一次迭代运算为一轮变换。

设输入为,轮密钥为,则轮函数F为:

合成置换T

$T:Z_2^{32} \rightarrow Z_2^{32}$,是一个可逆变换,由非线性变换$τ$和线性变换$L$复合而成,即$T(x)=L(τ(x))$

非线性变换τ

$\tau$ 由 4 个并行的S盒构成。
设输入为 , 输出为 , 则

线性变换L

非线性变换 的输出是线性变换 的输入。设输入为 , 输出为 , 则

S盒

image-20211205153046989

输入‘ef’,则经S盒后的值为表中第e行和第f列的值,Sbox(‘ef’)= ‘84’。

加/解密算法

定义反序变换 为:

设明文输入为 , 密文输出为, 轮密钥为 。则本算法的加密变换为:

本算法的解密变换与加密变换结构相同, 不同的仅是轮密钥的使用顺序。

密时轮密钥的使用顺序为:

解密时轮密钥的使用顺序为:

密钥扩展算法

本算法中加密算法的轮密钥由加密密钥通过密钥扩展算法生成。

加密密钥

, 轮密钥为 , 则轮密钥生成方法为:

首先,

然后, 对 $ i=0,1,2, \ldots, 31$ :

说明:

(1) $T^{\prime} $ 变换与加密算法轮函数中的 T 基本相同, 只将其中的线性变换 L 修改为以下$ L^{\prime} $ :

(2) 系统参数 FK的取值, 采用 16 进制表示为:

(3) 固定参数的取值方法为:

的第 字节 , 即 ,则。 32 个固定参数 , 其 16 进制表示为:

00070e15 1c232a31 383f464d 545b6269
70777e85 8c939aa1 a8afb6bd c4cbd2d9
e0e7eef5 fc030a11 181f262d 343b4249
50575e65 6c737a81 888f969d a4abb2b9
c0c7ced5 dce3eaf1 f8ff060d 141b2229
30373e45 4c535a61 686f767d 848b9299
a0a7aeb5 bcc3cad1 d8dfe6ed f4fb0209
10171e25 2c333a41 484f565d 646b7279

加密解密可逆分析

这里设加密密钥

, 则可以生成轮密钥为 ,

在加密中密钥, 在解密中, 即:每一轮的密钥一致。

设明文输入为

设 密文输出为,

根据加密原则:

而加密流程

故可解出,以此类推得到:

SM4算法流程

完整流程如下:

202112051250479.png

密钥扩展算法

第一步:密钥与系统参数的异或:

第二步:获取子密钥:

流程如下:

20210514162641653

函数T:

在这里插入图片描述

明文加密

明文处理大致分解为3步:

1)、将128bit的明文分成4个32bit的字

2)、将上述得到的字进行32轮的轮操作。

3)、最后将进行过32轮操作的4个字进行反序变换后组成128bit的密文。

在这里插入图片描述

在这里插入图片描述

SM4代码介绍

定义S盒和FK,CK

如下:

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

# 系统参数FK
FK = [0xa3b1bac6, 0x56aa3350, 0x677d9197, 0xb27022dc]

# 固定参数CK
CK = [0x00070e15, 0x1c232a31, 0x383f464d, 0x545b6269, 0x70777e85, 0x8c939aa1, 0xa8afb6bd, 0xc4cbd2d9,
      0xe0e7eef5, 0xfc030a11, 0x181f262d, 0x343b4249, 0x50575e65, 0x6c737a81, 0x888f969d, 0xa4abb2b9,
      0xc0c7ced5, 0xdce3eaf1, 0xf8ff060d, 0x141b2229, 0x30373e45, 0x4c535a61, 0x686f767d, 0x848b9299,
      0xa0a7aeb5, 0xbcc3cad1, 0xd8dfe6ed, 0xf4fb0209, 0x10171e25, 0x2c333a41, 0x484f565d, 0x646b7279]

定义T函数

$T(x)=L(τ(x))$

这里分别定义$τ(x)$和$L(x)$

$τ(x)$

@staticmethod
def _s_box(n: int):
    result = bytearray()
    # 将 32bit 拆分成 4x8bit,依次进行S盒变换
    for item in list(n.to_bytes(4, 'big')):
        result.append(S_BOX[item])
    return int.from_bytes(result, 'big')

$L(x)$

@staticmethod
def _rot_left(n, m):
    """循环左移"""
    return ((n << m) | (n >> (32 - m))) & 0xFFFFFFFF

密钥扩展

def _generate_key(self, key: bytes):
    """密钥生成"""
    key_r, key_temp = [0 for _ in range(32)], [0 for _ in range(4)]
    # 将 128bit 拆分成 4x32bit
    for i in range(4):
        temp = int.from_bytes(key[4 * i:4 * i + 4], 'big')
        key_temp[i] = temp ^ FK[i]
    # 循环生成轮密钥
    for i in range(32):
        box_in = key_temp[1] ^ key_temp[2] ^ key_temp[3] ^ CK[i]
        box_out = self._s_box(box_in)
        key_r[i] = key_temp[0] ^ box_out ^ self._rot_left(box_out, 13) ^ self._rot_left(box_out, 23)
        key_temp = key_temp[1:] + [key_r[i]]
    return key_r

主体循环(加密解密一样)

def _do(self, text: bytes, key_r: list):
       text_ = [0 for _ in range(4)]
       # 将 128bit 转化成 4x32bit
       for i in range(4):
           text_[i] = int.from_bytes(text[4 * i:4 * i + 4], 'big')
       for i in range(32):
           box_in = text_[1] ^ text_[2] ^ text_[3] ^ key_r[i]
           box_out = self._s_box(box_in)
           temp = text_[0] ^ box_out ^ self._rot_left(box_out, 2) ^ self._rot_left(box_out, 10)
           temp = temp ^ self._rot_left(box_out, 18) ^ self._rot_left(box_out, 24)
           text_ = text_[1:] + [temp]
       text_ = text_[::-1]  # 结果逆序
       # 将 4x32bit 合并成 128bit
       result = bytearray()
       for i in range(4):
           result.extend(text_[i].to_bytes(4, 'big'))
       return bytes(result)

加密解密接口

def encrypt(self, plaintext: bytes):
    return self._do(plaintext, self._key_r)

def decrypt(self, ciphertext: bytes):
    return self._do(ciphertext, self._key_r[::-1])

运行验证

这里只关注算法本身的加密,未实现分组密码的分组的加密模式和填充,故只支持标准的128bit输入和输出。

明文:00112233445566778899aabbccddeeff

密钥:0123456789ABCDEFFEDCBA9876543210

密文:09325c4853832dcb9337a5984f671b9a

运行代码:

if __name__ == "__main__":
    print("16进制Key:0123456789ABCDEFFEDCBA9876543210")
    # 128bit密钥
    key = bytes.fromhex("0123456789ABCDEFFEDCBA9876543210")
    print("16进制明文:00112233445566778899aabbccddeeff")
    # 128bit明文
    plaintext = bytes.fromhex("00112233445566778899aabbccddeeff")
    sm4 = SM4Cipher(key)
    print("16进制密文:"+ sm4.encrypt(plaintext).hex())
    # 09325c4853832dcb9337a5984f671b9a
    encryption = sm4.encrypt(plaintext).hex()
    encryp_txt = bytes.fromhex(str(encryption))

    print("16进制解密密文:"+sm4.decrypt(encryp_txt).hex())

结果:

image-20211205163627150

网站验证:

网站:SM4加密网站: https://the-x.cn/cryptography/Sm4.aspx

image-20211205163922636

附录

完整代码

"""
@Time    : 2021/12/5
@Author  : LowlyLi
@Version : 1.0
@File    : SM4.py
@Introduce: SM4 国密4
"""

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

# 系统参数FK
FK = [0xa3b1bac6, 0x56aa3350, 0x677d9197, 0xb27022dc]

# 固定参数CK
CK = [0x00070e15, 0x1c232a31, 0x383f464d, 0x545b6269, 0x70777e85, 0x8c939aa1, 0xa8afb6bd, 0xc4cbd2d9,
      0xe0e7eef5, 0xfc030a11, 0x181f262d, 0x343b4249, 0x50575e65, 0x6c737a81, 0x888f969d, 0xa4abb2b9,
      0xc0c7ced5, 0xdce3eaf1, 0xf8ff060d, 0x141b2229, 0x30373e45, 0x4c535a61, 0x686f767d, 0x848b9299,
      0xa0a7aeb5, 0xbcc3cad1, 0xd8dfe6ed, 0xf4fb0209, 0x10171e25, 0x2c333a41, 0x484f565d, 0x646b7279]


class SM4Cipher:
    def __init__(self, key: bytes):
        if not len(key) == 16:
            raise ValueError("SM4 key must be length of 16. ")
        self._key_r = self._generate_key(key)
        self.block_size = 16

    def encrypt(self, plaintext: bytes):
        return self._do(plaintext, self._key_r)

    def decrypt(self, ciphertext: bytes):
        return self._do(ciphertext, self._key_r[::-1])

    def _do(self, text: bytes, key_r: list):
        text_ = [0 for _ in range(4)]
        # 将 128bit 转化成 4x32bit
        for i in range(4):
            text_[i] = int.from_bytes(text[4 * i:4 * i + 4], 'big')
        for i in range(32):
            box_in = text_[1] ^ text_[2] ^ text_[3] ^ key_r[i]
            box_out = self._s_box(box_in)
            temp = text_[0] ^ box_out ^ self._rot_left(box_out, 2) ^ self._rot_left(box_out, 10)
            temp = temp ^ self._rot_left(box_out, 18) ^ self._rot_left(box_out, 24)
            text_ = text_[1:] + [temp]
        text_ = text_[::-1]  # 结果逆序
        # 将 4x32bit 合并成 128bit
        result = bytearray()
        for i in range(4):
            result.extend(text_[i].to_bytes(4, 'big'))
        return bytes(result)

    def _generate_key(self, key: bytes):
        """密钥生成"""
        key_r, key_temp = [0 for _ in range(32)], [0 for _ in range(4)]
        # 将 128bit 拆分成 4x32bit
        for i in range(4):
            temp = int.from_bytes(key[4 * i:4 * i + 4], 'big')
            key_temp[i] = temp ^ FK[i]
        # 循环生成轮密钥
        for i in range(32):
            box_in = key_temp[1] ^ key_temp[2] ^ key_temp[3] ^ CK[i]
            box_out = self._s_box(box_in)
            key_r[i] = key_temp[0] ^ box_out ^ self._rot_left(box_out, 13) ^ self._rot_left(box_out, 23)
            key_temp = key_temp[1:] + [key_r[i]]
        return key_r

    @staticmethod
    def _s_box(n: int):
        result = bytearray()
        # 将 32bit 拆分成 4x8bit,依次进行S盒变换
        for item in list(n.to_bytes(4, 'big')):
            result.append(S_BOX[item])
        return int.from_bytes(result, 'big')

    @staticmethod
    def _rot_left(n, m):
        """循环左移"""
        return ((n << m) | (n >> (32 - m))) & 0xFFFFFFFF

if __name__ == "__main__":
     # 128bit密钥
    key = bytes.fromhex("0123456789ABCDEFFEDCBA9876543210") 
    # 128bit明文
    plaintext = bytes.fromhex("00112233445566778899aabbccddeeff")  
    sm4 = SM4Cipher(key)
    print(sm4.encrypt(plaintext).hex())  
    # 09325c4853832dcb9337a5984f671b9a
    encryption = sm4.encrypt(plaintext).hex()
    encryp_txt = bytes.fromhex(str(encryption))
    print(sm4.decrypt(encryp_txt).hex())
    # 00112233445566778899aabbccddeeff

"""
# 样例一
key = "0123456789ABCDEFFEDCBA9876543210"  # 16进制字符串
plaintext = "0123456789ABCDEFFEDCBA9876543210"  # 16进制字符串
ciphertext = "681edf34d206965e86b3e94f536e4246"  # 16进制字符串

# 样例二
key = "0123456789ABCDEFFEDCBA9876543210"  # 16进制字符串
plaintext = "00112233445566778899aabbccddeeff"  # 16进制字符串
ciphertext = "09325c4853832dcb9337a5984f671b9a"  # 16进制字符串

# 样例三
key = "456789ABCDEFFEDCBA98765432100123"  # 16进制字符串
plaintext = "2233445566778899aabbccddeeff0011"  # 16进制字符串
ciphertext = "58ab414d84fb3008b0bee987f97021e6"  # 16进制字符串

# 样例四
key = "89ABCDEFFEDCBA987654321001234567"  # 16进制字符串
plaintext = "445566778899aabbccddeeff00112233"  # 16进制字符串
ciphertext = "5937a929a2d9137216c72a28cd9cf619"  # 16进制字符串

"""

文章作者: LowlyLi
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 LowlyLi !
评论
 上一篇
CUMT算法考试总结 CUMT算法考试总结
CUMT算法考试(信安专业),崩了,题量太大了,写不完,呜呜呜
2021-12-08 LowlyLi
下一篇 
Paillier算法 Paillier算法
这部分是联邦学习里面的最基础的同态加密,机器学习其实也只是简单的线性运算,而同态加密实现加密后运算保存一致,造就了在敏感数据行业机器学习,云计算的发展,采用联邦学习的分布式计算可以有效运用在金融,医疗等敏感信息上,随着个人隐私的加强,无法得到明文数据时,同态加密或许会成为互联网企业进行用户画像分析的下一个突破点。
2021-12-03
  目录