OSDN Git Service

libjava/ChangeLog:
[pf3gnuchains/gcc-fork.git] / libjava / classpath / gnu / javax / crypto / sasl / srp / SRPServer.java
1 /* SRPServer.java -- 
2    Copyright (C) 2003, 2006 Free Software Foundation, Inc.
3
4 This file is a part of GNU Classpath.
5
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or (at
9 your option) any later version.
10
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
19 USA
20
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library.  Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module.  An independent module is a module which is not derived from
33 or based on this library.  If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so.  If you do not wish to do so, delete this
36 exception statement from your version.  */
37
38
39 package gnu.javax.crypto.sasl.srp;
40
41 import gnu.java.lang.CPStringBuilder;
42
43 import gnu.java.security.Configuration;
44 import gnu.java.security.Registry;
45 import gnu.java.security.util.PRNG;
46 import gnu.java.security.util.Util;
47 import gnu.javax.crypto.assembly.Direction;
48 import gnu.javax.crypto.cipher.CipherFactory;
49 import gnu.javax.crypto.cipher.IBlockCipher;
50 import gnu.javax.crypto.key.IKeyAgreementParty;
51 import gnu.javax.crypto.key.IncomingMessage;
52 import gnu.javax.crypto.key.KeyAgreementException;
53 import gnu.javax.crypto.key.KeyAgreementFactory;
54 import gnu.javax.crypto.key.OutgoingMessage;
55 import gnu.javax.crypto.key.srp6.SRP6KeyAgreement;
56 import gnu.javax.crypto.sasl.IllegalMechanismStateException;
57 import gnu.javax.crypto.sasl.InputBuffer;
58 import gnu.javax.crypto.sasl.IntegrityException;
59 import gnu.javax.crypto.sasl.OutputBuffer;
60 import gnu.javax.crypto.sasl.ServerMechanism;
61
62 import java.io.ByteArrayOutputStream;
63 import java.io.IOException;
64 import java.io.UnsupportedEncodingException;
65 import java.math.BigInteger;
66 import java.util.Arrays;
67 import java.util.HashMap;
68 import java.util.StringTokenizer;
69 import java.util.logging.Logger;
70
71 import javax.security.sasl.AuthenticationException;
72 import javax.security.sasl.SaslException;
73 import javax.security.sasl.SaslServer;
74
75 /**
76  * The SASL-SRP server-side mechanism.
77  */
78 public class SRPServer
79     extends ServerMechanism
80     implements SaslServer
81 {
82   private static final Logger log = Logger.getLogger(SRPServer.class.getName());
83   private String U = null; // client's username
84   private BigInteger N, g, A, B;
85   private byte[] s; // salt
86   private byte[] cIV, sIV; // client+server IVs, when confidentiality is on
87   private byte[] cn, sn; // client's and server's nonce
88   private SRP srp; // SRP algorithm instance used by this server
89   private byte[] sid; // session ID when re-used
90   private int ttl = 360; // session time-to-live in seconds
91   private byte[] cCB; // peer's channel binding'
92   private String mandatory; // List of available options
93   private String L = null;
94   private String o;
95   private String chosenIntegrityAlgorithm;
96   private String chosenConfidentialityAlgorithm;
97   private int rawSendSize = Registry.SASL_BUFFER_MAX_LIMIT;
98   private byte[] K; // shared session key
99   private boolean replayDetection = true; // whether Replay Detection is on
100   private int inCounter = 0; // messages sequence numbers
101   private int outCounter = 0;
102   private IALG inMac, outMac; // if !null, use for integrity
103   private CALG inCipher, outCipher; // if !null, use for confidentiality
104   private IKeyAgreementParty serverHandler =
105       KeyAgreementFactory.getPartyBInstance(Registry.SRP_SASL_KA);
106   /** Our default source of randomness. */
107   private PRNG prng = null;
108
109   public SRPServer()
110   {
111     super(Registry.SASL_SRP_MECHANISM);
112   }
113
114   protected void initMechanism() throws SaslException
115   {
116     // TODO:
117     // we must have a means to map a given username to a preferred
118     // SRP hash algorithm; otherwise we end up using _always_ SHA.
119     // for the time being get it from the mechanism properties map
120     // and apply it for all users.
121     final String mda = (String) properties.get(SRPRegistry.SRP_HASH);
122     srp = SRP.instance(mda == null ? SRPRegistry.SRP_DEFAULT_DIGEST_NAME : mda);
123   }
124
125   protected void resetMechanism() throws SaslException
126   {
127     s = null;
128     A = B = null;
129     K = null;
130     inMac = outMac = null;
131     inCipher = outCipher = null;
132     sid = null;
133   }
134
135   public byte[] evaluateResponse(final byte[] response) throws SaslException
136   {
137     switch (state)
138       {
139       case 0:
140         if (response == null)
141           return null;
142         state++;
143         return sendProtocolElements(response);
144       case 1:
145         if (! complete)
146           {
147             state++;
148             return sendEvidence(response);
149           }
150       // else fall through
151       default:
152         throw new IllegalMechanismStateException("evaluateResponse()");
153       }
154   }
155
156   protected byte[] engineUnwrap(final byte[] incoming, final int offset,
157                                 final int len) throws SaslException
158   {
159     if (Configuration.DEBUG)
160       log.entering(this.getClass().getName(), "engineUnwrap");
161     if (inMac == null && inCipher == null)
162       throw new IllegalStateException("connection is not protected");
163     if (Configuration.DEBUG)
164       log.fine("Incoming buffer (before security): "
165                + Util.dumpString(incoming, offset, len));
166     // at this point one, or both, of confidentiality and integrity protection
167     // services are active.
168     final byte[] result;
169     try
170       {
171         if (inMac != null)
172           { // integrity bytes are at the end of the stream
173             final int macBytesCount = inMac.length();
174             final int payloadLength = len - macBytesCount;
175             final byte[] received_mac = new byte[macBytesCount];
176             System.arraycopy(incoming, offset + payloadLength, received_mac, 0,
177                              macBytesCount);
178             if (Configuration.DEBUG)
179               log.fine("Got C (received MAC): " + Util.dumpString(received_mac));
180             inMac.update(incoming, offset, payloadLength);
181             if (replayDetection)
182               {
183                 inCounter++;
184                 if (Configuration.DEBUG)
185                   log.fine("inCounter=" + String.valueOf(inCounter));
186                 inMac.update(new byte[] {
187                     (byte)(inCounter >>> 24),
188                     (byte)(inCounter >>> 16),
189                     (byte)(inCounter >>> 8),
190                     (byte) inCounter });
191               }
192             final byte[] computed_mac = inMac.doFinal();
193             if (Configuration.DEBUG)
194               log.fine("Computed MAC: " + Util.dumpString(computed_mac));
195             if (! Arrays.equals(received_mac, computed_mac))
196               throw new IntegrityException("engineUnwrap()");
197             // deal with the payload, which can be either plain or encrypted
198             if (inCipher != null)
199               result = inCipher.doFinal(incoming, offset, payloadLength);
200             else
201               {
202                 result = new byte[payloadLength];
203                 System.arraycopy(incoming, offset, result, 0, result.length);
204               }
205           }
206         else // no integrity protection; just confidentiality
207           result = inCipher.doFinal(incoming, offset, len);
208       }
209     catch (IOException x)
210       {
211         if (x instanceof SaslException)
212           throw (SaslException) x;
213         throw new SaslException("engineUnwrap()", x);
214       }
215     if (Configuration.DEBUG)
216       {
217         log.fine("Incoming buffer (after security): " + Util.dumpString(result));
218         log.exiting(this.getClass().getName(), "engineUnwrap");
219       }
220     return result;
221   }
222
223   protected byte[] engineWrap(final byte[] outgoing, final int offset,
224                               final int len) throws SaslException
225   {
226     if (Configuration.DEBUG)
227       log.entering(this.getClass().getName(), "engineWrap");
228     if (outMac == null && outCipher == null)
229       throw new IllegalStateException("connection is not protected");
230     if (Configuration.DEBUG)
231       {
232         log.fine("Outgoing buffer (before security) (hex): "
233                  + Util.dumpString(outgoing, offset, len));
234         log.fine("Outgoing buffer (before security) (str): \""
235                  + new String(outgoing, offset, len) + "\"");
236       }
237     // at this point one, or both, of confidentiality and integrity protection
238     // services are active.
239     byte[] result;
240     try
241       {
242         final ByteArrayOutputStream out = new ByteArrayOutputStream();
243         if (outCipher != null)
244           {
245             result = outCipher.doFinal(outgoing, offset, len);
246             if (Configuration.DEBUG)
247               log.fine("Encoding c (encrypted plaintext): "
248                        + Util.dumpString(result));
249             out.write(result);
250             if (outMac != null)
251               {
252                 outMac.update(result);
253                 if (replayDetection)
254                   {
255                     outCounter++;
256                     if (Configuration.DEBUG)
257                       log.fine("outCounter=" + outCounter);
258                     outMac.update(new byte[] {
259                         (byte)(outCounter >>> 24),
260                         (byte)(outCounter >>> 16),
261                         (byte)(outCounter >>> 8),
262                         (byte) outCounter });
263                   }
264                 final byte[] C = outMac.doFinal();
265                 out.write(C);
266                 if (Configuration.DEBUG)
267                   log.fine("Encoding C (integrity checksum): " + Util.dumpString(C));
268               }
269             // else ciphertext only; do nothing
270           }
271         else // no confidentiality; just integrity [+ replay detection]
272           {
273             if (Configuration.DEBUG)
274               log.fine("Encoding p (plaintext): "
275                        + Util.dumpString(outgoing, offset, len));
276             out.write(outgoing, offset, len);
277             outMac.update(outgoing, offset, len);
278             if (replayDetection)
279               {
280                 outCounter++;
281                 if (Configuration.DEBUG)
282                   log.fine("outCounter=" + outCounter);
283                 outMac.update(new byte[] {
284                     (byte)(outCounter >>> 24),
285                     (byte)(outCounter >>> 16),
286                     (byte)(outCounter >>> 8),
287                     (byte) outCounter });
288               }
289             final byte[] C = outMac.doFinal();
290             out.write(C);
291             if (Configuration.DEBUG)
292               log.fine("Encoding C (integrity checksum): " + Util.dumpString(C));
293           }
294         result = out.toByteArray();
295       }
296     catch (IOException x)
297       {
298         if (x instanceof SaslException)
299           throw (SaslException) x;
300         throw new SaslException("engineWrap()", x);
301       }
302     if (Configuration.DEBUG)
303       log.exiting(this.getClass().getName(), "engineWrap");
304     return result;
305   }
306
307   protected String getNegotiatedQOP()
308   {
309     if (inMac != null)
310       {
311         if (inCipher != null)
312           return Registry.QOP_AUTH_CONF;
313         return Registry.QOP_AUTH_INT;
314       }
315     return Registry.QOP_AUTH;
316   }
317
318   protected String getNegotiatedStrength()
319   {
320     if (inMac != null)
321       {
322         if (inCipher != null)
323           return Registry.STRENGTH_HIGH;
324         return Registry.STRENGTH_MEDIUM;
325       }
326     return Registry.STRENGTH_LOW;
327   }
328
329   protected String getNegotiatedRawSendSize()
330   {
331     return String.valueOf(rawSendSize);
332   }
333
334   protected String getReuse()
335   {
336     return Registry.REUSE_TRUE;
337   }
338
339   private byte[] sendProtocolElements(final byte[] input) throws SaslException
340   {
341     if (Configuration.DEBUG)
342       {
343         log.entering(this.getClass().getName(), "sendProtocolElements");
344         log.fine("C: " + Util.dumpString(input));
345       }
346     // Client send U, I, sid, cn
347     final InputBuffer frameIn = new InputBuffer(input);
348     try
349       {
350         U = frameIn.getText(); // Extract username
351         if (Configuration.DEBUG)
352           log.fine("Got U (username): \"" + U + "\"");
353         authorizationID = frameIn.getText(); // Extract authorisation ID
354         if (Configuration.DEBUG)
355           log.fine("Got I (userid): \"" + authorizationID + "\"");
356         sid = frameIn.getEOS();
357         if (Configuration.DEBUG)
358           log.fine("Got sid (session ID): " + new String(sid));
359         cn = frameIn.getOS();
360         if (Configuration.DEBUG)
361           log.fine("Got cn (client nonce): " + Util.dumpString(cn));
362         cCB = frameIn.getEOS();
363         if (Configuration.DEBUG)
364           log.fine("Got cCB (client channel binding): " + Util.dumpString(cCB));
365       }
366     catch (IOException x)
367       {
368         if (x instanceof SaslException)
369           throw (SaslException) x;
370         throw new AuthenticationException("sendProtocolElements()", x);
371       }
372     // do/can we re-use?
373     if (ServerStore.instance().isAlive(sid))
374       {
375         final SecurityContext ctx = ServerStore.instance().restoreSession(sid);
376         srp = SRP.instance(ctx.getMdName());
377         K = ctx.getK();
378         cIV = ctx.getClientIV();
379         sIV = ctx.getServerIV();
380         replayDetection = ctx.hasReplayDetection();
381         inCounter = ctx.getInCounter();
382         outCounter = ctx.getOutCounter();
383         inMac = ctx.getInMac();
384         outMac = ctx.getOutMac();
385         inCipher = ctx.getInCipher();
386         outCipher = ctx.getOutCipher();
387         if (sn == null || sn.length != 16)
388           sn = new byte[16];
389         getDefaultPRNG().nextBytes(sn);
390         setupSecurityServices(false);
391         final OutputBuffer frameOut = new OutputBuffer();
392         try
393           {
394             frameOut.setScalar(1, 0xFF);
395             frameOut.setOS(sn);
396             frameOut.setEOS(channelBinding);
397           }
398         catch (IOException x)
399           {
400             if (x instanceof SaslException)
401               throw (SaslException) x;
402             throw new AuthenticationException("sendProtocolElements()", x);
403           }
404         final byte[] result = frameOut.encode();
405         if (Configuration.DEBUG)
406           {
407             log.fine("Old session...");
408             log.fine("S: " + Util.dumpString(result));
409             log.fine("  sn = " + Util.dumpString(sn));
410             log.fine(" sCB = " + Util.dumpString(channelBinding));
411             log.exiting(this.getClass().getName(), "sendProtocolElements");
412           }
413         return result;
414       }
415     else
416       { // new session
417         authenticator.activate(properties);
418         // -------------------------------------------------------------------
419         final HashMap mapB = new HashMap();
420         mapB.put(SRP6KeyAgreement.HASH_FUNCTION, srp.getAlgorithm());
421         mapB.put(SRP6KeyAgreement.HOST_PASSWORD_DB, authenticator);
422         try
423           {
424             serverHandler.init(mapB);
425             OutgoingMessage out = new OutgoingMessage();
426             out.writeString(U);
427             IncomingMessage in = new IncomingMessage(out.toByteArray());
428             out = serverHandler.processMessage(in);
429             in = new IncomingMessage(out.toByteArray());
430             N = in.readMPI();
431             g = in.readMPI();
432             s = in.readMPI().toByteArray();
433             B = in.readMPI();
434           }
435         catch (KeyAgreementException x)
436           {
437             throw new SaslException("sendProtocolElements()", x);
438           }
439         // -------------------------------------------------------------------
440         if (Configuration.DEBUG)
441           {
442             log.fine("Encoding N (modulus): " + Util.dump(N));
443             log.fine("Encoding g (generator): " + Util.dump(g));
444             log.fine("Encoding s (client's salt): " + Util.dumpString(s));
445             log.fine("Encoding B (server ephemeral public key): " + Util.dump(B));
446           }
447         // The server creates an options list (L), which consists of a
448         // comma-separated list of option strings that specify the security
449         // service options the server supports.
450         L = createL();
451         if (Configuration.DEBUG)
452           {
453             log.fine("Encoding L (available options): \"" + L + "\"");
454             log.fine("Encoding sIV (server IV): " + Util.dumpString(sIV));
455           }
456         final OutputBuffer frameOut = new OutputBuffer();
457         try
458           {
459             frameOut.setScalar(1, 0x00);
460             frameOut.setMPI(N);
461             frameOut.setMPI(g);
462             frameOut.setOS(s);
463             frameOut.setMPI(B);
464             frameOut.setText(L);
465           }
466         catch (IOException x)
467           {
468             if (x instanceof SaslException)
469               throw (SaslException) x;
470             throw new AuthenticationException("sendProtocolElements()", x);
471           }
472         final byte[] result = frameOut.encode();
473         if (Configuration.DEBUG)
474           {
475             log.fine("New session...");
476             log.fine("S: " + Util.dumpString(result));
477             log.fine("   N = 0x" + N.toString(16));
478             log.fine("   g = 0x" + g.toString(16));
479             log.fine("   s = " + Util.dumpString(s));
480             log.fine("   B = 0x" + B.toString(16));
481             log.fine("   L = " + L);
482             log.exiting(this.getClass().getName(), "sendProtocolElements");
483           }
484         return result;
485       }
486   }
487
488   private byte[] sendEvidence(final byte[] input) throws SaslException
489   {
490     if (Configuration.DEBUG)
491       {
492         log.entering(this.getClass().getName(), "sendEvidence");
493         log.fine("C: " + Util.dumpString(input));
494       }
495     // Client send A, M1, o, cIV
496     final InputBuffer frameIn = new InputBuffer(input);
497     final byte[] M1;
498     try
499       {
500         A = frameIn.getMPI(); // Extract client's ephemeral public key
501         if (Configuration.DEBUG)
502           log.fine("Got A (client ephemeral public key): " + Util.dump(A));
503         M1 = frameIn.getOS(); // Extract evidence
504         if (Configuration.DEBUG)
505           log.fine("Got M1 (client evidence): " + Util.dumpString(M1));
506         o = frameIn.getText(); // Extract client's options list
507         if (Configuration.DEBUG)
508           log.fine("Got o (client chosen options): \"" + o + "\"");
509         cIV = frameIn.getOS(); // Extract client's IV
510         if (Configuration.DEBUG)
511           log.fine("Got cIV (client IV): " + Util.dumpString(cIV));
512       }
513     catch (IOException x)
514       {
515         if (x instanceof SaslException)
516           throw (SaslException) x;
517         throw new AuthenticationException("sendEvidence()", x);
518       }
519     // Parse client's options and set security layer variables
520     parseO(o);
521     // ----------------------------------------------------------------------
522     try
523       {
524         final OutgoingMessage out = new OutgoingMessage();
525         out.writeMPI(A);
526         final IncomingMessage in = new IncomingMessage(out.toByteArray());
527         serverHandler.processMessage(in);
528         K = serverHandler.getSharedSecret();
529       }
530     catch (KeyAgreementException x)
531       {
532         throw new SaslException("sendEvidence()", x);
533       }
534     // ----------------------------------------------------------------------
535     if (Configuration.DEBUG)
536       log.fine("K: " + Util.dumpString(K));
537     final byte[] expected;
538     try
539       {
540         expected = srp.generateM1(N, g, U, s, A, B, K, authorizationID, L, cn,
541                                   cCB);
542       }
543     catch (UnsupportedEncodingException x)
544       {
545         throw new AuthenticationException("sendEvidence()", x);
546       }
547     // Verify client evidence
548     if (! Arrays.equals(M1, expected))
549       throw new AuthenticationException("M1 mismatch");
550     setupSecurityServices(true);
551     final byte[] M2;
552     try
553       {
554         M2 = srp.generateM2(A, M1, K, U, authorizationID, o, sid, ttl, cIV,
555                             sIV, channelBinding);
556       }
557     catch (UnsupportedEncodingException x)
558       {
559         throw new AuthenticationException("sendEvidence()", x);
560       }
561     final OutputBuffer frameOut = new OutputBuffer();
562     try
563       {
564         frameOut.setOS(M2);
565         frameOut.setOS(sIV);
566         frameOut.setEOS(sid);
567         frameOut.setScalar(4, ttl);
568         frameOut.setEOS(channelBinding);
569       }
570     catch (IOException x)
571       {
572         if (x instanceof SaslException)
573           throw (SaslException) x;
574         throw new AuthenticationException("sendEvidence()", x);
575       }
576     final byte[] result = frameOut.encode();
577     if (Configuration.DEBUG)
578       {
579         log.fine("S: " + Util.dumpString(result));
580         log.fine("  M2 = " + Util.dumpString(M2));
581         log.fine(" sIV = " + Util.dumpString(sIV));
582         log.fine(" sid = " + new String(sid));
583         log.fine(" ttl = " + ttl);
584         log.fine(" sCB = " + Util.dumpString(channelBinding));
585         log.exiting(this.getClass().getName(), "sendEvidence");
586       }
587     return result;
588   }
589
590   private String createL()
591   {
592     if (Configuration.DEBUG)
593       log.entering(this.getClass().getName(), "createL()");
594     String s = (String) properties.get(SRPRegistry.SRP_MANDATORY);
595     if (s == null)
596       s = SRPRegistry.DEFAULT_MANDATORY;
597
598     if (! SRPRegistry.MANDATORY_NONE.equals(s)
599         && ! SRPRegistry.OPTION_REPLAY_DETECTION.equals(s)
600         && ! SRPRegistry.OPTION_INTEGRITY.equals(s)
601         && ! SRPRegistry.OPTION_CONFIDENTIALITY.equals(s))
602       {
603         if (Configuration.DEBUG)
604           log.fine("Unrecognised mandatory option (" + s + "). Using default...");
605         s = SRPRegistry.DEFAULT_MANDATORY;
606       }
607     mandatory = s;
608     s = (String) properties.get(SRPRegistry.SRP_CONFIDENTIALITY);
609     final boolean confidentiality = (s == null ? SRPRegistry.DEFAULT_CONFIDENTIALITY
610                                                : Boolean.valueOf(s).booleanValue());
611     s = (String) properties.get(SRPRegistry.SRP_INTEGRITY_PROTECTION);
612     boolean integrity = (s == null ? SRPRegistry.DEFAULT_INTEGRITY
613                                    : Boolean.valueOf(s).booleanValue());
614     s = (String) properties.get(SRPRegistry.SRP_REPLAY_DETECTION);
615     final boolean replayDetection = (s == null ? SRPRegistry.DEFAULT_REPLAY_DETECTION
616                                                : Boolean.valueOf(s).booleanValue());
617     final CPStringBuilder sb = new CPStringBuilder();
618     sb.append(SRPRegistry.OPTION_SRP_DIGEST).append("=")
619       .append(srp.getAlgorithm()).append(",");
620
621     if (! SRPRegistry.MANDATORY_NONE.equals(mandatory))
622       sb.append(SRPRegistry.OPTION_MANDATORY)
623         .append("=").append(mandatory).append(",");
624
625     if (replayDetection)
626       {
627         sb.append(SRPRegistry.OPTION_REPLAY_DETECTION).append(",");
628         // if replay detection is on then force integrity protection
629         integrity = true;
630       }
631     int i;
632     if (integrity)
633       {
634         for (i = 0; i < SRPRegistry.INTEGRITY_ALGORITHMS.length; i++)
635           sb.append(SRPRegistry.OPTION_INTEGRITY).append("=")
636             .append(SRPRegistry.INTEGRITY_ALGORITHMS[i]).append(",");
637       }
638     if (confidentiality)
639       {
640         IBlockCipher cipher;
641         for (i = 0; i < SRPRegistry.CONFIDENTIALITY_ALGORITHMS.length; i++)
642           {
643             cipher = CipherFactory.getInstance(SRPRegistry.CONFIDENTIALITY_ALGORITHMS[i]);
644             if (cipher != null)
645               sb.append(SRPRegistry.OPTION_CONFIDENTIALITY).append("=")
646                 .append(SRPRegistry.CONFIDENTIALITY_ALGORITHMS[i]).append(",");
647           }
648       }
649     final String result = sb.append(SRPRegistry.OPTION_MAX_BUFFER_SIZE)
650                             .append("=").append(Registry.SASL_BUFFER_MAX_LIMIT)
651                             .toString();
652     if (Configuration.DEBUG)
653       log.exiting(this.getClass().getName(), "createL");
654     return result;
655   }
656
657   // Parse client's options and set security layer variables
658   private void parseO(final String o) throws AuthenticationException
659   {
660     this.replayDetection = false;
661     boolean integrity = false;
662     boolean confidentiality = false;
663     String option;
664     int i;
665
666     final StringTokenizer st = new StringTokenizer(o.toLowerCase(), ",");
667     while (st.hasMoreTokens())
668       {
669         option = st.nextToken();
670         if (Configuration.DEBUG)
671           log.fine("option: <" + option + ">");
672         if (option.equals(SRPRegistry.OPTION_REPLAY_DETECTION))
673           replayDetection = true;
674         else if (option.startsWith(SRPRegistry.OPTION_INTEGRITY + "="))
675           {
676             if (integrity)
677               throw new AuthenticationException(
678                   "Only one integrity algorithm may be chosen");
679             option = option.substring(option.indexOf('=') + 1);
680             if (Configuration.DEBUG)
681               log.fine("algorithm: <" + option + ">");
682             for (i = 0; i < SRPRegistry.INTEGRITY_ALGORITHMS.length; i++)
683               {
684                 if (SRPRegistry.INTEGRITY_ALGORITHMS[i].equals(option))
685                   {
686                     chosenIntegrityAlgorithm = option;
687                     integrity = true;
688                     break;
689                   }
690               }
691             if (! integrity)
692               throw new AuthenticationException("Unknown integrity algorithm: "
693                                                 + option);
694           }
695         else if (option.startsWith(SRPRegistry.OPTION_CONFIDENTIALITY + "="))
696           {
697             if (confidentiality)
698               throw new AuthenticationException(
699                   "Only one confidentiality algorithm may be chosen");
700             option = option.substring(option.indexOf('=') + 1);
701             if (Configuration.DEBUG)
702               log.fine("algorithm: <" + option + ">");
703             for (i = 0; i < SRPRegistry.CONFIDENTIALITY_ALGORITHMS.length; i++)
704               {
705                 if (SRPRegistry.CONFIDENTIALITY_ALGORITHMS[i].equals(option))
706                   {
707                     chosenConfidentialityAlgorithm = option;
708                     confidentiality = true;
709                     break;
710                   }
711               }
712             if (! confidentiality)
713               throw new AuthenticationException("Unknown confidentiality algorithm: "
714                                                 + option);
715           }
716         else if (option.startsWith(SRPRegistry.OPTION_MAX_BUFFER_SIZE + "="))
717           {
718             final String maxBufferSize = option.substring(option.indexOf('=') + 1);
719             try
720               {
721                 rawSendSize = Integer.parseInt(maxBufferSize);
722                 if (rawSendSize > Registry.SASL_BUFFER_MAX_LIMIT
723                     || rawSendSize < 1)
724                   throw new AuthenticationException(
725                       "Illegal value for 'maxbuffersize' option");
726               }
727             catch (NumberFormatException x)
728               {
729                 throw new AuthenticationException(
730                     SRPRegistry.OPTION_MAX_BUFFER_SIZE + "=" + maxBufferSize, x);
731               }
732           }
733       }
734     // check if client did the right thing
735     if (replayDetection)
736       {
737         if (! integrity)
738           throw new AuthenticationException(
739               "Missing integrity protection algorithm but replay detection is chosen");
740       }
741     if (mandatory.equals(SRPRegistry.OPTION_REPLAY_DETECTION))
742       {
743         if (! replayDetection)
744           throw new AuthenticationException(
745               "Replay detection is mandatory but was not chosen");
746       }
747     if (mandatory.equals(SRPRegistry.OPTION_INTEGRITY))
748       {
749         if (! integrity)
750           throw new AuthenticationException(
751               "Integrity protection is mandatory but was not chosen");
752       }
753     if (mandatory.equals(SRPRegistry.OPTION_CONFIDENTIALITY))
754       {
755         if (! confidentiality)
756           throw new AuthenticationException(
757               "Confidentiality is mandatory but was not chosen");
758       }
759     int blockSize = 0;
760     if (chosenConfidentialityAlgorithm != null)
761       {
762         final IBlockCipher cipher = CipherFactory.getInstance(chosenConfidentialityAlgorithm);
763         if (cipher != null)
764           blockSize = cipher.defaultBlockSize();
765         else // should not happen
766           throw new AuthenticationException("Confidentiality algorithm ("
767                                             + chosenConfidentialityAlgorithm
768                                             + ") not available");
769       }
770     sIV = new byte[blockSize];
771     if (blockSize > 0)
772       getDefaultPRNG().nextBytes(sIV);
773   }
774
775   private void setupSecurityServices(final boolean newSession)
776       throws SaslException
777   {
778     complete = true; // signal end of authentication phase
779     if (newSession)
780       {
781         outCounter = inCounter = 0;
782         // instantiate cipher if confidentiality protection filter is active
783         if (chosenConfidentialityAlgorithm != null)
784           {
785             if (Configuration.DEBUG)
786               log.fine("Activating confidentiality protection filter");
787             inCipher = CALG.getInstance(chosenConfidentialityAlgorithm);
788             outCipher = CALG.getInstance(chosenConfidentialityAlgorithm);
789           }
790         // instantiate hmacs if integrity protection filter is active
791         if (chosenIntegrityAlgorithm != null)
792           {
793             if (Configuration.DEBUG)
794               log.fine("Activating integrity protection filter");
795             inMac = IALG.getInstance(chosenIntegrityAlgorithm);
796             outMac = IALG.getInstance(chosenIntegrityAlgorithm);
797           }
798         // generate a new sid if at least integrity is used
799         sid = (inMac != null ? ServerStore.getNewSessionID() : new byte[0]);
800       }
801     else // same session new keys
802       K = srp.generateKn(K, cn, sn);
803
804     final KDF kdf = KDF.getInstance(K);
805     // initialise in/out ciphers if confidentaility protection is used
806     if (inCipher != null)
807       {
808         outCipher.init(kdf, sIV, Direction.FORWARD);
809         inCipher.init(kdf, cIV, Direction.REVERSED);
810       }
811     // initialise in/out macs if integrity protection is used
812     if (inMac != null)
813       {
814         outMac.init(kdf);
815         inMac.init(kdf);
816       }
817     if (sid != null && sid.length != 0)
818       { // update the security context and save in map
819         if (Configuration.DEBUG)
820           log.fine("Updating security context for sid = " + new String(sid));
821         ServerStore.instance().cacheSession(ttl,
822                                             new SecurityContext(srp.getAlgorithm(),
823                                                                 sid,
824                                                                 K,
825                                                                 cIV,
826                                                                 sIV,
827                                                                 replayDetection,
828                                                                 inCounter,
829                                                                 outCounter,
830                                                                 inMac, outMac,
831                                                                 inCipher,
832                                                                 outCipher));
833       }
834   }
835
836   private PRNG getDefaultPRNG()
837   {
838     if (prng == null)
839       prng = PRNG.getInstance();
840     return prng;
841   }
842 }