You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

salted.go 2.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566
  1. // Copyright (c) 2016 Daniel Oaks <daniel@danieloaks.net>
  2. // released under the MIT license
  3. package passwd
  4. import (
  5. "crypto/rand"
  6. "golang.org/x/crypto/bcrypt"
  7. )
  8. const (
  9. // newSaltLen is how many bytes long newly-generated salts are.
  10. newSaltLen = 30
  11. // defaultPasswordCost is the bcrypt cost we use for passwords.
  12. defaultPasswordCost = 14
  13. )
  14. // NewSalt returns a salt for crypto uses.
  15. func NewSalt() ([]byte, error) {
  16. salt := make([]byte, newSaltLen)
  17. _, err := rand.Read(salt)
  18. if err != nil {
  19. var emptySalt []byte
  20. return emptySalt, err
  21. }
  22. return salt, nil
  23. }
  24. // SaltedManager supports the hashing and comparing of passwords with the given salt.
  25. type SaltedManager struct {
  26. salt []byte
  27. }
  28. // NewSaltedManager returns a new SaltedManager with the given salt.
  29. func NewSaltedManager(salt []byte) SaltedManager {
  30. return SaltedManager{
  31. salt: salt,
  32. }
  33. }
  34. // assemblePassword returns an assembled slice of bytes for the given password details.
  35. func (sm *SaltedManager) assemblePassword(specialSalt []byte, password string) []byte {
  36. var assembledPasswordBytes []byte
  37. assembledPasswordBytes = append(assembledPasswordBytes, sm.salt...)
  38. assembledPasswordBytes = append(assembledPasswordBytes, '-')
  39. assembledPasswordBytes = append(assembledPasswordBytes, specialSalt...)
  40. assembledPasswordBytes = append(assembledPasswordBytes, '-')
  41. assembledPasswordBytes = append(assembledPasswordBytes, []byte(password)...)
  42. return assembledPasswordBytes
  43. }
  44. // GenerateFromPassword encrypts the given password.
  45. func (sm *SaltedManager) GenerateFromPassword(specialSalt []byte, password string) ([]byte, error) {
  46. assembledPasswordBytes := sm.assemblePassword(specialSalt, password)
  47. return bcrypt.GenerateFromPassword(assembledPasswordBytes, defaultPasswordCost)
  48. }
  49. // CompareHashAndPassword compares a hashed password with its possible plaintext equivalent.
  50. // Returns nil on success, or an error on failure.
  51. func (sm *SaltedManager) CompareHashAndPassword(hashedPassword []byte, specialSalt []byte, password string) error {
  52. assembledPasswordBytes := sm.assemblePassword(specialSalt, password)
  53. return bcrypt.CompareHashAndPassword(hashedPassword, assembledPasswordBytes)
  54. }