github.com/lovung/GoCleanArchitecture@v0.0.0-20210302152432-50d91fd29f9f/pkg/hasher/password.go (about) 1 package hasher 2 3 import ( 4 "crypto/rand" 5 "crypto/subtle" 6 "encoding/base64" 7 "fmt" 8 "strings" 9 10 "golang.org/x/crypto/argon2" 11 ) 12 13 type passwordConfig struct { 14 time uint32 15 memory uint32 16 threads uint8 17 keyLen uint32 18 } 19 20 var _config = passwordConfig{ 21 time: 8, 22 memory: 64 * 1024, 23 threads: 8, 24 keyLen: 64, 25 } 26 27 // HashPassword hash password before saving 28 func HashPassword(password string) (string, error) { 29 return generatePassword(&_config, password) 30 } 31 32 // CheckPasswordHash verify the password 33 func CheckPasswordHash(password, hash string) (bool, error) { 34 return comparePassword(password, hash) 35 } 36 37 // generatePassword is used to generate a new password hash for storing and 38 // comparing at a later date. 39 func generatePassword(c *passwordConfig, password string) (string, error) { 40 // Generate a Salt 41 salt := make([]byte, 16) 42 if _, err := rand.Read(salt); err != nil { 43 return "", err 44 } 45 46 hash := argon2.IDKey([]byte(password), salt, c.time, c.memory, c.threads, c.keyLen) 47 48 // Base64 encode the salt and hashed password. 49 b64Salt := base64.RawStdEncoding.EncodeToString(salt) 50 b64Hash := base64.RawStdEncoding.EncodeToString(hash) 51 52 format := "$argon2id$v=%d$m=%d,t=%d,p=%d$%s$%s" 53 full := fmt.Sprintf(format, argon2.Version, c.memory, c.time, c.threads, b64Salt, b64Hash) 54 return full, nil 55 } 56 57 // comparePassword is used to compare a user-inputted password to a hash to see 58 // if the password matches or not. 59 func comparePassword(password, hash string) (bool, error) { 60 parts := strings.Split(hash, "$") 61 62 c := &passwordConfig{} 63 _, err := fmt.Sscanf(parts[3], "m=%d,t=%d,p=%d", &c.memory, &c.time, &c.threads) 64 if err != nil { 65 return false, err 66 } 67 68 salt, err := base64.RawStdEncoding.DecodeString(parts[4]) 69 if err != nil { 70 return false, err 71 } 72 73 decodedHash, err := base64.RawStdEncoding.DecodeString(parts[5]) 74 if err != nil { 75 return false, err 76 } 77 c.keyLen = uint32(len(decodedHash)) 78 79 comparisonHash := argon2.IDKey([]byte(password), salt, c.time, c.memory, c.threads, c.keyLen) 80 81 return (subtle.ConstantTimeCompare(decodedHash, comparisonHash) == 1), nil 82 }