123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956 |
- /* SRPClient.java --
- Copyright (C) 2003, 2006, 2010 Free Software Foundation, Inc.
- This file is a part of GNU Classpath.
- GNU Classpath 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 of the License, or (at
- your option) any later version.
- GNU Classpath 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 GNU Classpath; if not, write to the Free Software
- Foundation, Inc., 51 Franklin St, 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. */
- package gnu.javax.crypto.sasl.srp;
- import gnu.java.lang.CPStringBuilder;
- import gnu.java.security.Configuration;
- import gnu.java.security.Registry;
- import gnu.java.security.hash.MD5;
- import gnu.java.security.util.PRNG;
- import gnu.java.security.util.Util;
- import gnu.javax.crypto.assembly.Direction;
- import gnu.javax.crypto.cipher.CipherFactory;
- import gnu.javax.crypto.cipher.IBlockCipher;
- import gnu.javax.crypto.key.IKeyAgreementParty;
- import gnu.javax.crypto.key.IncomingMessage;
- import gnu.javax.crypto.key.KeyAgreementException;
- import gnu.javax.crypto.key.KeyAgreementFactory;
- import gnu.javax.crypto.key.OutgoingMessage;
- import gnu.javax.crypto.key.srp6.SRP6KeyAgreement;
- import gnu.javax.crypto.sasl.ClientMechanism;
- import gnu.javax.crypto.sasl.IllegalMechanismStateException;
- import gnu.javax.crypto.sasl.InputBuffer;
- import gnu.javax.crypto.sasl.IntegrityException;
- import gnu.javax.crypto.sasl.OutputBuffer;
- import gnu.javax.security.auth.Password;
- import java.io.ByteArrayOutputStream;
- import java.io.IOException;
- import java.io.UnsupportedEncodingException;
- import java.math.BigInteger;
- import java.security.NoSuchAlgorithmException;
- import java.util.Arrays;
- import java.util.HashMap;
- import java.util.StringTokenizer;
- import java.util.logging.Logger;
- import javax.security.auth.DestroyFailedException;
- import javax.security.auth.callback.Callback;
- import javax.security.auth.callback.NameCallback;
- import javax.security.auth.callback.PasswordCallback;
- import javax.security.auth.callback.UnsupportedCallbackException;
- import javax.security.sasl.AuthenticationException;
- import javax.security.sasl.SaslClient;
- import javax.security.sasl.SaslException;
- /**
- * The SASL-SRP client-side mechanism.
- */
- public class SRPClient
- extends ClientMechanism
- implements SaslClient
- {
- private static final Logger log = Configuration.DEBUG ?
- Logger.getLogger(SRPClient.class.getName()) : null;
- private String uid; // the unique key for this type of client
- private String U; // the authentication identity
- BigInteger N, g, A, B;
- private Password password; // the authentication credentials
- private byte[] s; // the user's salt
- private byte[] cIV, sIV; // client+server IVs, when confidentiality is on
- private byte[] M1, M2; // client+server evidences
- private byte[] cn, sn; // client's and server's nonce
- private SRP srp; // SRP algorithm instance used by this client
- private byte[] sid; // session ID when re-used
- private int ttl; // session time-to-live in seconds
- private byte[] sCB; // the peer's channel binding data
- private String L; // available options
- 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 clientHandler =
- KeyAgreementFactory.getPartyAInstance(Registry.SRP_SASL_KA);
- /** Our default source of randomness. */
- private PRNG prng = null;
- public SRPClient()
- {
- super(Registry.SASL_SRP_MECHANISM);
- }
- protected void initMechanism() throws SaslException
- {
- // we shall keep track of the sid (and the security context of this SRP
- // client) based on the initialisation parameters of an SRP session.
- // we shall compute a unique key for those parameters and key the sid
- // (and the security context) accordingly.
- // 1. compute the mapping key. use MD5 (the fastest) for this purpose
- final MD5 md = new MD5();
- byte[] b;
- b = authorizationID.getBytes();
- md.update(b, 0, b.length);
- b = serverName.getBytes();
- md.update(b, 0, b.length);
- b = protocol.getBytes();
- md.update(b, 0, b.length);
- if (channelBinding.length > 0)
- md.update(channelBinding, 0, channelBinding.length);
- uid = Util.toBase64(md.digest());
- if (ClientStore.instance().isAlive(uid))
- {
- final SecurityContext ctx = ClientStore.instance().restoreSession(uid);
- srp = SRP.instance(ctx.getMdName());
- sid = ctx.getSID();
- 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();
- }
- else
- {
- sid = new byte[0];
- ttl = 0;
- K = null;
- cIV = null;
- sIV = null;
- cn = null;
- sn = null;
- }
- }
- protected void resetMechanism() throws SaslException
- {
- try
- {
- password.destroy();
- }
- catch (DestroyFailedException dfe)
- {
- SaslException se = new SaslException("resetMechanism()");
- se.initCause(dfe);
- throw se;
- }
- password = null;
- M1 = null;
- K = null;
- cIV = null;
- sIV = null;
- inMac = outMac = null;
- inCipher = outCipher = null;
- sid = null;
- ttl = 0;
- cn = null;
- sn = null;
- }
- public boolean hasInitialResponse()
- {
- return true;
- }
- public byte[] evaluateChallenge(final byte[] challenge) throws SaslException
- {
- switch (state)
- {
- case 0:
- state++;
- return sendIdentities();
- case 1:
- state++;
- final byte[] result = sendPublicKey(challenge);
- try
- {
- password.destroy(); //don't need further this session
- }
- catch (DestroyFailedException x)
- {
- SaslException se = new SaslException("sendPublicKey()");
- se.initCause(se);
- throw se;
- }
- return result;
- case 2: // should only occur if session re-use was rejected
- if (! complete)
- {
- state++;
- return receiveEvidence(challenge);
- }
- // else fall through
- default:
- throw new IllegalMechanismStateException("evaluateChallenge()");
- }
- }
- protected byte[] engineUnwrap(final byte[] incoming, final int offset,
- final int len) throws SaslException
- {
- if (Configuration.DEBUG)
- log.entering(this.getClass().getName(), "engineUnwrap");
- if (inMac == null && inCipher == null)
- throw new IllegalStateException("connection is not protected");
- // 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 (Configuration.DEBUG)
- log.fine("Got C (received MAC): " + Util.dumpString(received_mac));
- inMac.update(incoming, offset, payloadLength);
- if (replayDetection)
- {
- inCounter++;
- if (Configuration.DEBUG)
- log.fine("inCounter=" + inCounter);
- inMac.update(new byte[] {
- (byte)(inCounter >>> 24),
- (byte)(inCounter >>> 16),
- (byte)(inCounter >>> 8),
- (byte) inCounter });
- }
- final byte[] computed_mac = inMac.doFinal();
- if (Configuration.DEBUG)
- log.fine("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[len - macBytesCount];
- System.arraycopy(incoming, offset, result, 0, result.length);
- }
- }
- else // no integrity protection; just confidentiality
- result = inCipher.doFinal(incoming, offset, len);
- }
- catch (IOException x)
- {
- if (x instanceof SaslException)
- throw (SaslException) x;
- throw new SaslException("engineUnwrap()", x);
- }
- if (Configuration.DEBUG)
- log.exiting(this.getClass().getName(), "engineUnwrap");
- return result;
- }
- protected byte[] engineWrap(final byte[] outgoing, final int offset,
- final int len) throws SaslException
- {
- if (Configuration.DEBUG)
- log.entering(this.getClass().getName(), "engineWrap");
- if (outMac == null && outCipher == null)
- throw new IllegalStateException("connection is not protected");
- // at this point one, or both, of confidentiality and integrity protection
- // services are active.
- byte[] result;
- try
- {
- final ByteArrayOutputStream out = new ByteArrayOutputStream();
- // Process the data
- if (outCipher != null)
- {
- result = outCipher.doFinal(outgoing, offset, len);
- if (Configuration.DEBUG)
- log.fine("Encoding c (encrypted plaintext): "
- + Util.dumpString(result));
- out.write(result);
- if (outMac != null)
- {
- outMac.update(result);
- if (replayDetection)
- {
- outCounter++;
- if (Configuration.DEBUG)
- log.fine("outCounter=" + 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 (Configuration.DEBUG)
- log.fine("Encoding C (integrity checksum): " + Util.dumpString(C));
- }
- // else confidentiality only; do nothing
- }
- else // no confidentiality; just integrity [+ replay detection]
- {
- if (Configuration.DEBUG)
- log.fine("Encoding p (plaintext): "
- + Util.dumpString(outgoing, offset, len));
- out.write(outgoing, offset, len);
- outMac.update(outgoing, offset, len);
- if (replayDetection)
- {
- outCounter++;
- if (Configuration.DEBUG)
- log.fine("outCounter=" + 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 (Configuration.DEBUG)
- log.fine("Encoding C (integrity checksum): " + Util.dumpString(C));
- }
- result = out.toByteArray();
- }
- catch (IOException x)
- {
- if (x instanceof SaslException)
- throw (SaslException) x;
- throw new SaslException("engineWrap()", x);
- }
- if (Configuration.DEBUG)
- log.exiting(this.getClass().getName(), "engineWrap");
- return result;
- }
- protected String getNegotiatedQOP()
- {
- if (inMac != null)
- {
- if (inCipher != null)
- return Registry.QOP_AUTH_CONF;
- return Registry.QOP_AUTH_INT;
- }
- return Registry.QOP_AUTH;
- }
- protected String getNegotiatedStrength()
- {
- if (inMac != null)
- {
- if (inCipher != null)
- return Registry.STRENGTH_HIGH;
- return Registry.STRENGTH_MEDIUM;
- }
- return Registry.STRENGTH_LOW;
- }
- protected String getNegotiatedRawSendSize()
- {
- return String.valueOf(rawSendSize);
- }
- protected String getReuse()
- {
- return Registry.REUSE_TRUE;
- }
- private byte[] sendIdentities() throws SaslException
- {
- if (Configuration.DEBUG)
- log.entering(this.getClass().getName(), "sendIdentities");
- // If necessary, prompt the client for the username and password
- getUsernameAndPassword();
- if (Configuration.DEBUG)
- {
- log.fine("Password: \"" + new String(password.getPassword()) + "\"");
- log.fine("Encoding U (username): \"" + U + "\"");
- log.fine("Encoding I (userid): \"" + authorizationID + "\"");
- }
- // if session re-use generate new 16-byte nonce
- if (sid.length != 0)
- {
- cn = new byte[16];
- getDefaultPRNG().nextBytes(cn);
- }
- else
- cn = new byte[0];
- final OutputBuffer frameOut = new OutputBuffer();
- try
- {
- frameOut.setText(U);
- frameOut.setText(authorizationID);
- frameOut.setEOS(sid); // session ID to re-use
- frameOut.setOS(cn); // client nonce
- frameOut.setEOS(channelBinding);
- }
- catch (IOException x)
- {
- if (x instanceof SaslException)
- throw (SaslException) x;
- throw new AuthenticationException("sendIdentities()", x);
- }
- final byte[] result = frameOut.encode();
- if (Configuration.DEBUG)
- {
- log.fine("C: " + Util.dumpString(result));
- log.fine(" U = " + U);
- log.fine(" I = " + authorizationID);
- log.fine("sid = " + new String(sid));
- log.fine(" cn = " + Util.dumpString(cn));
- log.fine("cCB = " + Util.dumpString(channelBinding));
- log.exiting(this.getClass().getName(), "sendIdentities");
- }
- return result;
- }
- private byte[] sendPublicKey(final byte[] input) throws SaslException
- {
- if (Configuration.DEBUG)
- {
- log.entering(this.getClass().getName(), "sendPublicKey");
- log.fine("S: " + Util.dumpString(input));
- }
- // Server sends [00], N, g, s, B, L
- // or [FF], sn, sCB
- final InputBuffer frameIn = new InputBuffer(input);
- final int ack;
- try
- {
- ack = (int) frameIn.getScalar(1);
- if (ack == 0x00) // new session
- {
- N = frameIn.getMPI();
- if (Configuration.DEBUG)
- log.fine("Got N (modulus): " + Util.dump(N));
- g = frameIn.getMPI();
- if (Configuration.DEBUG)
- log.fine("Got g (generator): " + Util.dump(g));
- s = frameIn.getOS();
- if (Configuration.DEBUG)
- log.fine("Got s (salt): " + Util.dumpString(s));
- B = frameIn.getMPI();
- if (Configuration.DEBUG)
- log.fine("Got B (server ephermeral public key): " + Util.dump(B));
- L = frameIn.getText();
- if (Configuration.DEBUG)
- log.fine("Got L (available options): \"" + L + "\"");
- }
- else if (ack == 0xFF) // session re-use
- {
- sn = frameIn.getOS();
- if (Configuration.DEBUG)
- log.fine("Got sn (server nonce): " + Util.dumpString(sn));
- sCB = frameIn.getEOS();
- if (Configuration.DEBUG)
- log.fine("Got sCB (server channel binding): " + Util.dumpString(sCB));
- }
- else // unexpected scalar
- throw new SaslException("sendPublicKey(): Invalid scalar (" + ack
- + ") in server's request");
- }
- catch (IOException x)
- {
- if (x instanceof SaslException)
- throw (SaslException) x;
- throw new SaslException("sendPublicKey()", x);
- }
- if (ack == 0x00)
- { // new session ---------------------------------------
- o = createO(L.toLowerCase()); // do this first to initialise the SRP hash
- final byte[] pBytes; // use ASCII encoding to inter-operate w/ non-java
- pBytes = password.getBytes();
- // ----------------------------------------------------------------------
- final HashMap mapA = new HashMap();
- mapA.put(SRP6KeyAgreement.HASH_FUNCTION, srp.getAlgorithm());
- mapA.put(SRP6KeyAgreement.USER_IDENTITY, U);
- mapA.put(SRP6KeyAgreement.USER_PASSWORD, pBytes);
- try
- {
- clientHandler.init(mapA);
- clientHandler.processMessage(null);
- }
- catch (KeyAgreementException x)
- {
- throw new SaslException("sendPublicKey()", x);
- }
- // -------------------------------------------------------------------
- try
- {
- OutgoingMessage out = new OutgoingMessage();
- out.writeMPI(N);
- out.writeMPI(g);
- out.writeMPI(new BigInteger(1, s));
- out.writeMPI(B);
- IncomingMessage in = new IncomingMessage(out.toByteArray());
- out = clientHandler.processMessage(in);
- in = new IncomingMessage(out.toByteArray());
- A = in.readMPI();
- K = clientHandler.getSharedSecret();
- }
- catch (KeyAgreementException x)
- {
- throw new SaslException("sendPublicKey()", x);
- }
- // -------------------------------------------------------------------
- if (Configuration.DEBUG)
- {
- log.fine("K: " + Util.dumpString(K));
- log.fine("Encoding A (client ephemeral public key): " + Util.dump(A));
- }
- try
- {
- M1 = srp.generateM1(N, g, U, s, A, B, K, authorizationID, L, cn,
- channelBinding);
- }
- catch (UnsupportedEncodingException x)
- {
- throw new AuthenticationException("sendPublicKey()", x);
- }
- if (Configuration.DEBUG)
- {
- log.fine("Encoding o (client chosen options): \"" + o + "\"");
- log.fine("Encoding cIV (client IV): \"" + Util.dumpString(cIV) + "\"");
- }
- final OutputBuffer frameOut = new OutputBuffer();
- try
- {
- frameOut.setMPI(A);
- frameOut.setOS(M1);
- frameOut.setText(o);
- frameOut.setOS(cIV);
- }
- catch (IOException x)
- {
- if (x instanceof SaslException)
- throw (SaslException) x;
- throw new AuthenticationException("sendPublicKey()", x);
- }
- final byte[] result = frameOut.encode();
- if (Configuration.DEBUG)
- {
- log.fine("New session, or session re-use rejected...");
- log.fine("C: " + Util.dumpString(result));
- log.fine(" A = 0x" + A.toString(16));
- log.fine(" M1 = " + Util.dumpString(M1));
- log.fine(" o = " + o);
- log.fine("cIV = " + Util.dumpString(cIV));
- log.exiting(this.getClass().getName(), "sendPublicKey");
- }
- return result;
- }
- else // session re-use accepted -------------------------------------------
- {
- setupSecurityServices(true);
- if (Configuration.DEBUG)
- {
- log.fine("Session re-use accepted...");
- log.exiting(this.getClass().getName(), "sendPublicKey");
- }
- return null;
- }
- }
- private byte[] receiveEvidence(byte[] input) throws SaslException
- {
- if (Configuration.DEBUG)
- {
- log.entering(this.getClass().getName(), "receiveEvidence");
- log.fine("S: " + Util.dumpString(input));
- }
- // Server send M2, sIV, sCB, sid, ttl
- final InputBuffer frameIn = new InputBuffer(input);
- try
- {
- M2 = frameIn.getOS();
- if (Configuration.DEBUG)
- log.fine("Got M2 (server evidence): " + Util.dumpString(M2));
- sIV = frameIn.getOS();
- if (Configuration.DEBUG)
- log.fine("Got sIV (server IV): " + Util.dumpString(sIV));
- sid = frameIn.getEOS();
- if (Configuration.DEBUG)
- log.fine("Got sid (session ID): " + new String(sid));
- ttl = (int) frameIn.getScalar(4);
- if (Configuration.DEBUG)
- log.fine("Got ttl (session time-to-live): " + ttl + "sec.");
- sCB = frameIn.getEOS();
- if (Configuration.DEBUG)
- log.fine("Got sCB (server channel binding): " + Util.dumpString(sCB));
- }
- catch (IOException x)
- {
- if (x instanceof SaslException)
- throw (SaslException) x;
- throw new AuthenticationException("receiveEvidence()", x);
- }
- final byte[] expected;
- try
- {
- expected = srp.generateM2(A, M1, K, U, authorizationID, o, sid, ttl,
- cIV, sIV, sCB);
- }
- catch (UnsupportedEncodingException x)
- {
- throw new AuthenticationException("receiveEvidence()", x);
- }
- if (Configuration.DEBUG)
- log.fine("Expected: " + Util.dumpString(expected));
- if (! Arrays.equals(M2, expected))
- throw new AuthenticationException("M2 mismatch");
- setupSecurityServices(false);
- if (Configuration.DEBUG)
- log.exiting(this.getClass().getName(), "receiveEvidence");
- return null;
- }
- private void getUsernameAndPassword() throws AuthenticationException
- {
- try
- {
- if ((! properties.containsKey(Registry.SASL_USERNAME))
- && (! properties.containsKey(Registry.SASL_PASSWORD)))
- {
- final NameCallback nameCB;
- final String defaultName = System.getProperty("user.name");
- if (defaultName == null)
- nameCB = new NameCallback("username: ");
- else
- nameCB = new NameCallback("username: ", defaultName);
- final PasswordCallback pwdCB = new PasswordCallback("password: ",
- false);
- handler.handle(new Callback[] { nameCB, pwdCB });
- U = nameCB.getName();
- password = new Password(pwdCB.getPassword());
- }
- else
- {
- if (properties.containsKey(Registry.SASL_USERNAME))
- this.U = (String) properties.get(Registry.SASL_USERNAME);
- else
- {
- final NameCallback nameCB;
- final String defaultName = System.getProperty("user.name");
- if (defaultName == null)
- nameCB = new NameCallback("username: ");
- else
- nameCB = new NameCallback("username: ", defaultName);
- this.handler.handle(new Callback[] { nameCB });
- this.U = nameCB.getName();
- }
- if (properties.containsKey(Registry.SASL_PASSWORD))
- {
- Object pw = properties.get(Registry.SASL_PASSWORD);
- if (pw instanceof char[])
- password = new Password((char[]) pw);
- else if (pw instanceof Password)
- password = (Password) pw;
- else if (pw instanceof String)
- password = new Password(((String) pw).toCharArray());
- else
- throw new IllegalArgumentException(pw.getClass().getName()
- + "is not a valid password class");
- }
- else
- {
- final PasswordCallback pwdCB = new PasswordCallback("password: ",
- false);
- this.handler.handle(new Callback[] { pwdCB });
- password = new Password(pwdCB.getPassword());
- }
- }
- if (U == null)
- throw new AuthenticationException("null username supplied");
- if (password == null)
- throw new AuthenticationException("null password supplied");
- }
- catch (UnsupportedCallbackException x)
- {
- throw new AuthenticationException("getUsernameAndPassword()", x);
- }
- catch (IOException x)
- {
- throw new AuthenticationException("getUsernameAndPassword()", x);
- }
- }
- // We go through the list of available services and for each available one
- // we decide whether or not we want it enabled, based on properties passed
- // to us by the client.
- private String createO(final String aol) throws AuthenticationException
- {
- if (Configuration.DEBUG)
- log.entering(this.getClass().getName(), "createO", aol);
- boolean replaydetectionAvailable = false;
- boolean integrityAvailable = false;
- boolean confidentialityAvailable = false;
- String option, mandatory = SRPRegistry.DEFAULT_MANDATORY;
- int i;
- String mdName = SRPRegistry.SRP_DEFAULT_DIGEST_NAME;
- final StringTokenizer st = new StringTokenizer(aol, ",");
- while (st.hasMoreTokens())
- {
- option = st.nextToken();
- if (option.startsWith(SRPRegistry.OPTION_SRP_DIGEST + "="))
- {
- option = option.substring(option.indexOf('=') + 1);
- if (Configuration.DEBUG)
- log.fine("mda: <" + option + ">");
- for (i = 0; i < SRPRegistry.INTEGRITY_ALGORITHMS.length; i++)
- if (SRPRegistry.SRP_ALGORITHMS[i].equals(option))
- {
- mdName = option;
- break;
- }
- }
- else if (option.equals(SRPRegistry.OPTION_REPLAY_DETECTION))
- replaydetectionAvailable = true;
- else if (option.startsWith(SRPRegistry.OPTION_INTEGRITY + "="))
- {
- option = option.substring(option.indexOf('=') + 1);
- if (Configuration.DEBUG)
- log.fine("ialg: <" + option + ">");
- for (i = 0; i < SRPRegistry.INTEGRITY_ALGORITHMS.length; i++)
- if (SRPRegistry.INTEGRITY_ALGORITHMS[i].equals(option))
- {
- chosenIntegrityAlgorithm = option;
- integrityAvailable = true;
- break;
- }
- }
- else if (option.startsWith(SRPRegistry.OPTION_CONFIDENTIALITY + "="))
- {
- option = option.substring(option.indexOf('=') + 1);
- if (Configuration.DEBUG)
- log.fine("calg: <" + option + ">");
- for (i = 0; i < SRPRegistry.CONFIDENTIALITY_ALGORITHMS.length; i++)
- if (SRPRegistry.CONFIDENTIALITY_ALGORITHMS[i].equals(option))
- {
- chosenConfidentialityAlgorithm = option;
- confidentialityAvailable = true;
- break;
- }
- }
- else if (option.startsWith(SRPRegistry.OPTION_MANDATORY + "="))
- mandatory = option.substring(option.indexOf('=') + 1);
- 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 + "=" + maxBufferSize, x);
- }
- }
- }
- String s;
- Boolean flag;
- s = (String) properties.get(SRPRegistry.SRP_REPLAY_DETECTION);
- flag = Boolean.valueOf(s);
- replayDetection = replaydetectionAvailable && flag.booleanValue();
- s = (String) properties.get(SRPRegistry.SRP_INTEGRITY_PROTECTION);
- flag = Boolean.valueOf(s);
- boolean integrity = integrityAvailable && flag.booleanValue();
- s = (String) properties.get(SRPRegistry.SRP_CONFIDENTIALITY);
- flag = Boolean.valueOf(s);
- boolean confidentiality = confidentialityAvailable && flag.booleanValue();
- // make sure we do the right thing
- if (SRPRegistry.OPTION_REPLAY_DETECTION.equals(mandatory))
- {
- replayDetection = true;
- integrity = true;
- }
- else if (SRPRegistry.OPTION_INTEGRITY.equals(mandatory))
- integrity = true;
- else if (SRPRegistry.OPTION_CONFIDENTIALITY.equals(mandatory))
- confidentiality = true;
- if (replayDetection)
- {
- if (chosenIntegrityAlgorithm == null)
- throw new AuthenticationException(
- "Replay detection is required but no integrity protection "
- + "algorithm was chosen");
- }
- if (integrity)
- {
- if (chosenIntegrityAlgorithm == null)
- throw new AuthenticationException(
- "Integrity protection is required but no algorithm was chosen");
- }
- if (confidentiality)
- {
- if (chosenConfidentialityAlgorithm == null)
- throw new AuthenticationException(
- "Confidentiality protection is required but no algorithm was chosen");
- }
- // 1. check if we'll be using confidentiality; if not set IV to 0-byte
- if (chosenConfidentialityAlgorithm == null)
- cIV = new byte[0];
- else
- {
- // 2. get the block size of the cipher
- final IBlockCipher cipher = CipherFactory.getInstance(chosenConfidentialityAlgorithm);
- if (cipher == null)
- throw new AuthenticationException("createO()",
- new NoSuchAlgorithmException());
- final int blockSize = cipher.defaultBlockSize();
- // 3. generate random iv
- cIV = new byte[blockSize];
- getDefaultPRNG().nextBytes(cIV);
- }
- srp = SRP.instance(mdName);
- // Now create the options list specifying which of the available options
- // we have chosen.
- // For now we just select the defaults. Later we need to add support for
- // properties (perhaps in a file) where a user can specify the list of
- // algorithms they would prefer to use.
- final CPStringBuilder sb = new CPStringBuilder();
- sb.append(SRPRegistry.OPTION_SRP_DIGEST)
- .append("=").append(mdName).append(",");
- if (replayDetection)
- sb.append(SRPRegistry.OPTION_REPLAY_DETECTION).append(",");
- if (integrity)
- sb.append(SRPRegistry.OPTION_INTEGRITY)
- .append("=").append(chosenIntegrityAlgorithm).append(",");
- if (confidentiality)
- sb.append(SRPRegistry.OPTION_CONFIDENTIALITY)
- .append("=").append(chosenConfidentialityAlgorithm).append(",");
- final String result = sb.append(SRPRegistry.OPTION_MAX_BUFFER_SIZE)
- .append("=").append(Registry.SASL_BUFFER_MAX_LIMIT)
- .toString();
- if (Configuration.DEBUG)
- log.exiting(this.getClass().getName(), "createO", result);
- return result;
- }
- private void setupSecurityServices(final boolean sessionReUse)
- throws SaslException
- {
- complete = true; // signal end of authentication phase
- if (! sessionReUse)
- {
- outCounter = inCounter = 0;
- // instantiate cipher if confidentiality protection filter is active
- if (chosenConfidentialityAlgorithm != null)
- {
- if (Configuration.DEBUG)
- log.fine("Activating confidentiality protection filter");
- inCipher = CALG.getInstance(chosenConfidentialityAlgorithm);
- outCipher = CALG.getInstance(chosenConfidentialityAlgorithm);
- }
- // instantiate hmacs if integrity protection filter is active
- if (chosenIntegrityAlgorithm != null)
- {
- if (Configuration.DEBUG)
- log.fine("Activating integrity protection filter");
- inMac = IALG.getInstance(chosenIntegrityAlgorithm);
- outMac = IALG.getInstance(chosenIntegrityAlgorithm);
- }
- }
- else // same session new Keys
- K = srp.generateKn(K, cn, sn);
- final KDF kdf = KDF.getInstance(K);
- // initialise in/out ciphers if confidentiality protection is used
- if (inCipher != null)
- {
- inCipher.init(kdf, sIV, Direction.REVERSED);
- outCipher.init(kdf, cIV, Direction.FORWARD);
- }
- // initialise in/out macs if integrity protection is used
- if (inMac != null)
- {
- inMac.init(kdf);
- outMac.init(kdf);
- }
- if (sid != null && sid.length != 0)
- { // update the security context and save in map
- if (Configuration.DEBUG)
- log.fine("Updating security context for UID = " + uid);
- ClientStore.instance().cacheSession(uid,
- ttl,
- new SecurityContext(srp.getAlgorithm(),
- sid,
- K,
- cIV,
- sIV,
- replayDetection,
- inCounter,
- outCounter,
- inMac, outMac,
- inCipher,
- outCipher));
- }
- }
- private PRNG getDefaultPRNG()
- {
- if (prng == null)
- prng = PRNG.getInstance();
- return prng;
- }
- }
|