Source for gnu.javax.crypto.sasl.srp.SRPServer

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