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 -full -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.
11package md5
12
13import (
14	"crypto"
15	"hash"
16)
17
18func init() {
19	crypto.RegisterHash(crypto.MD5, New)
20}
21
22// The size of an MD5 checksum in bytes.
23const Size = 16
24
25// The blocksize of MD5 in bytes.
26const BlockSize = 64
27
28const (
29	chunk = 64
30	init0 = 0x67452301
31	init1 = 0xEFCDAB89
32	init2 = 0x98BADCFE
33	init3 = 0x10325476
34)
35
36// digest represents the partial evaluation of a checksum.
37type digest struct {
38	s   [4]uint32
39	x   [chunk]byte
40	nx  int
41	len uint64
42}
43
44func (d *digest) Reset() {
45	d.s[0] = init0
46	d.s[1] = init1
47	d.s[2] = init2
48	d.s[3] = init3
49	d.nx = 0
50	d.len = 0
51}
52
53// New returns a new hash.Hash computing the MD5 checksum.
54func New() hash.Hash {
55	d := new(digest)
56	d.Reset()
57	return d
58}
59
60func (d *digest) Size() int { return Size }
61
62func (d *digest) BlockSize() int { return BlockSize }
63
64func (d *digest) Write(p []byte) (nn int, err error) {
65	nn = len(p)
66	d.len += uint64(nn)
67	if d.nx > 0 {
68		n := copy(d.x[d.nx:], p)
69		d.nx += n
70		if d.nx == chunk {
71			block(d, d.x[:])
72			d.nx = 0
73		}
74		p = p[n:]
75	}
76	if len(p) >= chunk {
77		n := len(p) &^ (chunk - 1)
78		block(d, p[:n])
79		p = p[n:]
80	}
81	if len(p) > 0 {
82		d.nx = copy(d.x[:], p)
83	}
84	return
85}
86
87func (d0 *digest) Sum(in []byte) []byte {
88	// Make a copy of d0 so that caller can keep writing and summing.
89	d := *d0
90	hash := d.checkSum()
91	return append(in, hash[:]...)
92}
93
94func (d *digest) checkSum() [Size]byte {
95	// Padding. Add a 1 bit and 0 bits until 56 bytes mod 64.
96	len := d.len
97	var tmp [64]byte
98	tmp[0] = 0x80
99	if len%64 < 56 {
100		d.Write(tmp[0 : 56-len%64])
101	} else {
102		d.Write(tmp[0 : 64+56-len%64])
103	}
104
105	// Length in bits.
106	len <<= 3
107	for i := uint(0); i < 8; i++ {
108		tmp[i] = byte(len >> (8 * i))
109	}
110	d.Write(tmp[0:8])
111
112	if d.nx != 0 {
113		panic("d.nx != 0")
114	}
115
116	var digest [Size]byte
117	for i, s := range d.s {
118		digest[i*4] = byte(s)
119		digest[i*4+1] = byte(s >> 8)
120		digest[i*4+2] = byte(s >> 16)
121		digest[i*4+3] = byte(s >> 24)
122	}
123
124	return digest
125}
126
127// Sum returns the MD5 checksum of the data.
128func Sum(data []byte) [Size]byte {
129	var d digest
130	d.Reset()
131	d.Write(data)
132	return d.checkSum()
133}
134