|  | // Copyright 2009 The Go Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style | 
|  | // license that can be found in the LICENSE file. | 
|  |  | 
|  | //go:generate go run gen.go -output md5block.go | 
|  |  | 
|  | // Package md5 implements the MD5 hash algorithm as defined in RFC 1321. | 
|  | // | 
|  | // MD5 is cryptographically broken and should not be used for secure | 
|  | // applications. | 
|  | package md5 | 
|  |  | 
|  | import ( | 
|  | "crypto" | 
|  | "encoding/binary" | 
|  | "errors" | 
|  | "hash" | 
|  | ) | 
|  |  | 
|  | func init() { | 
|  | crypto.RegisterHash(crypto.MD5, New) | 
|  | } | 
|  |  | 
|  | // The size of an MD5 checksum in bytes. | 
|  | const Size = 16 | 
|  |  | 
|  | // The blocksize of MD5 in bytes. | 
|  | const BlockSize = 64 | 
|  |  | 
|  | const ( | 
|  | init0 = 0x67452301 | 
|  | init1 = 0xEFCDAB89 | 
|  | init2 = 0x98BADCFE | 
|  | init3 = 0x10325476 | 
|  | ) | 
|  |  | 
|  | // digest represents the partial evaluation of a checksum. | 
|  | type digest struct { | 
|  | s   [4]uint32 | 
|  | x   [BlockSize]byte | 
|  | nx  int | 
|  | len uint64 | 
|  | } | 
|  |  | 
|  | func (d *digest) Reset() { | 
|  | d.s[0] = init0 | 
|  | d.s[1] = init1 | 
|  | d.s[2] = init2 | 
|  | d.s[3] = init3 | 
|  | d.nx = 0 | 
|  | d.len = 0 | 
|  | } | 
|  |  | 
|  | const ( | 
|  | magic         = "md5\x01" | 
|  | marshaledSize = len(magic) + 4*4 + BlockSize + 8 | 
|  | ) | 
|  |  | 
|  | func (d *digest) MarshalBinary() ([]byte, error) { | 
|  | b := make([]byte, 0, marshaledSize) | 
|  | b = append(b, magic...) | 
|  | b = appendUint32(b, d.s[0]) | 
|  | b = appendUint32(b, d.s[1]) | 
|  | b = appendUint32(b, d.s[2]) | 
|  | b = appendUint32(b, d.s[3]) | 
|  | b = append(b, d.x[:d.nx]...) | 
|  | b = b[:len(b)+len(d.x)-d.nx] // already zero | 
|  | b = appendUint64(b, d.len) | 
|  | return b, nil | 
|  | } | 
|  |  | 
|  | func (d *digest) UnmarshalBinary(b []byte) error { | 
|  | if len(b) < len(magic) || string(b[:len(magic)]) != magic { | 
|  | return errors.New("crypto/md5: invalid hash state identifier") | 
|  | } | 
|  | if len(b) != marshaledSize { | 
|  | return errors.New("crypto/md5: invalid hash state size") | 
|  | } | 
|  | b = b[len(magic):] | 
|  | b, d.s[0] = consumeUint32(b) | 
|  | b, d.s[1] = consumeUint32(b) | 
|  | b, d.s[2] = consumeUint32(b) | 
|  | b, d.s[3] = consumeUint32(b) | 
|  | b = b[copy(d.x[:], b):] | 
|  | b, d.len = consumeUint64(b) | 
|  | d.nx = int(d.len % BlockSize) | 
|  | return nil | 
|  | } | 
|  |  | 
|  | func appendUint64(b []byte, x uint64) []byte { | 
|  | var a [8]byte | 
|  | binary.BigEndian.PutUint64(a[:], x) | 
|  | return append(b, a[:]...) | 
|  | } | 
|  |  | 
|  | func appendUint32(b []byte, x uint32) []byte { | 
|  | var a [4]byte | 
|  | binary.BigEndian.PutUint32(a[:], x) | 
|  | return append(b, a[:]...) | 
|  | } | 
|  |  | 
|  | func consumeUint64(b []byte) ([]byte, uint64) { | 
|  | return b[8:], binary.BigEndian.Uint64(b[0:8]) | 
|  | } | 
|  |  | 
|  | func consumeUint32(b []byte) ([]byte, uint32) { | 
|  | return b[4:], binary.BigEndian.Uint32(b[0:4]) | 
|  | } | 
|  |  | 
|  | // New returns a new hash.Hash computing the MD5 checksum. The Hash also | 
|  | // implements encoding.BinaryMarshaler and encoding.BinaryUnmarshaler to | 
|  | // marshal and unmarshal the internal state of the hash. | 
|  | func New() hash.Hash { | 
|  | d := new(digest) | 
|  | d.Reset() | 
|  | return d | 
|  | } | 
|  |  | 
|  | func (d *digest) Size() int { return Size } | 
|  |  | 
|  | func (d *digest) BlockSize() int { return BlockSize } | 
|  |  | 
|  | func (d *digest) Write(p []byte) (nn int, err error) { | 
|  | // Note that we currently call block or blockGeneric | 
|  | // directly (guarded using haveAsm) because this allows | 
|  | // escape analysis to see that p and d don't escape. | 
|  | nn = len(p) | 
|  | d.len += uint64(nn) | 
|  | if d.nx > 0 { | 
|  | n := copy(d.x[d.nx:], p) | 
|  | d.nx += n | 
|  | if d.nx == BlockSize { | 
|  | if haveAsm { | 
|  | block(d, d.x[:]) | 
|  | } else { | 
|  | blockGeneric(d, d.x[:]) | 
|  | } | 
|  | d.nx = 0 | 
|  | } | 
|  | p = p[n:] | 
|  | } | 
|  | if len(p) >= BlockSize { | 
|  | n := len(p) &^ (BlockSize - 1) | 
|  | if haveAsm { | 
|  | block(d, p[:n]) | 
|  | } else { | 
|  | blockGeneric(d, p[:n]) | 
|  | } | 
|  | p = p[n:] | 
|  | } | 
|  | if len(p) > 0 { | 
|  | d.nx = copy(d.x[:], p) | 
|  | } | 
|  | return | 
|  | } | 
|  |  | 
|  | func (d *digest) Sum(in []byte) []byte { | 
|  | // Make a copy of d so that caller can keep writing and summing. | 
|  | d0 := *d | 
|  | hash := d0.checkSum() | 
|  | return append(in, hash[:]...) | 
|  | } | 
|  |  | 
|  | func (d *digest) checkSum() [Size]byte { | 
|  | // Append 0x80 to the end of the message and then append zeros | 
|  | // until the length is a multiple of 56 bytes. Finally append | 
|  | // 8 bytes representing the message length in bits. | 
|  | // | 
|  | // 1 byte end marker :: 0-63 padding bytes :: 8 byte length | 
|  | tmp := [1 + 63 + 8]byte{0x80} | 
|  | pad := (55 - d.len) % 64                             // calculate number of padding bytes | 
|  | binary.LittleEndian.PutUint64(tmp[1+pad:], d.len<<3) // append length in bits | 
|  | d.Write(tmp[:1+pad+8]) | 
|  |  | 
|  | // The previous write ensures that a whole number of | 
|  | // blocks (i.e. a multiple of 64 bytes) have been hashed. | 
|  | if d.nx != 0 { | 
|  | panic("d.nx != 0") | 
|  | } | 
|  |  | 
|  | var digest [Size]byte | 
|  | binary.LittleEndian.PutUint32(digest[0:], d.s[0]) | 
|  | binary.LittleEndian.PutUint32(digest[4:], d.s[1]) | 
|  | binary.LittleEndian.PutUint32(digest[8:], d.s[2]) | 
|  | binary.LittleEndian.PutUint32(digest[12:], d.s[3]) | 
|  | return digest | 
|  | } | 
|  |  | 
|  | // Sum returns the MD5 checksum of the data. | 
|  | func Sum(data []byte) [Size]byte { | 
|  | var d digest | 
|  | d.Reset() | 
|  | d.Write(data) | 
|  | return d.checkSum() | 
|  | } |