summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/org/jboss/security/srp/SRPClientSession.java289
-rw-r--r--src/org/jboss/security/srp/SRPConf.java113
-rw-r--r--src/org/jboss/security/srp/SRPParameters.java150
-rw-r--r--src/org/jboss/security/srp/SRPPermission.java66
-rw-r--r--src/org/jboss/security/srp/SRPServerListener.java42
-rw-r--r--src/org/jboss/security/srp/SRPServerSession.java285
-rw-r--r--src/org/jboss/security/srp/SRPSessionKey.java78
-rw-r--r--src/org/jboss/security/srp/SRPVerifierStore.java115
-rw-r--r--src/org/jboss/security/srp/SerialObjectStore.java216
-rw-r--r--src/se/leap/leapclient/ProviderAPI.java23
10 files changed, 1374 insertions, 3 deletions
diff --git a/src/org/jboss/security/srp/SRPClientSession.java b/src/org/jboss/security/srp/SRPClientSession.java
new file mode 100644
index 00000000..bafc4842
--- /dev/null
+++ b/src/org/jboss/security/srp/SRPClientSession.java
@@ -0,0 +1,289 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.security.srp;
+
+import java.math.BigInteger;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+
+import org.jboss.crypto.CryptoUtil;
+import org.jboss.logging.Logger;
+
+/** The client side logic to the SRP protocol. The class is intended to be used
+ * with a SRPServerSession object via the SRPServerInterface. The SRP algorithm
+ * using these classes consists of:
+ *
+ * 1. Get server, SRPServerInterface server = (SRPServerInterface) Naming.lookup(...);
+ * 2. Get SRP parameters, SRPParameters params = server.getSRPParameters(username);
+ * 3. Create a client session, SRPClientSession client = new SRPClientSession(username,
+ * password, params);
+ * 4. Exchange public keys, byte[] A = client.exponential();
+ * byte[] B = server.init(username, A);
+ * 5. Exchange challenges, byte[] M1 = client.response(B);
+ * byte[] M2 = server.verify(username, M1);
+ * 6. Verify the server response, if( client.verify(M2) == false )
+ * throw new SecurityException("Failed to validate server reply");
+ * 7. Validation complete
+ *
+ * Note that these steps are stateful. They must be performed in order and a
+ * step cannot be repeated to update the session state.
+ *
+ * This product uses the 'Secure Remote Password' cryptographic
+ * authentication system developed by Tom Wu (tjw@CS.Stanford.EDU).
+ *
+ * @author Scott.Stark@jboss.org
+ * @version $Revision: 81038 $
+ */
+public class SRPClientSession
+{
+ private static Logger log = Logger.getLogger(SRPClientSession.class);
+ private SRPParameters params;
+ private BigInteger N;
+ private BigInteger g;
+ private BigInteger x;
+ private BigInteger v;
+ private byte[] s;
+ private BigInteger a;
+ private BigInteger A;
+ private byte[] K;
+ /** The M1 = H(H(N) xor H(g) | H(U) | s | A | B | K) hash */
+ private MessageDigest clientHash;
+ /** The M2 = H(A | M | K) hash */
+ private MessageDigest serverHash;
+
+ private static int A_LEN = 64;
+
+ /** Creates a new SRP server session object from the username, password
+ verifier,
+ @param username, the user ID
+ @param password, the user clear text password
+ @param params, the SRP parameters for the session
+ */
+ public SRPClientSession(String username, char[] password, SRPParameters params)
+ {
+ this(username, password, params, null);
+ }
+
+ /** Creates a new SRP server session object from the username, password
+ verifier,
+ @param username, the user ID
+ @param password, the user clear text password
+ @param params, the SRP parameters for the session
+ @param abytes, the random exponent used in the A public key. This must be
+ 8 bytes in length.
+ */
+ public SRPClientSession(String username, char[] password, SRPParameters params,
+ byte[] abytes)
+ {
+ try
+ {
+ // Initialize the secure random number and message digests
+ CryptoUtil.init();
+ }
+ catch(NoSuchAlgorithmException e)
+ {
+ }
+ this.params = params;
+ this.g = new BigInteger(1, params.g);
+ this.N = new BigInteger(1, params.N);
+ if( abytes != null )
+ {
+ if( 8*abytes.length != A_LEN )
+ throw new IllegalArgumentException("The abytes param must be "
+ +(A_LEN/8)+" in length, abytes.length="+abytes.length);
+ this.a = new BigInteger(abytes);
+ }
+
+ if( log.isTraceEnabled() )
+ log.trace("g: "+CryptoUtil.tob64(params.g));
+ // Calculate x = H(s | H(U | ':' | password))
+ byte[] xb = CryptoUtil.calculatePasswordHash(username, password, params.s);
+ if( log.isTraceEnabled() )
+ log.trace("x: "+CryptoUtil.tob64(xb));
+ this.x = new BigInteger(1, xb);
+ this.v = g.modPow(x, N); // g^x % N
+ if( log.isTraceEnabled() )
+ log.trace("v: "+CryptoUtil.tob64(v.toByteArray()));
+
+ serverHash = CryptoUtil.newDigest();
+ clientHash = CryptoUtil.newDigest();
+ // H(N)
+ byte[] hn = CryptoUtil.newDigest().digest(params.N);
+ if( log.isTraceEnabled() )
+ log.trace("H(N): "+CryptoUtil.tob64(hn));
+ // H(g)
+ byte[] hg = CryptoUtil.newDigest().digest(params.g);
+ if( log.isTraceEnabled() )
+ log.trace("H(g): "+CryptoUtil.tob64(hg));
+ // clientHash = H(N) xor H(g)
+ byte[] hxg = CryptoUtil.xor(hn, hg, 20);
+ if( log.isTraceEnabled() )
+ log.trace("H(N) xor H(g): "+CryptoUtil.tob64(hxg));
+ clientHash.update(hxg);
+ if( log.isTraceEnabled() )
+ {
+ MessageDigest tmp = CryptoUtil.copy(clientHash);
+ log.trace("H[H(N) xor H(g)]: "+CryptoUtil.tob64(tmp.digest()));
+ }
+ // clientHash = H(N) xor H(g) | H(U)
+ clientHash.update(CryptoUtil.newDigest().digest(username.getBytes()));
+ if( log.isTraceEnabled() )
+ {
+ MessageDigest tmp = CryptoUtil.copy(clientHash);
+ log.trace("H[H(N) xor H(g) | H(U)]: "+CryptoUtil.tob64(tmp.digest()));
+ }
+ // clientHash = H(N) xor H(g) | H(U) | s
+ clientHash.update(params.s);
+ if( log.isTraceEnabled() )
+ {
+ MessageDigest tmp = CryptoUtil.copy(clientHash);
+ log.trace("H[H(N) xor H(g) | H(U) | s]: "+CryptoUtil.tob64(tmp.digest()));
+ }
+ K = null;
+ }
+
+ /**
+ * @returns The exponential residue (parameter A) to be sent to the server.
+ */
+ public byte[] exponential()
+ {
+ byte[] Abytes = null;
+ if(A == null)
+ {
+ /* If the random component of A has not been specified use a random
+ number */
+ if( a == null )
+ {
+ BigInteger one = BigInteger.ONE;
+ do
+ {
+ a = new BigInteger(A_LEN, CryptoUtil.getPRNG());
+ } while(a.compareTo(one) <= 0);
+ }
+ A = g.modPow(a, N);
+ Abytes = CryptoUtil.trim(A.toByteArray());
+ // clientHash = H(N) xor H(g) | H(U) | A
+ clientHash.update(Abytes);
+ if( log.isTraceEnabled() )
+ {
+ MessageDigest tmp = CryptoUtil.copy(clientHash);
+ log.trace("H[H(N) xor H(g) | H(U) | s | A]: "+CryptoUtil.tob64(tmp.digest()));
+ }
+ // serverHash = A
+ serverHash.update(Abytes);
+ }
+ return Abytes;
+ }
+
+ /**
+ @returns M1 = H(H(N) xor H(g) | H(U) | s | A | B | K)
+ @exception NoSuchAlgorithmException thrown if the session key
+ MessageDigest algorithm cannot be found.
+ */
+ public byte[] response(byte[] Bbytes) throws NoSuchAlgorithmException
+ {
+ // clientHash = H(N) xor H(g) | H(U) | s | A | B
+ clientHash.update(Bbytes);
+ if( log.isTraceEnabled() )
+ {
+ MessageDigest tmp = CryptoUtil.copy(clientHash);
+ log.trace("H[H(N) xor H(g) | H(U) | s | A | B]: "+CryptoUtil.tob64(tmp.digest()));
+ }
+ // Calculate u as the first 32 bits of H(B)
+ byte[] hB = CryptoUtil.newDigest().digest(Bbytes);
+ byte[] ub =
+ {hB[0], hB[1], hB[2], hB[3]};
+ // Calculate S = (B - g^x) ^ (a + u * x) % N
+ BigInteger B = new BigInteger(1, Bbytes);
+ if( log.isTraceEnabled() )
+ log.trace("B: "+CryptoUtil.tob64(B.toByteArray()));
+ if( B.compareTo(v) < 0 )
+ B = B.add(N);
+ if( log.isTraceEnabled() )
+ log.trace("B': "+CryptoUtil.tob64(B.toByteArray()));
+ if( log.isTraceEnabled() )
+ log.trace("v: "+CryptoUtil.tob64(v.toByteArray()));
+ BigInteger u = new BigInteger(1, ub);
+ if( log.isTraceEnabled() )
+ log.trace("u: "+CryptoUtil.tob64(u.toByteArray()));
+ BigInteger B_v = B.subtract(v);
+ if( log.isTraceEnabled() )
+ log.trace("B - v: "+CryptoUtil.tob64(B_v.toByteArray()));
+ BigInteger a_ux = a.add(u.multiply(x));
+ if( log.isTraceEnabled() )
+ log.trace("a + u * x: "+CryptoUtil.tob64(a_ux.toByteArray()));
+ BigInteger S = B_v.modPow(a_ux, N);
+ if( log.isTraceEnabled() )
+ log.trace("S: "+CryptoUtil.tob64(S.toByteArray()));
+ // K = SessionHash(S)
+ MessageDigest sessionDigest = MessageDigest.getInstance(params.hashAlgorithm);
+ K = sessionDigest.digest(S.toByteArray());
+ if( log.isTraceEnabled() )
+ log.trace("K: "+CryptoUtil.tob64(K));
+ // clientHash = H(N) xor H(g) | H(U) | A | B | K
+ clientHash.update(K);
+ byte[] M1 = clientHash.digest();
+ if( log.isTraceEnabled() )
+ log.trace("M1: H[H(N) xor H(g) | H(U) | s | A | B | K]: "+CryptoUtil.tob64(M1));
+ serverHash.update(M1);
+ serverHash.update(K);
+ if( log.isTraceEnabled() )
+ {
+ MessageDigest tmp = CryptoUtil.copy(serverHash);
+ log.trace("H[A | M1 | K]: "+CryptoUtil.tob64(tmp.digest()));
+ }
+ return M1;
+ }
+ /**
+ * @param M2 The server's response to the client's challenge
+ * @returns True if and only if the server's response was correct.
+ */
+ public boolean verify(byte[] M2)
+ {
+ // M2 = H(A | M1 | K)
+ byte[] myM2 = serverHash.digest();
+ boolean valid = Arrays.equals(M2, myM2);
+ if( log.isTraceEnabled() )
+ {
+ log.trace("verify serverM2: "+CryptoUtil.tob64(M2));
+ log.trace("verify M2: "+CryptoUtil.tob64(myM2));
+ }
+ return valid;
+ }
+
+ /** Returns the negotiated session K, K = SHA_Interleave(S)
+ @return the private session K byte[]
+ @throws SecurityException - if the current thread does not have an
+ getSessionKey SRPPermission.
+ */
+ public byte[] getSessionKey() throws SecurityException
+ {
+ SecurityManager sm = System.getSecurityManager();
+ if( sm != null )
+ {
+ SRPPermission p = new SRPPermission("getSessionKey");
+ sm.checkPermission(p);
+ }
+ return K;
+ }
+}
diff --git a/src/org/jboss/security/srp/SRPConf.java b/src/org/jboss/security/srp/SRPConf.java
new file mode 100644
index 00000000..c9b69428
--- /dev/null
+++ b/src/org/jboss/security/srp/SRPConf.java
@@ -0,0 +1,113 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.security.srp;
+
+import java.math.BigInteger;
+
+import org.jboss.crypto.CryptoUtil;
+
+/** A port of the libsrp/t_conf.c predefined constants for the N & g parameters
+of the SRP algorithm. It contains a collection of "good" primes for N and the
+corresponding the corresponding generator g.
+
+This product includes software developed by Tom Wu and Eugene
+Jhong for the SRP Distribution (http://srp.stanford.edu/srp/).
+
+@author Scott.Stark@jboss.org
+@version $Revision: 81038 $
+*/
+public class SRPConf
+{
+ /* Master builtin parameter storage object */
+ public static class SRPParams
+ {
+ String modb64;
+ String genb64;
+ String comment;
+ BigInteger N;
+ BigInteger g;
+ SRPParams(String modb64, String genb64, String comment)
+ {
+ this.modb64 = modb64;
+ this.genb64 = genb64;
+ this.comment = comment;
+ }
+ public byte[] Nbytes()
+ {
+ return CryptoUtil.fromb64(modb64);
+ }
+ public byte[] gbytes()
+ {
+ return CryptoUtil.fromb64(genb64);
+ }
+ public BigInteger N()
+ {
+ if( N == null )
+ N = new BigInteger(1, CryptoUtil.fromb64(modb64));
+ return N;
+ }
+ public BigInteger g()
+ {
+ if( g == null )
+ g = new BigInteger(1, CryptoUtil.fromb64(genb64));
+ return g;
+ }
+ public String getComment()
+ {
+ return comment;
+ }
+ }
+
+ static SRPParams[] pre_params = {
+ new SRPParams("3Kn/YYiomHkFkfM1x4kayR125MGkzpLUDy3y14FlTMwYnhZkjrMXnoC2TcFAecNlU5kFzgcpKYUbBOPZFRtyf3",
+ "2", null),
+ new SRPParams("CbDP.jR6YD6wAj2ByQWxQxQZ7.9J9xkn2.Uqb3zVm16vQyizprhBw9hi80psatZ8k54vwZfiIeEHZVsDnyqeWSSIpWso.wh5GD4OFgdhVI3",
+ "2", null),
+ new SRPParams("iqJ7nFZ4bGCRjE1F.FXEwL085Zb0kLM2TdHDaVVCdq0cKxvnH/0FLskJTKlDtt6sDl89dc//aEULTVFGtcbA/tDzc.bnFE.DWthQOu2n2JwKjgKfgCR2lZFWXdnWmoOh",
+ "2", null),
+ new SRPParams("///////////93zgY8MZ2DCJ6Oek0t1pHAG9E28fdp7G22xwcEnER8b5A27cED0JTxvKPiyqwGnimAmfjybyKDq/XDMrjKS95v8MrTc9UViRqJ4BffZes8F//////////",
+ "7", "oakley prime 1"),
+ new SRPParams("Ewl2hcjiutMd3Fu2lgFnUXWSc67TVyy2vwYCKoS9MLsrdJVT9RgWTCuEqWJrfB6uE3LsE9GkOlaZabS7M29sj5TnzUqOLJMjiwEzArfiLr9WbMRANlF68N5AVLcPWvNx6Zjl3m5Scp0BzJBz9TkgfhzKJZ.WtP3Mv/67I/0wmRZ",
+ "2", null),
+ new SRPParams("F//////////oG/QeY5emZJ4ncABWDmSqIa2JWYAPynq0Wk.fZiJco9HIWXvZZG4tU.L6RFDEaCRC2iARV9V53TFuJLjRL72HUI5jNPYNdx6z4n2wQOtxMiB/rosz0QtxUuuQ/jQYP.bhfya4NnB7.P9A6PHxEPJWV//////////",
+ "5", "oakley prime 2"),
+ new SRPParams("3NUKQ2Re4P5BEK0TLg2dX3gETNNNECPoe92h4OVMaDn3Xo/0QdjgG/EvM.hiVV1BdIGklSI14HA38Mpe5k04juR5/EXMU0r1WtsLhNXwKBlf2zEfoOh0zVmDvqInpU695f29Iy7sNW3U5RIogcs740oUp2Kdv5wuITwnIx84cnO.e467/IV1lPnvMCr0pd1dgS0a.RV5eBJr03Q65Xy61R",
+ "2", null),
+ new SRPParams("dUyyhxav9tgnyIg65wHxkzkb7VIPh4o0lkwfOKiPp4rVJrzLRYVBtb76gKlaO7ef5LYGEw3G.4E0jbMxcYBetDy2YdpiP/3GWJInoBbvYHIRO9uBuxgsFKTKWu7RnR7yTau/IrFTdQ4LY/q.AvoCzMxV0PKvD9Odso/LFIItn8PbTov3VMn/ZEH2SqhtpBUkWtmcIkEflhX/YY/fkBKfBbe27/zUaKUUZEUYZ2H2nlCL60.JIPeZJSzsu/xHDVcx",
+ "2", null),
+ new SRPParams("2iQzj1CagQc/5ctbuJYLWlhtAsPHc7xWVyCPAKFRLWKADpASkqe9djWPFWTNTdeJtL8nAhImCn3Sr/IAdQ1FrGw0WvQUstPx3FO9KNcXOwisOQ1VlL.gheAHYfbYyBaxXL.NcJx9TUwgWDT0hRzFzqSrdGGTN3FgSTA1v4QnHtEygNj3eZ.u0MThqWUaDiP87nqha7XnT66bkTCkQ8.7T8L4KZjIImrNrUftedTTBi.WCi.zlrBxDuOM0da0JbUkQlXqvp0yvJAPpC11nxmmZOAbQOywZGmu9nhZNuwTlxjfIro0FOdthaDTuZRL9VL7MRPUDo/DQEyW.d4H.UIlzp",
+ "2", null),
+ };
+
+ public int getPredefinedCount()
+ {
+ return pre_params.length;
+ }
+ public static SRPParams getPredefinedParams(int n)
+ {
+ return pre_params[n];
+ }
+ public static SRPParams getDefaultParams()
+ {
+ return pre_params[6];
+ }
+}
diff --git a/src/org/jboss/security/srp/SRPParameters.java b/src/org/jboss/security/srp/SRPParameters.java
new file mode 100644
index 00000000..43e2dad2
--- /dev/null
+++ b/src/org/jboss/security/srp/SRPParameters.java
@@ -0,0 +1,150 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.security.srp;
+
+import java.io.Serializable;
+import java.util.Arrays;
+
+import org.jboss.crypto.CryptoUtil;
+
+/** The RFC2945 algorithm session parameters that the client and server
+agree to use. In addition to the base RFC2945 parameters, one can choose an
+alternate hash algorithm for the private session key.
+
+@author Scott.Stark@jboss.org
+@version $Revision: 81038 $
+*/
+public class SRPParameters implements Cloneable, Serializable
+{
+ /** The serial version ID.
+ * @since 1.2.4.1
+ */
+ private static final long serialVersionUID = 6438772808805276693L;
+
+ /** The algorithm safe-prime modulus */
+ public final byte[] N;
+ /** The algorithm primitive generator */
+ public final byte[] g;
+ /** The random password salt originally used to verify the password */
+ public final byte[] s;
+ /** The algorithm to hash the session key to produce K. To be consistent
+ with the RFC2945 description this must be SHA_Interleave as implemented
+ by the JBossSX security provider. For compatibility with earlier JBossSX
+ SRP releases the algorithm must be SHA_ReverseInterleave. This name is
+ passed to java.security.MessageDigest.getInstance(). */
+ public final String hashAlgorithm;
+ /** The algorithm to use for any encryption of data.
+ */
+ public final String cipherAlgorithm;
+ /** The cipher intialization vector bytes
+ */
+ public byte[] cipherIV;
+
+ /** Creates a new instance of SRPParameters */
+ public SRPParameters(byte[] N, byte[] g, byte[] s)
+ {
+ this(N, g, s, "SHA_Interleave", null);
+ }
+ public SRPParameters(byte[] N, byte[] g, byte[] s, String hashAlgorithm)
+ {
+ this(N, g, s, hashAlgorithm, null);
+ }
+ public SRPParameters(byte[] N, byte[] g, byte[] s, String hashAlgorithm,
+ String cipherAlgorithm)
+ {
+ this(N, g, s, hashAlgorithm, cipherAlgorithm, null);
+ }
+ public SRPParameters(byte[] N, byte[] g, byte[] s, String hashAlgorithm,
+ String cipherAlgorithm, byte[] cipherIV)
+ {
+ this.N = N;
+ this.g = g;
+ this.s = s;
+ if( hashAlgorithm == null )
+ hashAlgorithm = "SHA_Interleave";
+ this.hashAlgorithm = hashAlgorithm;
+ this.cipherAlgorithm = cipherAlgorithm;
+ this.cipherIV = cipherIV;
+ }
+
+ public Object clone()
+ {
+ Object clone = null;
+ try
+ {
+ clone = super.clone();
+ }
+ catch(CloneNotSupportedException e)
+ {
+ }
+ return clone;
+ }
+
+ public int hashCode()
+ {
+ int hashCode = hashAlgorithm.hashCode();
+ for(int i = 0; i < N.length; i ++)
+ hashCode += N[i];
+ for(int i = 0; i < g.length; i ++)
+ hashCode += g[i];
+ for(int i = 0; i < s.length; i ++)
+ hashCode += s[i];
+ return hashCode;
+ }
+
+ public boolean equals(Object obj)
+ {
+ boolean equals = false;
+ if( obj instanceof SRPParameters )
+ {
+ SRPParameters p = (SRPParameters) obj;
+ equals = hashAlgorithm.equals(p.hashAlgorithm);
+ if( equals == true )
+ equals = Arrays.equals(N, p.N);
+ if( equals == true )
+ equals = Arrays.equals(g, p.g);
+ if( equals == true )
+ equals = Arrays.equals(s, p.s);
+ }
+ return equals;
+ }
+
+ public String toString()
+ {
+ StringBuffer tmp = new StringBuffer(super.toString());
+ tmp.append('{');
+ tmp.append("N: ");
+ tmp.append(CryptoUtil.encodeBase64(N));
+ tmp.append("|g: ");
+ tmp.append(CryptoUtil.encodeBase64(g));
+ tmp.append("|s: ");
+ tmp.append(CryptoUtil.encodeBase64(s));
+ tmp.append("|hashAlgorithm: ");
+ tmp.append(hashAlgorithm);
+ tmp.append("|cipherAlgorithm: ");
+ tmp.append(cipherAlgorithm);
+ tmp.append("|cipherIV: ");
+ tmp.append(cipherIV);
+ tmp.append('}');
+ return tmp.toString();
+ }
+}
diff --git a/src/org/jboss/security/srp/SRPPermission.java b/src/org/jboss/security/srp/SRPPermission.java
new file mode 100644
index 00000000..d1a77d64
--- /dev/null
+++ b/src/org/jboss/security/srp/SRPPermission.java
@@ -0,0 +1,66 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.security.srp;
+
+import java.security.BasicPermission;
+
+/** A custom permission class for protecting access to sensitive SRP information
+like the private session key and private key.
+
+The following table lists all the possible SRPPermission target names,
+and for each provides a description of what the permission allows
+and a discussion of the risks of granting code the permission.
+<table border=1 cellpadding=5>
+ <tr>
+ <th>Permission Target Name</th>
+ <th>What the Permission Allows</th>
+ <th>Risks of Allowing this Permission</th>
+ </tr>
+
+ <tr>
+ <td>getSessionKey</td>
+ <td>Access the private SRP session key</td>
+ <td>This provides access the the private session key that results from
+the SRP negiotation. Access to this key will allow one to encrypt/decrypt msgs
+that have been encrypted with the session key.
+ </td>
+ </tr>
+
+</table>
+
+@author Scott.Stark@jboss.org
+@version $Revision: 81038 $
+*/
+public class SRPPermission extends BasicPermission
+{
+
+ /** Creates new SRPPermission */
+ public SRPPermission(String name)
+ {
+ super(name);
+ }
+ public SRPPermission(String name, String actions)
+ {
+ super(name, actions);
+ }
+
+}
diff --git a/src/org/jboss/security/srp/SRPServerListener.java b/src/org/jboss/security/srp/SRPServerListener.java
new file mode 100644
index 00000000..a9e22cbb
--- /dev/null
+++ b/src/org/jboss/security/srp/SRPServerListener.java
@@ -0,0 +1,42 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.security.srp;
+
+/** A callback interface for SRP session events.
+
+@author Scott.Stark@jboss.org
+@version $Revision: 81038 $
+*/
+public interface SRPServerListener
+{
+ /** Called when a user has successfully completed the SRP handshake and any auxillary
+ * challenge verification.
+ * @param key, the {username, sessionID} pair
+ * @param session, the server SRP session information
+ */
+ public void verifiedUser(SRPSessionKey key, SRPServerSession session);
+ /** Called when a user requests that a session be closed
+ *
+ * @param key, the {username, sessionID} pair
+ */
+ public void closedUserSession(SRPSessionKey key);
+}
diff --git a/src/org/jboss/security/srp/SRPServerSession.java b/src/org/jboss/security/srp/SRPServerSession.java
new file mode 100644
index 00000000..11dc6d4c
--- /dev/null
+++ b/src/org/jboss/security/srp/SRPServerSession.java
@@ -0,0 +1,285 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.security.srp;
+
+import java.io.Serializable;
+import java.math.BigInteger;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+
+import org.jboss.logging.Logger;
+import org.jboss.crypto.CryptoUtil;
+
+/** The server side logic to the SRP protocol. The class is the server side
+ equivalent of the SRPClientSession object. An implementation of
+ SRPServerInterface creates an SRPServerSession on the start of a login
+ session.
+
+ The client side algorithm using these classes consists of:
+
+ 1. Get server, SRPServerInterface server = (SRPServerInterface) Naming.lookup(...);
+ 2. Get SRP parameters, SRPParameters params = server.getSRPParameters(username);
+ 3. Create a client session, SRPClientSession client = new SRPClientSession(username, password, params);
+ 4. Exchange public keys, byte[] A = client.exponential();
+ byte[] B = server.init(username, A);
+ 5. Exchange challenges, byte[] M1 = client.response(B);
+ byte[] M2 = server.verify(username, M1);
+ 6. Verify the server response, if( client.verify(M2) == false )
+ throw new SecurityException("Failed to validate server reply");
+ 7. Validation complete
+
+ Note that these steps are stateful. They must be performed in order and a
+ step cannot be repeated to update the session state.
+
+ This product uses the 'Secure Remote Password' cryptographic
+ authentication system developed by Tom Wu (tjw@CS.Stanford.EDU).
+
+ @author Scott.Stark@jboss.org
+ @version $Revision: 81038 $
+ */
+public class SRPServerSession implements Serializable
+{
+ /** The serial version ID
+ @since 1.6
+ */
+ static final long serialVersionUID = -2448005747721323704L;
+ private static int B_LEN = 64; // 64 bits for 'b'
+ private static Logger log = Logger.getLogger(SRPServerSession.class);
+
+ private SRPParameters params;
+ private BigInteger N;
+ private BigInteger g;
+ private BigInteger v;
+ private BigInteger b;
+ private BigInteger B;
+ private byte[] K;
+ /** The M1 = H(H(N) xor H(g) | H(U) | s | A | B | K) hash */
+ private transient MessageDigest clientHash;
+ private byte[] M1;
+ /** The M2 = H(A | M | K) hash */
+ private transient MessageDigest serverHash;
+ private byte[] M2;
+
+ /** Creates a new SRP server session object from the username, password
+ verifier, and session parameters.
+ @param username, the user ID
+ @param vb, the password verifier byte sequence
+ @param params, the SRP parameters for the session
+ */
+ public SRPServerSession(String username, byte[] vb, SRPParameters params)
+ {
+ this.params = params;
+ this.v = new BigInteger(1, vb);
+ this.g = new BigInteger(1, params.g);
+ this.N = new BigInteger(1, params.N);
+ if( log.isTraceEnabled() )
+ log.trace("g: "+CryptoUtil.tob64(params.g));
+ if( log.isTraceEnabled() )
+ log.trace("v: "+CryptoUtil.tob64(vb));
+ serverHash = CryptoUtil.newDigest();
+ clientHash = CryptoUtil.newDigest();
+ // H(N)
+ byte[] hn = CryptoUtil.newDigest().digest(params.N);
+ if( log.isTraceEnabled() )
+ log.trace("H(N): "+CryptoUtil.tob64(hn));
+ // H(g)
+ byte[] hg = CryptoUtil.newDigest().digest(params.g);
+ if( log.isTraceEnabled() )
+ log.trace("H(g): "+CryptoUtil.tob64(hg));
+ // clientHash = H(N) xor H(g)
+ byte[] hxg = CryptoUtil.xor(hn, hg, 20);
+ if( log.isTraceEnabled() )
+ log.trace("H(N) xor H(g): "+CryptoUtil.tob64(hxg));
+ clientHash.update(hxg);
+ if( log.isTraceEnabled() )
+ {
+ MessageDigest tmp = CryptoUtil.copy(clientHash);
+ log.trace("H[H(N) xor H(g)]: "+CryptoUtil.tob64(tmp.digest()));
+ }
+ // clientHash = H(N) xor H(g) | H(U)
+ clientHash.update(CryptoUtil.newDigest().digest(username.getBytes()));
+ if( log.isTraceEnabled() )
+ {
+ MessageDigest tmp = CryptoUtil.copy(clientHash);
+ log.trace("H[H(N) xor H(g) | H(U)]: "+CryptoUtil.tob64(tmp.digest()));
+ }
+ // clientHash = H(N) xor H(g) | H(U) | s
+ clientHash.update(params.s);
+ if( log.isTraceEnabled() )
+ {
+ MessageDigest tmp = CryptoUtil.copy(clientHash);
+ log.trace("H[H(N) xor H(g) | H(U) | s]: "+CryptoUtil.tob64(tmp.digest()));
+ }
+ K = null;
+ }
+
+ /**
+ * @returns The user's password salt
+ */
+ public SRPParameters getParameters()
+ {
+ return params;
+ }
+
+ /**
+ * @returns The exponential residue (parameter B) to be sent to the
+ * client.
+ */
+ public byte[] exponential()
+ {
+ if(B == null)
+ {
+ BigInteger one = BigInteger.valueOf(1);
+ do
+ {
+ b = new BigInteger(B_LEN, CryptoUtil.getPRNG());
+ } while(b.compareTo(one) <= 0);
+ B = v.add(g.modPow(b, N));
+ if(B.compareTo(N) >= 0)
+ B = B.subtract(N);
+ }
+ return CryptoUtil.trim(B.toByteArray());
+ }
+
+ /**
+ @param ab The client's exponential (parameter A).
+ @returns The secret shared session K between client and server
+ @exception NoSuchAlgorithmException thrown if the session key
+ MessageDigest algorithm cannot be found.
+ */
+ public void buildSessionKey(byte[] ab) throws NoSuchAlgorithmException
+ {
+ if( log.isTraceEnabled() )
+ log.trace("A: "+CryptoUtil.tob64(ab));
+ byte[] nb = CryptoUtil.trim(B.toByteArray());
+ // clientHash = H(N) xor H(g) | H(U) | s | A
+ clientHash.update(ab);
+ if( log.isTraceEnabled() )
+ {
+ MessageDigest tmp = CryptoUtil.copy(clientHash);
+ log.trace("H[H(N) xor H(g) | H(U) | s | A]: "+CryptoUtil.tob64(tmp.digest()));
+ }
+ // clientHash = H(N) xor H(g) | H(U) | A | B
+ clientHash.update(nb);
+ if( log.isTraceEnabled() )
+ {
+ MessageDigest tmp = CryptoUtil.copy(clientHash);
+ log.trace("H[H(N) xor H(g) | H(U) | s | A | B]: "+CryptoUtil.tob64(tmp.digest()));
+ }
+ // serverHash = A
+ serverHash.update(ab);
+ // Calculate u as the first 32 bits of H(B)
+ byte[] hB = CryptoUtil.newDigest().digest(nb);
+ byte[] ub =
+ {hB[0], hB[1], hB[2], hB[3]};
+ // Calculate S = (A * v^u) ^ b % N
+ BigInteger A = new BigInteger(1, ab);
+ if( log.isTraceEnabled() )
+ log.trace("A: "+CryptoUtil.tob64(A.toByteArray()));
+ if( log.isTraceEnabled() )
+ log.trace("B: "+CryptoUtil.tob64(B.toByteArray()));
+ if( log.isTraceEnabled() )
+ log.trace("v: "+CryptoUtil.tob64(v.toByteArray()));
+ BigInteger u = new BigInteger(1, ub);
+ if( log.isTraceEnabled() )
+ log.trace("u: "+CryptoUtil.tob64(u.toByteArray()));
+ BigInteger A_v2u = A.multiply(v.modPow(u, N)).mod(N);
+ if( log.isTraceEnabled() )
+ log.trace("A * v^u: "+CryptoUtil.tob64(A_v2u.toByteArray()));
+ BigInteger S = A_v2u.modPow(b, N);
+ if( log.isTraceEnabled() )
+ log.trace("S: "+CryptoUtil.tob64(S.toByteArray()));
+ // K = SessionHash(S)
+ MessageDigest sessionDigest = MessageDigest.getInstance(params.hashAlgorithm);
+ K = sessionDigest.digest(S.toByteArray());
+ if( log.isTraceEnabled() )
+ log.trace("K: "+CryptoUtil.tob64(K));
+ // clientHash = H(N) xor H(g) | H(U) | A | B | K
+ clientHash.update(K);
+ if( log.isTraceEnabled() )
+ {
+ MessageDigest tmp = CryptoUtil.copy(clientHash);
+ log.trace("H[H(N) xor H(g) | H(U) | s | A | B | K]: "+CryptoUtil.tob64(tmp.digest()));
+ }
+ }
+
+ /** Returns the negotiated session K, K = SessionHash(S)
+ @return the private session K byte[]
+ @throws SecurityException - if the current thread does not have an
+ getSessionKey SRPPermission.
+ */
+ public byte[] getSessionKey() throws SecurityException
+ {
+ SecurityManager sm = System.getSecurityManager();
+ if( sm != null )
+ {
+ SRPPermission p = new SRPPermission("getSessionKey");
+ sm.checkPermission(p);
+ }
+ return K;
+ }
+
+ /**
+ @returns M2 = H(A | M | K)
+ */
+ public byte[] getServerResponse()
+ {
+ if( M2 == null )
+ M2 = serverHash.digest();
+ return M2;
+ }
+ public byte[] getClientResponse()
+ {
+ return M1;
+ }
+
+ /**
+ * @param resp The client's response to the server's challenge
+ * @returns True if and only if the client's response was correct.
+ */
+ public boolean verify(byte[] clientM1)
+ {
+ boolean valid = false;
+ // M1 = H(H(N) xor H(g) | H(U) | A | B | K)
+ M1 = clientHash.digest();
+ if( log.isTraceEnabled() )
+ {
+ log.trace("verify M1: "+CryptoUtil.tob64(M1));
+ log.trace("verify clientM1: "+CryptoUtil.tob64(clientM1));
+ }
+ if( Arrays.equals(clientM1, M1) )
+ {
+ // serverHash = A | M
+ serverHash.update(M1);
+ // serverHash = A | M | K
+ serverHash.update(K);
+ if( log.isTraceEnabled() )
+ {
+ MessageDigest tmp = CryptoUtil.copy(serverHash);
+ log.trace("H(A | M1 | K)"+CryptoUtil.tob64(tmp.digest()));
+ }
+ valid = true;
+ }
+ return valid;
+ }
+}
diff --git a/src/org/jboss/security/srp/SRPSessionKey.java b/src/org/jboss/security/srp/SRPSessionKey.java
new file mode 100644
index 00000000..e0f21310
--- /dev/null
+++ b/src/org/jboss/security/srp/SRPSessionKey.java
@@ -0,0 +1,78 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.security.srp;
+
+import java.io.Serializable;
+
+/* An encapsulation of an SRP username and session id.
+ * @author Scott.Stark@jboss.org
+ * @version $Revision: 81038 $
+ */
+public class SRPSessionKey implements Serializable
+{
+ private static final long serialVersionUID = -7783783206948014409L;
+ public static final Integer NO_SESSION_ID = new Integer(0);
+ private String username;
+ private int sessionID;
+
+ public SRPSessionKey(String username)
+ {
+ this(username, NO_SESSION_ID);
+ }
+ public SRPSessionKey(String username, int sessionID)
+ {
+ this.username = username;
+ this.sessionID = sessionID;
+ }
+ public SRPSessionKey(String username, Integer sessionID)
+ {
+ this.username = username;
+ if( sessionID != null )
+ this.sessionID = sessionID.intValue();
+ }
+
+ public boolean equals(Object obj)
+ {
+ SRPSessionKey key = (SRPSessionKey) obj;
+ return this.username.equals(key.username) && this.sessionID == key.sessionID;
+ }
+
+ public int hashCode()
+ {
+ return this.username.hashCode() + this.sessionID;
+ }
+
+ public int getSessionID()
+ {
+ return sessionID;
+ }
+
+ public String getUsername()
+ {
+ return username;
+ }
+
+ public String toString()
+ {
+ return "{username="+username+", sessionID="+sessionID+"}";
+ }
+}
diff --git a/src/org/jboss/security/srp/SRPVerifierStore.java b/src/org/jboss/security/srp/SRPVerifierStore.java
new file mode 100644
index 00000000..b4487ddc
--- /dev/null
+++ b/src/org/jboss/security/srp/SRPVerifierStore.java
@@ -0,0 +1,115 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.security.srp;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.io.ObjectStreamField;
+import java.security.KeyException;
+
+/** An interface describing the requirements of a password verifier store.
+This is an abstraction that allows the <username, verifier, salt> information
+needed by the server to be plugged in from various sources. E.g., LDAP
+servers, databases, files, etc.
+
+ @author Scott.Stark@jboss.org
+ @version $Revision: 81038 $
+*/
+public interface SRPVerifierStore
+{
+ public static class VerifierInfo implements Serializable
+ {
+ /** The serial version UID @since 1.2.4.1 */
+ private static final long serialVersionUID = 7420301687504271098L;
+ private static final ObjectStreamField[] serialPersistentFields = {
+ new ObjectStreamField("username", String.class),
+ new ObjectStreamField("verifier", byte[].class),
+ new ObjectStreamField("salt", byte[].class),
+ new ObjectStreamField("g", byte[].class),
+ new ObjectStreamField("N", byte[].class),
+ new ObjectStreamField("hashAlgorithm", String.class),
+ new ObjectStreamField("cipherAlgorithm", String.class),
+ new ObjectStreamField("cipherIV", byte[].class)
+ };
+
+ /** The username the information applies to. Perhaps redundant but it
+ * makes the object self contained.
+ * @serialField username String username
+ */
+ public String username;
+ /** The SRP password verifier hash
+ * @serialField verifier byte[] password verifier
+ */
+ public byte[] verifier;
+ /** The random password salt originally used to verify the password
+ * @serialField salt originally used to verify the password
+ */
+ public byte[] salt;
+ /** The SRP algorithm primitive generator
+ * @serialField g primitive generator
+ */
+ public byte[] g;
+ /** The algorithm safe-prime modulus
+ * @serialField N safe-prime modulus
+ */
+ public byte[] N;
+ /** The algorithm to hash the session key to produce K. To be consistent
+ with the RFC2945 description this must be SHA_Interleave as implemented
+ by the JBossSX security provider. For compatibility with earlier JBossSX
+ SRP releases the algorithm must be SHA_ReverseInterleave. This name is
+ passed to java.security.MessageDigest.getInstance().
+ * @serialField hashAlgorithm algorithm to hash the session key
+ * @since 1.2.4.2
+ */
+ public String hashAlgorithm;
+ /** The algorithm to use for any encryption of data.
+ * @serialField cipherAlgorithm algorithm to use for any encryption
+ * @since 1.2.4.2
+ */
+ public String cipherAlgorithm;
+ /** The initialization vector to use for any encryption of data.
+ * @serialField cipherIV initialization vector to use for any encryption
+ * @since 1.6
+ */
+ public byte[] cipherIV;
+ }
+
+ /** Get the indicated user's password verifier information.
+ */
+ public VerifierInfo getUserVerifier(String username)
+ throws KeyException, IOException;
+ /** Set the indicated users' password verifier information. This is equivalent
+ to changing a user's password and should generally invalidate any existing
+ SRP sessions and caches.
+ */
+ public void setUserVerifier(String username, VerifierInfo info)
+ throws IOException;
+
+ /** Verify an optional auxillary challenge sent from the client to the server. The
+ * auxChallenge object will have been decrypted if it was sent encrypted from the
+ * client. An example of a auxillary challenge would be the validation of a hardware
+ * token (SafeWord, SecureID, iButton) that the server validates to further strengthen
+ * the SRP password exchange.
+ */
+ public void verifyUserChallenge(String username, Object auxChallenge)
+ throws SecurityException;
+}
diff --git a/src/org/jboss/security/srp/SerialObjectStore.java b/src/org/jboss/security/srp/SerialObjectStore.java
new file mode 100644
index 00000000..3170488f
--- /dev/null
+++ b/src/org/jboss/security/srp/SerialObjectStore.java
@@ -0,0 +1,216 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.security.srp;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.math.BigInteger;
+import java.security.KeyException;
+import java.security.NoSuchAlgorithmException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.jboss.logging.Logger;
+import org.jboss.crypto.CryptoUtil;
+import org.jboss.security.srp.SRPConf;
+import org.jboss.security.srp.SRPVerifierStore;
+import org.jboss.security.srp.SRPVerifierStore.VerifierInfo;
+
+/** A simple implementation of the SRPVerifierStore that uses a
+file store made up of VerifierInfo serialized objects. Users and
+be added or removed using the addUser and delUser methods. User passwords
+are never stored in plaintext either in memory or in the serialized file.
+Note that usernames and passwords are logged when a user is added
+via the addUser operation. This is a development class and its use in
+a production environment is not advised.
+
+@see #addUser(String, String)
+@see #delUser(String)
+
+@author Scott.Stark@jboss.org
+@version $Revision: 81038 $
+*/
+public class SerialObjectStore implements SRPVerifierStore
+{
+ private static Logger log = Logger.getLogger(SerialObjectStore.class);
+ private Map infoMap;
+ private BigInteger g;
+ private BigInteger N;
+
+ /** Create an in memory store and load any VerifierInfo found in
+ ./SerialObjectStore.ser if it exists.
+ */
+ public SerialObjectStore() throws IOException
+ {
+ this(null);
+ }
+ /** Create an in memory store and load any VerifierInfo found in
+ the storeFile archive if it exists.
+ */
+ public SerialObjectStore(File storeFile) throws IOException
+ {
+ if( storeFile == null )
+ storeFile = new File("SerialObjectStore.ser");
+ if( storeFile.exists() == true )
+ {
+ FileInputStream fis = new FileInputStream(storeFile);
+ ObjectInputStream ois = new ObjectInputStream(fis);
+ try
+ {
+ infoMap = (Map) ois.readObject();
+ }
+ catch(ClassNotFoundException e)
+ {
+ }
+ ois.close();
+ fis.close();
+ }
+ else
+ {
+ infoMap = Collections.synchronizedMap(new HashMap());
+ }
+
+ try
+ {
+ CryptoUtil.init();
+ }
+ catch(NoSuchAlgorithmException e)
+ {
+ e.printStackTrace();
+ throw new IOException("Failed to initialzed security utils: "+e.getMessage());
+ }
+ N = SRPConf.getDefaultParams().N();
+ g = SRPConf.getDefaultParams().g();
+ log.trace("N: "+CryptoUtil.tob64(N.toByteArray()));
+ log.trace("g: "+CryptoUtil.tob64(g.toByteArray()));
+ byte[] hn = CryptoUtil.newDigest().digest(N.toByteArray());
+ log.trace("H(N): "+CryptoUtil.tob64(hn));
+ byte[] hg = CryptoUtil.newDigest().digest(g.toByteArray());
+ log.trace("H(g): "+CryptoUtil.tob64(hg));
+ }
+
+// --- Begin SRPVerifierStore interface methods
+ public VerifierInfo getUserVerifier(String username) throws KeyException, IOException
+ {
+ VerifierInfo info = null;
+ if( infoMap != null )
+ info = (VerifierInfo) infoMap.get(username);
+ if( info == null )
+ throw new KeyException("username: "+username+" not found");
+ return info;
+ }
+ public void setUserVerifier(String username, VerifierInfo info)
+ {
+ infoMap.put(username, info);
+ }
+
+ public void verifyUserChallenge(String username, Object auxChallenge)
+ throws SecurityException
+ {
+ throw new SecurityException("verifyUserChallenge not supported");
+ }
+// --- End SRPVerifierStore interface methods
+
+ /** Save the current in memory map of VerifierInfo to the indicated
+ storeFile by simply serializing the map to the file.
+ */
+ public void save(File storeFile) throws IOException
+ {
+ FileOutputStream fos = new FileOutputStream(storeFile);
+ ObjectOutputStream oos = new ObjectOutputStream(fos);
+ synchronized( infoMap )
+ {
+ oos.writeObject(infoMap);
+ }
+ oos.close();
+ fos.close();
+ }
+
+ public void addUser(String username, String password)
+ {
+ log.trace("addUser, username='"+username+"', password='"+password+"'");
+ VerifierInfo info = new VerifierInfo();
+ info.username = username;
+ /*
+ long r = Util.nextLong();
+ String rs = Long.toHexString(r);
+ */
+ String rs = "123456";
+ info.salt = rs.getBytes();
+ try
+ {
+ char[] pass = password.toCharArray();
+ info.verifier = CryptoUtil.calculateVerifier(username, pass,
+ info.salt, N, g);
+ info.g = g.toByteArray();
+ info.N = N.toByteArray();
+ if( log.isTraceEnabled() )
+ {
+ log.trace("N: "+CryptoUtil.tob64(info.N));
+ log.trace("g: "+CryptoUtil.tob64(info.g));
+ log.trace("s: "+CryptoUtil.tob64(info.salt));
+ byte[] xb = CryptoUtil.calculatePasswordHash(username, pass, info.salt);
+ log.trace("x: "+CryptoUtil.tob64(xb));
+ log.trace("v: "+CryptoUtil.tob64(info.verifier));
+ byte[] hn = CryptoUtil.newDigest().digest(info.N);
+ log.trace("H(N): "+CryptoUtil.tob64(hn));
+ byte[] hg = CryptoUtil.newDigest().digest(info.g);
+ log.trace("H(g): "+CryptoUtil.tob64(hg));
+ }
+ }
+ catch(Throwable t)
+ {
+ log.error("Failed to calculate verifier", t);
+ return;
+ }
+
+ setUserVerifier(username, info);
+ }
+ public void delUser(String username)
+ {
+ infoMap.remove(username);
+ }
+
+ public static void main(String[] args) throws IOException
+ {
+ File storeFile = new File("SerialObjectStore.ser");
+ SerialObjectStore store = new SerialObjectStore();
+
+ for(int a = 0; a < args.length; a ++)
+ {
+ if( args[a].startsWith("-a") )
+ {
+ store.addUser(args[a+1], args[a+2]);
+ }
+ else if( args[a].startsWith("-d") )
+ {
+ store.delUser(args[a+1]);
+ }
+ }
+ store.save(storeFile);
+ }
+}
diff --git a/src/se/leap/leapclient/ProviderAPI.java b/src/se/leap/leapclient/ProviderAPI.java
index e2af08e3..591bdc9f 100644
--- a/src/se/leap/leapclient/ProviderAPI.java
+++ b/src/se/leap/leapclient/ProviderAPI.java
@@ -171,9 +171,12 @@ public class ProviderAPI extends IntentService {
srp_client.init(n, ConfigHelper.g, new SHA256Digest(), new SecureRandom());
BigInteger salt = BigInteger.probablePrime(1024, null);
- BigInteger clientA = srp_client.generateClientCredentials(salt.toByteArray(), username.getBytes(), password.getBytes());
+
+ BigInteger clientA = srp_client.generateClientCredentials(salt.toString(16).getBytes(), username.getBytes(), password.getBytes());
+
try {
BigInteger serverB = sendAToSRPServer(authentication_server, username, clientA);
+
if(serverB == BigInteger.ZERO)
return false; // TODO Show error: error trying to start authentication with provider
@@ -241,7 +244,7 @@ public class ProviderAPI extends IntentService {
}
public BigInteger generateM1(BigInteger K, BigInteger salt, BigInteger clientA, BigInteger serverB, String username) throws NoSuchAlgorithmException {
- String digest_of_N_as_string = new BigInteger(MessageDigest.getInstance("SHA-256").digest(ConfigHelper.NG_1024.getBytes())).toString(16);
+ String digest_of_N_as_string = new BigInteger(1, MessageDigest.getInstance("SHA-256").digest(hex2ascii(ConfigHelper.NG_1024).getBytes())).toString(16);
String digest_of_G_as_string = new BigInteger(1, MessageDigest.getInstance("SHA-256").digest(ConfigHelper.g.toString(16).getBytes())).toString(16);
@@ -297,7 +300,11 @@ public class ProviderAPI extends IntentService {
private String hexXor(String a, String b) {
String str = "";
for (int i = 0; i < a.length(); i += 2) {
- int xor = Integer.parseInt(a.substring(i, 2 + i), 16) ^ Integer.parseInt(b.substring(i, 2 + i), 16);
+ int xor = 0;
+ if(a.length() > i + 2)
+ xor = Integer.parseInt(a.substring(i, 2 + i), 16) ^ Integer.parseInt(b.substring(i, 2 + i), 16);
+ else
+ xor = Integer.parseInt(a.substring(i, 1 + i), 16) ^ Integer.parseInt(b.substring(i, 1 + i), 16);
String xor_string = String.valueOf(Integer.valueOf(String.valueOf(xor), 16));
str += (xor_string.length() == 1) ? ("0" + xor) : xor_string;
}
@@ -328,6 +335,16 @@ public class ProviderAPI extends IntentService {
}
return buffer.toString();
}
+
+ private String hex2ascii(String hex) {
+ StringBuilder output = new StringBuilder();
+ for (int i = 0; i < hex.length(); i+=2) {
+ String str = hex.substring(i, i+2);
+ output.append((char)Integer.parseInt(str, 16));
+ }
+ String debug = output.toString();
+ return output.toString();
+ }
private BigInteger sendM1ToSRPServer(String server_url, String username, BigInteger m1) throws ClientProtocolException, IOException, JSONException {
DefaultHttpClient client = new LeapHttpClient(getApplicationContext());