crypt.go 1.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  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. // Crypt wraps C library crypt_r
  18. func Crypt(Password, Salt string) (*string, error) {
  19. data := C.struct_crypt_data{}
  20. ckey := C.CString(Password)
  21. csalt := C.CString(Salt)
  22. cpass, err := C.crypt_r(ckey, csalt, &data)
  23. if err != nil {
  24. return nil, err
  25. }
  26. out := C.GoString(cpass)
  27. C.free(unsafe.Pointer(ckey))
  28. C.free(unsafe.Pointer(csalt))
  29. return &out, nil
  30. }
  31. // MakeSaltSHA512 generates a random salt for SHA512
  32. // encrypted password hash.
  33. func MakeSaltSHA512() string {
  34. var LetterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./")
  35. salt := make([]rune, 8)
  36. for i := range salt {
  37. salt[i] = LetterRunes[rand.Intn(len(LetterRunes))]
  38. }
  39. return "$6$" + string(salt) + "$"
  40. }
  41. // Verify checks key against previously crypted hash
  42. func CompareHashAndPassword(Hash, Password string) error {
  43. // ignore empty hashes
  44. if len(Hash) == 0 {
  45. return EPasswordIncorrect
  46. }
  47. // Split salt from password
  48. HashItems := strings.SplitN(Hash, "$", 4)
  49. Salt := "$" + HashItems[1] + "$" + HashItems[2] + "$"
  50. hash, err := Crypt(Password, Salt)
  51. if err != nil {
  52. // Crypt error
  53. return err
  54. }
  55. if *hash == Hash {
  56. // Passwords match
  57. return nil
  58. }
  59. return EPasswordIncorrect
  60. }
  61. func init() {
  62. // Initialize RNG seed
  63. rand.Seed(time.Now().UnixNano())
  64. }