SM3 研究与实现

博客介绍了SM3密码杂凑算法,即Hash算法,其整体分消息填充、扩展、压缩迭代、输出Hash值4个阶段。还总结了文章易错点,如<<<符号表示32位循环左移。并提到在Golang中实现SM3,可参考Golang MD5实现结构和逻辑。

SM3

GB/T 32905-2016

SM3全称SM3密码杂凑算法,所谓的杂凑就是Hash算法,参考原文《术语解释》章节; 对消息输出消息摘要,即数字签名;
整体分成4个阶段: 消息填充、消息扩展、压缩迭代、输出Hash值。
在这里插入图片描述
这里针对文章中易错点总结一下:

  • < < < 符号的含义 <<< 符号的含义 <<<符号的含义

该符号表示32位循环左移; 代码表示如下

func rol32(x uint32, n int) uint32 {
	return (x << n) | ((x & 0xffffffff) >> (32 - n))
}
  • 公式中 i 和 j 公式中 i 和 j 公式中ij
    在这里插入图片描述
    注意这里的 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(Wj16(Wj3<<<15))(Wi13<<<7)(Wi6)

在这里插入图片描述
注意这里的 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
	}
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

cugriver

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值