/*
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;
}
}