Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -289,9 +289,10 @@ public void processChallenge(
this.state = State.FAILED;
throw new MalformedChallengeException("SCRAM server-first missing r/s/i");
}
if (this.clientNonce == null || !r.startsWith(this.clientNonce)) {
if (this.clientNonce == null || !r.startsWith(this.clientNonce)
|| r.length() <= this.clientNonce.length()) {
this.state = State.FAILED;
throw new AuthenticationException("SCRAM server nonce does not start with client nonce");
throw new AuthenticationException("SCRAM server nonce does not extend client nonce");
}

try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,36 @@ void strictScram_invalidServerNonce_rejectedAt401() throws Exception {
assertTrue(scheme.isChallengeComplete());
}

@Test
void strictScram_serverNonceNotExtended_rejectedAt401() throws Exception {
final ScramScheme scheme = new ScramScheme();
final BasicCredentialsProvider creds = new BasicCredentialsProvider();
creds.setCredentials(new AuthScope(HOST, REALM, scheme.getName()),
new UsernamePasswordCredentials(USER, PASS.toCharArray()));
final HttpClientContext ctx = HttpClientContext.create();

scheme.processChallenge(HOST, true,
new AuthChallenge(ChallengeType.TARGET, scheme.getName(),
new BasicNameValuePair("realm", REALM)),
ctx);
assertTrue(scheme.isResponseReady(HOST, creds, ctx));

final String authz1 = scheme.generateAuthResponse(HOST, null, ctx);
final String clientFirstBare = deb64s(splitHeader(authz1).get("data")).substring("n,,".length());
final String clientNonce = parseCsvAttrs(clientFirstBare).get("r");

// Server echoes the client nonce verbatim, contributing no server nonce of its own.
final String serverFirst = "r=" + clientNonce + ",s=" + b64("salt".getBytes(StandardCharsets.UTF_8)) + ",i=4096";
final AuthenticationException ex = assertThrows(AuthenticationException.class, () ->
scheme.processChallenge(HOST, true,
new AuthChallenge(ChallengeType.TARGET, scheme.getName(),
new BasicNameValuePair("sid", SID),
new BasicNameValuePair("data", b64(serverFirst.getBytes(StandardCharsets.UTF_8)))),
ctx));
assertTrue(ex.getMessage().toLowerCase(Locale.ROOT).contains("nonce"));
assertTrue(scheme.isChallengeComplete());
}

@Test
void strictScram_minIterations_enforced() throws Exception {
final ScramScheme scheme = new ScramScheme(4096, 4096, 100000, null);
Expand Down
Loading