re PR libgcj/37636 (java tools are unable to find resource files)
[gcc.git] / libjava / classpath / gnu / javax / crypto / sasl / srp / SRPClient.java
1 /* SRPClient.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.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;
63
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;
73
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;
82
83 /**
84 * The SASL-SRP client-side mechanism.
85 */
86 public class SRPClient
87 extends ClientMechanism
88 implements SaslClient
89 {
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;
118
119 public SRPClient()
120 {
121 super(Registry.SASL_SRP_MECHANISM);
122 }
123
124 protected void initMechanism() throws SaslException
125 {
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);
141
142 uid = Util.toBase64(md.digest());
143 if (ClientStore.instance().isAlive(uid))
144 {
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();
158 }
159 else
160 {
161 sid = new byte[0];
162 ttl = 0;
163 K = null;
164 cIV = null;
165 sIV = null;
166 cn = null;
167 sn = null;
168 }
169 }
170
171 protected void resetMechanism() throws SaslException
172 {
173 try
174 {
175 password.destroy();
176 }
177 catch (DestroyFailedException dfe)
178 {
179 SaslException se = new SaslException("resetMechanism()");
180 se.initCause(dfe);
181 throw se;
182 }
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;
194 }
195
196 public boolean hasInitialResponse()
197 {
198 return true;
199 }
200
201 public byte[] evaluateChallenge(final byte[] challenge) throws SaslException
202 {
203 switch (state)
204 {
205 case 0:
206 state++;
207 return sendIdentities();
208 case 1:
209 state++;
210 final byte[] result = sendPublicKey(challenge);
211 try
212 {
213 password.destroy(); //don't need further this session
214 }
215 catch (DestroyFailedException x)
216 {
217 SaslException se = new SaslException("sendPublicKey()");
218 se.initCause(se);
219 throw se;
220 }
221 return result;
222 case 2: // should only occur if session re-use was rejected
223 if (! complete)
224 {
225 state++;
226 return receiveEvidence(challenge);
227 }
228 // else fall through
229 default:
230 throw new IllegalMechanismStateException("evaluateChallenge()");
231 }
232 }
233
234 protected byte[] engineUnwrap(final byte[] incoming, final int offset,
235 final int len) throws SaslException
236 {
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;
244 try
245 {
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)
257 {
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 });
266 }
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
276 {
277 result = new byte[len - macBytesCount];
278 System.arraycopy(incoming, offset, result, 0, result.length);
279 }
280 }
281 else // no integrity protection; just confidentiality
282 result = inCipher.doFinal(incoming, offset, len);
283 }
284 catch (IOException x)
285 {
286 if (x instanceof SaslException)
287 throw (SaslException) x;
288 throw new SaslException("engineUnwrap()", x);
289 }
290 if (Configuration.DEBUG)
291 log.exiting(this.getClass().getName(), "engineUnwrap");
292 return result;
293 }
294
295 protected byte[] engineWrap(final byte[] outgoing, final int offset,
296 final int len) throws SaslException
297 {
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;
305 try
306 {
307 final ByteArrayOutputStream out = new ByteArrayOutputStream();
308 // Process the data
309 if (outCipher != null)
310 {
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)
317 {
318 outMac.update(result);
319 if (replayDetection)
320 {
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 });
329 }
330 final byte[] C = outMac.doFinal();
331 out.write(C);
332 if (Configuration.DEBUG)
333 log.fine("Encoding C (integrity checksum): " + Util.dumpString(C));
334 }
335 // else confidentiality only; do nothing
336 }
337 else // no confidentiality; just integrity [+ replay detection]
338 {
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)
345 {
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 });
354 }
355 final byte[] C = outMac.doFinal();
356 out.write(C);
357 if (Configuration.DEBUG)
358 log.fine("Encoding C (integrity checksum): " + Util.dumpString(C));
359 }
360 result = out.toByteArray();
361 }
362 catch (IOException x)
363 {
364 if (x instanceof SaslException)
365 throw (SaslException) x;
366 throw new SaslException("engineWrap()", x);
367 }
368 if (Configuration.DEBUG)
369 log.exiting(this.getClass().getName(), "engineWrap");
370 return result;
371 }
372
373 protected String getNegotiatedQOP()
374 {
375 if (inMac != null)
376 {
377 if (inCipher != null)
378 return Registry.QOP_AUTH_CONF;
379 return Registry.QOP_AUTH_INT;
380 }
381 return Registry.QOP_AUTH;
382 }
383
384 protected String getNegotiatedStrength()
385 {
386 if (inMac != null)
387 {
388 if (inCipher != null)
389 return Registry.STRENGTH_HIGH;
390 return Registry.STRENGTH_MEDIUM;
391 }
392 return Registry.STRENGTH_LOW;
393 }
394
395 protected String getNegotiatedRawSendSize()
396 {
397 return String.valueOf(rawSendSize);
398 }
399
400 protected String getReuse()
401 {
402 return Registry.REUSE_TRUE;
403 }
404
405 private byte[] sendIdentities() throws SaslException
406 {
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)
412 {
413 log.fine("Password: \"" + new String(password.getPassword()) + "\"");
414 log.fine("Encoding U (username): \"" + U + "\"");
415 log.fine("Encoding I (userid): \"" + authorizationID + "\"");
416 }
417 // if session re-use generate new 16-byte nonce
418 if (sid.length != 0)
419 {
420 cn = new byte[16];
421 getDefaultPRNG().nextBytes(cn);
422 }
423 else
424 cn = new byte[0];
425 final OutputBuffer frameOut = new OutputBuffer();
426 try
427 {
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);
433 }
434 catch (IOException x)
435 {
436 if (x instanceof SaslException)
437 throw (SaslException) x;
438 throw new AuthenticationException("sendIdentities()", x);
439 }
440 final byte[] result = frameOut.encode();
441 if (Configuration.DEBUG)
442 {
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");
450 }
451 return result;
452 }
453
454 private byte[] sendPublicKey(final byte[] input) throws SaslException
455 {
456 if (Configuration.DEBUG)
457 {
458 log.entering(this.getClass().getName(), "sendPublicKey");
459 log.fine("S: " + Util.dumpString(input));
460 }
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;
465 try
466 {
467 ack = (int) frameIn.getScalar(1);
468 if (ack == 0x00) // new session
469 {
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 + "\"");
485 }
486 else if (ack == 0xFF) // session re-use
487 {
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));
494 }
495 else // unexpected scalar
496 throw new SaslException("sendPublicKey(): Invalid scalar (" + ack
497 + ") in server's request");
498 }
499 catch (IOException x)
500 {
501 if (x instanceof SaslException)
502 throw (SaslException) x;
503 throw new SaslException("sendPublicKey()", x);
504 }
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);
515 try
516 {
517 clientHandler.init(mapA);
518 clientHandler.processMessage(null);
519 }
520 catch (KeyAgreementException x)
521 {
522 throw new SaslException("sendPublicKey()", x);
523 }
524 // -------------------------------------------------------------------
525 try
526 {
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();
537 }
538 catch (KeyAgreementException x)
539 {
540 throw new SaslException("sendPublicKey()", x);
541 }
542 // -------------------------------------------------------------------
543 if (Configuration.DEBUG)
544 {
545 log.fine("K: " + Util.dumpString(K));
546 log.fine("Encoding A (client ephemeral public key): " + Util.dump(A));
547 }
548 try
549 {
550 M1 = srp.generateM1(N, g, U, s, A, B, K, authorizationID, L, cn,
551 channelBinding);
552 }
553 catch (UnsupportedEncodingException x)
554 {
555 throw new AuthenticationException("sendPublicKey()", x);
556 }
557 if (Configuration.DEBUG)
558 {
559 log.fine("Encoding o (client chosen options): \"" + o + "\"");
560 log.fine("Encoding cIV (client IV): \"" + Util.dumpString(cIV) + "\"");
561 }
562 final OutputBuffer frameOut = new OutputBuffer();
563 try
564 {
565 frameOut.setMPI(A);
566 frameOut.setOS(M1);
567 frameOut.setText(o);
568 frameOut.setOS(cIV);
569 }
570 catch (IOException x)
571 {
572 if (x instanceof SaslException)
573 throw (SaslException) x;
574 throw new AuthenticationException("sendPublicKey()", x);
575 }
576 final byte[] result = frameOut.encode();
577 if (Configuration.DEBUG)
578 {
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");
586 }
587 return result;
588 }
589 else // session re-use accepted -------------------------------------------
590 {
591 setupSecurityServices(true);
592 if (Configuration.DEBUG)
593 {
594 log.fine("Session re-use accepted...");
595 log.exiting(this.getClass().getName(), "sendPublicKey");
596 }
597 return null;
598 }
599 }
600
601 private byte[] receiveEvidence(byte[] input) throws SaslException
602 {
603 if (Configuration.DEBUG)
604 {
605 log.entering(this.getClass().getName(), "receiveEvidence");
606 log.fine("S: " + Util.dumpString(input));
607 }
608 // Server send M2, sIV, sCB, sid, ttl
609 final InputBuffer frameIn = new InputBuffer(input);
610 try
611 {
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));
627 }
628 catch (IOException x)
629 {
630 if (x instanceof SaslException)
631 throw (SaslException) x;
632 throw new AuthenticationException("receiveEvidence()", x);
633 }
634
635 final byte[] expected;
636 try
637 {
638 expected = srp.generateM2(A, M1, K, U, authorizationID, o, sid, ttl,
639 cIV, sIV, sCB);
640 }
641 catch (UnsupportedEncodingException x)
642 {
643 throw new AuthenticationException("receiveEvidence()", x);
644 }
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;
653 }
654
655 private void getUsernameAndPassword() throws AuthenticationException
656 {
657 try
658 {
659 if ((! properties.containsKey(Registry.SASL_USERNAME))
660 && (! properties.containsKey(Registry.SASL_PASSWORD)))
661 {
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());
673 }
674 else
675 {
676 if (properties.containsKey(Registry.SASL_USERNAME))
677 this.U = (String) properties.get(Registry.SASL_USERNAME);
678 else
679 {
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();
688 }
689
690 if (properties.containsKey(Registry.SASL_PASSWORD))
691 {
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");
702 }
703 else
704 {
705 final PasswordCallback pwdCB = new PasswordCallback("password: ",
706 false);
707 this.handler.handle(new Callback[] { pwdCB });
708 password = new Password(pwdCB.getPassword());
709 }
710 }
711
712 if (U == null)
713 throw new AuthenticationException("null username supplied");
714 if (password == null)
715 throw new AuthenticationException("null password supplied");
716 }
717 catch (UnsupportedCallbackException x)
718 {
719 throw new AuthenticationException("getUsernameAndPassword()", x);
720 }
721 catch (IOException x)
722 {
723 throw new AuthenticationException("getUsernameAndPassword()", x);
724 }
725 }
726
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
731 {
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;
739
740 String mdName = SRPRegistry.SRP_DEFAULT_DIGEST_NAME;
741 final StringTokenizer st = new StringTokenizer(aol, ",");
742 while (st.hasMoreTokens())
743 {
744 option = st.nextToken();
745 if (option.startsWith(SRPRegistry.OPTION_SRP_DIGEST + "="))
746 {
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))
752 {
753 mdName = option;
754 break;
755 }
756 }
757 else if (option.equals(SRPRegistry.OPTION_REPLAY_DETECTION))
758 replaydetectionAvailable = true;
759 else if (option.startsWith(SRPRegistry.OPTION_INTEGRITY + "="))
760 {
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))
766 {
767 chosenIntegrityAlgorithm = option;
768 integrityAvailable = true;
769 break;
770 }
771 }
772 else if (option.startsWith(SRPRegistry.OPTION_CONFIDENTIALITY + "="))
773 {
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))
779 {
780 chosenConfidentialityAlgorithm = option;
781 confidentialityAvailable = true;
782 break;
783 }
784 }
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 + "="))
788 {
789 final String maxBufferSize = option.substring(option.indexOf('=') + 1);
790 try
791 {
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");
797 }
798 catch (NumberFormatException x)
799 {
800 throw new AuthenticationException(
801 SRPRegistry.OPTION_MAX_BUFFER_SIZE + "=" + maxBufferSize, x);
802 }
803 }
804 }
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))
818 {
819 replayDetection = true;
820 integrity = true;
821 }
822 else if (SRPRegistry.OPTION_INTEGRITY.equals(mandatory))
823 integrity = true;
824 else if (SRPRegistry.OPTION_CONFIDENTIALITY.equals(mandatory))
825 confidentiality = true;
826
827 if (replayDetection)
828 {
829 if (chosenIntegrityAlgorithm == null)
830 throw new AuthenticationException(
831 "Replay detection is required but no integrity protection "
832 + "algorithm was chosen");
833 }
834 if (integrity)
835 {
836 if (chosenIntegrityAlgorithm == null)
837 throw new AuthenticationException(
838 "Integrity protection is required but no algorithm was chosen");
839 }
840 if (confidentiality)
841 {
842 if (chosenConfidentialityAlgorithm == null)
843 throw new AuthenticationException(
844 "Confidentiality protection is required but no algorithm was chosen");
845 }
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
850 {
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);
860 }
861 srp = SRP.instance(mdName);
862 // Now create the options list specifying which of the available options
863 // we have chosen.
864
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(",");
879
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;
886 }
887
888 private void setupSecurityServices(final boolean sessionReUse)
889 throws SaslException
890 {
891 complete = true; // signal end of authentication phase
892 if (! sessionReUse)
893 {
894 outCounter = inCounter = 0;
895 // instantiate cipher if confidentiality protection filter is active
896 if (chosenConfidentialityAlgorithm != null)
897 {
898 if (Configuration.DEBUG)
899 log.fine("Activating confidentiality protection filter");
900 inCipher = CALG.getInstance(chosenConfidentialityAlgorithm);
901 outCipher = CALG.getInstance(chosenConfidentialityAlgorithm);
902 }
903 // instantiate hmacs if integrity protection filter is active
904 if (chosenIntegrityAlgorithm != null)
905 {
906 if (Configuration.DEBUG)
907 log.fine("Activating integrity protection filter");
908 inMac = IALG.getInstance(chosenIntegrityAlgorithm);
909 outMac = IALG.getInstance(chosenIntegrityAlgorithm);
910 }
911 }
912 else // same session new Keys
913 K = srp.generateKn(K, cn, sn);
914
915 final KDF kdf = KDF.getInstance(K);
916 // initialise in/out ciphers if confidentiality protection is used
917 if (inCipher != null)
918 {
919 inCipher.init(kdf, sIV, Direction.REVERSED);
920 outCipher.init(kdf, cIV, Direction.FORWARD);
921 }
922 // initialise in/out macs if integrity protection is used
923 if (inMac != null)
924 {
925 inMac.init(kdf);
926 outMac.init(kdf);
927 }
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,
936 K,
937 cIV,
938 sIV,
939 replayDetection,
940 inCounter,
941 outCounter,
942 inMac, outMac,
943 inCipher,
944 outCipher));
945 }
946 }
947
948 private PRNG getDefaultPRNG()
949 {
950 if (prng == null)
951 prng = PRNG.getInstance();
952 return prng;
953 }
954 }