package crypt import ( "errors" "math/rand" "strings" "time" "unsafe" ) // #cgo LDFLAGS: -lcrypt // #define _GNU_SOURCE // #include // #include // #include import "C" // EPasswordIncorrect is a password verification failure error var EPasswordIncorrect = errors.New("passwords do not match") var EBadPasswordFormat = errors.New("bad password format") // Crypt wraps C library crypt_r func Crypt(Password, Salt string) (*string, error) { data := C.struct_crypt_data{} ckey := C.CString(Password) csalt := C.CString(Salt) cpass, err := C.crypt_r(ckey, csalt, &data) if err != nil { return nil, err } out := C.GoString(cpass) C.free(unsafe.Pointer(ckey)) C.free(unsafe.Pointer(csalt)) return &out, nil } // MakeSaltSHA512 generates a random salt for SHA512 // encrypted password hash. func MakeSaltSHA512() string { var LetterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./") salt := make([]rune, 8) for i := range salt { salt[i] = LetterRunes[rand.Intn(len(LetterRunes))] } return "$6$" + string(salt) + "$" } // Verify checks key against previously crypted hash func CompareHashAndPassword(Hash, Password string) error { // ignore empty hashes if len(Hash) == 0 { return EPasswordIncorrect } // Split salt from password HashItems := strings.SplitN(Hash, "$", 4) if len(HashItems) < 3 { return EBadPasswordFormat } Salt := "$" + HashItems[1] + "$" + HashItems[2] + "$" hash, err := Crypt(Password, Salt) if err != nil { // Crypt error return err } if *hash == Hash { // Passwords match return nil } return EPasswordIncorrect } func init() { // Initialize RNG seed rand.Seed(time.Now().UnixNano()) }