原创代码:https://github.com/ZZMarquis/gm
引用时,请导入原创代码库。本文仅以注释方式详解代码逻辑,供学习研究使用。
对原创代码的修改内容
- 修改了部分常量、变量、结构体属性的名称, 以便与GO语言标准包规范相统一
- 加入中文注释,解释代码逻辑
注释者及联系邮箱
Paul Lee
paul_lee0919@163.com
// Encrypt() 为SM4的加密方法函数。
// (1) 校验输入消息字节数组的长度
// (2) 校验输出消息字节数组的长度
// (3) 调用分组消息处理函数processBlock()
func (c *sm4Cipher) Encrypt(dst, src []byte) {
if len(src) < BlockSize {
panic("sm4: input not full block")
}
if len(dst) < BlockSize {
panic("sm4: output not full block")
}
processBlock(c.enc, src, dst)
}
Encrypt() 为SM4的加密方法函数。其中:
- (1) 校验输入消息字节数组的长度
- (2) 校验输出消息字节数组的长度
- (3) 调用分组消息处理函数processBlock( )
// Decrypt() 为SM4的解密方法函数。
// (1) 校验输入消息字节数组的长度
// (2) 校验输出消息字节数组的长度
// (3) 调用分组处理函数processBlock()
func (c *sm4Cipher) Decrypt(dst, src []byte) {
if len(src) < BlockSize {
panic("sm4: input not full block")
}
if len(dst) < BlockSize {
panic("sm4: output not full block")
}
processBlock(c.dec, src, dst)
}
Decrypt( ) 为SM4的解密方法函数。其中:
- (1) 校验输入消息字节数组的长度
- (2) 校验输出消息字节数组的长度
- (3) 调用分组数据处理函数processBlock( )
// tau() 为国标(6.2.(a))规定的Sbox非线性变换τ(.),其中:
// (1) 如果将Sbox二维表逐行展开为一维数组s[],则数组元素的序号就是Sbox行序号乘16加列序号;
// (2) 因为Sbox二维表本身为16x16的表,而1个字节存储单元能够存储8位二进制数,折算16进制就是2位16进制数;
// (3) 所以,若将1个字节所表示的二进制数字a,折算成16进制数字(假设为EF),然后将其高位E作为行坐标、将低位F作为列坐标,
// 则数字a就可以用来表示Sbox二维表中某个元素的行列坐标(E,F):
// (4) Sbox二维表中,坐标为(E,F)的元素展开成数组s[]后,其数组序号就是: Ex16 + F,这其实就是数字a的值;
// (5) 所以,根据非线性变换τ(.)的定义,Sbox(a)=s[a]
func tau(a uint32) uint32 {
var aArr [4]byte
var bArr [4]byte
binary.BigEndian.PutUint32(aArr[:], a)
bArr[0] = sBox[aArr[0]]
bArr[1] = sBox[aArr[1]]
bArr[2] = sBox[aArr[2]]
bArr[3] = sBox[aArr[3]]
return binary.BigEndian.Uint32(bArr[:])
}
tau( ) 为国标(6.2.(a))规定的Sbox非线性变换τ(.),其中:
- (1) 如果将Sbox二维表逐行展开为一维数组s[],则数组元素的序号就是Sbox行序号乘16加列序号;
- (2) 因为Sbox二维表本身为16x16的表,而1个字节存储单元能够存储8位二进制数,折算16进制就是2位16进制数;
- (3) 所以,若将1个字节所表示的二进制数字a,折算成16进制数字(假设为EF),然后将其高位E作为行坐标、将低位F作为列坐标,则数字a就可以用来表示Sbox二维表中某个元素的行列坐标(E,F);
- (4) Sbox二维表中,坐标为(E,F)的元素展开成数组s[]后,其数组序号就是: Ex16 + F,这其实就是数字a的值;
- (5) 所以,根据非线性变换τ(.)的定义,Sbox(a)=s[a]
// processBlock 为SM4核心算法函数:
// (1) 将in[]数组存储的输入消息(128位)按4个字节为1个“字”(32位)来分组,划分成4组;
// (2) 结合轮秘钥rk,根据国标(7.1.(a))规定,按轮函数F算法进行32轮迭代加密;
// (3) 根据国标(7.1.(b))规定,进行反序变换;
// (4) 将推算结果写入out[]数组。
func processBlock(rk []uint32, in []byte, out []byte) {
var x [BlockSize / 4]uint32
x[0] = binary.BigEndian.Uint32(in[0:4])
x[1] = binary.BigEndian.Uint32(in[4:8])
x[2] = binary.BigEndian.Uint32(in[8:12])
x[3] = binary.BigEndian.Uint32(in[12:16])
for i := 0; i < 32; i += 4 {
x[0] = f0(x[:], rk[i])
x[1] = f1(x[:], rk[i+1])
x[2] = f2(x[:], rk[i+2])
x[3] = f3(x[:], rk[i+3])
}
r(x[:])
binary.BigEndian.PutUint32(out[0:4], x[0])
binary.BigEndian.PutUint32(out[4:8], x[1])
binary.BigEndian.PutUint32(out[8:12], x[2])
binary.BigEndian.PutUint32(out[12:16], x[3])
}
processBlock( ) 为分组数据处理函数,是SM4核心算法函数:
- (1) 将in[ ]数组存储的输入消息(128位)按4个字节为1个“字”(32位)来分组,划分成4组;
- (2) 结合轮秘钥rk,根据国标(7.1.(a))规定,按轮函数F算法(详见下文)进行32轮迭代加密;
- (3) 根据国标(7.1.(b))规定,进行反序变换(详见下文);
- (4) 将推算结果写入out[ ]数组。
// l() 为国标(6.2.(b))规定的合成置换函数T(.)的第二步骤:线性变换函数L()。
func l(b uint32) uint32 {
return b ^ bits.RotateLeft32(b, 2) ^ bits.RotateLeft32(b, 10) ^
bits.RotateLeft32(b, 18) ^ bits.RotateLeft32(b, 24)
}
// t() 为国标(6.2)规定的合成置换函数T(.)
func t(z uint32) uint32 {
return l(tau(z))
}
// f0() 代表国标(6.1和7.1.(a))定义的轮函数F(),在i=0时对应的实例。
func f0(x []uint32, rk uint32) uint32 {
return x[0] ^ t(x[1]^x[2]^x[3]^rk)
}
// f1() 代表国标(6.1和7.1.(a))定义的轮函数F(),在i=1时对应的实例。
func f1(x []uint32, rk uint32) uint32 {
return x[1] ^ t(x[2]^x[3]^x[0]^rk)
}
// f2() 代表国标(6.1和7.1.(a))定义的轮函数F(),在i=2时对应的实例。
func f2(x []uint32, rk uint32) uint32 {
return x[2] ^ t(x[3]^x[0]^x[1]^rk)
}
// f3() 代表国标(6.1和7.1.(a))定义的轮函数F(),在i=3时对应的实例。
func f3(x []uint32, rk uint32) uint32 {
return x[3] ^ t(x[0]^x[1]^x[2]^rk)
}
以上代码为四层嵌套调用函数:
- 最外层为fi( ) 函数,代表i = (0, 1, 2…3) 时国标(6.1和7.1.(a)定义的轮函数F( )算法
- 第二层为t( )函数,代表国标(6.2)规定的合成置换函数T(.)
- 第三层为l( )函数,代表国标(6.2.(b))规定的合成置换函数T(.)的第二步骤----线性变换函数L( )
- 第四层为τ( )函数,代表国标(6.2.(b))规定的合成置换函数T(.)的第一个步骤----非线性变换函数τ( ) (详见上文)
// r() 为国标(7.1.(b))定义的反序变换函数:
// (1) 两个二进制数A0和B0, 初次异或运算结果为A1, 则A1=A0^B0;
// (2) A1与B0进行第二次异或运算结果为B1,则B1=A1^B0=A0^B0^B0=A0;
// (3) A1与B1进行第三次异或运算结果为A2,则A2=A1^B1=A0^B0^A0=B0;
// (4) 若A、B均为变量,将A0/A1/A2均用变量A存储,B0/B1均用变量B存储,则
// 上述计算可简述为:三次异或运算交换变量值。
func r(a []uint32) {
a[0] = a[0] ^ a[3]
a[3] = a[0] ^ a[3]
a[0] = a[0] ^ a[3]
a[1] = a[1] ^ a[2]
a[2] = a[1] ^ a[2]
a[1] = a[1] ^ a[2]
}
r( ) 为国标(7.1.(b))定义的反序变换函数:
- 两个二进制数A0和B0, 初次异或运算结果为A1, 则A1=A0^B0;
- A1与B0进行第二次异或运算结果为B1,则B1=A1 ^ B0 = A0 ^ B0 ^ B0 = A0;
- A1与B1进行第三次异或运算结果为A2,则A2 = A1 ^ B1 = A0 ^ B0 ^ A0 = B0;
- 若A、B均为变量,将A0/A1/A2均用变量A存储,B0/B1均用变量B存储,则上述计算可简述为:三次异或运算交换变量值。
(未完待续)