SM4算法原理和硬件实现

#声明:本文创作内容含代码均为个人创作所得,允许学习、传阅,不得用于商业用途#

#本文包含国密SM4从算法到硬件实现的全部# 

#实践会让你更加透彻理解算法原理,才能让知识灌入大脑#

一:背景

SM4是中国密码局公布的块加密算法,是一种对称算法,与国际上AES算法相类似(专类文件有介绍AES)。使用场景比如数据存储和传送的时候,为了防止被窃取,我们可以先将其加密,然后再存储、传送,这样数据安全才有保障。

二:算法说明

建议国密官网下载“SM4 Block Cipher Algorithm” PDF查阅

SM4加解密的明文和秘钥的长度均为128bit,加解密后,密文的长度也是128bit;

假设秘钥用MK表示,将其分成四个字,每个字32bit,eq?%5Cmathrm%7BMK%20%3D%20MK_%7B0%7D+MK_%7B1%7D+MK_%7B2%7D+MK_%7B3%7D%7D

1. 输入秘钥和系统参数,经过32轮迭代运算后,得到32个轮秘钥,每个秘钥32bit;

2. 输入密文和轮秘钥经过32轮迭代运算,每轮迭代需要一个轮密码,得到中间密文;

3. 将上述中间密文反转后得到最终密文;

 加密迭代过程如下:

​​​​​                        eq?X_%7Bi+4%7D%20%3DF%28X_%7Bi%7D%2CX_%7Bi+1%7D%2CX_%7Bi+2%7D%2CX_%7Bi+3%7D%2Crk_%7Bi%7D%29%2C%20i%3D0%2C1%2C%2C%2C31

其中rki是第一步128bit秘钥经过32轮迭代后生成的32个轮秘钥。那这些轮秘钥生成方式和加解迭代函数F具体形式是啥呢?往下看:

轮秘钥生成方式:

2cc959837cb6456a94baa2180e548742.png

输入的128bit密钥按每32bit划分为MK0,MK1,MK2,MK3,FK0,FK1,FK2,FK3是固定已知的参数,其中 FK0=A3B1BAC6,FK1=56AA3350,FK2=6777D9197,FK3=B27022DC;输入的秘钥和固定参数异或后得到初始值K0,K1,K2,K3,然后再经过32轮迭代;参数Ki是固定已知的,如下图所示:

6c3640c03e72496ebfee3031d616c0e2.png

迭代函数T`是非线性的,表达式如下:

       eq?T%28B%29%3DL%7B%7D%27%28%5Ctau%20%28B%29%20%29 ,其中eq?L%7B%7D%27%28B%29%3DB%5Cbigoplus%20%28B%3C%3C%3C13%29%5Cbigoplus%20%28B%3C%3C%3C23%29  <<<是循环移位,

T(B)是一个查找表,表格如下:

3c6557899f0c4038bed1d4cfc0288844.png

b722de10ab3c459b9686d9702e8eefc4.png

查找表介绍:这个查找表是一个非线性函数,XY的交叉点就是输出值,例如,X=C,Y=3,那输出值是92,,因为T函数的输入是32bit,而查找表是基于8bit的,所以需要将T函数的32bit输入划分为4个byte,每个byte经过查找表后结果再拼起来得到一个32bit输出,然后再经过函数L的逻辑运算;

举个例子:B=1A02344C, T(B) = 13E9C9E6

经过上述操作,我们可以得到32个轮密钥

加密迭代计算:

 

                eq?X_%7Bi&plus;4%7D%3DX_%7Bi%7D%5Coplus%20T%28X_%7Bi&plus;1%7D%5Cbigotimes%20X_%7Bi&plus;2%7D%5Coplus%20X_%7Bi&plus;3%7D%5Coplus%20rk_%7Bi%7D%29   eq?i%3D0%2C1%2C2..31

其中   eq?T%28B%29%3DL%7B%7D%28%5Ctau%20%28B%29%20%29 ,其中eq?L%7B%7D%28B%29%3DB%5Cbigoplus%20%28B%3C%3C%3C2%29%5Cbigoplus%20%28B%3C%3C%3C10%29%5Coplus%20%28B%3C%3C%3C18%29%5Coplus%20%28B%3C%3C%3C24%29 

其中T(B)是和轮密钥生成一样的查找表。

经过32轮迭代后,可以得到X32,X33,X34,X35

密文反转:        

        输出密文为:Y=(X35,X34,X33,X32)

下面图片展示了第一轮迭代过程,密钥的迭代结构是一样的,唯一区别是L(B) 替换为L`(B)

581f84839fc4423ab8552afb0c5ba747.jpeg

三:算法python代码

代码阅读完成后还是建议大家自己亲自手写一遍,加强对算法和python语法的熟悉

说明:加密数据和key是16进制的字符串输入;大家在抄写查找表一定要细心,这里面是一个坑;相比于SM3,这个算法Python实现起来更简单。这里面数据运算一般是基于32bit,也就是4个16进制数。

class Sm4Algorithm:

    FK = ['a3b1bac6', '56aa3350', '677d9197', 'b27022dc']

    CK = ['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']

    TABLE = {'00': 'd6', '01': '90', '02': 'e9', '03': 'fe', '04': 'cc', '05': 'e1', '06': '3d', '07': 'b7',
                  '08': '16', '09': 'b6', '0a': '14', '0b': 'c2', '0c': '28', '0d': 'fb', '0e': '2c', '0f': '05',

                  '10': '2b', '11': '67', '12': '9a', '13': '76', '14': '2a', '15': 'be', '16': '04', '17': 'c3',
                  '18': 'aa', '19': '44', '1a': '13', '1b': '26', '1c': '49', '1d': '86', '1e': '06', '1f': '99',

                  '20': '9c', '21': '42', '22': '50', '23': 'f4', '24': '91', '25': 'ef', '26': '98', '27': '7a',
                  '28': '33', '29': '54', '2a': '0b', '2b': '43', '2c': 'ed', '2d': 'cf', '2e': 'ac', '2f': '62',

                  '30': 'e4', '31': 'b3', '32': '1c', '33': 'a9', '34': 'c9', '35': '08', '36': 'e8', '37': '95',
                  '38': '80', '39': 'df', '3a': '94', '3b': 'fa', '3c': '75', '3d': '8f', '3e': '3f', '3f': 'a6',

                  '40': '47', '41': '07', '42': 'a7', '43': 'fc', '44': 'f3', '45': '73', '46': '17', '47': 'ba',
                  '48': '83', '49': '59', '4a': '3c', '4b': '19', '4c': 'e6', '4d': '85', '4e': '4f', '4f': 'a8',

                  '50': '68', '51': '6b', '52': '81', '53': 'b2', '54': '71', '55': '64', '56': 'da', '57': '8b',
                  '58': 'f8', '59': 'eb', '5a': '0f', '5b': '4b', '5c': '70', '5d': '56', '5e': '9d', '5f': '35',

                  '60': '1e', '61': '24', '62': '0e', '63': '5e', '64': '63', '65': '58', '66': 'd1', '67': 'a2',
                  '68': '25', '69': '22', '6a': '7c', '6b': '3b', '6c': '01', '6d': '21', '6e': '78', '6f': '87',

                  '70': 'd4', '71': '00', '72': '46', '73': '57', '74': '9f', '75': 'd3', '76': '27', '77': '52',
                  '78': '4c', '79': '36', '7a': '02', '7b': 'e7', '7c': 'a0', '7d': 'c4', '7e': 'c8', '7f': '9e',

                  '80': 'ea', '81': 'bf', '82': '8a', '83': 'd2', '84': '40', '85': 'c7', '86': '38', '87': 'b5',
                  '88': 'a3', '89': 'f7', '8a': 'f2', '8b': 'ce', '8c': 'f9', '8d': '61', '8e': '15', '8f': 'a1',

                  '90': 'e0', '91': 'ae', '92': '5d', '93': 'a4', '94': '9b', '95': '34', '96': '1a', '97': '55',
                  '98': 'ad', '99': '93', '9a': '32', '9b': '30', '9c': 'f5', '9d': '8c', '9e': 'b1', '9f': 'e3',

                  'a0': '1d', 'a1': 'f6', 'a2': 'e2', 'a3': '2e', 'a4': '82', 'a5': '66', 'a6': 'ca', 'a7': '60',
                  'a8': 'c0', 'a9': '29', 'aa': '23', 'ab': 'ab', 'ac': '0d', 'ad': '53', 'ae': '4e', 'af': '6f',

                  'b0': 'd5', 'b1': 'db', 'b2': '37', 'b3': '45', 'b4': 'de', 'b5': 'fd', 'b6': '8e', 'b7': '2f',
                  'b8': '03', 'b9': 'ff', 'ba': '6a', 'bb': '72', 'bc': '6d', 'bd': '6c', 'be': '5b', 'bf': '51',

                  'c0': '8d', 'c1': '1b', 'c2': 'af', 'c3': '92', 'c4': 'bb', 'c5': 'dd', 'c6': 'bc', 'c7': '7f',
                  'c8': '11', 'c9': 'd9', 'ca': '5c', 'cb': '41', 'cc': '1f', 'cd': '10', 'ce': '5a', 'cf': 'd8',

                  'd0': '0a', 'd1': 'c1', 'd2': '31', 'd3': '88', 'd4': 'a5', 'd5': 'cd', 'd6': '7b', 'd7': 'bd',
                  'd8': '2d', 'd9': '74', 'da': 'd0', 'db': '12', 'dc': 'b8', 'dd': 'e5', 'de': 'b4', 'df': 'b0',

                  'e0': '89', 'e1': '69', 'e2': '97', 'e3': '4a', 'e4': '0c', 'e5': '96', 'e6': '77', 'e7': '7e',
                  'e8': '65', 'e9': 'b9', 'ea': 'f1', 'eb': '09', 'ec': 'c5', 'ed': '6e', 'ee': 'c6', 'ef': '84',

                  'f0': '18', 'f1': 'f0', 'f2': '7d', 'f3': 'ec', 'f4': '3a', 'f5': 'dc', 'f6': '4d', 'f7': '20',
                  'f8': '79', 'f9': 'ee', 'fa': '5f', 'fb': '3e', 'fc': 'd7', 'fd': 'cb', 'fe': '39', 'ff': '48'}

    def x_box(self, x):
        x_str = '{:08x}'.format(x)
        y_str = ''
        for i in range(4):
            y_str = y_str + self.TABLE[x_str[i*2:i*2+2]]
        return int(y_str, 16) & 0xFFFFFFFF

    def rotate_left(self, a, k):
        k = k % 32
        return ((a << k) & 0xFFFFFFFF) | ((a & 0xFFFFFFFF) >> (32-k))

    def l_of_plaintext(self, x):
        return (x ^ self.rotate_left(x,2) ^ self.rotate_left(x,10) ^ self.rotate_left(x,18) ^self.rotate_left(x,24)) & 0xFFFFFFFF

    def l_of_key(self, x):
        return (x ^ self.rotate_left(x,13) ^ self.rotate_left(x,23)) & 0xFFFFFFFF

    def t_of_plaintext(self,x):
        return self.l_of_plaintext(self.x_box(x))

    def t_of_key(self, x):
        return self.l_of_key(self.x_box(x))

    def key_output(self, key):
        mk0 = int(key[8*0:8*1], 16) & 0xFFFFFFFF
        mk1 = int(key[8*1:8*2], 16) & 0xFFFFFFFF
        mk2 = int(key[8*2:8*3], 16) & 0xFFFFFFFF
        mk3 = int(key[8*3:8*4], 16) & 0xFFFFFFFF

        k0 = mk0 ^ (int(self.FK[0], 16) & 0xFFFFFFFF)
        k1 = mk1 ^ (int(self.FK[1], 16) & 0xFFFFFFFF)
        k2 = mk2 ^ (int(self.FK[2], 16) & 0xFFFFFFFF)
        k3 = mk3 ^ (int(self.FK[3], 16) & 0xFFFFFFFF)

        k = [0] * 36
        k[0:4] = [k0, k1, k2, k3]
        rk = [0] * 32
        for i in range(32):
            tmp = k[i+1] ^ k[i+2] ^ k[i+3] ^ (int(self.CK[i], 16) & 0xFFFFFFFF)
            k[i+4] = k[i] ^ self.t_of_key(tmp)
            # print(i,hex(k[i+4]))
            rk[i] = k[i+4]
        return rk

    def encryption(self, x, key):
        ciphertext = [0] * 36
        ciphertext[0] = int(x[8*0:8*1], 16) & 0xFFFFFFFF
        ciphertext[1] = int(x[8*1:8*2], 16) & 0xFFFFFFFF
        ciphertext[2] = int(x[8*2:8*3], 16) & 0xFFFFFFFF
        ciphertext[3] = int(x[8*3:8*4], 16) & 0xFFFFFFFF

        get_key = key
        rk = self.key_output(get_key)

        for i in range(32):
            tmp = ciphertext[i+1] ^ ciphertext[i+2] ^ ciphertext[i+3] ^ rk[i]
            ciphertext[i+4] = ciphertext[i] ^ self.t_of_plaintext(tmp)
            # print(i+4,hex(ciphertext[i+4]))

        y = '{:08x}'.format(ciphertext[35]) + '{:08x}'.format(ciphertext[34]) + '{:08x}'.format(ciphertext[33]) + '{:08x}'.format(ciphertext[32])
        return y

if __name__ == '__main__':
    get_class = Sm4Algorithm()
    # case 1
    date = get_class.encryption('0123456789abcdeffedcba9876543210', '0123456789abcdeffedcba9876543210')  # case1
    print(date)
    print(type(date))

    # case 2
    for i in range(1000000):
        if i == 0 :
            data = '0123456789abcdeffedcba9876543210'
            output = get_class.encryption(data, '0123456789abcdeffedcba9876543210')
        else :
            tmp = output
            output= get_class.encryption(tmp, '0123456789abcdeffedcba9876543210')

        if i == 999999:
            print(output)

四:Verilog硬件实现

“后续待补充”

 

  • 39
    点赞
  • 50
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值