diff options
author | Sean Leonard <meanderingcode@aetherislands.net> | 2013-06-20 18:36:16 -0600 |
---|---|---|
committer | Sean Leonard <meanderingcode@aetherislands.net> | 2013-06-20 18:36:16 -0600 |
commit | 195d5ab2d518f6f3960edd3c636e941830c2664d (patch) | |
tree | 81b6b0d0e0b374e82afa6fc15c32716f981f0aa4 /src/se/leap/leapclient | |
parent | 5a7e2365365a3b1f773212cefdeeaa4ee587a590 (diff) | |
parent | 3f8e2f6569f893a49bf1819416cae792c702d370 (diff) |
Merge 'parmegv/feature/authGui' into develop
Diffstat (limited to 'src/se/leap/leapclient')
-rw-r--r-- | src/se/leap/leapclient/ConfigHelper.java | 288 | ||||
-rw-r--r-- | src/se/leap/leapclient/ConfigurationWizard.java | 321 | ||||
-rw-r--r-- | src/se/leap/leapclient/Dashboard.java | 200 | ||||
-rw-r--r-- | src/se/leap/leapclient/LeapHttpClient.java | 93 | ||||
-rw-r--r-- | src/se/leap/leapclient/LeapSRPSession.java | 221 | ||||
-rw-r--r-- | src/se/leap/leapclient/LogInDialog.java | 100 | ||||
-rw-r--r-- | src/se/leap/leapclient/NewProviderDialog.java | 34 | ||||
-rw-r--r-- | src/se/leap/leapclient/Provider.java | 5 | ||||
-rw-r--r-- | src/se/leap/leapclient/ProviderAPI.java | 662 | ||||
-rw-r--r-- | src/se/leap/leapclient/ProviderAPIResultReceiver.java | 12 | ||||
-rw-r--r-- | src/se/leap/leapclient/ProviderListContent.java | 74 | ||||
-rw-r--r-- | src/se/leap/leapclient/ProviderListFragment.java | 3 |
12 files changed, 1428 insertions, 585 deletions
diff --git a/src/se/leap/leapclient/ConfigHelper.java b/src/se/leap/leapclient/ConfigHelper.java index 7476c89a..dd20112c 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 0d445227..ce279425 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 dce99b1e..dd8ee335 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 d1908c34..42f9a523 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 abdf6b2c..d21ccffe 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 00000000..dcb92d89 --- /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 88e4711c..678e805f 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 4235acfd..50bd2925 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 4ffd2762..00d7d820 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 a6a8d9d8..e32f6ffd 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 dd227bfd..8727b16b 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 4316e9f3..ee3ee8ea 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;
|