diff --git a/scram-client/src/test/java/com/ongres/scram/client/ScramBuilderTest.java b/scram-client/src/test/java/com/ongres/scram/client/ScramBuilderTest.java index 82019af..f5a230d 100644 --- a/scram-client/src/test/java/com/ongres/scram/client/ScramBuilderTest.java +++ b/scram-client/src/test/java/com/ongres/scram/client/ScramBuilderTest.java @@ -13,8 +13,6 @@ import java.util.Arrays; import java.util.Base64; -import java.util.Collection; -import java.util.Collections; import com.ongres.scram.common.ClientFinalMessage; import com.ongres.scram.common.ClientFirstMessage; diff --git a/scram-common/src/main/java/com/ongres/scram/common/ServerFirstMessage.java b/scram-common/src/main/java/com/ongres/scram/common/ServerFirstMessage.java index 53c8f56..0573194 100644 --- a/scram-common/src/main/java/com/ongres/scram/common/ServerFirstMessage.java +++ b/scram-common/src/main/java/com/ongres/scram/common/ServerFirstMessage.java @@ -150,6 +150,9 @@ public int getIterationCount() { throw new ScramParseException( "nonce must be the 1st element of the server-first-message"); } + if (!isValidNonce(nonce.getValue())) { + throw new ScramParseException("nonce contains invalid characters"); + } if (!nonce.getValue().startsWith(clientNonce)) { throw new ScramParseException("parsed nonce does not start with client nonce"); } @@ -176,6 +179,17 @@ public int getIterationCount() { salt.getValue(), iterationInt); } + // RFC 5802 Section 7: printable = %x21-2B / %x2D-7E (printable ASCII excluding comma) + private static boolean isValidNonce(String nonce) { + for (int i = 0; i < nonce.length(); i++) { + char c = nonce.charAt(i); + if (c < 0x21 || c > 0x7E || c == ',') { + return false; + } + } + return true; + } + @Override StringBuilder writeTo(StringBuilder sb) { return StringWritableCsv.writeTo( diff --git a/scram-common/src/test/java/com/ongres/scram/common/ServerFirstMessageTest.java b/scram-common/src/test/java/com/ongres/scram/common/ServerFirstMessageTest.java index 142c54a..3980385 100644 --- a/scram-common/src/test/java/com/ongres/scram/common/ServerFirstMessageTest.java +++ b/scram-common/src/test/java/com/ongres/scram/common/ServerFirstMessageTest.java @@ -8,9 +8,14 @@ import static com.ongres.scram.common.RfcExampleSha1.CLIENT_NONCE; import static com.ongres.scram.common.RfcExampleSha1.SERVER_FIRST_MESSAGE; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.stream.Stream; import com.ongres.scram.common.exception.ScramParseException; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; class ServerFirstMessageTest { @Test @@ -31,4 +36,20 @@ void validParseFrom() throws ScramParseException { assertEquals(SERVER_FIRST_MESSAGE, serverFirstMessage.toString()); } + + static Stream invalidServerNonceSuffixes() { + return Stream.of( + "server,nonce", // printable ASCII except "," + "server nonce", // space (0x20) is below the printable ASCII range + "servernonce" + (char) 0x7F // DEL (0x7F) is above the printable ASCII range + ); + } + + @ParameterizedTest(name = "nonce with invalid char rejected") + @MethodSource("invalidServerNonceSuffixes") + void invalidNonceCharactersRejected(String serverNonceSuffix) { + String message = "r=" + CLIENT_NONCE + serverNonceSuffix + ",s=QSXCR+Q6sek8bf92,i=4096"; + assertThrows(ScramParseException.class, + () -> ServerFirstMessage.parseFrom(message, CLIENT_NONCE)); + } }