2020-05-24 14:41:19 +03:00
|
|
|
package utils
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/base64"
|
|
|
|
"errors"
|
2023-07-08 20:15:59 +03:00
|
|
|
"github.com/alexedwards/argon2id"
|
2020-05-25 22:42:45 +03:00
|
|
|
"golang.org/x/crypto/bcrypt"
|
2020-05-24 14:41:19 +03:00
|
|
|
"net/http"
|
|
|
|
"regexp"
|
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
2020-05-25 23:24:29 +03:00
|
|
|
var md5Regex = regexp.MustCompile(`^[a-f0-9]{32}$`)
|
|
|
|
|
2020-05-24 14:41:19 +03:00
|
|
|
func ExtractBasicAuth(r *http.Request) (username, password string, err error) {
|
|
|
|
authHeader := strings.Split(r.Header.Get("Authorization"), " ")
|
|
|
|
if len(authHeader) != 2 || authHeader[0] != "Basic" {
|
|
|
|
return username, password, errors.New("failed to extract API key")
|
|
|
|
}
|
|
|
|
|
|
|
|
hash, err := base64.StdEncoding.DecodeString(authHeader[1])
|
|
|
|
userKey := strings.TrimSpace(string(hash))
|
|
|
|
if err != nil {
|
|
|
|
return username, password, err
|
|
|
|
}
|
|
|
|
|
|
|
|
re := regexp.MustCompile(`^(.+):(.+)$`)
|
|
|
|
groups := re.FindAllStringSubmatch(userKey, -1)
|
|
|
|
if len(groups) == 0 || len(groups[0]) != 3 {
|
|
|
|
return username, password, errors.New("failed to parse user agent string")
|
|
|
|
}
|
|
|
|
username, password = groups[0][1], groups[0][2]
|
|
|
|
return username, password, err
|
|
|
|
}
|
|
|
|
|
|
|
|
func ExtractBearerAuth(r *http.Request) (key string, err error) {
|
|
|
|
authHeader := strings.Split(r.Header.Get("Authorization"), " ")
|
2021-02-12 20:12:46 +03:00
|
|
|
if len(authHeader) != 2 || (authHeader[0] != "Basic" && authHeader[0] != "Bearer") {
|
2020-05-24 14:41:19 +03:00
|
|
|
return key, errors.New("failed to extract API key")
|
|
|
|
}
|
|
|
|
|
|
|
|
keyBytes, err := base64.StdEncoding.DecodeString(authHeader[1])
|
|
|
|
return string(keyBytes), err
|
|
|
|
}
|
|
|
|
|
2023-07-08 20:15:59 +03:00
|
|
|
// password hashing
|
|
|
|
|
|
|
|
func ComparePassword(hashed, plain, pepper string) bool {
|
|
|
|
if hashed[0:10] == "$argon2id$" {
|
|
|
|
return CompareArgon2Id(hashed, plain, pepper)
|
|
|
|
}
|
|
|
|
return CompareBcrypt(hashed, plain, pepper)
|
|
|
|
}
|
|
|
|
|
|
|
|
func HashPassword(plain, pepper string) (string, error) {
|
|
|
|
return HashArgon2Id(plain, pepper)
|
|
|
|
}
|
|
|
|
|
|
|
|
func CompareBcrypt(hashed, plain, pepper string) bool {
|
|
|
|
plainPepperedPassword := []byte(strings.TrimSpace(plain) + pepper)
|
|
|
|
err := bcrypt.CompareHashAndPassword([]byte(hashed), plainPepperedPassword)
|
2020-05-25 22:42:45 +03:00
|
|
|
return err == nil
|
|
|
|
}
|
|
|
|
|
2020-10-16 17:11:14 +03:00
|
|
|
func HashBcrypt(plain, pepper string) (string, error) {
|
|
|
|
plainPepperedPassword := []byte(strings.TrimSpace(plain) + pepper)
|
|
|
|
bytes, err := bcrypt.GenerateFromPassword(plainPepperedPassword, bcrypt.DefaultCost)
|
2020-05-25 22:42:45 +03:00
|
|
|
if err == nil {
|
2020-10-16 17:11:14 +03:00
|
|
|
return string(bytes), nil
|
2020-05-24 14:41:19 +03:00
|
|
|
}
|
2020-10-16 17:11:14 +03:00
|
|
|
return "", err
|
|
|
|
}
|
2023-07-08 20:15:59 +03:00
|
|
|
|
|
|
|
func CompareArgon2Id(hashed, plain, pepper string) bool {
|
|
|
|
plainPepperedPassword := strings.TrimSpace(plain) + pepper
|
|
|
|
match, err := argon2id.ComparePasswordAndHash(plainPepperedPassword, hashed)
|
|
|
|
return err == nil && match
|
|
|
|
}
|
|
|
|
|
|
|
|
func HashArgon2Id(plain, pepper string) (string, error) {
|
|
|
|
plainPepperedPassword := strings.TrimSpace(plain) + pepper
|
|
|
|
hash, err := argon2id.CreateHash(plainPepperedPassword, argon2id.DefaultParams)
|
|
|
|
if err == nil {
|
|
|
|
return hash, nil
|
|
|
|
}
|
|
|
|
return "", err
|
|
|
|
}
|