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.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: }