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  }