use of org.apache.kafka.common.security.scram.internals.ScramMessages.ServerFirstMessage in project kafka by apache.
the class ScramSaslClient method evaluateChallenge.
@Override
public byte[] evaluateChallenge(byte[] challenge) throws SaslException {
try {
switch(state) {
case SEND_CLIENT_FIRST_MESSAGE:
if (challenge != null && challenge.length != 0)
throw new SaslException("Expected empty challenge");
clientNonce = formatter.secureRandomString();
NameCallback nameCallback = new NameCallback("Name:");
ScramExtensionsCallback extensionsCallback = new ScramExtensionsCallback();
try {
callbackHandler.handle(new Callback[] { nameCallback });
try {
callbackHandler.handle(new Callback[] { extensionsCallback });
} catch (UnsupportedCallbackException e) {
log.debug("Extensions callback is not supported by client callback handler {}, no extensions will be added", callbackHandler);
}
} catch (Throwable e) {
throw new SaslException("User name or extensions could not be obtained", e);
}
String username = nameCallback.getName();
String saslName = ScramFormatter.saslName(username);
Map<String, String> extensions = extensionsCallback.extensions();
this.clientFirstMessage = new ScramMessages.ClientFirstMessage(saslName, clientNonce, extensions);
setState(State.RECEIVE_SERVER_FIRST_MESSAGE);
return clientFirstMessage.toBytes();
case RECEIVE_SERVER_FIRST_MESSAGE:
this.serverFirstMessage = new ServerFirstMessage(challenge);
if (!serverFirstMessage.nonce().startsWith(clientNonce))
throw new SaslException("Invalid server nonce: does not start with client nonce");
if (serverFirstMessage.iterations() < mechanism.minIterations())
throw new SaslException("Requested iterations " + serverFirstMessage.iterations() + " is less than the minimum " + mechanism.minIterations() + " for " + mechanism);
PasswordCallback passwordCallback = new PasswordCallback("Password:", false);
try {
callbackHandler.handle(new Callback[] { passwordCallback });
} catch (Throwable e) {
throw new SaslException("User name could not be obtained", e);
}
this.clientFinalMessage = handleServerFirstMessage(passwordCallback.getPassword());
setState(State.RECEIVE_SERVER_FINAL_MESSAGE);
return clientFinalMessage.toBytes();
case RECEIVE_SERVER_FINAL_MESSAGE:
ServerFinalMessage serverFinalMessage = new ServerFinalMessage(challenge);
if (serverFinalMessage.error() != null)
throw new SaslException("Sasl authentication using " + mechanism + " failed with error: " + serverFinalMessage.error());
handleServerFinalMessage(serverFinalMessage.serverSignature());
setState(State.COMPLETE);
return null;
default:
throw new IllegalSaslStateException("Unexpected challenge in Sasl client state " + state);
}
} catch (SaslException e) {
setState(State.FAILED);
throw e;
}
}
use of org.apache.kafka.common.security.scram.internals.ScramMessages.ServerFirstMessage in project kafka by apache.
the class ScramSaslServer method evaluateResponse.
/**
* @throws SaslAuthenticationException if the requested authorization id is not the same as username.
* <p>
* <b>Note:</b> This method may throw {@link SaslAuthenticationException} to provide custom error messages
* to clients. But care should be taken to avoid including any information in the exception message that
* should not be leaked to unauthenticated clients. It may be safer to throw {@link SaslException} in
* most cases so that a standard error message is returned to clients.
* </p>
*/
@Override
public byte[] evaluateResponse(byte[] response) throws SaslException, SaslAuthenticationException {
try {
switch(state) {
case RECEIVE_CLIENT_FIRST_MESSAGE:
this.clientFirstMessage = new ClientFirstMessage(response);
this.scramExtensions = clientFirstMessage.extensions();
if (!SUPPORTED_EXTENSIONS.containsAll(scramExtensions.map().keySet())) {
log.debug("Unsupported extensions will be ignored, supported {}, provided {}", SUPPORTED_EXTENSIONS, scramExtensions.map().keySet());
}
String serverNonce = formatter.secureRandomString();
try {
String saslName = clientFirstMessage.saslName();
this.username = ScramFormatter.username(saslName);
NameCallback nameCallback = new NameCallback("username", username);
ScramCredentialCallback credentialCallback;
if (scramExtensions.tokenAuthenticated()) {
DelegationTokenCredentialCallback tokenCallback = new DelegationTokenCredentialCallback();
credentialCallback = tokenCallback;
callbackHandler.handle(new Callback[] { nameCallback, tokenCallback });
if (tokenCallback.tokenOwner() == null)
throw new SaslException("Token Authentication failed: Invalid tokenId : " + username);
this.authorizationId = tokenCallback.tokenOwner();
this.tokenExpiryTimestamp = tokenCallback.tokenExpiryTimestamp();
} else {
credentialCallback = new ScramCredentialCallback();
callbackHandler.handle(new Callback[] { nameCallback, credentialCallback });
this.authorizationId = username;
this.tokenExpiryTimestamp = null;
}
this.scramCredential = credentialCallback.scramCredential();
if (scramCredential == null)
throw new SaslException("Authentication failed: Invalid user credentials");
String authorizationIdFromClient = clientFirstMessage.authorizationId();
if (!authorizationIdFromClient.isEmpty() && !authorizationIdFromClient.equals(username))
throw new SaslAuthenticationException("Authentication failed: Client requested an authorization id that is different from username");
if (scramCredential.iterations() < mechanism.minIterations())
throw new SaslException("Iterations " + scramCredential.iterations() + " is less than the minimum " + mechanism.minIterations() + " for " + mechanism);
this.serverFirstMessage = new ServerFirstMessage(clientFirstMessage.nonce(), serverNonce, scramCredential.salt(), scramCredential.iterations());
setState(State.RECEIVE_CLIENT_FINAL_MESSAGE);
return serverFirstMessage.toBytes();
} catch (SaslException | AuthenticationException e) {
throw e;
} catch (Throwable e) {
throw new SaslException("Authentication failed: Credentials could not be obtained", e);
}
case RECEIVE_CLIENT_FINAL_MESSAGE:
try {
ClientFinalMessage clientFinalMessage = new ClientFinalMessage(response);
verifyClientProof(clientFinalMessage);
byte[] serverKey = scramCredential.serverKey();
byte[] serverSignature = formatter.serverSignature(serverKey, clientFirstMessage, serverFirstMessage, clientFinalMessage);
ServerFinalMessage serverFinalMessage = new ServerFinalMessage(null, serverSignature);
clearCredentials();
setState(State.COMPLETE);
return serverFinalMessage.toBytes();
} catch (InvalidKeyException e) {
throw new SaslException("Authentication failed: Invalid client final message", e);
}
default:
throw new IllegalSaslStateException("Unexpected challenge in Sasl server state " + state);
}
} catch (SaslException | AuthenticationException e) {
clearCredentials();
setState(State.FAILED);
throw e;
}
}
use of org.apache.kafka.common.security.scram.internals.ScramMessages.ServerFirstMessage in project kafka by apache.
the class ScramFormatterTest method rfc7677Example.
/**
* Tests that the formatter implementation produces the same values for the
* example included in <a href="https://tools.ietf.org/html/rfc5802#section-5">RFC 7677</a>
*/
@Test
public void rfc7677Example() throws Exception {
ScramFormatter formatter = new ScramFormatter(ScramMechanism.SCRAM_SHA_256);
String password = "pencil";
String c1 = "n,,n=user,r=rOprNGfwEbeRWgbNEkqO";
String s1 = "r=rOprNGfwEbeRWgbNEkqO%hvYDpWUa2RaTCAfuxFIlj)hNlF$k0,s=W22ZaJ0SNY7soEsUEjb6gQ==,i=4096";
String c2 = "c=biws,r=rOprNGfwEbeRWgbNEkqO%hvYDpWUa2RaTCAfuxFIlj)hNlF$k0,p=dHzbZapWIk4jUhN+Ute9ytag9zjfMHgsqmmiz7AndVQ=";
String s2 = "v=6rriTRBi23WpRR/wtup+mMhUZUn/dB5nLTJRsjl95G4=";
ClientFirstMessage clientFirst = new ClientFirstMessage(ScramFormatter.toBytes(c1));
ServerFirstMessage serverFirst = new ServerFirstMessage(ScramFormatter.toBytes(s1));
ClientFinalMessage clientFinal = new ClientFinalMessage(ScramFormatter.toBytes(c2));
ServerFinalMessage serverFinal = new ServerFinalMessage(ScramFormatter.toBytes(s2));
String username = clientFirst.saslName();
assertEquals("user", username);
String clientNonce = clientFirst.nonce();
assertEquals("rOprNGfwEbeRWgbNEkqO", clientNonce);
String serverNonce = serverFirst.nonce().substring(clientNonce.length());
assertEquals("%hvYDpWUa2RaTCAfuxFIlj)hNlF$k0", serverNonce);
byte[] salt = serverFirst.salt();
assertArrayEquals(Base64.getDecoder().decode("W22ZaJ0SNY7soEsUEjb6gQ=="), salt);
int iterations = serverFirst.iterations();
assertEquals(4096, iterations);
byte[] channelBinding = clientFinal.channelBinding();
assertArrayEquals(Base64.getDecoder().decode("biws"), channelBinding);
byte[] serverSignature = serverFinal.serverSignature();
assertArrayEquals(Base64.getDecoder().decode("6rriTRBi23WpRR/wtup+mMhUZUn/dB5nLTJRsjl95G4="), serverSignature);
byte[] saltedPassword = formatter.saltedPassword(password, salt, iterations);
byte[] serverKey = formatter.serverKey(saltedPassword);
byte[] computedProof = formatter.clientProof(saltedPassword, clientFirst, serverFirst, clientFinal);
assertArrayEquals(clientFinal.proof(), computedProof);
byte[] computedSignature = formatter.serverSignature(serverKey, clientFirst, serverFirst, clientFinal);
assertArrayEquals(serverFinal.serverSignature(), computedSignature);
// Minimum iterations defined in RFC-7677
assertEquals(4096, ScramMechanism.SCRAM_SHA_256.minIterations());
}
use of org.apache.kafka.common.security.scram.internals.ScramMessages.ServerFirstMessage in project kafka by apache.
the class ScramMessagesTest method validServerFirstMessage.
@Test
public void validServerFirstMessage() throws SaslException {
String clientNonce = formatter.secureRandomString();
String serverNonce = formatter.secureRandomString();
String nonce = clientNonce + serverNonce;
String salt = randomBytesAsString();
ServerFirstMessage m = new ServerFirstMessage(clientNonce, serverNonce, toBytes(salt), 8192);
checkServerFirstMessage(m, nonce, salt, 8192);
// Default format used by Kafka clients, only nonce, salt and iterations are specified
String str = String.format("r=%s,s=%s,i=4096", nonce, salt);
m = createScramMessage(ServerFirstMessage.class, str);
checkServerFirstMessage(m, nonce, salt, 4096);
m = new ServerFirstMessage(m.toBytes());
checkServerFirstMessage(m, nonce, salt, 4096);
// Optional reserved value
for (String reserved : VALID_RESERVED) {
str = String.format("%s,r=%s,s=%s,i=4096", reserved, nonce, salt);
checkServerFirstMessage(createScramMessage(ServerFirstMessage.class, str), nonce, salt, 4096);
}
// Optional extension
for (String extension : VALID_EXTENSIONS) {
str = String.format("r=%s,s=%s,i=4096,%s", nonce, salt, extension);
checkServerFirstMessage(createScramMessage(ServerFirstMessage.class, str), nonce, salt, 4096);
}
}
Aggregations