libjava/ChangeLog:
[official-gcc.git] / libjava / classpath / gnu / javax / crypto / sasl / srp / SRPClient.java
blob3406c14cec452628e70e6de1a46d19111e74674d
1 /* SRPClient.java --
2 Copyright (C) 2003, 2006 Free Software Foundation, Inc.
4 This file is a part of GNU Classpath.
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.
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.
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
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.
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. */
39 package gnu.javax.crypto.sasl.srp;
41 import gnu.java.lang.CPStringBuilder;
43 import gnu.java.security.Configuration;
44 import gnu.java.security.Registry;
45 import gnu.java.security.hash.MD5;
46 import gnu.java.security.util.PRNG;
47 import gnu.java.security.util.Util;
48 import gnu.javax.crypto.assembly.Direction;
49 import gnu.javax.crypto.cipher.CipherFactory;
50 import gnu.javax.crypto.cipher.IBlockCipher;
51 import gnu.javax.crypto.key.IKeyAgreementParty;
52 import gnu.javax.crypto.key.IncomingMessage;
53 import gnu.javax.crypto.key.KeyAgreementException;
54 import gnu.javax.crypto.key.KeyAgreementFactory;
55 import gnu.javax.crypto.key.OutgoingMessage;
56 import gnu.javax.crypto.key.srp6.SRP6KeyAgreement;
57 import gnu.javax.crypto.sasl.ClientMechanism;
58 import gnu.javax.crypto.sasl.IllegalMechanismStateException;
59 import gnu.javax.crypto.sasl.InputBuffer;
60 import gnu.javax.crypto.sasl.IntegrityException;
61 import gnu.javax.crypto.sasl.OutputBuffer;
62 import gnu.javax.security.auth.Password;
64 import java.io.ByteArrayOutputStream;
65 import java.io.IOException;
66 import java.io.UnsupportedEncodingException;
67 import java.math.BigInteger;
68 import java.security.NoSuchAlgorithmException;
69 import java.util.Arrays;
70 import java.util.HashMap;
71 import java.util.StringTokenizer;
72 import java.util.logging.Logger;
74 import javax.security.auth.DestroyFailedException;
75 import javax.security.auth.callback.Callback;
76 import javax.security.auth.callback.NameCallback;
77 import javax.security.auth.callback.PasswordCallback;
78 import javax.security.auth.callback.UnsupportedCallbackException;
79 import javax.security.sasl.AuthenticationException;
80 import javax.security.sasl.SaslClient;
81 import javax.security.sasl.SaslException;
83 /**
84 * The SASL-SRP client-side mechanism.
86 public class SRPClient
87 extends ClientMechanism
88 implements SaslClient
90 private static final Logger log = Logger.getLogger(SRPClient.class.getName());
91 private String uid; // the unique key for this type of client
92 private String U; // the authentication identity
93 BigInteger N, g, A, B;
94 private Password password; // the authentication credentials
95 private byte[] s; // the user's salt
96 private byte[] cIV, sIV; // client+server IVs, when confidentiality is on
97 private byte[] M1, M2; // client+server evidences
98 private byte[] cn, sn; // client's and server's nonce
99 private SRP srp; // SRP algorithm instance used by this client
100 private byte[] sid; // session ID when re-used
101 private int ttl; // session time-to-live in seconds
102 private byte[] sCB; // the peer's channel binding data
103 private String L; // available options
104 private String o;
105 private String chosenIntegrityAlgorithm;
106 private String chosenConfidentialityAlgorithm;
107 private int rawSendSize = Registry.SASL_BUFFER_MAX_LIMIT;
108 private byte[] K; // shared session key
109 private boolean replayDetection = true; // whether Replay Detection is on
110 private int inCounter = 0; // messages sequence numbers
111 private int outCounter = 0;
112 private IALG inMac, outMac; // if !null, use for integrity
113 private CALG inCipher, outCipher; // if !null, use for confidentiality
114 private IKeyAgreementParty clientHandler =
115 KeyAgreementFactory.getPartyAInstance(Registry.SRP_SASL_KA);
116 /** Our default source of randomness. */
117 private PRNG prng = null;
119 public SRPClient()
121 super(Registry.SASL_SRP_MECHANISM);
124 protected void initMechanism() throws SaslException
126 // we shall keep track of the sid (and the security context of this SRP
127 // client) based on the initialisation parameters of an SRP session.
128 // we shall compute a unique key for those parameters and key the sid
129 // (and the security context) accordingly.
130 // 1. compute the mapping key. use MD5 (the fastest) for this purpose
131 final MD5 md = new MD5();
132 byte[] b;
133 b = authorizationID.getBytes();
134 md.update(b, 0, b.length);
135 b = serverName.getBytes();
136 md.update(b, 0, b.length);
137 b = protocol.getBytes();
138 md.update(b, 0, b.length);
139 if (channelBinding.length > 0)
140 md.update(channelBinding, 0, channelBinding.length);
142 uid = Util.toBase64(md.digest());
143 if (ClientStore.instance().isAlive(uid))
145 final SecurityContext ctx = ClientStore.instance().restoreSession(uid);
146 srp = SRP.instance(ctx.getMdName());
147 sid = ctx.getSID();
148 K = ctx.getK();
149 cIV = ctx.getClientIV();
150 sIV = ctx.getServerIV();
151 replayDetection = ctx.hasReplayDetection();
152 inCounter = ctx.getInCounter();
153 outCounter = ctx.getOutCounter();
154 inMac = ctx.getInMac();
155 outMac = ctx.getOutMac();
156 inCipher = ctx.getInCipher();
157 outCipher = ctx.getOutCipher();
159 else
161 sid = new byte[0];
162 ttl = 0;
163 K = null;
164 cIV = null;
165 sIV = null;
166 cn = null;
167 sn = null;
171 protected void resetMechanism() throws SaslException
175 password.destroy();
177 catch (DestroyFailedException dfe)
179 SaslException se = new SaslException("resetMechanism()");
180 se.initCause(dfe);
181 throw se;
183 password = null;
184 M1 = null;
185 K = null;
186 cIV = null;
187 sIV = null;
188 inMac = outMac = null;
189 inCipher = outCipher = null;
190 sid = null;
191 ttl = 0;
192 cn = null;
193 sn = null;
196 public boolean hasInitialResponse()
198 return true;
201 public byte[] evaluateChallenge(final byte[] challenge) throws SaslException
203 switch (state)
205 case 0:
206 state++;
207 return sendIdentities();
208 case 1:
209 state++;
210 final byte[] result = sendPublicKey(challenge);
213 password.destroy(); //don't need further this session
215 catch (DestroyFailedException x)
217 SaslException se = new SaslException("sendPublicKey()");
218 se.initCause(se);
219 throw se;
221 return result;
222 case 2: // should only occur if session re-use was rejected
223 if (! complete)
225 state++;
226 return receiveEvidence(challenge);
228 // else fall through
229 default:
230 throw new IllegalMechanismStateException("evaluateChallenge()");
234 protected byte[] engineUnwrap(final byte[] incoming, final int offset,
235 final int len) throws SaslException
237 if (Configuration.DEBUG)
238 log.entering(this.getClass().getName(), "engineUnwrap");
239 if (inMac == null && inCipher == null)
240 throw new IllegalStateException("connection is not protected");
241 // at this point one, or both, of confidentiality and integrity protection
242 // services are active.
243 final byte[] result;
246 if (inMac != null)
247 { // integrity bytes are at the end of the stream
248 final int macBytesCount = inMac.length();
249 final int payloadLength = len - macBytesCount;
250 final byte[] received_mac = new byte[macBytesCount];
251 System.arraycopy(incoming, offset + payloadLength, received_mac, 0,
252 macBytesCount);
253 if (Configuration.DEBUG)
254 log.fine("Got C (received MAC): " + Util.dumpString(received_mac));
255 inMac.update(incoming, offset, payloadLength);
256 if (replayDetection)
258 inCounter++;
259 if (Configuration.DEBUG)
260 log.fine("inCounter=" + inCounter);
261 inMac.update(new byte[] {
262 (byte)(inCounter >>> 24),
263 (byte)(inCounter >>> 16),
264 (byte)(inCounter >>> 8),
265 (byte) inCounter });
267 final byte[] computed_mac = inMac.doFinal();
268 if (Configuration.DEBUG)
269 log.fine("Computed MAC: " + Util.dumpString(computed_mac));
270 if (! Arrays.equals(received_mac, computed_mac))
271 throw new IntegrityException("engineUnwrap()");
272 // deal with the payload, which can be either plain or encrypted
273 if (inCipher != null)
274 result = inCipher.doFinal(incoming, offset, payloadLength);
275 else
277 result = new byte[len - macBytesCount];
278 System.arraycopy(incoming, offset, result, 0, result.length);
281 else // no integrity protection; just confidentiality
282 result = inCipher.doFinal(incoming, offset, len);
284 catch (IOException x)
286 if (x instanceof SaslException)
287 throw (SaslException) x;
288 throw new SaslException("engineUnwrap()", x);
290 if (Configuration.DEBUG)
291 log.exiting(this.getClass().getName(), "engineUnwrap");
292 return result;
295 protected byte[] engineWrap(final byte[] outgoing, final int offset,
296 final int len) throws SaslException
298 if (Configuration.DEBUG)
299 log.entering(this.getClass().getName(), "engineWrap");
300 if (outMac == null && outCipher == null)
301 throw new IllegalStateException("connection is not protected");
302 // at this point one, or both, of confidentiality and integrity protection
303 // services are active.
304 byte[] result;
307 final ByteArrayOutputStream out = new ByteArrayOutputStream();
308 // Process the data
309 if (outCipher != null)
311 result = outCipher.doFinal(outgoing, offset, len);
312 if (Configuration.DEBUG)
313 log.fine("Encoding c (encrypted plaintext): "
314 + Util.dumpString(result));
315 out.write(result);
316 if (outMac != null)
318 outMac.update(result);
319 if (replayDetection)
321 outCounter++;
322 if (Configuration.DEBUG)
323 log.fine("outCounter=" + outCounter);
324 outMac.update(new byte[] {
325 (byte)(outCounter >>> 24),
326 (byte)(outCounter >>> 16),
327 (byte)(outCounter >>> 8),
328 (byte) outCounter });
330 final byte[] C = outMac.doFinal();
331 out.write(C);
332 if (Configuration.DEBUG)
333 log.fine("Encoding C (integrity checksum): " + Util.dumpString(C));
335 // else confidentiality only; do nothing
337 else // no confidentiality; just integrity [+ replay detection]
339 if (Configuration.DEBUG)
340 log.fine("Encoding p (plaintext): "
341 + Util.dumpString(outgoing, offset, len));
342 out.write(outgoing, offset, len);
343 outMac.update(outgoing, offset, len);
344 if (replayDetection)
346 outCounter++;
347 if (Configuration.DEBUG)
348 log.fine("outCounter=" + outCounter);
349 outMac.update(new byte[] {
350 (byte)(outCounter >>> 24),
351 (byte)(outCounter >>> 16),
352 (byte)(outCounter >>> 8),
353 (byte) outCounter });
355 final byte[] C = outMac.doFinal();
356 out.write(C);
357 if (Configuration.DEBUG)
358 log.fine("Encoding C (integrity checksum): " + Util.dumpString(C));
360 result = out.toByteArray();
362 catch (IOException x)
364 if (x instanceof SaslException)
365 throw (SaslException) x;
366 throw new SaslException("engineWrap()", x);
368 if (Configuration.DEBUG)
369 log.exiting(this.getClass().getName(), "engineWrap");
370 return result;
373 protected String getNegotiatedQOP()
375 if (inMac != null)
377 if (inCipher != null)
378 return Registry.QOP_AUTH_CONF;
379 return Registry.QOP_AUTH_INT;
381 return Registry.QOP_AUTH;
384 protected String getNegotiatedStrength()
386 if (inMac != null)
388 if (inCipher != null)
389 return Registry.STRENGTH_HIGH;
390 return Registry.STRENGTH_MEDIUM;
392 return Registry.STRENGTH_LOW;
395 protected String getNegotiatedRawSendSize()
397 return String.valueOf(rawSendSize);
400 protected String getReuse()
402 return Registry.REUSE_TRUE;
405 private byte[] sendIdentities() throws SaslException
407 if (Configuration.DEBUG)
408 log.entering(this.getClass().getName(), "sendIdentities");
409 // If necessary, prompt the client for the username and password
410 getUsernameAndPassword();
411 if (Configuration.DEBUG)
413 log.fine("Password: \"" + new String(password.getPassword()) + "\"");
414 log.fine("Encoding U (username): \"" + U + "\"");
415 log.fine("Encoding I (userid): \"" + authorizationID + "\"");
417 // if session re-use generate new 16-byte nonce
418 if (sid.length != 0)
420 cn = new byte[16];
421 getDefaultPRNG().nextBytes(cn);
423 else
424 cn = new byte[0];
425 final OutputBuffer frameOut = new OutputBuffer();
428 frameOut.setText(U);
429 frameOut.setText(authorizationID);
430 frameOut.setEOS(sid); // session ID to re-use
431 frameOut.setOS(cn); // client nonce
432 frameOut.setEOS(channelBinding);
434 catch (IOException x)
436 if (x instanceof SaslException)
437 throw (SaslException) x;
438 throw new AuthenticationException("sendIdentities()", x);
440 final byte[] result = frameOut.encode();
441 if (Configuration.DEBUG)
443 log.fine("C: " + Util.dumpString(result));
444 log.fine(" U = " + U);
445 log.fine(" I = " + authorizationID);
446 log.fine("sid = " + new String(sid));
447 log.fine(" cn = " + Util.dumpString(cn));
448 log.fine("cCB = " + Util.dumpString(channelBinding));
449 log.exiting(this.getClass().getName(), "sendIdentities");
451 return result;
454 private byte[] sendPublicKey(final byte[] input) throws SaslException
456 if (Configuration.DEBUG)
458 log.entering(this.getClass().getName(), "sendPublicKey");
459 log.fine("S: " + Util.dumpString(input));
461 // Server sends [00], N, g, s, B, L
462 // or [FF], sn, sCB
463 final InputBuffer frameIn = new InputBuffer(input);
464 final int ack;
467 ack = (int) frameIn.getScalar(1);
468 if (ack == 0x00) // new session
470 N = frameIn.getMPI();
471 if (Configuration.DEBUG)
472 log.fine("Got N (modulus): " + Util.dump(N));
473 g = frameIn.getMPI();
474 if (Configuration.DEBUG)
475 log.fine("Got g (generator): " + Util.dump(g));
476 s = frameIn.getOS();
477 if (Configuration.DEBUG)
478 log.fine("Got s (salt): " + Util.dumpString(s));
479 B = frameIn.getMPI();
480 if (Configuration.DEBUG)
481 log.fine("Got B (server ephermeral public key): " + Util.dump(B));
482 L = frameIn.getText();
483 if (Configuration.DEBUG)
484 log.fine("Got L (available options): \"" + L + "\"");
486 else if (ack == 0xFF) // session re-use
488 sn = frameIn.getOS();
489 if (Configuration.DEBUG)
490 log.fine("Got sn (server nonce): " + Util.dumpString(sn));
491 sCB = frameIn.getEOS();
492 if (Configuration.DEBUG)
493 log.fine("Got sCB (server channel binding): " + Util.dumpString(sCB));
495 else // unexpected scalar
496 throw new SaslException("sendPublicKey(): Invalid scalar (" + ack
497 + ") in server's request");
499 catch (IOException x)
501 if (x instanceof SaslException)
502 throw (SaslException) x;
503 throw new SaslException("sendPublicKey()", x);
505 if (ack == 0x00)
506 { // new session ---------------------------------------
507 o = createO(L.toLowerCase()); // do this first to initialise the SRP hash
508 final byte[] pBytes; // use ASCII encoding to inter-operate w/ non-java
509 pBytes = password.getBytes();
510 // ----------------------------------------------------------------------
511 final HashMap mapA = new HashMap();
512 mapA.put(SRP6KeyAgreement.HASH_FUNCTION, srp.getAlgorithm());
513 mapA.put(SRP6KeyAgreement.USER_IDENTITY, U);
514 mapA.put(SRP6KeyAgreement.USER_PASSWORD, pBytes);
517 clientHandler.init(mapA);
518 clientHandler.processMessage(null);
520 catch (KeyAgreementException x)
522 throw new SaslException("sendPublicKey()", x);
524 // -------------------------------------------------------------------
527 OutgoingMessage out = new OutgoingMessage();
528 out.writeMPI(N);
529 out.writeMPI(g);
530 out.writeMPI(new BigInteger(1, s));
531 out.writeMPI(B);
532 IncomingMessage in = new IncomingMessage(out.toByteArray());
533 out = clientHandler.processMessage(in);
534 in = new IncomingMessage(out.toByteArray());
535 A = in.readMPI();
536 K = clientHandler.getSharedSecret();
538 catch (KeyAgreementException x)
540 throw new SaslException("sendPublicKey()", x);
542 // -------------------------------------------------------------------
543 if (Configuration.DEBUG)
545 log.fine("K: " + Util.dumpString(K));
546 log.fine("Encoding A (client ephemeral public key): " + Util.dump(A));
550 M1 = srp.generateM1(N, g, U, s, A, B, K, authorizationID, L, cn,
551 channelBinding);
553 catch (UnsupportedEncodingException x)
555 throw new AuthenticationException("sendPublicKey()", x);
557 if (Configuration.DEBUG)
559 log.fine("Encoding o (client chosen options): \"" + o + "\"");
560 log.fine("Encoding cIV (client IV): \"" + Util.dumpString(cIV) + "\"");
562 final OutputBuffer frameOut = new OutputBuffer();
565 frameOut.setMPI(A);
566 frameOut.setOS(M1);
567 frameOut.setText(o);
568 frameOut.setOS(cIV);
570 catch (IOException x)
572 if (x instanceof SaslException)
573 throw (SaslException) x;
574 throw new AuthenticationException("sendPublicKey()", x);
576 final byte[] result = frameOut.encode();
577 if (Configuration.DEBUG)
579 log.fine("New session, or session re-use rejected...");
580 log.fine("C: " + Util.dumpString(result));
581 log.fine(" A = 0x" + A.toString(16));
582 log.fine(" M1 = " + Util.dumpString(M1));
583 log.fine(" o = " + o);
584 log.fine("cIV = " + Util.dumpString(cIV));
585 log.exiting(this.getClass().getName(), "sendPublicKey");
587 return result;
589 else // session re-use accepted -------------------------------------------
591 setupSecurityServices(true);
592 if (Configuration.DEBUG)
594 log.fine("Session re-use accepted...");
595 log.exiting(this.getClass().getName(), "sendPublicKey");
597 return null;
601 private byte[] receiveEvidence(byte[] input) throws SaslException
603 if (Configuration.DEBUG)
605 log.entering(this.getClass().getName(), "receiveEvidence");
606 log.fine("S: " + Util.dumpString(input));
608 // Server send M2, sIV, sCB, sid, ttl
609 final InputBuffer frameIn = new InputBuffer(input);
612 M2 = frameIn.getOS();
613 if (Configuration.DEBUG)
614 log.fine("Got M2 (server evidence): " + Util.dumpString(M2));
615 sIV = frameIn.getOS();
616 if (Configuration.DEBUG)
617 log.fine("Got sIV (server IV): " + Util.dumpString(sIV));
618 sid = frameIn.getEOS();
619 if (Configuration.DEBUG)
620 log.fine("Got sid (session ID): " + new String(sid));
621 ttl = (int) frameIn.getScalar(4);
622 if (Configuration.DEBUG)
623 log.fine("Got ttl (session time-to-live): " + ttl + "sec.");
624 sCB = frameIn.getEOS();
625 if (Configuration.DEBUG)
626 log.fine("Got sCB (server channel binding): " + Util.dumpString(sCB));
628 catch (IOException x)
630 if (x instanceof SaslException)
631 throw (SaslException) x;
632 throw new AuthenticationException("receiveEvidence()", x);
635 final byte[] expected;
638 expected = srp.generateM2(A, M1, K, U, authorizationID, o, sid, ttl,
639 cIV, sIV, sCB);
641 catch (UnsupportedEncodingException x)
643 throw new AuthenticationException("receiveEvidence()", x);
645 if (Configuration.DEBUG)
646 log.fine("Expected: " + Util.dumpString(expected));
647 if (! Arrays.equals(M2, expected))
648 throw new AuthenticationException("M2 mismatch");
649 setupSecurityServices(false);
650 if (Configuration.DEBUG)
651 log.exiting(this.getClass().getName(), "receiveEvidence");
652 return null;
655 private void getUsernameAndPassword() throws AuthenticationException
659 if ((! properties.containsKey(Registry.SASL_USERNAME))
660 && (! properties.containsKey(Registry.SASL_PASSWORD)))
662 final NameCallback nameCB;
663 final String defaultName = System.getProperty("user.name");
664 if (defaultName == null)
665 nameCB = new NameCallback("username: ");
666 else
667 nameCB = new NameCallback("username: ", defaultName);
668 final PasswordCallback pwdCB = new PasswordCallback("password: ",
669 false);
670 handler.handle(new Callback[] { nameCB, pwdCB });
671 U = nameCB.getName();
672 password = new Password(pwdCB.getPassword());
674 else
676 if (properties.containsKey(Registry.SASL_USERNAME))
677 this.U = (String) properties.get(Registry.SASL_USERNAME);
678 else
680 final NameCallback nameCB;
681 final String defaultName = System.getProperty("user.name");
682 if (defaultName == null)
683 nameCB = new NameCallback("username: ");
684 else
685 nameCB = new NameCallback("username: ", defaultName);
686 this.handler.handle(new Callback[] { nameCB });
687 this.U = nameCB.getName();
690 if (properties.containsKey(Registry.SASL_PASSWORD))
692 Object pw = properties.get(Registry.SASL_PASSWORD);
693 if (pw instanceof char[])
694 password = new Password((char[]) pw);
695 else if (pw instanceof Password)
696 password = (Password) pw;
697 else if (pw instanceof String)
698 password = new Password(((String) pw).toCharArray());
699 else
700 throw new IllegalArgumentException(pw.getClass().getName()
701 + "is not a valid password class");
703 else
705 final PasswordCallback pwdCB = new PasswordCallback("password: ",
706 false);
707 this.handler.handle(new Callback[] { pwdCB });
708 password = new Password(pwdCB.getPassword());
712 if (U == null)
713 throw new AuthenticationException("null username supplied");
714 if (password == null)
715 throw new AuthenticationException("null password supplied");
717 catch (UnsupportedCallbackException x)
719 throw new AuthenticationException("getUsernameAndPassword()", x);
721 catch (IOException x)
723 throw new AuthenticationException("getUsernameAndPassword()", x);
727 // We go through the list of available services and for each available one
728 // we decide whether or not we want it enabled, based on properties passed
729 // to us by the client.
730 private String createO(final String aol) throws AuthenticationException
732 if (Configuration.DEBUG)
733 log.entering(this.getClass().getName(), "createO", aol);
734 boolean replaydetectionAvailable = false;
735 boolean integrityAvailable = false;
736 boolean confidentialityAvailable = false;
737 String option, mandatory = SRPRegistry.DEFAULT_MANDATORY;
738 int i;
740 String mdName = SRPRegistry.SRP_DEFAULT_DIGEST_NAME;
741 final StringTokenizer st = new StringTokenizer(aol, ",");
742 while (st.hasMoreTokens())
744 option = st.nextToken();
745 if (option.startsWith(SRPRegistry.OPTION_SRP_DIGEST + "="))
747 option = option.substring(option.indexOf('=') + 1);
748 if (Configuration.DEBUG)
749 log.fine("mda: <" + option + ">");
750 for (i = 0; i < SRPRegistry.INTEGRITY_ALGORITHMS.length; i++)
751 if (SRPRegistry.SRP_ALGORITHMS[i].equals(option))
753 mdName = option;
754 break;
757 else if (option.equals(SRPRegistry.OPTION_REPLAY_DETECTION))
758 replaydetectionAvailable = true;
759 else if (option.startsWith(SRPRegistry.OPTION_INTEGRITY + "="))
761 option = option.substring(option.indexOf('=') + 1);
762 if (Configuration.DEBUG)
763 log.fine("ialg: <" + option + ">");
764 for (i = 0; i < SRPRegistry.INTEGRITY_ALGORITHMS.length; i++)
765 if (SRPRegistry.INTEGRITY_ALGORITHMS[i].equals(option))
767 chosenIntegrityAlgorithm = option;
768 integrityAvailable = true;
769 break;
772 else if (option.startsWith(SRPRegistry.OPTION_CONFIDENTIALITY + "="))
774 option = option.substring(option.indexOf('=') + 1);
775 if (Configuration.DEBUG)
776 log.fine("calg: <" + option + ">");
777 for (i = 0; i < SRPRegistry.CONFIDENTIALITY_ALGORITHMS.length; i++)
778 if (SRPRegistry.CONFIDENTIALITY_ALGORITHMS[i].equals(option))
780 chosenConfidentialityAlgorithm = option;
781 confidentialityAvailable = true;
782 break;
785 else if (option.startsWith(SRPRegistry.OPTION_MANDATORY + "="))
786 mandatory = option.substring(option.indexOf('=') + 1);
787 else if (option.startsWith(SRPRegistry.OPTION_MAX_BUFFER_SIZE + "="))
789 final String maxBufferSize = option.substring(option.indexOf('=') + 1);
792 rawSendSize = Integer.parseInt(maxBufferSize);
793 if (rawSendSize > Registry.SASL_BUFFER_MAX_LIMIT
794 || rawSendSize < 1)
795 throw new AuthenticationException(
796 "Illegal value for 'maxbuffersize' option");
798 catch (NumberFormatException x)
800 throw new AuthenticationException(
801 SRPRegistry.OPTION_MAX_BUFFER_SIZE + "=" + maxBufferSize, x);
805 String s;
806 Boolean flag;
807 s = (String) properties.get(SRPRegistry.SRP_REPLAY_DETECTION);
808 flag = Boolean.valueOf(s);
809 replayDetection = replaydetectionAvailable && flag.booleanValue();
810 s = (String) properties.get(SRPRegistry.SRP_INTEGRITY_PROTECTION);
811 flag = Boolean.valueOf(s);
812 boolean integrity = integrityAvailable && flag.booleanValue();
813 s = (String) properties.get(SRPRegistry.SRP_CONFIDENTIALITY);
814 flag = Boolean.valueOf(s);
815 boolean confidentiality = confidentialityAvailable && flag.booleanValue();
816 // make sure we do the right thing
817 if (SRPRegistry.OPTION_REPLAY_DETECTION.equals(mandatory))
819 replayDetection = true;
820 integrity = true;
822 else if (SRPRegistry.OPTION_INTEGRITY.equals(mandatory))
823 integrity = true;
824 else if (SRPRegistry.OPTION_CONFIDENTIALITY.equals(mandatory))
825 confidentiality = true;
827 if (replayDetection)
829 if (chosenIntegrityAlgorithm == null)
830 throw new AuthenticationException(
831 "Replay detection is required but no integrity protection "
832 + "algorithm was chosen");
834 if (integrity)
836 if (chosenIntegrityAlgorithm == null)
837 throw new AuthenticationException(
838 "Integrity protection is required but no algorithm was chosen");
840 if (confidentiality)
842 if (chosenConfidentialityAlgorithm == null)
843 throw new AuthenticationException(
844 "Confidentiality protection is required but no algorithm was chosen");
846 // 1. check if we'll be using confidentiality; if not set IV to 0-byte
847 if (chosenConfidentialityAlgorithm == null)
848 cIV = new byte[0];
849 else
851 // 2. get the block size of the cipher
852 final IBlockCipher cipher = CipherFactory.getInstance(chosenConfidentialityAlgorithm);
853 if (cipher == null)
854 throw new AuthenticationException("createO()",
855 new NoSuchAlgorithmException());
856 final int blockSize = cipher.defaultBlockSize();
857 // 3. generate random iv
858 cIV = new byte[blockSize];
859 getDefaultPRNG().nextBytes(cIV);
861 srp = SRP.instance(mdName);
862 // Now create the options list specifying which of the available options
863 // we have chosen.
865 // For now we just select the defaults. Later we need to add support for
866 // properties (perhaps in a file) where a user can specify the list of
867 // algorithms they would prefer to use.
868 final CPStringBuilder sb = new CPStringBuilder();
869 sb.append(SRPRegistry.OPTION_SRP_DIGEST)
870 .append("=").append(mdName).append(",");
871 if (replayDetection)
872 sb.append(SRPRegistry.OPTION_REPLAY_DETECTION).append(",");
873 if (integrity)
874 sb.append(SRPRegistry.OPTION_INTEGRITY)
875 .append("=").append(chosenIntegrityAlgorithm).append(",");
876 if (confidentiality)
877 sb.append(SRPRegistry.OPTION_CONFIDENTIALITY)
878 .append("=").append(chosenConfidentialityAlgorithm).append(",");
880 final String result = sb.append(SRPRegistry.OPTION_MAX_BUFFER_SIZE)
881 .append("=").append(Registry.SASL_BUFFER_MAX_LIMIT)
882 .toString();
883 if (Configuration.DEBUG)
884 log.exiting(this.getClass().getName(), "createO", result);
885 return result;
888 private void setupSecurityServices(final boolean sessionReUse)
889 throws SaslException
891 complete = true; // signal end of authentication phase
892 if (! sessionReUse)
894 outCounter = inCounter = 0;
895 // instantiate cipher if confidentiality protection filter is active
896 if (chosenConfidentialityAlgorithm != null)
898 if (Configuration.DEBUG)
899 log.fine("Activating confidentiality protection filter");
900 inCipher = CALG.getInstance(chosenConfidentialityAlgorithm);
901 outCipher = CALG.getInstance(chosenConfidentialityAlgorithm);
903 // instantiate hmacs if integrity protection filter is active
904 if (chosenIntegrityAlgorithm != null)
906 if (Configuration.DEBUG)
907 log.fine("Activating integrity protection filter");
908 inMac = IALG.getInstance(chosenIntegrityAlgorithm);
909 outMac = IALG.getInstance(chosenIntegrityAlgorithm);
912 else // same session new Keys
913 K = srp.generateKn(K, cn, sn);
915 final KDF kdf = KDF.getInstance(K);
916 // initialise in/out ciphers if confidentiality protection is used
917 if (inCipher != null)
919 inCipher.init(kdf, sIV, Direction.REVERSED);
920 outCipher.init(kdf, cIV, Direction.FORWARD);
922 // initialise in/out macs if integrity protection is used
923 if (inMac != null)
925 inMac.init(kdf);
926 outMac.init(kdf);
928 if (sid != null && sid.length != 0)
929 { // update the security context and save in map
930 if (Configuration.DEBUG)
931 log.fine("Updating security context for UID = " + uid);
932 ClientStore.instance().cacheSession(uid,
933 ttl,
934 new SecurityContext(srp.getAlgorithm(),
935 sid,
937 cIV,
938 sIV,
939 replayDetection,
940 inCounter,
941 outCounter,
942 inMac, outMac,
943 inCipher,
944 outCipher));
948 private PRNG getDefaultPRNG()
950 if (prng == null)
951 prng = PRNG.getInstance();
952 return prng;