Source file src/crypto/md5/md5.go

Documentation: crypto/md5

     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 = appendUint32(b, d.s[0])
    63  	b = appendUint32(b, d.s[1])
    64  	b = appendUint32(b, d.s[2])
    65  	b = 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 = 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 appendUint64(b []byte, x uint64) []byte {
    91  	var a [8]byte
    92  	binary.BigEndian.PutUint64(a[:], x)
    93  	return append(b, a[:]...)
    94  }
    95  
    96  func appendUint32(b []byte, x uint32) []byte {
    97  	var a [4]byte
    98  	binary.BigEndian.PutUint32(a[:], x)
    99  	return append(b, a[:]...)
   100  }
   101  
   102  func consumeUint64(b []byte) ([]byte, uint64) {
   103  	return b[8:], binary.BigEndian.Uint64(b[0:8])
   104  }
   105  
   106  func consumeUint32(b []byte) ([]byte, uint32) {
   107  	return b[4:], binary.BigEndian.Uint32(b[0:4])
   108  }
   109  
   110  // New returns a new hash.Hash computing the MD5 checksum. The Hash also
   111  // implements encoding.BinaryMarshaler and encoding.BinaryUnmarshaler to
   112  // marshal and unmarshal the internal state of the hash.
   113  func New() hash.Hash {
   114  	d := new(digest)
   115  	d.Reset()
   116  	return d
   117  }
   118  
   119  func (d *digest) Size() int { return Size }
   120  
   121  func (d *digest) BlockSize() int { return BlockSize }
   122  
   123  func (d *digest) Write(p []byte) (nn int, err error) {
   124  	// Note that we currently call block or blockGeneric
   125  	// directly (guarded using haveAsm) because this allows
   126  	// escape analysis to see that p and d don't escape.
   127  	nn = len(p)
   128  	d.len += uint64(nn)
   129  	if d.nx > 0 {
   130  		n := copy(d.x[d.nx:], p)
   131  		d.nx += n
   132  		if d.nx == BlockSize {
   133  			if haveAsm {
   134  				block(d, d.x[:])
   135  			} else {
   136  				blockGeneric(d, d.x[:])
   137  			}
   138  			d.nx = 0
   139  		}
   140  		p = p[n:]
   141  	}
   142  	if len(p) >= BlockSize {
   143  		n := len(p) &^ (BlockSize - 1)
   144  		if haveAsm {
   145  			block(d, p[:n])
   146  		} else {
   147  			blockGeneric(d, p[:n])
   148  		}
   149  		p = p[n:]
   150  	}
   151  	if len(p) > 0 {
   152  		d.nx = copy(d.x[:], p)
   153  	}
   154  	return
   155  }
   156  
   157  func (d *digest) Sum(in []byte) []byte {
   158  	// Make a copy of d so that caller can keep writing and summing.
   159  	d0 := *d
   160  	hash := d0.checkSum()
   161  	return append(in, hash[:]...)
   162  }
   163  
   164  func (d *digest) checkSum() [Size]byte {
   165  	// Append 0x80 to the end of the message and then append zeros
   166  	// until the length is a multiple of 56 bytes. Finally append
   167  	// 8 bytes representing the message length in bits.
   168  	//
   169  	// 1 byte end marker :: 0-63 padding bytes :: 8 byte length
   170  	tmp := [1 + 63 + 8]byte{0x80}
   171  	pad := (55 - d.len) % 64                             // calculate number of padding bytes
   172  	binary.LittleEndian.PutUint64(tmp[1+pad:], d.len<<3) // append length in bits
   173  	d.Write(tmp[:1+pad+8])
   174  
   175  	// The previous write ensures that a whole number of
   176  	// blocks (i.e. a multiple of 64 bytes) have been hashed.
   177  	if d.nx != 0 {
   178  		panic("d.nx != 0")
   179  	}
   180  
   181  	var digest [Size]byte
   182  	binary.LittleEndian.PutUint32(digest[0:], d.s[0])
   183  	binary.LittleEndian.PutUint32(digest[4:], d.s[1])
   184  	binary.LittleEndian.PutUint32(digest[8:], d.s[2])
   185  	binary.LittleEndian.PutUint32(digest[12:], d.s[3])
   186  	return digest
   187  }
   188  
   189  // Sum returns the MD5 checksum of the data.
   190  func Sum(data []byte) [Size]byte {
   191  	var d digest
   192  	d.Reset()
   193  	d.Write(data)
   194  	return d.checkSum()
   195  }
   196  

View as plain text