Imported GNU Classpath 0.90
[official-gcc.git] / libjava / classpath / gnu / javax / crypto / sasl / srp / SRPClient.java
blob1a1664ff79d4aa3b1923bf5ab6087cb5a302cc63
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.security.Registry;
42 import gnu.java.security.hash.MD5;
43 import gnu.java.security.util.PRNG;
44 import gnu.java.security.util.Util;
46 import gnu.javax.crypto.key.IKeyAgreementParty;
47 import gnu.javax.crypto.key.IncomingMessage;
48 import gnu.javax.crypto.key.KeyAgreementFactory;
49 import gnu.javax.crypto.key.KeyAgreementException;
50 import gnu.javax.crypto.key.OutgoingMessage;
51 import gnu.javax.crypto.key.srp6.SRP6KeyAgreement;
52 import gnu.javax.crypto.assembly.Direction;
53 import gnu.javax.crypto.cipher.CipherFactory;
54 import gnu.javax.crypto.cipher.IBlockCipher;
55 import gnu.javax.crypto.sasl.ClientMechanism;
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;
61 import gnu.javax.security.auth.Password;
63 import java.io.IOException;
64 import java.io.PrintWriter;
65 import java.io.ByteArrayOutputStream;
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;
73 import javax.security.auth.callback.Callback;
74 import javax.security.auth.callback.NameCallback;
75 import javax.security.auth.callback.PasswordCallback;
76 import javax.security.auth.callback.UnsupportedCallbackException;
77 import javax.security.auth.DestroyFailedException;
78 import javax.security.sasl.AuthenticationException;
79 import javax.security.sasl.SaslClient;
80 import javax.security.sasl.SaslException;
82 /**
83 * <p>The SASL-SRP client-side mechanism.</p>
85 public class SRPClient extends ClientMechanism implements SaslClient
88 // Debugging methods and variables
89 // -------------------------------------------------------------------------
91 private static final String NAME = "SRPClient";
93 // private static final String ERROR = "ERROR";
94 // private static final String WARN = " WARN";
95 private static final String INFO = " INFO";
97 private static final String TRACE = "DEBUG";
99 private static final boolean DEBUG = true;
101 private static final int debuglevel = 3;
103 private static final PrintWriter err = new PrintWriter(System.out, true);
105 private static void debug(final String level, final Object obj)
107 err.println("[" + level + "] " + NAME + ": " + String.valueOf(obj));
110 // Constants and variables
111 // -------------------------------------------------------------------------
113 // private static final HashMap uid2ctx = new HashMap();
115 private String uid; // the unique key for this type of client
117 private String U; // the authentication identity
119 BigInteger N, g, A, B;
121 private Password password; // the authentication credentials
123 private byte[] s; // the user's salt
125 private byte[] cIV, sIV; // client+server IVs, when confidentiality is on
127 private byte[] M1, M2; // client+server evidences
129 private byte[] cn, sn; // client's and server's nonce
131 private SRP srp; // SRP algorithm instance used by this client
133 private byte[] sid; // session ID when re-used
135 private int ttl; // session time-to-live in seconds
137 private byte[] sCB; // the peer's channel binding data
139 private String L; // available options
141 private String o;
143 private String chosenIntegrityAlgorithm;
145 private String chosenConfidentialityAlgorithm;
147 private int rawSendSize = Registry.SASL_BUFFER_MAX_LIMIT;
149 private byte[] K; // shared session key
151 private boolean replayDetection = true; // whether Replay Detection is on
153 private int inCounter = 0; // messages sequence numbers
155 private int outCounter = 0;
157 private IALG inMac, outMac; // if !null, use for integrity
159 private CALG inCipher, outCipher; // if !null, use for confidentiality
161 private IKeyAgreementParty clientHandler = KeyAgreementFactory.getPartyAInstance(Registry.SRP_SASL_KA);
163 /** Our default source of randomness. */
164 private PRNG prng = null;
166 // Constructor(s)
167 // -------------------------------------------------------------------------
169 public SRPClient()
171 super(Registry.SASL_SRP_MECHANISM);
174 // Class methods
175 // -------------------------------------------------------------------------
177 // Instance methods
178 // -------------------------------------------------------------------------
180 // abstract methods implementation -----------------------------------------
182 protected void initMechanism() throws SaslException
184 // we shall keep track of the sid (and the security context of this
185 // SRP client) based on the initialisation parameters of an SRP session.
186 // we shall compute a unique key for those parameters and key the sid
187 // (and the security context) accordingly.
188 // 1. compute the mapping key. use MD5 (the fastest) for this purpose
189 final MD5 md = new MD5();
190 byte[] b;
191 b = authorizationID.getBytes();
192 md.update(b, 0, b.length);
193 b = serverName.getBytes();
194 md.update(b, 0, b.length);
195 b = protocol.getBytes();
196 md.update(b, 0, b.length);
197 if (channelBinding.length > 0)
199 md.update(channelBinding, 0, channelBinding.length);
201 uid = Util.toBase64(md.digest());
202 if (ClientStore.instance().isAlive(uid))
204 final SecurityContext ctx = ClientStore.instance().restoreSession(uid);
205 srp = SRP.instance(ctx.getMdName());
206 sid = ctx.getSID();
207 K = ctx.getK();
208 cIV = ctx.getClientIV();
209 sIV = ctx.getServerIV();
210 replayDetection = ctx.hasReplayDetection();
211 inCounter = ctx.getInCounter();
212 outCounter = ctx.getOutCounter();
213 inMac = ctx.getInMac();
214 outMac = ctx.getOutMac();
215 inCipher = ctx.getInCipher();
216 outCipher = ctx.getOutCipher();
218 else
220 sid = new byte[0];
221 ttl = 0;
222 K = null;
223 cIV = null;
224 sIV = null;
225 cn = null;
226 sn = null;
230 protected void resetMechanism() throws SaslException
234 password.destroy();
236 catch (DestroyFailedException dfe)
238 SaslException se = new SaslException("resetMechanism()");
239 se.initCause(dfe);
240 throw se;
242 password = null;
243 M1 = null;
244 K = null;
245 cIV = null;
246 sIV = null;
247 inMac = outMac = null;
248 inCipher = outCipher = null;
250 sid = null;
251 ttl = 0;
252 cn = null;
253 sn = null;
256 // javax.security.sasl.SaslClient interface implementation -----------------
258 public boolean hasInitialResponse()
260 return true;
263 public byte[] evaluateChallenge(final byte[] challenge) throws SaslException
265 switch (state)
267 case 0:
268 state++;
269 return sendIdentities();
270 case 1:
271 state++;
272 final byte[] result = sendPublicKey(challenge);
275 password.destroy(); //don't need further this session
277 catch (DestroyFailedException x)
279 SaslException se = new SaslException("sendPublicKey()");
280 se.initCause(se);
281 throw se;
283 return result;
284 case 2: // should only occur if session re-use was rejected
285 if (!complete)
287 state++;
288 return receiveEvidence(challenge);
290 // else fall through
291 default:
292 throw new IllegalMechanismStateException("evaluateChallenge()");
296 protected byte[] engineUnwrap(final byte[] incoming, final int offset,
297 final int len) throws SaslException
299 if (DEBUG && debuglevel > 8)
300 debug(TRACE, "==> engineUnwrap()");
302 if (inMac == null && inCipher == null)
304 throw new IllegalStateException("connection is not protected");
307 // at this point one, or both, of confidentiality and integrity protection
308 // services are active.
310 final byte[] result;
313 // final InputBuffer frameIn = InputBuffer.getInstance(incoming, offset, len);
314 // result = frameIn.getEOS();
315 if (inMac != null)
316 { // integrity bytes are at the end of the stream
317 final int macBytesCount = inMac.length();
318 final int payloadLength = len - macBytesCount;
319 // final byte[] received_mac = frameIn.getOS();
320 final byte[] received_mac = new byte[macBytesCount];
321 System.arraycopy(incoming, offset + payloadLength, received_mac, 0,
322 macBytesCount);
323 if (DEBUG && debuglevel > 6)
324 debug(TRACE, "Got C (received MAC): "
325 + Util.dumpString(received_mac));
326 // inMac.update(result);
327 inMac.update(incoming, offset, payloadLength);
328 if (replayDetection)
330 inCounter++;
331 if (DEBUG && debuglevel > 6)
332 debug(TRACE, "inCounter=" + String.valueOf(inCounter));
333 inMac.update(new byte[] { (byte) (inCounter >>> 24),
334 (byte) (inCounter >>> 16),
335 (byte) (inCounter >>> 8),
336 (byte) inCounter });
339 final byte[] computed_mac = inMac.doFinal();
340 if (DEBUG && debuglevel > 6)
341 debug(TRACE, "Computed MAC: " + Util.dumpString(computed_mac));
342 if (!Arrays.equals(received_mac, computed_mac))
344 throw new IntegrityException("engineUnwrap()");
347 // deal with the payload, which can be either plain or encrypted
348 if (inCipher != null)
350 result = inCipher.doFinal(incoming, offset, payloadLength);
352 else
354 result = new byte[len - macBytesCount];
355 System.arraycopy(incoming, offset, result, 0, result.length);
358 else
359 { // no integrity protection; just confidentiality
360 // if (inCipher != null) {
361 result = inCipher.doFinal(incoming, offset, len);
362 // } else {
363 // result = new byte[len];
364 // System.arraycopy(incoming, offset, result, 0, len);
365 // }
367 // if (inCipher != null) {
368 // result = inCipher.doFinal(result);
369 // }
371 catch (IOException x)
373 if (x instanceof SaslException)
375 throw (SaslException) x;
377 throw new SaslException("engineUnwrap()", x);
380 if (DEBUG && debuglevel > 8)
381 debug(TRACE, "<== engineUnwrap()");
382 return result;
385 protected byte[] engineWrap(final byte[] outgoing, final int offset,
386 final int len) throws SaslException
388 if (DEBUG && debuglevel > 8)
389 debug(TRACE, "==> engineWrap()");
391 if (outMac == null && outCipher == null)
393 throw new IllegalStateException("connection is not protected");
396 // at this point one, or both, of confidentiality and integrity protection
397 // services are active.
399 // byte[] data = new byte[len];
400 // System.arraycopy(outgoing, offset, data, 0, len);
401 byte[] result;
404 // OutputBuffer frameOut = new OutputBuffer();
405 final ByteArrayOutputStream out = new ByteArrayOutputStream();
406 // Process the data
407 if (outCipher != null)
409 // data = outCipher.doFinal(data);
410 result = outCipher.doFinal(outgoing, offset, len);
411 if (DEBUG && debuglevel > 6)
412 debug(TRACE, "Encoding c (encrypted plaintext): "
413 + Util.dumpString(result));
415 // frameOut.setEOS(data);
416 out.write(result);
418 if (outMac != null)
420 outMac.update(result);
421 if (replayDetection)
423 outCounter++;
424 if (DEBUG && debuglevel > 6)
425 debug(TRACE, "outCounter=" + String.valueOf(outCounter));
426 outMac.update(new byte[] { (byte) (outCounter >>> 24),
427 (byte) (outCounter >>> 16),
428 (byte) (outCounter >>> 8),
429 (byte) outCounter });
431 final byte[] C = outMac.doFinal();
432 // frameOut.setOS(C);
433 out.write(C);
434 if (DEBUG && debuglevel > 6)
435 debug(TRACE, "Encoding C (integrity checksum): "
436 + Util.dumpString(C));
437 } // else confidentiality only; do nothing
439 else
440 { // no confidentiality; just integrity [+ replay detection]
441 // if (DEBUG && debuglevel > 6) debug(TRACE, "Encoding p (plaintext): "+Util.dumpString(data));
442 if (DEBUG && debuglevel > 6)
443 debug(TRACE, "Encoding p (plaintext): "
444 + Util.dumpString(outgoing, offset, len));
446 // frameOut.setEOS(data);
447 out.write(outgoing, offset, len);
449 // if (outMac != null) {
450 // outMac.update(data);
451 outMac.update(outgoing, offset, len);
452 if (replayDetection)
454 outCounter++;
455 if (DEBUG && debuglevel > 6)
456 debug(TRACE, "outCounter=" + String.valueOf(outCounter));
457 outMac.update(new byte[] { (byte) (outCounter >>> 24),
458 (byte) (outCounter >>> 16),
459 (byte) (outCounter >>> 8),
460 (byte) outCounter });
462 final byte[] C = outMac.doFinal();
463 // frameOut.setOS(C);
464 out.write(C);
465 if (DEBUG && debuglevel > 6)
466 debug(TRACE, "Encoding C (integrity checksum): "
467 + Util.dumpString(C));
468 // }
471 // frameOut.setEOS(data);
473 // if (outMac != null) {
474 // outMac.update(data);
475 // if (replayDetection) {
476 // outCounter++;
477 // if (DEBUG && debuglevel > 6) debug(TRACE, "outCounter="+String.valueOf(outCounter));
478 // outMac.update(new byte[] {
479 // (byte)(outCounter >>> 24),
480 // (byte)(outCounter >>> 16),
481 // (byte)(outCounter >>> 8),
482 // (byte) outCounter });
483 // }
484 // byte[] C = outMac.doFinal();
485 // frameOut.setOS(C);
486 // if (DEBUG && debuglevel > 6) debug(TRACE, "Encoding C (integrity checksum): "+Util.dumpString(C));
487 // }
489 // result = frameOut.wrap();
490 result = out.toByteArray();
493 catch (IOException x)
495 if (x instanceof SaslException)
497 throw (SaslException) x;
499 throw new SaslException("engineWrap()", x);
502 if (DEBUG && debuglevel > 8)
503 debug(TRACE, "<== engineWrap()");
504 return result;
507 protected String getNegotiatedQOP()
509 if (inMac != null)
511 if (inCipher != null)
513 return Registry.QOP_AUTH_CONF;
515 else
517 return Registry.QOP_AUTH_INT;
520 return Registry.QOP_AUTH;
523 protected String getNegotiatedStrength()
525 if (inMac != null)
527 if (inCipher != null)
529 return Registry.STRENGTH_HIGH;
531 else
533 return Registry.STRENGTH_MEDIUM;
536 return Registry.STRENGTH_LOW;
539 protected String getNegotiatedRawSendSize()
541 return String.valueOf(rawSendSize);
544 protected String getReuse()
546 return Registry.REUSE_TRUE;
549 // other methods -----------------------------------------------------------
551 private byte[] sendIdentities() throws SaslException
553 if (DEBUG && debuglevel > 8)
554 debug(TRACE, "==> sendIdentities()");
556 // If necessary, prompt the client for the username and password
557 getUsernameAndPassword();
559 if (DEBUG && debuglevel > 6)
560 debug(TRACE, "Password: \"" + new String(password.getPassword()) + "\"");
561 if (DEBUG && debuglevel > 6)
562 debug(TRACE, "Encoding U (username): \"" + U + "\"");
563 if (DEBUG && debuglevel > 6)
564 debug(TRACE, "Encoding I (userid): \"" + authorizationID + "\"");
566 // if session re-use generate new 16-byte nonce
567 if (sid.length != 0)
569 cn = new byte[16];
570 getDefaultPRNG().nextBytes(cn);
572 else
574 cn = new byte[0];
577 final OutputBuffer frameOut = new OutputBuffer();
580 frameOut.setText(U);
581 frameOut.setText(authorizationID);
582 frameOut.setEOS(sid); // session ID to re-use
583 frameOut.setOS(cn); // client nonce
584 frameOut.setEOS(channelBinding);
586 catch (IOException x)
588 if (x instanceof SaslException)
590 throw (SaslException) x;
592 throw new AuthenticationException("sendIdentities()", x);
594 final byte[] result = frameOut.encode();
595 if (DEBUG && debuglevel > 8)
596 debug(TRACE, "<== sendIdentities()");
597 if (DEBUG && debuglevel > 2)
598 debug(INFO, "C: " + Util.dumpString(result));
599 if (DEBUG && debuglevel > 2)
600 debug(INFO, " U = " + U);
601 if (DEBUG && debuglevel > 2)
602 debug(INFO, " I = " + authorizationID);
603 if (DEBUG && debuglevel > 2)
604 debug(INFO, "sid = " + new String(sid));
605 if (DEBUG && debuglevel > 2)
606 debug(INFO, " cn = " + Util.dumpString(cn));
607 if (DEBUG && debuglevel > 2)
608 debug(INFO, "cCB = " + Util.dumpString(channelBinding));
609 return result;
612 private byte[] sendPublicKey(final byte[] input) throws SaslException
614 if (DEBUG && debuglevel > 8)
615 debug(TRACE, "==> sendPublicKey()");
616 if (DEBUG && debuglevel > 6)
617 debug(TRACE, "S: " + Util.dumpString(input));
619 // Server sends [00], N, g, s, B, L
620 // or [FF], sn, sCB
621 final InputBuffer frameIn = new InputBuffer(input);
622 final int ack;
625 ack = (int) frameIn.getScalar(1);
626 if (ack == 0x00)
627 { // new session
628 N = frameIn.getMPI();
629 if (DEBUG && debuglevel > 6)
630 debug(TRACE, "Got N (modulus): " + Util.dump(N));
631 g = frameIn.getMPI();
632 if (DEBUG && debuglevel > 6)
633 debug(TRACE, "Got g (generator): " + Util.dump(g));
634 s = frameIn.getOS();
635 if (DEBUG && debuglevel > 6)
636 debug(TRACE, "Got s (salt): " + Util.dumpString(s));
637 B = frameIn.getMPI();
638 if (DEBUG && debuglevel > 6)
639 debug(TRACE, "Got B (server ephermeral public key): "
640 + Util.dump(B));
641 L = frameIn.getText();
642 if (DEBUG && debuglevel > 6)
643 debug(TRACE, "Got L (available options): \"" + L + "\"");
645 else if (ack == 0xFF)
646 { // session re-use
647 sn = frameIn.getOS();
648 if (DEBUG && debuglevel > 6)
649 debug(TRACE, "Got sn (server nonce): " + Util.dumpString(sn));
650 sCB = frameIn.getEOS();
651 if (DEBUG && debuglevel > 6)
652 debug(TRACE, "Got sCB (server channel binding): "
653 + Util.dumpString(sCB));
655 else
656 { // unexpected scalar
657 throw new SaslException("sendPublicKey(): Invalid scalar (" + ack
658 + ") in server's request");
661 catch (IOException x)
663 if (x instanceof SaslException)
665 throw (SaslException) x;
667 throw new SaslException("sendPublicKey()", x);
670 if (ack == 0x00)
671 { // new session ---------------------------------------
672 o = createO(L.toLowerCase()); // do this first to initialise the SRP hash
674 final byte[] pBytes; // use ASCII encoding to inter-operate w/ non-java
675 pBytes = password.getBytes();
677 // ----------------------------------------------------------------------
678 final HashMap mapA = new HashMap();
679 // mapA.put(SRP6KeyAgreement.HASH_FUNCTION, srp.newDigest());
680 mapA.put(SRP6KeyAgreement.HASH_FUNCTION, srp.getAlgorithm());
681 mapA.put(SRP6KeyAgreement.USER_IDENTITY, U);
682 mapA.put(SRP6KeyAgreement.USER_PASSWORD, pBytes);
685 clientHandler.init(mapA);
686 clientHandler.processMessage(null);
688 catch (KeyAgreementException x)
690 throw new SaslException("sendPublicKey()", x);
693 // ----------------------------------------------------------------------
695 // -------------------------------------------------------------------
698 OutgoingMessage out = new OutgoingMessage();
699 out.writeMPI(N);
700 out.writeMPI(g);
701 out.writeMPI(new BigInteger(1, s));
702 out.writeMPI(B);
703 IncomingMessage in = new IncomingMessage(out.toByteArray());
704 out = clientHandler.processMessage(in);
706 in = new IncomingMessage(out.toByteArray());
707 A = in.readMPI();
708 K = clientHandler.getSharedSecret();
710 catch (KeyAgreementException x)
712 throw new SaslException("sendPublicKey()", x);
714 // -------------------------------------------------------------------
716 if (DEBUG && debuglevel > 6)
717 debug(TRACE, "K: " + Util.dumpString(K));
718 if (DEBUG && debuglevel > 6)
719 debug(TRACE, "Encoding A (client ephemeral public key): "
720 + Util.dump(A));
724 M1 = srp.generateM1(N, g, U, s, A, B, K, authorizationID, L, cn,
725 channelBinding);
727 catch (UnsupportedEncodingException x)
729 throw new AuthenticationException("sendPublicKey()", x);
732 if (DEBUG && debuglevel > 6)
733 debug(TRACE, "Encoding o (client chosen options): \"" + o + "\"");
734 if (DEBUG && debuglevel > 6)
735 debug(TRACE, "Encoding cIV (client IV): \"" + Util.dumpString(cIV)
736 + "\"");
738 final OutputBuffer frameOut = new OutputBuffer();
741 frameOut.setMPI(A);
742 frameOut.setOS(M1);
743 frameOut.setText(o);
744 frameOut.setOS(cIV);
746 catch (IOException x)
748 if (x instanceof SaslException)
750 throw (SaslException) x;
752 throw new AuthenticationException("sendPublicKey()", x);
754 final byte[] result = frameOut.encode();
755 if (DEBUG && debuglevel > 8)
756 debug(TRACE, "<== sendPublicKey()");
757 if (DEBUG && debuglevel > 2)
758 debug(INFO, "New session, or session re-use rejected...");
759 if (DEBUG && debuglevel > 2)
760 debug(INFO, "C: " + Util.dumpString(result));
761 if (DEBUG && debuglevel > 2)
762 debug(INFO, " A = 0x" + A.toString(16));
763 if (DEBUG && debuglevel > 2)
764 debug(INFO, " M1 = " + Util.dumpString(M1));
765 if (DEBUG && debuglevel > 2)
766 debug(INFO, " o = " + o);
767 if (DEBUG && debuglevel > 2)
768 debug(INFO, "cIV = " + Util.dumpString(cIV));
770 return result;
772 else
773 { // session re-use accepted -------------------------------------
774 setupSecurityServices(true);
775 if (DEBUG && debuglevel > 8)
776 debug(TRACE, "<== sendPublicKey()");
777 if (DEBUG && debuglevel > 2)
778 debug(INFO, "Session re-use accepted...");
779 return null;
783 private byte[] receiveEvidence(byte[] input) throws SaslException
785 if (DEBUG && debuglevel > 8)
786 debug(TRACE, "==> receiveEvidence()");
787 if (DEBUG && debuglevel > 6)
788 debug(TRACE, "S: " + Util.dumpString(input));
790 // Server send M2, sIV, sCB, sid, ttl
791 final InputBuffer frameIn = new InputBuffer(input);
794 M2 = frameIn.getOS();
795 if (DEBUG && debuglevel > 6)
796 debug(TRACE, "Got M2 (server evidence): " + Util.dumpString(M2));
797 sIV = frameIn.getOS();
798 if (DEBUG && debuglevel > 6)
799 debug(TRACE, "Got sIV (server IV): " + Util.dumpString(sIV));
800 sid = frameIn.getEOS();
801 if (DEBUG && debuglevel > 6)
802 debug(TRACE, "Got sid (session ID): " + new String(sid));
803 ttl = (int) frameIn.getScalar(4);
804 if (DEBUG && debuglevel > 6)
805 debug(TRACE, "Got ttl (session time-to-live): " + ttl + "sec.");
806 sCB = frameIn.getEOS();
807 if (DEBUG && debuglevel > 6)
808 debug(TRACE, "Got sCB (server channel binding): "
809 + Util.dumpString(sCB));
811 catch (IOException x)
813 if (x instanceof SaslException)
815 throw (SaslException) x;
817 throw new AuthenticationException("receiveEvidence()", x);
820 final byte[] expected;
823 expected = srp.generateM2(A, M1, K, U, authorizationID, o, sid, ttl,
824 cIV, sIV, sCB);
826 catch (UnsupportedEncodingException x)
828 throw new AuthenticationException("receiveEvidence()", x);
831 if (DEBUG && debuglevel > 6)
832 debug(TRACE, "Expected: " + Util.dumpString(expected));
833 if (!Arrays.equals(M2, expected))
835 throw new AuthenticationException("M2 mismatch");
838 setupSecurityServices(false);
840 if (DEBUG && debuglevel > 8)
841 debug(TRACE, "<== receiveEvidence()");
842 return null;
845 private void getUsernameAndPassword() throws AuthenticationException
849 if ((!properties.containsKey(Registry.SASL_USERNAME))
850 && (!properties.containsKey(Registry.SASL_PASSWORD)))
852 final NameCallback nameCB;
853 final String defaultName = System.getProperty("user.name");
854 if (defaultName == null)
856 nameCB = new NameCallback("username: ");
858 else
860 nameCB = new NameCallback("username: ", defaultName);
862 final PasswordCallback pwdCB = new PasswordCallback("password: ",
863 false);
864 handler.handle(new Callback[] { nameCB, pwdCB });
865 U = nameCB.getName();
866 password = new Password(pwdCB.getPassword());
868 else
870 if (properties.containsKey(Registry.SASL_USERNAME))
872 this.U = (String) properties.get(Registry.SASL_USERNAME);
874 else
876 final NameCallback nameCB;
877 final String defaultName = System.getProperty("user.name");
878 if (defaultName == null)
880 nameCB = new NameCallback("username: ");
882 else
884 nameCB = new NameCallback("username: ", defaultName);
886 this.handler.handle(new Callback[] { nameCB });
887 this.U = nameCB.getName();
890 if (properties.containsKey(Registry.SASL_PASSWORD))
892 Object pw = properties.get(Registry.SASL_PASSWORD);
893 if (pw instanceof char[])
894 password = new Password((char[]) pw);
895 else if (pw instanceof Password)
896 password = (Password) pw;
897 else if (pw instanceof String)
898 password = new Password(((String) pw).toCharArray());
899 else
900 throw new IllegalArgumentException(
901 pw.getClass().getName()
902 + "is not a valid password class");
904 else
906 final PasswordCallback pwdCB = new PasswordCallback(
907 "password: ",
908 false);
909 this.handler.handle(new Callback[] { pwdCB });
910 password = new Password(pwdCB.getPassword());
914 if (U == null)
916 throw new AuthenticationException("null username supplied");
918 if (password == null)
920 throw new AuthenticationException("null password supplied");
923 catch (UnsupportedCallbackException x)
925 throw new AuthenticationException("getUsernameAndPassword()", x);
927 catch (IOException x)
929 throw new AuthenticationException("getUsernameAndPassword()", x);
933 // We go through the list of available services and for each available one
934 // we decide whether or not we want it enabled, based on properties passed
935 // to us by the client.
936 private String createO(final String aol) throws AuthenticationException
938 if (DEBUG && debuglevel > 8)
939 debug(TRACE, "==> createO(\"" + aol + "\")");
941 boolean replaydetectionAvailable = false;
942 boolean integrityAvailable = false;
943 boolean confidentialityAvailable = false;
944 String option, mandatory = SRPRegistry.DEFAULT_MANDATORY;
945 int i;
947 String mdName = SRPRegistry.SRP_DEFAULT_DIGEST_NAME;
949 final StringTokenizer st = new StringTokenizer(aol, ",");
950 while (st.hasMoreTokens())
952 option = st.nextToken();
953 if (option.startsWith(SRPRegistry.OPTION_SRP_DIGEST + "="))
955 option = option.substring(option.indexOf('=') + 1);
956 if (DEBUG && debuglevel > 6)
957 debug(TRACE, "mda: <" + option + ">");
958 for (i = 0; i < SRPRegistry.INTEGRITY_ALGORITHMS.length; i++)
960 if (SRPRegistry.SRP_ALGORITHMS[i].equals(option))
962 mdName = option;
963 break;
967 else if (option.equals(SRPRegistry.OPTION_REPLAY_DETECTION))
969 replaydetectionAvailable = true;
971 else if (option.startsWith(SRPRegistry.OPTION_INTEGRITY + "="))
973 option = option.substring(option.indexOf('=') + 1);
974 if (DEBUG && debuglevel > 6)
975 debug(TRACE, "ialg: <" + option + ">");
976 for (i = 0; i < SRPRegistry.INTEGRITY_ALGORITHMS.length; i++)
978 if (SRPRegistry.INTEGRITY_ALGORITHMS[i].equals(option))
980 chosenIntegrityAlgorithm = option;
981 integrityAvailable = true;
982 break;
986 else if (option.startsWith(SRPRegistry.OPTION_CONFIDENTIALITY + "="))
988 option = option.substring(option.indexOf('=') + 1);
989 if (DEBUG && debuglevel > 6)
990 debug(TRACE, "calg: <" + option + ">");
991 for (i = 0; i < SRPRegistry.CONFIDENTIALITY_ALGORITHMS.length; i++)
993 if (SRPRegistry.CONFIDENTIALITY_ALGORITHMS[i].equals(option))
995 chosenConfidentialityAlgorithm = option;
996 confidentialityAvailable = true;
997 break;
1001 else if (option.startsWith(SRPRegistry.OPTION_MANDATORY + "="))
1003 mandatory = option.substring(option.indexOf('=') + 1);
1005 else if (option.startsWith(SRPRegistry.OPTION_MAX_BUFFER_SIZE + "="))
1007 final String maxBufferSize = option.substring(option.indexOf('=') + 1);
1010 rawSendSize = Integer.parseInt(maxBufferSize);
1011 if (rawSendSize > Registry.SASL_BUFFER_MAX_LIMIT
1012 || rawSendSize < 1)
1014 throw new AuthenticationException(
1015 "Illegal value for 'maxbuffersize' option");
1018 catch (NumberFormatException x)
1020 throw new AuthenticationException(
1021 SRPRegistry.OPTION_MAX_BUFFER_SIZE
1022 + "="
1023 + String.valueOf(maxBufferSize),
1029 replayDetection = replaydetectionAvailable
1030 && Boolean.valueOf(
1031 (String) properties.get(SRPRegistry.SRP_REPLAY_DETECTION)).booleanValue();
1032 boolean integrity = integrityAvailable
1033 && Boolean.valueOf(
1034 (String) properties.get(SRPRegistry.SRP_INTEGRITY_PROTECTION)).booleanValue();
1035 boolean confidentiality = confidentialityAvailable
1036 && Boolean.valueOf(
1037 (String) properties.get(SRPRegistry.SRP_CONFIDENTIALITY)).booleanValue();
1039 // make sure we do the right thing
1040 if (SRPRegistry.OPTION_REPLAY_DETECTION.equals(mandatory))
1042 replayDetection = true;
1043 integrity = true;
1045 else if (SRPRegistry.OPTION_INTEGRITY.equals(mandatory))
1047 integrity = true;
1049 else if (SRPRegistry.OPTION_CONFIDENTIALITY.equals(mandatory))
1051 confidentiality = true;
1053 if (replayDetection)
1055 if (chosenIntegrityAlgorithm == null)
1057 throw new AuthenticationException(
1058 "Replay detection is required but no "
1059 + "integrity protection algorithm was chosen");
1062 if (integrity)
1064 if (chosenIntegrityAlgorithm == null)
1066 throw new AuthenticationException(
1067 "Integrity protection is required but no "
1068 + "algorithm was chosen");
1071 if (confidentiality)
1073 if (chosenConfidentialityAlgorithm == null)
1075 throw new AuthenticationException(
1076 "Confidentiality protection is required "
1077 + "but no algorithm was chosen");
1081 // 1. check if we'll be using confidentiality; if not set IV to 0-byte
1082 if (chosenConfidentialityAlgorithm == null)
1084 cIV = new byte[0];
1086 else
1088 // 2. get the block size of the cipher
1089 final IBlockCipher cipher = CipherFactory.getInstance(chosenConfidentialityAlgorithm);
1090 if (cipher == null)
1092 throw new AuthenticationException("createO()",
1093 new NoSuchAlgorithmException());
1095 final int blockSize = cipher.defaultBlockSize();
1096 // 3. generate random iv
1097 cIV = new byte[blockSize];
1098 getDefaultPRNG().nextBytes(cIV);
1101 srp = SRP.instance(mdName);
1103 // Now create the options list specifying which of the available options
1104 // we have chosen.
1106 // For now we just select the defaults. Later we need to add support for
1107 // properties (perhaps in a file) where a user can specify the list of
1108 // algorithms they would prefer to use.
1110 final StringBuffer sb = new StringBuffer();
1111 sb.append(SRPRegistry.OPTION_SRP_DIGEST).append("=").append(mdName).append(
1112 ",");
1113 if (replayDetection)
1115 sb.append(SRPRegistry.OPTION_REPLAY_DETECTION).append(",");
1117 if (integrity)
1119 sb.append(SRPRegistry.OPTION_INTEGRITY).append("=").append(
1120 chosenIntegrityAlgorithm).append(
1121 ",");
1123 if (confidentiality)
1125 sb.append(SRPRegistry.OPTION_CONFIDENTIALITY).append("=").append(
1126 chosenConfidentialityAlgorithm).append(
1127 ",");
1129 final String result = sb.append(SRPRegistry.OPTION_MAX_BUFFER_SIZE).append(
1130 "=").append(
1131 Registry.SASL_BUFFER_MAX_LIMIT).toString();
1133 if (DEBUG && debuglevel > 8)
1134 debug(TRACE, "<== createO() --> " + result);
1135 return result;
1138 private void setupSecurityServices(final boolean sessionReUse)
1139 throws SaslException
1141 complete = true; // signal end of authentication phase
1142 if (!sessionReUse)
1144 outCounter = inCounter = 0;
1145 // instantiate cipher if confidentiality protection filter is active
1146 if (chosenConfidentialityAlgorithm != null)
1148 if (DEBUG && debuglevel > 2)
1149 debug(INFO, "Activating confidentiality protection filter");
1150 inCipher = CALG.getInstance(chosenConfidentialityAlgorithm);
1151 outCipher = CALG.getInstance(chosenConfidentialityAlgorithm);
1153 // instantiate hmacs if integrity protection filter is active
1154 if (chosenIntegrityAlgorithm != null)
1156 if (DEBUG && debuglevel > 2)
1157 debug(INFO, "Activating integrity protection filter");
1158 inMac = IALG.getInstance(chosenIntegrityAlgorithm);
1159 outMac = IALG.getInstance(chosenIntegrityAlgorithm);
1162 else
1163 { // same session new Keys
1164 K = srp.generateKn(K, cn, sn);
1167 final KDF kdf = KDF.getInstance(K);
1169 // initialise in/out ciphers if confidentiality protection is used
1170 if (inCipher != null)
1172 inCipher.init(kdf, sIV, Direction.REVERSED);
1173 outCipher.init(kdf, cIV, Direction.FORWARD);
1175 // initialise in/out macs if integrity protection is used
1176 if (inMac != null)
1178 inMac.init(kdf);
1179 outMac.init(kdf);
1182 if (sid != null && sid.length != 0)
1183 { // update the security context and save in map
1184 if (DEBUG && debuglevel > 2)
1185 debug(INFO, "Updating security context for UID = " + uid);
1186 ClientStore.instance().cacheSession(
1187 uid,
1188 ttl,
1189 new SecurityContext(
1190 srp.getAlgorithm(),
1191 sid,
1193 cIV,
1194 sIV,
1195 replayDetection,
1196 inCounter,
1197 outCounter,
1198 inMac, outMac,
1199 inCipher,
1200 outCipher));
1204 private PRNG getDefaultPRNG()
1206 if (prng == null)
1207 prng = PRNG.getInstance();
1209 return prng;