github.com/tommi2day/gomodules@v1.13.2-0.20240423190010-b7d55d252a27/pwlib/scram.go (about) 1 package pwlib 2 3 // Origin: https://github.com/tv42/scram-password/tree/main/internal/scramble 4 // License: https://github.com/tv42/scram-password/blob/main/LICENSE 5 6 import ( 7 "crypto/rand" 8 "encoding/base64" 9 "fmt" 10 11 "github.com/xdg-go/scram" 12 ) 13 14 func makeSalt(size int) ([]byte, error) { 15 salt := make([]byte, size) 16 if _, err := rand.Read(salt); err != nil { 17 return nil, err 18 } 19 return salt, nil 20 } 21 22 func hashWithKF(username string, password string, kf scram.KeyFactors) (string, error) { 23 // We could expose this as a command-line flag, but first need a use case we can test against. 24 const authID = "" 25 // We could make the algorithm a command-line flag. 26 client, err := scram.SHA256.NewClient(username, password, authID) 27 if err != nil { 28 return "", err 29 } 30 31 credentials := client.GetStoredCredentials(kf) 32 33 // SCRAM-SHA-256$<iter>:<salt>$<StoredKey>:<ServerKey> 34 hashed := fmt.Sprintf("SCRAM-SHA-256$%d:%s$%s:%s", 35 credentials.Iters, 36 base64.StdEncoding.EncodeToString([]byte(credentials.Salt)), 37 base64.StdEncoding.EncodeToString(credentials.StoredKey), 38 base64.StdEncoding.EncodeToString(credentials.ServerKey), 39 ) 40 return hashed, nil 41 } 42 43 // ScramPassword returns a SCRAM-SHA-256 password hash for the given username and password as used by postgresql11+ 44 func ScramPassword(username string, password string) (string, error) { 45 // We could take a known salt (as base64) as a command-line flag. 46 47 // We could take salt size as a command-line flag. 48 // 49 // Postgres 14 uses salt size 16. 50 // We'd rather be ahead of the curve than behind. 51 const saltSize = 24 52 salt, err := makeSalt(saltSize) 53 if err != nil { 54 return "", err 55 } 56 kf := scram.KeyFactors{ 57 Salt: string(salt), 58 // We could take iterations as a command-line flag. 59 Iters: 4096, 60 } 61 return hashWithKF(username, password, kf) 62 }