-
Notifications
You must be signed in to change notification settings - Fork 1
/
md5.go
118 lines (106 loc) · 2.83 KB
/
md5.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
// Copyright 2018 Philipp Brüll ([email protected])
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package crypt
import (
"bytes"
"crypto/md5"
)
// MD5Prefix defines the settings prefix for md5 hashes.
const MD5Prefix = "$1$"
const (
md5Rounds = 1000
md5MaxSaltSize = 8
)
func init() {
RegisterAlgorithm(MD5Prefix, md5Algorithm)
}
func md5Algorithm(password, settings string) (string, error) {
passwordBytes := []byte(password)
_, _, salt, _, err := DecodeSettings(settings)
if err != nil {
return "", err
}
saltBytes := []byte(salt)
if len(saltBytes) > md5MaxSaltSize {
saltBytes = saltBytes[:md5MaxSaltSize]
}
keyLen := len(passwordBytes)
h := md5.New()
// Compute sumB
h.Write(passwordBytes)
h.Write(saltBytes)
h.Write(passwordBytes)
sumB := h.Sum(nil)
// Compute sumA
h.Reset()
h.Write(passwordBytes)
h.Write([]byte(MD5Prefix))
h.Write(saltBytes)
h.Write(repeatByteSequence(sumB, keyLen))
// The original implementation now does something weird:
// For every 1 bit in the key, the first 0 is added to the buffer
// For every 0 bit, the first character of the key
// This does not seem to be what was intended but we have to follow this to
// be compatible.
for i := keyLen; i > 0; i >>= 1 {
if i%2 == 0 {
h.Write(passwordBytes[0:1])
} else {
h.Write([]byte{0})
}
}
sumA := h.Sum(nil)
cleanSensitiveData(sumB)
// In fear of password crackers here comes a quite long loop which just
// processes the output of the previous round again.
// We cannot ignore this here.
for i := 0; i < md5Rounds; i++ {
h.Reset()
// Add key or last result.
if i%2 != 0 {
h.Write(passwordBytes)
} else {
h.Write(sumA)
}
// Add salt for numbers not divisible by 3.
if i%3 != 0 {
h.Write(saltBytes)
}
// Add key for numbers not divisible by 7.
if i%7 != 0 {
h.Write(passwordBytes)
}
// Add key or last result.
if i&1 != 0 {
h.Write(sumA)
} else {
h.Write(passwordBytes)
}
copy(sumA, h.Sum(nil))
}
buf := bytes.Buffer{}
buf.Grow(len([]byte(MD5Prefix)) + len(saltBytes) + 1 + 22)
buf.Write([]byte(MD5Prefix))
buf.Write(saltBytes)
buf.WriteByte('$')
buf.Write(Encode24BitBase64([]byte{
sumA[12], sumA[6], sumA[0],
sumA[13], sumA[7], sumA[1],
sumA[14], sumA[8], sumA[2],
sumA[15], sumA[9], sumA[3],
sumA[5], sumA[10], sumA[4],
sumA[11],
}))
return buf.String(), nil
}