github.com/resonatecoop/id@v1.1.0-43/util/password/password.go (about) 1 package password 2 3 import ( 4 "errors" 5 "fmt" 6 "os" 7 "sync" 8 9 "github.com/apokalyptik/phpass" 10 "github.com/trustelem/zxcvbn" 11 "golang.org/x/crypto/bcrypt" 12 ) 13 14 var ( 15 phpassVar = phpass.New(phpass.NewConfig()) 16 phpassMutex = &sync.Mutex{} 17 18 MinPasswordLength = 9 19 MaxPasswordLength = 72 20 21 // ErrPasswordTooShort ... 22 ErrPasswordTooShort = fmt.Errorf( 23 "Password must be at least %d characters long", 24 MinPasswordLength, 25 ) 26 27 // ErrPasswordTooLong ... 28 ErrPasswordTooLong = fmt.Errorf( 29 "Password must be at maximum %d characters long", 30 MaxPasswordLength, 31 ) 32 33 // ErrPasswordTooWeak ... 34 ErrPasswordTooWeak = errors.New("Password is too weak") 35 ) 36 37 // VerifyPassword compares password and the hashed password 38 // Fallback to phpass if bcrypt fails 39 func VerifyPassword(passwordHash, password string) error { 40 if bcrypt.CompareHashAndPassword([]byte(passwordHash), []byte(password)) != nil { 41 fmt.Fprintln(os.Stderr, "Not Bcrypt password") 42 phpassMutex.Lock() 43 ret := phpassVar.Check([]byte(password), []byte(passwordHash)) 44 phpassMutex.Unlock() 45 // If passwords don't match return mismatch error 46 // use bcrypt's error code for compat 47 if !ret { 48 fmt.Fprintln(os.Stderr, "No password match") 49 return bcrypt.ErrMismatchedHashAndPassword 50 } 51 fmt.Fprintln(os.Stderr, "PHPassword matched") 52 return nil 53 } 54 fmt.Fprintln(os.Stderr, "Bcrypt Password matched") 55 return nil 56 } 57 58 // HashPassword creates a bcrypt password hash 59 func HashPassword(password string) ([]byte, error) { 60 return bcrypt.GenerateFromPassword([]byte(password), 3) 61 } 62 63 // HashWpPassword creates a phpass password hash 64 func HashWpPassword(password string) ([]byte, error) { 65 phpassMutex.Lock() 66 passwordHashWp, err := phpassVar.Hash([]byte(password)) 67 phpassMutex.Unlock() 68 if err != nil { 69 return nil, err 70 } 71 return passwordHashWp, nil 72 } 73 74 // ValidatePassword 75 func ValidatePassword(password string) error { 76 if len(password) < MinPasswordLength { 77 return ErrPasswordTooShort 78 } 79 80 if len(password) > MaxPasswordLength { 81 return ErrPasswordTooLong 82 } 83 84 // enforce strong enough passwords 85 passwordStrength := zxcvbn.PasswordStrength(password, nil) 86 87 if passwordStrength.Score < 3 { 88 return ErrPasswordTooWeak 89 } 90 91 return nil 92 }