Source file src/crypto/md5/md5.go

     1  // Copyright 2009 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  //go:generate go run gen.go -output md5block.go
     6  
     7  // Package md5 implements the MD5 hash algorithm as defined in RFC 1321.
     8  //
     9  // MD5 is cryptographically broken and should not be used for secure
    10  // applications.
    11  package md5
    12  
    13  import (
    14  	"crypto"
    15  	"encoding/binary"
    16  	"errors"
    17  	"hash"
    18  )
    19  
    20  func init() {
    21  	crypto.RegisterHash(crypto.MD5, New)
    22  }
    23  
    24  // The size of an MD5 checksum in bytes.
    25  const Size = 16
    26  
    27  // The blocksize of MD5 in bytes.
    28  const BlockSize = 64
    29  
    30  const (
    31  	init0 = 0x67452301
    32  	init1 = 0xEFCDAB89
    33  	init2 = 0x98BADCFE
    34  	init3 = 0x10325476
    35  )
    36  
    37  // digest represents the partial evaluation of a checksum.
    38  type digest struct {
    39  	s   [4]uint32
    40  	x   [BlockSize]byte
    41  	nx  int
    42  	len uint64
    43  }
    44  
    45  func (d *digest) Reset() {
    46  	d.s[0] = init0
    47  	d.s[1] = init1
    48  	d.s[2] = init2
    49  	d.s[3] = init3
    50  	d.nx = 0
    51  	d.len = 0
    52  }
    53  
    54  const (
    55  	magic         = "md5\x01"
    56  	marshaledSize = len(magic) + 4*4 + BlockSize + 8
    57  )
    58  
    59  func (d *digest) MarshalBinary() ([]byte, error) {
    60  	b := make([]byte, 0, marshaledSize)
    61  	b = append(b, magic...)
    62  	b = binary.BigEndian.AppendUint32(b, d.s[0])
    63  	b = binary.BigEndian.AppendUint32(b, d.s[1])
    64  	b = binary.BigEndian.AppendUint32(b, d.s[2])
    65  	b = binary.BigEndian.AppendUint32(b, d.s[3])
    66  	b = append(b, d.x[:d.nx]...)
    67  	b = b[:len(b)+len(d.x)-d.nx] // already zero
    68  	b = binary.BigEndian.AppendUint64(b, d.len)
    69  	return b, nil
    70  }
    71  
    72  func (d *digest) UnmarshalBinary(b []byte) error {
    73  	if len(b) < len(magic) || string(b[:len(magic)]) != magic {
    74  		return errors.New("crypto/md5: invalid hash state identifier")
    75  	}
    76  	if len(b) != marshaledSize {
    77  		return errors.New("crypto/md5: invalid hash state size")
    78  	}
    79  	b = b[len(magic):]
    80  	b, d.s[0] = consumeUint32(b)
    81  	b, d.s[1] = consumeUint32(b)
    82  	b, d.s[2] = consumeUint32(b)
    83  	b, d.s[3] = consumeUint32(b)
    84  	b = b[copy(d.x[:], b):]
    85  	b, d.len = consumeUint64(b)
    86  	d.nx = int(d.len % BlockSize)
    87  	return nil
    88  }
    89  
    90  func consumeUint64(b []byte) ([]byte, uint64) {
    91  	return b[8:], binary.BigEndian.Uint64(b[0:8])
    92  }
    93  
    94  func consumeUint32(b []byte) ([]byte, uint32) {
    95  	return b[4:], binary.BigEndian.Uint32(b[0:4])
    96  }
    97  
    98  // New returns a new hash.Hash computing the MD5 checksum. The Hash also
    99  // implements [encoding.BinaryMarshaler] and [encoding.BinaryUnmarshaler] to
   100  // marshal and unmarshal the internal state of the hash.
   101  func New() hash.Hash {
   102  	d := new(digest)
   103  	d.Reset()
   104  	return d
   105  }
   106  
   107  func (d *digest) Size() int { return Size }
   108  
   109  func (d *digest) BlockSize() int { return BlockSize }
   110  
   111  func (d *digest) Write(p []byte) (nn int, err error) {
   112  	// Note that we currently call block or blockGeneric
   113  	// directly (guarded using haveAsm) because this allows
   114  	// escape analysis to see that p and d don't escape.
   115  	nn = len(p)
   116  	d.len += uint64(nn)
   117  	if d.nx > 0 {
   118  		n := copy(d.x[d.nx:], p)
   119  		d.nx += n
   120  		if d.nx == BlockSize {
   121  			if haveAsm {
   122  				block(d, d.x[:])
   123  			} else {
   124  				blockGeneric(d, d.x[:])
   125  			}
   126  			d.nx = 0
   127  		}
   128  		p = p[n:]
   129  	}
   130  	if len(p) >= BlockSize {
   131  		n := len(p) &^ (BlockSize - 1)
   132  		if haveAsm {
   133  			block(d, p[:n])
   134  		} else {
   135  			blockGeneric(d, p[:n])
   136  		}
   137  		p = p[n:]
   138  	}
   139  	if len(p) > 0 {
   140  		d.nx = copy(d.x[:], p)
   141  	}
   142  	return
   143  }
   144  
   145  func (d *digest) Sum(in []byte) []byte {
   146  	// Make a copy of d so that caller can keep writing and summing.
   147  	d0 := *d
   148  	hash := d0.checkSum()
   149  	return append(in, hash[:]...)
   150  }
   151  
   152  func (d *digest) checkSum() [Size]byte {
   153  	// Append 0x80 to the end of the message and then append zeros
   154  	// until the length is a multiple of 56 bytes. Finally append
   155  	// 8 bytes representing the message length in bits.
   156  	//
   157  	// 1 byte end marker :: 0-63 padding bytes :: 8 byte length
   158  	tmp := [1 + 63 + 8]byte{0x80}
   159  	pad := (55 - d.len) % 64                             // calculate number of padding bytes
   160  	binary.LittleEndian.PutUint64(tmp[1+pad:], d.len<<3) // append length in bits
   161  	d.Write(tmp[:1+pad+8])
   162  
   163  	// The previous write ensures that a whole number of
   164  	// blocks (i.e. a multiple of 64 bytes) have been hashed.
   165  	if d.nx != 0 {
   166  		panic("d.nx != 0")
   167  	}
   168  
   169  	var digest [Size]byte
   170  	binary.LittleEndian.PutUint32(digest[0:], d.s[0])
   171  	binary.LittleEndian.PutUint32(digest[4:], d.s[1])
   172  	binary.LittleEndian.PutUint32(digest[8:], d.s[2])
   173  	binary.LittleEndian.PutUint32(digest[12:], d.s[3])
   174  	return digest
   175  }
   176  
   177  // Sum returns the MD5 checksum of the data.
   178  func Sum(data []byte) [Size]byte {
   179  	var d digest
   180  	d.Reset()
   181  	d.Write(data)
   182  	return d.checkSum()
   183  }
   184  

View as plain text