crypt.go 1.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  1. package crypt
  2. import (
  3. "errors"
  4. "math/rand"
  5. "strings"
  6. "time"
  7. "unsafe"
  8. )
  9. // #cgo LDFLAGS: -lcrypt
  10. // #define _GNU_SOURCE
  11. // #include <crypt.h>
  12. // #include <stdlib.h>
  13. // #include <errno.h>
  14. import "C"
  15. // EPasswordIncorrect is a password verification failure error
  16. var EPasswordIncorrect = errors.New("passwords do not match")
  17. var EBadPasswordFormat = errors.New("bad password format")
  18. // Crypt wraps C library crypt_r
  19. func Crypt(Password, Salt string) (*string, error) {
  20. data := C.struct_crypt_data{}
  21. ckey := C.CString(Password)
  22. csalt := C.CString(Salt)
  23. cpass, err := C.crypt_r(ckey, csalt, &data)
  24. if err != nil {
  25. return nil, err
  26. }
  27. out := C.GoString(cpass)
  28. C.free(unsafe.Pointer(ckey))
  29. C.free(unsafe.Pointer(csalt))
  30. return &out, nil
  31. }
  32. // MakeSaltSHA512 generates a random salt for SHA512
  33. // encrypted password hash.
  34. func MakeSaltSHA512() string {
  35. var LetterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./")
  36. salt := make([]rune, 8)
  37. for i := range salt {
  38. salt[i] = LetterRunes[rand.Intn(len(LetterRunes))]
  39. }
  40. return "$6$" + string(salt) + "$"
  41. }
  42. // Verify checks key against previously crypted hash
  43. func CompareHashAndPassword(Hash, Password string) error {
  44. // ignore empty hashes
  45. if len(Hash) == 0 {
  46. return EPasswordIncorrect
  47. }
  48. // Split salt from password
  49. HashItems := strings.SplitN(Hash, "$", 4)
  50. if len(HashItems) < 3 {
  51. return EBadPasswordFormat
  52. }
  53. Salt := "$" + HashItems[1] + "$" + HashItems[2] + "$"
  54. hash, err := Crypt(Password, Salt)
  55. if err != nil {
  56. // Crypt error
  57. return err
  58. }
  59. if *hash == Hash {
  60. // Passwords match
  61. return nil
  62. }
  63. return EPasswordIncorrect
  64. }
  65. func init() {
  66. // Initialize RNG seed
  67. rand.Seed(time.Now().UnixNano())
  68. }