mirror of
https://github.com/emmansun/gmsm.git
synced 2025-04-26 20:26:19 +08:00
SM3
This commit is contained in:
parent
d275df7790
commit
4d7305a6f6
205
sm3/sm3.go
Normal file
205
sm3/sm3.go
Normal file
@ -0,0 +1,205 @@
|
||||
package sm3
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"hash"
|
||||
)
|
||||
|
||||
// Size the size of a SM3 checksum in bytes.
|
||||
const Size = 32
|
||||
|
||||
// BlockSize the blocksize of SM3 in bytes.
|
||||
const BlockSize = 64
|
||||
|
||||
const (
|
||||
chunk = 64
|
||||
init0 = 0x7380166f
|
||||
init1 = 0x4914b2b9
|
||||
init2 = 0x172442d7
|
||||
init3 = 0xda8a0600
|
||||
init4 = 0xa96f30bc
|
||||
init5 = 0x163138aa
|
||||
init6 = 0xe38dee4d
|
||||
init7 = 0xb0fb0e4e
|
||||
)
|
||||
|
||||
// digest represents the partial evaluation of a checksum.
|
||||
type digest struct {
|
||||
h [8]uint32
|
||||
x [chunk]byte
|
||||
nx int
|
||||
len uint64
|
||||
}
|
||||
|
||||
const (
|
||||
magic256 = "sm3\x03"
|
||||
marshaledSize = len(magic256) + 8*4 + chunk + 8
|
||||
)
|
||||
|
||||
func (d *digest) MarshalBinary() ([]byte, error) {
|
||||
b := make([]byte, 0, marshaledSize)
|
||||
b = append(b, magic256...)
|
||||
b = appendUint32(b, d.h[0])
|
||||
b = appendUint32(b, d.h[1])
|
||||
b = appendUint32(b, d.h[2])
|
||||
b = appendUint32(b, d.h[3])
|
||||
b = appendUint32(b, d.h[4])
|
||||
b = appendUint32(b, d.h[5])
|
||||
b = appendUint32(b, d.h[6])
|
||||
b = appendUint32(b, d.h[7])
|
||||
b = append(b, d.x[:d.nx]...)
|
||||
b = b[:len(b)+len(d.x)-int(d.nx)] // already zero
|
||||
b = appendUint64(b, d.len)
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (d *digest) UnmarshalBinary(b []byte) error {
|
||||
if len(b) < len(magic256) || (string(b[:len(magic256)]) != magic256) {
|
||||
return errors.New("sm3: invalid hash state identifier")
|
||||
}
|
||||
if len(b) != marshaledSize {
|
||||
return errors.New("sm3: invalid hash state size")
|
||||
}
|
||||
b = b[len(magic256):]
|
||||
b, d.h[0] = consumeUint32(b)
|
||||
b, d.h[1] = consumeUint32(b)
|
||||
b, d.h[2] = consumeUint32(b)
|
||||
b, d.h[3] = consumeUint32(b)
|
||||
b, d.h[4] = consumeUint32(b)
|
||||
b, d.h[5] = consumeUint32(b)
|
||||
b, d.h[6] = consumeUint32(b)
|
||||
b, d.h[7] = consumeUint32(b)
|
||||
b = b[copy(d.x[:], b):]
|
||||
b, d.len = consumeUint64(b)
|
||||
d.nx = int(d.len % chunk)
|
||||
return nil
|
||||
}
|
||||
|
||||
func appendUint64(b []byte, x uint64) []byte {
|
||||
var a [8]byte
|
||||
binary.BigEndian.PutUint64(a[:], x)
|
||||
return append(b, a[:]...)
|
||||
}
|
||||
|
||||
func appendUint32(b []byte, x uint32) []byte {
|
||||
var a [4]byte
|
||||
binary.BigEndian.PutUint32(a[:], x)
|
||||
return append(b, a[:]...)
|
||||
}
|
||||
|
||||
func consumeUint64(b []byte) ([]byte, uint64) {
|
||||
_ = b[7]
|
||||
x := uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 |
|
||||
uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56
|
||||
return b[8:], x
|
||||
}
|
||||
|
||||
func consumeUint32(b []byte) ([]byte, uint32) {
|
||||
_ = b[3]
|
||||
x := uint32(b[3]) | uint32(b[2])<<8 | uint32(b[1])<<16 | uint32(b[0])<<24
|
||||
return b[4:], x
|
||||
}
|
||||
|
||||
// New returns a new hash.Hash computing the SM3 checksum. The Hash
|
||||
// also implements encoding.BinaryMarshaler and
|
||||
// encoding.BinaryUnmarshaler to marshal and unmarshal the internal
|
||||
// state of the hash.
|
||||
func New() hash.Hash {
|
||||
d := new(digest)
|
||||
d.Reset()
|
||||
return d
|
||||
}
|
||||
|
||||
// Sum appends the current hash to b and returns the resulting slice.
|
||||
// It does not change the underlying hash state.
|
||||
func (d *digest) Sum(in []byte) []byte {
|
||||
// Make a copy of d so that caller can keep writing and summing.
|
||||
d0 := *d
|
||||
hash := d0.checkSum()
|
||||
return append(in, hash[:]...)
|
||||
}
|
||||
|
||||
func (d *digest) checkSum() [Size]byte {
|
||||
len := d.len
|
||||
// Padding. Add a 1 bit and 0 bits until 56 bytes mod 64.
|
||||
var tmp [64]byte
|
||||
tmp[0] = 0x80
|
||||
if len%64 < 56 {
|
||||
d.Write(tmp[0 : 56-len%64])
|
||||
} else {
|
||||
d.Write(tmp[0 : 64+56-len%64])
|
||||
}
|
||||
// Length in bits.
|
||||
len <<= 3
|
||||
binary.BigEndian.PutUint64(tmp[:], len)
|
||||
d.Write(tmp[0:8])
|
||||
|
||||
if d.nx != 0 {
|
||||
panic("d.nx != 0")
|
||||
}
|
||||
|
||||
var digest [Size]byte
|
||||
|
||||
binary.BigEndian.PutUint32(digest[0:], d.h[0])
|
||||
binary.BigEndian.PutUint32(digest[4:], d.h[1])
|
||||
binary.BigEndian.PutUint32(digest[8:], d.h[2])
|
||||
binary.BigEndian.PutUint32(digest[12:], d.h[3])
|
||||
binary.BigEndian.PutUint32(digest[16:], d.h[4])
|
||||
binary.BigEndian.PutUint32(digest[20:], d.h[5])
|
||||
binary.BigEndian.PutUint32(digest[24:], d.h[6])
|
||||
binary.BigEndian.PutUint32(digest[28:], d.h[7])
|
||||
|
||||
return digest
|
||||
}
|
||||
|
||||
func (d *digest) Write(p []byte) (nn int, err error) {
|
||||
nn = len(p)
|
||||
d.len += uint64(nn)
|
||||
if d.nx > 0 {
|
||||
n := copy(d.x[d.nx:], p)
|
||||
d.nx += n
|
||||
if d.nx == chunk {
|
||||
block(d, d.x[:])
|
||||
d.nx = 0
|
||||
}
|
||||
p = p[n:]
|
||||
}
|
||||
if len(p) >= chunk {
|
||||
n := len(p) &^ (chunk - 1)
|
||||
block(d, p[:n])
|
||||
p = p[n:]
|
||||
}
|
||||
if len(p) > 0 {
|
||||
d.nx = copy(d.x[:], p)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (d *digest) Size() int {
|
||||
return Size
|
||||
}
|
||||
|
||||
func (d *digest) BlockSize() int { return BlockSize }
|
||||
|
||||
// Reset resets the Hash to its initial state.
|
||||
func (d *digest) Reset() {
|
||||
d.h[0] = init0
|
||||
d.h[1] = init1
|
||||
d.h[2] = init2
|
||||
d.h[3] = init3
|
||||
d.h[4] = init4
|
||||
d.h[5] = init5
|
||||
d.h[6] = init6
|
||||
d.h[7] = init7
|
||||
d.nx = 0
|
||||
d.len = 0
|
||||
}
|
||||
|
||||
// Sum returns the SM3 checksum of the data.
|
||||
func Sum(data []byte) [Size]byte {
|
||||
var d digest
|
||||
d.Reset()
|
||||
d.Write(data)
|
||||
return d.checkSum()
|
||||
}
|
129
sm3/sm3_test.go
Normal file
129
sm3/sm3_test.go
Normal file
@ -0,0 +1,129 @@
|
||||
package sm3
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding"
|
||||
"fmt"
|
||||
"hash"
|
||||
"io"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type sm3Test struct {
|
||||
out string
|
||||
in string
|
||||
halfState string // marshaled hash state after first half of in written, used by TestGoldenMarshal
|
||||
}
|
||||
|
||||
var golden = []sm3Test{
|
||||
{"66c7f0f462eeedd9d1f2d46bdc10e4e24167c4875cf2f7a2297da02b8f4ba8e0", "abc", "sm3\x03s\x80\x16oI\x14\xb2\xb9\x17$B\xd7ڊ\x06\x00\xa9o0\xbc\x1618\xaa\xe3\x8d\xeeM\xb0\xfb\x0eNa\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"},
|
||||
{"debe9ff92275b8a138604889c18e5a4d6fdb70e5387e5765293dcba39c0c5732", "abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd", "sm3\x03s\x80\x16oI\x14\xb2\xb9\x17$B\xd7ڊ\x06\x00\xa9o0\xbc\x1618\xaa\xe3\x8d\xeeM\xb0\xfb\x0eNabcdabcdabcdabcdabcdabcdabcdabcd\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 "},
|
||||
}
|
||||
|
||||
func TestGolden(t *testing.T) {
|
||||
for i := 0; i < len(golden); i++ {
|
||||
g := golden[i]
|
||||
s := fmt.Sprintf("%x", Sum([]byte(g.in)))
|
||||
if s != g.out {
|
||||
t.Fatalf("SM3 function: sm3(%s) = %s want %s", g.in, s, g.out)
|
||||
}
|
||||
c := New()
|
||||
for j := 0; j < 3; j++ {
|
||||
if j < 2 {
|
||||
io.WriteString(c, g.in)
|
||||
} else {
|
||||
io.WriteString(c, g.in[0:len(g.in)/2])
|
||||
c.Sum(nil)
|
||||
io.WriteString(c, g.in[len(g.in)/2:])
|
||||
}
|
||||
s := fmt.Sprintf("%x", c.Sum(nil))
|
||||
if s != g.out {
|
||||
t.Fatalf("sm3[%d](%s) = %s want %s", j, g.in, s, g.out)
|
||||
}
|
||||
c.Reset()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGoldenMarshal(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
newHash func() hash.Hash
|
||||
gold []sm3Test
|
||||
}{
|
||||
{"", New, golden},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
for _, g := range tt.gold {
|
||||
h := tt.newHash()
|
||||
h2 := tt.newHash()
|
||||
|
||||
io.WriteString(h, g.in[:len(g.in)/2])
|
||||
|
||||
state, err := h.(encoding.BinaryMarshaler).MarshalBinary()
|
||||
if err != nil {
|
||||
t.Errorf("could not marshal: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
if string(state) != g.halfState {
|
||||
t.Errorf("sm3%s(%q) state = %q, want %q", tt.name, g.in, state, g.halfState)
|
||||
continue
|
||||
}
|
||||
|
||||
if err := h2.(encoding.BinaryUnmarshaler).UnmarshalBinary(state); err != nil {
|
||||
t.Errorf("could not unmarshal: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
io.WriteString(h, g.in[len(g.in)/2:])
|
||||
io.WriteString(h2, g.in[len(g.in)/2:])
|
||||
|
||||
if actual, actual2 := h.Sum(nil), h2.Sum(nil); !bytes.Equal(actual, actual2) {
|
||||
t.Errorf("sm3%s(%q) = 0x%x != marshaled 0x%x", tt.name, g.in, actual, actual2)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSize(t *testing.T) {
|
||||
c := New()
|
||||
if got := c.Size(); got != Size {
|
||||
t.Errorf("Size = %d; want %d", got, Size)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBlockSize(t *testing.T) {
|
||||
c := New()
|
||||
if got := c.BlockSize(); got != BlockSize {
|
||||
t.Errorf("BlockSize = %d want %d", got, BlockSize)
|
||||
}
|
||||
}
|
||||
|
||||
var bench = New()
|
||||
var buf = make([]byte, 8192)
|
||||
|
||||
func benchmarkSize(b *testing.B, size int) {
|
||||
b.SetBytes(int64(size))
|
||||
sum := make([]byte, bench.Size())
|
||||
for i := 0; i < b.N; i++ {
|
||||
bench.Reset()
|
||||
bench.Write(buf[:size])
|
||||
bench.Sum(sum[:0])
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkHash8Bytes(b *testing.B) {
|
||||
benchmarkSize(b, 8)
|
||||
}
|
||||
|
||||
func BenchmarkHash1K(b *testing.B) {
|
||||
benchmarkSize(b, 1024)
|
||||
}
|
||||
|
||||
func BenchmarkHash8K(b *testing.B) {
|
||||
benchmarkSize(b, 8192)
|
||||
}
|
80
sm3/sm3block.go
Normal file
80
sm3/sm3block.go
Normal file
@ -0,0 +1,80 @@
|
||||
package sm3
|
||||
|
||||
import "math/bits"
|
||||
|
||||
var _T = []uint32{
|
||||
0x79cc4519,
|
||||
0x7a879d8a,
|
||||
}
|
||||
|
||||
func t(j uint8) uint32 {
|
||||
if j < 16 {
|
||||
return _T[0]
|
||||
}
|
||||
return _T[1]
|
||||
}
|
||||
|
||||
func p0(x uint32) uint32 {
|
||||
return x ^ bits.RotateLeft32(x, 9) ^ bits.RotateLeft32(x, 17)
|
||||
}
|
||||
|
||||
func p1(x uint32) uint32 {
|
||||
return x ^ bits.RotateLeft32(x, 15) ^ bits.RotateLeft32(x, 23)
|
||||
}
|
||||
|
||||
func ff(j uint8, x, y, z uint32) uint32 {
|
||||
if j < 16 {
|
||||
return x ^ y ^ z
|
||||
}
|
||||
return (x & y) | (x & z) | (y & z)
|
||||
}
|
||||
|
||||
func gg(j uint8, x, y, z uint32) uint32 {
|
||||
if j < 16 {
|
||||
return x ^ y ^ z
|
||||
}
|
||||
return (x & y) | (^x & z)
|
||||
}
|
||||
|
||||
func block(dig *digest, p []byte) {
|
||||
var w [68]uint32
|
||||
var w1 [64]uint32
|
||||
h0, h1, h2, h3, h4, h5, h6, h7 := dig.h[0], dig.h[1], dig.h[2], dig.h[3], dig.h[4], dig.h[5], dig.h[6], dig.h[7]
|
||||
for len(p) >= chunk {
|
||||
for i := 0; i < 16; i++ {
|
||||
j := i * 4
|
||||
w[i] = uint32(p[j])<<24 | uint32(p[j+1])<<16 | uint32(p[j+2])<<8 | uint32(p[j+3])
|
||||
}
|
||||
for i := 16; i < 68; i++ {
|
||||
w[i] = p1(w[i-16]^w[i-9]^bits.RotateLeft32(w[i-3], 15)) ^ bits.RotateLeft32(w[i-13], 7) ^ w[i-6]
|
||||
}
|
||||
for i := 0; i < 64; i++ {
|
||||
w1[i] = w[i] ^ w[i+4]
|
||||
}
|
||||
a, b, c, d, e, f, g, h := h0, h1, h2, h3, h4, h5, h6, h7
|
||||
for i := 0; i < 64; i++ {
|
||||
ss1 := bits.RotateLeft32(bits.RotateLeft32(a, 12)+e+bits.RotateLeft32(t(uint8(i)), i), 7)
|
||||
ss2 := ss1 ^ bits.RotateLeft32(a, 12)
|
||||
tt1 := ff(uint8(i), a, b, c) + d + ss2 + w1[i]
|
||||
tt2 := gg(uint8(i), e, f, g) + h + ss1 + w[i]
|
||||
d = c
|
||||
c = bits.RotateLeft32(b, 9)
|
||||
b = a
|
||||
a = tt1
|
||||
h = g
|
||||
g = bits.RotateLeft32(f, 19)
|
||||
f = e
|
||||
e = p0(tt2)
|
||||
}
|
||||
h0 ^= a
|
||||
h1 ^= b
|
||||
h2 ^= c
|
||||
h3 ^= d
|
||||
h4 ^= e
|
||||
h5 ^= f
|
||||
h6 ^= g
|
||||
h7 ^= h
|
||||
p = p[chunk:]
|
||||
}
|
||||
dig.h[0], dig.h[1], dig.h[2], dig.h[3], dig.h[4], dig.h[5], dig.h[6], dig.h[7] = h0, h1, h2, h3, h4, h5, h6, h7
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user