(serverFirstMessage []byte)
| 242 | } |
| 243 | |
| 244 | func (sc *scramClient) recvServerFirstMessage(serverFirstMessage []byte) error { |
| 245 | sc.serverFirstMessage = serverFirstMessage |
| 246 | buf := serverFirstMessage |
| 247 | if !bytes.HasPrefix(buf, []byte("r=")) { |
| 248 | return errors.New("invalid SCRAM server-first-message received from server: did not include r=") |
| 249 | } |
| 250 | buf = buf[2:] |
| 251 | |
| 252 | idx := bytes.IndexByte(buf, ',') |
| 253 | if idx == -1 { |
| 254 | return errors.New("invalid SCRAM server-first-message received from server: did not include s=") |
| 255 | } |
| 256 | sc.clientAndServerNonce = buf[:idx] |
| 257 | buf = buf[idx+1:] |
| 258 | |
| 259 | if !bytes.HasPrefix(buf, []byte("s=")) { |
| 260 | return errors.New("invalid SCRAM server-first-message received from server: did not include s=") |
| 261 | } |
| 262 | buf = buf[2:] |
| 263 | |
| 264 | idx = bytes.IndexByte(buf, ',') |
| 265 | if idx == -1 { |
| 266 | return errors.New("invalid SCRAM server-first-message received from server: did not include i=") |
| 267 | } |
| 268 | saltStr := buf[:idx] |
| 269 | buf = buf[idx+1:] |
| 270 | |
| 271 | if !bytes.HasPrefix(buf, []byte("i=")) { |
| 272 | return errors.New("invalid SCRAM server-first-message received from server: did not include i=") |
| 273 | } |
| 274 | buf = buf[2:] |
| 275 | iterationsStr := buf |
| 276 | |
| 277 | var err error |
| 278 | sc.salt, err = base64.StdEncoding.DecodeString(string(saltStr)) |
| 279 | if err != nil { |
| 280 | return fmt.Errorf("invalid SCRAM salt received from server: %w", err) |
| 281 | } |
| 282 | |
| 283 | sc.iterations, err = strconv.Atoi(string(iterationsStr)) |
| 284 | if err != nil || sc.iterations <= 0 { |
| 285 | return fmt.Errorf("invalid SCRAM iteration count received from server: %w", err) |
| 286 | } |
| 287 | // Bound server-supplied iteration count to prevent a malicious server from forcing the client |
| 288 | // to spend unbounded CPU in PBKDF2. PostgreSQL's scram_iterations defaults to 4096; this ceiling |
| 289 | // is ~2500x that. |
| 290 | const maxScramIterations = 10_000_000 |
| 291 | if sc.iterations > maxScramIterations { |
| 292 | return fmt.Errorf("SCRAM iteration count from server too high: %d (max %d)", sc.iterations, maxScramIterations) |
| 293 | } |
| 294 | |
| 295 | if !bytes.HasPrefix(sc.clientAndServerNonce, sc.clientNonce) { |
| 296 | return errors.New("invalid SCRAM nonce: did not start with client nonce") |
| 297 | } |
| 298 | |
| 299 | if len(sc.clientAndServerNonce) <= len(sc.clientNonce) { |
| 300 | return errors.New("invalid SCRAM nonce: did not include server nonce") |
| 301 | } |
no outgoing calls