md5.mx raw
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 "crypto/internal/fips140only"
16 "errors"
17 "hash"
18 "internal/byteorder"
19 )
20
21 func init() {
22 crypto.RegisterHash(crypto.MD5, New)
23 }
24
25 // The size of an MD5 checksum in bytes.
26 const Size = 16
27
28 // The blocksize of MD5 in bytes.
29 const BlockSize = 64
30
31 // The maximum number of bytes that can be passed to block(). The limit exists
32 // because implementations that rely on assembly routines are not preemptible.
33 const maxAsmIters = 1024
34 const maxAsmSize = BlockSize * maxAsmIters // 64KiB
35
36 const (
37 init0 = 0x67452301
38 init1 = 0xEFCDAB89
39 init2 = 0x98BADCFE
40 init3 = 0x10325476
41 )
42
43 // digest represents the partial evaluation of a checksum.
44 type digest struct {
45 s [4]uint32
46 x [BlockSize]byte
47 nx int
48 len uint64
49 }
50
51 func (d *digest) Reset() {
52 d.s[0] = init0
53 d.s[1] = init1
54 d.s[2] = init2
55 d.s[3] = init3
56 d.nx = 0
57 d.len = 0
58 }
59
60 const (
61 magic = "md5\x01"
62 marshaledSize = len(magic) + 4*4 + BlockSize + 8
63 )
64
65 func (d *digest) MarshalBinary() ([]byte, error) {
66 return d.AppendBinary([]byte{:0:marshaledSize})
67 }
68
69 func (d *digest) AppendBinary(b []byte) ([]byte, error) {
70 b = append(b, magic...)
71 b = byteorder.BEAppendUint32(b, d.s[0])
72 b = byteorder.BEAppendUint32(b, d.s[1])
73 b = byteorder.BEAppendUint32(b, d.s[2])
74 b = byteorder.BEAppendUint32(b, d.s[3])
75 b = append(b, d.x[:d.nx]...)
76 b = append(b, []byte{:len(d.x)-d.nx}...)
77 b = byteorder.BEAppendUint64(b, d.len)
78 return b, nil
79 }
80
81 func (d *digest) UnmarshalBinary(b []byte) error {
82 if len(b) < len(magic) || b[:len(magic)] != magic {
83 return errors.New("crypto/md5: invalid hash state identifier")
84 }
85 if len(b) != marshaledSize {
86 return errors.New("crypto/md5: invalid hash state size")
87 }
88 b = b[len(magic):]
89 b, d.s[0] = consumeUint32(b)
90 b, d.s[1] = consumeUint32(b)
91 b, d.s[2] = consumeUint32(b)
92 b, d.s[3] = consumeUint32(b)
93 b = b[copy(d.x[:], b):]
94 b, d.len = consumeUint64(b)
95 d.nx = int(d.len % BlockSize)
96 return nil
97 }
98
99 func consumeUint64(b []byte) ([]byte, uint64) {
100 return b[8:], byteorder.BEUint64(b[0:8])
101 }
102
103 func consumeUint32(b []byte) ([]byte, uint32) {
104 return b[4:], byteorder.BEUint32(b[0:4])
105 }
106
107 func (d *digest) Clone() (hash.Cloner, error) {
108 r := *d
109 return &r, nil
110 }
111
112 // New returns a new [hash.Hash] computing the MD5 checksum. The Hash
113 // also implements [encoding.BinaryMarshaler], [encoding.BinaryAppender] and
114 // [encoding.BinaryUnmarshaler] to marshal and unmarshal the internal
115 // state of the hash.
116 func New() hash.Hash {
117 d := &digest{}
118 d.Reset()
119 return d
120 }
121
122 func (d *digest) Size() int { return Size }
123
124 func (d *digest) BlockSize() int { return BlockSize }
125
126 func (d *digest) Write(p []byte) (nn int, err error) {
127 if fips140only.Enabled {
128 return 0, errors.New("crypto/md5: use of MD5 is not allowed in FIPS 140-only mode")
129 }
130 // Note that we currently call block or blockGeneric
131 // directly (guarded using haveAsm) because this allows
132 // escape analysis to see that p and d don't escape.
133 nn = len(p)
134 d.len += uint64(nn)
135 if d.nx > 0 {
136 n := copy(d.x[d.nx:], p)
137 d.nx += n
138 if d.nx == BlockSize {
139 if haveAsm {
140 block(d, d.x[:])
141 } else {
142 blockGeneric(d, d.x[:])
143 }
144 d.nx = 0
145 }
146 p = p[n:]
147 }
148 if len(p) >= BlockSize {
149 n := len(p) &^ (BlockSize - 1)
150 if haveAsm {
151 for n > maxAsmSize {
152 block(d, p[:maxAsmSize])
153 p = p[maxAsmSize:]
154 n -= maxAsmSize
155 }
156 block(d, p[:n])
157 } else {
158 blockGeneric(d, p[:n])
159 }
160 p = p[n:]
161 }
162 if len(p) > 0 {
163 d.nx = copy(d.x[:], p)
164 }
165 return
166 }
167
168 func (d *digest) Sum(in []byte) []byte {
169 // Make a copy of d so that caller can keep writing and summing.
170 d0 := *d
171 hash := d0.checkSum()
172 return append(in, hash[:]...)
173 }
174
175 func (d *digest) checkSum() [Size]byte {
176 if fips140only.Enabled {
177 panic("crypto/md5: use of MD5 is not allowed in FIPS 140-only mode")
178 }
179
180 // Append 0x80 to the end of the message and then append zeros
181 // until the length is a multiple of 56 bytes. Finally append
182 // 8 bytes representing the message length in bits.
183 //
184 // 1 byte end marker :: 0-63 padding bytes :: 8 byte length
185 tmp := [1 + 63 + 8]byte{0x80}
186 pad := (55 - d.len) % 64 // calculate number of padding bytes
187 byteorder.LEPutUint64(tmp[1+pad:], d.len<<3) // append length in bits
188 d.Write(tmp[:1+pad+8])
189
190 // The previous write ensures that a whole number of
191 // blocks (i.e. a multiple of 64 bytes) have been hashed.
192 if d.nx != 0 {
193 panic("d.nx != 0")
194 }
195
196 var digest [Size]byte
197 byteorder.LEPutUint32(digest[0:], d.s[0])
198 byteorder.LEPutUint32(digest[4:], d.s[1])
199 byteorder.LEPutUint32(digest[8:], d.s[2])
200 byteorder.LEPutUint32(digest[12:], d.s[3])
201 return digest
202 }
203
204 // Sum returns the MD5 checksum of the data.
205 func Sum(data []byte) [Size]byte {
206 var d digest
207 d.Reset()
208 d.Write(data)
209 return d.checkSum()
210 }
211