github.com/zuoyebang/bitalosdb@v1.1.1-0.20240516111551-79a8c4d8ce20/internal/hash/md5.go (about) 1 // Copyright 2021 The Bitalosdb author(hustxrb@163.com) and other contributors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package hash 16 17 import ( 18 "encoding/binary" 19 "errors" 20 "hash" 21 ) 22 23 // Size The size of an MD5 checksum in bytes. 24 const Size = 16 25 26 // BlockSize The blocksize of MD5 in bytes. 27 const BlockSize = 64 28 29 const ( 30 init0 = 0x67452301 31 init1 = 0xEFCDAB89 32 init2 = 0x98BADCFE 33 init3 = 0x10325476 34 ) 35 36 // digest represents the partial evaluation of a checksum. 37 type digest struct { 38 s [4]uint32 39 x [BlockSize]byte 40 nx int 41 len uint64 42 } 43 44 func (d *digest) Reset() { 45 d.s[0] = init0 46 d.s[1] = init1 47 d.s[2] = init2 48 d.s[3] = init3 49 d.nx = 0 50 d.len = 0 51 } 52 53 const ( 54 magic = "md5\x01" 55 marshaledSize = len(magic) + 4*4 + BlockSize + 8 56 ) 57 58 func (d *digest) MarshalBinary() ([]byte, error) { 59 b := make([]byte, 0, marshaledSize) 60 b = append(b, magic...) 61 b = binary.BigEndian.AppendUint32(b, d.s[0]) 62 b = binary.BigEndian.AppendUint32(b, d.s[1]) 63 b = binary.BigEndian.AppendUint32(b, d.s[2]) 64 b = binary.BigEndian.AppendUint32(b, d.s[3]) 65 b = append(b, d.x[:d.nx]...) 66 b = b[:len(b)+len(d.x)-d.nx] // already zero 67 b = binary.BigEndian.AppendUint64(b, d.len) 68 return b, nil 69 } 70 71 func (d *digest) UnmarshalBinary(b []byte) error { 72 if len(b) < len(magic) || string(b[:len(magic)]) != magic { 73 return errors.New("crypto/md5: invalid hash state identifier") 74 } 75 if len(b) != marshaledSize { 76 return errors.New("crypto/md5: invalid hash state size") 77 } 78 b = b[len(magic):] 79 b, d.s[0] = consumeUint32(b) 80 b, d.s[1] = consumeUint32(b) 81 b, d.s[2] = consumeUint32(b) 82 b, d.s[3] = consumeUint32(b) 83 b = b[copy(d.x[:], b):] 84 b, d.len = consumeUint64(b) 85 d.nx = int(d.len % BlockSize) 86 return nil 87 } 88 89 func consumeUint64(b []byte) ([]byte, uint64) { 90 return b[8:], binary.BigEndian.Uint64(b[0:8]) 91 } 92 93 func consumeUint32(b []byte) ([]byte, uint32) { 94 return b[4:], binary.BigEndian.Uint32(b[0:4]) 95 } 96 97 // New returns a new hash.Hash computing the MD5 checksum. The Hash also 98 // implements [encoding.BinaryMarshaler] and [encoding.BinaryUnmarshaler] to 99 // marshal and unmarshal the internal state of the hash. 100 func New() hash.Hash { 101 d := new(digest) 102 d.Reset() 103 return d 104 } 105 106 func (d *digest) Size() int { return Size } 107 108 func (d *digest) BlockSize() int { return BlockSize } 109 110 func (d *digest) Write(p []byte) (nn int, err error) { 111 // Note that we currently call block or blockGeneric 112 // directly (guarded using haveAsm) because this allows 113 // escape analysis to see that p and d don't escape. 114 nn = len(p) 115 d.len += uint64(nn) 116 if d.nx > 0 { 117 n := copy(d.x[d.nx:], p) 118 d.nx += n 119 if d.nx == BlockSize { 120 if haveAsm { 121 block(d, d.x[:]) 122 } else { 123 blockGeneric(d, d.x[:]) 124 } 125 d.nx = 0 126 } 127 p = p[n:] 128 } 129 if len(p) >= BlockSize { 130 n := len(p) &^ (BlockSize - 1) 131 if haveAsm { 132 block(d, p[:n]) 133 } else { 134 blockGeneric(d, p[:n]) 135 } 136 p = p[n:] 137 } 138 if len(p) > 0 { 139 d.nx = copy(d.x[:], p) 140 } 141 return 142 } 143 144 func (d *digest) Sum(in []byte) []byte { 145 // Make a copy of d so that caller can keep writing and summing. 146 d0 := *d 147 hash := d0.checkSum() 148 return append(in, hash[:]...) 149 } 150 151 func (d *digest) checkSum() [Size]byte { 152 // Append 0x80 to the end of the message and then append zeros 153 // until the length is a multiple of 56 bytes. Finally append 154 // 8 bytes representing the message length in bits. 155 // 156 // 1 byte end marker :: 0-63 padding bytes :: 8 byte length 157 tmp := [1 + 63 + 8]byte{0x80} 158 pad := (55 - d.len) % 64 // calculate number of padding bytes 159 binary.LittleEndian.PutUint64(tmp[1+pad:], d.len<<3) // append length in bits 160 d.Write(tmp[:1+pad+8]) 161 162 // The previous write ensures that a whole number of 163 // blocks (i.e. a multiple of 64 bytes) have been hashed. 164 if d.nx != 0 { 165 panic("d.nx != 0") 166 } 167 168 var digest [Size]byte 169 binary.LittleEndian.PutUint32(digest[0:], d.s[0]) 170 binary.LittleEndian.PutUint32(digest[4:], d.s[1]) 171 binary.LittleEndian.PutUint32(digest[8:], d.s[2]) 172 binary.LittleEndian.PutUint32(digest[12:], d.s[3]) 173 return digest 174 } 175 176 // Sum returns the MD5 checksum of the data. 177 func Sum(data []byte) [Size]byte { 178 var d digest 179 d.Reset() 180 d.Write(data) 181 return d.checkSum() 182 } 183 184 func MD5(data []byte) (h []byte) { 185 hasher := New() 186 hasher.Write(data) 187 h = hasher.Sum(nil) 188 return h 189 } 190 191 func MD5Hash(data []byte) (h []byte, hi, lo uint64) { 192 hasher := New() 193 hasher.Write(data) 194 h = hasher.Sum(nil) 195 hi = binary.BigEndian.Uint64(h[:8]) 196 lo = binary.BigEndian.Uint64(h[8:16]) 197 return 198 } 199 200 //go:inline 201 func MD5Sum(p []byte) (h []byte, hi, lo uint64) { 202 var d = digest{s: [4]uint32{init0, init1, init2, init3}} 203 // Write 204 d.len += uint64(len(p)) 205 if d.nx > 0 { 206 n := copy(d.x[d.nx:], p) 207 d.nx += n 208 if d.nx == BlockSize { 209 if haveAsm { 210 block(&d, d.x[:]) 211 } else { 212 blockGeneric(&d, d.x[:]) 213 } 214 d.nx = 0 215 } 216 p = p[n:] 217 } 218 if len(p) >= BlockSize { 219 n := len(p) &^ (BlockSize - 1) 220 if haveAsm { 221 block(&d, p[:n]) 222 } else { 223 blockGeneric(&d, p[:n]) 224 } 225 p = p[n:] 226 } 227 if len(p) > 0 { 228 d.nx = copy(d.x[:], p) 229 } 230 231 // Checksum 232 tmp := [1 + 63 + 8]byte{0x80} 233 pad := (55 - d.len) % 64 // calculate number of padding bytes 234 binary.LittleEndian.PutUint64(tmp[1+pad:], d.len<<3) // append length in bits 235 // d.Write(tmp[:1+pad+8]) 236 tp := tmp[:1+pad+8] 237 d.len += uint64(len(tp)) 238 if d.nx > 0 { 239 n := copy(d.x[d.nx:], tp) 240 d.nx += n 241 if d.nx == BlockSize { 242 if haveAsm { 243 block(&d, d.x[:]) 244 } else { 245 blockGeneric(&d, d.x[:]) 246 } 247 d.nx = 0 248 } 249 tp = tp[n:] 250 } 251 if len(tp) >= BlockSize { 252 n := len(tp) &^ (BlockSize - 1) 253 if haveAsm { 254 block(&d, tp[:n]) 255 } else { 256 blockGeneric(&d, tp[:n]) 257 } 258 tp = tp[n:] 259 } 260 if len(tp) > 0 { 261 d.nx = copy(d.x[:], tp) 262 } 263 264 if d.nx != 0 { 265 panic("d.nx != 0") 266 } 267 268 h = make([]byte, Size) 269 binary.LittleEndian.PutUint32(h[0:], d.s[0]) 270 binary.LittleEndian.PutUint32(h[4:], d.s[1]) 271 binary.LittleEndian.PutUint32(h[8:], d.s[2]) 272 binary.LittleEndian.PutUint32(h[12:], d.s[3]) 273 274 hi = binary.BigEndian.Uint64(h[:8]) 275 lo = binary.BigEndian.Uint64(h[8:16]) 276 return 277 } 278 279 //go:inline 280 func MD5Uint64(p []byte) (hi, lo uint64) { 281 var d = digest{s: [4]uint32{init0, init1, init2, init3}} 282 // Write 283 d.len += uint64(len(p)) 284 if d.nx > 0 { 285 n := copy(d.x[d.nx:], p) 286 d.nx += n 287 if d.nx == BlockSize { 288 if haveAsm { 289 block(&d, d.x[:]) 290 } else { 291 blockGeneric(&d, d.x[:]) 292 } 293 d.nx = 0 294 } 295 p = p[n:] 296 } 297 if len(p) >= BlockSize { 298 n := len(p) &^ (BlockSize - 1) 299 if haveAsm { 300 block(&d, p[:n]) 301 } else { 302 blockGeneric(&d, p[:n]) 303 } 304 p = p[n:] 305 } 306 if len(p) > 0 { 307 d.nx = copy(d.x[:], p) 308 } 309 310 // Checksum 311 tmp := [1 + 63 + 8]byte{0x80} 312 pad := (55 - d.len) % 64 // calculate number of padding bytes 313 binary.LittleEndian.PutUint64(tmp[1+pad:], d.len<<3) // append length in bits 314 // d.Write(tmp[:1+pad+8]) 315 tp := tmp[:1+pad+8] 316 d.len += uint64(len(tp)) 317 if d.nx > 0 { 318 n := copy(d.x[d.nx:], tp) 319 d.nx += n 320 if d.nx == BlockSize { 321 if haveAsm { 322 block(&d, d.x[:]) 323 } else { 324 blockGeneric(&d, d.x[:]) 325 } 326 d.nx = 0 327 } 328 tp = tp[n:] 329 } 330 if len(tp) >= BlockSize { 331 n := len(tp) &^ (BlockSize - 1) 332 if haveAsm { 333 block(&d, tp[:n]) 334 } else { 335 blockGeneric(&d, tp[:n]) 336 } 337 tp = tp[n:] 338 } 339 if len(tp) > 0 { 340 d.nx = copy(d.x[:], tp) 341 } 342 343 if d.nx != 0 { 344 panic("d.nx != 0") 345 } 346 347 hi = uint64(d.s[0])<<32 | uint64(d.s[1]) 348 lo = uint64(d.s[2])<<32 | uint64(d.s[3]) 349 return 350 } 351 352 //go:inline 353 func MD5HL(h []byte) (hi, lo uint64) { 354 hi = binary.BigEndian.Uint64(h[0:8]) 355 lo = binary.BigEndian.Uint64(h[8:16]) 356 return hi, lo 357 }