SM3 研究与实现

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
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
sm2算法是一种国家密码学标准算法,它主要用于椭圆曲线公钥密码体制中的加密和数字签名。sm2算法基于椭圆曲线离散对数问题,具有安全性和高效性的特点。 sm2算法的C语言实现主要包括以下几个步骤: 1. 选择椭圆曲线参数:sm2算法使用的是国家标准椭圆曲线参数,可以在标准文档中找到。选择合适的参数,初始化椭圆曲线。 2. 生成密钥对:使用随机数生成器生成一个私钥,并通过椭圆曲线上的点运算得到对应的公钥。 3. 加密过程:假设要加密的明文为M,首先将M转换成点P,再生成一个随机数k。通过点运算得到C1=kG,其中G为基点。然后计算点S=kP,将S的x坐标转换成字节数组,作为SM3算法的输入,得到哈希值H。最后计算C2=M⨁H,C3=k^-1·(hash(kP)+C2)。 4. 解密过程:给定密文C=(C1,C2,C3),首先计算C1的k倍点为kC1,并将kC1的x坐标作为SM3算法的输入,得到哈希值H。然后计算C2=M⨁H,并利用C1的k倍点计算kP。最后计算hash(kP)+C2,乘以C3的k^-1得到明文M。 5. 数字签名过程:给定要签名的消息M和私钥d,首先计算消息的哈希值H。然后生成一个随机数k,并通过点运算得到点R=kG。将R的x坐标转换成字节数组,作为SM3算法的输入,得到哈希值h。计算s=(h+d·r)·⁻¹·k。最后签名为(R,s)。 以上是sm2算法的简要介绍和C语言实现的整体步骤,实现过程中需注意安全性和正确性。具体的C语言代码实现需要详细研究和阅读sm2算法的相关标准文档,理解椭圆曲线运算和密码哈希算法的具体细节,然后按照算法规范进行编码实现

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

cugriver

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

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

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

打赏作者

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

抵扣说明:

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

余额充值