summaryrefslogtreecommitdiff
path: root/src/se/leap
diff options
context:
space:
mode:
authorSean Leonard <meanderingcode@aetherislands.net>2013-06-20 18:36:16 -0600
committerSean Leonard <meanderingcode@aetherislands.net>2013-06-20 18:36:16 -0600
commit195d5ab2d518f6f3960edd3c636e941830c2664d (patch)
tree81b6b0d0e0b374e82afa6fc15c32716f981f0aa4 /src/se/leap
parent5a7e2365365a3b1f773212cefdeeaa4ee587a590 (diff)
parent3f8e2f6569f893a49bf1819416cae792c702d370 (diff)
Merge 'parmegv/feature/authGui' into develop
Diffstat (limited to 'src/se/leap')
-rw-r--r--src/se/leap/leapclient/ConfigHelper.java288
-rw-r--r--src/se/leap/leapclient/ConfigurationWizard.java321
-rw-r--r--src/se/leap/leapclient/Dashboard.java200
-rw-r--r--src/se/leap/leapclient/LeapHttpClient.java93
-rw-r--r--src/se/leap/leapclient/LeapSRPSession.java221
-rw-r--r--src/se/leap/leapclient/LogInDialog.java100
-rw-r--r--src/se/leap/leapclient/NewProviderDialog.java34
-rw-r--r--src/se/leap/leapclient/Provider.java5
-rw-r--r--src/se/leap/leapclient/ProviderAPI.java662
-rw-r--r--src/se/leap/leapclient/ProviderAPIResultReceiver.java12
-rw-r--r--src/se/leap/leapclient/ProviderListContent.java74
-rw-r--r--src/se/leap/leapclient/ProviderListFragment.java3
12 files changed, 1428 insertions, 585 deletions
diff --git a/src/se/leap/leapclient/ConfigHelper.java b/src/se/leap/leapclient/ConfigHelper.java
index 7476c89..dd20112 100644
--- a/src/se/leap/leapclient/ConfigHelper.java
+++ b/src/se/leap/leapclient/ConfigHelper.java
@@ -1,11 +1,8 @@
package se.leap.leapclient;
-
-import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
-import java.io.FileWriter;
import java.io.IOException;
import java.math.BigInteger;
import java.io.InputStream;
@@ -19,86 +16,188 @@ import java.security.cert.X509Certificate;
import org.json.JSONException;
import org.json.JSONObject;
+import android.content.Context;
import android.content.SharedPreferences;
import android.os.Environment;
-import android.util.Log;
+/**
+ * Stores constants, and implements auxiliary methods used across all LEAP Android classes.
+ *
+ * @author parmegv
+ * @author MeanderingCode
+ *
+ */
public class ConfigHelper {
public static SharedPreferences shared_preferences;
private static KeyStore keystore_trusted;
- 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_main_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 String
+ DOWNLOAD_JSON_FILES_BUNDLE_EXTRA = "downloadJSONFiles",
+ UPDATE_PROVIDER_DOTJSON = "updateProviderDotJSON",
+ DOWNLOAD_NEW_PROVIDER_DOTJSON = "downloadNewProviderDotJSON",
+ LOG_IN_DIALOG = "logInDialog",
+ NEW_PROVIDER_DIALOG = "logInDialog",
+ SRP_REGISTER = "srpRegister",
+ SRP_AUTH = "srpAuth",
+ M1_KEY = "M1",
+ M2_KEY = "M2",
+ LOG_IN = "logIn",
+ LOG_OUT = "logOut",
+ DOWNLOAD_CERTIFICATE = "downloadUserAuthedCertificate",
+ API_VERSION_KEY = "api_version",
+ RESULT_KEY = "result",
+ RECEIVER_KEY = "receiver",
+ PROVIDER_KEY = "provider",
+ SERVICE_KEY = "service",
+ ALLOWED_ANON = "allow_anonymous",
+ MAIN_CERT_KEY = "main_cert",
+ CERT_KEY = "cert",
+ EIP_SERVICE_KEY = "eip",
+ TYPE_OF_CERTIFICATE = "type_of_certificate",
+ ANON_CERTIFICATE = "anon_certificate",
+ AUTHED_CERTIFICATE = "authed_certificate",
+ SALT_KEY = "salt",
+ SESSION_ID_COOKIE_KEY = "session_id_cookie_key",
+ SESSION_ID_KEY = "session_id",
+ PREFERENCES_KEY = "LEAPPreferences",
+ USER_DIRECTORY = "leap_android",
+ PROVIDER_NAME = "provider_name",
+ PROVIDER_MAIN_URL = "provider_main_url",
+ PROVIDER_JSON_URL = "provider_json_url",
+ CUSTOM = "custom",
+ DANGER_ON = "danger_on",
+ API_URL_KEY = "api_uri",
+ USERNAME_KEY = "username",
+ PASSWORD_KEY = "password",
+ ALLOW_REGISTRATION_KEY = "allow_registration",
+ EIP_SERVICE_API_PATH = "config/eip-service.json",
+ ERRORS_KEY = "errors"
+ ;
- 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;
+ final public static String NG_1024 =
+ "eeaf0ab9adb38dd69c33f80afa8fc5e86072618775ff3c0b9ea2314c9c256576d674df7496ea81d3383b4813d692c6e0e0d5d8e250b98be48e495c1d6089dad15dc7d7b46154d6b6ce8ef4ad69b15d4982559b297bcf1885c529f566660e57ec68edbc3c05726cc02fd4cbf4976eaa9afd5138fe8376435b9fc61d2fc0eb06e3";
+ final public static BigInteger G = new BigInteger("2");
- static void saveSharedPref(String shared_preferences_key, JSONObject content) {
+ final public static int
+ CUSTOM_PROVIDER_ADDED = 0,
+ CORRECTLY_DOWNLOADED_JSON_FILES = 1,
+ INCORRECTLY_DOWNLOADED_JSON_FILES = 2,
+ SRP_AUTHENTICATION_SUCCESSFUL = 3,
+ SRP_AUTHENTICATION_FAILED = 4,
+ SRP_REGISTRATION_SUCCESSFUL = 5,
+ SRP_REGISTRATION_FAILED = 6,
+ LOGOUT_SUCCESSFUL = 7,
+ LOGOUT_FAILED = 8,
+ CORRECTLY_DOWNLOADED_CERTIFICATE = 9,
+ INCORRECTLY_DOWNLOADED_CERTIFICATE = 10,
+ CORRECTLY_UPDATED_PROVIDER_DOT_JSON = 11,
+ INCORRECTLY_UPDATED_PROVIDER_DOT_JSON = 12,
+ CORRECTLY_DOWNLOADED_ANON_CERTIFICATE = 13,
+ INCORRECTLY_DOWNLOADED_ANON_CERTIFICATE = 14
+ ;
+
+
+ private static boolean checkSharedPrefs() {
+ try {
+ shared_preferences = Dashboard.getAppContext().getSharedPreferences(PREFERENCES_KEY,Context.MODE_PRIVATE);
+ } catch (Exception e) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Saves a JSON object into class scope Shared Preferences
+ * @param shared_preferences_key
+ * @param content
+ */
+ public static void saveSharedPref(String shared_preferences_key, JSONObject content) {
SharedPreferences.Editor shared_preferences_editor = shared_preferences
.edit();
shared_preferences_editor.putString(shared_preferences_key,
content.toString());
shared_preferences_editor.commit();
- System.out.println("Shared preferences updated: key = "
- + shared_preferences_key
- + " Content = "
- + shared_preferences.getString(
- shared_preferences_key, "Default"));
}
- static void rescueJSONException(JSONException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
+ /**
+ * Saves a String object into class scope Shared Preferences
+ * @param shared_preferences_key
+ * @param content
+ */
+ public static void saveSharedPref(String shared_preferences_key, String content) {
+
+ SharedPreferences.Editor shared_preferences_editor = shared_preferences
+ .edit();
+ shared_preferences_editor.putString(shared_preferences_key,
+ content);
+ shared_preferences_editor.commit();
}
- static void saveFile(String filename, String content) {
- File root = Environment.getExternalStorageDirectory();
- File leap_dir = new File(root.getAbsolutePath() + File.separator + user_directory);
- if (!leap_dir.isDirectory()) {
- leap_dir.mkdir();
+ /**
+ * Saves a boolean object into class scope Shared Preferences
+ * @param shared_preferences_key
+ * @param content
+ */
+ public static void saveSharedPref(String shared_preferences_key, boolean content) {
+
+ SharedPreferences.Editor shared_preferences_editor = shared_preferences
+ .edit();
+ shared_preferences_editor.putBoolean(shared_preferences_key, content);
+ shared_preferences_editor.commit();
+ }
+
+ /**
+ * Gets String object from class scope Shared Preferences
+ * @param shared_preferences_key
+ * @return the string correspondent to the key parameter
+ */
+ public static String getStringFromSharedPref(String shared_preferences_key) {
+ String content = null;
+ if ( checkSharedPrefs() ) {
+ content = shared_preferences.getString(shared_preferences_key, "");
}
- try {
- if (!leap_dir.isDirectory()) {
- throw new IOException(
- "Unable to create directory " + user_directory + ". Maybe the SD card is mounted?");
- }
- File outputFile = new File(leap_dir, filename);
- BufferedWriter writer = new BufferedWriter(new FileWriter(outputFile));
- writer.write(content);
- writer.close();
- } catch (IOException e) {
- Log.w("leap_android", e.getMessage(), e);
+ return content;
+ }
+
+ /**
+ * Gets JSON object from class scope Shared Preferences
+ * @param shared_preferences_key
+ * @return the JSON object correspondent to the key parameter
+ */
+ public static JSONObject getJsonFromSharedPref(String shared_preferences_key) throws JSONException {
+ JSONObject content = null;
+ if ( checkSharedPrefs() ) {
+ content = new JSONObject( shared_preferences.getString(shared_preferences_key, "") );
}
+
+ return content;
}
- static FileInputStream openFileInputStream(String filename) {
+ /*
+ * This method defaults to false.
+ * If you use this method, be sure to fail-closed on false!
+ * TODO This is obviously less than ideal...solve it!
+ */
+ public static boolean getBoolFromSharedPref(String shared_preferences_key) {
+ boolean value = false;
+ if ( checkSharedPrefs() ) {
+ value = shared_preferences.getBoolean(shared_preferences_key, false);
+ }
+ return value;
+ }
+
+ /**
+ * Opens a FileInputStream from the user directory of the external storage directory.
+ * @param filename
+ * @return a file input stream
+ */
+ public static FileInputStream openFileInputStream(String filename) {
FileInputStream input_stream = null;
File root = Environment.getExternalStorageDirectory();
- File leap_dir = new File(root.getAbsolutePath() + File.separator + user_directory);
+ File leap_dir = new File(root.getAbsolutePath() + File.separator + USER_DIRECTORY);
try {
input_stream = new FileInputStream(leap_dir + File.separator + filename);
} catch (FileNotFoundException e) {
@@ -108,11 +207,40 @@ public class ConfigHelper {
return input_stream;
}
+ /**
+ * Treat the input as the MSB representation of a number,
+ * and lop off leading zero elements. For efficiency, the
+ * input is simply returned if no leading zeroes are found.
+ *
+ * @param in array to be trimmed
+ */
+ public static byte[] trim(byte[] in) {
+ if(in.length == 0 || in[0] != 0)
+ return in;
+
+ int len = in.length;
+ int i = 1;
+ while(in[i] == 0 && i < len)
+ ++i;
+ byte[] ret = new byte[len - i];
+ System.arraycopy(in, i, ret, 0, len - i);
+ return ret;
+ }
+
+ /**
+ * Sets class scope Shared Preferences
+ * @param shared_preferences
+ */
public static void setSharedPreferences(
SharedPreferences shared_preferences) {
ConfigHelper.shared_preferences = shared_preferences;
}
+ /**
+ * Adds a new X509 certificate given its input stream and its provider name
+ * @param provider used to store the certificate in the keystore
+ * @param inputStream from which X509 certificate must be generated.
+ */
public static void addTrustedCertificate(String provider, InputStream inputStream) {
CertificateFactory cf;
try {
@@ -129,14 +257,22 @@ public class ConfigHelper {
}
}
+ /**
+ * Adds a new X509 certificate given in its string from and using its provider name
+ * @param provider used to store the certificate in the keystore
+ * @param certificate
+ */
public static void addTrustedCertificate(String provider, String certificate) {
String filename_to_save = provider + "_certificate.cer";
- saveFile(filename_to_save, certificate);
CertificateFactory cf;
try {
cf = CertificateFactory.getInstance("X.509");
X509Certificate cert =
(X509Certificate)cf.generateCertificate(openFileInputStream(filename_to_save));
+ if(keystore_trusted == null) {
+ keystore_trusted = KeyStore.getInstance("BKS");
+ keystore_trusted.load(null);
+ }
keystore_trusted.setCertificateEntry(provider, cert);
} catch (CertificateException e) {
// TODO Auto-generated catch block
@@ -144,43 +280,19 @@ public class ConfigHelper {
} catch (KeyStoreException e) {
// TODO Auto-generated catch block
e.printStackTrace();
- }
- }
-
- public static KeyStore getKeystore() {
- return keystore_trusted;
- }
-
- public static void getNewKeystore(InputStream leap_keystore) {
- try {
- keystore_trusted = KeyStore.getInstance("BKS");
- try {
- // Initialize the keystore with the provided trusted certificates
- // Also provide the password of the keystore
- keystore_trusted.load(leap_keystore, "uer92jf".toCharArray());
- //keystore_trusted.load(null, null);
- } finally {
- leap_keystore.close();
- }
- } catch (KeyStoreException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
- } catch (CertificateException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
-
- public static int getSrpAuthenticationFailed() {
- return SRP_AUTHENTICATION_FAILED;
- }static String extractProviderName(String provider_main_url) {
-
- return null;
+
+ /**
+ * @return class wide keystore
+ */
+ public static KeyStore getKeystore() {
+ return keystore_trusted;
}
}
diff --git a/src/se/leap/leapclient/ConfigurationWizard.java b/src/se/leap/leapclient/ConfigurationWizard.java
index 0d44522..ce27942 100644
--- a/src/se/leap/leapclient/ConfigurationWizard.java
+++ b/src/se/leap/leapclient/ConfigurationWizard.java
@@ -1,21 +1,20 @@
package se.leap.leapclient;
import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
import java.util.Iterator;
-import java.util.Scanner;
import org.json.JSONException;
import org.json.JSONObject;
import se.leap.leapclient.ProviderAPIResultReceiver.Receiver;
import se.leap.leapclient.ProviderListContent.ProviderItem;
+import se.leap.leapclient.R;
import android.app.Activity;
import android.app.DialogFragment;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
+import android.app.ProgressDialog;
import android.content.Intent;
import android.content.res.AssetManager;
import android.os.Bundle;
@@ -23,33 +22,24 @@ import android.os.Handler;
import android.view.View;
import android.widget.Toast;
-
/**
- * An activity representing a list of Providers. This activity
- * has different presentations for handset and tablet-size devices. On
- * handsets, the activity presents a list of items, which when touched,
- * lead to a {@link DashboardActivity} representing
- * item details. On tablets, the activity presents the list of items and
- * item details side-by-side using two vertical panes.
- * <p>
- * The activity makes heavy use of fragments. The list of items is a
- * {@link ProviderListFragment} and the item details
- * (if present) is a {@link DashboardFragment}.
- * <p>
- * This activity also implements the required
- * {@link ProviderListFragment.Callbacks} interface
- * to listen for item selections.
+ * Activity that builds and shows the list of known available providers.
+ *
+ * It also allows the user to enter custom providers with a button.
+ *
+ * @author parmegv
+ *
*/
public class ConfigurationWizard extends Activity
- implements ProviderListFragment.Callbacks, NewProviderDialog.NewProviderDialogInterface, Receiver {
+implements ProviderListFragment.Callbacks, NewProviderDialog.NewProviderDialogInterface, Receiver {
-
- /**
- * Whether or not the activity is in two-pane mode, i.e. running on a tablet
- * device.
- */
- private boolean mTwoPane;
+ private ProviderItem mSelectedProvider;
+ private ProgressDialog mProgressDialog;
+ private Intent mConfigState = new Intent();
+ protected static final String PROVIDER_SET = "PROVIDER SET";
+ protected static final String SERVICES_RETRIEVED = "SERVICES RETRIEVED";
+
public ProviderAPIResultReceiver providerAPI_result_receiver;
@Override
@@ -57,16 +47,14 @@ public class ConfigurationWizard extends Activity
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_configuration_wizard);
+
+ providerAPI_result_receiver = new ProviderAPIResultReceiver(new Handler());
+ providerAPI_result_receiver.setReceiver(this);
ConfigHelper.setSharedPreferences(getSharedPreferences(ConfigHelper.PREFERENCES_KEY, MODE_PRIVATE));
loadPreseededProviders();
- if(ConfigHelper.getKeystore() == null) {
- InputStream keystore_input_stream = getResources().openRawResource(R.raw.leapkeystore);
- ConfigHelper.getNewKeystore(keystore_input_stream);
- }
-
// Only create our fragments if we're not restoring a saved instance
if ( savedInstanceState == null ){
// TODO Some welcome screen?
@@ -82,7 +70,103 @@ public class ConfigurationWizard extends Activity
// TODO: If exposing deep links into your app, handle intents here.
}
- private void loadPreseededProviders() {
+ @Override
+ public void onReceiveResult(int resultCode, Bundle resultData) {
+ if(resultCode == ConfigHelper.CUSTOM_PROVIDER_ADDED){
+ ProviderListFragment providerList = new ProviderListFragment();
+
+ FragmentManager fragmentManager = getFragmentManager();
+ fragmentManager.beginTransaction()
+ .replace(R.id.configuration_wizard_layout, providerList, "providerlist")
+ .commit();
+ }
+ else if(resultCode == ConfigHelper.CORRECTLY_UPDATED_PROVIDER_DOT_JSON) {
+ JSONObject provider_json;
+ try {
+ provider_json = new JSONObject(resultData.getString(ConfigHelper.PROVIDER_KEY));
+ boolean danger_on = resultData.getBoolean(ConfigHelper.DANGER_ON);
+ ConfigHelper.saveSharedPref(ConfigHelper.PROVIDER_KEY, provider_json);
+ ConfigHelper.saveSharedPref(ConfigHelper.DANGER_ON, danger_on);
+ ConfigHelper.saveSharedPref(ConfigHelper.ALLOWED_ANON, provider_json.getJSONObject(ConfigHelper.SERVICE_KEY).getBoolean(ConfigHelper.ALLOWED_ANON));
+
+ mConfigState.setAction(PROVIDER_SET);
+
+ mProgressDialog.setMessage(getResources().getString(R.string.config_downloading_services));
+ downloadJSONFiles(mSelectedProvider);
+ } catch (JSONException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+
+ mProgressDialog.dismiss();
+ Toast.makeText(this, getResources().getString(R.string.config_error_parsing), Toast.LENGTH_LONG);
+ setResult(RESULT_CANCELED, mConfigState);
+ finish();
+ }
+ }
+ else if(resultCode == ConfigHelper.INCORRECTLY_UPDATED_PROVIDER_DOT_JSON) {
+ mProgressDialog.dismiss();
+ Toast.makeText(getApplicationContext(), R.string.incorrectly_updated_provider_dot_json_message, Toast.LENGTH_LONG).show();
+ setResult(RESULT_CANCELED, mConfigState);
+ finish();
+ }
+ else if(resultCode == ConfigHelper.CORRECTLY_DOWNLOADED_JSON_FILES) {
+ if (ConfigHelper.getBoolFromSharedPref(ConfigHelper.ALLOWED_ANON)){
+ mProgressDialog.setMessage(getResources().getString(R.string.config_downloading_certificates));
+ mConfigState.putExtra(SERVICES_RETRIEVED, true);
+ downloadAnonCert();
+ } else {
+ mProgressDialog.dismiss();
+ Toast.makeText(getApplicationContext(), R.string.success, Toast.LENGTH_LONG).show();
+ setResult(RESULT_OK);
+ finish();
+ }
+ }
+ else if(resultCode == ConfigHelper.INCORRECTLY_DOWNLOADED_JSON_FILES) {
+ Toast.makeText(getApplicationContext(), R.string.incorrectly_downloaded_json_files_message, Toast.LENGTH_LONG).show();
+ setResult(RESULT_CANCELED, mConfigState);
+ finish();
+ }
+ else if(resultCode == ConfigHelper.CORRECTLY_DOWNLOADED_CERTIFICATE) {
+ mProgressDialog.dismiss();
+ Toast.makeText(getApplicationContext(), R.string.correctly_downloaded_json_files_message, Toast.LENGTH_LONG).show();
+ Toast.makeText(getApplicationContext(), R.string.success, Toast.LENGTH_LONG).show();
+ //mConfigState.putExtra(CERTIFICATE_RETRIEVED, true); // If this isn't the last step and finish() is moved...
+ setResult(RESULT_OK);
+ finish();
+ } else if(resultCode == ConfigHelper.INCORRECTLY_DOWNLOADED_CERTIFICATE) {
+ mProgressDialog.dismiss();
+ Toast.makeText(getApplicationContext(), R.string.incorrectly_downloaded_certificate_message, Toast.LENGTH_LONG).show();
+ setResult(RESULT_CANCELED, mConfigState);
+ finish();
+ }
+ }
+
+ /**
+ * Callback method from {@link ProviderListFragment.Callbacks}
+ * indicating that the item with the given ID was selected.
+ */
+ @Override
+ public void onItemSelected(String id) {
+ //TODO Code 2 pane view
+ Iterator<ProviderItem> preseeded_providers_iterator = ProviderListContent.ITEMS.iterator();
+ while(preseeded_providers_iterator.hasNext())
+ {
+ ProviderItem provider = preseeded_providers_iterator.next();
+ if(provider.id.equalsIgnoreCase(id))
+ {
+ mProgressDialog = ProgressDialog.show(this, getResources().getString(R.string.config_wait_title), getResources().getString(R.string.config_connecting_provider), true);
+ mSelectedProvider = provider;
+ saveProviderJson(mSelectedProvider);
+ }
+ }
+ }
+
+ /**
+ * Loads providers data from url file contained in the project
+ * @return true if the file was read correctly
+ */
+ private boolean loadPreseededProviders() {
+ boolean loaded_preseeded_providers = false;
AssetManager asset_manager = getAssets();
String[] urls_filepaths = null;
try {
@@ -95,126 +179,131 @@ public class ConfigurationWizard extends Activity
boolean custom = false;
provider_name = url_filepath.subSequence(0, url_filepath.indexOf(".")).toString();
if(ProviderListContent.ITEMS.isEmpty()) //TODO I have to implement a way of checking if a provider new or is already present in that ITEMS list
- ProviderListContent.addItem(new ProviderItem(provider_name, asset_manager.open(url_files_folder + "/" + url_filepath), custom));
+ ProviderListContent.addItem(new ProviderItem(provider_name, asset_manager.open(url_files_folder + "/" + url_filepath), custom, true)); // By default, it trusts the provider
+ loaded_preseeded_providers = true;
}
} catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
+ loaded_preseeded_providers = false;
+ }
+
+ return loaded_preseeded_providers;
}
- /**
- * Callback method from {@link ProviderListFragment.Callbacks}
- * indicating that the item with the given ID was selected.
+ /**
+ * Saves provider.json file associated with provider.
+ *
+ * If the provider is custom, the file has already been downloaded so we load it from memory.
+ * If not, the file is updated using the provider's URL.
+ * @param provider
*/
- @Override
- public void onItemSelected(String id) {
- if (mTwoPane) {
- // TODO Hmmm...is this how we should do this? What if it /is/ two pane?
- } else {
- // In single-pane mode, simply start the detail activity
- // for the selected item ID.
-
- Iterator<ProviderItem> preseeded_providers_iterator = ProviderListContent.ITEMS.iterator();
- while(preseeded_providers_iterator.hasNext())
- {
- ProviderItem current_provider_item = preseeded_providers_iterator.next();
- if(current_provider_item.id.equalsIgnoreCase(id))
- {
- try {
- saveProviderJson(current_provider_item);
- downloadJSONFiles(current_provider_item);
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- }
- }
+ private void saveProviderJson(ProviderItem provider) {
+ JSONObject provider_json = new JSONObject();
+ try {
+ if(!provider.custom) {
+ updateProviderDotJson(provider.name, provider.provider_json_url, provider.danger_on);
+ } else {
+ // FIXME!! We should we be updating our seeded providers list at ConfigurationWizard onStart() ?
+ // I think yes, but if so, where does this list live? leap.se, as it's the non-profit project for the software?
+ // If not, we should just be getting names/urls, and fetching the provider.json like in custom entries
+ provider_json = provider.provider_json;
+ ConfigHelper.saveSharedPref(ConfigHelper.PROVIDER_KEY, provider_json);
+ ConfigHelper.saveSharedPref(ConfigHelper.ALLOWED_ANON, provider_json.getJSONObject(ConfigHelper.SERVICE_KEY).getBoolean(ConfigHelper.ALLOWED_ANON));
+ ConfigHelper.saveSharedPref(ConfigHelper.DANGER_ON, provider.danger_on);
+
+ mProgressDialog.setMessage(getResources().getString(R.string.config_downloading_services));
+ downloadJSONFiles(mSelectedProvider);
+ }
+ } catch (JSONException e) {
+ setResult(RESULT_CANCELED);
+ finish();
+ }
}
- private void saveProviderJson(ProviderItem current_provider_item) {
- AssetManager assets_manager = getAssets();
- JSONObject provider_json = new JSONObject();
- try {
- String provider_contents = "";
- if(!current_provider_item.custom)
- provider_contents = new Scanner(new InputStreamReader(assets_manager.open(current_provider_item.provider_json_filename))).useDelimiter("\\A").next();
- else
- provider_contents = new Scanner(ConfigHelper.openFileInputStream(current_provider_item.provider_json_filename)).useDelimiter("\\A").next();
- provider_json = new JSONObject(provider_contents);
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (JSONException e) {
- ConfigHelper.rescueJSONException(e);
- }
- ConfigHelper.saveSharedPref(ConfigHelper.provider_key, provider_json);
- }
-
- private void downloadJSONFiles(ProviderItem current_provider_item) throws IOException {
- providerAPI_result_receiver = new ProviderAPIResultReceiver(new Handler());
- providerAPI_result_receiver.setReceiver(this);
-
+ /**
+ * Asks ProviderAPI to download provider site's certificate and eip-service.json
+ *
+ * URLs are fetched from the provider parameter
+ * @param provider from which certificate and eip-service.json files are going to be downloaded
+ */
+ private void downloadJSONFiles(ProviderItem provider) {
Intent provider_API_command = new Intent(this, ProviderAPI.class);
Bundle method_and_parameters = new Bundle();
- method_and_parameters.putString(ConfigHelper.provider_key, current_provider_item.name);
- method_and_parameters.putString(ConfigHelper.cert_key, current_provider_item.cert_json_url);
- method_and_parameters.putString(ConfigHelper.eip_service_key, current_provider_item.eip_service_json_url);
+ method_and_parameters.putString(ConfigHelper.PROVIDER_KEY, provider.name);
+ method_and_parameters.putString(ConfigHelper.MAIN_CERT_KEY, provider.cert_json_url);
+ method_and_parameters.putString(ConfigHelper.EIP_SERVICE_KEY, provider.eip_service_json_url);
+ method_and_parameters.putBoolean(ConfigHelper.DANGER_ON, provider.danger_on);
- provider_API_command.putExtra(ConfigHelper.downloadJsonFilesBundleExtra, method_and_parameters);
- provider_API_command.putExtra("receiver", providerAPI_result_receiver);
+ provider_API_command.putExtra(ConfigHelper.DOWNLOAD_JSON_FILES_BUNDLE_EXTRA, method_and_parameters);
+ provider_API_command.putExtra(ConfigHelper.RECEIVER_KEY, providerAPI_result_receiver);
startService(provider_API_command);
}
+ /**
+ * Asks ProviderAPI to download an anonymous (anon) VPN certificate.
+ */
+ private void downloadAnonCert() {
+ Intent provider_API_command = new Intent(this, ProviderAPI.class);
+
+ Bundle method_and_parameters = new Bundle();
+
+ method_and_parameters.putString(ConfigHelper.TYPE_OF_CERTIFICATE, ConfigHelper.ANON_CERTIFICATE);
+
+ provider_API_command.putExtra(ConfigHelper.DOWNLOAD_CERTIFICATE, method_and_parameters);
+ provider_API_command.putExtra(ConfigHelper.RECEIVER_KEY, providerAPI_result_receiver);
+
+ startService(provider_API_command);
+ }
+
+ /**
+ * Open the new provider dialog
+ * @param view from which the dialog is showed
+ */
public void addNewProvider(View view) {
FragmentTransaction fragment_transaction = getFragmentManager().beginTransaction();
- Fragment previous_new_provider_dialog = getFragmentManager().findFragmentByTag("newProviderDialog");
+ Fragment previous_new_provider_dialog = getFragmentManager().findFragmentByTag(ConfigHelper.NEW_PROVIDER_DIALOG);
if (previous_new_provider_dialog != null) {
fragment_transaction.remove(previous_new_provider_dialog);
}
fragment_transaction.addToBackStack(null);
DialogFragment newFragment = NewProviderDialog.newInstance();
- newFragment.show(fragment_transaction, "newProviderDialog");
+ newFragment.show(fragment_transaction, ConfigHelper.NEW_PROVIDER_DIALOG);
}
@Override
- public void saveProvider(String provider_main_url) {
- providerAPI_result_receiver = new ProviderAPIResultReceiver(new Handler());
- providerAPI_result_receiver.setReceiver(this);
-
+ public void saveProvider(String provider_main_url, boolean danger_on) {
Intent provider_API_command = new Intent(this, ProviderAPI.class);
Bundle method_and_parameters = new Bundle();
- method_and_parameters.putString(ConfigHelper.provider_main_url, provider_main_url);
+ method_and_parameters.putString(ConfigHelper.PROVIDER_MAIN_URL, provider_main_url);
+ method_and_parameters.putBoolean(ConfigHelper.DANGER_ON, danger_on);
- provider_API_command.putExtra(ConfigHelper.downloadNewProviderDotJSON, method_and_parameters);
- provider_API_command.putExtra("receiver", providerAPI_result_receiver);
+ provider_API_command.putExtra(ConfigHelper.DOWNLOAD_NEW_PROVIDER_DOTJSON, method_and_parameters);
+ provider_API_command.putExtra(ConfigHelper.RECEIVER_KEY, providerAPI_result_receiver);
startService(provider_API_command);
}
+
+ /**
+ * Asks ProviderAPI to download a new provider.json file
+ * @param provider_name
+ * @param provider_json_url
+ * @param danger_on tells if HTTPS client should bypass certificate errors
+ */
+ public void updateProviderDotJson(String provider_name, String provider_json_url, boolean danger_on) {
+ Intent provider_API_command = new Intent(this, ProviderAPI.class);
- @Override
- public void onReceiveResult(int resultCode, Bundle resultData) {
- if(resultCode == ConfigHelper.CUSTOM_PROVIDER_ADDED){
- ProviderListFragment providerList = new ProviderListFragment();
+ Bundle method_and_parameters = new Bundle();
+ method_and_parameters.putString(ConfigHelper.PROVIDER_NAME, provider_name);
+ method_and_parameters.putString(ConfigHelper.PROVIDER_JSON_URL, provider_json_url);
+ method_and_parameters.putBoolean(ConfigHelper.DANGER_ON, danger_on);
- FragmentManager fragmentManager = getFragmentManager();
- 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);
- Toast.makeText(getApplicationContext(), "You have not entered a LEAP provider URL", Toast.LENGTH_LONG).show();
- }
+ provider_API_command.putExtra(ConfigHelper.UPDATE_PROVIDER_DOTJSON, method_and_parameters);
+ provider_API_command.putExtra(ConfigHelper.RECEIVER_KEY, providerAPI_result_receiver);
+
+ startService(provider_API_command);
}
}
diff --git a/src/se/leap/leapclient/Dashboard.java b/src/se/leap/leapclient/Dashboard.java
index dce99b1..dd8ee33 100644
--- a/src/se/leap/leapclient/Dashboard.java
+++ b/src/se/leap/leapclient/Dashboard.java
@@ -1,44 +1,66 @@
package se.leap.leapclient;
+import org.apache.http.cookie.Cookie;
+import org.apache.http.impl.cookie.BasicClientCookie;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import se.leap.leapclient.ProviderAPIResultReceiver.Receiver;
+import se.leap.leapclient.R;
import se.leap.openvpn.AboutFragment;
import se.leap.openvpn.MainActivity;
import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.DialogFragment;
import android.app.Fragment;
import android.app.FragmentTransaction;
+import android.content.Context;
+import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
+import android.os.Handler;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
+import android.view.ViewGroup;
import android.view.ViewStub;
import android.widget.CompoundButton;
import android.widget.Switch;
import android.widget.TextView;
+import android.widget.Toast;
-public class Dashboard extends Activity {
+public class Dashboard extends Activity implements LogInDialog.LogInDialogInterface, Receiver {
protected static final int CONFIGURE_LEAP = 0;
+ private static Context app;
private static SharedPreferences preferences;
private static Provider provider;
private TextView providerNameTV;
private TextView eipTypeTV;
+ public ProviderAPIResultReceiver providerAPI_result_receiver;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+
+ app = this;
+
setContentView(R.layout.client_dashboard);
preferences = getSharedPreferences(ConfigHelper.PREFERENCES_KEY,MODE_PRIVATE);
-
+ if(ConfigHelper.shared_preferences == null)
+ ConfigHelper.setSharedPreferences(preferences);
+
// Check if we have preferences, run configuration wizard if not
// TODO We should do a better check for config that this!
- if (!preferences.contains("provider") )
- startActivityForResult(new Intent(this,ConfigurationWizard.class),CONFIGURE_LEAP);
- else
+ if (preferences.contains("provider") && preferences.getString(ConfigHelper.PROVIDER_KEY, null) != null)
buildDashboard();
+ else
+ startActivityForResult(new Intent(this,ConfigurationWizard.class),CONFIGURE_LEAP);
}
@Override
@@ -50,8 +72,26 @@ public class Dashboard extends Activity {
buildDashboard();
} else {
- // Something went wrong... TODO figure out what
- // TODO Error dialog
+ // Something went wrong in configuration
+ AlertDialog.Builder alertBuilder = new AlertDialog.Builder(getAppContext());
+ alertBuilder.setTitle(getResources().getString(R.string.setup_error_title));
+ alertBuilder
+ .setMessage(getResources().getString(R.string.setup_error_text))
+ .setCancelable(false)
+ .setPositiveButton(getResources().getString(R.string.setup_error_configure_button), new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ startActivityForResult(new Intent(getAppContext(),ConfigurationWizard.class),CONFIGURE_LEAP);
+ }
+ })
+ .setNegativeButton(getResources().getString(R.string.setup_error_close_button), new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ SharedPreferences.Editor prefsEdit = getSharedPreferences(ConfigHelper.PREFERENCES_KEY, MODE_PRIVATE).edit();
+ prefsEdit.remove(ConfigHelper.PROVIDER_KEY).commit();
+ finish();
+ }
+ });
}
}
}
@@ -73,7 +113,7 @@ public class Dashboard extends Activity {
private void serviceItemEIP() {
// FIXME Provider service (eip/openvpn)
- View eipOverview = ((ViewStub) findViewById(R.id.eipOverviewStub)).inflate();
+ ((ViewStub) findViewById(R.id.eipOverviewStub)).inflate();
// Set our EIP type title
eipTypeTV = (TextView) findViewById(R.id.eipType);
@@ -101,6 +141,23 @@ public class Dashboard extends Activity {
}
@Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ JSONObject provider_json;
+ try {
+ provider_json = ConfigHelper.getJsonFromSharedPref(ConfigHelper.PROVIDER_KEY);
+ JSONObject service_description = provider_json.getJSONObject(ConfigHelper.SERVICE_KEY);
+ if(service_description.getBoolean(ConfigHelper.ALLOW_REGISTRATION_KEY)) {
+ menu.findItem(R.id.login_button).setVisible(true);
+ menu.findItem(R.id.logout_button).setVisible(true);
+ }
+ } catch (JSONException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ return true;
+ }
+
+ @Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.client_dashboard, menu);
@@ -128,6 +185,13 @@ public class Dashboard extends Activity {
intent = new Intent(this,MainActivity.class);
startActivity(intent);
return true;
+ case R.id.login_button:
+ View view = ((ViewGroup)findViewById(android.R.id.content)).getChildAt(0);
+ logInDialog(view);
+ return true;
+ case R.id.logout_button:
+ logOut();
+ return true;
default:
return super.onOptionsItemSelected(item);
}
@@ -139,4 +203,124 @@ public class Dashboard extends Activity {
// TODO Expand the one line overview item to show some details
}
+ @Override
+ public void authenticate(String username, String password) {
+ providerAPI_result_receiver = new ProviderAPIResultReceiver(new Handler());
+ providerAPI_result_receiver.setReceiver(this);
+
+ Intent provider_API_command = new Intent(this, ProviderAPI.class);
+
+ Bundle method_and_parameters = new Bundle();
+ method_and_parameters.putString(ConfigHelper.USERNAME_KEY, username);
+ method_and_parameters.putString(ConfigHelper.PASSWORD_KEY, password);
+
+ JSONObject provider_json;
+ try {
+ provider_json = new JSONObject(preferences.getString(ConfigHelper.PROVIDER_KEY, ""));
+ method_and_parameters.putString(ConfigHelper.API_URL_KEY, provider_json.getString(ConfigHelper.API_URL_KEY) + "/" + provider_json.getString(ConfigHelper.API_VERSION_KEY));
+ } catch (JSONException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ provider_API_command.putExtra(ConfigHelper.SRP_AUTH, method_and_parameters);
+ provider_API_command.putExtra(ConfigHelper.RECEIVER_KEY, providerAPI_result_receiver);
+
+ startService(provider_API_command);
+ }
+
+ /**
+ * Asks ProviderAPI to log out.
+ */
+ public void logOut() {
+ providerAPI_result_receiver = new ProviderAPIResultReceiver(new Handler());
+ providerAPI_result_receiver.setReceiver(this);
+ Intent provider_API_command = new Intent(this, ProviderAPI.class);
+
+ Bundle method_and_parameters = new Bundle();
+
+ JSONObject provider_json;
+ try {
+ provider_json = new JSONObject(preferences.getString(ConfigHelper.PROVIDER_KEY, ""));
+ method_and_parameters.putString(ConfigHelper.API_URL_KEY, provider_json.getString(ConfigHelper.API_URL_KEY) + "/" + provider_json.getString(ConfigHelper.API_VERSION_KEY));
+ } catch (JSONException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ provider_API_command.putExtra(ConfigHelper.LOG_OUT, method_and_parameters);
+ provider_API_command.putExtra(ConfigHelper.RECEIVER_KEY, providerAPI_result_receiver);
+
+ startService(provider_API_command);
+ }
+
+ /**
+ * Shows the log in dialog.
+ * @param view from which the dialog is created.
+ */
+ public void logInDialog(View view) {
+ FragmentTransaction fragment_transaction = getFragmentManager().beginTransaction();
+ Fragment previous_log_in_dialog = getFragmentManager().findFragmentByTag(ConfigHelper.LOG_IN_DIALOG);
+ if (previous_log_in_dialog != null) {
+ fragment_transaction.remove(previous_log_in_dialog);
+ }
+ fragment_transaction.addToBackStack(null);
+
+ DialogFragment newFragment = LogInDialog.newInstance();
+ newFragment.show(fragment_transaction, ConfigHelper.LOG_IN_DIALOG);
+ }
+
+ /**
+ * Asks ProviderAPI to download an authenticated OpenVPN certificate.
+ * @param session_id cookie for the server to allow us to download the certificate.
+ */
+ private void downloadAuthedUserCertificate(Cookie session_id) {
+ providerAPI_result_receiver = new ProviderAPIResultReceiver(new Handler());
+ providerAPI_result_receiver.setReceiver(this);
+
+ Intent provider_API_command = new Intent(this, ProviderAPI.class);
+
+ Bundle method_and_parameters = new Bundle();
+ method_and_parameters.putString(ConfigHelper.TYPE_OF_CERTIFICATE, ConfigHelper.AUTHED_CERTIFICATE);
+ method_and_parameters.putString(ConfigHelper.SESSION_ID_COOKIE_KEY, session_id.getName());
+ method_and_parameters.putString(ConfigHelper.SESSION_ID_KEY, session_id.getValue());
+
+ provider_API_command.putExtra(ConfigHelper.DOWNLOAD_CERTIFICATE, method_and_parameters);
+ provider_API_command.putExtra(ConfigHelper.RECEIVER_KEY, providerAPI_result_receiver);
+
+ startService(provider_API_command);
+ }
+
+ @Override
+ public void onReceiveResult(int resultCode, Bundle resultData) {
+ if(resultCode == ConfigHelper.SRP_AUTHENTICATION_SUCCESSFUL){
+ String session_id_cookie_key = resultData.getString(ConfigHelper.SESSION_ID_COOKIE_KEY);
+ String session_id_string = resultData.getString(ConfigHelper.SESSION_ID_KEY);
+ setResult(RESULT_OK);
+ Toast.makeText(getApplicationContext(), R.string.succesful_authentication_message, Toast.LENGTH_LONG).show();
+
+ Cookie session_id = new BasicClientCookie(session_id_cookie_key, session_id_string);
+ downloadAuthedUserCertificate(session_id);
+ } else if(resultCode == ConfigHelper.SRP_AUTHENTICATION_FAILED) {
+ setResult(RESULT_CANCELED);
+ Toast.makeText(getApplicationContext(), R.string.authentication_failed_message, Toast.LENGTH_LONG).show();
+ } else if(resultCode == ConfigHelper.LOGOUT_SUCCESSFUL) {
+ setResult(RESULT_OK);
+ Toast.makeText(getApplicationContext(), R.string.successful_log_out_message, Toast.LENGTH_LONG).show();
+ } else if(resultCode == ConfigHelper.LOGOUT_FAILED) {
+ setResult(RESULT_CANCELED);
+ Toast.makeText(getApplicationContext(), R.string.log_out_failed_message, Toast.LENGTH_LONG).show();
+ } else if(resultCode == ConfigHelper.CORRECTLY_DOWNLOADED_CERTIFICATE) {
+ setResult(RESULT_CANCELED);
+ Toast.makeText(getApplicationContext(), R.string.successful_authed_cert_downloaded_message, Toast.LENGTH_LONG).show();
+ } else if(resultCode == ConfigHelper.INCORRECTLY_DOWNLOADED_CERTIFICATE) {
+ setResult(RESULT_CANCELED);
+ Toast.makeText(getApplicationContext(), R.string.authed_cert_download_failed_message, Toast.LENGTH_LONG).show();
+ }
+ }
+
+ // Used for getting Context when outside of a class extending Context
+ public static Context getAppContext() {
+ return app;
+ }
}
diff --git a/src/se/leap/leapclient/LeapHttpClient.java b/src/se/leap/leapclient/LeapHttpClient.java
index d1908c3..42f9a52 100644
--- a/src/se/leap/leapclient/LeapHttpClient.java
+++ b/src/se/leap/leapclient/LeapHttpClient.java
@@ -9,50 +9,63 @@ import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.SingleClientConnManager;
-
import android.content.Context;
+/**
+ * Implements an HTTP client, enabling LEAP Android app to manage its own runtime keystore or bypass default Android security measures.
+ *
+ * @author rafa
+ *
+ */
public class LeapHttpClient extends DefaultHttpClient {
final Context context;
-
+
private static LeapHttpClient client;
- public LeapHttpClient(Context context) {
- this.context = context;
- }
-
- @Override
- protected ClientConnectionManager createClientConnectionManager() {
- SchemeRegistry registry = new SchemeRegistry();
- registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
- // Register for port 443 our SSLSocketFactory with our keystore
- // to the ConnectionManager
- registry.register(new Scheme("https", newSslSocketFactory(), 443));
- return new SingleClientConnManager(getParams(), registry);
- }
-
- private SSLSocketFactory newSslSocketFactory() {
- try {
- // Get an instance of the Bouncy Castle KeyStore format
- KeyStore trusted = ConfigHelper.getKeystore();
-
- // Pass the keystore to the SSLSocketFactory. The factory is responsible
- // for the verification of the server certificate.
- SSLSocketFactory sf = new SSLSocketFactory(trusted);
-
- // Hostname verification from certificate
- // http://hc.apache.org/httpcomponents-client-ga/tutorial/html/connmgmt.html#d4e506
- sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
-
- return sf;
- } catch (Exception e) {
- throw new AssertionError(e);
- }
- }
-
- public static LeapHttpClient getInstance(Context context) {
- if(client == null)
- client = new LeapHttpClient(context);
- return client;
- }
+ /**
+ * If the class scope client is null, it creates one and imports, if existing, the main certificate from Shared Preferences.
+ * @param context
+ * @return the new client.
+ */
+ public static LeapHttpClient getInstance(Context context) {
+ if(client == null) {
+ client = new LeapHttpClient(context);
+ String cert_string = ConfigHelper.getStringFromSharedPref(ConfigHelper.MAIN_CERT_KEY);
+ if(cert_string != null) {
+ ConfigHelper.addTrustedCertificate("recovered_certificate", cert_string);
+ }
+ }
+ return client;
+ }
+
+ @Override
+ protected ClientConnectionManager createClientConnectionManager() {
+ SchemeRegistry registry = new SchemeRegistry();
+ registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
+ registry.register(new Scheme("https", newSslSocketFactory(), 443));
+
+ return new SingleClientConnManager(getParams(), registry);
+ }
+
+ /**
+ * Uses keystore from ConfigHelper for the SSLSocketFactory.
+ *
+ * Sets hostname verifier to allow all hostname verifier.
+ * @return
+ */
+ private SSLSocketFactory newSslSocketFactory() {
+ try {
+ KeyStore trusted = ConfigHelper.getKeystore();
+ SSLSocketFactory sf = new SSLSocketFactory(trusted);
+ sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
+
+ return sf;
+ } catch (Exception e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ public LeapHttpClient(Context context) {
+ this.context = context;
+ }
}
diff --git a/src/se/leap/leapclient/LeapSRPSession.java b/src/se/leap/leapclient/LeapSRPSession.java
index abdf6b2..d21ccff 100644
--- a/src/se/leap/leapclient/LeapSRPSession.java
+++ b/src/se/leap/leapclient/LeapSRPSession.java
@@ -4,23 +4,33 @@ import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
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;
+/**
+ * Implements all SRP algorithm logic.
+ *
+ * It's derived from JBoss implementation, with adjustments to make it work with LEAP platform.
+ *
+ * @author parmegv
+ *
+ */
public class LeapSRPSession {
private SRPParameters params;
+ private String username;
+ private String password;
private BigInteger N;
+ private byte[] N_bytes;
private BigInteger g;
private BigInteger x;
private BigInteger v;
private BigInteger a;
private BigInteger A;
private byte[] K;
+ private SecureRandom pseudoRng;
/** 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 */
@@ -34,7 +44,7 @@ public class LeapSRPSession {
@param password, the user clear text password
@param params, the SRP parameters for the session
*/
- public LeapSRPSession(String username, char[] password, SRPParameters params)
+ public LeapSRPSession(String username, String password, SRPParameters params)
{
this(username, password, params, null);
}
@@ -44,23 +54,24 @@ public class LeapSRPSession {
@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.
+ @param abytes, the random exponent used in the A public key
*/
- public LeapSRPSession(String username, char[] password, SRPParameters params,
+ public LeapSRPSession(String username, String 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);
+ N_bytes = ConfigHelper.trim(params.N);
this.N = new BigInteger(1, N_bytes);
-
+ this.username = username;
+ this.password = password;
+
+ try {
+ pseudoRng = SecureRandom.getInstance("SHA1PRNG");
+ } catch (NoSuchAlgorithmException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
if( abytes != null ) {
A_LEN = 8*abytes.length;
/* TODO Why did they put this condition?
@@ -70,42 +81,11 @@ public class LeapSRPSession {
*/
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);
+ else
+ A_LEN = 64;
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;
}
/**
@@ -115,49 +95,38 @@ public class LeapSRPSession {
* @param salt the salt of the user
* @return x
*/
- public byte[] calculatePasswordHash(String username, char[] password, byte[] salt)
+ public byte[] calculatePasswordHash(String username, String password, byte[] salt)
{
+ //password = password.replaceAll("\\\\", "\\\\\\\\");
// Calculate x = H(s | H(U | ':' | password))
MessageDigest x_digest = newDigest();
-
- // Try to convert the username to a byte[] using UTF-8
+ // Try to convert the username to a byte[] using ISO-8859-1
byte[] user = null;
+ byte[] password_bytes = null;
byte[] colon = {};
+ String encoding = "ISO-8859-1";
try {
- user = Util.trim(username.getBytes("UTF-8"));
- colon = Util.trim(":".getBytes("UTF-8"));
+ user = ConfigHelper.trim(username.getBytes(encoding));
+ colon = ConfigHelper.trim(":".getBytes(encoding));
+ password_bytes = ConfigHelper.trim(password.getBytes(encoding));
}
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;
+ user = ConfigHelper.trim(username.getBytes());
+ colon = ConfigHelper.trim(":".getBytes());
+ password_bytes = ConfigHelper.trim(password.getBytes());
}
-
+
// Build the hash
x_digest.update(user);
x_digest.update(colon);
- x_digest.update(passBytes, 0, passBytesLength);
+ x_digest.update(password_bytes);
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());
+ byte[] x_digest_bytes = x_digest.digest();
return x_digest_bytes;
}
@@ -169,14 +138,22 @@ public class LeapSRPSession {
*/
private BigInteger calculateV(String k_string) {
BigInteger k = new BigInteger(k_string, 16);
- return k.multiply(g.modPow(x, N)); // g^x % N
+ BigInteger v = k.multiply(g.modPow(x, N)); // g^x % N
+ return v;
}
- public byte[] xor(byte[] b1, byte[] b2, int length)
+ /**
+ * Calculates the trimmed xor from two BigInteger numbers
+ * @param b1 the positive source to build first BigInteger
+ * @param b2 the positive source to build second BigInteger
+ * @param length
+ * @return
+ */
+ public byte[] xor(byte[] b1, byte[] b2)
{
//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);
+ return ConfigHelper.trim(xor_digest);
}
/**
@@ -190,18 +167,11 @@ public class LeapSRPSession {
if( a == null ) {
BigInteger one = BigInteger.ONE;
do {
- a = new BigInteger(A_LEN, Util.getPRNG());
+ a = new BigInteger(A_LEN, pseudoRng);
} 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);
+ Abytes = ConfigHelper.trim(A.toByteArray());
}
return Abytes;
}
@@ -209,35 +179,68 @@ public class LeapSRPSession {
/**
* 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.
+ * It uses a predefined k.
+ * @param salt_bytes
* @param Bbytes the parameter received from the server, in bytes
* @return the parameter M1
* @throws NoSuchAlgorithmException
*/
- public byte[] response(byte[] Bbytes) throws NoSuchAlgorithmException {
+ public byte[] response(byte[] salt_bytes, byte[] Bbytes) throws NoSuchAlgorithmException {
+ // Calculate x = H(s | H(U | ':' | password))
+ byte[] xb = calculatePasswordHash(username, password, ConfigHelper.trim(salt_bytes));
+ this.x = new BigInteger(1, xb);
+
+ // Calculate v = kg^x mod N
+ String k_string = "bf66c44a428916cad64aa7c679f3fd897ad4c375e9bbb4cbf2f5de241d618ef0";
+ this.v = calculateV(k_string);
+
+ // 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);
+ clientHash.update(xor_digest);
+
+ // clientHash = H(N) xor H(g) | H(U)
+ byte[] username_digest = newDigest().digest(ConfigHelper.trim(username.getBytes()));
+ username_digest = ConfigHelper.trim(username_digest);
+ clientHash.update(username_digest);
+
+ // clientHash = H(N) xor H(g) | H(U) | s
+ clientHash.update(ConfigHelper.trim(salt_bytes));
+
+ K = null;
+
+ // clientHash = H(N) xor H(g) | H(U) | A
+ byte[] Abytes = ConfigHelper.trim(A.toByteArray());
+ clientHash.update(Abytes);
+
// clientHash = H(N) xor H(g) | H(U) | s | A | B
- Bbytes = Util.trim(Bbytes);
- //String Bbytes_string = new BigInteger(1, Bbytes).toString(16);
+ Bbytes = ConfigHelper.trim(Bbytes);
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);
+ byte[] S_bytes = ConfigHelper.trim(S.toByteArray());
// 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);
+ K = ConfigHelper.trim(sessionDigest.digest(S_bytes));
// clientHash = H(N) xor H(g) | H(U) | A | B | K
clientHash.update(K);
- byte[] M1 = clientHash.digest();
+
+ byte[] M1 = ConfigHelper.trim(clientHash.digest());
// serverHash = Astr + M + K
+ serverHash.update(Abytes);
serverHash.update(M1);
serverHash.update(K);
+
return M1;
}
@@ -247,19 +250,16 @@ public class LeapSRPSession {
* @return the parameter S
*/
private BigInteger calculateS(byte[] Bbytes) {
- byte[] Abytes = Util.trim(A.toByteArray());
+ byte[] Abytes = ConfigHelper.trim(A.toByteArray());
+ Bbytes = ConfigHelper.trim(Bbytes);
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;
}
@@ -271,9 +271,10 @@ public class LeapSRPSession {
*/
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();
+ u_digest.update(ConfigHelper.trim(Abytes));
+ u_digest.update(ConfigHelper.trim(Bbytes));
+ byte[] u_digest_bytes = u_digest.digest();
+ return ConfigHelper.trim(new BigInteger(1, u_digest_bytes).toByteArray());
}
/**
@@ -283,22 +284,12 @@ public class LeapSRPSession {
public boolean verify(byte[] M2)
{
// M2 = H(A | M1 | K)
- byte[] myM2 = serverHash.digest();
+ M2 = ConfigHelper.trim(M2);
+ byte[] myM2 = ConfigHelper.trim(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.
*/
diff --git a/src/se/leap/leapclient/LogInDialog.java b/src/se/leap/leapclient/LogInDialog.java
new file mode 100644
index 0000000..dcb92d8
--- /dev/null
+++ b/src/se/leap/leapclient/LogInDialog.java
@@ -0,0 +1,100 @@
+package se.leap.leapclient;
+
+import se.leap.leapclient.R;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.DialogFragment;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.Toast;
+
+/**
+ * Implements the log in dialog, currently without progress dialog.
+ *
+ * It returns to the previous fragment when finished, and sends username and password to the authenticate method.
+ *
+ * It also notifies the user if the password is not valid.
+ *
+ * @author parmegv
+ *
+ */
+public class LogInDialog extends DialogFragment {
+
+ public AlertDialog onCreateDialog(Bundle savedInstanceState) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ LayoutInflater inflater = getActivity().getLayoutInflater();
+ View log_in_dialog_view = inflater.inflate(R.layout.log_in_dialog, null);
+
+ final EditText username_field = (EditText)log_in_dialog_view.findViewById(R.id.username_entered);
+ final EditText password_field = (EditText)log_in_dialog_view.findViewById(R.id.password_entered);
+
+ builder.setView(log_in_dialog_view)
+ .setPositiveButton(R.string.log_in_button, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ String username = username_field.getText().toString().trim();
+ String password = password_field.getText().toString().trim();
+ if(wellFormedPassword(password)) {
+ interface_with_Dashboard.authenticate(username, password);
+ } else {
+ password_field.setText("");
+ Toast.makeText(getActivity().getApplicationContext(), R.string.not_valid_password_message, Toast.LENGTH_LONG).show();
+ }
+ }
+ })
+ .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ dialog.cancel();
+ }
+ });
+
+ return builder.create();
+ }
+
+ /**
+ * Validates a password
+ * @param entered_password
+ * @return true if the entered password length is greater or equal to eight (8).
+ */
+ private boolean wellFormedPassword(String entered_password) {
+ return entered_password.length() >= 8;
+ }
+
+ /**
+ * Interface used to communicate LogInDialog with Dashboard.
+ *
+ * @author parmegv
+ *
+ */
+ public interface LogInDialogInterface {
+ /**
+ * Starts authentication process.
+ * @param username
+ * @param password
+ */
+ public void authenticate(String username, String password);
+ }
+
+ LogInDialogInterface interface_with_Dashboard;
+
+ /**
+ * @return a new instance of this DialogFragment.
+ */
+ public static DialogFragment newInstance() {
+ LogInDialog dialog_fragment = new LogInDialog();
+ return dialog_fragment;
+ }
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+ try {
+ interface_with_Dashboard = (LogInDialogInterface) activity;
+ } catch (ClassCastException e) {
+ throw new ClassCastException(activity.toString()
+ + " must implement NoticeDialogListener");
+ }
+ }
+}
diff --git a/src/se/leap/leapclient/NewProviderDialog.java b/src/se/leap/leapclient/NewProviderDialog.java
index 88e4711..678e805 100644
--- a/src/se/leap/leapclient/NewProviderDialog.java
+++ b/src/se/leap/leapclient/NewProviderDialog.java
@@ -1,5 +1,6 @@
package se.leap.leapclient;
+import se.leap.leapclient.R;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
@@ -8,17 +9,27 @@ import android.content.DialogInterface;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
+import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.Toast;
+/**
+ * Implements the new custom provider dialog.
+ *
+ * @author parmegv
+ *
+ */
public class NewProviderDialog extends DialogFragment {
public interface NewProviderDialogInterface {
- public void saveProvider(String url_provider);
+ public void saveProvider(String url_provider, boolean danger_on);
}
NewProviderDialogInterface interface_with_ConfigurationWizard;
+ /**
+ * @return a new instance of this DialogFragment.
+ */
public static DialogFragment newInstance() {
NewProviderDialog dialog_fragment = new NewProviderDialog();
return dialog_fragment;
@@ -27,33 +38,37 @@ public class NewProviderDialog extends DialogFragment {
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
- // Verify that the host activity implements the callback interface
try {
- // Instantiate the NoticeDialogListener so we can send events to the host
interface_with_ConfigurationWizard = (NewProviderDialogInterface) activity;
} catch (ClassCastException e) {
- // The activity doesn't implement the interface, throw exception
throw new ClassCastException(activity.toString()
+ " must implement NoticeDialogListener");
}
}
+ @Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
LayoutInflater inflater = getActivity().getLayoutInflater();
View new_provider_dialog_view = inflater.inflate(R.layout.new_provider_dialog, null);
final EditText url_input_field = (EditText)new_provider_dialog_view.findViewById(R.id.new_provider_url);
+ final CheckBox danger_checkbox = (CheckBox)new_provider_dialog_view.findViewById(R.id.danger_checkbox);
+
builder.setView(new_provider_dialog_view)
.setMessage(R.string.introduce_new_provider)
.setPositiveButton(R.string.save, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
String entered_url = url_input_field.getText().toString().trim();
+ if(!entered_url.startsWith("https://")) {
+ entered_url = "https://".concat(entered_url);
+ }
+ boolean danger_on = danger_checkbox.isChecked();
if(validURL(entered_url)) {
- interface_with_ConfigurationWizard.saveProvider(entered_url);
- Toast.makeText(getActivity().getApplicationContext(), "It seems your URL is well formed", Toast.LENGTH_LONG).show();
+ interface_with_ConfigurationWizard.saveProvider(entered_url, danger_on);
+ Toast.makeText(getActivity().getApplicationContext(), R.string.valid_url_entered, Toast.LENGTH_LONG).show();
} else {
url_input_field.setText("");
- Toast.makeText(getActivity().getApplicationContext(), "It seems your URL is not well formed", Toast.LENGTH_LONG).show();
+ Toast.makeText(getActivity().getApplicationContext(), R.string.not_valid_password_message, Toast.LENGTH_LONG).show();
}
}
})
@@ -66,6 +81,11 @@ public class NewProviderDialog extends DialogFragment {
return builder.create();
}
+ /**
+ * Checks if the entered url is valid or not.
+ * @param entered_url
+ * @return true if it's not empty nor contains only the protocol.
+ */
boolean validURL(String entered_url) {
return !entered_url.isEmpty() && entered_url.matches("http[s]?://.+") && !entered_url.replaceFirst("http[s]?://", "").isEmpty();
}
diff --git a/src/se/leap/leapclient/Provider.java b/src/se/leap/leapclient/Provider.java
index 4235acf..50bd292 100644
--- a/src/se/leap/leapclient/Provider.java
+++ b/src/se/leap/leapclient/Provider.java
@@ -11,6 +11,7 @@ import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
+import android.content.Context;
import android.app.Activity;
import android.content.SharedPreferences;
@@ -18,7 +19,7 @@ import android.content.SharedPreferences;
* @author Sean Leonard <meanderingcode@aetherislands.net>
*
*/
-final class Provider implements Serializable {
+public final class Provider implements Serializable {
private static final long serialVersionUID = 6003835972151761353L;
@@ -67,7 +68,7 @@ final class Provider implements Serializable {
//preferences = context.getgetPreferences(0); // 0 == MODE_PRIVATE, but we don't extend Android's classes...
// Load SharedPreferences
- preferences = activity.getSharedPreferences(ConfigHelper.PREFERENCES_KEY,0); // We don't get MODE_PRIVATE by importing SharedPreferences; i guess it's in Activity?
+ preferences = activity.getSharedPreferences(ConfigHelper.PREFERENCES_KEY,Context.MODE_PRIVATE);
// Inflate our provider.json data
try {
definition = new JSONObject( preferences.getString("provider", "") );
diff --git a/src/se/leap/leapclient/ProviderAPI.java b/src/se/leap/leapclient/ProviderAPI.java
index 4ffd276..00d7d82 100644
--- a/src/se/leap/leapclient/ProviderAPI.java
+++ b/src/se/leap/leapclient/ProviderAPI.java
@@ -1,20 +1,41 @@
package se.leap.leapclient;
+import java.io.ByteArrayInputStream;
import java.io.IOException;
+import java.io.InputStream;
import java.math.BigInteger;
+import java.security.KeyManagementException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
-import java.util.List;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.net.CookieHandler;
+import java.net.CookieManager;
+import java.net.HttpCookie;
import java.net.MalformedURLException;
+import java.net.SocketTimeoutException;
+import java.net.URISyntaxException;
import java.net.URL;
-import java.net.UnknownHostException;
+import java.net.URLConnection;
import java.util.Scanner;
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+
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.HttpDelete;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
+import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.protocol.ClientContext;
import org.apache.http.cookie.Cookie;
import org.apache.http.impl.client.DefaultHttpClient;
@@ -30,8 +51,18 @@ import android.app.IntentService;
import android.content.Intent;
import android.os.Bundle;
import android.os.ResultReceiver;
+import android.util.Base64;
import android.util.Log;
+/**
+ * Implements HTTP api methods used to manage communications with the provider server.
+ *
+ * It's an IntentService because it downloads data fromt he Internet, so it operates in the background.
+ *
+ * @author parmegv
+ * @author MeanderingCode
+ *
+ */
public class ProviderAPI extends IntentService {
public ProviderAPI() {
@@ -44,240 +75,533 @@ public class ProviderAPI extends IntentService {
final ResultReceiver receiver = task_for.getParcelableExtra("receiver");
Bundle task;
- if((task = task_for.getBundleExtra(ConfigHelper.downloadJsonFilesBundleExtra)) != null) {
- if(!downloadJsonFiles(task))
+ if((task = task_for.getBundleExtra(ConfigHelper.DOWNLOAD_JSON_FILES_BUNDLE_EXTRA)) != null) {
+ if(!downloadJsonFiles(task)) {
receiver.send(ConfigHelper.INCORRECTLY_DOWNLOADED_JSON_FILES, Bundle.EMPTY);
- else
+ } else {
receiver.send(ConfigHelper.CORRECTLY_DOWNLOADED_JSON_FILES, Bundle.EMPTY);
+ }
}
- else if ((task = task_for.getBundleExtra(ConfigHelper.downloadNewProviderDotJSON)) != null) {
- if(downloadNewProviderDotJSON(task))
- receiver.send(ConfigHelper.CORRECTLY_DOWNLOADED_JSON_FILES, Bundle.EMPTY);
- else
+ else if ((task = task_for.getBundleExtra(ConfigHelper.UPDATE_PROVIDER_DOTJSON)) != null) {
+ Bundle result = updateProviderDotJSON(task);
+ if(result.getBoolean(ConfigHelper.RESULT_KEY)) {
+ receiver.send(ConfigHelper.CORRECTLY_UPDATED_PROVIDER_DOT_JSON, result);
+ } else {
+ receiver.send(ConfigHelper.INCORRECTLY_UPDATED_PROVIDER_DOT_JSON, Bundle.EMPTY);
+ }
+ }
+ else if ((task = task_for.getBundleExtra(ConfigHelper.DOWNLOAD_NEW_PROVIDER_DOTJSON)) != null) {
+ if(downloadNewProviderDotJSON(task)) {
+ receiver.send(ConfigHelper.CUSTOM_PROVIDER_ADDED, Bundle.EMPTY);
+ } else {
receiver.send(ConfigHelper.INCORRECTLY_DOWNLOADED_JSON_FILES, Bundle.EMPTY);
+ }
+ }
+ else if ((task = task_for.getBundleExtra(ConfigHelper.SRP_AUTH)) != null) {
+ Bundle session_id_bundle = authenticateBySRP(task);
+ if(session_id_bundle.getBoolean(ConfigHelper.RESULT_KEY)) {
+ receiver.send(ConfigHelper.SRP_AUTHENTICATION_SUCCESSFUL, session_id_bundle);
+ } else {
+ receiver.send(ConfigHelper.SRP_AUTHENTICATION_FAILED, Bundle.EMPTY);
+ }
+ }
+ else if ((task = task_for.getBundleExtra(ConfigHelper.LOG_OUT)) != null) {
+ if(logOut(task)) {
+ receiver.send(ConfigHelper.LOGOUT_SUCCESSFUL, Bundle.EMPTY);
+ } else {
+ receiver.send(ConfigHelper.LOGOUT_FAILED, Bundle.EMPTY);
+ }
+ }
+ else if ((task = task_for.getBundleExtra(ConfigHelper.DOWNLOAD_CERTIFICATE)) != null) {
+ if(getNewCert(task)) {
+ receiver.send(ConfigHelper.CORRECTLY_DOWNLOADED_CERTIFICATE, Bundle.EMPTY);
+ } else {
+ receiver.send(ConfigHelper.INCORRECTLY_DOWNLOADED_CERTIFICATE, Bundle.EMPTY);
+ }
}
}
+ /**
+ * Downloads the main cert and the eip-service.json files given through the task parameter
+ * @param task
+ * @return true if eip-service.json was parsed as a JSON object correctly.
+ */
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);
+ String cert_url = task.getString(ConfigHelper.MAIN_CERT_KEY);
+ String eip_service_json_url = task.getString(ConfigHelper.EIP_SERVICE_KEY);
+ boolean danger_on = task.getBoolean(ConfigHelper.DANGER_ON);
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);
+ String cert_string = getStringFromProvider(cert_url, danger_on);
+ ConfigHelper.saveSharedPref(ConfigHelper.MAIN_CERT_KEY, cert_string);
+ JSONObject eip_service_json = getJSONFromProvider(eip_service_json_url, danger_on);
+ 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);
+ /**
+ * Starts the authentication process using SRP protocol.
+ *
+ * @param task containing: username, password and api url.
+ * @return a bundle with a boolean value mapped to a key named ConfigHelper.RESULT_KEY, and which is true if authentication was successful.
+ */
+ private Bundle authenticateBySRP(Bundle task) {
+ Bundle session_id_bundle = new Bundle();
- String salt = "abcd";
+ String username = (String) task.get(ConfigHelper.USERNAME_KEY);
+ String password = (String) task.get(ConfigHelper.PASSWORD_KEY);
+ String authentication_server = (String) task.get(ConfigHelper.API_URL_KEY);
- 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);
+ SRPParameters params = new SRPParameters(new BigInteger(ConfigHelper.NG_1024, 16).toByteArray(), ConfigHelper.G.toByteArray(), BigInteger.ZERO.toByteArray(), "SHA-256");
+ LeapSRPSession client = new LeapSRPSession(username, password, params);
byte[] A = client.exponential();
try {
- JSONObject saltAndB = sendAToSRPServer(authentication_server, username, new BigInteger(A).toString(16));
+ JSONObject saltAndB = sendAToSRPServer(authentication_server, username, new BigInteger(1, 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;
+ String salt = saltAndB.getString(ConfigHelper.SALT_KEY);
+ byte[] Bbytes = new BigInteger(saltAndB.getString("B"), 16).toByteArray();
+ byte[] M1 = client.response(new BigInteger(salt, 16).toByteArray(), Bbytes);
+ JSONObject session_idAndM2 = sendM1ToSRPServer(authentication_server, username, M1);
+ if( client.verify((byte[])session_idAndM2.get("M2")) == false ) {
+ session_id_bundle.putBoolean(ConfigHelper.RESULT_KEY, false);
+ } else {
+ session_id_bundle.putBoolean(ConfigHelper.RESULT_KEY, true);
+ session_id_bundle.putString(ConfigHelper.SESSION_ID_KEY, session_idAndM2.getString(ConfigHelper.SESSION_ID_KEY));
+ session_id_bundle.putString(ConfigHelper.SESSION_ID_COOKIE_KEY, session_idAndM2.getString(ConfigHelper.SESSION_ID_COOKIE_KEY));
+ }
+ } else {
+ session_id_bundle.putBoolean(ConfigHelper.RESULT_KEY, false);
}
- 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 (ClientProtocolException e) {
+ session_id_bundle.putBoolean(ConfigHelper.RESULT_KEY, false);
+ } catch (IOException e) {
+ session_id_bundle.putBoolean(ConfigHelper.RESULT_KEY, false);
+ } catch (JSONException e) {
+ session_id_bundle.putBoolean(ConfigHelper.RESULT_KEY, false);
} catch (NoSuchAlgorithmException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- return false;
+ session_id_bundle.putBoolean(ConfigHelper.RESULT_KEY, false);
}
+
+ return session_id_bundle;
}
+ /**
+ * Sends an HTTP POST request to the authentication server with the SRP Parameter A.
+ * @param server_url
+ * @param username
+ * @param clientA First SRP parameter sent
+ * @return response from authentication server
+ * @throws ClientProtocolException
+ * @throws IOException
+ * @throws JSONException
+ */
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);
+ HttpPost post = new HttpPost(server_url + "/sessions.json" + "?" + "login=" + username + "&&" + "A=" + clientA);
+ return sendToServer(post);
+ }
+
+ /**
+ * Sends an HTTP PUT request to the authentication server with the SRP Parameter M1 (or simply M).
+ * @param server_url
+ * @param username
+ * @param m1 Second SRP parameter sent
+ * @return response from authentication server
+ * @throws ClientProtocolException
+ * @throws IOException
+ * @throws JSONException
+ */
+ private JSONObject sendM1ToSRPServer(String server_url, String username, byte[] m1) throws ClientProtocolException, IOException, JSONException {
+ HttpPut put = new HttpPut(server_url + "/sessions/" + username +".json" + "?" + "client_auth" + "=" + new BigInteger(1, ConfigHelper.trim(m1)).toString(16));
+ JSONObject json_response = sendToServer(put);
+
+ JSONObject session_idAndM2 = new JSONObject();
+ if(json_response.length() > 0) {
+ byte[] M2_not_trimmed = new BigInteger(json_response.getString(ConfigHelper.M2_KEY), 16).toByteArray();
+ Cookie session_id_cookie = LeapHttpClient.getInstance(getApplicationContext()).getCookieStore().getCookies().get(0);
+ session_idAndM2.put(ConfigHelper.SESSION_ID_COOKIE_KEY, session_id_cookie.getName());
+ session_idAndM2.put(ConfigHelper.SESSION_ID_KEY, session_id_cookie.getValue());
+ session_idAndM2.put(ConfigHelper.M2_KEY, ConfigHelper.trim(M2_not_trimmed));
+ }
+ return session_idAndM2;
+ }
- HttpResponse getResponse = client.execute(post);
+ /**
+ * Executes an HTTP request expecting a JSON response.
+ * @param request
+ * @return response from authentication server
+ * @throws ClientProtocolException
+ * @throws IOException
+ * @throws JSONException
+ */
+ private JSONObject sendToServer(HttpUriRequest request) throws ClientProtocolException, IOException, JSONException {
+ DefaultHttpClient client = LeapHttpClient.getInstance(getApplicationContext());
+ HttpContext localContext = new BasicHttpContext();
+ localContext.setAttribute(ClientContext.COOKIE_STORE, client.getCookieStore());
+
+ HttpResponse getResponse = client.execute(request, 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")) {
+ if(!json_response.isNull(ConfigHelper.ERRORS_KEY) || json_response.has(ConfigHelper.ERRORS_KEY)) {
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());
+ /**
+ * Downloads a provider.json from a given URL, adding a new provider using the given name.
+ * @param task containing a boolean meaning if the provider is custom or not, another boolean meaning if the user completely trusts this provider, the provider name and its provider.json url.
+ * @return a bundle with a boolean value mapped to a key named ConfigHelper.RESULT_KEY, and which is true if the update was successful.
+ */
+ private Bundle updateProviderDotJSON(Bundle task) {
+ Bundle result = new Bundle();
+ boolean custom = task.getBoolean(ConfigHelper.CUSTOM);
+ boolean danger_on = task.getBoolean(ConfigHelper.DANGER_ON);
+ String provider_json_url = task.getString(ConfigHelper.PROVIDER_JSON_URL);
+ String provider_name = task.getString(ConfigHelper.PROVIDER_NAME);
- 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];
+ try {
+ JSONObject provider_json = getJSONFromProvider(provider_json_url, danger_on);
+ if(provider_json == null) {
+ result.putBoolean(ConfigHelper.RESULT_KEY, false);
+ } else {
+ ConfigHelper.saveSharedPref(ConfigHelper.ALLOWED_ANON, provider_json.getJSONObject(ConfigHelper.SERVICE_KEY).getBoolean(ConfigHelper.ALLOWED_ANON));
+
+ ProviderListContent.addItem(new ProviderItem(provider_name, provider_json_url, provider_json, custom, danger_on));
+ result.putBoolean(ConfigHelper.RESULT_KEY, true);
+ result.putString(ConfigHelper.PROVIDER_KEY, provider_json.toString());
+ result.putBoolean(ConfigHelper.DANGER_ON, danger_on);
+ }
+ } catch (JSONException e) {
+ result.putBoolean(ConfigHelper.RESULT_KEY, false);
}
- return json_response.getString("M2").getBytes();
+ return result;
}
+ /**
+ * Downloads a custom provider provider.json file
+ * @param task containing a boolean meaning if the user completely trusts this provider, and the provider main url entered in the new custom provider dialog.
+ * @return true if provider.json file was successfully parsed as a JSON object.
+ */
private boolean downloadNewProviderDotJSON(Bundle task) {
boolean custom = true;
- String provider_main_url = (String) task.get(ConfigHelper.provider_main_url);
+ boolean danger_on = task.getBoolean(ConfigHelper.DANGER_ON);
+
+ String provider_main_url = (String) task.get(ConfigHelper.PROVIDER_MAIN_URL);
String provider_name = provider_main_url.replaceFirst("http[s]?://", "").replaceFirst("\\/", "_");
- String provider_json_url = guessURL(provider_main_url);
- JSONObject provider_json = null;
+ String provider_json_url = guessProviderDotJsonURL(provider_main_url);
+
+ JSONObject provider_json;
try {
- provider_json = getJSONFromProvider(provider_json_url);
- } catch (IOException e) {
- // It could happen that an https site used a certificate not trusted.
- provider_json = downloadNewProviderDotJsonWithoutCert(provider_json_url);
+ provider_json = getJSONFromProvider(provider_json_url, danger_on);
+ ProviderListContent.addItem(new ProviderItem(provider_name, provider_json_url, provider_json, custom, danger_on));
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return false;
}
- if(provider_json == null) {
- return false;
- } else {
- String filename = provider_name + "_provider.json".replaceFirst("__", "_");
- ConfigHelper.saveFile(filename, provider_json.toString());
- ConfigHelper.saveSharedPref(ConfigHelper.provider_key, provider_json);
+ return true;
+ }
+
+ /**
+ * Tries to download whatever is pointed by the string_url.
+ *
+ * If danger_on flag is true, SSL exceptions will be managed by futher methods that will try to use some bypass methods.
+ * @param string_url
+ * @param danger_on if the user completely trusts this provider
+ * @return
+ */
+ private String getStringFromProvider(String string_url, boolean danger_on) {
+
+ String json_file_content = "";
+
+ URL provider_url = null;
+ int seconds_of_timeout = 1;
+ try {
+ provider_url = new URL(string_url);
+ URLConnection url_connection = provider_url.openConnection();
+ url_connection.setConnectTimeout(seconds_of_timeout*1000);
+ json_file_content = new Scanner(url_connection.getInputStream()).useDelimiter("\\A").next();
+ } catch (MalformedURLException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch(SocketTimeoutException e) {
+ return "";
+ } catch (IOException e) {
+ // TODO SSLHandshakeException
+ // This means that we have not added ca.crt to the trusted certificates.
+ if(provider_url != null && danger_on) {
+ json_file_content = getStringFromProviderWithoutValidate(provider_url);
+ }
+ //json_file_content = downloadStringFromProviderWithCACertAdded(string_url);
+ e.printStackTrace();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
- ProviderListContent.addItem(new ProviderItem(provider_name, ConfigHelper.openFileInputStream(filename), custom));
- return true;
+ return json_file_content;
+ }
+
+ /**
+ * Tries to download a string from given url without verifying the hostname.
+ *
+ * If a IOException still occurs, it tries with another bypass method: getStringFromProviderWithCACertAdded.
+ * @param string_url
+ * @return an empty string if everything fails, the url content if not.
+ */
+ private String getStringFromProviderWithoutValidate(
+ URL string_url) {
+
+ String json_string = "";
+ HostnameVerifier hostnameVerifier = new HostnameVerifier() {
+ @Override
+ public boolean verify(String hostname, SSLSession session) {
+ return true;
+ }
+ };
+
+ try {
+ HttpsURLConnection urlConnection =
+ (HttpsURLConnection)string_url.openConnection();
+ urlConnection.setHostnameVerifier(hostnameVerifier);
+ json_string = new Scanner(urlConnection.getInputStream()).useDelimiter("\\A").next();
+ } catch (MalformedURLException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (IOException e) {
+ json_string = getStringFromProviderWithCACertAdded(string_url);
+ //e.printStackTrace();
}
+
+ return json_string;
}
- private boolean downloadJsonFilesBundleExtra(Bundle task) {
- //TODO task only contains provider main url -> we need to infer cert_url, provider_name and eip_service_json_url from that.
- String provider_main_url = (String) task.get(ConfigHelper.provider_main_url);
- String provider_name = ConfigHelper.extractProviderName(provider_main_url);
- String cert_url = (String) task.get(ConfigHelper.cert_key);
- String eip_service_json_url = (String) task.get(ConfigHelper.eip_service_key);
+ /**
+ * Tries to download the contents of the provided url using main certificate from choosen provider.
+ * @param url
+ * @return an empty string if it fails, the url content if not.
+ */
+ private String getStringFromProviderWithCACertAdded(URL url) {
+ String json_file_content = "";
+
+ // Load CAs from an InputStream
+ // (could be from a resource or ByteArrayInputStream or ...)
+ String cert_string = ConfigHelper.getStringFromSharedPref(ConfigHelper.MAIN_CERT_KEY);
+ if(cert_string.isEmpty()) {
+ cert_string = downloadCertificateWithoutTrusting(url.getProtocol() + "://" + url.getHost() + "/" + "ca.crt");
+ ConfigHelper.saveSharedPref(ConfigHelper.MAIN_CERT_KEY, cert_string);
+ }
+ CertificateFactory cf;
try {
- //JSONObject provider_json = new JSONObject("{ \"provider\" : \"" + provider_name + "\"}");
- //ConfigHelper.saveSharedPref(ConfigHelper.provider_key, provider_json);
-
- /*String cert_string = getStringFromProvider(cert_url);
- JSONObject cert_json = new JSONObject("{ \"certificate\" : \"" + cert_string + "\"}");
- ConfigHelper.saveSharedPref(ConfigHelper.cert_key, cert_json);
- ConfigHelper.addTrustedCertificate(provider_name, cert_string);*/
- URL cacert = new URL(cert_url);
- ConfigHelper.addTrustedCertificate(provider_name, cacert.openStream());
- JSONObject eip_service_json = getJSONFromProvider(eip_service_json_url);
- ConfigHelper.saveSharedPref(ConfigHelper.eip_service_key, eip_service_json);
- return true;
+ cf = CertificateFactory.getInstance("X.509");
+
+ cert_string = cert_string.replaceFirst("-----BEGIN CERTIFICATE-----", "").replaceFirst("-----END CERTIFICATE-----", "").trim();
+ byte[] cert_bytes = Base64.decode(cert_string, Base64.DEFAULT);
+ InputStream caInput = new ByteArrayInputStream(cert_bytes);
+ java.security.cert.Certificate ca;
+ try {
+ ca = cf.generateCertificate(caInput);
+ System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN());
+ } finally {
+ caInput.close();
+ }
+
+ // Create a KeyStore containing our trusted CAs
+ String keyStoreType = KeyStore.getDefaultType();
+ KeyStore keyStore = KeyStore.getInstance(keyStoreType);
+ keyStore.load(null, null);
+ keyStore.setCertificateEntry("ca", ca);
+
+ // Create a TrustManager that trusts the CAs in our KeyStore
+ String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
+ TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
+ tmf.init(keyStore);
+
+ // Create an SSLContext that uses our TrustManager
+ SSLContext context = SSLContext.getInstance("TLS");
+ context.init(null, tmf.getTrustManagers(), null);
+
+ // Tell the URLConnection to use a SocketFactory from our SSLContext
+ HttpsURLConnection urlConnection =
+ (HttpsURLConnection)url.openConnection();
+ urlConnection.setSSLSocketFactory(context.getSocketFactory());
+ json_file_content = new Scanner(urlConnection.getInputStream()).useDelimiter("\\A").next();
+ } catch (CertificateException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
} catch (IOException e) {
- //TODO It could happen when the url is not valid.
+ // TODO Auto-generated catch block
e.printStackTrace();
- return false;
- } catch (JSONException e) {
- ConfigHelper.rescueJSONException(e);
- return false;
- } catch(Exception e) {
+ } catch (KeyStoreException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (NoSuchAlgorithmException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (KeyManagementException e) {
+ // TODO Auto-generated catch block
e.printStackTrace();
- return false;
}
+ return json_file_content;
}
+
+ /**
+ * Downloads the certificate from the parameter url bypassing self signed certificate SSL errors.
+ * @param certificate_url_string
+ * @return the certificate, as a string
+ */
+ private String downloadCertificateWithoutTrusting(String certificate_url_string) {
+
+ String cert_string = "";
+ HostnameVerifier hostnameVerifier = new HostnameVerifier() {
+ @Override
+ public boolean verify(String hostname, SSLSession session) {
+ return true;
+ }
+ };
+
+ TrustManager[] trustAllCerts = new TrustManager[]{
+ new X509TrustManager() {
+ public java.security.cert.X509Certificate[] getAcceptedIssuers() {
+ return null;
+ }
+ public void checkClientTrusted( java.security.cert.X509Certificate[] certs, String authType) {
+ }
+ public void checkServerTrusted( java.security.cert.X509Certificate[] certs, String authType) {
+ }
+ }
+ };
- private JSONObject downloadNewProviderDotJsonWithoutCert(
- String provider_json_url) {
- JSONObject provider_json = null;
try {
- URL provider_url = new URL(provider_json_url);
- String provider_json_string = new Scanner(provider_url.openStream()).useDelimiter("\\A").next();
- provider_json = new JSONObject(provider_json_string);
- } catch (MalformedURLException e1) {
- e1.printStackTrace();
- } catch (UnknownHostException e1) {
- e1.printStackTrace();
- } catch (IOException e1) {
- e1.printStackTrace();
- } catch (JSONException e1) {
- e1.printStackTrace();
+ URL certificate_url = new URL(certificate_url_string);
+ HttpsURLConnection urlConnection =
+ (HttpsURLConnection)certificate_url.openConnection();
+ urlConnection.setHostnameVerifier(hostnameVerifier);
+
+ SSLContext sc = SSLContext.getInstance("TLS");
+ sc.init(null, trustAllCerts, new java.security.SecureRandom());
+
+ urlConnection.setSSLSocketFactory(sc.getSocketFactory());
+
+ cert_string = new Scanner(urlConnection.getInputStream()).useDelimiter("\\A").next();
+
+ } catch (MalformedURLException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (IOException e) {
+ // This should never happen
+ e.printStackTrace();
+ } catch (NoSuchAlgorithmException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (KeyManagementException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
}
- return provider_json;
+
+ return cert_string;
}
- private String guessURL(String provider_main_url) {
- return provider_main_url + "/provider.json";
+ /**
+ * Downloads a JSON object from the given url.
+ *
+ * It first downloads the JSON object as a String, and then parses it to JSON object.
+ * @param json_url
+ * @param danger_on if the user completely trusts the certificate of the url address.
+ * @return
+ * @throws JSONException
+ */
+ private JSONObject getJSONFromProvider(String json_url, boolean danger_on) throws JSONException {
+ String json_file_content = getStringFromProvider(json_url, danger_on);
+ return new JSONObject(json_file_content);
}
- private String getStringFromProvider(String string_url) throws IOException {
-
- String json_file_content = "";
-
+ /**
+ * Tries to guess the provider.json url given the main provider url.
+ * @param provider_main_url
+ * @return the guessed provider.json url
+ */
+ private String guessProviderDotJsonURL(String provider_main_url) {
+ return provider_main_url + "/provider.json";
+ }
+
+ /**
+ * Logs out from the api url retrieved from the task.
+ * @param task containing api url from which the user will log out
+ * @return true if there were no exceptions
+ */
+ private boolean logOut(Bundle task) {
DefaultHttpClient client = LeapHttpClient.getInstance(getApplicationContext());
- HttpGet get = new HttpGet(string_url);
- // Execute the GET call and obtain the response
- HttpResponse getResponse = client.execute(get);
- HttpEntity responseEntity = getResponse.getEntity();
-
- json_file_content = new Scanner(responseEntity.getContent()).useDelimiter("\\A").next();
-
- return json_file_content;
+ int session_id_index = 0;
+ //String delete_url = task.getString(ConfigHelper.srp_server_url_key) + "/sessions/" + client.getCookieStore().getCookies().get(0).getValue();
+ try {
+ String delete_url = task.getString(ConfigHelper.API_URL_KEY) + "/logout" + "?authenticity_token=" + client.getCookieStore().getCookies().get(session_id_index).getValue();
+ HttpDelete delete = new HttpDelete(delete_url);
+ HttpResponse getResponse = client.execute(delete);
+ HttpEntity responseEntity = getResponse.getEntity();
+ responseEntity.consumeContent();
+ } catch (ClientProtocolException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ return false;
+ } catch (IndexOutOfBoundsException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ return false;
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ return false;
+ }
+ return true;
}
- private JSONObject getJSONFromProvider(String json_url) throws IOException, JSONException {
- String json_file_content = getStringFromProvider(json_url);
- return new JSONObject(json_file_content);
+
+ /**
+ * Downloads a new OpenVPN certificate, attaching authenticated cookie for authenticated certificate.
+ *
+ * @param task containing the type of the certificate to be downloaded
+ * @return true if certificate was downloaded correctly, false if provider.json or danger_on flag are not present in SharedPreferences, or if the certificate url could not be parsed as a URI, or if there was an SSL error.
+ */
+ private boolean getNewCert(Bundle task) {
+ String type_of_certificate = task.getString(ConfigHelper.TYPE_OF_CERTIFICATE);
+ try {
+ JSONObject provider_json = ConfigHelper.getJsonFromSharedPref(ConfigHelper.PROVIDER_KEY);
+ URL provider_main_url = new URL(provider_json.getString(ConfigHelper.API_URL_KEY));
+ String new_cert_string_url = provider_main_url.toString() + "/" + provider_json.getString(ConfigHelper.API_VERSION_KEY) + "/" + ConfigHelper.CERT_KEY;
+
+ if(type_of_certificate.equalsIgnoreCase(ConfigHelper.AUTHED_CERTIFICATE)) {
+ HttpCookie session_id_cookie = new HttpCookie(task.getString(ConfigHelper.SESSION_ID_COOKIE_KEY), task.getString(ConfigHelper.SESSION_ID_KEY));
+
+ CookieManager cookieManager = new CookieManager();
+ cookieManager.getCookieStore().add(provider_main_url.toURI(), session_id_cookie);
+ CookieHandler.setDefault(cookieManager);
+ }
+
+ boolean danger_on = ConfigHelper.getBoolFromSharedPref(ConfigHelper.DANGER_ON);
+ String cert_string = getStringFromProvider(new_cert_string_url, danger_on);
+ if(!cert_string.isEmpty()) {
+ ConfigHelper.saveSharedPref(ConfigHelper.CERT_KEY, cert_string);
+ return true;
+ } else {
+ return false;
+ }
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ return false;
+ } catch (JSONException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ return false;
+ } catch (URISyntaxException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ return false;
+ }
}
}
diff --git a/src/se/leap/leapclient/ProviderAPIResultReceiver.java b/src/se/leap/leapclient/ProviderAPIResultReceiver.java
index a6a8d9d..e32f6ff 100644
--- a/src/se/leap/leapclient/ProviderAPIResultReceiver.java
+++ b/src/se/leap/leapclient/ProviderAPIResultReceiver.java
@@ -4,6 +4,11 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.ResultReceiver;
+/**
+ * Implements the ResultReceiver needed by Activities using ProviderAPI to receive the results of its operations.
+ * @author parmegv
+ *
+ */
public class ProviderAPIResultReceiver extends ResultReceiver {
private Receiver mReceiver;
@@ -11,11 +16,16 @@ public class ProviderAPIResultReceiver extends ResultReceiver {
super(handler);
// TODO Auto-generated constructor stub
}
-
+
public void setReceiver(Receiver receiver) {
mReceiver = receiver;
}
+ /**
+ * Interface to enable ProviderAPIResultReceiver to receive results from the ProviderAPI IntentService.
+ * @author parmegv
+ *
+ */
public interface Receiver {
public void onReceiveResult(int resultCode, Bundle resultData);
}
diff --git a/src/se/leap/leapclient/ProviderListContent.java b/src/se/leap/leapclient/ProviderListContent.java
index dd227bf..8727b16 100644
--- a/src/se/leap/leapclient/ProviderListContent.java
+++ b/src/se/leap/leapclient/ProviderListContent.java
@@ -1,6 +1,5 @@
package se.leap.leapclient;
-import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
@@ -11,49 +10,48 @@ import java.util.Map;
import org.json.JSONException;
import org.json.JSONObject;
-
+/**
+ * Models the provider list shown in the ConfigurationWizard.
+ *
+ * @author parmegv
+ *
+ */
public class ProviderListContent {
- /**
- * An array of sample (dummy) items.
- */
public static List<ProviderItem> ITEMS = new ArrayList<ProviderItem>();
- /**
- * A map of sample (dummy) items, by ID.
- */
public static Map<String, ProviderItem> ITEM_MAP = new HashMap<String, ProviderItem>();
-
- static {
- //addItem(new ProviderItem("1", "bitmask", "https://bitmask.net/provider.json", "https://api.bitmask.net:4430/1/config/eip-service.json"));
- }
+ /**
+ * Adds a new provider item to the end of the items map, and to the items list.
+ * @param item
+ */
public static void addItem(ProviderItem item) {
ITEMS.add(item);
ITEM_MAP.put(String.valueOf(ITEMS.size()), item);
}
/**
- * A dummy item representing a piece of content.
+ * A provider item.
*/
public static class ProviderItem {
public boolean custom = false;
public String id;
public String name;
public String provider_json_url;
+ public JSONObject provider_json;
public String provider_json_filename;
public String eip_service_json_url;
public String cert_json_url;
-
- public ProviderItem(String id, String name, String provider_json_url, String eip_service_json_url, String cert_json_url) {
- this.id = id;
- this.name = name;
- this.provider_json_url = provider_json_url;
- this.eip_service_json_url = eip_service_json_url;
- this.cert_json_url = cert_json_url;
- }
+ public boolean danger_on = false;
- public ProviderItem(String name, InputStream urls_file_input_stream, boolean custom) {
+ /**
+ * @param name of the provider
+ * @param urls_file_input_stream file input stream linking with the assets url file
+ * @param custom if it's a new provider entered by the user or not
+ * @param danger_on if the user trusts completely the new provider
+ */
+ public ProviderItem(String name, InputStream urls_file_input_stream, boolean custom, boolean danger_on) {
try {
byte[] urls_file_bytes = new byte[urls_file_input_stream.available()];
@@ -62,11 +60,12 @@ public class ProviderListContent {
JSONObject file_contents = new JSONObject(urls_file_content);
id = name;
this.name = name;
- provider_json_url = (String) file_contents.get("json_provider");
- provider_json_filename = (String) file_contents.get("assets_json_provider");
- eip_service_json_url = (String) file_contents.get("json_eip_service");
- cert_json_url = (String) file_contents.get("cert");
+ provider_json_url = file_contents.getString(ConfigHelper.PROVIDER_JSON_URL);
+ provider_json_filename = file_contents.getString("assets_json_provider");
+ eip_service_json_url = file_contents.getString("json_eip_service");
+ cert_json_url = file_contents.getString(ConfigHelper.CERT_KEY);
this.custom = custom;
+ this.danger_on = danger_on;
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
@@ -76,26 +75,29 @@ public class ProviderListContent {
}
}
- public ProviderItem(String name, FileInputStream provider_json, boolean custom) {
+ /**
+ * @param name of the provider
+ * @param provider_json_url used to download provider.json file of the provider
+ * @param provider_json already downloaded
+ * @param custom if it's a new provider entered by the user or not
+ * @param danger_on if the user trusts completely the new provider
+ */
+ public ProviderItem(String name, String provider_json_url, JSONObject provider_json, boolean custom, boolean danger_on) {
try {
- byte[] urls_file_bytes = new byte[provider_json.available()];
- provider_json.read(urls_file_bytes);
- String urls_file_content = new String(urls_file_bytes);
- JSONObject file_contents = new JSONObject(urls_file_content);
id = name;
this.name = name;
- eip_service_json_url = (String) file_contents.get("api_uri") + ConfigHelper.eip_service_api_path;
- cert_json_url = (String) file_contents.get("ca_cert_uri");
+ this.provider_json_url = provider_json_url;
+ this.provider_json = provider_json;
+ eip_service_json_url = provider_json.getString(ConfigHelper.API_URL_KEY) + "/" + provider_json.getString(ConfigHelper.API_VERSION_KEY) + "/" + ConfigHelper.EIP_SERVICE_API_PATH;
+ cert_json_url = (String) provider_json.get("ca_cert_uri");
this.custom = custom;
+ this.danger_on = danger_on;
if(custom)
provider_json_filename = name + "_provider.json".replaceFirst("__", "_");
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
}
}
diff --git a/src/se/leap/leapclient/ProviderListFragment.java b/src/se/leap/leapclient/ProviderListFragment.java
index 4316e9f..ee3ee8e 100644
--- a/src/se/leap/leapclient/ProviderListFragment.java
+++ b/src/se/leap/leapclient/ProviderListFragment.java
@@ -2,9 +2,6 @@ package se.leap.leapclient;
import se.leap.leapclient.ProviderListContent.ProviderItem;
import android.app.Activity;
-import android.app.DialogFragment;
-import android.app.Fragment;
-import android.app.FragmentTransaction;
import android.app.ListFragment;
import android.os.Bundle;
import android.view.LayoutInflater;