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  }