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  	"errors"
    16  	"hash"
    17  	"internal/byteorder"
    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  	return d.AppendBinary(make([]byte, 0, marshaledSize))
    61  }
    62  
    63  func (d *digest) AppendBinary(b []byte) ([]byte, error) {
    64  	b = append(b, magic...)
    65  	b = byteorder.BeAppendUint32(b, d.s[0])
    66  	b = byteorder.BeAppendUint32(b, d.s[1])
    67  	b = byteorder.BeAppendUint32(b, d.s[2])
    68  	b = byteorder.BeAppendUint32(b, d.s[3])
    69  	b = append(b, d.x[:d.nx]...)
    70  	b = append(b, make([]byte, len(d.x)-d.nx)...)
    71  	b = byteorder.BeAppendUint64(b, d.len)
    72  	return b, nil
    73  }
    74  
    75  func (d *digest) UnmarshalBinary(b []byte) error {
    76  	if len(b) < len(magic) || string(b[:len(magic)]) != magic {
    77  		return errors.New("crypto/md5: invalid hash state identifier")
    78  	}
    79  	if len(b) != marshaledSize {
    80  		return errors.New("crypto/md5: invalid hash state size")
    81  	}
    82  	b = b[len(magic):]
    83  	b, d.s[0] = consumeUint32(b)
    84  	b, d.s[1] = consumeUint32(b)
    85  	b, d.s[2] = consumeUint32(b)
    86  	b, d.s[3] = consumeUint32(b)
    87  	b = b[copy(d.x[:], b):]
    88  	b, d.len = consumeUint64(b)
    89  	d.nx = int(d.len % BlockSize)
    90  	return nil
    91  }
    92  
    93  func consumeUint64(b []byte) ([]byte, uint64) {
    94  	return b[8:], byteorder.BeUint64(b[0:8])
    95  }
    96  
    97  func consumeUint32(b []byte) ([]byte, uint32) {
    98  	return b[4:], byteorder.BeUint32(b[0:4])
    99  }
   100  
   101  // New returns a new [hash.Hash] computing the MD5 checksum. The Hash
   102  // also implements [encoding.BinaryMarshaler], [encoding.AppendBinary] and
   103  // [encoding.BinaryUnmarshaler] to marshal and unmarshal the internal
   104  // state of the hash.
   105  func New() hash.Hash {
   106  	d := new(digest)
   107  	d.Reset()
   108  	return d
   109  }
   110  
   111  func (d *digest) Size() int { return Size }
   112  
   113  func (d *digest) BlockSize() int { return BlockSize }
   114  
   115  func (d *digest) Write(p []byte) (nn int, err error) {
   116  	// Note that we currently call block or blockGeneric
   117  	// directly (guarded using haveAsm) because this allows
   118  	// escape analysis to see that p and d don't escape.
   119  	nn = len(p)
   120  	d.len += uint64(nn)
   121  	if d.nx > 0 {
   122  		n := copy(d.x[d.nx:], p)
   123  		d.nx += n
   124  		if d.nx == BlockSize {
   125  			if haveAsm {
   126  				block(d, d.x[:])
   127  			} else {
   128  				blockGeneric(d, d.x[:])
   129  			}
   130  			d.nx = 0
   131  		}
   132  		p = p[n:]
   133  	}
   134  	if len(p) >= BlockSize {
   135  		n := len(p) &^ (BlockSize - 1)
   136  		if haveAsm {
   137  			block(d, p[:n])
   138  		} else {
   139  			blockGeneric(d, p[:n])
   140  		}
   141  		p = p[n:]
   142  	}
   143  	if len(p) > 0 {
   144  		d.nx = copy(d.x[:], p)
   145  	}
   146  	return
   147  }
   148  
   149  func (d *digest) Sum(in []byte) []byte {
   150  	// Make a copy of d so that caller can keep writing and summing.
   151  	d0 := *d
   152  	hash := d0.checkSum()
   153  	return append(in, hash[:]...)
   154  }
   155  
   156  func (d *digest) checkSum() [Size]byte {
   157  	// Append 0x80 to the end of the message and then append zeros
   158  	// until the length is a multiple of 56 bytes. Finally append
   159  	// 8 bytes representing the message length in bits.
   160  	//
   161  	// 1 byte end marker :: 0-63 padding bytes :: 8 byte length
   162  	tmp := [1 + 63 + 8]byte{0x80}
   163  	pad := (55 - d.len) % 64                     // calculate number of padding bytes
   164  	byteorder.LePutUint64(tmp[1+pad:], d.len<<3) // append length in bits
   165  	d.Write(tmp[:1+pad+8])
   166  
   167  	// The previous write ensures that a whole number of
   168  	// blocks (i.e. a multiple of 64 bytes) have been hashed.
   169  	if d.nx != 0 {
   170  		panic("d.nx != 0")
   171  	}
   172  
   173  	var digest [Size]byte
   174  	byteorder.LePutUint32(digest[0:], d.s[0])
   175  	byteorder.LePutUint32(digest[4:], d.s[1])
   176  	byteorder.LePutUint32(digest[8:], d.s[2])
   177  	byteorder.LePutUint32(digest[12:], d.s[3])
   178  	return digest
   179  }
   180  
   181  // Sum returns the MD5 checksum of the data.
   182  func Sum(data []byte) [Size]byte {
   183  	var d digest
   184  	d.Reset()
   185  	d.Write(data)
   186  	return d.checkSum()
   187  }
   188  

View as plain text