SM3
SM3全称SM3密码杂凑算法,所谓的杂凑就是Hash算法,参考原文《术语解释》章节; 对消息输出消息摘要,即数字签名;
整体分成4个阶段: 消息填充、消息扩展、压缩迭代、输出Hash值。
这里针对文章中易错点总结一下:
- < < < 符号的含义 <<< 符号的含义 <<<符号的含义
该符号表示32位循环左移; 代码表示如下
func rol32(x uint32, n int) uint32 {
return (x << n) | ((x & 0xffffffff) >> (32 - n))
}
-
公式中
i
和
j
公式中 i 和 j
公式中i和j
注意这里的 i i i 就是 j j j
W i = P 1 ( W j − 16 ⊕ ( W j − 3 < < < 15 ) ) ⊕ ( W i − 13 < < < 7 ) ⊕ ( W i − 6 ) W_{i}=P_{1}(W_{j-16}\oplus(W_{j-3}<<<15))\oplus(W_{i-13}<<<7)\oplus(W_{i-6}) Wi=P1(Wj−16⊕(Wj−3<<<15))⊕(Wi−13<<<7)⊕(Wi−6)
注意这里的
i
i
i 也是
j
j
j
实现
SM3和MD5很类似,保持在golang语言实现的统一性; 可参考golang md5 实现结构和逻辑; https://github.com/golang/go/tree/master/src/crypto/md5
目录结构
cryptox/sm3/
|-------const.go 常量定义和常量公式定义
|-------sm3.go SM3实现源文件
|-------sm3_test.go SM3单元测试
|-------sm3block.go SM3数字签名生成逻辑
代码实现
const.go
package sm3
// GB/T 32905-2016
// The size of an SM3 checksum in bytes.
// link: http://c.gb688.cn/bzgk/gb/showGb?type=online&hcno=45B1A67F20F3BF339211C391E9278F5E
const Size = 32
const BlockSize = 64
const (
init0 = 0x7380166f
init1 = 0x4914b2b9
init2 = 0x172442d7
init3 = 0xda8a0600
init4 = 0xa96f30bc
init5 = 0x163138aa
init6 = 0xe38dee4d
init7 = 0xb0fb0e4e
)
func kk(i int) uint32 {
if i < 16 {
return 0x79cc4519
} else {
return 0x7a879d8a
}
}
func ff(i int, x, y, z uint32) uint32 {
if i < 16 {
return x ^ y ^ z
} else {
return (x & y) | (x & z) | (y & z)
}
}
func gg(i int, x, y, z uint32) uint32 {
if i < 16 {
return x ^ y ^ z
} else {
return (x & y) | (^x & z)
}
}
func p0(x uint32) uint32 {
return x ^ rol32(x, 9) ^ rol32(x, 17)
}
func p1(x uint32) uint32 {
return x ^ rol32(x, 15) ^ rol32(x, 23)
}
func rol32(x uint32, n int) uint32 {
return (x << n) | ((x & 0xffffffff) >> (32 - n))
}
sm3.go
package sm3
import (
"encoding/binary"
"hash"
)
func New() hash.Hash {
d := new(digest)
d.Reset()
return d
}
// GB/T 32905-2016
type digest struct {
s [8]uint32
x [BlockSize]byte
nx int
l uint64
}
func (d *digest) Reset() {
d.s[0] = init0
d.s[1] = init1
d.s[2] = init2
d.s[3] = init3
d.s[4] = init4
d.s[5] = init5
d.s[6] = init6
d.s[7] = init7
d.nx = 0
d.l = 0
}
func (d *digest) Size() int { return Size }
func (d *digest) BlockSize() int { return BlockSize }
func (d *digest) Write(p []byte) (nn int, err error) {
nn = len(p)
d.l += uint64(nn)
if d.nx > 0 {
n := copy(d.x[d.nx:], p)
d.nx += n
if d.nx == BlockSize {
blockGeneric(d, d.x[:])
d.nx = 0
}
p = p[n:]
}
if len(p) >= BlockSize {
n := len(p) &^ (BlockSize - 1)
blockGeneric(d, p[:n])
p = p[n:]
}
if len(p) > 0 {
d.nx = copy(d.x[:], p)
}
return
}
func (d *digest) Sum(in []byte) []byte {
d0 := *d
hash := d0.checkSum()
return append(in, hash[:]...)
}
func (d *digest) checkSum() [Size]byte {
// Append 0x80 to the end of the message and then append zeros
// until the length is a multiple of 56 bytes. Finally append
// 8 bytes representing the message length in bits.
//
// 1 byte end marker :: 0-63 padding bytes :: 8 byte length
tmp := [1 + 63 + 8]byte{0x80}
pad := (55 - d.l) % 64 // calculate number of padding bytes
binary.BigEndian.PutUint64(tmp[1+pad:], d.l<<3) // append length in bits
d.Write(tmp[:1+pad+8])
// The previous write ensures that a whole number of
// blocks (i.e. a multiple of 64 bytes) have been hashed.
if d.nx != 0 {
panic("d.nx != 0")
}
var digest [Size]byte
binary.BigEndian.PutUint32(digest[0x00:], d.s[0])
binary.BigEndian.PutUint32(digest[0x04:], d.s[1])
binary.BigEndian.PutUint32(digest[0x08:], d.s[2])
binary.BigEndian.PutUint32(digest[0x0c:], d.s[3])
binary.BigEndian.PutUint32(digest[0x10:], d.s[4])
binary.BigEndian.PutUint32(digest[0x14:], d.s[5])
binary.BigEndian.PutUint32(digest[0x18:], d.s[6])
binary.BigEndian.PutUint32(digest[0x1c:], d.s[7])
return digest
}
func Sum(data []byte) [Size]byte {
var d digest
d.Reset()
d.Write(data)
return d.checkSum()
}
sm3block.go
package sm3
import "encoding/binary"
func blockGeneric(dig *digest, p []byte) {
//load state
a, b, c, d, e, f, g, h := dig.s[0], dig.s[1], dig.s[2], dig.s[3], dig.s[4], dig.s[5], dig.s[6], dig.s[7]
//GB/T 32905-2016
for i := 0; i <= len(p)-BlockSize; i += BlockSize {
//GB/T 32905-2016 5.3.2. Message Extension
q := p[i:]
q = q[:BlockSize:BlockSize]
w := [132]uint32{}
for j := 0; j < 132; j++ {
switch {
case j < 16:
w[j] = binary.BigEndian.Uint32(q[4*j:])
case j < 68:
w[j] = p1(w[j-16]^w[j-9]^rol32(w[j-3], 15)) ^ rol32(w[j-13], 7) ^ w[j-6]
default:
w[j] = w[j-68] ^ w[j-64]
}
}
for j := 0; j < 64; j++ {
ss1 := rol32(rol32(a, 12)+e+rol32(kk(j), j%32), 7)
ss2 := ss1 ^ rol32(a, 12)
tt1 := ff(j, a, b, c) + d + ss2 + w[68+j]
tt2 := gg(j, e, f, g) + h + ss1 + w[j]
d = c
c = rol32(b, 9)
b = a
a = tt1
h = g
g = rol32(f, 19)
f = e
e = p0(tt2)
}
dig.s[0] ^= a
dig.s[1] ^= b
dig.s[2] ^= c
dig.s[3] ^= d
dig.s[4] ^= e
dig.s[5] ^= f
dig.s[6] ^= g
dig.s[7] ^= h
}
}