Preparations for support of onion keyservers. Refactoring of Keyserver and proxy related classes
This commit is contained in:
@@ -19,15 +19,14 @@ package org.sufficientlysecure.keychain.util;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import org.sufficientlysecure.keychain.keyimport.HkpKeyserver;
|
||||
import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
|
||||
import org.sufficientlysecure.keychain.keyimport.Keyserver;
|
||||
import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver;
|
||||
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
|
||||
import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
|
||||
import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
|
||||
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
|
||||
|
||||
import java.net.Proxy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
@@ -41,14 +40,15 @@ public class EmailKeyHelper {
|
||||
implements CryptoOperationHelper.Callback<ImportKeyringParcel, ImportKeyResult> {
|
||||
|
||||
private ArrayList<ParcelableKeyRing> mKeyList;
|
||||
private String mKeyserver;
|
||||
private ParcelableHkpKeyserver mKeyserver;
|
||||
|
||||
public ImportContactKeysCallback(Context context, String keyserver, Proxy proxy) {
|
||||
public ImportContactKeysCallback(Context context, ParcelableHkpKeyserver keyserver,
|
||||
ParcelableProxy proxy) {
|
||||
this(context, new ContactHelper(context).getContactMails(), keyserver, proxy);
|
||||
}
|
||||
|
||||
public ImportContactKeysCallback(Context context, List<String> mails, String keyserver,
|
||||
Proxy proxy) {
|
||||
public ImportContactKeysCallback(Context context, List<String> mails,
|
||||
ParcelableHkpKeyserver keyserver, ParcelableProxy proxy) {
|
||||
Set<ImportKeysListEntry> entries = new HashSet<>();
|
||||
for (String mail : mails) {
|
||||
entries.addAll(getEmailKeys(context, mail, proxy));
|
||||
@@ -62,39 +62,41 @@ public class EmailKeyHelper {
|
||||
mKeyList = keys;
|
||||
mKeyserver = keyserver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImportKeyringParcel createOperationInput() {
|
||||
return new ImportKeyringParcel(mKeyList, mKeyserver);
|
||||
}
|
||||
}
|
||||
|
||||
public static Set<ImportKeysListEntry> getEmailKeys(Context context, String mail, Proxy proxy) {
|
||||
public static Set<ImportKeysListEntry> getEmailKeys(Context context, String mail,
|
||||
ParcelableProxy proxy) {
|
||||
Set<ImportKeysListEntry> keys = new HashSet<>();
|
||||
|
||||
// Try _hkp._tcp SRV record first
|
||||
String[] mailparts = mail.split("@");
|
||||
if (mailparts.length == 2) {
|
||||
HkpKeyserver hkp = HkpKeyserver.resolve(mailparts[1], proxy);
|
||||
ParcelableHkpKeyserver hkp = ParcelableHkpKeyserver.resolve(mailparts[1]);
|
||||
if (hkp != null) {
|
||||
keys.addAll(getEmailKeys(mail, hkp));
|
||||
keys.addAll(getEmailKeys(mail, hkp, proxy));
|
||||
}
|
||||
}
|
||||
|
||||
if (keys.isEmpty()) {
|
||||
// Most users don't have the SRV record, so ask a default server as well
|
||||
String server = Preferences.getPreferences(context).getPreferredKeyserver();
|
||||
ParcelableHkpKeyserver server = Preferences.getPreferences(context).getPreferredKeyserver();
|
||||
if (server != null) {
|
||||
HkpKeyserver hkp = new HkpKeyserver(server, proxy);
|
||||
keys.addAll(getEmailKeys(mail, hkp));
|
||||
keys.addAll(getEmailKeys(mail, server, proxy));
|
||||
}
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
public static List<ImportKeysListEntry> getEmailKeys(String mail, Keyserver keyServer) {
|
||||
public static List<ImportKeysListEntry> getEmailKeys(String mail, Keyserver keyServer,
|
||||
ParcelableProxy proxy) {
|
||||
Set<ImportKeysListEntry> keys = new HashSet<>();
|
||||
try {
|
||||
for (ImportKeysListEntry key : keyServer.search(mail)) {
|
||||
for (ImportKeysListEntry key : keyServer.search(mail, proxy)) {
|
||||
if (key.isRevoked() || key.isExpired()) continue;
|
||||
for (String userId : key.getUserIds()) {
|
||||
if (userId.toLowerCase().contains(mail.toLowerCase(Locale.ENGLISH))) {
|
||||
|
||||
@@ -32,21 +32,28 @@ public class ParcelableProxy implements Parcelable {
|
||||
private String mProxyHost;
|
||||
private int mProxyPort;
|
||||
private Proxy.Type mProxyType;
|
||||
private int mProxyMode;
|
||||
|
||||
public ParcelableProxy(String hostName, int port, Proxy.Type type) {
|
||||
public static final int PROXY_MODE_NORMAL = 0;
|
||||
public static final int PROXY_MODE_TOR = 1;
|
||||
|
||||
public ParcelableProxy(String hostName, int port, Proxy.Type type, int proxyMode) {
|
||||
mProxyHost = hostName;
|
||||
|
||||
if (hostName == null) {
|
||||
if (mProxyHost == null) {
|
||||
return; // represents a null proxy
|
||||
}
|
||||
|
||||
mProxyPort = port;
|
||||
|
||||
mProxyType = type;
|
||||
mProxyMode = proxyMode;
|
||||
}
|
||||
|
||||
public static ParcelableProxy getForNoProxy() {
|
||||
return new ParcelableProxy(null, -1, null);
|
||||
return new ParcelableProxy(null, -1, null, PROXY_MODE_NORMAL);
|
||||
}
|
||||
|
||||
public boolean isTorEnabled() {
|
||||
return (mProxyMode == PROXY_MODE_TOR);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@@ -65,6 +72,7 @@ public class ParcelableProxy implements Parcelable {
|
||||
mProxyHost = in.readString();
|
||||
mProxyPort = in.readInt();
|
||||
mProxyType = (Proxy.Type) in.readSerializable();
|
||||
mProxyMode = in.readInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -77,6 +85,7 @@ public class ParcelableProxy implements Parcelable {
|
||||
dest.writeString(mProxyHost);
|
||||
dest.writeInt(mProxyPort);
|
||||
dest.writeSerializable(mProxyType);
|
||||
dest.writeInt(mProxyMode);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
|
||||
@@ -27,19 +27,17 @@ import android.content.SharedPreferences;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.Constants.Pref;
|
||||
import org.sufficientlysecure.keychain.KeychainApplication;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver;
|
||||
import org.sufficientlysecure.keychain.service.KeyserverSyncAdapterService;
|
||||
import org.sufficientlysecure.keychain.util.orbot.OrbotStatusReceiver;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.net.Proxy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
@@ -47,7 +45,6 @@ import java.util.HashSet;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Vector;
|
||||
|
||||
/**
|
||||
* Singleton Implementation of a Preference Helper
|
||||
@@ -131,40 +128,50 @@ public class Preferences {
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
public String[] getKeyServers() {
|
||||
public ArrayList<ParcelableHkpKeyserver> getKeyServers() {
|
||||
String rawData = mSharedPreferences.getString(Constants.Pref.KEY_SERVERS,
|
||||
Constants.Defaults.KEY_SERVERS);
|
||||
if ("".equals(rawData)) {
|
||||
return new String[0];
|
||||
return new ArrayList<>();
|
||||
}
|
||||
Vector<String> servers = new Vector<>();
|
||||
String chunks[] = rawData.split(",");
|
||||
for (String c : chunks) {
|
||||
String tmp = c.trim();
|
||||
if (tmp.length() > 0) {
|
||||
servers.add(tmp);
|
||||
}
|
||||
}
|
||||
return servers.toArray(chunks);
|
||||
}
|
||||
ArrayList<ParcelableHkpKeyserver> servers = new ArrayList<>();
|
||||
String[] entries = rawData.split(",");
|
||||
for (String entry : entries) {
|
||||
|
||||
public String getPreferredKeyserver() {
|
||||
String[] keyservers = getKeyServers();
|
||||
return keyservers.length == 0 ? null : keyservers[0];
|
||||
}
|
||||
String[] addresses = entry.trim().split(";");
|
||||
String url = addresses[0];
|
||||
String onion = addresses.length == 1 ? null : addresses[1];
|
||||
|
||||
public void setKeyServers(String[] value) {
|
||||
SharedPreferences.Editor editor = mSharedPreferences.edit();
|
||||
String rawData = "";
|
||||
for (String v : value) {
|
||||
String tmp = v.trim();
|
||||
if (tmp.length() == 0) {
|
||||
if (url.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
servers.add(new ParcelableHkpKeyserver(url, onion));
|
||||
}
|
||||
return servers;
|
||||
}
|
||||
|
||||
public ParcelableHkpKeyserver getPreferredKeyserver() {
|
||||
ArrayList<ParcelableHkpKeyserver> keyservers = getKeyServers();
|
||||
return keyservers.size() == 0 ? null : keyservers.get(0);
|
||||
}
|
||||
|
||||
public void setKeyServers(ArrayList<ParcelableHkpKeyserver> keyservers) {
|
||||
SharedPreferences.Editor editor = mSharedPreferences.edit();
|
||||
String rawData = "";
|
||||
for (ParcelableHkpKeyserver server : keyservers) {
|
||||
if (server.getUrl().isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
rawData += server.getUrl();
|
||||
|
||||
if (server.getOnion() != null && !server.getOnion().isEmpty()) {
|
||||
rawData += ";" + server.getOnion();
|
||||
}
|
||||
|
||||
if (!"".equals(rawData)) {
|
||||
rawData += ",";
|
||||
}
|
||||
rawData += tmp;
|
||||
}
|
||||
editor.putString(Constants.Pref.KEY_SERVERS, rawData);
|
||||
editor.commit();
|
||||
@@ -249,7 +256,7 @@ public class Preferences {
|
||||
return Integer.parseInt(mSharedPreferences.getString(Pref.PROXY_PORT, "-1"));
|
||||
}
|
||||
|
||||
public Proxy.Type getProxyType() {
|
||||
Proxy.Type getProxyType() {
|
||||
final String typeHttp = Pref.ProxyType.TYPE_HTTP;
|
||||
final String typeSocks = Pref.ProxyType.TYPE_SOCKS;
|
||||
|
||||
@@ -266,47 +273,24 @@ public class Preferences {
|
||||
}
|
||||
}
|
||||
|
||||
public ProxyPrefs getProxyPrefs() {
|
||||
public ParcelableProxy getParcelableProxy() {
|
||||
boolean useTor = getUseTorProxy();
|
||||
boolean useNormalProxy = getUseNormalProxy();
|
||||
|
||||
if (useTor) {
|
||||
//TODO: Replace Constants.Orbot.PROXY_PORT with OrbotStatusReceiver.getProxyPortHttp()
|
||||
//TODO: in order to always have the actual port Orbot is offering?
|
||||
return new ProxyPrefs(true, false, Constants.Orbot.PROXY_HOST, Constants.Orbot.PROXY_PORT,
|
||||
Constants.Orbot.PROXY_TYPE);
|
||||
|
||||
return new ParcelableProxy(Constants.Orbot.PROXY_HOST, Constants.Orbot.PROXY_PORT,
|
||||
Constants.Orbot.PROXY_TYPE, ParcelableProxy.PROXY_MODE_TOR);
|
||||
} else if (useNormalProxy) {
|
||||
return new ProxyPrefs(false, true, getProxyHost(), getProxyPort(), getProxyType());
|
||||
return new ParcelableProxy(getProxyHost(), getProxyPort(), getProxyType(),
|
||||
ParcelableProxy.PROXY_MODE_NORMAL);
|
||||
} else {
|
||||
return new ProxyPrefs(false, false, null, -1, null);
|
||||
return new ParcelableProxy(null, -1, null, ParcelableProxy.PROXY_MODE_NORMAL);
|
||||
}
|
||||
}
|
||||
|
||||
public static class ProxyPrefs {
|
||||
public final ParcelableProxy parcelableProxy;
|
||||
public final boolean torEnabled;
|
||||
public final boolean normalPorxyEnabled;
|
||||
|
||||
/**
|
||||
* torEnabled and normalProxyEnabled are not expected to both be true
|
||||
*
|
||||
* @param torEnabled if Tor is to be used
|
||||
* @param normalPorxyEnabled if user-specified proxy is to be used
|
||||
*/
|
||||
public ProxyPrefs(boolean torEnabled, boolean normalPorxyEnabled, String hostName, int port, Proxy.Type type) {
|
||||
this.torEnabled = torEnabled;
|
||||
this.normalPorxyEnabled = normalPorxyEnabled;
|
||||
if (!torEnabled && !normalPorxyEnabled) this.parcelableProxy = new ParcelableProxy(null, -1, null);
|
||||
else this.parcelableProxy = new ParcelableProxy(hostName, port, type);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Proxy getProxy() {
|
||||
return parcelableProxy.getProxy();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if a periodic sync exists and is set to run automatically, false otherwise
|
||||
*/
|
||||
@@ -339,10 +323,11 @@ public class Preferences {
|
||||
}
|
||||
|
||||
public static class CacheTTLPrefs implements Serializable {
|
||||
public static final Map<Integer,Integer> CACHE_TTL_NAMES;
|
||||
public static final Map<Integer, Integer> CACHE_TTL_NAMES;
|
||||
public static final ArrayList<Integer> CACHE_TTLS;
|
||||
|
||||
static {
|
||||
HashMap<Integer,Integer> cacheTtlNames = new HashMap<>();
|
||||
HashMap<Integer, Integer> cacheTtlNames = new HashMap<>();
|
||||
cacheTtlNames.put(0, R.string.cache_ttl_lock_screen);
|
||||
cacheTtlNames.put(60 * 10, R.string.cache_ttl_ten_minutes);
|
||||
cacheTtlNames.put(60 * 30, R.string.cache_ttl_thirty_minutes);
|
||||
@@ -397,7 +382,7 @@ public class Preferences {
|
||||
public final boolean searchKeyserver;
|
||||
public final boolean searchKeybase;
|
||||
public final boolean searchFacebook;
|
||||
public final String keyserver;
|
||||
public final ParcelableHkpKeyserver keyserver;
|
||||
|
||||
/**
|
||||
* @param searchKeyserver should passed keyserver be searched
|
||||
@@ -405,7 +390,7 @@ public class Preferences {
|
||||
* @param keyserver the keyserver url authority to search on
|
||||
*/
|
||||
public CloudSearchPrefs(boolean searchKeyserver, boolean searchKeybase,
|
||||
boolean searchFacebook, String keyserver) {
|
||||
boolean searchFacebook, ParcelableHkpKeyserver keyserver) {
|
||||
this.searchKeyserver = searchKeyserver;
|
||||
this.searchKeybase = searchKeybase;
|
||||
this.searchFacebook = searchFacebook;
|
||||
@@ -416,7 +401,7 @@ public class Preferences {
|
||||
searchKeyserver = in.readByte() != 0x00;
|
||||
searchKeybase = in.readByte() != 0x00;
|
||||
searchFacebook = in.readByte() != 0x00;
|
||||
keyserver = in.readString();
|
||||
keyserver = in.readParcelable(ParcelableHkpKeyserver.class.getClassLoader());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -429,7 +414,7 @@ public class Preferences {
|
||||
dest.writeByte((byte) (searchKeyserver ? 0x01 : 0x00));
|
||||
dest.writeByte((byte) (searchKeybase ? 0x01 : 0x00));
|
||||
dest.writeByte((byte) (searchFacebook ? 0x01 : 0x00));
|
||||
dest.writeString(keyserver);
|
||||
dest.writeParcelable(keyserver, flags);
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<CloudSearchPrefs> CREATOR
|
||||
@@ -476,22 +461,21 @@ public class Preferences {
|
||||
// fall through
|
||||
case 3: {
|
||||
// migrate keyserver to hkps
|
||||
String[] serversArray = getKeyServers();
|
||||
ArrayList<String> servers = new ArrayList<>(Arrays.asList(serversArray));
|
||||
ListIterator<String> it = servers.listIterator();
|
||||
ArrayList<ParcelableHkpKeyserver> servers = getKeyServers();
|
||||
ListIterator<ParcelableHkpKeyserver> it = servers.listIterator();
|
||||
while (it.hasNext()) {
|
||||
String server = it.next();
|
||||
ParcelableHkpKeyserver server = it.next();
|
||||
if (server == null) {
|
||||
continue;
|
||||
}
|
||||
switch (server) {
|
||||
switch (server.getUrl()) {
|
||||
case "pool.sks-keyservers.net":
|
||||
// use HKPS!
|
||||
it.set("hkps://hkps.pool.sks-keyservers.net");
|
||||
it.set(new ParcelableHkpKeyserver("hkps://hkps.pool.sks-keyservers.net", null));
|
||||
break;
|
||||
case "pgp.mit.edu":
|
||||
// use HKPS!
|
||||
it.set("hkps://pgp.mit.edu");
|
||||
it.set(new ParcelableHkpKeyserver("hkps://pgp.mit.edu", null));
|
||||
break;
|
||||
case "subkeys.pgp.net":
|
||||
// remove, because often down and no HKPS!
|
||||
@@ -500,7 +484,7 @@ public class Preferences {
|
||||
}
|
||||
|
||||
}
|
||||
setKeyServers(servers.toArray(new String[servers.size()]));
|
||||
setKeyServers(servers);
|
||||
}
|
||||
// fall through
|
||||
case 4: {
|
||||
@@ -513,6 +497,27 @@ public class Preferences {
|
||||
// fall through
|
||||
case 6: {
|
||||
}
|
||||
// fall through
|
||||
case 7: {
|
||||
// add onion address to sks-keyservers.net
|
||||
ArrayList<ParcelableHkpKeyserver> servers = getKeyServers();
|
||||
ListIterator<ParcelableHkpKeyserver> it = servers.listIterator();
|
||||
while (it.hasNext()) {
|
||||
ParcelableHkpKeyserver server = it.next();
|
||||
if (server == null) {
|
||||
continue;
|
||||
}
|
||||
switch (server.getUrl()) {
|
||||
case "hkps://hkps.pool.sks-keyservers.net":
|
||||
it.set(new ParcelableHkpKeyserver(
|
||||
"hkps://hkps.pool.sks-keyservers.net",
|
||||
"hkp://jirk5u4osbsr34t5.onion"));
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
setKeyServers(servers);
|
||||
}
|
||||
}
|
||||
|
||||
// write new preference version
|
||||
|
||||
@@ -72,6 +72,7 @@ import org.sufficientlysecure.keychain.ui.dialog.OrbotStartDialogFragment;
|
||||
import org.sufficientlysecure.keychain.ui.dialog.PreferenceInstallDialogFragment;
|
||||
import org.sufficientlysecure.keychain.ui.util.ThemeChanger;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
import org.sufficientlysecure.keychain.util.ParcelableProxy;
|
||||
import org.sufficientlysecure.keychain.util.Preferences;
|
||||
|
||||
import java.util.List;
|
||||
@@ -153,6 +154,7 @@ public class OrbotHelper {
|
||||
/**
|
||||
* Initialize the OrbotStatusReceiver (if not already happened) and check, whether Orbot is
|
||||
* running or not.
|
||||
*
|
||||
* @param context context
|
||||
* @return if Orbot is running
|
||||
*/
|
||||
@@ -285,8 +287,8 @@ public class OrbotHelper {
|
||||
* otherwise
|
||||
*/
|
||||
public static boolean isOrbotInRequiredState(Context context) {
|
||||
Preferences.ProxyPrefs proxyPrefs = Preferences.getPreferences(context).getProxyPrefs();
|
||||
if (!proxyPrefs.torEnabled) {
|
||||
ParcelableProxy proxy = Preferences.getPreferences(context).getParcelableProxy();
|
||||
if (!proxy.isTorEnabled()) {
|
||||
return true;
|
||||
} else if (!OrbotHelper.isOrbotInstalled(context) || !OrbotHelper.isOrbotRunning(context)) {
|
||||
return false;
|
||||
@@ -298,15 +300,15 @@ public class OrbotHelper {
|
||||
* checks if Tor is enabled and if it is, that Orbot is installed and running. Generates appropriate dialogs.
|
||||
*
|
||||
* @param middleButton resourceId of string to display as the middle button of install and enable dialogs
|
||||
* @param proxyPrefs proxy preferences used to determine if Tor is required to be started
|
||||
* @param proxy proxy preferences used to determine if Tor is required to be started
|
||||
* @return true if Tor is not enabled or Tor is enabled and Orbot is installed and running, else false
|
||||
*/
|
||||
public static boolean putOrbotInRequiredState(final int middleButton,
|
||||
final DialogActions dialogActions,
|
||||
Preferences.ProxyPrefs proxyPrefs,
|
||||
ParcelableProxy proxy,
|
||||
final FragmentActivity fragmentActivity) {
|
||||
|
||||
if (!proxyPrefs.torEnabled) {
|
||||
if (!proxy.isTorEnabled()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -377,7 +379,7 @@ public class OrbotHelper {
|
||||
FragmentActivity fragmentActivity) {
|
||||
return putOrbotInRequiredState(R.string.orbot_ignore_tor,
|
||||
dialogActions,
|
||||
Preferences.getPreferences(fragmentActivity).getProxyPrefs(),
|
||||
Preferences.getPreferences(fragmentActivity).getParcelableProxy(),
|
||||
fragmentActivity);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user