diff options
Diffstat (limited to 'src/se')
-rw-r--r-- | src/se/leap/leapclient/ConfigHelper.java | 15 | ||||
-rw-r--r-- | src/se/leap/leapclient/ConfigurationWizard.java | 12 | ||||
-rw-r--r-- | src/se/leap/leapclient/LeapHttpClient.java | 8 | ||||
-rw-r--r-- | src/se/leap/leapclient/LeapSRPSession.java | 315 | ||||
-rw-r--r-- | src/se/leap/leapclient/ProviderAPI.java | 145 |
5 files changed, 480 insertions, 15 deletions
diff --git a/src/se/leap/leapclient/ConfigHelper.java b/src/se/leap/leapclient/ConfigHelper.java index c2f6c413..11401df5 100644 --- a/src/se/leap/leapclient/ConfigHelper.java +++ b/src/se/leap/leapclient/ConfigHelper.java @@ -7,6 +7,7 @@ import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileWriter; import java.io.IOException; +import java.math.BigInteger; import java.io.InputStream; import java.security.KeyStore; import java.security.KeyStoreException; @@ -29,17 +30,31 @@ public class ConfigHelper { final static String downloadJsonFilesBundleExtra = "downloadJSONFiles"; final static String downloadNewProviderDotJSON = "downloadNewProviderDotJSON"; + public static final String srpRegister = "srpRegister"; + final public static String srpAuth = "srpAuth"; + final public static String resultKey = "result"; final static String provider_key = "provider"; final static String cert_key = "cert"; final static String eip_service_key = "eip"; public static final String PREFERENCES_KEY = "LEAPPreferences"; public static final String user_directory = "leap_android"; public static String provider_key_url = "provider_main_url"; + final public static String srp_server_url_key = "srp_server_url"; + final public static String username_key = "username"; + final public static String password_key = "password"; final public static String eip_service_api_path = "/config/eip-service.json"; + final public static String NG_1024 = + "eeaf0ab9adb38dd69c33f80afa8fc5e86072618775ff3c0b9ea2314c9c256576d674df7496ea81d3383b4813d692c6e0e0d5d8e250b98be48e495c1d6089dad15dc7d7b46154d6b6ce8ef4ad69b15d4982559b297bcf1885c529f566660e57ec68edbc3c05726cc02fd4cbf4976eaa9afd5138fe8376435b9fc61d2fc0eb06e3"; + final public static BigInteger g = BigInteger.valueOf(2); + final public static int CUSTOM_PROVIDER_ADDED = 0; final public static int CORRECTLY_DOWNLOADED_JSON_FILES = 1; final public static int INCORRECTLY_DOWNLOADED_JSON_FILES = 2; + final public static int SRP_AUTHENTICATION_SUCCESSFUL = 3; + final public static int SRP_AUTHENTICATION_FAILED = 4; + public static final int SRP_REGISTRATION_SUCCESSFUL = 5; + public static final int SRP_REGISTRATION_FAILED = 6; static void saveSharedPref(String shared_preferences_key, JSONObject content) { diff --git a/src/se/leap/leapclient/ConfigurationWizard.java b/src/se/leap/leapclient/ConfigurationWizard.java index 90e74e51..5b93cbbe 100644 --- a/src/se/leap/leapclient/ConfigurationWizard.java +++ b/src/se/leap/leapclient/ConfigurationWizard.java @@ -49,8 +49,6 @@ public class ConfigurationWizard extends Activity * device.
*/
private boolean mTwoPane;
-
- static SharedPreferences shared_preferences;
public ProviderAPIResultReceiver providerAPI_result_receiver;
@@ -59,9 +57,9 @@ public class ConfigurationWizard extends Activity super.onCreate(savedInstanceState);
setContentView(R.layout.activity_configuration_wizard);
-
+ ConfigHelper.setSharedPreferences(getSharedPreferences(ConfigHelper.PREFERENCES_KEY,MODE_PRIVATE));
-
+ loadPreseededProviders();
if(ConfigHelper.getKeystore() == null) {
@@ -168,6 +166,7 @@ public class ConfigurationWizard extends Activity provider_API_command.putExtra(ConfigHelper.downloadJsonFilesBundleExtra, method_and_parameters);
provider_API_command.putExtra("receiver", providerAPI_result_receiver);
+ startService(provider_API_command);
}
@@ -195,6 +194,7 @@ public class ConfigurationWizard extends Activity provider_API_command.putExtra(ConfigHelper.downloadNewProviderDotJSON, method_and_parameters);
provider_API_command.putExtra("receiver", providerAPI_result_receiver);
+
startService(provider_API_command);
}
@@ -207,14 +207,14 @@ public class ConfigurationWizard extends Activity fragmentManager.beginTransaction()
.replace(R.id.configuration_wizard_layout, providerList, "providerlist")
.commit();
- }
+ } else if(resultCode == ConfigHelper.CORRECTLY_DOWNLOADED_JSON_FILES) {
setResult(RESULT_OK);
finish();
}
else if(resultCode == ConfigHelper.INCORRECTLY_DOWNLOADED_JSON_FILES) {
setResult(RESULT_CANCELED);
- finish();
+ finish(); }
}
}
diff --git a/src/se/leap/leapclient/LeapHttpClient.java b/src/se/leap/leapclient/LeapHttpClient.java index 9ee0a95e..51b76b2c 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; @@ -48,4 +50,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..abdf6b2c --- /dev/null +++ b/src/se/leap/leapclient/LeapSRPSession.java @@ -0,0 +1,315 @@ +package se.leap.leapclient; + +import java.io.UnsupportedEncodingException; +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 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); + byte[] N_bytes = Util.trim(params.N); + this.N = new BigInteger(1, N_bytes); + + 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[] salt_bytes = Util.trim(params.s); + byte[] xb = calculatePasswordHash(username, password, salt_bytes); + this.x = new BigInteger(1, xb); + + // Calculate v = kg^x mod N + String k_string = "bf66c44a428916cad64aa7c679f3fd897ad4c375e9bbb4cbf2f5de241d618ef0"; + this.v = calculateV(k_string); + //String v_string = v.toString(16); + + serverHash = newDigest(); + clientHash = newDigest(); + + // H(N) + byte[] digest_of_n = newDigest().digest(N_bytes); + + // H(g) + byte[] digest_of_g = newDigest().digest(params.g); + + // clientHash = H(N) xor H(g) + byte[] xor_digest = xor(digest_of_n, digest_of_g, digest_of_g.length); + //String hxg_string = new BigInteger(1, xor_digest).toString(16); + clientHash.update(xor_digest); + + // clientHash = H(N) xor H(g) | H(U) + byte[] username_digest = newDigest().digest(Util.trim(username.getBytes())); + username_digest = Util.trim(username_digest); + //String username_digest_string = new BigInteger(1, username_digest).toString(16); + clientHash.update(username_digest); + + // clientHash = H(N) xor H(g) | H(U) | s + //String salt_string = new BigInteger(1, salt_bytes).toString(16); + clientHash.update(salt_bytes); + + K = null; + } + + /** + * Calculates the parameter x of the SRP-6a algorithm. + * @param username + * @param password + * @param salt the salt of the user + * @return x + */ + public byte[] calculatePasswordHash(String username, char[] password, byte[] salt) + { + // Calculate x = H(s | H(U | ':' | password)) + MessageDigest x_digest = newDigest(); + + // Try to convert the username to a byte[] using UTF-8 + byte[] user = null; + byte[] colon = {}; + try { + user = Util.trim(username.getBytes("UTF-8")); + colon = Util.trim(":".getBytes("UTF-8")); + } + catch(UnsupportedEncodingException e) { + // Use the default platform encoding + user = Util.trim(username.getBytes()); + colon = Util.trim(":".getBytes()); + } + + byte[] passBytes = new byte[2*password.length]; + int passBytesLength = 0; + for(int p = 0; p < password.length; p++) { + int c = (password[p] & 0x00FFFF); + // The low byte of the char + byte b0 = (byte) (c & 0x0000FF); + // The high byte of the char + byte b1 = (byte) ((c & 0x00FF00) >> 8); + passBytes[passBytesLength ++] = b0; + // Only encode the high byte if c is a multi-byte char + if( c > 255 ) + passBytes[passBytesLength ++] = b1; + } + + // Build the hash + x_digest.update(user); + x_digest.update(colon); + x_digest.update(passBytes, 0, passBytesLength); + byte[] h = x_digest.digest(); + //h = Util.trim(h); + + x_digest.reset(); + x_digest.update(salt); + x_digest.update(h); + byte[] x_digest_bytes = Util.trim(x_digest.digest()); + + return x_digest_bytes; + } + + /** + * Calculates the parameter V of the SRP-6a algorithm. + * @param k_string constant k predefined by the SRP server implementation. + * @return the value of V + */ + private BigInteger calculateV(String k_string) { + BigInteger k = new BigInteger(k_string, 16); + return k.multiply(g.modPow(x, N)); // g^x % N + } + + public byte[] xor(byte[] b1, byte[] b2, int length) + { + //TODO Check if length matters in the order, when b2 is smaller than b1 or viceversa + byte[] xor_digest = new BigInteger(1, b1).xor(new BigInteger(1, b2)).toByteArray(); + return Util.trim(xor_digest); + } + + /** + * @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()); + //String Abytes_string = new BigInteger(1, Abytes).toString(16); + + // clientHash = H(N) xor H(g) | H(U) | A + clientHash.update(Abytes); + + // serverHash = A + serverHash.update(Abytes); + } + return Abytes; + } + + /** + * Calculates the parameter M1, to be sent to the SRP server. + * It also updates hashes of client and server for further calculations in other methods. + * @param Bbytes the parameter received from the server, in bytes + * @return the parameter M1 + * @throws NoSuchAlgorithmException + */ + public byte[] response(byte[] Bbytes) throws NoSuchAlgorithmException { + // clientHash = H(N) xor H(g) | H(U) | s | A | B + Bbytes = Util.trim(Bbytes); + //String Bbytes_string = new BigInteger(1, Bbytes).toString(16); + clientHash.update(Bbytes); + + // Calculate S = (B - kg^x) ^ (a + u * x) % N + BigInteger S = calculateS(Bbytes); + byte[] S_bytes = Util.trim(S.toByteArray()); + //String S_bytes_string = new BigInteger(1, S_bytes).toString(16); + + // K = SessionHash(S) + String hash_algorithm = params.hashAlgorithm; + MessageDigest sessionDigest = MessageDigest.getInstance(hash_algorithm); + K = sessionDigest.digest(S_bytes); + //K = Util.trim(K); + //String K_bytes_string = new BigInteger(1, K).toString(16); + + // clientHash = H(N) xor H(g) | H(U) | A | B | K + clientHash.update(K); + byte[] M1 = clientHash.digest(); + + // serverHash = Astr + M + K + serverHash.update(M1); + serverHash.update(K); + return M1; + } + + /** + * It calculates the parameter S used by response() to obtain session hash K. + * @param Bbytes the parameter received from the server, in bytes + * @return the parameter S + */ + private BigInteger calculateS(byte[] Bbytes) { + byte[] Abytes = Util.trim(A.toByteArray()); + byte[] u_bytes = getU(Abytes, Bbytes); + //ub = Util.trim(ub); + + BigInteger B = new BigInteger(1, Bbytes); + BigInteger u = new BigInteger(1, u_bytes); + //String u_string = u.toString(16); + + BigInteger B_minus_v = B.subtract(v); + BigInteger a_ux = a.add(u.multiply(x)); + //String a_ux_string = a_ux.toString(16); + BigInteger S = B_minus_v.modPow(a_ux, N); + + return S; + } + + /** + * It calculates the parameter u used by calculateS to obtain S. + * @param Abytes the exponential residue sent to the server + * @param Bbytes the parameter received from the server, in bytes + * @return + */ + public byte[] getU(byte[] Abytes, byte[] Bbytes) { + MessageDigest u_digest = newDigest(); + u_digest.update(Abytes); + u_digest.update(Bbytes); + return new BigInteger(1, u_digest.digest()).toByteArray(); + } + + /** + * @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 + { + return K; + } + + + /** + * @return a new SHA-256 digest. + */ + public MessageDigest newDigest() + { + MessageDigest md = null; + try { + md = MessageDigest.getInstance("SHA-256"); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } + return md; + } +} diff --git a/src/se/leap/leapclient/ProviderAPI.java b/src/se/leap/leapclient/ProviderAPI.java index afbf1c01..6b09eb9d 100644 --- a/src/se/leap/leapclient/ProviderAPI.java +++ b/src/se/leap/leapclient/ProviderAPI.java @@ -1,16 +1,25 @@ package se.leap.leapclient; import java.io.IOException; +import java.math.BigInteger; +import java.security.NoSuchAlgorithmException; +import java.util.List; import java.net.MalformedURLException; import java.net.URL; -import java.security.Provider; -import java.security.Security; import java.util.Scanner; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; +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.SRPParameters; import org.json.JSONException; import org.json.JSONObject; @@ -27,25 +36,143 @@ public class ProviderAPI extends IntentService { public ProviderAPI() { super("ProviderAPI"); Log.v("ClassName", "Provider API"); - // TODO Auto-generated constructor stub } @Override protected void onHandleIntent(Intent task_for) { final ResultReceiver receiver = task_for.getParcelableExtra("receiver"); + Bundle task; if((task = task_for.getBundleExtra(ConfigHelper.downloadJsonFilesBundleExtra)) != null) { + if(!downloadJsonFiles(task)) + receiver.send(ConfigHelper.INCORRECTLY_DOWNLOADED_JSON_FILES, Bundle.EMPTY); + else + receiver.send(ConfigHelper.CORRECTLY_DOWNLOADED_JSON_FILES, Bundle.EMPTY); if(downloadJsonFilesBundleExtra(task)) receiver.send(ConfigHelper.CORRECTLY_DOWNLOADED_JSON_FILES, Bundle.EMPTY); else receiver.send(ConfigHelper.INCORRECTLY_DOWNLOADED_JSON_FILES, Bundle.EMPTY); } - else if ((task = task_for.getBundleExtra(ConfigHelper.downloadNewProviderDotJSON)) != null) { - if(downloadNewProviderDotJSON(task)) - receiver.send(ConfigHelper.CUSTOM_PROVIDER_ADDED, Bundle.EMPTY); - else - receiver.send(ConfigHelper.INCORRECTLY_DOWNLOADED_JSON_FILES, Bundle.EMPTY); + } + + private boolean downloadJsonFiles(Bundle task) { + String cert_url = (String) task.get(ConfigHelper.cert_key); + String eip_service_json_url = (String) task.get(ConfigHelper.eip_service_key); + try { + String cert_string = getStringFromProvider(cert_url); + JSONObject cert_json = new JSONObject("{ \"certificate\" : \"" + cert_string + "\"}"); + ConfigHelper.saveSharedPref(ConfigHelper.cert_key, cert_json); + JSONObject eip_service_json = getJSONFromProvider(eip_service_json_url); + ConfigHelper.saveSharedPref(ConfigHelper.eip_service_key, eip_service_json); + return true; + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + return false; + } catch (JSONException e) { + ConfigHelper.rescueJSONException(e); + return false; + } catch(Exception e) { + e.printStackTrace(); + return false; + } + } + + private boolean registerWithSRP(Bundle task) { + 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); + + BigInteger ng_1024 = new BigInteger(ConfigHelper.NG_1024, 16); + BigInteger salt = ng_1024.probablePrime(1024, null); + byte[] salt_in_bytes = salt.toByteArray(); + + return false; + } + + private boolean authenticateBySRP(Bundle task) { + 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"; + + 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, 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(); + return false; + } catch (IOException e1) { + // TODO Auto-generated catch block + e1.printStackTrace(); + return false; + } catch (JSONException e1) { + // TODO Auto-generated catch block + e1.printStackTrace(); + return false; + } catch (NoSuchAlgorithmException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + return false; + } + } + + private JSONObject sendAToSRPServer(String server_url, String username, String clientA) throws ClientProtocolException, IOException, JSONException { + DefaultHttpClient client = LeapHttpClient.getInstance(getApplicationContext()); + String parameter_chain = "A" + "=" + 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 JSONObject(); } + List<Cookie> cookies = client.getCookieStore().getCookies(); + if(!cookies.isEmpty()) { + String session_id = cookies.get(0).getValue(); + } + return json_response; + } + + private byte[] sendM1ToSRPServer(String server_url, String username, byte[] m1) throws ClientProtocolException, IOException, JSONException { + 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, localContext); + 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]; + } + + return json_response.getString("M2").getBytes(); } private boolean downloadNewProviderDotJSON(Bundle task) { @@ -131,7 +258,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); |