| 179 | } |
| 180 | |
| 181 | func newScramClient(serverAuthMechanisms []string, password string) (*scramClient, error) { |
| 182 | sc := &scramClient{ |
| 183 | serverAuthMechanisms: serverAuthMechanisms, |
| 184 | authMechanism: scramSHA256Name, |
| 185 | } |
| 186 | |
| 187 | // Ensure the server supports SCRAM-SHA-256. SCRAM-SHA-256-PLUS is the |
| 188 | // channel binding variant and is only advertised when the server supports |
| 189 | // SSL. PostgreSQL always advertises the base SCRAM-SHA-256 mechanism |
| 190 | // regardless of SSL. |
| 191 | if !slices.Contains(sc.serverAuthMechanisms, scramSHA256Name) { |
| 192 | return nil, errors.New("server does not support SCRAM-SHA-256") |
| 193 | } |
| 194 | |
| 195 | // precis.OpaqueString is equivalent to SASLprep for password. |
| 196 | var err error |
| 197 | sc.password, err = precis.OpaqueString.String(password) |
| 198 | if err != nil { |
| 199 | // PostgreSQL allows passwords invalid according to SCRAM / SASLprep. |
| 200 | sc.password = password |
| 201 | } |
| 202 | |
| 203 | buf := make([]byte, clientNonceLen) |
| 204 | _, err = rand.Read(buf) |
| 205 | if err != nil { |
| 206 | return nil, err |
| 207 | } |
| 208 | sc.clientNonce = make([]byte, base64.RawStdEncoding.EncodedLen(len(buf))) |
| 209 | base64.RawStdEncoding.Encode(sc.clientNonce, buf) |
| 210 | |
| 211 | return sc, nil |
| 212 | } |
| 213 | |
| 214 | func (sc *scramClient) clientFirstMessage() []byte { |
| 215 | // The client-first-message is the GS2 header concatenated with the bare |