package gnu.crypto.sasl.srp; // ---------------------------------------------------------------------------- // $Id: SRPServer.java,v 1.7 2005/10/06 04:24:18 rsdio Exp $ // // Copyright (C) 2003 Free Software Foundation, Inc. // // This file is part of GNU Crypto. // // GNU Crypto is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2, or (at your option) // any later version. // // GNU Crypto is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; see the file COPYING. If not, write to the // // Free Software Foundation Inc., // 51 Franklin Street, Fifth Floor, // Boston, MA 02110-1301 // USA // // Linking this library statically or dynamically with other modules is // making a combined work based on this library. Thus, the terms and // conditions of the GNU General Public License cover the whole // combination. // // As a special exception, the copyright holders of this library give // you permission to link this library with independent modules to // produce an executable, regardless of the license terms of these // independent modules, and to copy and distribute the resulting // executable under terms of your choice, provided that you also meet, // for each linked independent module, the terms and conditions of the // license of that module. An independent module is a module which is // not derived from or based on this library. If you modify this // library, you may extend this exception to your version of the // library, but you are not obligated to do so. If you do not wish to // do so, delete this exception statement from your version. // ---------------------------------------------------------------------------- import gnu.crypto.Registry; import gnu.crypto.assembly.Direction; import gnu.crypto.cipher.CipherFactory; import gnu.crypto.cipher.IBlockCipher; import gnu.crypto.key.IKeyAgreementParty; import gnu.crypto.key.KeyAgreementFactory; import gnu.crypto.key.KeyAgreementException; import gnu.crypto.key.OutgoingMessage; import gnu.crypto.key.IncomingMessage; import gnu.crypto.key.srp6.SRP6KeyAgreement; import gnu.crypto.sasl.IllegalMechanismStateException; import gnu.crypto.sasl.InputBuffer; import gnu.crypto.sasl.IntegrityException; import gnu.crypto.sasl.OutputBuffer; import gnu.crypto.sasl.ServerMechanism; import gnu.crypto.util.PRNG; import gnu.crypto.util.Util; import java.io.IOException; import java.io.PrintWriter; import java.io.ByteArrayOutputStream; import java.io.UnsupportedEncodingException; import java.math.BigInteger; import java.util.Arrays; import java.util.HashMap; import java.util.StringTokenizer; import javax.security.sasl.AuthenticationException; import javax.security.sasl.SaslException; import javax.security.sasl.SaslServer; /** * <p>The SASL-SRP server-side mechanism.</p> * * @version $Revision: 1.7 $ */ public class SRPServer extends ServerMechanism implements SaslServer { // Debugging methods and variables // ------------------------------------------------------------------------- private static final String NAME = "SRPServer"; // private static final String ERROR = "ERROR"; private static final String WARN = " WARN"; private static final String INFO = " INFO"; private static final String TRACE = "DEBUG"; private static final boolean DEBUG = true; private static final int debuglevel = 3; private static final PrintWriter err = new PrintWriter(System.out, true); private static void debug(final String level, final Object obj) { err.println("["+level+"] "+NAME+": "+String.valueOf(obj)); } // Constants and variables // ------------------------------------------------------------------------- private String U = null; // client's username private BigInteger N, g, A, B; private byte[] s; // salt private byte[] cIV, sIV; // client+server IVs, when confidentiality is on private byte[] cn, sn; // client's and server's nonce private SRP srp; // SRP algorithm instance used by this server private byte[] sid; // session ID when re-used private int ttl = 360; // session time-to-live in seconds private byte[] cCB; // peer's channel binding' private String mandatory; // List of available options private String L = null; private String o; private String chosenIntegrityAlgorithm; private String chosenConfidentialityAlgorithm; private int rawSendSize = Registry.SASL_BUFFER_MAX_LIMIT; private byte[] K; // shared session key private boolean replayDetection = true; // whether Replay Detection is on private int inCounter = 0; // messages sequence numbers private int outCounter = 0; private IALG inMac, outMac; // if !null, use for integrity private CALG inCipher, outCipher; // if !null, use for confidentiality private IKeyAgreementParty serverHandler = KeyAgreementFactory.getPartyBInstance(Registry.SRP_SASL_KA); // Constructor(s) // ------------------------------------------------------------------------- public SRPServer() { super(Registry.SASL_SRP_MECHANISM); } // Class methods // ------------------------------------------------------------------------- // Instance methods // ------------------------------------------------------------------------- // abstract methods implementation ----------------------------------------- protected void initMechanism() throws SaslException { // TODO: // we must have a means to map a given username to a preferred // SRP hash algorithm; otherwise we end up using _always_ SHA. // for the time being get it from the mechanism properties map // and apply it for all users. final String mda = (String) properties.get(SRPRegistry.SRP_HASH); srp = SRP.instance(mda == null ? SRPRegistry.SRP_DEFAULT_DIGEST_NAME : mda); } protected void resetMechanism() throws SaslException { s = null; A = B = null; K = null; inMac = outMac = null; inCipher = outCipher = null; sid = null; } // javax.security.sasl.SaslServer interface implementation ----------------- public byte[] evaluateResponse(final byte[] response) throws SaslException { switch (state) { case 0: if (response == null) { return null; } state++; return sendProtocolElements(response); case 1: if (!complete) { state++; return sendEvidence(response); } // else fall through default: throw new IllegalMechanismStateException("evaluateResponse()"); } } protected byte[] engineUnwrap(final byte[] incoming, final int offset, final int len) throws SaslException { // if (DEBUG && debuglevel > 8) debug(TRACE, "==> engineUnwrap()"); // // if (inMac == null && inCipher == null) { // throw new IllegalStateException("connection is not protected"); // } // // if (DEBUG && debuglevel > 6) debug(TRACE, "Incoming buffer (before security): "+Util.dumpString(incoming, offset, len)); // // byte[] data = null; // try { // InputBuffer frameIn = InputBuffer.getInstance(incoming, offset, len); // data = frameIn.getEOS(); // if (inMac != null) { // byte[] received_mac = frameIn.getOS(); // if (DEBUG && debuglevel > 6) debug(TRACE, "Got C (received MAC): "+Util.dumpString(received_mac)); // inMac.update(data); // if (replayDetection) { // inCounter++; // if (DEBUG && debuglevel > 6) debug(TRACE, "inCounter="+String.valueOf(inCounter)); // inMac.update(new byte[] { // (byte)(inCounter >>> 24), // (byte)(inCounter >>> 16), // (byte)(inCounter >>> 8), // (byte) inCounter }); // } // final byte[] computed_mac = inMac.doFinal(); // if (DEBUG && debuglevel > 6) debug(TRACE, "Computed MAC: "+Util.dumpString(computed_mac)); // if (!Arrays.equals(received_mac, computed_mac)) // throw new IntegrityException("engineUnwrap()"); // } // if (inCipher != null) { // data = inCipher.doFinal(data); // } // } catch (IOException x) { // if (x instanceof SaslException) { // throw (SaslException) x; // } // throw new SaslException("engineUnwrap()", x); // } // // if (DEBUG && debuglevel > 6) debug(TRACE, "Incoming buffer (after security): "+Util.dumpString(data)); // if (DEBUG && debuglevel > 8) debug(TRACE, "<== engineUnwrap()"); // return data; if (DEBUG && debuglevel > 8) debug(TRACE, "==> engineUnwrap()"); if (inMac == null && inCipher == null) { throw new IllegalStateException("connection is not protected"); } if (DEBUG && debuglevel > 6) debug(TRACE, "Incoming buffer (before security): "+Util.dumpString(incoming, offset, len)); // at this point one, or both, of confidentiality and integrity protection // services are active. final byte[] result; try { if (inMac != null) { // integrity bytes are at the end of the stream final int macBytesCount = inMac.length(); final int payloadLength = len - macBytesCount; final byte[] received_mac = new byte[macBytesCount]; System.arraycopy(incoming, offset + payloadLength, received_mac, 0, macBytesCount); if (DEBUG && debuglevel > 6) debug(TRACE, "Got C (received MAC): "+Util.dumpString(received_mac)); inMac.update(incoming, offset, payloadLength); if (replayDetection) { inCounter++; if (DEBUG && debuglevel > 6) debug(TRACE, "inCounter="+String.valueOf(inCounter)); inMac.update(new byte[] { (byte)(inCounter >>> 24), (byte)(inCounter >>> 16), (byte)(inCounter >>> 8), (byte) inCounter}); } final byte[] computed_mac = inMac.doFinal(); if (DEBUG && debuglevel > 6) debug(TRACE, "Computed MAC: "+Util.dumpString(computed_mac)); if (!Arrays.equals(received_mac, computed_mac)) { throw new IntegrityException("engineUnwrap()"); } // deal with the payload, which can be either plain or encrypted if (inCipher != null) { result = inCipher.doFinal(incoming, offset, payloadLength); } else { result = new byte[payloadLength]; System.arraycopy(incoming, offset, result, 0, result.length); } } else { // no integrity protection; just confidentiality // if (inCipher != null) { result = inCipher.doFinal(incoming, offset, len); // } else { // result = new byte[len]; // System.arraycopy(incoming, offset, result, 0, len); // } } } catch (IOException x) { if (x instanceof SaslException) { throw (SaslException) x; } throw new SaslException("engineUnwrap()", x); } if (DEBUG && debuglevel > 6) debug(TRACE, "Incoming buffer (after security): "+Util.dumpString(result)); if (DEBUG && debuglevel > 8) debug(TRACE, "<== engineUnwrap()"); return result; } protected byte[] engineWrap(final byte[] outgoing, final int offset, final int len) throws SaslException { // if (DEBUG && debuglevel > 8) debug(TRACE, "==> engineWrap()"); // // if (outMac == null && outCipher == null) { // throw new IllegalStateException("connection is not protected"); // } // // byte[] data = new byte[len]; // System.arraycopy(outgoing, offset, data, 0, len); // // if (DEBUG && debuglevel > 6) debug(TRACE, "Outgoing buffer (before security) (hex): "+Util.dumpString(data)); // if (DEBUG && debuglevel > 6) debug(TRACE, "Outgoing buffer (before security) (str): \""+new String(data)+"\""); // // final byte[] result; // try { // OutputBuffer frameOut = new OutputBuffer(); // // Process the data // if (outCipher != null) { // data = outCipher.doFinal(data); // if (DEBUG && debuglevel > 6) debug(TRACE, "Encoding c (encrypted plaintext): "+Util.dumpString(data)); // } else { // if (DEBUG && debuglevel > 6) debug(TRACE, "Encoding p (plaintext): "+Util.dumpString(data)); // } // frameOut.setEOS(data); // if (outMac != null) { // outMac.update(data); // if (replayDetection) { // outCounter++; // if (DEBUG && debuglevel > 6) debug(TRACE, "outCounter="+String.valueOf(outCounter)); // outMac.update(new byte[] { // (byte)(outCounter >>> 24), // (byte)(outCounter >>> 16), // (byte)(outCounter >>> 8), // (byte) outCounter}); // } // byte[] C = outMac.doFinal(); // frameOut.setOS(C); // if (DEBUG && debuglevel > 6) debug(TRACE, "Encoding C (integrity checksum): "+Util.dumpString(C)); // } // result = frameOut.wrap(); // // } catch (IOException x) { // if (x instanceof SaslException) { // throw (SaslException) x; // } // throw new SaslException("engineWrap()", x); // } // // if (DEBUG && debuglevel > 8) debug(TRACE, "<== engineWrap()"); // return result; if (DEBUG && debuglevel > 8) debug(TRACE, "==> engineWrap()"); if (outMac == null && outCipher == null) { throw new IllegalStateException("connection is not protected"); } if (DEBUG && debuglevel > 6) debug(TRACE, "Outgoing buffer (before security) (hex): "+Util.dumpString(outgoing, offset, len)); if (DEBUG && debuglevel > 6) debug(TRACE, "Outgoing buffer (before security) (str): \""+new String(outgoing, offset, len)+"\""); // at this point one, or both, of confidentiality and integrity protection // services are active. byte[] result; try { final ByteArrayOutputStream out = new ByteArrayOutputStream(); if (outCipher != null) { result = outCipher.doFinal(outgoing, offset, len); if (DEBUG && debuglevel > 6) debug(TRACE, "Encoding c (encrypted plaintext): "+Util.dumpString(result)); out.write(result); if (outMac != null) { outMac.update(result); if (replayDetection) { outCounter++; if (DEBUG && debuglevel > 6) debug(TRACE, "outCounter="+String.valueOf(outCounter)); outMac.update(new byte[] { (byte)(outCounter >>> 24), (byte)(outCounter >>> 16), (byte)(outCounter >>> 8), (byte) outCounter }); } final byte[] C = outMac.doFinal(); out.write(C); if (DEBUG && debuglevel > 6) debug(TRACE, "Encoding C (integrity checksum): "+Util.dumpString(C)); } // else ciphertext only; do nothing } else { // no confidentiality; just integrity [+ replay detection] if (DEBUG && debuglevel > 6) debug(TRACE, "Encoding p (plaintext): "+Util.dumpString(outgoing, offset, len)); out.write(outgoing, offset, len); // if (outMac != null) { outMac.update(outgoing, offset, len); if (replayDetection) { outCounter++; if (DEBUG && debuglevel > 6) debug(TRACE, "outCounter="+String.valueOf(outCounter)); outMac.update(new byte[] { (byte)(outCounter >>> 24), (byte)(outCounter >>> 16), (byte)(outCounter >>> 8), (byte) outCounter }); } final byte[] C = outMac.doFinal(); out.write(C); if (DEBUG && debuglevel > 6) debug(TRACE, "Encoding C (integrity checksum): "+Util.dumpString(C)); // } // else plaintext only; do nothing } result = out.toByteArray(); } catch (IOException x) { if (x instanceof SaslException) { throw (SaslException) x; } throw new SaslException("engineWrap()", x); } if (DEBUG && debuglevel > 8) debug(TRACE, "<== engineWrap()"); return result; } protected String getNegotiatedQOP() { if (inMac != null) { if (inCipher != null) { return Registry.QOP_AUTH_CONF; } else { return Registry.QOP_AUTH_INT; } } return Registry.QOP_AUTH; } protected String getNegotiatedStrength() { if (inMac != null) { if (inCipher != null) { return Registry.STRENGTH_HIGH; } else { return Registry.STRENGTH_MEDIUM; } } return Registry.STRENGTH_LOW; } protected String getNegotiatedRawSendSize() { return String.valueOf(rawSendSize); } protected String getReuse() { return Registry.REUSE_TRUE; } // other methods ----------------------------------------------------------- private byte[] sendProtocolElements(final byte[] input) throws SaslException { if (DEBUG && debuglevel > 8) debug(TRACE, "==> sendProtocolElements()"); if (DEBUG && debuglevel > 6) debug(TRACE, "C: "+Util.dumpString(input)); // Client send U, I, sid, cn final InputBuffer frameIn = new InputBuffer(input); try { U = frameIn.getText(); // Extract username if (DEBUG && debuglevel > 6) debug(TRACE, "Got U (username): \""+U+"\""); authorizationID = frameIn.getText(); // Extract authorisation ID if (DEBUG && debuglevel > 6) debug(TRACE, "Got I (userid): \""+authorizationID+"\""); sid = frameIn.getEOS(); if (DEBUG && debuglevel > 6) debug(TRACE, "Got sid (session ID): "+new String(sid)); cn = frameIn.getOS(); if (DEBUG && debuglevel > 6) debug(TRACE, "Got cn (client nonce): "+Util.dumpString(cn)); cCB = frameIn.getEOS(); if (DEBUG && debuglevel > 6) debug(TRACE, "Got cCB (client channel binding): "+Util.dumpString(cCB)); } catch (IOException x) { if (x instanceof SaslException) { throw (SaslException) x; } throw new AuthenticationException("sendProtocolElements()", x); } // do/can we re-use? if (ServerStore.instance().isAlive(sid)) { final SecurityContext ctx = ServerStore.instance().restoreSession(sid); srp = SRP.instance(ctx.getMdName()); K = ctx.getK(); cIV = ctx.getClientIV(); sIV = ctx.getServerIV(); replayDetection = ctx.hasReplayDetection(); inCounter = ctx.getInCounter(); outCounter = ctx.getOutCounter(); inMac = ctx.getInMac(); outMac = ctx.getOutMac(); inCipher = ctx.getInCipher(); outCipher = ctx.getOutCipher(); if (sn == null || sn.length != 16) { sn = new byte[16]; } PRNG.nextBytes(sn); setupSecurityServices(false); final OutputBuffer frameOut = new OutputBuffer(); try { frameOut.setScalar(1, 0xFF); frameOut.setOS(sn); frameOut.setEOS(channelBinding); } catch (IOException x) { if (x instanceof SaslException) { throw (SaslException) x; } throw new AuthenticationException("sendProtocolElements()", x); } final byte[] result = frameOut.encode(); if (DEBUG && debuglevel > 8) debug(TRACE, "<== sendProtocolElements()"); if (DEBUG && debuglevel > 2) debug(INFO, "Old session..."); if (DEBUG && debuglevel > 2) debug(INFO, "S: "+Util.dumpString(result)); if (DEBUG && debuglevel > 2) debug(INFO, " sn = "+Util.dumpString(sn)); if (DEBUG && debuglevel > 2) debug(INFO, " sCB = "+Util.dumpString(channelBinding)); return result; } else { // new session authenticator.activate(properties); // ------------------------------------------------------------------- final HashMap mapB = new HashMap(); // mapB.put(SRP6KeyAgreement.HASH_FUNCTION, srp.newDigest()); mapB.put(SRP6KeyAgreement.HASH_FUNCTION, srp.getAlgorithm()); mapB.put(SRP6KeyAgreement.HOST_PASSWORD_DB, authenticator); try { serverHandler.init(mapB); OutgoingMessage out = new OutgoingMessage(); out.writeString(U); IncomingMessage in = new IncomingMessage(out.toByteArray()); out = serverHandler.processMessage(in); in = new IncomingMessage(out.toByteArray()); N = in.readMPI(); g = in.readMPI(); s = in.readMPI().toByteArray(); B = in.readMPI(); } catch (KeyAgreementException x) { throw new SaslException("sendProtocolElements()", x); } // ------------------------------------------------------------------- if (DEBUG && debuglevel > 6) debug(TRACE, "Encoding N (modulus): "+Util.dump(N)); if (DEBUG && debuglevel > 6) debug(TRACE, "Encoding g (generator): "+Util.dump(g)); if (DEBUG && debuglevel > 6) debug(TRACE, "Encoding s (client's salt): "+Util.dumpString(s)); if (DEBUG && debuglevel > 6) debug(TRACE, "Encoding B (server ephemeral public key): "+Util.dump(B)); // The server creates an options list (L), which consists of a // comma-separated list of option strings that specify the security // service options the server supports. L = createL(); if (DEBUG && debuglevel > 6) debug(TRACE, "Encoding L (available options): \""+L+"\""); if (DEBUG && debuglevel > 6) debug(TRACE, "Encoding sIV (server IV): "+Util.dumpString(sIV)); final OutputBuffer frameOut = new OutputBuffer(); try { frameOut.setScalar(1, 0x00); frameOut.setMPI(N); frameOut.setMPI(g); frameOut.setOS(s); frameOut.setMPI(B); frameOut.setText(L); } catch (IOException x) { if (x instanceof SaslException) { throw (SaslException) x; } throw new AuthenticationException("sendProtocolElements()", x); } final byte[] result = frameOut.encode(); if (DEBUG && debuglevel > 8) debug(TRACE, "<== sendProtocolElements()"); if (DEBUG && debuglevel > 2) debug(INFO, "New session..."); if (DEBUG && debuglevel > 2) debug(INFO, "S: "+Util.dumpString(result)); if (DEBUG && debuglevel > 2) debug(INFO, " N = 0x"+N.toString(16)); if (DEBUG && debuglevel > 2) debug(INFO, " g = 0x"+g.toString(16)); if (DEBUG && debuglevel > 2) debug(INFO, " s = "+Util.dumpString(s)); if (DEBUG && debuglevel > 2) debug(INFO, " B = 0x"+B.toString(16)); if (DEBUG && debuglevel > 2) debug(INFO, " L = "+L); return result; } } private byte[] sendEvidence(final byte[] input) throws SaslException { if (DEBUG && debuglevel > 8) debug(TRACE, "==> sendEvidence()"); if (DEBUG && debuglevel > 6) debug(TRACE, "C: "+Util.dumpString(input)); // Client send A, M1, o, cIV final InputBuffer frameIn = new InputBuffer(input); final byte[] M1; try { A = frameIn.getMPI(); // Extract client's ephemeral public key if (DEBUG && debuglevel > 6) debug(TRACE, "Got A (client ephemeral public key): "+Util.dump(A)); M1 = frameIn.getOS(); // Extract evidence if (DEBUG && debuglevel > 6) debug(TRACE, "Got M1 (client evidence): "+Util.dumpString(M1)); o = frameIn.getText(); // Extract client's options list if (DEBUG && debuglevel > 6) debug(TRACE, "Got o (client chosen options): \""+o+"\""); cIV = frameIn.getOS(); // Extract client's IV if (DEBUG && debuglevel > 6) debug(TRACE, "Got cIV (client IV): "+Util.dumpString(cIV)); } catch (IOException x) { if (x instanceof SaslException) { throw (SaslException) x; } throw new AuthenticationException("sendEvidence()", x); } // Parse client's options and set security layer variables parseO(o); // ---------------------------------------------------------------------- try { final OutgoingMessage out = new OutgoingMessage(); out.writeMPI(A); final IncomingMessage in = new IncomingMessage(out.toByteArray()); serverHandler.processMessage(in); K = serverHandler.getSharedSecret(); } catch (KeyAgreementException x) { throw new SaslException("sendEvidence()", x); } // ---------------------------------------------------------------------- if (DEBUG && debuglevel > 6) debug(TRACE, "K: "+Util.dumpString(K)); final byte[] expected; try { expected = srp.generateM1( N, g, U, s, A, B, K, authorizationID, L, cn, cCB); } catch (UnsupportedEncodingException x) { throw new AuthenticationException("sendEvidence()", x); } // Verify client evidence if (!Arrays.equals(M1, expected)) { throw new AuthenticationException("M1 mismatch"); } setupSecurityServices(true); final byte[] M2; try { M2 = srp.generateM2( A, M1, K, U, authorizationID, o, sid, ttl, cIV, sIV, channelBinding); } catch (UnsupportedEncodingException x) { throw new AuthenticationException("sendEvidence()", x); } final OutputBuffer frameOut = new OutputBuffer(); try { frameOut.setOS(M2); frameOut.setOS(sIV); frameOut.setEOS(sid); frameOut.setScalar(4, ttl); frameOut.setEOS(channelBinding); } catch (IOException x) { if (x instanceof SaslException) { throw (SaslException) x; } throw new AuthenticationException("sendEvidence()", x); } final byte[] result = frameOut.encode(); if (DEBUG && debuglevel > 2) debug(INFO, "S: "+Util.dumpString(result)); if (DEBUG && debuglevel > 2) debug(INFO, " M2 = "+Util.dumpString(M2)); if (DEBUG && debuglevel > 2) debug(INFO, " sIV = "+Util.dumpString(sIV)); if (DEBUG && debuglevel > 2) debug(INFO, " sid = "+new String(sid)); if (DEBUG && debuglevel > 2) debug(INFO, " ttl = "+ttl); if (DEBUG && debuglevel > 2) debug(INFO, " sCB = "+Util.dumpString(channelBinding)); if (DEBUG && debuglevel > 8) debug(TRACE, "<== sendEvidence()"); return result; } private String createL() { if (DEBUG && debuglevel > 8) debug(TRACE, "==> createL()"); String s = (String) properties.get(SRPRegistry.SRP_MANDATORY); if (s == null) { s = SRPRegistry.DEFAULT_MANDATORY; } if (!SRPRegistry.MANDATORY_NONE.equals(s) && !SRPRegistry.OPTION_REPLAY_DETECTION.equals(s) && !SRPRegistry.OPTION_INTEGRITY.equals(s) && !SRPRegistry.OPTION_CONFIDENTIALITY.equals(s)) { if (DEBUG && debuglevel > 4) debug(WARN, "Unrecognised mandatory option ("+s+"). Using default..."); s = SRPRegistry.DEFAULT_MANDATORY; } mandatory = s; s = (String) properties.get(SRPRegistry.SRP_CONFIDENTIALITY); final boolean confidentiality = (s == null ? SRPRegistry.DEFAULT_CONFIDENTIALITY : Boolean.valueOf(s).booleanValue()); s = (String) properties.get(SRPRegistry.SRP_INTEGRITY_PROTECTION); boolean integrity = (s == null ? SRPRegistry.DEFAULT_INTEGRITY : Boolean.valueOf(s).booleanValue()); s = (String) properties.get(SRPRegistry.SRP_REPLAY_DETECTION); final boolean replayDetection = (s == null ? SRPRegistry.DEFAULT_REPLAY_DETECTION : Boolean.valueOf(s).booleanValue()); final StringBuffer sb = new StringBuffer(); sb.append(SRPRegistry.OPTION_SRP_DIGEST).append("=") .append(srp.getAlgorithm()).append(","); if (!SRPRegistry.MANDATORY_NONE.equals(mandatory)) { sb.append(SRPRegistry.OPTION_MANDATORY).append("=") .append(mandatory).append(","); } if (replayDetection) { sb.append(SRPRegistry.OPTION_REPLAY_DETECTION).append(","); // if replay detection is on then force integrity protection integrity = true; } int i; if (integrity) { for (i = 0; i < SRPRegistry.INTEGRITY_ALGORITHMS.length; i++) { sb.append(SRPRegistry.OPTION_INTEGRITY).append("=") .append(SRPRegistry.INTEGRITY_ALGORITHMS[i]).append(","); } } if (confidentiality) { IBlockCipher cipher; for (i = 0; i < SRPRegistry.CONFIDENTIALITY_ALGORITHMS.length; i++) { cipher = CipherFactory.getInstance(SRPRegistry.CONFIDENTIALITY_ALGORITHMS[i]); if (cipher != null) { sb.append(SRPRegistry.OPTION_CONFIDENTIALITY).append("=") .append(SRPRegistry.CONFIDENTIALITY_ALGORITHMS[i]).append(","); } } } final String result = sb .append(SRPRegistry.OPTION_MAX_BUFFER_SIZE).append("=") .append(Registry.SASL_BUFFER_MAX_LIMIT).toString(); if (DEBUG && debuglevel > 8) debug(TRACE, "<== createL()"); return result; } // Parse client's options and set security layer variables private void parseO(final String o) throws AuthenticationException { this.replayDetection = false; boolean integrity = false; boolean confidentiality = false; String option; int i; final StringTokenizer st = new StringTokenizer(o.toLowerCase(), ","); while (st.hasMoreTokens()) { option = st.nextToken(); if (DEBUG && debuglevel > 6) debug(TRACE, "option: <"+option+">"); if (option.equals(SRPRegistry.OPTION_REPLAY_DETECTION)) { replayDetection = true; } else if (option.startsWith(SRPRegistry.OPTION_INTEGRITY + "=")) { if (integrity) { throw new AuthenticationException("Only one integrity algorithm may be chosen"); } else { option = option.substring(option.indexOf('=') + 1); if (DEBUG && debuglevel > 6) debug(TRACE, "algorithm: <"+option+">"); for (i = 0; i < SRPRegistry.INTEGRITY_ALGORITHMS.length; i++) { if (SRPRegistry.INTEGRITY_ALGORITHMS[i].equals(option)) { chosenIntegrityAlgorithm = option; integrity = true; break; } } if (!integrity) { throw new AuthenticationException("Unknown integrity algorithm: "+option); } } } else if (option.startsWith(SRPRegistry.OPTION_CONFIDENTIALITY + "=")) { if (confidentiality) { throw new AuthenticationException("Only one confidentiality algorithm may be chosen"); } else { option = option.substring(option.indexOf('=') + 1); if (DEBUG && debuglevel > 6) debug(TRACE, "algorithm: <"+option+">"); for (i = 0; i < SRPRegistry.CONFIDENTIALITY_ALGORITHMS.length; i++) { if (SRPRegistry.CONFIDENTIALITY_ALGORITHMS[i].equals(option)) { chosenConfidentialityAlgorithm = option; confidentiality = true; break; } } if (!confidentiality) { throw new AuthenticationException("Unknown confidentiality algorithm: "+option); } } } else if (option.startsWith(SRPRegistry.OPTION_MAX_BUFFER_SIZE + "=")) { final String maxBufferSize = option.substring(option.indexOf('=') + 1); try { rawSendSize = Integer.parseInt(maxBufferSize); if (rawSendSize > Registry.SASL_BUFFER_MAX_LIMIT || rawSendSize < 1) throw new AuthenticationException("Illegal value for 'maxbuffersize' option"); } catch (NumberFormatException x) { throw new AuthenticationException(SRPRegistry.OPTION_MAX_BUFFER_SIZE + "=" + String.valueOf(maxBufferSize), x); } } } // check if client did the right thing if (replayDetection) { if (!integrity) { throw new AuthenticationException("Missing integrity protection algorithm " +"but replay detection is chosen"); } } if (mandatory.equals(SRPRegistry.OPTION_REPLAY_DETECTION)) { if (!replayDetection) { throw new AuthenticationException( "Replay detection is mandatory but was not chosen"); } } if (mandatory.equals(SRPRegistry.OPTION_INTEGRITY)) { if (!integrity) { throw new AuthenticationException( "Integrity protection is mandatory but was not chosen"); } } if (mandatory.equals(SRPRegistry.OPTION_CONFIDENTIALITY)) { if (!confidentiality) { throw new AuthenticationException("Confidentiality is mandatory but was not chosen"); } } int blockSize = 0; if (chosenConfidentialityAlgorithm != null) { final IBlockCipher cipher = CipherFactory.getInstance(chosenConfidentialityAlgorithm); if (cipher != null) { blockSize = cipher.defaultBlockSize(); } else { // should not happen throw new AuthenticationException("Confidentiality algorithm (" +chosenConfidentialityAlgorithm+") not available"); } } sIV = new byte[blockSize]; if (blockSize > 0) { PRNG.nextBytes(sIV); } } private void setupSecurityServices(final boolean newSession) throws SaslException { complete = true; // signal end of authentication phase if (newSession) { outCounter = inCounter = 0; // instantiate cipher if confidentiality protection filter is active if (chosenConfidentialityAlgorithm != null) { if (DEBUG && debuglevel > 2) debug(INFO, "Activating confidentiality protection filter"); inCipher = CALG.getInstance(chosenConfidentialityAlgorithm); outCipher = CALG.getInstance(chosenConfidentialityAlgorithm); } // instantiate hmacs if integrity protection filter is active if (chosenIntegrityAlgorithm != null) { if (DEBUG && debuglevel > 2) debug(INFO, "Activating integrity protection filter"); inMac = IALG.getInstance(chosenIntegrityAlgorithm); outMac = IALG.getInstance(chosenIntegrityAlgorithm); } // generate a new sid if at least integrity is used sid = (inMac != null ? ServerStore.getNewSessionID() : new byte[0]); } else { // same session new keys K = srp.generateKn(K, cn, sn); } final KDF kdf = KDF.getInstance(K); // initialise in/out ciphers if confidentaility protection is used if (inCipher != null) { outCipher.init(kdf, sIV, Direction.FORWARD); inCipher.init(kdf, cIV, Direction.REVERSED); } // initialise in/out macs if integrity protection is used if (inMac != null) { outMac.init(kdf); inMac.init(kdf); } if (sid != null && sid.length != 0) { // update the security context and save in map if (DEBUG && debuglevel > 2) debug(INFO, "Updating security context for sid = "+new String(sid)); ServerStore.instance().cacheSession(ttl, new SecurityContext(srp.getAlgorithm(), sid, K, cIV, sIV, replayDetection, inCounter, outCounter, inMac, outMac, inCipher, outCipher)); } } }