github.com/emmansun/gmsm@v0.29.1/sm3/sm3.go (about)

     1  // Package sm3 implements ShangMi(SM) sm3 hash algorithm.
     2  package sm3
     3  
     4  // [GM/T] SM3 GB/T 32905-2016
     5  
     6  import (
     7  	"encoding/binary"
     8  	"errors"
     9  	"hash"
    10  )
    11  
    12  // Size the size of a SM3 checksum in bytes.
    13  const Size = 32
    14  
    15  // SizeBitSize the bit size of Size.
    16  const SizeBitSize = 5
    17  
    18  // BlockSize the blocksize of SM3 in bytes.
    19  const BlockSize = 64
    20  
    21  const (
    22  	chunk = 64
    23  	init0 = 0x7380166f
    24  	init1 = 0x4914b2b9
    25  	init2 = 0x172442d7
    26  	init3 = 0xda8a0600
    27  	init4 = 0xa96f30bc
    28  	init5 = 0x163138aa
    29  	init6 = 0xe38dee4d
    30  	init7 = 0xb0fb0e4e
    31  )
    32  
    33  // digest represents the partial evaluation of a checksum.
    34  type digest struct {
    35  	h   [8]uint32
    36  	x   [chunk]byte
    37  	nx  int
    38  	len uint64
    39  }
    40  
    41  const (
    42  	magic      = "sm3\x03"
    43  	marshaledSize = len(magic) + 8*4 + chunk + 8
    44  )
    45  
    46  func (d *digest) MarshalBinary() ([]byte, error) {
    47  	return d.AppendBinary(make([]byte, 0, marshaledSize))
    48  }
    49  
    50  func (d *digest) AppendBinary(b []byte) ([]byte, error) {
    51  	b = append(b, magic...)
    52  	b = appendUint32(b, d.h[0])
    53  	b = appendUint32(b, d.h[1])
    54  	b = appendUint32(b, d.h[2])
    55  	b = appendUint32(b, d.h[3])
    56  	b = appendUint32(b, d.h[4])
    57  	b = appendUint32(b, d.h[5])
    58  	b = appendUint32(b, d.h[6])
    59  	b = appendUint32(b, d.h[7])
    60  	b = append(b, d.x[:d.nx]...)
    61  	b = append(b, make([]byte, len(d.x)-d.nx)...)
    62  	b = appendUint64(b, d.len)
    63  	return b, nil
    64  }
    65  
    66  func (d *digest) UnmarshalBinary(b []byte) error {
    67  	if len(b) < len(magic) || (string(b[:len(magic)]) != magic) {
    68  		return errors.New("sm3: invalid hash state identifier")
    69  	}
    70  	if len(b) != marshaledSize {
    71  		return errors.New("sm3: invalid hash state size")
    72  	}
    73  	b = b[len(magic):]
    74  	b, d.h[0] = consumeUint32(b)
    75  	b, d.h[1] = consumeUint32(b)
    76  	b, d.h[2] = consumeUint32(b)
    77  	b, d.h[3] = consumeUint32(b)
    78  	b, d.h[4] = consumeUint32(b)
    79  	b, d.h[5] = consumeUint32(b)
    80  	b, d.h[6] = consumeUint32(b)
    81  	b, d.h[7] = consumeUint32(b)
    82  	b = b[copy(d.x[:], b):]
    83  	b, d.len = consumeUint64(b)
    84  	d.nx = int(d.len % chunk)
    85  	return nil
    86  }
    87  
    88  func appendUint64(b []byte, x uint64) []byte {
    89  	var a [8]byte
    90  	binary.BigEndian.PutUint64(a[:], x)
    91  	return append(b, a[:]...)
    92  }
    93  
    94  func appendUint32(b []byte, x uint32) []byte {
    95  	var a [4]byte
    96  	binary.BigEndian.PutUint32(a[:], x)
    97  	return append(b, a[:]...)
    98  }
    99  
   100  func consumeUint64(b []byte) ([]byte, uint64) {
   101  	_ = b[7]
   102  	x := uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 |
   103  		uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56
   104  	return b[8:], x
   105  }
   106  
   107  func consumeUint32(b []byte) ([]byte, uint32) {
   108  	_ = b[3]
   109  	x := uint32(b[3]) | uint32(b[2])<<8 | uint32(b[1])<<16 | uint32(b[0])<<24
   110  	return b[4:], x
   111  }
   112  
   113  // New returns a new hash.Hash computing the SM3 checksum. The Hash
   114  // also implements encoding.BinaryMarshaler and
   115  // encoding.BinaryUnmarshaler to marshal and unmarshal the internal
   116  // state of the hash.
   117  func New() hash.Hash {
   118  	d := new(digest)
   119  	d.Reset()
   120  	return d
   121  }
   122  
   123  // Sum appends the current hash to in and returns the resulting slice.
   124  // It does not change the underlying hash state.
   125  func (d *digest) Sum(in []byte) []byte {
   126  	// Make a copy of d so that caller can keep writing and summing.
   127  	d0 := *d
   128  	hash := d0.checkSum()
   129  	return append(in, hash[:]...)
   130  }
   131  
   132  func (d *digest) checkSum() [Size]byte {
   133  	len := d.len
   134  	// Padding. Add a 1 bit and 0 bits until 56 bytes mod 64.
   135  	var tmp [64 + 8]byte // padding + length buffer
   136  	tmp[0] = 0x80
   137  	var t uint64
   138  	if len%64 < 56 {
   139  		t = 56 - len%64
   140  	} else {
   141  		t = 64 + 56 - len%64
   142  	}
   143  	// Length in bits.
   144  	len <<= 3
   145  	padlen := tmp[:t+8]
   146  	binary.BigEndian.PutUint64(padlen[t:], len)
   147  	d.Write(padlen)
   148  
   149  	if d.nx != 0 {
   150  		panic("d.nx != 0")
   151  	}
   152  
   153  	var digest [Size]byte
   154  
   155  	binary.BigEndian.PutUint32(digest[0:], d.h[0])
   156  	binary.BigEndian.PutUint32(digest[4:], d.h[1])
   157  	binary.BigEndian.PutUint32(digest[8:], d.h[2])
   158  	binary.BigEndian.PutUint32(digest[12:], d.h[3])
   159  	binary.BigEndian.PutUint32(digest[16:], d.h[4])
   160  	binary.BigEndian.PutUint32(digest[20:], d.h[5])
   161  	binary.BigEndian.PutUint32(digest[24:], d.h[6])
   162  	binary.BigEndian.PutUint32(digest[28:], d.h[7])
   163  
   164  	return digest
   165  }
   166  
   167  func (d *digest) Write(p []byte) (nn int, err error) {
   168  	nn = len(p)
   169  	d.len += uint64(nn)
   170  	if d.nx > 0 {
   171  		n := copy(d.x[d.nx:], p)
   172  		d.nx += n
   173  		if d.nx == chunk {
   174  			block(d, d.x[:])
   175  			d.nx = 0
   176  		}
   177  		p = p[n:]
   178  	}
   179  	if len(p) >= chunk {
   180  		n := len(p) &^ (chunk - 1)
   181  		block(d, p[:n])
   182  		p = p[n:]
   183  	}
   184  	if len(p) > 0 {
   185  		d.nx = copy(d.x[:], p)
   186  	}
   187  	return
   188  }
   189  
   190  func (d *digest) Size() int {
   191  	return Size
   192  }
   193  
   194  func (d *digest) BlockSize() int { return BlockSize }
   195  
   196  // Reset resets the Hash to its initial state.
   197  func (d *digest) Reset() {
   198  	d.h[0] = init0
   199  	d.h[1] = init1
   200  	d.h[2] = init2
   201  	d.h[3] = init3
   202  	d.h[4] = init4
   203  	d.h[5] = init5
   204  	d.h[6] = init6
   205  	d.h[7] = init7
   206  	d.nx = 0
   207  	d.len = 0
   208  }
   209  
   210  // Sum returns the SM3 checksum of the data.
   211  func Sum(data []byte) [Size]byte {
   212  	var d digest
   213  	d.Reset()
   214  	d.Write(data)
   215  	return d.checkSum()
   216  }
   217  
   218  // Kdf key derivation function using SM3, compliance with GB/T 32918.4-2016 5.4.3.
   219  func (baseMD *digest) Kdf(z []byte, keyLen int) []byte {
   220  	limit := uint64(keyLen+Size-1) / uint64(Size)
   221  	if limit >= uint64(1<<32)-1 {
   222  		panic("sm3: key length too long")
   223  	}
   224  	baseMD.Reset()
   225  	baseMD.Write(z)
   226  	return kdf(baseMD, keyLen, int(limit))
   227  }
   228  
   229  func kdfGeneric(baseMD *digest, keyLen int, limit int) []byte {
   230  	var countBytes [4]byte
   231  	var ct uint32 = 1
   232  	k := make([]byte, keyLen)
   233  	for i := 0; i < limit; i++ {
   234  		binary.BigEndian.PutUint32(countBytes[:], ct)
   235  		md := *baseMD
   236  		md.Write(countBytes[:])
   237  		h := md.checkSum()
   238  		copy(k[i*Size:], h[:])
   239  		ct++
   240  	}
   241  	return k
   242  }
   243  
   244  func Kdf(z []byte, keyLen int) []byte {
   245  	baseMD := new(digest)
   246  	return baseMD.Kdf(z, keyLen)
   247  }