SRPClient.java 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956
  1. /* SRPClient.java --
  2. Copyright (C) 2003, 2006, 2010 Free Software Foundation, Inc.
  3. This file is a part of GNU Classpath.
  4. GNU Classpath is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2 of the License, or (at
  7. your option) any later version.
  8. GNU Classpath is distributed in the hope that it will be useful, but
  9. WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with GNU Classpath; if not, write to the Free Software
  14. Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
  15. USA
  16. Linking this library statically or dynamically with other modules is
  17. making a combined work based on this library. Thus, the terms and
  18. conditions of the GNU General Public License cover the whole
  19. combination.
  20. As a special exception, the copyright holders of this library give you
  21. permission to link this library with independent modules to produce an
  22. executable, regardless of the license terms of these independent
  23. modules, and to copy and distribute the resulting executable under
  24. terms of your choice, provided that you also meet, for each linked
  25. independent module, the terms and conditions of the license of that
  26. module. An independent module is a module which is not derived from
  27. or based on this library. If you modify this library, you may extend
  28. this exception to your version of the library, but you are not
  29. obligated to do so. If you do not wish to do so, delete this
  30. exception statement from your version. */
  31. package gnu.javax.crypto.sasl.srp;
  32. import gnu.java.lang.CPStringBuilder;
  33. import gnu.java.security.Configuration;
  34. import gnu.java.security.Registry;
  35. import gnu.java.security.hash.MD5;
  36. import gnu.java.security.util.PRNG;
  37. import gnu.java.security.util.Util;
  38. import gnu.javax.crypto.assembly.Direction;
  39. import gnu.javax.crypto.cipher.CipherFactory;
  40. import gnu.javax.crypto.cipher.IBlockCipher;
  41. import gnu.javax.crypto.key.IKeyAgreementParty;
  42. import gnu.javax.crypto.key.IncomingMessage;
  43. import gnu.javax.crypto.key.KeyAgreementException;
  44. import gnu.javax.crypto.key.KeyAgreementFactory;
  45. import gnu.javax.crypto.key.OutgoingMessage;
  46. import gnu.javax.crypto.key.srp6.SRP6KeyAgreement;
  47. import gnu.javax.crypto.sasl.ClientMechanism;
  48. import gnu.javax.crypto.sasl.IllegalMechanismStateException;
  49. import gnu.javax.crypto.sasl.InputBuffer;
  50. import gnu.javax.crypto.sasl.IntegrityException;
  51. import gnu.javax.crypto.sasl.OutputBuffer;
  52. import gnu.javax.security.auth.Password;
  53. import java.io.ByteArrayOutputStream;
  54. import java.io.IOException;
  55. import java.io.UnsupportedEncodingException;
  56. import java.math.BigInteger;
  57. import java.security.NoSuchAlgorithmException;
  58. import java.util.Arrays;
  59. import java.util.HashMap;
  60. import java.util.StringTokenizer;
  61. import java.util.logging.Logger;
  62. import javax.security.auth.DestroyFailedException;
  63. import javax.security.auth.callback.Callback;
  64. import javax.security.auth.callback.NameCallback;
  65. import javax.security.auth.callback.PasswordCallback;
  66. import javax.security.auth.callback.UnsupportedCallbackException;
  67. import javax.security.sasl.AuthenticationException;
  68. import javax.security.sasl.SaslClient;
  69. import javax.security.sasl.SaslException;
  70. /**
  71. * The SASL-SRP client-side mechanism.
  72. */
  73. public class SRPClient
  74. extends ClientMechanism
  75. implements SaslClient
  76. {
  77. private static final Logger log = Configuration.DEBUG ?
  78. Logger.getLogger(SRPClient.class.getName()) : null;
  79. private String uid; // the unique key for this type of client
  80. private String U; // the authentication identity
  81. BigInteger N, g, A, B;
  82. private Password password; // the authentication credentials
  83. private byte[] s; // the user's salt
  84. private byte[] cIV, sIV; // client+server IVs, when confidentiality is on
  85. private byte[] M1, M2; // client+server evidences
  86. private byte[] cn, sn; // client's and server's nonce
  87. private SRP srp; // SRP algorithm instance used by this client
  88. private byte[] sid; // session ID when re-used
  89. private int ttl; // session time-to-live in seconds
  90. private byte[] sCB; // the peer's channel binding data
  91. private String L; // available options
  92. private String o;
  93. private String chosenIntegrityAlgorithm;
  94. private String chosenConfidentialityAlgorithm;
  95. private int rawSendSize = Registry.SASL_BUFFER_MAX_LIMIT;
  96. private byte[] K; // shared session key
  97. private boolean replayDetection = true; // whether Replay Detection is on
  98. private int inCounter = 0; // messages sequence numbers
  99. private int outCounter = 0;
  100. private IALG inMac, outMac; // if !null, use for integrity
  101. private CALG inCipher, outCipher; // if !null, use for confidentiality
  102. private IKeyAgreementParty clientHandler =
  103. KeyAgreementFactory.getPartyAInstance(Registry.SRP_SASL_KA);
  104. /** Our default source of randomness. */
  105. private PRNG prng = null;
  106. public SRPClient()
  107. {
  108. super(Registry.SASL_SRP_MECHANISM);
  109. }
  110. protected void initMechanism() throws SaslException
  111. {
  112. // we shall keep track of the sid (and the security context of this SRP
  113. // client) based on the initialisation parameters of an SRP session.
  114. // we shall compute a unique key for those parameters and key the sid
  115. // (and the security context) accordingly.
  116. // 1. compute the mapping key. use MD5 (the fastest) for this purpose
  117. final MD5 md = new MD5();
  118. byte[] b;
  119. b = authorizationID.getBytes();
  120. md.update(b, 0, b.length);
  121. b = serverName.getBytes();
  122. md.update(b, 0, b.length);
  123. b = protocol.getBytes();
  124. md.update(b, 0, b.length);
  125. if (channelBinding.length > 0)
  126. md.update(channelBinding, 0, channelBinding.length);
  127. uid = Util.toBase64(md.digest());
  128. if (ClientStore.instance().isAlive(uid))
  129. {
  130. final SecurityContext ctx = ClientStore.instance().restoreSession(uid);
  131. srp = SRP.instance(ctx.getMdName());
  132. sid = ctx.getSID();
  133. K = ctx.getK();
  134. cIV = ctx.getClientIV();
  135. sIV = ctx.getServerIV();
  136. replayDetection = ctx.hasReplayDetection();
  137. inCounter = ctx.getInCounter();
  138. outCounter = ctx.getOutCounter();
  139. inMac = ctx.getInMac();
  140. outMac = ctx.getOutMac();
  141. inCipher = ctx.getInCipher();
  142. outCipher = ctx.getOutCipher();
  143. }
  144. else
  145. {
  146. sid = new byte[0];
  147. ttl = 0;
  148. K = null;
  149. cIV = null;
  150. sIV = null;
  151. cn = null;
  152. sn = null;
  153. }
  154. }
  155. protected void resetMechanism() throws SaslException
  156. {
  157. try
  158. {
  159. password.destroy();
  160. }
  161. catch (DestroyFailedException dfe)
  162. {
  163. SaslException se = new SaslException("resetMechanism()");
  164. se.initCause(dfe);
  165. throw se;
  166. }
  167. password = null;
  168. M1 = null;
  169. K = null;
  170. cIV = null;
  171. sIV = null;
  172. inMac = outMac = null;
  173. inCipher = outCipher = null;
  174. sid = null;
  175. ttl = 0;
  176. cn = null;
  177. sn = null;
  178. }
  179. public boolean hasInitialResponse()
  180. {
  181. return true;
  182. }
  183. public byte[] evaluateChallenge(final byte[] challenge) throws SaslException
  184. {
  185. switch (state)
  186. {
  187. case 0:
  188. state++;
  189. return sendIdentities();
  190. case 1:
  191. state++;
  192. final byte[] result = sendPublicKey(challenge);
  193. try
  194. {
  195. password.destroy(); //don't need further this session
  196. }
  197. catch (DestroyFailedException x)
  198. {
  199. SaslException se = new SaslException("sendPublicKey()");
  200. se.initCause(se);
  201. throw se;
  202. }
  203. return result;
  204. case 2: // should only occur if session re-use was rejected
  205. if (! complete)
  206. {
  207. state++;
  208. return receiveEvidence(challenge);
  209. }
  210. // else fall through
  211. default:
  212. throw new IllegalMechanismStateException("evaluateChallenge()");
  213. }
  214. }
  215. protected byte[] engineUnwrap(final byte[] incoming, final int offset,
  216. final int len) throws SaslException
  217. {
  218. if (Configuration.DEBUG)
  219. log.entering(this.getClass().getName(), "engineUnwrap");
  220. if (inMac == null && inCipher == null)
  221. throw new IllegalStateException("connection is not protected");
  222. // at this point one, or both, of confidentiality and integrity protection
  223. // services are active.
  224. final byte[] result;
  225. try
  226. {
  227. if (inMac != null)
  228. { // integrity bytes are at the end of the stream
  229. final int macBytesCount = inMac.length();
  230. final int payloadLength = len - macBytesCount;
  231. final byte[] received_mac = new byte[macBytesCount];
  232. System.arraycopy(incoming, offset + payloadLength, received_mac, 0,
  233. macBytesCount);
  234. if (Configuration.DEBUG)
  235. log.fine("Got C (received MAC): " + Util.dumpString(received_mac));
  236. inMac.update(incoming, offset, payloadLength);
  237. if (replayDetection)
  238. {
  239. inCounter++;
  240. if (Configuration.DEBUG)
  241. log.fine("inCounter=" + inCounter);
  242. inMac.update(new byte[] {
  243. (byte)(inCounter >>> 24),
  244. (byte)(inCounter >>> 16),
  245. (byte)(inCounter >>> 8),
  246. (byte) inCounter });
  247. }
  248. final byte[] computed_mac = inMac.doFinal();
  249. if (Configuration.DEBUG)
  250. log.fine("Computed MAC: " + Util.dumpString(computed_mac));
  251. if (! Arrays.equals(received_mac, computed_mac))
  252. throw new IntegrityException("engineUnwrap()");
  253. // deal with the payload, which can be either plain or encrypted
  254. if (inCipher != null)
  255. result = inCipher.doFinal(incoming, offset, payloadLength);
  256. else
  257. {
  258. result = new byte[len - macBytesCount];
  259. System.arraycopy(incoming, offset, result, 0, result.length);
  260. }
  261. }
  262. else // no integrity protection; just confidentiality
  263. result = inCipher.doFinal(incoming, offset, len);
  264. }
  265. catch (IOException x)
  266. {
  267. if (x instanceof SaslException)
  268. throw (SaslException) x;
  269. throw new SaslException("engineUnwrap()", x);
  270. }
  271. if (Configuration.DEBUG)
  272. log.exiting(this.getClass().getName(), "engineUnwrap");
  273. return result;
  274. }
  275. protected byte[] engineWrap(final byte[] outgoing, final int offset,
  276. final int len) throws SaslException
  277. {
  278. if (Configuration.DEBUG)
  279. log.entering(this.getClass().getName(), "engineWrap");
  280. if (outMac == null && outCipher == null)
  281. throw new IllegalStateException("connection is not protected");
  282. // at this point one, or both, of confidentiality and integrity protection
  283. // services are active.
  284. byte[] result;
  285. try
  286. {
  287. final ByteArrayOutputStream out = new ByteArrayOutputStream();
  288. // Process the data
  289. if (outCipher != null)
  290. {
  291. result = outCipher.doFinal(outgoing, offset, len);
  292. if (Configuration.DEBUG)
  293. log.fine("Encoding c (encrypted plaintext): "
  294. + Util.dumpString(result));
  295. out.write(result);
  296. if (outMac != null)
  297. {
  298. outMac.update(result);
  299. if (replayDetection)
  300. {
  301. outCounter++;
  302. if (Configuration.DEBUG)
  303. log.fine("outCounter=" + outCounter);
  304. outMac.update(new byte[] {
  305. (byte)(outCounter >>> 24),
  306. (byte)(outCounter >>> 16),
  307. (byte)(outCounter >>> 8),
  308. (byte) outCounter });
  309. }
  310. final byte[] C = outMac.doFinal();
  311. out.write(C);
  312. if (Configuration.DEBUG)
  313. log.fine("Encoding C (integrity checksum): " + Util.dumpString(C));
  314. }
  315. // else confidentiality only; do nothing
  316. }
  317. else // no confidentiality; just integrity [+ replay detection]
  318. {
  319. if (Configuration.DEBUG)
  320. log.fine("Encoding p (plaintext): "
  321. + Util.dumpString(outgoing, offset, len));
  322. out.write(outgoing, offset, len);
  323. outMac.update(outgoing, offset, len);
  324. if (replayDetection)
  325. {
  326. outCounter++;
  327. if (Configuration.DEBUG)
  328. log.fine("outCounter=" + outCounter);
  329. outMac.update(new byte[] {
  330. (byte)(outCounter >>> 24),
  331. (byte)(outCounter >>> 16),
  332. (byte)(outCounter >>> 8),
  333. (byte) outCounter });
  334. }
  335. final byte[] C = outMac.doFinal();
  336. out.write(C);
  337. if (Configuration.DEBUG)
  338. log.fine("Encoding C (integrity checksum): " + Util.dumpString(C));
  339. }
  340. result = out.toByteArray();
  341. }
  342. catch (IOException x)
  343. {
  344. if (x instanceof SaslException)
  345. throw (SaslException) x;
  346. throw new SaslException("engineWrap()", x);
  347. }
  348. if (Configuration.DEBUG)
  349. log.exiting(this.getClass().getName(), "engineWrap");
  350. return result;
  351. }
  352. protected String getNegotiatedQOP()
  353. {
  354. if (inMac != null)
  355. {
  356. if (inCipher != null)
  357. return Registry.QOP_AUTH_CONF;
  358. return Registry.QOP_AUTH_INT;
  359. }
  360. return Registry.QOP_AUTH;
  361. }
  362. protected String getNegotiatedStrength()
  363. {
  364. if (inMac != null)
  365. {
  366. if (inCipher != null)
  367. return Registry.STRENGTH_HIGH;
  368. return Registry.STRENGTH_MEDIUM;
  369. }
  370. return Registry.STRENGTH_LOW;
  371. }
  372. protected String getNegotiatedRawSendSize()
  373. {
  374. return String.valueOf(rawSendSize);
  375. }
  376. protected String getReuse()
  377. {
  378. return Registry.REUSE_TRUE;
  379. }
  380. private byte[] sendIdentities() throws SaslException
  381. {
  382. if (Configuration.DEBUG)
  383. log.entering(this.getClass().getName(), "sendIdentities");
  384. // If necessary, prompt the client for the username and password
  385. getUsernameAndPassword();
  386. if (Configuration.DEBUG)
  387. {
  388. log.fine("Password: \"" + new String(password.getPassword()) + "\"");
  389. log.fine("Encoding U (username): \"" + U + "\"");
  390. log.fine("Encoding I (userid): \"" + authorizationID + "\"");
  391. }
  392. // if session re-use generate new 16-byte nonce
  393. if (sid.length != 0)
  394. {
  395. cn = new byte[16];
  396. getDefaultPRNG().nextBytes(cn);
  397. }
  398. else
  399. cn = new byte[0];
  400. final OutputBuffer frameOut = new OutputBuffer();
  401. try
  402. {
  403. frameOut.setText(U);
  404. frameOut.setText(authorizationID);
  405. frameOut.setEOS(sid); // session ID to re-use
  406. frameOut.setOS(cn); // client nonce
  407. frameOut.setEOS(channelBinding);
  408. }
  409. catch (IOException x)
  410. {
  411. if (x instanceof SaslException)
  412. throw (SaslException) x;
  413. throw new AuthenticationException("sendIdentities()", x);
  414. }
  415. final byte[] result = frameOut.encode();
  416. if (Configuration.DEBUG)
  417. {
  418. log.fine("C: " + Util.dumpString(result));
  419. log.fine(" U = " + U);
  420. log.fine(" I = " + authorizationID);
  421. log.fine("sid = " + new String(sid));
  422. log.fine(" cn = " + Util.dumpString(cn));
  423. log.fine("cCB = " + Util.dumpString(channelBinding));
  424. log.exiting(this.getClass().getName(), "sendIdentities");
  425. }
  426. return result;
  427. }
  428. private byte[] sendPublicKey(final byte[] input) throws SaslException
  429. {
  430. if (Configuration.DEBUG)
  431. {
  432. log.entering(this.getClass().getName(), "sendPublicKey");
  433. log.fine("S: " + Util.dumpString(input));
  434. }
  435. // Server sends [00], N, g, s, B, L
  436. // or [FF], sn, sCB
  437. final InputBuffer frameIn = new InputBuffer(input);
  438. final int ack;
  439. try
  440. {
  441. ack = (int) frameIn.getScalar(1);
  442. if (ack == 0x00) // new session
  443. {
  444. N = frameIn.getMPI();
  445. if (Configuration.DEBUG)
  446. log.fine("Got N (modulus): " + Util.dump(N));
  447. g = frameIn.getMPI();
  448. if (Configuration.DEBUG)
  449. log.fine("Got g (generator): " + Util.dump(g));
  450. s = frameIn.getOS();
  451. if (Configuration.DEBUG)
  452. log.fine("Got s (salt): " + Util.dumpString(s));
  453. B = frameIn.getMPI();
  454. if (Configuration.DEBUG)
  455. log.fine("Got B (server ephermeral public key): " + Util.dump(B));
  456. L = frameIn.getText();
  457. if (Configuration.DEBUG)
  458. log.fine("Got L (available options): \"" + L + "\"");
  459. }
  460. else if (ack == 0xFF) // session re-use
  461. {
  462. sn = frameIn.getOS();
  463. if (Configuration.DEBUG)
  464. log.fine("Got sn (server nonce): " + Util.dumpString(sn));
  465. sCB = frameIn.getEOS();
  466. if (Configuration.DEBUG)
  467. log.fine("Got sCB (server channel binding): " + Util.dumpString(sCB));
  468. }
  469. else // unexpected scalar
  470. throw new SaslException("sendPublicKey(): Invalid scalar (" + ack
  471. + ") in server's request");
  472. }
  473. catch (IOException x)
  474. {
  475. if (x instanceof SaslException)
  476. throw (SaslException) x;
  477. throw new SaslException("sendPublicKey()", x);
  478. }
  479. if (ack == 0x00)
  480. { // new session ---------------------------------------
  481. o = createO(L.toLowerCase()); // do this first to initialise the SRP hash
  482. final byte[] pBytes; // use ASCII encoding to inter-operate w/ non-java
  483. pBytes = password.getBytes();
  484. // ----------------------------------------------------------------------
  485. final HashMap mapA = new HashMap();
  486. mapA.put(SRP6KeyAgreement.HASH_FUNCTION, srp.getAlgorithm());
  487. mapA.put(SRP6KeyAgreement.USER_IDENTITY, U);
  488. mapA.put(SRP6KeyAgreement.USER_PASSWORD, pBytes);
  489. try
  490. {
  491. clientHandler.init(mapA);
  492. clientHandler.processMessage(null);
  493. }
  494. catch (KeyAgreementException x)
  495. {
  496. throw new SaslException("sendPublicKey()", x);
  497. }
  498. // -------------------------------------------------------------------
  499. try
  500. {
  501. OutgoingMessage out = new OutgoingMessage();
  502. out.writeMPI(N);
  503. out.writeMPI(g);
  504. out.writeMPI(new BigInteger(1, s));
  505. out.writeMPI(B);
  506. IncomingMessage in = new IncomingMessage(out.toByteArray());
  507. out = clientHandler.processMessage(in);
  508. in = new IncomingMessage(out.toByteArray());
  509. A = in.readMPI();
  510. K = clientHandler.getSharedSecret();
  511. }
  512. catch (KeyAgreementException x)
  513. {
  514. throw new SaslException("sendPublicKey()", x);
  515. }
  516. // -------------------------------------------------------------------
  517. if (Configuration.DEBUG)
  518. {
  519. log.fine("K: " + Util.dumpString(K));
  520. log.fine("Encoding A (client ephemeral public key): " + Util.dump(A));
  521. }
  522. try
  523. {
  524. M1 = srp.generateM1(N, g, U, s, A, B, K, authorizationID, L, cn,
  525. channelBinding);
  526. }
  527. catch (UnsupportedEncodingException x)
  528. {
  529. throw new AuthenticationException("sendPublicKey()", x);
  530. }
  531. if (Configuration.DEBUG)
  532. {
  533. log.fine("Encoding o (client chosen options): \"" + o + "\"");
  534. log.fine("Encoding cIV (client IV): \"" + Util.dumpString(cIV) + "\"");
  535. }
  536. final OutputBuffer frameOut = new OutputBuffer();
  537. try
  538. {
  539. frameOut.setMPI(A);
  540. frameOut.setOS(M1);
  541. frameOut.setText(o);
  542. frameOut.setOS(cIV);
  543. }
  544. catch (IOException x)
  545. {
  546. if (x instanceof SaslException)
  547. throw (SaslException) x;
  548. throw new AuthenticationException("sendPublicKey()", x);
  549. }
  550. final byte[] result = frameOut.encode();
  551. if (Configuration.DEBUG)
  552. {
  553. log.fine("New session, or session re-use rejected...");
  554. log.fine("C: " + Util.dumpString(result));
  555. log.fine(" A = 0x" + A.toString(16));
  556. log.fine(" M1 = " + Util.dumpString(M1));
  557. log.fine(" o = " + o);
  558. log.fine("cIV = " + Util.dumpString(cIV));
  559. log.exiting(this.getClass().getName(), "sendPublicKey");
  560. }
  561. return result;
  562. }
  563. else // session re-use accepted -------------------------------------------
  564. {
  565. setupSecurityServices(true);
  566. if (Configuration.DEBUG)
  567. {
  568. log.fine("Session re-use accepted...");
  569. log.exiting(this.getClass().getName(), "sendPublicKey");
  570. }
  571. return null;
  572. }
  573. }
  574. private byte[] receiveEvidence(byte[] input) throws SaslException
  575. {
  576. if (Configuration.DEBUG)
  577. {
  578. log.entering(this.getClass().getName(), "receiveEvidence");
  579. log.fine("S: " + Util.dumpString(input));
  580. }
  581. // Server send M2, sIV, sCB, sid, ttl
  582. final InputBuffer frameIn = new InputBuffer(input);
  583. try
  584. {
  585. M2 = frameIn.getOS();
  586. if (Configuration.DEBUG)
  587. log.fine("Got M2 (server evidence): " + Util.dumpString(M2));
  588. sIV = frameIn.getOS();
  589. if (Configuration.DEBUG)
  590. log.fine("Got sIV (server IV): " + Util.dumpString(sIV));
  591. sid = frameIn.getEOS();
  592. if (Configuration.DEBUG)
  593. log.fine("Got sid (session ID): " + new String(sid));
  594. ttl = (int) frameIn.getScalar(4);
  595. if (Configuration.DEBUG)
  596. log.fine("Got ttl (session time-to-live): " + ttl + "sec.");
  597. sCB = frameIn.getEOS();
  598. if (Configuration.DEBUG)
  599. log.fine("Got sCB (server channel binding): " + Util.dumpString(sCB));
  600. }
  601. catch (IOException x)
  602. {
  603. if (x instanceof SaslException)
  604. throw (SaslException) x;
  605. throw new AuthenticationException("receiveEvidence()", x);
  606. }
  607. final byte[] expected;
  608. try
  609. {
  610. expected = srp.generateM2(A, M1, K, U, authorizationID, o, sid, ttl,
  611. cIV, sIV, sCB);
  612. }
  613. catch (UnsupportedEncodingException x)
  614. {
  615. throw new AuthenticationException("receiveEvidence()", x);
  616. }
  617. if (Configuration.DEBUG)
  618. log.fine("Expected: " + Util.dumpString(expected));
  619. if (! Arrays.equals(M2, expected))
  620. throw new AuthenticationException("M2 mismatch");
  621. setupSecurityServices(false);
  622. if (Configuration.DEBUG)
  623. log.exiting(this.getClass().getName(), "receiveEvidence");
  624. return null;
  625. }
  626. private void getUsernameAndPassword() throws AuthenticationException
  627. {
  628. try
  629. {
  630. if ((! properties.containsKey(Registry.SASL_USERNAME))
  631. && (! properties.containsKey(Registry.SASL_PASSWORD)))
  632. {
  633. final NameCallback nameCB;
  634. final String defaultName = System.getProperty("user.name");
  635. if (defaultName == null)
  636. nameCB = new NameCallback("username: ");
  637. else
  638. nameCB = new NameCallback("username: ", defaultName);
  639. final PasswordCallback pwdCB = new PasswordCallback("password: ",
  640. false);
  641. handler.handle(new Callback[] { nameCB, pwdCB });
  642. U = nameCB.getName();
  643. password = new Password(pwdCB.getPassword());
  644. }
  645. else
  646. {
  647. if (properties.containsKey(Registry.SASL_USERNAME))
  648. this.U = (String) properties.get(Registry.SASL_USERNAME);
  649. else
  650. {
  651. final NameCallback nameCB;
  652. final String defaultName = System.getProperty("user.name");
  653. if (defaultName == null)
  654. nameCB = new NameCallback("username: ");
  655. else
  656. nameCB = new NameCallback("username: ", defaultName);
  657. this.handler.handle(new Callback[] { nameCB });
  658. this.U = nameCB.getName();
  659. }
  660. if (properties.containsKey(Registry.SASL_PASSWORD))
  661. {
  662. Object pw = properties.get(Registry.SASL_PASSWORD);
  663. if (pw instanceof char[])
  664. password = new Password((char[]) pw);
  665. else if (pw instanceof Password)
  666. password = (Password) pw;
  667. else if (pw instanceof String)
  668. password = new Password(((String) pw).toCharArray());
  669. else
  670. throw new IllegalArgumentException(pw.getClass().getName()
  671. + "is not a valid password class");
  672. }
  673. else
  674. {
  675. final PasswordCallback pwdCB = new PasswordCallback("password: ",
  676. false);
  677. this.handler.handle(new Callback[] { pwdCB });
  678. password = new Password(pwdCB.getPassword());
  679. }
  680. }
  681. if (U == null)
  682. throw new AuthenticationException("null username supplied");
  683. if (password == null)
  684. throw new AuthenticationException("null password supplied");
  685. }
  686. catch (UnsupportedCallbackException x)
  687. {
  688. throw new AuthenticationException("getUsernameAndPassword()", x);
  689. }
  690. catch (IOException x)
  691. {
  692. throw new AuthenticationException("getUsernameAndPassword()", x);
  693. }
  694. }
  695. // We go through the list of available services and for each available one
  696. // we decide whether or not we want it enabled, based on properties passed
  697. // to us by the client.
  698. private String createO(final String aol) throws AuthenticationException
  699. {
  700. if (Configuration.DEBUG)
  701. log.entering(this.getClass().getName(), "createO", aol);
  702. boolean replaydetectionAvailable = false;
  703. boolean integrityAvailable = false;
  704. boolean confidentialityAvailable = false;
  705. String option, mandatory = SRPRegistry.DEFAULT_MANDATORY;
  706. int i;
  707. String mdName = SRPRegistry.SRP_DEFAULT_DIGEST_NAME;
  708. final StringTokenizer st = new StringTokenizer(aol, ",");
  709. while (st.hasMoreTokens())
  710. {
  711. option = st.nextToken();
  712. if (option.startsWith(SRPRegistry.OPTION_SRP_DIGEST + "="))
  713. {
  714. option = option.substring(option.indexOf('=') + 1);
  715. if (Configuration.DEBUG)
  716. log.fine("mda: <" + option + ">");
  717. for (i = 0; i < SRPRegistry.INTEGRITY_ALGORITHMS.length; i++)
  718. if (SRPRegistry.SRP_ALGORITHMS[i].equals(option))
  719. {
  720. mdName = option;
  721. break;
  722. }
  723. }
  724. else if (option.equals(SRPRegistry.OPTION_REPLAY_DETECTION))
  725. replaydetectionAvailable = true;
  726. else if (option.startsWith(SRPRegistry.OPTION_INTEGRITY + "="))
  727. {
  728. option = option.substring(option.indexOf('=') + 1);
  729. if (Configuration.DEBUG)
  730. log.fine("ialg: <" + option + ">");
  731. for (i = 0; i < SRPRegistry.INTEGRITY_ALGORITHMS.length; i++)
  732. if (SRPRegistry.INTEGRITY_ALGORITHMS[i].equals(option))
  733. {
  734. chosenIntegrityAlgorithm = option;
  735. integrityAvailable = true;
  736. break;
  737. }
  738. }
  739. else if (option.startsWith(SRPRegistry.OPTION_CONFIDENTIALITY + "="))
  740. {
  741. option = option.substring(option.indexOf('=') + 1);
  742. if (Configuration.DEBUG)
  743. log.fine("calg: <" + option + ">");
  744. for (i = 0; i < SRPRegistry.CONFIDENTIALITY_ALGORITHMS.length; i++)
  745. if (SRPRegistry.CONFIDENTIALITY_ALGORITHMS[i].equals(option))
  746. {
  747. chosenConfidentialityAlgorithm = option;
  748. confidentialityAvailable = true;
  749. break;
  750. }
  751. }
  752. else if (option.startsWith(SRPRegistry.OPTION_MANDATORY + "="))
  753. mandatory = option.substring(option.indexOf('=') + 1);
  754. else if (option.startsWith(SRPRegistry.OPTION_MAX_BUFFER_SIZE + "="))
  755. {
  756. final String maxBufferSize = option.substring(option.indexOf('=') + 1);
  757. try
  758. {
  759. rawSendSize = Integer.parseInt(maxBufferSize);
  760. if (rawSendSize > Registry.SASL_BUFFER_MAX_LIMIT
  761. || rawSendSize < 1)
  762. throw new AuthenticationException(
  763. "Illegal value for 'maxbuffersize' option");
  764. }
  765. catch (NumberFormatException x)
  766. {
  767. throw new AuthenticationException(
  768. SRPRegistry.OPTION_MAX_BUFFER_SIZE + "=" + maxBufferSize, x);
  769. }
  770. }
  771. }
  772. String s;
  773. Boolean flag;
  774. s = (String) properties.get(SRPRegistry.SRP_REPLAY_DETECTION);
  775. flag = Boolean.valueOf(s);
  776. replayDetection = replaydetectionAvailable && flag.booleanValue();
  777. s = (String) properties.get(SRPRegistry.SRP_INTEGRITY_PROTECTION);
  778. flag = Boolean.valueOf(s);
  779. boolean integrity = integrityAvailable && flag.booleanValue();
  780. s = (String) properties.get(SRPRegistry.SRP_CONFIDENTIALITY);
  781. flag = Boolean.valueOf(s);
  782. boolean confidentiality = confidentialityAvailable && flag.booleanValue();
  783. // make sure we do the right thing
  784. if (SRPRegistry.OPTION_REPLAY_DETECTION.equals(mandatory))
  785. {
  786. replayDetection = true;
  787. integrity = true;
  788. }
  789. else if (SRPRegistry.OPTION_INTEGRITY.equals(mandatory))
  790. integrity = true;
  791. else if (SRPRegistry.OPTION_CONFIDENTIALITY.equals(mandatory))
  792. confidentiality = true;
  793. if (replayDetection)
  794. {
  795. if (chosenIntegrityAlgorithm == null)
  796. throw new AuthenticationException(
  797. "Replay detection is required but no integrity protection "
  798. + "algorithm was chosen");
  799. }
  800. if (integrity)
  801. {
  802. if (chosenIntegrityAlgorithm == null)
  803. throw new AuthenticationException(
  804. "Integrity protection is required but no algorithm was chosen");
  805. }
  806. if (confidentiality)
  807. {
  808. if (chosenConfidentialityAlgorithm == null)
  809. throw new AuthenticationException(
  810. "Confidentiality protection is required but no algorithm was chosen");
  811. }
  812. // 1. check if we'll be using confidentiality; if not set IV to 0-byte
  813. if (chosenConfidentialityAlgorithm == null)
  814. cIV = new byte[0];
  815. else
  816. {
  817. // 2. get the block size of the cipher
  818. final IBlockCipher cipher = CipherFactory.getInstance(chosenConfidentialityAlgorithm);
  819. if (cipher == null)
  820. throw new AuthenticationException("createO()",
  821. new NoSuchAlgorithmException());
  822. final int blockSize = cipher.defaultBlockSize();
  823. // 3. generate random iv
  824. cIV = new byte[blockSize];
  825. getDefaultPRNG().nextBytes(cIV);
  826. }
  827. srp = SRP.instance(mdName);
  828. // Now create the options list specifying which of the available options
  829. // we have chosen.
  830. // For now we just select the defaults. Later we need to add support for
  831. // properties (perhaps in a file) where a user can specify the list of
  832. // algorithms they would prefer to use.
  833. final CPStringBuilder sb = new CPStringBuilder();
  834. sb.append(SRPRegistry.OPTION_SRP_DIGEST)
  835. .append("=").append(mdName).append(",");
  836. if (replayDetection)
  837. sb.append(SRPRegistry.OPTION_REPLAY_DETECTION).append(",");
  838. if (integrity)
  839. sb.append(SRPRegistry.OPTION_INTEGRITY)
  840. .append("=").append(chosenIntegrityAlgorithm).append(",");
  841. if (confidentiality)
  842. sb.append(SRPRegistry.OPTION_CONFIDENTIALITY)
  843. .append("=").append(chosenConfidentialityAlgorithm).append(",");
  844. final String result = sb.append(SRPRegistry.OPTION_MAX_BUFFER_SIZE)
  845. .append("=").append(Registry.SASL_BUFFER_MAX_LIMIT)
  846. .toString();
  847. if (Configuration.DEBUG)
  848. log.exiting(this.getClass().getName(), "createO", result);
  849. return result;
  850. }
  851. private void setupSecurityServices(final boolean sessionReUse)
  852. throws SaslException
  853. {
  854. complete = true; // signal end of authentication phase
  855. if (! sessionReUse)
  856. {
  857. outCounter = inCounter = 0;
  858. // instantiate cipher if confidentiality protection filter is active
  859. if (chosenConfidentialityAlgorithm != null)
  860. {
  861. if (Configuration.DEBUG)
  862. log.fine("Activating confidentiality protection filter");
  863. inCipher = CALG.getInstance(chosenConfidentialityAlgorithm);
  864. outCipher = CALG.getInstance(chosenConfidentialityAlgorithm);
  865. }
  866. // instantiate hmacs if integrity protection filter is active
  867. if (chosenIntegrityAlgorithm != null)
  868. {
  869. if (Configuration.DEBUG)
  870. log.fine("Activating integrity protection filter");
  871. inMac = IALG.getInstance(chosenIntegrityAlgorithm);
  872. outMac = IALG.getInstance(chosenIntegrityAlgorithm);
  873. }
  874. }
  875. else // same session new Keys
  876. K = srp.generateKn(K, cn, sn);
  877. final KDF kdf = KDF.getInstance(K);
  878. // initialise in/out ciphers if confidentiality protection is used
  879. if (inCipher != null)
  880. {
  881. inCipher.init(kdf, sIV, Direction.REVERSED);
  882. outCipher.init(kdf, cIV, Direction.FORWARD);
  883. }
  884. // initialise in/out macs if integrity protection is used
  885. if (inMac != null)
  886. {
  887. inMac.init(kdf);
  888. outMac.init(kdf);
  889. }
  890. if (sid != null && sid.length != 0)
  891. { // update the security context and save in map
  892. if (Configuration.DEBUG)
  893. log.fine("Updating security context for UID = " + uid);
  894. ClientStore.instance().cacheSession(uid,
  895. ttl,
  896. new SecurityContext(srp.getAlgorithm(),
  897. sid,
  898. K,
  899. cIV,
  900. sIV,
  901. replayDetection,
  902. inCounter,
  903. outCounter,
  904. inMac, outMac,
  905. inCipher,
  906. outCipher));
  907. }
  908. }
  909. private PRNG getDefaultPRNG()
  910. {
  911. if (prng == null)
  912. prng = PRNG.getInstance();
  913. return prng;
  914. }
  915. }