1// (C) Copyright 2012, Jeramey Crawford <jeramey@antihe.ro>. All
2// rights reserved. Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// Package md5_crypt implements the standard Unix MD5-crypt algorithm created by
6// Poul-Henning Kamp for FreeBSD.
7package md5_crypt
8
9import (
10	"bytes"
11	"crypto/md5"
12	"crypto/subtle"
13
14	"github.com/GehirnInc/crypt"
15	"github.com/GehirnInc/crypt/common"
16	"github.com/GehirnInc/crypt/internal"
17)
18
19func init() {
20	crypt.RegisterCrypt(crypt.MD5, New, MagicPrefix)
21}
22
23// NOTE: Cisco IOS only allows salts of length 4.
24
25const (
26	MagicPrefix   = "$1$"
27	SaltLenMin    = 1 // Real minimum is 0, but that isn't useful.
28	SaltLenMax    = 8
29	RoundsDefault = 1000
30)
31
32type crypter struct{ Salt common.Salt }
33
34// New returns a new crypt.Crypter computing the MD5-crypt password hashing.
35func New() crypt.Crypter {
36	return &crypter{
37		common.Salt{
38			MagicPrefix:   []byte(MagicPrefix),
39			SaltLenMin:    SaltLenMin,
40			SaltLenMax:    SaltLenMax,
41			RoundsDefault: RoundsDefault,
42		},
43	}
44}
45
46func (c *crypter) Generate(key, salt []byte) (result string, err error) {
47	if len(salt) == 0 {
48		salt = c.Salt.Generate(SaltLenMax)
49	}
50	salt, _, _, _, err = c.Salt.Decode(salt)
51	if err != nil {
52		return
53	}
54
55	keyLen := len(key)
56	h := md5.New()
57
58	// Compute sumB
59	h.Write(key)
60	h.Write(salt)
61	h.Write(key)
62	sumB := h.Sum(nil)
63
64	// Compute sumA
65	h.Reset()
66	h.Write(key)
67	h.Write(c.Salt.MagicPrefix)
68	h.Write(salt)
69	h.Write(internal.RepeatByteSequence(sumB, keyLen))
70	// The original implementation now does something weird:
71	//   For every 1 bit in the key, the first 0 is added to the buffer
72	//   For every 0 bit, the first character of the key
73	// This does not seem to be what was intended but we have to follow this to
74	// be compatible.
75	for i := keyLen; i > 0; i >>= 1 {
76		if i%2 == 0 {
77			h.Write(key[0:1])
78		} else {
79			h.Write([]byte{0})
80		}
81	}
82	sumA := h.Sum(nil)
83	internal.CleanSensitiveData(sumB)
84
85	// In fear of password crackers here comes a quite long loop which just
86	// processes the output of the previous round again.
87	// We cannot ignore this here.
88	for i := 0; i < RoundsDefault; i++ {
89		h.Reset()
90
91		// Add key or last result.
92		if i%2 != 0 {
93			h.Write(key)
94		} else {
95			h.Write(sumA)
96		}
97		// Add salt for numbers not divisible by 3.
98		if i%3 != 0 {
99			h.Write(salt)
100		}
101		// Add key for numbers not divisible by 7.
102		if i%7 != 0 {
103			h.Write(key)
104		}
105		// Add key or last result.
106		if i&1 != 0 {
107			h.Write(sumA)
108		} else {
109			h.Write(key)
110		}
111		copy(sumA, h.Sum(nil))
112	}
113
114	buf := bytes.Buffer{}
115	buf.Grow(len(c.Salt.MagicPrefix) + len(salt) + 1 + 22)
116	buf.Write(c.Salt.MagicPrefix)
117	buf.Write(salt)
118	buf.WriteByte('$')
119	buf.Write(common.Base64_24Bit([]byte{
120		sumA[12], sumA[6], sumA[0],
121		sumA[13], sumA[7], sumA[1],
122		sumA[14], sumA[8], sumA[2],
123		sumA[15], sumA[9], sumA[3],
124		sumA[5], sumA[10], sumA[4],
125		sumA[11],
126	}))
127	return buf.String(), nil
128}
129
130func (c *crypter) Verify(hashedKey string, key []byte) error {
131	newHash, err := c.Generate(key, []byte(hashedKey))
132	if err != nil {
133		return err
134	}
135	if subtle.ConstantTimeCompare([]byte(newHash), []byte(hashedKey)) != 1 {
136		return crypt.ErrKeyMismatch
137	}
138	return nil
139}
140
141func (c *crypter) Cost(hashedKey string) (int, error) { return RoundsDefault, nil }
142
143func (c *crypter) SetSalt(salt common.Salt) { c.Salt = salt }
144