github.com/emmansun/gmsm@v0.29.1/zuc/eia.go (about) 1 package zuc 2 3 import ( 4 "encoding/binary" 5 "fmt" 6 ) 7 8 const ( 9 chunk = 16 10 ) 11 12 type ZUC128Mac struct { 13 zucState32 // current zuc state 14 k0 [8]uint32 // keywords 15 t uint32 // tag 16 x [chunk]byte //buffer 17 nx int // remaining data in x 18 len uint64 // total data length 19 tagSize int // tag size 20 initState zucState32 // initial state for reset 21 } 22 23 // NewHash create hash for zuc-128 eia, with arguments key and iv. 24 // Both key/iv size are 16 in bytes. 25 func NewHash(key, iv []byte) (*ZUC128Mac, error) { 26 k := len(key) 27 ivLen := len(iv) 28 mac := &ZUC128Mac{} 29 mac.tagSize = 4 30 31 switch k { 32 default: 33 return nil, fmt.Errorf("zuc: invalid key size %d, expect 16 in bytes", k) 34 case 16: // ZUC-128 35 if ivLen != IVSize128 { 36 return nil, fmt.Errorf("zuc: invalid iv size %d, expect %d in bytes", ivLen, IVSize128) 37 } 38 mac.loadKeyIV16(key, iv) 39 } 40 41 // initialization 42 for i := 0; i < 32; i++ { 43 mac.bitReorganization() 44 w := mac.f32() 45 mac.enterInitMode(w >> 1) 46 } 47 48 // work state 49 mac.bitReorganization() 50 mac.f32() 51 mac.enterWorkMode() 52 53 mac.initState.r1 = mac.r1 54 mac.initState.r2 = mac.r2 55 56 copy(mac.initState.lfsr[:], mac.lfsr[:]) 57 mac.Reset() 58 return mac, nil 59 } 60 61 func genIV4EIA(count, bearer, direction uint32) []byte { 62 iv := make([]byte, 16) 63 binary.BigEndian.PutUint32(iv, count) 64 copy(iv[9:12], iv[1:4]) 65 iv[4] = byte(bearer << 3) 66 iv[12] = iv[4] 67 iv[8] = iv[0] ^ byte(direction<<7) 68 iv[14] = byte(direction << 7) 69 return iv 70 } 71 72 // NewEIAHash create hash for zuc-128 eia, with arguments key, count, bearer and direction 73 func NewEIAHash(key []byte, count, bearer, direction uint32) (*ZUC128Mac, error) { 74 return NewHash(key, genIV4EIA(count, bearer, direction)) 75 } 76 77 func (m *ZUC128Mac) Size() int { 78 return m.tagSize 79 } 80 81 func (m *ZUC128Mac) BlockSize() int { 82 return chunk 83 } 84 85 // Reset resets the Hash to its initial state. 86 func (m *ZUC128Mac) Reset() { 87 m.t = 0 88 m.nx = 0 89 m.len = 0 90 m.r1 = m.initState.r1 91 m.r2 = m.initState.r2 92 copy(m.lfsr[:], m.initState.lfsr[:]) 93 m.genKeywords(m.k0[:len(m.k0)/2]) 94 } 95 96 func blockGeneric(m *ZUC128Mac, p []byte) { 97 // use 64 bits to shift left 2 keywords 98 var k64, t64 uint64 99 t64 = uint64(m.t) << 32 100 for len(p) >= chunk { 101 // generate next 4 keywords 102 m.genKeywords(m.k0[4:]) 103 k64 = uint64(m.k0[0])<<32 | uint64(m.k0[1]) 104 // process first 32 bits 105 w := binary.BigEndian.Uint32(p[0:4]) 106 for j := 0; j < 32; j++ { 107 // t64 ^= (w >> 31) ? k64 : 0 108 t64 ^= ^(uint64(w>>31) - 1) & k64 109 w <<= 1 110 k64 <<= 1 111 } 112 // process second 32 bits 113 k64 = uint64(m.k0[1])<<32 | uint64(m.k0[2]) 114 w = binary.BigEndian.Uint32(p[4:8]) 115 for j := 0; j < 32; j++ { 116 t64 ^= ^(uint64(w>>31) - 1) & k64 117 w <<= 1 118 k64 <<= 1 119 } 120 // process third 32 bits 121 k64 = uint64(m.k0[2])<<32 | uint64(m.k0[3]) 122 w = binary.BigEndian.Uint32(p[8:12]) 123 for j := 0; j < 32; j++ { 124 t64 ^= ^(uint64(w>>31) - 1) & k64 125 w <<= 1 126 k64 <<= 1 127 } 128 // process fourth 32 bits 129 k64 = uint64(m.k0[3])<<32 | uint64(m.k0[4]) 130 w = binary.BigEndian.Uint32(p[12:16]) 131 for j := 0; j < 32; j++ { 132 t64 ^= ^(uint64(w>>31) - 1) & k64 133 w <<= 1 134 k64 <<= 1 135 } 136 // Move the new keywords to the first 4 137 copy(m.k0[:4], m.k0[4:]) 138 p = p[chunk:] 139 } 140 m.t = uint32(t64 >> 32) 141 } 142 143 func (m *ZUC128Mac) Write(p []byte) (nn int, err error) { 144 nn = len(p) 145 m.len += uint64(nn) 146 if m.nx > 0 { 147 n := copy(m.x[m.nx:], p) 148 m.nx += n 149 if m.nx == chunk { 150 block(m, m.x[:]) 151 m.nx = 0 152 } 153 p = p[n:] 154 } 155 if len(p) >= chunk { 156 n := len(p) &^ (chunk - 1) 157 block(m, p[:n]) 158 p = p[n:] 159 } 160 if len(p) > 0 { 161 m.nx = copy(m.x[:], p) 162 } 163 return 164 } 165 166 func (m *ZUC128Mac) checkSum(additionalBits int, b byte) [4]byte { 167 if m.nx >= chunk { 168 panic("m.nx >= chunk") 169 } 170 kIdx := 0 171 if m.nx > 0 || additionalBits > 0 { 172 var k64, t64 uint64 173 t64 = uint64(m.t) << 32 174 m.x[m.nx] = b 175 // total bits to handle 176 nRemainBits := 8*m.nx + additionalBits 177 if nRemainBits > 2*32 { 178 // generate next 2 keywords 179 m.genKeywords(m.k0[4:6]) 180 } 181 // nwords <= 4 182 nwords := (nRemainBits + 31) / 32 183 // process 32 bits at a time for first complete words 184 for i := 0; i < nwords-1; i++ { 185 k64 = uint64(m.k0[i])<<32 | uint64(m.k0[i+1]) 186 w := binary.BigEndian.Uint32(m.x[i*4:]) 187 for j := 0; j < 32; j++ { 188 t64 ^= ^(uint64(w>>31) - 1) & k64 189 w <<= 1 190 k64 <<= 1 191 } 192 } 193 nRemainBits -= (nwords - 1) * 32 194 // current key word index, 0 <= kIdx <= 3 195 kIdx = nwords - 1 196 // process remaining bits less than 32 197 if nRemainBits > 0 { 198 k64 = uint64(m.k0[kIdx])<<32 | uint64(m.k0[kIdx+1]) 199 w := binary.BigEndian.Uint32(m.x[(nwords-1)*4:]) 200 for j := 0; j < nRemainBits; j++ { 201 t64 ^= ^(uint64(w>>31) - 1) & k64 202 w <<= 1 203 k64 <<= 1 204 } 205 // Reset for fianal computation 206 m.k0[kIdx] = uint32(k64 >> 32) // key[LENGTH] 207 m.k0[kIdx+1] = m.k0[kIdx+2] // Last key word 208 } 209 m.t = uint32(t64 >> 32) 210 } 211 m.t ^= m.k0[kIdx] 212 m.t ^= m.k0[kIdx+1] 213 214 var digest [4]byte 215 binary.BigEndian.PutUint32(digest[:], m.t) 216 return digest 217 } 218 219 // Finish this function hash nbits data in p and return mac value, after this function call, 220 // the hash state will be reset. 221 // In general, we will use byte level function, this is just for test/verify. 222 // nbits: number of bits to hash in p. 223 func (m *ZUC128Mac) Finish(p []byte, nbits int) []byte { 224 if len(p) < (nbits+7)/8 { 225 panic("invalid p length") 226 } 227 nbytes := nbits / 8 228 nRemainBits := nbits - nbytes*8 229 if nbytes > 0 { 230 m.Write(p[:nbytes]) 231 } 232 var b byte 233 if nRemainBits > 0 { 234 b = p[nbytes] 235 } 236 digest := m.checkSum(nRemainBits, b) 237 m.Reset() 238 return digest[:] 239 } 240 241 // Sum appends the current hash to in and returns the resulting slice. 242 // It does not change the underlying hash state. 243 func (m *ZUC128Mac) Sum(in []byte) []byte { 244 // Make a copy of d so that caller can keep writing and summing. 245 d0 := *m 246 hash := d0.checkSum(0, 0) 247 return append(in, hash[:]...) 248 }