Package org.jivesoftware.openfire.sasl
Class ScramSaslServer
java.lang.Object
org.jivesoftware.openfire.sasl.ScramSaslServer
- All Implemented Interfaces:
SaslServer
- Direct Known Subclasses:
ScramSha1SaslServer,ScramSha256SaslServer,ScramSha512SaslServer
Implements the server side of the SCRAM SASL exchange as defined in RFC 5802, including the channel binding (-PLUS)
variants defined there and profiled for other hash functions in RFC 7677.
The exchange logic in this class is hash-function agnostic. Concrete subclasses bind a specific hash function by
implementing the small set of abstract methods: the base mechanism name (which doubles as the credential storage
key), the HMAC and message digest algorithm names, the default iteration count, and the server-side secret used to
derive indistinguishable fake credentials for non-existent users.
Instances are session-specific and must not be reused across sessions or users. The available SASL mechanisms are
established when the instance is created and are used, in particular, to correctly process the GS2 header and enforce
channel-binding downgrade protection.
- Author:
- Richard Midwinter, Guus der Kinderen
-
Field Summary
FieldsModifier and TypeFieldDescriptionstatic final StringThe name of the negotiated property through which the channel binding type that was used during authentication is exposed. -
Constructor Summary
ConstructorsModifierConstructorDescriptionprotectedScramSaslServer(boolean isPlusMechanism, Map<String, ?> props, ChannelBindingProviderManager channelBindingProviderManager, Set<String> availableMechanismsForSession) Creates a new, client-specific, instance. -
Method Summary
Modifier and TypeMethodDescriptionvoiddispose()Disposes of any system resources or security-sensitive information the SaslServer might be using.byte[]evaluateResponse(byte[] response) Evaluates a single client response and advances the SCRAM exchange one step.protected static byte[]extractRawGS2Header(byte[] data) Extracts the raw GS2 header from a SCRAM client-first-message byte array.Reports the authorization ID in effect for the client of this session.protected abstract intThe iteration count to advertise when no per-user value is available.protected abstract StringThe JCA name of the message digest that corresponds to this mechanism's hash function, for exampleSHA-1.protected abstract StringThe JCA name of the HMAC algorithm that corresponds to this mechanism's hash function, for exampleHmacSHA1.protected abstract StringThe IANA-registered name of the base (non-PLUS) mechanism implemented by this server, for exampleSCRAM-SHA-1.Returns the IANA-registered mechanism name of this SASL server, which is the base mechanism name with a-PLUSsuffix for the channel binding variant.getNegotiatedProperty(String propName) Retrieves the negotiated property.protected abstract StringA server-side secret from which deterministic fake credentials are derived for non-existent users, so that authentication processing for non-existing users is indistinguishable from that of existing users.protected byte[]getOrCreateSalt(String username) Retrieve the salt for a given username.protected byte[]getOrFakeServerKey(String username) Retrieve the server key from the database for a given username, but returns a fake key if none is found.protected byte[]getOrFakeStoredKey(String username) Retrieve the stored key from the database for a given username, but returns a fake key if none is found.booleanDetermines whether the authentication exchange has completed.byte[]unwrap(byte[] incoming, int offset, int len) Unwraps a byte array received from the client.byte[]wrap(byte[] outgoing, int offset, int len) Wraps a byte array to be sent to the client.
-
Field Details
-
PROPNAME_CHANNELBINDINGTYPE
The name of the negotiated property through which the channel binding type that was used during authentication is exposed.- See Also:
-
-
Constructor Details
-
ScramSaslServer
protected ScramSaslServer(boolean isPlusMechanism, Map<String, ?> props, ChannelBindingProviderManager channelBindingProviderManager, @Nonnull Set<String> availableMechanismsForSession) Creates a new, client-specific, instance.- Parameters:
isPlusMechanism- Denotes if this instance supports channel-binding (true) or not (false).props- The possibly null set of properties used to select the SASL mechanism and to configure the authentication exchange of the selected mechanism.channelBindingProviderManager- Manages a set of providers that can extract channel binding data of various types from SSL engines. Must be set for plus-mechanisms.availableMechanismsForSession- The names of SASL mechanisms that are available to this particular session (as opposed to the set of globally available mechanism names).
-
-
Method Details
-
getMechanismBaseName
The IANA-registered name of the base (non-PLUS) mechanism implemented by this server, for exampleSCRAM-SHA-1. This value is also the key under which SCRAM credentials for this mechanism are stored: the -PLUS variant shares the credential of the base mechanism.- Returns:
- A non-null string representing the IANA-registered (base) mechanism name.
-
getHmacAlgorithmName
The JCA name of the HMAC algorithm that corresponds to this mechanism's hash function, for exampleHmacSHA1.- Returns:
- the HMAC algorithm name.
-
getDigestAlgorithmName
The JCA name of the message digest that corresponds to this mechanism's hash function, for exampleSHA-1. Used to computeH(ClientKey)when verifying the client proof.- Returns:
- the message digest algorithm name.
-
getDefaultIterationCount
protected abstract int getDefaultIterationCount()The iteration count to advertise when no per-user value is available.- Returns:
- the default iteration count for this mechanism.
-
getNonExistentUserSecret
A server-side secret from which deterministic fake credentials are derived for non-existent users, so that authentication processing for non-existing users is indistinguishable from that of existing users.- Returns:
- the server secret for this mechanism.
- See Also:
-
getMechanismName
Returns the IANA-registered mechanism name of this SASL server, which is the base mechanism name with a-PLUSsuffix for the channel binding variant.- Specified by:
getMechanismNamein interfaceSaslServer- Returns:
- A non-null string representing the IANA-registered mechanism name.
-
evaluateResponse
Evaluates a single client response and advances the SCRAM exchange one step. Dispatch is driven bystate:ScramSaslServer.State.INITIALtreats the response asclient-first-message(seegenerateServerFirstMessage(byte[]));ScramSaslServer.State.IN_PROGRESStreats it asclient-final-message(seegenerateServerFinalMessage(byte[])); onceScramSaslServer.State.COMPLETE, an empty response is tolerated but a non-empty one is rejected. AnyRuntimeExceptionthrown while processing is re-wrapped as aSaslException, so an implementation defect surfaces as a failed authentication attempt rather than an unchecked exception.- Specified by:
evaluateResponsein interfaceSaslServer- Parameters:
response- The non-null (but possibly empty) response sent by the client.- Returns:
- The possibly null challenge to send to the client; null only once the exchange has concluded.
- Throws:
SaslException- if the response is invalid for the current state, or arrives after completion.
-
isComplete
public boolean isComplete()Determines whether the authentication exchange has completed. This method is typically called after each invocation ofevaluateResponse()to determine whether the authentication has completed successfully or should be continued.- Specified by:
isCompletein interfaceSaslServer- Returns:
- true if the authentication exchange has completed; false otherwise.
-
getAuthorizationID
Reports the authorization ID in effect for the client of this session. This method can only be called if isComplete() returns true.- Specified by:
getAuthorizationIDin interfaceSaslServer- Returns:
- The authorization ID of the client.
- Throws:
IllegalStateException- if this authentication session has not completed
-
unwrap
Unwraps a byte array received from the client. SCRAM supports no security layer.- Specified by:
unwrapin interfaceSaslServer- Returns:
- the unwrapped byte array.
- Throws:
SaslException- if attempted to use this method.
-
wrap
Wraps a byte array to be sent to the client. SCRAM supports no security layer.- Specified by:
wrapin interfaceSaslServer- Throws:
SaslException- if attempted to use this method.
-
getNegotiatedProperty
Retrieves the negotiated property. This method can be called only after the authentication exchange has completed (i.e., whenisComplete()returns true); otherwise, anIllegalStateExceptionis thrown.- Specified by:
getNegotiatedPropertyin interfaceSaslServer- Parameters:
propName- the property- Returns:
- The value of the negotiated property. If null, the property was not negotiated or is not applicable to this mechanism.
- Throws:
IllegalStateException- if this authentication exchange has not completed
-
dispose
Disposes of any system resources or security-sensitive information the SaslServer might be using. Invoking this method invalidates the SaslServer instance. This method is idempotent.- Specified by:
disposein interfaceSaslServer- Throws:
SaslException- If a problem was encountered while disposing the resources.
-
getOrCreateSalt
Retrieve the salt for a given username. When a salt does not currently exist for an existing user, but a password is set, that value is used to create and persist a new salt for that user. Returns a username-specific salt if the user doesn't exist to mimic an invalid password. This also guards against user enumeration attacks. -
getOrFakeServerKey
Retrieve the server key from the database for a given username, but returns a fake key if none is found.Returning a fake key helps guard against timing attacks: instead of short-circuiting the operation, a fake key is generated to ensure consistent response times and prevent potential timing attacks.
- Returns:
- The server key for the given username.
- See Also:
-
getOrFakeStoredKey
Retrieve the stored key from the database for a given username, but returns a fake key if none is found.Returning a fake key helps guard against timing attacks: instead of short-circuiting the operation, a fake key is generated to ensure consistent response times and prevent potential timing attacks.
- Returns:
- The stored key for the given username.
- See Also:
-
extractRawGS2Header
Extracts the raw GS2 header from a SCRAM client-first-message byte array. The GS2 header is defined in RFC 5802 as:gs2-header = gs2-cbind-flag "," [authzid] ","
and always terminates with a trailing comma. This method performs a byte-level scan of the input and returns a copy of the original byte array from index0up to and including the second comma (i.e., the full GS2 header including its trailing comma). No character decoding or normalization is performed. This ensures that the returned GS2 header is byte-for- byte identical to the original input, which is required for correct -PLUS channel binding validation in SCRAM mechanisms.- Parameters:
data- the raw SCRAM client-first-message bytes- Returns:
- a byte array containing the complete GS2 header including the trailing comma
- Throws:
SaslException- if the input does not contain a valid GS2 header
-