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  }