blob: 53d911ce79561fe3928da82298ca7ffe871e491a [file] [log] [blame]
/*
* 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);
}
}