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 }