| package scram |
| |
| // |
| // @see https://github.com/postgres/postgres/blob/c30f54ad732ca5c8762bb68bbe0f51de9137dd72/src/interfaces/libpq/fe-auth.c#L1167-L1285 |
| // @see https://github.com/postgres/postgres/blob/e6bdfd9700ebfc7df811c97c2fc46d7e94e329a2/src/interfaces/libpq/fe-auth-scram.c#L868-L905 |
| // @see https://github.com/postgres/postgres/blob/c30f54ad732ca5c8762bb68bbe0f51de9137dd72/src/port/pg_strong_random.c#L66-L96 |
| // @see https://github.com/postgres/postgres/blob/e6bdfd9700ebfc7df811c97c2fc46d7e94e329a2/src/common/scram-common.c#L160-L274 |
| // @see https://github.com/postgres/postgres/blob/e6bdfd9700ebfc7df811c97c2fc46d7e94e329a2/src/common/scram-common.c#L27-L85 |
| |
| // Implementation from https://github.com/supercaracal/scram-sha-256/blob/d3c05cd927770a11c6e12de3e3a99c3446a1f78d/main.go |
| import ( |
| "crypto/hmac" |
| "crypto/rand" |
| "crypto/sha256" |
| "encoding/base64" |
| "fmt" |
| "io" |
| |
| "golang.org/x/crypto/pbkdf2" |
| ) |
| |
| const ( |
| // @see https://github.com/postgres/postgres/blob/e6bdfd9700ebfc7df811c97c2fc46d7e94e329a2/src/include/common/scram-common.h#L36-L41 |
| saltSize = 16 |
| |
| // @see https://github.com/postgres/postgres/blob/c30f54ad732ca5c8762bb68bbe0f51de9137dd72/src/include/common/sha2.h#L22 |
| digestLen = 32 |
| |
| // @see https://github.com/postgres/postgres/blob/e6bdfd9700ebfc7df811c97c2fc46d7e94e329a2/src/include/common/scram-common.h#L43-L47 |
| iterationCnt = 4096 |
| ) |
| |
| var ( |
| clientRawKey = []byte("Client Key") |
| serverRawKey = []byte("Server Key") |
| ) |
| |
| func genSalt(size int) ([]byte, error) { |
| salt := make([]byte, size) |
| if _, err := io.ReadFull(rand.Reader, salt); err != nil { |
| return nil, err |
| } |
| return salt, nil |
| } |
| |
| func encodeB64(src []byte) (dst []byte) { |
| dst = make([]byte, base64.StdEncoding.EncodedLen(len(src))) |
| base64.StdEncoding.Encode(dst, src) |
| return |
| } |
| |
| func getHMACSum(key, msg []byte) []byte { |
| h := hmac.New(sha256.New, key) |
| _, _ = h.Write(msg) |
| return h.Sum(nil) |
| } |
| |
| func getSHA256Sum(key []byte) []byte { |
| h := sha256.New() |
| _, _ = h.Write(key) |
| return h.Sum(nil) |
| } |
| |
| func hashPassword(rawPassword, salt []byte, iter, keyLen int) string { |
| digestKey := pbkdf2.Key(rawPassword, salt, iter, keyLen, sha256.New) |
| clientKey := getHMACSum(digestKey, clientRawKey) |
| storedKey := getSHA256Sum(clientKey) |
| serverKey := getHMACSum(digestKey, serverRawKey) |
| |
| return fmt.Sprintf("SCRAM-SHA-256$%d:%s$%s:%s", |
| iter, |
| string(encodeB64(salt)), |
| string(encodeB64(storedKey)), |
| string(encodeB64(serverKey)), |
| ) |
| } |
| |
| func Hash(password string) (string, error) { |
| salt, err := genSalt(saltSize) |
| if err != nil { |
| return "", err |
| } |
| |
| hashedPassword := hashPassword([]byte(password), salt, iterationCnt, digestLen) |
| return hashedPassword, nil |
| } |