You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by lq...@apache.org on 2017/11/08 12:36:52 UTC

qpid-jms-amqp-0-x git commit: QPID-7964: [Java Client, AMQP 0-x] In SCRAM SASL implementation do not incorrectly encode "=" and ", " for passwords

Repository: qpid-jms-amqp-0-x
Updated Branches:
  refs/heads/master 56bacf627 -> 1043d0f4d


QPID-7964: [Java Client, AMQP 0-x] In SCRAM SASL implementation do not incorrectly encode "=" and "," for passwords


Project: http://git-wip-us.apache.org/repos/asf/qpid-jms-amqp-0-x/repo
Commit: http://git-wip-us.apache.org/repos/asf/qpid-jms-amqp-0-x/commit/1043d0f4
Tree: http://git-wip-us.apache.org/repos/asf/qpid-jms-amqp-0-x/tree/1043d0f4
Diff: http://git-wip-us.apache.org/repos/asf/qpid-jms-amqp-0-x/diff/1043d0f4

Branch: refs/heads/master
Commit: 1043d0f4de9539b7a861cebcc3925c4acf13a431
Parents: 56bacf6
Author: Lorenz Quack <lq...@apache.org>
Authored: Wed Nov 8 12:36:15 2017 +0000
Committer: Lorenz Quack <lq...@apache.org>
Committed: Wed Nov 8 12:36:15 2017 +0000

----------------------------------------------------------------------
 .../security/scram/AbstractScramSaslClient.java |  25 ++-
 .../security/scram/ScramSHA1SaslClient.java     |   4 +-
 .../scram/ScramSHA1SaslClientFactory.java       |   3 +-
 .../security/scram/ScramSHA256SaslClient.java   |   4 +-
 .../scram/ScramSHA256SaslClientFactory.java     |   3 +-
 .../AbstractScramSHAMechanismTestBase.java      | 202 +++++++++++++++++++
 .../security/scram/ScramSHA1MechanismTest.java  | 104 ++++++++++
 .../scram/ScramSHA256MechanismTest.java         |  65 ++++++
 8 files changed, 398 insertions(+), 12 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/qpid-jms-amqp-0-x/blob/1043d0f4/client/src/main/java/org/apache/qpid/client/security/scram/AbstractScramSaslClient.java
----------------------------------------------------------------------
diff --git a/client/src/main/java/org/apache/qpid/client/security/scram/AbstractScramSaslClient.java b/client/src/main/java/org/apache/qpid/client/security/scram/AbstractScramSaslClient.java
index d9f6f53..6c23fd0 100644
--- a/client/src/main/java/org/apache/qpid/client/security/scram/AbstractScramSaslClient.java
+++ b/client/src/main/java/org/apache/qpid/client/security/scram/AbstractScramSaslClient.java
@@ -27,7 +27,6 @@ import java.security.InvalidKeyException;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.util.Arrays;
-import java.util.UUID;
 
 import javax.crypto.Mac;
 import javax.crypto.spec.SecretKeySpec;
@@ -53,7 +52,7 @@ public abstract class AbstractScramSaslClient implements SaslClient
     private final String _hmacName;
 
     private String _username;
-    private final String _clientNonce = UUID.randomUUID().toString();
+    private final String _clientNonce;
     private String _serverNonce;
     private byte[] _salt;
     private int _iterationCount;
@@ -77,13 +76,14 @@ public abstract class AbstractScramSaslClient implements SaslClient
     public AbstractScramSaslClient(final CallbackHandler cbh,
                                    final String mechanism,
                                    final String digestName,
-                                   final String hmacName)
+                                   final String hmacName,
+                                   final String clientNonce)
     {
         _callbackHandler = cbh;
         _mechanism = mechanism;
         _digestName = digestName;
         _hmacName = hmacName;
-
+        _clientNonce = clientNonce;
     }
 
     @Override
@@ -131,7 +131,15 @@ public abstract class AbstractScramSaslClient implements SaslClient
         {
             throw new SaslException("Server final message did not contain verifier");
         }
-        byte[] serverSignature = Strings.decodeBase64(parts[0].substring(2));
+        byte[] serverSignature;
+        try
+        {
+            serverSignature = Strings.decodeBase64(parts[0].substring(2));
+        }
+        catch (IllegalArgumentException e)
+        {
+            throw new SaslException("Server signature did not match");
+        }
         if(!Arrays.equals(_serverSignature, serverSignature))
         {
             throw new SaslException("Server signature did not match");
@@ -289,7 +297,7 @@ public abstract class AbstractScramSaslClient implements SaslClient
             NameCallback nameCallback = new NameCallback("Username?");
             _callbackHandler.handle(new Callback[] { nameCallback });
             _username = nameCallback.getName();
-            buf.append(saslPrep(_username));
+            buf.append(escapeUsername(saslPrep(_username)));
             buf.append(",r=");
             buf.append(_clientNonce);
             _clientFirstMessageBare = buf.toString();
@@ -314,6 +322,11 @@ public abstract class AbstractScramSaslClient implements SaslClient
             throw new SaslException("Can only encode names and passwords which are restricted to ASCII characters");
         }
 
+        return name;
+    }
+
+    private String escapeUsername(String name)
+    {
         name = name.replace("=", "=3D");
         name = name.replace(",", "=2C");
         return name;

http://git-wip-us.apache.org/repos/asf/qpid-jms-amqp-0-x/blob/1043d0f4/client/src/main/java/org/apache/qpid/client/security/scram/ScramSHA1SaslClient.java
----------------------------------------------------------------------
diff --git a/client/src/main/java/org/apache/qpid/client/security/scram/ScramSHA1SaslClient.java b/client/src/main/java/org/apache/qpid/client/security/scram/ScramSHA1SaslClient.java
index b6704e9..bd05964 100644
--- a/client/src/main/java/org/apache/qpid/client/security/scram/ScramSHA1SaslClient.java
+++ b/client/src/main/java/org/apache/qpid/client/security/scram/ScramSHA1SaslClient.java
@@ -27,8 +27,8 @@ public class ScramSHA1SaslClient extends AbstractScramSaslClient
 
     public static final String MECHANISM = "SCRAM-SHA-1";
 
-    public ScramSHA1SaslClient(final CallbackHandler cbh)
+    public ScramSHA1SaslClient(final CallbackHandler cbh, final String clientNonce)
     {
-        super(cbh, MECHANISM, "SHA-1", "HmacSHA1");
+        super(cbh, MECHANISM, "SHA-1", "HmacSHA1", clientNonce);
     }
 }

http://git-wip-us.apache.org/repos/asf/qpid-jms-amqp-0-x/blob/1043d0f4/client/src/main/java/org/apache/qpid/client/security/scram/ScramSHA1SaslClientFactory.java
----------------------------------------------------------------------
diff --git a/client/src/main/java/org/apache/qpid/client/security/scram/ScramSHA1SaslClientFactory.java b/client/src/main/java/org/apache/qpid/client/security/scram/ScramSHA1SaslClientFactory.java
index 59ef236..7c0af3a 100644
--- a/client/src/main/java/org/apache/qpid/client/security/scram/ScramSHA1SaslClientFactory.java
+++ b/client/src/main/java/org/apache/qpid/client/security/scram/ScramSHA1SaslClientFactory.java
@@ -21,6 +21,7 @@
 package org.apache.qpid.client.security.scram;
 
 import java.util.Map;
+import java.util.UUID;
 
 import javax.security.auth.callback.CallbackHandler;
 import javax.security.sasl.SaslClient;
@@ -46,7 +47,7 @@ public class ScramSHA1SaslClientFactory implements SaslClientFactory
                 {
                     throw new SaslException("CallbackHandler must not be null");
                 }
-                return new ScramSHA1SaslClient(cbh);
+                return new ScramSHA1SaslClient(cbh, UUID.randomUUID().toString());
             }
         }
         return null;

http://git-wip-us.apache.org/repos/asf/qpid-jms-amqp-0-x/blob/1043d0f4/client/src/main/java/org/apache/qpid/client/security/scram/ScramSHA256SaslClient.java
----------------------------------------------------------------------
diff --git a/client/src/main/java/org/apache/qpid/client/security/scram/ScramSHA256SaslClient.java b/client/src/main/java/org/apache/qpid/client/security/scram/ScramSHA256SaslClient.java
index 8779c36..30fe02f 100644
--- a/client/src/main/java/org/apache/qpid/client/security/scram/ScramSHA256SaslClient.java
+++ b/client/src/main/java/org/apache/qpid/client/security/scram/ScramSHA256SaslClient.java
@@ -27,8 +27,8 @@ public class ScramSHA256SaslClient extends AbstractScramSaslClient
 
     public static final String MECHANISM = "SCRAM-SHA-256";
 
-    public ScramSHA256SaslClient(final CallbackHandler cbh)
+    public ScramSHA256SaslClient(final CallbackHandler cbh, final String clientNonce)
     {
-        super(cbh, MECHANISM, "SHA-256", "HmacSHA256");
+        super(cbh, MECHANISM, "SHA-256", "HmacSHA256", clientNonce);
     }
 }

http://git-wip-us.apache.org/repos/asf/qpid-jms-amqp-0-x/blob/1043d0f4/client/src/main/java/org/apache/qpid/client/security/scram/ScramSHA256SaslClientFactory.java
----------------------------------------------------------------------
diff --git a/client/src/main/java/org/apache/qpid/client/security/scram/ScramSHA256SaslClientFactory.java b/client/src/main/java/org/apache/qpid/client/security/scram/ScramSHA256SaslClientFactory.java
index fff762f..5dd7544 100644
--- a/client/src/main/java/org/apache/qpid/client/security/scram/ScramSHA256SaslClientFactory.java
+++ b/client/src/main/java/org/apache/qpid/client/security/scram/ScramSHA256SaslClientFactory.java
@@ -21,6 +21,7 @@
 package org.apache.qpid.client.security.scram;
 
 import java.util.Map;
+import java.util.UUID;
 
 import javax.security.auth.callback.CallbackHandler;
 import javax.security.sasl.SaslClient;
@@ -46,7 +47,7 @@ public class ScramSHA256SaslClientFactory implements SaslClientFactory
                 {
                     throw new SaslException("CallbackHandler must not be null");
                 }
-                return new ScramSHA256SaslClient(cbh);
+                return new ScramSHA256SaslClient(cbh, UUID.randomUUID().toString());
             }
 
         }

http://git-wip-us.apache.org/repos/asf/qpid-jms-amqp-0-x/blob/1043d0f4/client/src/test/java/org/apache/qpid/client/security/scram/AbstractScramSHAMechanismTestBase.java
----------------------------------------------------------------------
diff --git a/client/src/test/java/org/apache/qpid/client/security/scram/AbstractScramSHAMechanismTestBase.java b/client/src/test/java/org/apache/qpid/client/security/scram/AbstractScramSHAMechanismTestBase.java
new file mode 100644
index 0000000..53d911c
--- /dev/null
+++ b/client/src/test/java/org/apache/qpid/client/security/scram/AbstractScramSHAMechanismTestBase.java
@@ -0,0 +1,202 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.qpid.client.security.scram;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import java.nio.charset.StandardCharsets;
+
+import javax.security.sasl.SaslException;
+
+import org.apache.qpid.test.utils.QpidTestCase;
+
+/**
+ * The quoted text in the test method javadoc is taken from RFC 5802.
+ */
+public abstract class AbstractScramSHAMechanismTestBase extends QpidTestCase
+{
+    private final byte[] expectedClientInitialResponse;
+    private final byte[] serverFirstMessage;
+    private final byte[] expectedClientFinalMessage;
+    private final byte[] serverFinalMessage;
+
+    public AbstractScramSHAMechanismTestBase(byte[] expectedClientInitialResponse,
+                                             byte[] serverFirstMessage,
+                                             byte[] expectedClientFinalMessage,
+                                             byte[] serverFinalMessage)
+    {
+        this.expectedClientInitialResponse = expectedClientInitialResponse;
+        this.serverFirstMessage = serverFirstMessage;
+        this.expectedClientFinalMessage = expectedClientFinalMessage;
+        this.serverFinalMessage = serverFinalMessage;
+    }
+
+    protected abstract AbstractScramSaslClient getScramSaslClient() throws Exception;
+    protected abstract AbstractScramSaslClient getScramSaslClient(String username, String password) throws Exception;
+    protected abstract String getExpectedInitialResponseString(final String escapedUsername);
+
+
+    public void testSuccessfulAuthentication() throws Exception
+    {
+        AbstractScramSaslClient mechanism = getScramSaslClient();
+
+        byte[] clientInitialResponse = mechanism.evaluateChallenge(null);
+        assertArrayEquals(expectedClientInitialResponse, clientInitialResponse);
+
+        byte[] clientFinalMessage = mechanism.evaluateChallenge(serverFirstMessage);
+        assertArrayEquals(expectedClientFinalMessage, clientFinalMessage);
+
+        byte[] expectedFinalChallengeResponse = "".getBytes();
+        assertArrayEquals(expectedFinalChallengeResponse, mechanism.evaluateChallenge(serverFinalMessage));
+
+        assertTrue(mechanism.isComplete());
+    }
+
+    public void testServerFirstMessageMalformed() throws Exception
+    {
+        AbstractScramSaslClient mechanism = getScramSaslClient();
+
+        mechanism.evaluateChallenge(null);
+        try
+        {
+            mechanism.evaluateChallenge("badserverfirst".getBytes());
+            fail("Exception not thrown");
+        }
+        catch (SaslException s)
+        {
+            // PASS
+        }
+    }
+
+    /**
+     * 5.1.  SCRAM Attributes
+     * "m: This attribute is reserved for future extensibility.  In this
+     * version of SCRAM, its presence in a client or a server message
+     * MUST cause authentication failure when the attribute is parsed by
+     * the other end."
+     *
+     * @throws Exception if an unexpected exception is thrown.
+     */
+    public void testServerFirstMessageMandatoryExtensionRejected() throws Exception
+    {
+        AbstractScramSaslClient mechanism = getScramSaslClient();
+
+        mechanism.evaluateChallenge(null);
+        try
+        {
+            mechanism.evaluateChallenge("m=notsupported,s=,i=".getBytes());
+            fail("Exception not thrown");
+        }
+        catch (SaslException s)
+        {
+            // PASS
+        }
+    }
+
+    /**
+     * 5.  SCRAM Authentication Exchange
+     * "In [the server first] response, the server sends a "server-first-message" containing the
+     * user's iteration count i and the user's salt, and appends its own
+     * nonce to the client-specified one."
+     *
+     * @throws Exception if an unexpected exception is thrown.
+     */
+    public void testServerFirstMessageInvalidNonceRejected() throws Exception
+    {
+        AbstractScramSaslClient mechanism = getScramSaslClient();
+
+        mechanism.evaluateChallenge(null);
+        try
+        {
+            mechanism.evaluateChallenge("r=invalidnonce,s=W22ZaJ0SNY7soEsUEjb6gQ==,i=4096".getBytes());
+            fail("Exception not thrown");
+        }
+        catch (SaslException s)
+        {
+            // PASS
+        }
+    }
+
+    /**
+     * 5.  SCRAM Authentication Exchange
+     * "The client then authenticates the server by computing the
+     * ServerSignature and comparing it to the value sent by the server.  If
+     * the two are different, the client MUST consider the authentication
+     * exchange to be unsuccessful, and it might have to drop the
+     * connection."
+     *
+     * @throws Exception if an unexpected exception is thrown.
+     */
+    public void testServerSignatureDiffer() throws Exception
+    {
+        AbstractScramSaslClient mechanism = getScramSaslClient();
+
+        mechanism.evaluateChallenge(null);
+        mechanism.evaluateChallenge(serverFirstMessage);
+        try
+        {
+            mechanism.evaluateChallenge("v=badserverfinal".getBytes(StandardCharsets.US_ASCII));
+            fail("Exception not thrown");
+        }
+        catch (SaslException e)
+        {
+            // PASS
+        }
+    }
+
+    public void testIncompleteExchange() throws Exception
+    {
+        AbstractScramSaslClient mechanism = getScramSaslClient();
+
+        byte[] clientInitialResponse = mechanism.evaluateChallenge(null);
+        assertArrayEquals(expectedClientInitialResponse, clientInitialResponse);
+
+        byte[] clientFinalMessage = mechanism.evaluateChallenge(serverFirstMessage);
+        assertArrayEquals(expectedClientFinalMessage, clientFinalMessage);
+
+        assertFalse(mechanism.isComplete());
+    }
+
+    public void testDifferentClientNonceOnEachInstance() throws Exception
+    {
+        AbstractScramSaslClient mech = getScramSaslClient();
+
+        AbstractScramSaslClient mech2 = getScramSaslClient();
+
+        byte[] clientInitialResponse = mech.evaluateChallenge(null);
+        byte[] clientInitialResponse2 = mech2.evaluateChallenge(null);
+
+        assertTrue(new String(clientInitialResponse, StandardCharsets.UTF_8).startsWith("n,,n=user,r="));
+        assertTrue(new String(clientInitialResponse2, StandardCharsets.UTF_8).startsWith("n,,n=user,r="));
+
+        assertNotEquals(clientInitialResponse, clientInitialResponse2);
+    }
+
+
+    public void testUsernameCommaEqualsCharactersEscaped() throws Exception
+    {
+        String originalUsername = "user,name=";
+        String escapedUsername = "user=2Cname=3D";
+
+        String expectedInitialResponseString = getExpectedInitialResponseString(escapedUsername);
+
+        AbstractScramSaslClient mech = getScramSaslClient(originalUsername, "password");
+        byte[] clientInitialResponse = mech.evaluateChallenge(null);
+        assertArrayEquals(expectedInitialResponseString.getBytes(StandardCharsets.UTF_8), clientInitialResponse);
+    }
+}

http://git-wip-us.apache.org/repos/asf/qpid-jms-amqp-0-x/blob/1043d0f4/client/src/test/java/org/apache/qpid/client/security/scram/ScramSHA1MechanismTest.java
----------------------------------------------------------------------
diff --git a/client/src/test/java/org/apache/qpid/client/security/scram/ScramSHA1MechanismTest.java b/client/src/test/java/org/apache/qpid/client/security/scram/ScramSHA1MechanismTest.java
new file mode 100644
index 0000000..8824bb6
--- /dev/null
+++ b/client/src/test/java/org/apache/qpid/client/security/scram/ScramSHA1MechanismTest.java
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.qpid.client.security.scram;
+
+import static org.junit.Assert.assertArrayEquals;
+
+import java.nio.charset.StandardCharsets;
+
+import org.apache.qpid.client.AMQConnectionURL;
+import org.apache.qpid.client.security.UsernamePasswordCallbackHandler;
+import org.apache.qpid.jms.ConnectionURL;
+
+/**
+ * The known good used by these tests is taken from the example in RFC 5802 section 5.
+ */
+public class ScramSHA1MechanismTest extends AbstractScramSHAMechanismTestBase
+{
+
+    private static final String USERNAME = "user";
+    private static final String PASSWORD = "pencil";
+
+    private static final String CLIENT_NONCE = "fyko+d2lbbFgONRv9qkxdawL";
+
+    private static final byte[] EXPECTED_CLIENT_INITIAL_RESPONSE =
+            "n,,n=user,r=fyko+d2lbbFgONRv9qkxdawL".getBytes(StandardCharsets.UTF_8);
+    private static final byte[] SERVER_FIRST_MESSAGE =
+            "r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j,s=QSXCR+Q6sek8bf92,i=4096".getBytes(StandardCharsets.UTF_8);
+    private static final byte[] EXPECTED_CLIENT_FINAL_MESSAGE =
+            "c=biws,r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j,p=v0X8v3Bz2T0CJGbJQyF0X+HI4Ts=".getBytes(
+                    StandardCharsets.UTF_8);
+    private static final byte[] SERVER_FINAL_MESSAGE =
+            "v=rmF9pqV8S7suAoZWja4dJRkFsKQ=".getBytes(StandardCharsets.UTF_8);
+
+    public ScramSHA1MechanismTest()
+    {
+        super(EXPECTED_CLIENT_INITIAL_RESPONSE,
+              SERVER_FIRST_MESSAGE,
+              EXPECTED_CLIENT_FINAL_MESSAGE,
+              SERVER_FINAL_MESSAGE);
+    }
+
+    @Override
+    protected AbstractScramSaslClient getScramSaslClient() throws Exception
+    {
+        return getScramSaslClient(USERNAME, PASSWORD);
+    }
+
+    @Override
+    protected AbstractScramSaslClient getScramSaslClient(final String username, final String password) throws Exception
+    {
+
+        UsernamePasswordCallbackHandler callbackHandler = new UsernamePasswordCallbackHandler();
+        ConnectionURL connectionURL = new AMQConnectionURL(String.format("amqp://%s:%s@////", username, password));
+        callbackHandler.initialise(connectionURL);
+
+        return new ScramSHA1SaslClient(callbackHandler, CLIENT_NONCE);
+    }
+
+    @Override
+    protected String getExpectedInitialResponseString(final String escapedUsername)
+    {
+        return "n,,n=" + escapedUsername + ",r=" + CLIENT_NONCE;
+    }
+
+    public void testPasswordCommaEqualsCharactersNotEscaped() throws Exception
+    {
+        AbstractScramSaslClient mechanism = getScramSaslClient(USERNAME, PASSWORD + ",=");
+
+        byte[] clientInitialResponse = mechanism.evaluateChallenge(null);
+        assertArrayEquals(EXPECTED_CLIENT_INITIAL_RESPONSE, clientInitialResponse);
+
+        byte[] serverFirstMessage =
+                "r=fyko+d2lbbFgONRv9qkxdawLdcbfa301-1618-46ee-96c1-2bf60139dc7f,s=Q0zM1qzKMOmI0sAzE7dXt6ru4ZIXhAzn40g4mQXKQdw=,i=4096"
+                        .getBytes(StandardCharsets.UTF_8);
+        byte[] expectedClientFinalMessage =
+                "c=biws,r=fyko+d2lbbFgONRv9qkxdawLdcbfa301-1618-46ee-96c1-2bf60139dc7f,p=quRNWvZqGUvPXoazebZe0ZYsjQI=".getBytes(
+                        StandardCharsets.UTF_8);
+
+        byte[] clientFinalMessage = mechanism.evaluateChallenge(serverFirstMessage);
+
+        assertArrayEquals(expectedClientFinalMessage, clientFinalMessage);
+
+        byte[] serverFinalMessage = "v=dnJDHm3fp6WwVrl5yjZuqKp03lQ=".getBytes(StandardCharsets.UTF_8);
+        byte[] expectedFinalChallengeResponse = "".getBytes();
+
+        assertArrayEquals(expectedFinalChallengeResponse, mechanism.evaluateChallenge(serverFinalMessage));
+
+        assertTrue(mechanism.isComplete());
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/qpid-jms-amqp-0-x/blob/1043d0f4/client/src/test/java/org/apache/qpid/client/security/scram/ScramSHA256MechanismTest.java
----------------------------------------------------------------------
diff --git a/client/src/test/java/org/apache/qpid/client/security/scram/ScramSHA256MechanismTest.java b/client/src/test/java/org/apache/qpid/client/security/scram/ScramSHA256MechanismTest.java
new file mode 100644
index 0000000..18c0770
--- /dev/null
+++ b/client/src/test/java/org/apache/qpid/client/security/scram/ScramSHA256MechanismTest.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.qpid.client.security.scram;
+
+import org.apache.qpid.client.AMQConnectionURL;
+import org.apache.qpid.client.security.UsernamePasswordCallbackHandler;
+import org.apache.qpid.jms.ConnectionURL;
+
+/**
+ * The known good used by these tests is taken from the example in RFC 7677 section 3.
+ */
+public class ScramSHA256MechanismTest extends AbstractScramSHAMechanismTestBase {
+
+    private static final String USERNAME = "user";
+    private static final String PASSWORD = "pencil";
+
+    private static final String CLIENT_NONCE = "rOprNGfwEbeRWgbNEkqO";
+
+    private static final byte[] EXPECTED_CLIENT_INITIAL_RESPONSE = "n,,n=user,r=rOprNGfwEbeRWgbNEkqO".getBytes();
+    private static final byte[] SERVER_FIRST_MESSAGE = "r=rOprNGfwEbeRWgbNEkqO%hvYDpWUa2RaTCAfuxFIlj)hNlF$k0,s=W22ZaJ0SNY7soEsUEjb6gQ==,i=4096".getBytes();
+    private static final byte[] EXPECTED_CLIENT_FINAL_MESSAGE = "c=biws,r=rOprNGfwEbeRWgbNEkqO%hvYDpWUa2RaTCAfuxFIlj)hNlF$k0,p=dHzbZapWIk4jUhN+Ute9ytag9zjfMHgsqmmiz7AndVQ=".getBytes();
+    private static final byte[] SERVER_FINAL_MESSAGE = "v=6rriTRBi23WpRR/wtup+mMhUZUn/dB5nLTJRsjl95G4=".getBytes();
+
+    public ScramSHA256MechanismTest() {
+        super(EXPECTED_CLIENT_INITIAL_RESPONSE,
+              SERVER_FIRST_MESSAGE,
+              EXPECTED_CLIENT_FINAL_MESSAGE,
+              SERVER_FINAL_MESSAGE);
+    }
+
+    @Override
+    protected AbstractScramSaslClient getScramSaslClient() throws Exception
+    {
+        return getScramSaslClient(USERNAME, PASSWORD);
+    }
+
+    @Override
+    protected AbstractScramSaslClient getScramSaslClient(String username, String password) throws Exception
+    {
+        UsernamePasswordCallbackHandler callbackHandler = new UsernamePasswordCallbackHandler();
+        ConnectionURL connectionURL = new AMQConnectionURL(String.format("amqp://%s:%s@////", username, password));
+        callbackHandler.initialise(connectionURL);
+        return new ScramSHA256SaslClient(callbackHandler, CLIENT_NONCE);
+    }
+
+    @Override
+    protected String getExpectedInitialResponseString(final String escapedUsername)
+    {
+        return "n,,n=" + escapedUsername + ",r=" + CLIENT_NONCE;
+    }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org