summaryrefslogtreecommitdiff
path: root/src/org/jboss/security/srp/SRPClientSession.java
diff options
context:
space:
mode:
authorParménides GV <parmegv@sdf.org>2013-03-12 18:23:11 +0100
committerParménides GV <parmegv@sdf.org>2013-03-12 18:23:11 +0100
commit86856f497377aa0c9b39551a2df013b915be21ce (patch)
tree5b2411a882a06f3e78bafa4bbda5b10e178f4f78 /src/org/jboss/security/srp/SRPClientSession.java
parent34400de64dcf68a451b5a1bf1dbea52a49bb24eb (diff)
Gonna try jboss srp implementation, because if I implement myself one we
will have to audit it.
Diffstat (limited to 'src/org/jboss/security/srp/SRPClientSession.java')
-rw-r--r--src/org/jboss/security/srp/SRPClientSession.java289
1 files changed, 289 insertions, 0 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;
+ }
+}