6464import org .slf4j .Logger ;
6565import org .slf4j .LoggerFactory ;
6666
67- /**
68- * Strict HTTP SCRAM client implementing {@code SCRAM-SHA-256} per RFC 7804
69- * with SCRAM core per RFC 5802/7677.
70- * <p>HTTP SCRAM uses <em>no channel binding</em> (GS2 header {@code "n,,"}; {@code c=biws}).</p>
71- *
72- * @since 5.6
73- */
74-
75- /**
76- * Strict HTTP SCRAM client implementing {@code SCRAM-SHA-256} per RFC 7804
77- * with SCRAM core per RFC 5802/7677.
78- * <p>HTTP SCRAM uses <em>no channel binding</em> (GS2 header {@code "n,,"}; {@code c=biws}).</p>
79- *
80- * @since 5.6
81- */
8267
8368/**
8469 * Strict HTTP SCRAM client implementing {@code SCRAM-SHA-256} per RFC 7804
@@ -130,7 +115,8 @@ private enum State {
130115 private byte [] salt ;
131116 private int iterations ;
132117
133- // Expected server signature (raw bytes) for constant-time check on 2xx Authentication-Info
118+ // Expected server signature (raw bytes) for constant-time check on Authentication-Info
119+ // (may appear on any final response status code)
134120 private byte [] expectedV ;
135121
136122 /**
@@ -177,7 +163,7 @@ public boolean isConnectionBased() {
177163 }
178164
179165 /**
180- * SCRAM must inspect successful responses to verify {@code v=} in {@code Authentication-Info}.
166+ * SCRAM must inspect final responses to verify {@code v=} in {@code Authentication-Info}.
181167 *
182168 * @since 5.6
183169 */
@@ -202,7 +188,8 @@ public void processChallenge(final AuthChallenge authChallenge, final HttpContex
202188 }
203189
204190 /**
205- * Handles 401 challenges (with/without {@code data}) and 2xx {@code Authentication-Info}.
191+ * Handles 401 challenges (with/without {@code data}) and final responses carrying
192+ * {@code Authentication-Info} (any status code).
206193 *
207194 * @since 5.6
208195 */
@@ -217,7 +204,7 @@ public void processChallenge(
217204
218205 if (authChallenge == null ) {
219206 if (!challenged ) {
220- // 2xx with no info : nothing to do
207+ // Final response with no Authentication-Info : nothing to do
221208 return ;
222209 }
223210 throw new MalformedChallengeException ("Null SCRAM challenge" );
@@ -250,7 +237,7 @@ public void processChallenge(
250237 final String r = attrs .get ("r" );
251238 final String s = attrs .get ("s" );
252239 final String i = attrs .get ("i" );
253- if (r == null || s == null || i == null ) {
240+ if (r == null || r . isEmpty () || s == null || s . isEmpty () || i == null || i . isEmpty () ) {
254241 this .state = State .FAILED ;
255242 throw new MalformedChallengeException ("SCRAM server-first missing r/s/i" );
256243 }
@@ -262,12 +249,18 @@ public void processChallenge(
262249 this .sid = params .get ("sid" );
263250 try {
264251 this .salt = B64D .decode (s );
252+ if (this .salt .length == 0 ) {
253+ throw new IllegalArgumentException ("empty salt" );
254+ }
265255 } catch (final IllegalArgumentException e ) {
266256 this .state = State .FAILED ;
267257 throw new MalformedChallengeException ("Invalid base64 salt" , e );
268258 }
269259 try {
270260 this .iterations = Integer .parseInt (i );
261+ if (this .iterations <= 0 ) {
262+ throw new NumberFormatException ("i<=0" );
263+ }
271264 } catch (final NumberFormatException e ) {
272265 this .state = State .FAILED ;
273266 throw new MalformedChallengeException ("Invalid iteration count" , e );
@@ -289,7 +282,7 @@ public void processChallenge(
289282 return ;
290283 }
291284
292- // --- 2xx path (Authentication-Info) ---
285+ // --- final-response path (Authentication-Info on any status ) ---
293286 // For Authentication-Info, RFC 7804 does NOT mandate a scheme token; do NOT enforce scheme name here.
294287 final String data = params .get ("data" );
295288 if (data == null ) {
@@ -361,9 +354,8 @@ public boolean isResponseReady(
361354 final CredentialsProvider credentialsProvider ,
362355 final HttpContext context ) throws AuthenticationException {
363356
364- if (credentialsProvider == null ) {
365- throw new IllegalArgumentException ("CredentialsProvider" );
366- }
357+ Args .notNull (credentialsProvider , "Credentials provider" );
358+
367359 final HttpClientContext clientContext = HttpClientContext .cast (context );
368360 final AuthScope scope = new AuthScope (host , this .realm , getName ());
369361 final Credentials creds = credentialsProvider .getCredentials (scope , clientContext );
@@ -682,4 +674,8 @@ public String toString() {
682674 return this .name ;
683675 }
684676 }
677+
678+ private static boolean isBlank (final String s ) {
679+ return s == null || s .isEmpty ();
680+ }
685681}
0 commit comments