summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorParménides GV <parmegv@sdf.org>2013-04-03 19:34:40 +0200
committerParménides GV <parmegv@sdf.org>2013-04-03 19:34:40 +0200
commit3e9a68fcc6c16be69abfa27d5fd3a2cbfc620bb0 (patch)
treeeee38b81b77877de0524112b0636eb8ec3d50850
parent7af9519591ea481718b4f903b97463250cc5f116 (diff)
Fixed bug #2146 => A calculation is now fine. Next step: fix M1
calculation, since right now (using tests) response() method is not doing OK. Added new SRPSession modifying response() method from JBoss SRP implementation. Added hosts-for-android-emulator. Use with the following commands to be able to test on api.lvh.me: adb shell mount -o rw,remount -t yaffs2 /dev/block/mtdblock3 /system adb push ~/workspace/leap_android/hosts-for-android-emulator /system/etc/hosts
-rw-r--r--hosts-for-android-emulator9
-rw-r--r--src/se/leap/leapclient/LeapHttpClient.java8
-rw-r--r--src/se/leap/leapclient/LeapSRPSession.java193
-rw-r--r--src/se/leap/leapclient/ProviderAPI.java99
4 files changed, 249 insertions, 60 deletions
diff --git a/hosts-for-android-emulator b/hosts-for-android-emulator
new file mode 100644
index 00000000..b10103ef
--- /dev/null
+++ b/hosts-for-android-emulator
@@ -0,0 +1,9 @@
+127.0.0.1 localhost
+10.0.2.2 api.lvh.me
+
+# The following lines are desirable for IPv6 capable hosts
+::1 ip6-localhost ip6-loopback
+fe00::0 ip6-localnet
+ff00::0 ip6-mcastprefix
+ff02::1 ip6-allnodes
+ff02::2 ip6-allrouters
diff --git a/src/se/leap/leapclient/LeapHttpClient.java b/src/se/leap/leapclient/LeapHttpClient.java
index 9e1a541b..fd6db745 100644
--- a/src/se/leap/leapclient/LeapHttpClient.java
+++ b/src/se/leap/leapclient/LeapHttpClient.java
@@ -15,6 +15,8 @@ import android.content.Context;
public class LeapHttpClient extends DefaultHttpClient {
final Context context;
+
+ private static LeapHttpClient client;
public LeapHttpClient(Context context) {
this.context = context;
@@ -55,4 +57,10 @@ public class LeapHttpClient extends DefaultHttpClient {
throw new AssertionError(e);
}
}
+
+ public static LeapHttpClient getInstance(Context context) {
+ if(client == null)
+ client = new LeapHttpClient(context);
+ return client;
+ }
}
diff --git a/src/se/leap/leapclient/LeapSRPSession.java b/src/se/leap/leapclient/LeapSRPSession.java
new file mode 100644
index 00000000..1d1f0c9d
--- /dev/null
+++ b/src/se/leap/leapclient/LeapSRPSession.java
@@ -0,0 +1,193 @@
+package se.leap.leapclient;
+
+import java.math.BigInteger;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+
+import org.jboss.security.Util;
+import org.jboss.security.srp.SRPClientSession;
+import org.jboss.security.srp.SRPParameters;
+import org.jboss.security.srp.SRPPermission;
+
+public class LeapSRPSession {
+
+ 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;
+
+ /** 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 LeapSRPSession(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 LeapSRPSession(String username, char[] password, SRPParameters params,
+ byte[] abytes)
+ {
+ try
+ {
+ // Initialize the secure random number and message digests
+ Util.init();
+ }
+ catch(NoSuchAlgorithmException e)
+ {
+ }
+ this.params = params;
+ this.g = new BigInteger(1, params.g);
+ this.N = new BigInteger(1, params.N);
+ if( abytes != null )
+ {
+ A_LEN = 8*abytes.length;
+ /* TODO Why did they put this condition?
+ 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);
+ }
+
+ // Calculate x = H(s | H(U | ':' | password))
+ byte[] xb = Util.calculatePasswordHash(username, password, params.s);
+ this.x = new BigInteger(1, xb);
+ this.v = g.modPow(x, N); // g^x % N
+
+ serverHash = Util.newDigest();
+ clientHash = Util.newDigest();
+ // H(N)
+ byte[] hn = Util.newDigest().digest(params.N);
+ // H(g)
+ byte[] hg = Util.newDigest().digest(params.g);
+ // clientHash = H(N) xor H(g)
+ byte[] hxg = Util.xor(hn, hg, 20);
+ clientHash.update(hxg);
+ // clientHash = H(N) xor H(g) | H(U)
+ clientHash.update(Util.newDigest().digest(username.getBytes()));
+ // clientHash = H(N) xor H(g) | H(U) | s
+ clientHash.update(params.s);
+ 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, Util.getPRNG());
+ } while(a.compareTo(one) <= 0);
+ }
+ A = g.modPow(a, N);
+ Abytes = Util.trim(A.toByteArray());
+ // clientHash = H(N) xor H(g) | H(U) | A
+ clientHash.update(Abytes);
+ // serverHash = A
+ serverHash.update(Abytes);
+ }
+ return Abytes;
+ }
+
+ public byte[] response(byte[] Bbytes) throws NoSuchAlgorithmException {
+ // clientHash = H(N) xor H(g) | H(U) | s | A | B
+ clientHash.update(Bbytes);
+
+ /*
+ var B = new BigInteger(ephemeral, 16);
+ var Bstr = ephemeral;
+ // u = H(A,B)
+ var u = new BigInteger(SHA256(hex2a(Astr + Bstr)), 16);
+ // x = H(s, H(I:p))
+ var x = this.calcX(salt);
+ //S = (B - kg^x) ^ (a + ux)
+ var kgx = k.multiply(g.modPow(x, N));
+ var aux = a.add(u.multiply(x));
+ S = B.subtract(kgx).modPow(aux, N);
+ K = SHA256(hex2a(S.toString(16)));
+ */
+ byte[] hA = Util.newDigest().digest(A.toByteArray());
+ MessageDigest u_digest = Util.newDigest();
+ u_digest.update(A.toByteArray());
+ u_digest.update(Bbytes);
+ clientHash.update(u_digest.digest());
+ byte[] ub = new BigInteger(clientHash.digest()).toByteArray();
+ // Calculate S = (B - g^x) ^ (a + u * x) % N
+ BigInteger B = new BigInteger(1, Bbytes);
+ if( B.compareTo(v) < 0 )
+ B = B.add(N);
+ BigInteger u = new BigInteger(1, ub);
+ BigInteger B_v = B.subtract(v);
+ BigInteger a_ux = a.add(u.multiply(x));
+ BigInteger S = B_v.modPow(a_ux, N);
+ // K = SessionHash(S)
+ MessageDigest sessionDigest = MessageDigest.getInstance(params.hashAlgorithm);
+ K = sessionDigest.digest(S.toByteArray());
+ // clientHash = H(N) xor H(g) | H(U) | A | B | K
+ clientHash.update(K);
+ byte[] M1 = clientHash.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);
+ 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/se/leap/leapclient/ProviderAPI.java b/src/se/leap/leapclient/ProviderAPI.java
index b8d6a765..ea1410ad 100644
--- a/src/se/leap/leapclient/ProviderAPI.java
+++ b/src/se/leap/leapclient/ProviderAPI.java
@@ -3,7 +3,6 @@ package se.leap.leapclient;
import java.io.IOException;
import java.math.BigInteger;
import java.security.NoSuchAlgorithmException;
-import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
@@ -13,15 +12,16 @@ import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
+import org.apache.http.client.protocol.ClientContext;
import org.apache.http.cookie.Cookie;
import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.protocol.BasicHttpContext;
+import org.apache.http.protocol.HttpContext;
+import org.jboss.security.srp.SRPClientSession;
+import org.jboss.security.srp.SRPParameters;
import org.json.JSONException;
import org.json.JSONObject;
-import com.jordanzimmerman.SRPClientSession;
-import com.jordanzimmerman.SRPConstants;
-import com.jordanzimmerman.SRPFactory;
-
import se.leap.leapclient.ProviderListContent.ProviderItem;
import android.app.IntentService;
@@ -121,22 +121,31 @@ public class ProviderAPI extends IntentService {
String username = (String) task.get(ConfigHelper.username_key);
String password = (String) task.get(ConfigHelper.password_key);
String authentication_server = (String) task.get(ConfigHelper.srp_server_url_key);
+
+ String salt = "abcd";
- SRPConstants constants = new SRPConstants(new BigInteger(ConfigHelper.NG_1024, 16), ConfigHelper.g);
- SRPFactory factory = SRPFactory.getInstance(constants);
- SRPClientSession session = factory.newClientSession(password.getBytes());
- session.setSalt_s(new BigInteger("1"));
- byte[] A = session.getPublicKey_A().toString(16).getBytes();
+ SRPParameters params = new SRPParameters(new BigInteger(ConfigHelper.NG_1024, 16).toByteArray(), new BigInteger("2").toByteArray(), new BigInteger(salt, 16).toByteArray(), "SHA-256");
+ //SRPClientSession client = new SRPClientSession(username, password.toCharArray(), params);
+ LeapSRPSession client = new LeapSRPSession(username, password.toCharArray(), params);
+ byte[] A = client.exponential();
try {
- JSONObject saltAndB = sendAToSRPServer(authentication_server, username, getHexString(A));
- String B = saltAndB.getString("B");
- String salt = saltAndB.getString("salt");
- session.setSalt_s(new BigInteger(salt, 16));
- session.setServerPublicKey_B(new BigInteger(B, 16));
- byte[] M1 = session.getEvidenceValue_M1().toString(16).getBytes();
- byte[] M2 = sendM1ToSRPServer(authentication_server, username, M1);
- session.validateServerEvidenceValue_M2(new BigInteger(getHexString(M2), 16));
- return true;
+ JSONObject saltAndB = sendAToSRPServer(authentication_server, username, new BigInteger(A).toString(16));
+ if(saltAndB.length() > 0) {
+ byte[] B = saltAndB.getString("B").getBytes();
+ salt = saltAndB.getString("salt");
+ params = new SRPParameters(new BigInteger(ConfigHelper.NG_1024, 16).toByteArray(), new BigInteger("2").toByteArray(), new BigInteger(salt, 16).toByteArray(), "SHA-256");
+ //client = new SRPClientSession(username, password.toCharArray(), params);
+ client = new LeapSRPSession(username, password.toCharArray(), params);
+ A = client.exponential();
+ saltAndB = sendAToSRPServer(authentication_server, username, new BigInteger(A).toString(16));
+ String Bhex = saltAndB.getString("B");
+ byte[] M1 = client.response(new BigInteger(Bhex, 16).toByteArray());
+ byte[] M2 = sendM1ToSRPServer(authentication_server, username, M1);
+ if( client.verify(M2) == false )
+ throw new SecurityException("Failed to validate server reply");
+ return true;
+ }
+ else return false;
} catch (ClientProtocolException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
@@ -149,25 +158,15 @@ public class ProviderAPI extends IntentService {
// TODO Auto-generated catch block
e1.printStackTrace();
return false;
+ } catch (NoSuchAlgorithmException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ return false;
}
}
-
- public static String getHexString(byte[] b) {
- String result = "";
- StringBuffer sb = new StringBuffer();
- for (byte tmp : b) {
- sb.append(Integer.toHexString((int) (tmp & 0xff)));
- }
- return sb.toString();
-// for (int i=0; i < b.length; i++) {
-// result +=
-// Integer.toString( ( b[i] & 0xff ) + 0x100, 16).substring( 1 );
-// }
-// return result;
- }
private JSONObject sendAToSRPServer(String server_url, String username, String clientA) throws ClientProtocolException, IOException, JSONException {
- DefaultHttpClient client = new LeapHttpClient(getApplicationContext());
+ DefaultHttpClient client = LeapHttpClient.getInstance(getApplicationContext());
String parameter_chain = "A" + "=" + clientA + "&" + "login" + "=" + username;
HttpPost post = new HttpPost(server_url + "/sessions.json" + "?" + parameter_chain);
@@ -185,31 +184,14 @@ public class ProviderAPI extends IntentService {
return json_response;
}
- private byte[] sendAToSRPServer(String server_url, String username, byte[] clientA) throws ClientProtocolException, IOException, JSONException {
- DefaultHttpClient client = new LeapHttpClient(getApplicationContext());
- String parameter_chain = "A" + "=" + getHexString(clientA) + "&" + "login" + "=" + username;
- HttpPost post = new HttpPost(server_url + "/sessions.json" + "?" + parameter_chain);
-
- HttpResponse getResponse = client.execute(post);
- HttpEntity responseEntity = getResponse.getEntity();
- String plain_response = new Scanner(responseEntity.getContent()).useDelimiter("\\A").next();
- JSONObject json_response = new JSONObject(plain_response);
- if(!json_response.isNull("errors") || json_response.has("errors")) {
- return new byte[0];
- }
- List<Cookie> cookies = client.getCookieStore().getCookies();
- if(!cookies.isEmpty()) {
- String session_id = cookies.get(0).getValue();
- }
- return json_response.getString("B").getBytes();
- }
-
private byte[] sendM1ToSRPServer(String server_url, String username, byte[] m1) throws ClientProtocolException, IOException, JSONException {
- DefaultHttpClient client = new LeapHttpClient(getApplicationContext());
- String parameter_chain = "client_auth" + "=" + getHexString(m1);
+ DefaultHttpClient client = LeapHttpClient.getInstance(getApplicationContext());
+ String parameter_chain = "client_auth" + "=" + new BigInteger(m1).toString(16);
HttpPut put = new HttpPut(server_url + "/sessions/" + username +".json" + "?" + parameter_chain);
+ HttpContext localContext = new BasicHttpContext();
+ localContext.setAttribute(ClientContext.COOKIE_STORE, client.getCookieStore());
- HttpResponse getResponse = client.execute(put);
+ HttpResponse getResponse = client.execute(put, localContext);
HttpEntity responseEntity = getResponse.getEntity();
String plain_response = new Scanner(responseEntity.getContent()).useDelimiter("\\A").next();
JSONObject json_response = new JSONObject(plain_response);
@@ -217,9 +199,6 @@ public class ProviderAPI extends IntentService {
return new byte[0];
}
- List<Cookie> cookies = client.getCookieStore().getCookies();
- String session_id = cookies.get(0).getValue();
-
return json_response.getString("M2").getBytes();
}
@@ -231,7 +210,7 @@ public class ProviderAPI extends IntentService {
String json_file_content = "";
- DefaultHttpClient client = new LeapHttpClient(getApplicationContext());
+ DefaultHttpClient client = LeapHttpClient.getInstance(getApplicationContext());
HttpGet get = new HttpGet(string_url);
// Execute the GET call and obtain the response
HttpResponse getResponse = client.execute(get);