github.com/avenga/couper@v1.12.2/accesscontrol/basic_auth_algos.go (about) 1 package accesscontrol 2 3 import ( 4 "bytes" 5 "crypto/md5" 6 "crypto/subtle" 7 "strings" 8 9 "golang.org/x/crypto/bcrypt" 10 ) 11 12 const ( 13 pwdPrefixApr1 = "$apr1$" 14 pwdPrefixBcrypt2a = "$2a$" 15 pwdPrefixBcrypt2b = "$2b$" 16 pwdPrefixBcrypt2x = "$2x$" 17 pwdPrefixBcrypt2y = "$2y$" 18 pwdPrefixMD5 = "$1$" 19 ) 20 21 const ( 22 pwdTypeUnknown = iota 23 pwdTypeApr1 24 pwdTypeBcrypt 25 pwdTypeMD5 26 ) 27 28 const ( 29 aprCharacters = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" 30 aprMd5DigestSize = 16 31 aprMuddleRounds = 1000 32 ) 33 34 var pwdPrefixes = map[string]int{ 35 pwdPrefixApr1: pwdTypeApr1, 36 pwdPrefixBcrypt2a: pwdTypeBcrypt, 37 pwdPrefixBcrypt2b: pwdTypeBcrypt, 38 pwdPrefixBcrypt2x: pwdTypeBcrypt, 39 pwdPrefixBcrypt2y: pwdTypeBcrypt, 40 pwdPrefixMD5: pwdTypeMD5, 41 } 42 43 type htData map[string]pwd 44 45 type pwd struct { 46 pwdOrig []byte 47 pwdPrefix string 48 pwdSalt string 49 pwdType int 50 } 51 52 func getPwdType(pass string) int { 53 for p, t := range pwdPrefixes { 54 if strings.HasPrefix(pass, p) { 55 return t 56 } 57 } 58 59 return pwdTypeUnknown 60 } 61 62 func validateAccessData(plainUser, plainPass string, data htData) bool { 63 for user, pass := range data { 64 if user == plainUser { 65 switch pass.pwdType { 66 case pwdTypeApr1: 67 fallthrough 68 case pwdTypeMD5: 69 if subtle.ConstantTimeCompare(apr1MD5(plainPass, pass.pwdSalt, pass.pwdPrefix), pass.pwdOrig) == 1 { 70 return true 71 } 72 case pwdTypeBcrypt: 73 if err := bcrypt.CompareHashAndPassword(pass.pwdOrig, []byte(plainPass)); err == nil { 74 return true 75 } 76 } 77 } 78 } 79 80 return false 81 } 82 83 func apr1MD5(pass, salt, pref string) []byte { 84 var passLen int = len(pass) 85 86 h := md5.New() 87 88 h.Write([]byte(pass + salt + pass)) 89 bin := h.Sum(nil) 90 91 h.Reset() 92 h.Write([]byte(pass + pref + salt)) 93 94 for i := passLen; i > 0; i -= aprMd5DigestSize { 95 if i > aprMd5DigestSize { 96 h.Write(bin[0:aprMd5DigestSize]) 97 } else { 98 h.Write(bin[0:i]) 99 } 100 } 101 102 for i := passLen; i > 0; i >>= 1 { 103 if (i & 1) == 1 { 104 h.Write([]byte{0}) 105 } else { 106 h.Write([]byte(pass[0:1])) 107 } 108 } 109 110 sum := h.Sum(nil) 111 112 for i := 0; i < aprMuddleRounds; i++ { 113 h.Reset() 114 115 if (i & 1) == 1 { 116 h.Write([]byte(pass)) 117 } else { 118 h.Write(sum) 119 } 120 121 if (i % 3) != 0 { 122 h.Write([]byte(salt)) 123 } 124 125 if (i % 7) != 0 { 126 h.Write([]byte(pass)) 127 } 128 129 if (i & 1) == 1 { 130 h.Write(sum) 131 } else { 132 h.Write([]byte(pass)) 133 } 134 135 copy(sum, h.Sum(nil)) 136 } 137 138 buf := bytes.Buffer{} 139 buf.Grow(len(pref) + len(salt) + 1 + 22) 140 buf.WriteString(pref) 141 buf.WriteString(salt) 142 buf.WriteByte('$') 143 144 add := func(a, b, c byte, last bool) { 145 v := (uint(a) << 16) + (uint(b) << 8) + uint(c) 146 147 iterations := 4 148 if last { 149 iterations = 2 150 } 151 152 for i := 0; i < iterations; i++ { 153 buf.WriteByte(aprCharacters[v&0x3f]) 154 v >>= 6 155 } 156 } 157 158 add(sum[0], sum[6], sum[12], false) 159 add(sum[1], sum[7], sum[13], false) 160 add(sum[2], sum[8], sum[14], false) 161 add(sum[3], sum[9], sum[15], false) 162 add(sum[4], sum[10], sum[5], false) 163 add(0, 0, sum[11], true) 164 165 return buf.Bytes() 166 }