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