SRPServer.java 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844
  1. /* SRPServer.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.util.PRNG;
  36. import gnu.java.security.util.Util;
  37. import gnu.javax.crypto.assembly.Direction;
  38. import gnu.javax.crypto.cipher.CipherFactory;
  39. import gnu.javax.crypto.cipher.IBlockCipher;
  40. import gnu.javax.crypto.key.IKeyAgreementParty;
  41. import gnu.javax.crypto.key.IncomingMessage;
  42. import gnu.javax.crypto.key.KeyAgreementException;
  43. import gnu.javax.crypto.key.KeyAgreementFactory;
  44. import gnu.javax.crypto.key.OutgoingMessage;
  45. import gnu.javax.crypto.key.srp6.SRP6KeyAgreement;
  46. import gnu.javax.crypto.sasl.IllegalMechanismStateException;
  47. import gnu.javax.crypto.sasl.InputBuffer;
  48. import gnu.javax.crypto.sasl.IntegrityException;
  49. import gnu.javax.crypto.sasl.OutputBuffer;
  50. import gnu.javax.crypto.sasl.ServerMechanism;
  51. import java.io.ByteArrayOutputStream;
  52. import java.io.IOException;
  53. import java.io.UnsupportedEncodingException;
  54. import java.math.BigInteger;
  55. import java.util.Arrays;
  56. import java.util.HashMap;
  57. import java.util.StringTokenizer;
  58. import java.util.logging.Logger;
  59. import javax.security.sasl.AuthenticationException;
  60. import javax.security.sasl.SaslException;
  61. import javax.security.sasl.SaslServer;
  62. /**
  63. * The SASL-SRP server-side mechanism.
  64. */
  65. public class SRPServer
  66. extends ServerMechanism
  67. implements SaslServer
  68. {
  69. private static final Logger log = Configuration.DEBUG ?
  70. Logger.getLogger(SRPServer.class.getName()) : null;
  71. private String U = null; // client's username
  72. private BigInteger N, g, A, B;
  73. private byte[] s; // salt
  74. private byte[] cIV, sIV; // client+server IVs, when confidentiality is on
  75. private byte[] cn, sn; // client's and server's nonce
  76. private SRP srp; // SRP algorithm instance used by this server
  77. private byte[] sid; // session ID when re-used
  78. private int ttl = 360; // session time-to-live in seconds
  79. private byte[] cCB; // peer's channel binding'
  80. private String mandatory; // List of available options
  81. private String L = null;
  82. private String o;
  83. private String chosenIntegrityAlgorithm;
  84. private String chosenConfidentialityAlgorithm;
  85. private int rawSendSize = Registry.SASL_BUFFER_MAX_LIMIT;
  86. private byte[] K; // shared session key
  87. private boolean replayDetection = true; // whether Replay Detection is on
  88. private int inCounter = 0; // messages sequence numbers
  89. private int outCounter = 0;
  90. private IALG inMac, outMac; // if !null, use for integrity
  91. private CALG inCipher, outCipher; // if !null, use for confidentiality
  92. private IKeyAgreementParty serverHandler =
  93. KeyAgreementFactory.getPartyBInstance(Registry.SRP_SASL_KA);
  94. /** Our default source of randomness. */
  95. private PRNG prng = null;
  96. public SRPServer()
  97. {
  98. super(Registry.SASL_SRP_MECHANISM);
  99. }
  100. protected void initMechanism() throws SaslException
  101. {
  102. // TODO:
  103. // we must have a means to map a given username to a preferred
  104. // SRP hash algorithm; otherwise we end up using _always_ SHA.
  105. // for the time being get it from the mechanism properties map
  106. // and apply it for all users.
  107. final String mda = (String) properties.get(SRPRegistry.SRP_HASH);
  108. srp = SRP.instance(mda == null ? SRPRegistry.SRP_DEFAULT_DIGEST_NAME : mda);
  109. }
  110. protected void resetMechanism() throws SaslException
  111. {
  112. s = null;
  113. A = B = null;
  114. K = null;
  115. inMac = outMac = null;
  116. inCipher = outCipher = null;
  117. sid = null;
  118. }
  119. public byte[] evaluateResponse(final byte[] response) throws SaslException
  120. {
  121. switch (state)
  122. {
  123. case 0:
  124. if (response == null)
  125. return null;
  126. state++;
  127. return sendProtocolElements(response);
  128. case 1:
  129. if (! complete)
  130. {
  131. state++;
  132. return sendEvidence(response);
  133. }
  134. // else fall through
  135. default:
  136. throw new IllegalMechanismStateException("evaluateResponse()");
  137. }
  138. }
  139. protected byte[] engineUnwrap(final byte[] incoming, final int offset,
  140. final int len) throws SaslException
  141. {
  142. if (Configuration.DEBUG)
  143. log.entering(this.getClass().getName(), "engineUnwrap");
  144. if (inMac == null && inCipher == null)
  145. throw new IllegalStateException("connection is not protected");
  146. if (Configuration.DEBUG)
  147. log.fine("Incoming buffer (before security): "
  148. + Util.dumpString(incoming, offset, len));
  149. // at this point one, or both, of confidentiality and integrity protection
  150. // services are active.
  151. final byte[] result;
  152. try
  153. {
  154. if (inMac != null)
  155. { // integrity bytes are at the end of the stream
  156. final int macBytesCount = inMac.length();
  157. final int payloadLength = len - macBytesCount;
  158. final byte[] received_mac = new byte[macBytesCount];
  159. System.arraycopy(incoming, offset + payloadLength, received_mac, 0,
  160. macBytesCount);
  161. if (Configuration.DEBUG)
  162. log.fine("Got C (received MAC): " + Util.dumpString(received_mac));
  163. inMac.update(incoming, offset, payloadLength);
  164. if (replayDetection)
  165. {
  166. inCounter++;
  167. if (Configuration.DEBUG)
  168. log.fine("inCounter=" + String.valueOf(inCounter));
  169. inMac.update(new byte[] {
  170. (byte)(inCounter >>> 24),
  171. (byte)(inCounter >>> 16),
  172. (byte)(inCounter >>> 8),
  173. (byte) inCounter });
  174. }
  175. final byte[] computed_mac = inMac.doFinal();
  176. if (Configuration.DEBUG)
  177. log.fine("Computed MAC: " + Util.dumpString(computed_mac));
  178. if (! Arrays.equals(received_mac, computed_mac))
  179. throw new IntegrityException("engineUnwrap()");
  180. // deal with the payload, which can be either plain or encrypted
  181. if (inCipher != null)
  182. result = inCipher.doFinal(incoming, offset, payloadLength);
  183. else
  184. {
  185. result = new byte[payloadLength];
  186. System.arraycopy(incoming, offset, result, 0, result.length);
  187. }
  188. }
  189. else // no integrity protection; just confidentiality
  190. result = inCipher.doFinal(incoming, offset, len);
  191. }
  192. catch (IOException x)
  193. {
  194. if (x instanceof SaslException)
  195. throw (SaslException) x;
  196. throw new SaslException("engineUnwrap()", x);
  197. }
  198. if (Configuration.DEBUG)
  199. {
  200. log.fine("Incoming buffer (after security): " + Util.dumpString(result));
  201. log.exiting(this.getClass().getName(), "engineUnwrap");
  202. }
  203. return result;
  204. }
  205. protected byte[] engineWrap(final byte[] outgoing, final int offset,
  206. final int len) throws SaslException
  207. {
  208. if (Configuration.DEBUG)
  209. log.entering(this.getClass().getName(), "engineWrap");
  210. if (outMac == null && outCipher == null)
  211. throw new IllegalStateException("connection is not protected");
  212. if (Configuration.DEBUG)
  213. {
  214. log.fine("Outgoing buffer (before security) (hex): "
  215. + Util.dumpString(outgoing, offset, len));
  216. log.fine("Outgoing buffer (before security) (str): \""
  217. + new String(outgoing, offset, len) + "\"");
  218. }
  219. // at this point one, or both, of confidentiality and integrity protection
  220. // services are active.
  221. byte[] result;
  222. try
  223. {
  224. final ByteArrayOutputStream out = new ByteArrayOutputStream();
  225. if (outCipher != null)
  226. {
  227. result = outCipher.doFinal(outgoing, offset, len);
  228. if (Configuration.DEBUG)
  229. log.fine("Encoding c (encrypted plaintext): "
  230. + Util.dumpString(result));
  231. out.write(result);
  232. if (outMac != null)
  233. {
  234. outMac.update(result);
  235. if (replayDetection)
  236. {
  237. outCounter++;
  238. if (Configuration.DEBUG)
  239. log.fine("outCounter=" + outCounter);
  240. outMac.update(new byte[] {
  241. (byte)(outCounter >>> 24),
  242. (byte)(outCounter >>> 16),
  243. (byte)(outCounter >>> 8),
  244. (byte) outCounter });
  245. }
  246. final byte[] C = outMac.doFinal();
  247. out.write(C);
  248. if (Configuration.DEBUG)
  249. log.fine("Encoding C (integrity checksum): " + Util.dumpString(C));
  250. }
  251. // else ciphertext only; do nothing
  252. }
  253. else // no confidentiality; just integrity [+ replay detection]
  254. {
  255. if (Configuration.DEBUG)
  256. log.fine("Encoding p (plaintext): "
  257. + Util.dumpString(outgoing, offset, len));
  258. out.write(outgoing, offset, len);
  259. outMac.update(outgoing, offset, len);
  260. if (replayDetection)
  261. {
  262. outCounter++;
  263. if (Configuration.DEBUG)
  264. log.fine("outCounter=" + outCounter);
  265. outMac.update(new byte[] {
  266. (byte)(outCounter >>> 24),
  267. (byte)(outCounter >>> 16),
  268. (byte)(outCounter >>> 8),
  269. (byte) outCounter });
  270. }
  271. final byte[] C = outMac.doFinal();
  272. out.write(C);
  273. if (Configuration.DEBUG)
  274. log.fine("Encoding C (integrity checksum): " + Util.dumpString(C));
  275. }
  276. result = out.toByteArray();
  277. }
  278. catch (IOException x)
  279. {
  280. if (x instanceof SaslException)
  281. throw (SaslException) x;
  282. throw new SaslException("engineWrap()", x);
  283. }
  284. if (Configuration.DEBUG)
  285. log.exiting(this.getClass().getName(), "engineWrap");
  286. return result;
  287. }
  288. protected String getNegotiatedQOP()
  289. {
  290. if (inMac != null)
  291. {
  292. if (inCipher != null)
  293. return Registry.QOP_AUTH_CONF;
  294. return Registry.QOP_AUTH_INT;
  295. }
  296. return Registry.QOP_AUTH;
  297. }
  298. protected String getNegotiatedStrength()
  299. {
  300. if (inMac != null)
  301. {
  302. if (inCipher != null)
  303. return Registry.STRENGTH_HIGH;
  304. return Registry.STRENGTH_MEDIUM;
  305. }
  306. return Registry.STRENGTH_LOW;
  307. }
  308. protected String getNegotiatedRawSendSize()
  309. {
  310. return String.valueOf(rawSendSize);
  311. }
  312. protected String getReuse()
  313. {
  314. return Registry.REUSE_TRUE;
  315. }
  316. private byte[] sendProtocolElements(final byte[] input) throws SaslException
  317. {
  318. if (Configuration.DEBUG)
  319. {
  320. log.entering(this.getClass().getName(), "sendProtocolElements");
  321. log.fine("C: " + Util.dumpString(input));
  322. }
  323. // Client send U, I, sid, cn
  324. final InputBuffer frameIn = new InputBuffer(input);
  325. try
  326. {
  327. U = frameIn.getText(); // Extract username
  328. if (Configuration.DEBUG)
  329. log.fine("Got U (username): \"" + U + "\"");
  330. authorizationID = frameIn.getText(); // Extract authorisation ID
  331. if (Configuration.DEBUG)
  332. log.fine("Got I (userid): \"" + authorizationID + "\"");
  333. sid = frameIn.getEOS();
  334. if (Configuration.DEBUG)
  335. log.fine("Got sid (session ID): " + new String(sid));
  336. cn = frameIn.getOS();
  337. if (Configuration.DEBUG)
  338. log.fine("Got cn (client nonce): " + Util.dumpString(cn));
  339. cCB = frameIn.getEOS();
  340. if (Configuration.DEBUG)
  341. log.fine("Got cCB (client channel binding): " + Util.dumpString(cCB));
  342. }
  343. catch (IOException x)
  344. {
  345. if (x instanceof SaslException)
  346. throw (SaslException) x;
  347. throw new AuthenticationException("sendProtocolElements()", x);
  348. }
  349. // do/can we re-use?
  350. if (ServerStore.instance().isAlive(sid))
  351. {
  352. final SecurityContext ctx = ServerStore.instance().restoreSession(sid);
  353. srp = SRP.instance(ctx.getMdName());
  354. K = ctx.getK();
  355. cIV = ctx.getClientIV();
  356. sIV = ctx.getServerIV();
  357. replayDetection = ctx.hasReplayDetection();
  358. inCounter = ctx.getInCounter();
  359. outCounter = ctx.getOutCounter();
  360. inMac = ctx.getInMac();
  361. outMac = ctx.getOutMac();
  362. inCipher = ctx.getInCipher();
  363. outCipher = ctx.getOutCipher();
  364. if (sn == null || sn.length != 16)
  365. sn = new byte[16];
  366. getDefaultPRNG().nextBytes(sn);
  367. setupSecurityServices(false);
  368. final OutputBuffer frameOut = new OutputBuffer();
  369. try
  370. {
  371. frameOut.setScalar(1, 0xFF);
  372. frameOut.setOS(sn);
  373. frameOut.setEOS(channelBinding);
  374. }
  375. catch (IOException x)
  376. {
  377. if (x instanceof SaslException)
  378. throw (SaslException) x;
  379. throw new AuthenticationException("sendProtocolElements()", x);
  380. }
  381. final byte[] result = frameOut.encode();
  382. if (Configuration.DEBUG)
  383. {
  384. log.fine("Old session...");
  385. log.fine("S: " + Util.dumpString(result));
  386. log.fine(" sn = " + Util.dumpString(sn));
  387. log.fine(" sCB = " + Util.dumpString(channelBinding));
  388. log.exiting(this.getClass().getName(), "sendProtocolElements");
  389. }
  390. return result;
  391. }
  392. else
  393. { // new session
  394. authenticator.activate(properties);
  395. // -------------------------------------------------------------------
  396. final HashMap mapB = new HashMap();
  397. mapB.put(SRP6KeyAgreement.HASH_FUNCTION, srp.getAlgorithm());
  398. mapB.put(SRP6KeyAgreement.HOST_PASSWORD_DB, authenticator);
  399. try
  400. {
  401. serverHandler.init(mapB);
  402. OutgoingMessage out = new OutgoingMessage();
  403. out.writeString(U);
  404. IncomingMessage in = new IncomingMessage(out.toByteArray());
  405. out = serverHandler.processMessage(in);
  406. in = new IncomingMessage(out.toByteArray());
  407. N = in.readMPI();
  408. g = in.readMPI();
  409. s = in.readMPI().toByteArray();
  410. B = in.readMPI();
  411. }
  412. catch (KeyAgreementException x)
  413. {
  414. throw new SaslException("sendProtocolElements()", x);
  415. }
  416. // -------------------------------------------------------------------
  417. if (Configuration.DEBUG)
  418. {
  419. log.fine("Encoding N (modulus): " + Util.dump(N));
  420. log.fine("Encoding g (generator): " + Util.dump(g));
  421. log.fine("Encoding s (client's salt): " + Util.dumpString(s));
  422. log.fine("Encoding B (server ephemeral public key): " + Util.dump(B));
  423. }
  424. // The server creates an options list (L), which consists of a
  425. // comma-separated list of option strings that specify the security
  426. // service options the server supports.
  427. L = createL();
  428. if (Configuration.DEBUG)
  429. {
  430. log.fine("Encoding L (available options): \"" + L + "\"");
  431. log.fine("Encoding sIV (server IV): " + Util.dumpString(sIV));
  432. }
  433. final OutputBuffer frameOut = new OutputBuffer();
  434. try
  435. {
  436. frameOut.setScalar(1, 0x00);
  437. frameOut.setMPI(N);
  438. frameOut.setMPI(g);
  439. frameOut.setOS(s);
  440. frameOut.setMPI(B);
  441. frameOut.setText(L);
  442. }
  443. catch (IOException x)
  444. {
  445. if (x instanceof SaslException)
  446. throw (SaslException) x;
  447. throw new AuthenticationException("sendProtocolElements()", x);
  448. }
  449. final byte[] result = frameOut.encode();
  450. if (Configuration.DEBUG)
  451. {
  452. log.fine("New session...");
  453. log.fine("S: " + Util.dumpString(result));
  454. log.fine(" N = 0x" + N.toString(16));
  455. log.fine(" g = 0x" + g.toString(16));
  456. log.fine(" s = " + Util.dumpString(s));
  457. log.fine(" B = 0x" + B.toString(16));
  458. log.fine(" L = " + L);
  459. log.exiting(this.getClass().getName(), "sendProtocolElements");
  460. }
  461. return result;
  462. }
  463. }
  464. private byte[] sendEvidence(final byte[] input) throws SaslException
  465. {
  466. if (Configuration.DEBUG)
  467. {
  468. log.entering(this.getClass().getName(), "sendEvidence");
  469. log.fine("C: " + Util.dumpString(input));
  470. }
  471. // Client send A, M1, o, cIV
  472. final InputBuffer frameIn = new InputBuffer(input);
  473. final byte[] M1;
  474. try
  475. {
  476. A = frameIn.getMPI(); // Extract client's ephemeral public key
  477. if (Configuration.DEBUG)
  478. log.fine("Got A (client ephemeral public key): " + Util.dump(A));
  479. M1 = frameIn.getOS(); // Extract evidence
  480. if (Configuration.DEBUG)
  481. log.fine("Got M1 (client evidence): " + Util.dumpString(M1));
  482. o = frameIn.getText(); // Extract client's options list
  483. if (Configuration.DEBUG)
  484. log.fine("Got o (client chosen options): \"" + o + "\"");
  485. cIV = frameIn.getOS(); // Extract client's IV
  486. if (Configuration.DEBUG)
  487. log.fine("Got cIV (client IV): " + Util.dumpString(cIV));
  488. }
  489. catch (IOException x)
  490. {
  491. if (x instanceof SaslException)
  492. throw (SaslException) x;
  493. throw new AuthenticationException("sendEvidence()", x);
  494. }
  495. // Parse client's options and set security layer variables
  496. parseO(o);
  497. // ----------------------------------------------------------------------
  498. try
  499. {
  500. final OutgoingMessage out = new OutgoingMessage();
  501. out.writeMPI(A);
  502. final IncomingMessage in = new IncomingMessage(out.toByteArray());
  503. serverHandler.processMessage(in);
  504. K = serverHandler.getSharedSecret();
  505. }
  506. catch (KeyAgreementException x)
  507. {
  508. throw new SaslException("sendEvidence()", x);
  509. }
  510. // ----------------------------------------------------------------------
  511. if (Configuration.DEBUG)
  512. log.fine("K: " + Util.dumpString(K));
  513. final byte[] expected;
  514. try
  515. {
  516. expected = srp.generateM1(N, g, U, s, A, B, K, authorizationID, L, cn,
  517. cCB);
  518. }
  519. catch (UnsupportedEncodingException x)
  520. {
  521. throw new AuthenticationException("sendEvidence()", x);
  522. }
  523. // Verify client evidence
  524. if (! Arrays.equals(M1, expected))
  525. throw new AuthenticationException("M1 mismatch");
  526. setupSecurityServices(true);
  527. final byte[] M2;
  528. try
  529. {
  530. M2 = srp.generateM2(A, M1, K, U, authorizationID, o, sid, ttl, cIV,
  531. sIV, channelBinding);
  532. }
  533. catch (UnsupportedEncodingException x)
  534. {
  535. throw new AuthenticationException("sendEvidence()", x);
  536. }
  537. final OutputBuffer frameOut = new OutputBuffer();
  538. try
  539. {
  540. frameOut.setOS(M2);
  541. frameOut.setOS(sIV);
  542. frameOut.setEOS(sid);
  543. frameOut.setScalar(4, ttl);
  544. frameOut.setEOS(channelBinding);
  545. }
  546. catch (IOException x)
  547. {
  548. if (x instanceof SaslException)
  549. throw (SaslException) x;
  550. throw new AuthenticationException("sendEvidence()", x);
  551. }
  552. final byte[] result = frameOut.encode();
  553. if (Configuration.DEBUG)
  554. {
  555. log.fine("S: " + Util.dumpString(result));
  556. log.fine(" M2 = " + Util.dumpString(M2));
  557. log.fine(" sIV = " + Util.dumpString(sIV));
  558. log.fine(" sid = " + new String(sid));
  559. log.fine(" ttl = " + ttl);
  560. log.fine(" sCB = " + Util.dumpString(channelBinding));
  561. log.exiting(this.getClass().getName(), "sendEvidence");
  562. }
  563. return result;
  564. }
  565. private String createL()
  566. {
  567. if (Configuration.DEBUG)
  568. log.entering(this.getClass().getName(), "createL()");
  569. String s = (String) properties.get(SRPRegistry.SRP_MANDATORY);
  570. if (s == null)
  571. s = SRPRegistry.DEFAULT_MANDATORY;
  572. if (! SRPRegistry.MANDATORY_NONE.equals(s)
  573. && ! SRPRegistry.OPTION_REPLAY_DETECTION.equals(s)
  574. && ! SRPRegistry.OPTION_INTEGRITY.equals(s)
  575. && ! SRPRegistry.OPTION_CONFIDENTIALITY.equals(s))
  576. {
  577. if (Configuration.DEBUG)
  578. log.fine("Unrecognised mandatory option (" + s + "). Using default...");
  579. s = SRPRegistry.DEFAULT_MANDATORY;
  580. }
  581. mandatory = s;
  582. s = (String) properties.get(SRPRegistry.SRP_CONFIDENTIALITY);
  583. final boolean confidentiality = (s == null ? SRPRegistry.DEFAULT_CONFIDENTIALITY
  584. : Boolean.valueOf(s).booleanValue());
  585. s = (String) properties.get(SRPRegistry.SRP_INTEGRITY_PROTECTION);
  586. boolean integrity = (s == null ? SRPRegistry.DEFAULT_INTEGRITY
  587. : Boolean.valueOf(s).booleanValue());
  588. s = (String) properties.get(SRPRegistry.SRP_REPLAY_DETECTION);
  589. final boolean replayDetection = (s == null ? SRPRegistry.DEFAULT_REPLAY_DETECTION
  590. : Boolean.valueOf(s).booleanValue());
  591. final CPStringBuilder sb = new CPStringBuilder();
  592. sb.append(SRPRegistry.OPTION_SRP_DIGEST).append("=")
  593. .append(srp.getAlgorithm()).append(",");
  594. if (! SRPRegistry.MANDATORY_NONE.equals(mandatory))
  595. sb.append(SRPRegistry.OPTION_MANDATORY)
  596. .append("=").append(mandatory).append(",");
  597. if (replayDetection)
  598. {
  599. sb.append(SRPRegistry.OPTION_REPLAY_DETECTION).append(",");
  600. // if replay detection is on then force integrity protection
  601. integrity = true;
  602. }
  603. int i;
  604. if (integrity)
  605. {
  606. for (i = 0; i < SRPRegistry.INTEGRITY_ALGORITHMS.length; i++)
  607. sb.append(SRPRegistry.OPTION_INTEGRITY).append("=")
  608. .append(SRPRegistry.INTEGRITY_ALGORITHMS[i]).append(",");
  609. }
  610. if (confidentiality)
  611. {
  612. IBlockCipher cipher;
  613. for (i = 0; i < SRPRegistry.CONFIDENTIALITY_ALGORITHMS.length; i++)
  614. {
  615. cipher = CipherFactory.getInstance(SRPRegistry.CONFIDENTIALITY_ALGORITHMS[i]);
  616. if (cipher != null)
  617. sb.append(SRPRegistry.OPTION_CONFIDENTIALITY).append("=")
  618. .append(SRPRegistry.CONFIDENTIALITY_ALGORITHMS[i]).append(",");
  619. }
  620. }
  621. final String result = sb.append(SRPRegistry.OPTION_MAX_BUFFER_SIZE)
  622. .append("=").append(Registry.SASL_BUFFER_MAX_LIMIT)
  623. .toString();
  624. if (Configuration.DEBUG)
  625. log.exiting(this.getClass().getName(), "createL");
  626. return result;
  627. }
  628. // Parse client's options and set security layer variables
  629. private void parseO(final String o) throws AuthenticationException
  630. {
  631. this.replayDetection = false;
  632. boolean integrity = false;
  633. boolean confidentiality = false;
  634. String option;
  635. int i;
  636. final StringTokenizer st = new StringTokenizer(o.toLowerCase(), ",");
  637. while (st.hasMoreTokens())
  638. {
  639. option = st.nextToken();
  640. if (Configuration.DEBUG)
  641. log.fine("option: <" + option + ">");
  642. if (option.equals(SRPRegistry.OPTION_REPLAY_DETECTION))
  643. replayDetection = true;
  644. else if (option.startsWith(SRPRegistry.OPTION_INTEGRITY + "="))
  645. {
  646. if (integrity)
  647. throw new AuthenticationException(
  648. "Only one integrity algorithm may be chosen");
  649. option = option.substring(option.indexOf('=') + 1);
  650. if (Configuration.DEBUG)
  651. log.fine("algorithm: <" + option + ">");
  652. for (i = 0; i < SRPRegistry.INTEGRITY_ALGORITHMS.length; i++)
  653. {
  654. if (SRPRegistry.INTEGRITY_ALGORITHMS[i].equals(option))
  655. {
  656. chosenIntegrityAlgorithm = option;
  657. integrity = true;
  658. break;
  659. }
  660. }
  661. if (! integrity)
  662. throw new AuthenticationException("Unknown integrity algorithm: "
  663. + option);
  664. }
  665. else if (option.startsWith(SRPRegistry.OPTION_CONFIDENTIALITY + "="))
  666. {
  667. if (confidentiality)
  668. throw new AuthenticationException(
  669. "Only one confidentiality algorithm may be chosen");
  670. option = option.substring(option.indexOf('=') + 1);
  671. if (Configuration.DEBUG)
  672. log.fine("algorithm: <" + option + ">");
  673. for (i = 0; i < SRPRegistry.CONFIDENTIALITY_ALGORITHMS.length; i++)
  674. {
  675. if (SRPRegistry.CONFIDENTIALITY_ALGORITHMS[i].equals(option))
  676. {
  677. chosenConfidentialityAlgorithm = option;
  678. confidentiality = true;
  679. break;
  680. }
  681. }
  682. if (! confidentiality)
  683. throw new AuthenticationException("Unknown confidentiality algorithm: "
  684. + option);
  685. }
  686. else if (option.startsWith(SRPRegistry.OPTION_MAX_BUFFER_SIZE + "="))
  687. {
  688. final String maxBufferSize = option.substring(option.indexOf('=') + 1);
  689. try
  690. {
  691. rawSendSize = Integer.parseInt(maxBufferSize);
  692. if (rawSendSize > Registry.SASL_BUFFER_MAX_LIMIT
  693. || rawSendSize < 1)
  694. throw new AuthenticationException(
  695. "Illegal value for 'maxbuffersize' option");
  696. }
  697. catch (NumberFormatException x)
  698. {
  699. throw new AuthenticationException(
  700. SRPRegistry.OPTION_MAX_BUFFER_SIZE + "=" + maxBufferSize, x);
  701. }
  702. }
  703. }
  704. // check if client did the right thing
  705. if (replayDetection)
  706. {
  707. if (! integrity)
  708. throw new AuthenticationException(
  709. "Missing integrity protection algorithm but replay detection is chosen");
  710. }
  711. if (mandatory.equals(SRPRegistry.OPTION_REPLAY_DETECTION))
  712. {
  713. if (! replayDetection)
  714. throw new AuthenticationException(
  715. "Replay detection is mandatory but was not chosen");
  716. }
  717. if (mandatory.equals(SRPRegistry.OPTION_INTEGRITY))
  718. {
  719. if (! integrity)
  720. throw new AuthenticationException(
  721. "Integrity protection is mandatory but was not chosen");
  722. }
  723. if (mandatory.equals(SRPRegistry.OPTION_CONFIDENTIALITY))
  724. {
  725. if (! confidentiality)
  726. throw new AuthenticationException(
  727. "Confidentiality is mandatory but was not chosen");
  728. }
  729. int blockSize = 0;
  730. if (chosenConfidentialityAlgorithm != null)
  731. {
  732. final IBlockCipher cipher = CipherFactory.getInstance(chosenConfidentialityAlgorithm);
  733. if (cipher != null)
  734. blockSize = cipher.defaultBlockSize();
  735. else // should not happen
  736. throw new AuthenticationException("Confidentiality algorithm ("
  737. + chosenConfidentialityAlgorithm
  738. + ") not available");
  739. }
  740. sIV = new byte[blockSize];
  741. if (blockSize > 0)
  742. getDefaultPRNG().nextBytes(sIV);
  743. }
  744. private void setupSecurityServices(final boolean newSession)
  745. throws SaslException
  746. {
  747. complete = true; // signal end of authentication phase
  748. if (newSession)
  749. {
  750. outCounter = inCounter = 0;
  751. // instantiate cipher if confidentiality protection filter is active
  752. if (chosenConfidentialityAlgorithm != null)
  753. {
  754. if (Configuration.DEBUG)
  755. log.fine("Activating confidentiality protection filter");
  756. inCipher = CALG.getInstance(chosenConfidentialityAlgorithm);
  757. outCipher = CALG.getInstance(chosenConfidentialityAlgorithm);
  758. }
  759. // instantiate hmacs if integrity protection filter is active
  760. if (chosenIntegrityAlgorithm != null)
  761. {
  762. if (Configuration.DEBUG)
  763. log.fine("Activating integrity protection filter");
  764. inMac = IALG.getInstance(chosenIntegrityAlgorithm);
  765. outMac = IALG.getInstance(chosenIntegrityAlgorithm);
  766. }
  767. // generate a new sid if at least integrity is used
  768. sid = (inMac != null ? ServerStore.getNewSessionID() : new byte[0]);
  769. }
  770. else // same session new keys
  771. K = srp.generateKn(K, cn, sn);
  772. final KDF kdf = KDF.getInstance(K);
  773. // initialise in/out ciphers if confidentaility protection is used
  774. if (inCipher != null)
  775. {
  776. outCipher.init(kdf, sIV, Direction.FORWARD);
  777. inCipher.init(kdf, cIV, Direction.REVERSED);
  778. }
  779. // initialise in/out macs if integrity protection is used
  780. if (inMac != null)
  781. {
  782. outMac.init(kdf);
  783. inMac.init(kdf);
  784. }
  785. if (sid != null && sid.length != 0)
  786. { // update the security context and save in map
  787. if (Configuration.DEBUG)
  788. log.fine("Updating security context for sid = " + new String(sid));
  789. ServerStore.instance().cacheSession(ttl,
  790. new SecurityContext(srp.getAlgorithm(),
  791. sid,
  792. K,
  793. cIV,
  794. sIV,
  795. replayDetection,
  796. inCounter,
  797. outCounter,
  798. inMac, outMac,
  799. inCipher,
  800. outCipher));
  801. }
  802. }
  803. private PRNG getDefaultPRNG()
  804. {
  805. if (prng == null)
  806. prng = PRNG.getInstance();
  807. return prng;
  808. }
  809. }