...

Source file src/pkg/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 = 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