diff options
Diffstat (limited to 'src/crypto/md5/md5.go')
-rw-r--r-- | src/crypto/md5/md5.go | 183 |
1 files changed, 183 insertions, 0 deletions
diff --git a/src/crypto/md5/md5.go b/src/crypto/md5/md5.go new file mode 100644 index 0000000..ccee4ea --- /dev/null +++ b/src/crypto/md5/md5.go @@ -0,0 +1,183 @@ +// 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 = binary.BigEndian.AppendUint32(b, d.s[0]) + b = binary.BigEndian.AppendUint32(b, d.s[1]) + b = binary.BigEndian.AppendUint32(b, d.s[2]) + b = binary.BigEndian.AppendUint32(b, d.s[3]) + b = append(b, d.x[:d.nx]...) + b = b[:len(b)+len(d.x)-d.nx] // already zero + b = binary.BigEndian.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 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() +} |