Class HtSaslServer

java.lang.Object
org.jivesoftware.openfire.sasl.HtSaslServer
All Implemented Interfaces:
SaslServer

public class HtSaslServer extends Object
Implementation of the HT-* family of SASL mechanisms for FAST (XEP-0484), supporting all hash (SHA-256, SHA-512, SHA3-512) and channel-binding (NONE, UNIQ, ENDP, EXPR) variants.

The HT-09 initial response is authcid NUL initiator-hashed-token.

This is a single-round-trip mechanism: the client sends the initial response and the server returns a responder HMAC on success for mutual authentication.

Channel-binding data is resolved by the base class AbstractHtSaslServer before this class's doEvaluateResponse(byte[], byte[]) is called. For channel-binding variants the data is incorporated into both HMAC proofs.

See Also:
  • Field Details

    • mechanismName

      protected final String mechanismName
      The SASL mechanism name (e.g. HT-SHA-256-NONE or HT2-SHA-512-EXPR).
    • props

      protected final Map<String,?> props
      The SASL properties map; must contain a LocalSession under LocalSession.class.getCanonicalName()
    • tokenValidator

      protected final org.jivesoftware.openfire.sasl.AbstractHtSaslServer.HashedTokenValidator tokenValidator
    • complete

      protected boolean complete
    • authorizationId

      protected String authorizationId
    • rotatedToken

      protected FastToken rotatedToken
  • Constructor Details

    • HtSaslServer

      public HtSaslServer(@Nonnull String mechanismName, @Nonnull Map<String,?> props)
      Constructs an HtSaslServer for the given mechanism name.

      The mechanism name must follow the pattern HT-{HASH}-{CBTYPE}, e.g. HT-SHA-256-NONE, HT-SHA-512-UNIQ, or HT-SHA3-512-EXPR.

      Parameters:
      mechanismName - the SASL mechanism name (cannot be null)
      props - the SASL properties map, which must contain the LocalSession instance under LocalSession.class.getCanonicalName() for UNIQ/ENDP/EXPR channel-binding variants (cannot be null)
  • Method Details

    • doEvaluateResponse

      protected byte[] doEvaluateResponse(byte[] response, byte[] channelBindingData) throws SaslException
      Evaluates the client's initial response (mechanism-specific part).

      Called by SaslServer.evaluateResponse(byte[]) after guard checks and channel-binding resolution. The channelBindingData bytes have already been fetched from the live TLS session (or are empty for NONE variants) and are incorporated into both HT-* HMAC proofs.

      Expected format: authcid NUL initiator-hashed-token.

      Parameters:
      response - the client's initial response bytes (never null or empty)
      channelBindingData - the resolved channel-binding bytes (empty for NONE variants)
      Returns:
      the responder HMAC
      Throws:
      SaslException - if authentication fails
    • recordAuthenticatedClient

      protected final void recordAuthenticatedClient(String clientId)
    • getMechanismName

      public String getMechanismName()
      Specified by:
      getMechanismName in interface SaslServer
    • evaluateResponse

      public final byte[] evaluateResponse(byte[] response) throws SaslException
      Evaluates the client's response.

      This method handles the common guard checks (already-complete, null/empty response) and resolves channel-binding data for the mechanism's CB variant before delegating to doEvaluateResponse(byte[], byte[]).

      Specified by:
      evaluateResponse in interface SaslServer
      Parameters:
      response - the client response bytes
      Returns:
      mechanism-specific success bytes (the responder HMAC for HT-*, or the framed responder proof for HT2-*)
      Throws:
      SaslException - if authentication fails
    • resolveChannelBindingData

      protected byte[] resolveChannelBindingData() throws SaslException
      Resolves the channel-binding data for this mechanism.

      The channel-binding type is derived from the mechanism name suffix: -UNIQtls-unique, -ENDPtls-server-end-point, -EXPRtls-exporter, -NONE → no channel binding (empty array).

      For non-NONE variants the server verifies that the required binding type is available, retrieves the actual bytes from the live TLS session, and throws SaslException if they cannot be obtained — matching the SCRAM-SHA-1-PLUS behaviour.

      Returns:
      the channel-binding bytes (never null; empty array for NONE variants)
      Throws:
      SaslException - if channel-binding data is required but cannot be retrieved
    • isComplete

      public boolean isComplete()
      Specified by:
      isComplete in interface SaslServer
    • getAuthorizationID

      public String getAuthorizationID()
      Specified by:
      getAuthorizationID in interface SaslServer
    • getRotatedToken

      public FastToken getRotatedToken()
      Returns the rotated FAST token produced after successful authentication, or null if authentication has not completed successfully.
      Returns:
      the rotated FastToken, or null
    • unwrap

      public byte[] unwrap(byte[] incoming, int offset, int len) throws SaslException
      Specified by:
      unwrap in interface SaslServer
      Throws:
      SaslException
    • wrap

      public byte[] wrap(byte[] outgoing, int offset, int len) throws SaslException
      Specified by:
      wrap in interface SaslServer
      Throws:
      SaslException
    • getNegotiatedProperty

      public Object getNegotiatedProperty(String propName)
      Specified by:
      getNegotiatedProperty in interface SaslServer
    • dispose

      public void dispose() throws SaslException
      Specified by:
      dispose in interface SaslServer
      Throws:
      SaslException
    • indexOf

      protected static int indexOf(byte[] array, byte target, int fromIndex)
      Returns the index of the first occurrence of target in array starting at fromIndex, or -1 if not found.
    • decodeUtf8

      protected final String decodeUtf8(byte[] value, int offset, int length, String field) throws SaslException
      Throws:
      SaslException
    • decodeAuthcId

      protected String decodeAuthcId(@Nonnull String value) throws SaslException
      Converts the authcid from a FAST initiator message into a normalized local username. The value is expected to be a bare username, but a domain-qualified form (username@domain) is also accepted, provided that the domain matches the domain of this server. In both cases the returned value is the stringprep'ed node, which is the form that the caller compares against the username claimed in the stream's 'from' attribute.
      Parameters:
      value - the raw authcid as sent by the client (cannot be null)
      Returns:
      the normalized local username (never null)
      Throws:
      SaslException - if the value cannot be prepared as a username, or names another domain