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