/* Swisscom Safe Connect Copyright (C) 2014 Swisscom This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.swisscom.safeconnect.utils; import android.content.Context; import android.content.SharedPreferences; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.preference.PreferenceManager; import android.util.Log; import com.swisscom.safeconnect.BuildConfig; import com.swisscom.safeconnect.security.Encryptor; import com.swisscom.safeconnect.utils.gcm.GcmManager; import org.strongswan.android.logic.VpnStateService; import java.math.BigInteger; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; /** * Created by cianci on 17.04.14. */ public class Config { //app wide used public static final String TAG = "PipeOfTrust"; public static final String LOG_FNAME = "pipeoftrust.log"; public static final String GPLAY_URL = "https://play.google.com/store/apps/details?id=com.swisscom.safeconnect"; public static final String VPN_ADDR; public static final String PLUMBER_ADDR; public static final String SERVER_AUTH; public static final String VPN_NAME = "Swisscom Safe Connect"; public static final String VPN_PROFILE_BUNDLE_KEY = "vpn_profile"; public static final boolean USE_BYOD = true; //we need to load byod libraries, otherwise the app will crash on android 4.2.x public static final int INCIDENTS_LIMIT = 10; //Splash & turorial public static final int SPLASH_DELAY = 2000; private static final String KEY_SHOW_TUTORIAL = "pref_key_tutorial"; private static final String KEY_TERMS_ACCEPTED = "pref_key_terms_accepted"; //keys for preferences private static final String KEY_PHONE_NUMBER = "pref_phone_number"; private static final String KEY_AUTH_TOKEN = "pref_auth_token"; private static final String KEY_R = "pref_date_valid"; public static final String KEY_TRAFFIC_VIEW = "pref_traffic_view"; private static final String KEY_APP_VERSION = "pref_app_version"; private static final String KEY_FIRST_VPN_LAUNCH = "pref_vpn_first_time"; //widget public static final String VPN_CONNECT = "com.swisscom.safeconnect.VPN_CONNECT"; public static final String UPDATE_INCIDENTS = "com.swisscom.safeconnect.UPDATE_INCIDENTS"; public static final String UPDATE_SUBSCRIPTION = "com.swisscom.safeconnect.UPDATE_SUBSCRIPTION"; public static final String KEY_VPN_STATE = "pref_vpn_state"; public static final String KEY_SUBSCRIPTION_TS = "pref_subscription_ts"; public final static int PLAY_SERVICES_RESOLUTION_REQUEST = 9000; public final static String SENDER_ID = "0"; private String phoneNumber = ""; private SharedPreferences prefs = null; private String deviceId; private int statsPeriod; private int appVersion; private boolean showTutorial = true; private boolean termsAccepted = false; private boolean firstVpnLaunch = true; private boolean appGotUpdated = false; private long subscriptionValidTill = 0; private VpnStateService.State vpnState = VpnStateService.State.DISABLED; public enum StatisticsPeriod { SESSION, TODAY, MONTH, OVERALL; } //token-part private String authToken = ""; private String randomPart = ""; private byte[] key = new byte[32]; private static final String r = "8CyG2hSwwpSYaacB"; private Encryptor encryptor; static { if (BuildConfig.DEBUG || BuildConfig.TESTING) { VPN_ADDR = "proxy-vpn.pot.vptt.ch"; PLUMBER_ADDR = "proxy-plumber.pot.vptt.ch"; } else { VPN_ADDR = "vpn.pot.vptt.ch"; PLUMBER_ADDR = "plumber.pot.vptt.ch"; } SERVER_AUTH = "@" + VPN_ADDR; } private Config() {} private static class ConfigHolder { public static final Config INSTANCE = new Config(); } public static Config getInstance() { return ConfigHolder.INSTANCE; } public void init(Context context) { if (prefs != null) return; DeviceIdProvider.init(context); deviceId = DeviceIdProvider.getDeviceId(); prefs = PreferenceManager. getDefaultSharedPreferences(context.getApplicationContext()); // load all preferences phoneNumber = prefs.getString(KEY_PHONE_NUMBER, phoneNumber); authToken = prefs.getString(KEY_AUTH_TOKEN, authToken); randomPart = prefs.getString(KEY_R, ""); statsPeriod = Integer.valueOf(prefs.getString(KEY_TRAFFIC_VIEW, "2")); appVersion = prefs.getInt(KEY_APP_VERSION, 0); showTutorial = prefs.getBoolean(KEY_SHOW_TUTORIAL, showTutorial); termsAccepted = prefs.getBoolean(KEY_TERMS_ACCEPTED, termsAccepted); firstVpnLaunch = prefs.getBoolean(KEY_FIRST_VPN_LAUNCH, firstVpnLaunch); vpnState = VpnStateService.State.values()[prefs.getInt(KEY_VPN_STATE, 0)]; subscriptionValidTill = prefs.getLong(KEY_SUBSCRIPTION_TS, 0); // check if app upgrade is needed checkForPrefsUpgrade(context); // generate random part at first start if (randomPart.isEmpty()) { SecureRandom random = new SecureRandom(); randomPart = new BigInteger(128, random).toString(32); saveRandomPart(randomPart); if (BuildConfig.DEBUG) { Log.d(TAG, "Random part: " + randomPart); } } String preKey = randomPart + r; // hash the value to get the key try { MessageDigest messageDigest = MessageDigest.getInstance("SHA-256"); messageDigest.update(preKey.getBytes()); key = messageDigest.digest(); } catch (NoSuchAlgorithmException e) { if (BuildConfig.DEBUG) Log.d(TAG, "No digest", e); } encryptor = new Encryptor(key, r); // token is stored encrypted... String encryptedToken = prefs.getString(KEY_AUTH_TOKEN, authToken); authToken = encryptor.decrypt(encryptedToken); if (BuildConfig.DEBUG) Log.d(TAG, "token decrypted " + authToken); } public String savePhoneNumber(String nmbr) { SharedPreferences.Editor editor = prefs.edit(); editor.putString(KEY_PHONE_NUMBER, nmbr); editor.commit(); this.phoneNumber = nmbr; return phoneNumber; } /** * stores the key */ public String saveRandomPart(String key) { SharedPreferences.Editor editor = prefs.edit(); editor.putString(KEY_R, key); editor.commit(); return authToken; } /** * stores auth token in preferences */ public String saveAuthToken(String authToken) { SharedPreferences.Editor editor = prefs.edit(); // encrypt String encrypted = encryptor.encrypt(authToken); if (BuildConfig.DEBUG) Log.d(TAG, "token " + authToken + " encrypted: " + encrypted); editor.putString(KEY_AUTH_TOKEN, encrypted); if (editor.commit()) { this.authToken = authToken; } return authToken; } public void removeAuthToken() { SharedPreferences.Editor editor = prefs.edit(); editor.remove(KEY_AUTH_TOKEN); editor.commit(); this.authToken = ""; } public void setSubscriptionValidTill(long ts){ SharedPreferences.Editor editor = prefs.edit(); editor.putLong(KEY_SUBSCRIPTION_TS, ts); editor.commit(); this.subscriptionValidTill = ts; } public void setVpnState(VpnStateService.State state) { SharedPreferences.Editor editor = prefs.edit(); editor.putInt(KEY_VPN_STATE, state.ordinal()); editor.commit(); this.vpnState = state; } public String getPhoneNumber() { return phoneNumber; } public String getAuthToken() { return authToken; } public String getDeviceId() { return deviceId; } public StatisticsPeriod getStatPeriod() { return StatisticsPeriod.values()[statsPeriod]; } public VpnStateService.State getVpnState() { return vpnState; } public long getSubscriptionValidTill() { return subscriptionValidTill; } public boolean setShowTutorial(boolean show) { SharedPreferences.Editor editor = prefs.edit(); editor.putBoolean(KEY_SHOW_TUTORIAL, show); if (editor.commit()) { this.showTutorial = show; } return show; } public boolean isShowTutorial() { return showTutorial; } public boolean setTermsAccepted(boolean state) { SharedPreferences.Editor editor = prefs.edit(); editor.putBoolean(KEY_TERMS_ACCEPTED, state); if (editor.commit()) { this.termsAccepted = state; } return state; } public boolean setFirstVpnLaunch(boolean val) { SharedPreferences.Editor editor = prefs.edit(); editor.putBoolean(KEY_FIRST_VPN_LAUNCH, val); if (editor.commit()) { this.firstVpnLaunch = val; } return val; } public boolean isFirstVpnLaunch() { return firstVpnLaunch; } public boolean areTermsAccepted() { return termsAccepted; } /** * @return Application's version code from the {@code PackageManager}. */ private static int getAppVersion(Context context) { try { PackageInfo packageInfo = context.getPackageManager() .getPackageInfo(context.getPackageName(), 0); return packageInfo.versionCode; } catch (PackageManager.NameNotFoundException e) { // should never happen throw new RuntimeException("Could not get package name: " + e); } } private void saveAppVersion(int version){ if (BuildConfig.DEBUG) { Log.d(TAG, "New app version, saving version number"); } SharedPreferences.Editor editor = prefs.edit(); editor.putInt(KEY_APP_VERSION, version); editor.commit(); } private void checkForPrefsUpgrade(Context ctx) { int curVersion = getAppVersion(ctx); if (appVersion != curVersion) { if (BuildConfig.DEBUG) Log.d(Config.TAG, "version changed from " + appVersion + " to " + curVersion); // if we are registered and version changed, request a new gcm id and send it to a // server if (authToken != null && !authToken.isEmpty()) { new GcmManager(ctx).registerPlayServices(); } saveAppVersion(curVersion); appGotUpdated = true; } } public StatisticsPeriod setStatPeriod(StatisticsPeriod val) { SharedPreferences.Editor editor = prefs.edit(); editor.putString(KEY_TRAFFIC_VIEW, String.valueOf(val.ordinal())); if (editor.commit()) { this.statsPeriod = val.ordinal(); } return val; } /** * call when preference values get changed via Settings */ public void onPreferencesUpdated() { if (prefs == null) return; statsPeriod = Integer.valueOf(prefs.getString(KEY_TRAFFIC_VIEW, String.valueOf(statsPeriod))); } public void setDeviceId(String id) { deviceId = id; } /** * @return true if the app got updated */ public boolean isAppGotUpdated() { return appGotUpdated; } public void clearAppUpdatedFlag() { appGotUpdated = false; } }