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

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