diff options
author | Adithya Abraham Philip <adithyaphilip@gmail.com> | 2015-11-25 01:35:41 +0530 |
---|---|---|
committer | Adithya Abraham Philip <adithyaphilip@gmail.com> | 2015-12-06 00:46:52 +0530 |
commit | f29280bbb268d112426c6662e1227118819fb904 (patch) | |
tree | 117ca2b3282fca1e10340e95f4bb07026eac7507 /OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport | |
parent | a0b46b0d3b7dd201568d1f236cb25b0a794fc2c2 (diff) | |
download | open-keychain-f29280bbb268d112426c6662e1227118819fb904.tar.gz open-keychain-f29280bbb268d112426c6662e1227118819fb904.tar.bz2 open-keychain-f29280bbb268d112426c6662e1227118819fb904.zip |
added Facebook links support, reworked Preferences
Diffstat (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport')
7 files changed, 252 insertions, 18 deletions
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/CloudSearch.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/CloudSearch.java index 869d107ab..df45de11f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/CloudSearch.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/CloudSearch.java @@ -48,6 +48,9 @@ public class CloudSearch { if (cloudPrefs.searchKeybase) { servers.add(new KeybaseKeyserver(proxy)); } + if (cloudPrefs.searchFacebook) { + servers.add(new FacebookKeyserver(proxy)); + } final ImportKeysList results = new ImportKeysList(servers.size()); ArrayList<Thread> searchThreads = new ArrayList<>(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/FacebookKeyserver.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/FacebookKeyserver.java new file mode 100644 index 000000000..d87a82a24 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/FacebookKeyserver.java @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de> + * Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com> + * Copyright (C) 2015 Adithya Abraham Philip <adithyaphilip@gmail.com> + * + * 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 org.sufficientlysecure.keychain.keyimport; + +import android.net.Uri; +import android.support.annotation.NonNull; + +import com.squareup.okhttp.OkHttpClient; +import com.squareup.okhttp.Request; +import com.squareup.okhttp.Response; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.pgp.PgpHelper; +import org.sufficientlysecure.keychain.pgp.UncachedKeyRing; +import org.sufficientlysecure.keychain.pgp.UncachedPublicKey; +import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; +import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; +import org.sufficientlysecure.keychain.util.Log; + +import java.io.IOException; +import java.net.Proxy; + +import java.net.URI; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; + +public class FacebookKeyserver extends Keyserver { + + private static final String FB_KEY_URL_FORMAT + = "https://www.facebook.com/%s/publickey/download"; + private static final String FB_HOST = "facebook.com"; + private static final String FB_HOST_WWW = "www." + FB_HOST; + + public static final String FB_URL = "https://" + FB_HOST_WWW; + + public static final String ORIGIN = FB_URL; + + private final Proxy mProxy; + + public FacebookKeyserver(Proxy proxy) { + mProxy = proxy; + } + + @Override + public List<ImportKeysListEntry> search(String fbUsername) + throws QueryFailedException, QueryNeedsRepairException { + List<ImportKeysListEntry> entry = new ArrayList<>(1); + + String data = get(fbUsername); + // if we're here that means key retrieval succeeded, + // would have thrown an exception otherwise + try { + UncachedKeyRing keyRing = UncachedKeyRing.decodeFromData(data.getBytes()); + try { + entry.add(getEntry(keyRing, fbUsername)); + } catch (UnsupportedOperationException e) { + Log.e(Constants.TAG, "Parsing retrieved Facebook key failed!"); + } + } catch (PgpGeneralException | IOException e) { + Log.e(Constants.TAG, "Failed parsing key from Facebook during search", e); + throw new QueryFailedException("No valid key found on Facebook"); + } + return entry; + } + + @Override + public String get(String fbUsername) throws QueryFailedException { + Log.d(Constants.TAG, "FacebookKeyserver get: " + fbUsername + " using Proxy: " + mProxy); + + String data = query(fbUsername); + + if (data == null) { + throw new QueryFailedException("data is null"); + } + + Matcher matcher = PgpHelper.PGP_PUBLIC_KEY.matcher(data); + if (matcher.find()) { + return matcher.group(1); + } + throw new QueryFailedException("data is null"); + } + + private String query(String fbUsername) throws QueryFailedException { + try { + String request = String.format(FB_KEY_URL_FORMAT, fbUsername); + Log.d(Constants.TAG, "fetching from Facebook with: " + request + " proxy: " + mProxy); + + OkHttpClient client = new OkHttpClient(); + client.setProxy(mProxy); + + URL url = new URL(request); + + Response response = client.newCall(new Request.Builder().url(url).build()).execute(); + + // contains body both in case of success or failure + String responseBody = response.body().string(); + + if (response.isSuccessful()) { + return responseBody; + } else { + // probably a 404 indicating that the key does not exist + throw new QueryFailedException("key for " + fbUsername + " not found on Facebook"); + } + + } catch (IOException e) { + Log.e(Constants.TAG, "IOException at Facebook key download", e); + throw new QueryFailedException("Cannot connect to Facebook. " + + "Check your Internet connection!" + + (mProxy == Proxy.NO_PROXY ? "" : " Using proxy " + mProxy)); + } + } + + @Override + public void add(String armoredKey) throws AddKeyException { + // Implementing will require usage of FB API + throw new UnsupportedOperationException("Uploading keys not supported yet"); + } + + /** + * Facebook returns the entire key even during our searching phase. + * + * @throws UnsupportedOperationException if the key could not be parsed + */ + @NonNull + public static ImportKeysListEntry getEntry(UncachedKeyRing ring, String fbUsername) + throws UnsupportedOperationException { + ImportKeysListEntry entry = new ImportKeysListEntry(); + entry.setSecretKey(false); // keys imported from Facebook must be public + entry.addOrigin(ORIGIN); + + // so we can query for the Facebook username directly, and to identify the source to + // download the key from + entry.setFbUsername(fbUsername); + + UncachedPublicKey key = ring.getPublicKey(); + + entry.setPrimaryUserId(key.getPrimaryUserIdWithFallback()); + entry.setUserIds(key.getUnorderedUserIds()); + entry.updateMergedUserIds(); + + entry.setPrimaryUserId(key.getPrimaryUserIdWithFallback()); + + entry.setKeyId(key.getKeyId()); + entry.setKeyIdHex(KeyFormattingUtils.convertKeyIdToHex(key.getKeyId())); + + entry.setFingerprintHex(KeyFormattingUtils.convertFingerprintToHex(key.getFingerprint())); + + + try { + if (key.isEC()) { // unsupported key format (ECDH or ECDSA) + Log.e(Constants.TAG, "ECDH/ECDSA key - not supported."); + throw new UnsupportedOperationException( + "ECDH/ECDSA keys not supported yet"); + } + entry.setBitStrength(key.getBitStrength()); + final int algorithm = key.getAlgorithm(); + entry.setAlgorithm(KeyFormattingUtils.getAlgorithmInfo(algorithm, key.getBitStrength(), + key.getCurveOid())); + } catch (NumberFormatException | NullPointerException e) { + Log.e(Constants.TAG, "Conversion for bit size, algorithm, or creation date failed.", e); + // can't use this key + throw new UnsupportedOperationException( + "Conversion for bit size, algorithm, or creation date failed."); + } + + return entry; + } + + public static String getUsernameFromUri(Uri uri) { + // path pattern is /username/publickey/download + return uri.getPathSegments().get(0); + } + + public static boolean isFacebookHost(Uri uri) { + String host = uri.getHost(); + return host.equalsIgnoreCase(FB_HOST) || host.equalsIgnoreCase(FB_HOST_WWW); + } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysList.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysList.java index 03439228b..75a219191 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysList.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysList.java @@ -77,9 +77,15 @@ public class ImportKeysList extends ArrayList<ImportKeysListEntry> { for (String origin : incoming.getOrigins()) { existing.addOrigin(origin); - // to work properly, Keybase-sourced entries need to pass along the extra - if (KeybaseKeyserver.ORIGIN.equals(origin)) { - existing.setExtraData(incoming.getExtraData()); + // to work properly, Keybase-sourced/Facebook-sourced entries need to pass along the + // identifying name/id + if (incoming.getKeybaseName() != null) { + existing.setKeybaseName(incoming.getKeybaseName()); + // one of the origins is not a HKP keyserver + incomingFromHkpServer = false; + } + if (incoming.getFbUsername() != null) { + existing.setFbUsername(incoming.getFbUsername()); // one of the origins is not a HKP keyserver incomingFromHkpServer = false; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java index bb86d272f..b3cf70c9f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java @@ -49,7 +49,8 @@ public class ImportKeysListEntry implements Serializable, Parcelable { private String mAlgorithm; private boolean mSecretKey; private String mPrimaryUserId; - private String mExtraData; + private String mKeybaseName; + private String mFbUsername; private String mQuery; private ArrayList<String> mOrigins; private Integer mHashCode = null; @@ -81,7 +82,8 @@ public class ImportKeysListEntry implements Serializable, Parcelable { dest.writeString(mAlgorithm); dest.writeByte((byte) (mSecretKey ? 1 : 0)); dest.writeByte((byte) (mSelected ? 1 : 0)); - dest.writeString(mExtraData); + dest.writeString(mKeybaseName); + dest.writeString(mFbUsername); dest.writeStringList(mOrigins); } @@ -102,7 +104,8 @@ public class ImportKeysListEntry implements Serializable, Parcelable { vr.mAlgorithm = source.readString(); vr.mSecretKey = source.readByte() == 1; vr.mSelected = source.readByte() == 1; - vr.mExtraData = source.readString(); + vr.mKeybaseName = source.readString(); + vr.mFbUsername = source.readString(); vr.mOrigins = new ArrayList<>(); source.readStringList(vr.mOrigins); @@ -229,12 +232,20 @@ public class ImportKeysListEntry implements Serializable, Parcelable { mPrimaryUserId = uid; } - public String getExtraData() { - return mExtraData; + public String getKeybaseName() { + return mKeybaseName; } - public void setExtraData(String extraData) { - mExtraData = extraData; + public String getFbUsername() { + return mFbUsername; + } + + public void setKeybaseName(String keybaseName) { + mKeybaseName = keybaseName; + } + + public void setFbUsername(String fbUsername) { + mFbUsername = fbUsername; } public String getQuery() { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java index e4cd6738b..9243926df 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java @@ -81,8 +81,9 @@ public class KeybaseKeyserver extends Keyserver { entry.setFingerprintHex(fingerprint); entry.setKeyIdHex("0x" + match.getKeyID()); - // store extra info, so we can query for the keybase id directly - entry.setExtraData(username); + // so we can query for the keybase id directly, and to identify the location from which the + // key is to be retrieved + entry.setKeybaseName(username); final int bitStrength = match.getBitStrength(); entry.setBitStrength(bitStrength); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/Keyserver.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/Keyserver.java index 00e8d6ac5..53e71f7a1 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/Keyserver.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/Keyserver.java @@ -21,7 +21,6 @@ package org.sufficientlysecure.keychain.keyimport; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; -import java.net.Proxy; import java.util.List; public abstract class Keyserver { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ParcelableKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ParcelableKeyRing.java index 6f6c816ea..a94ce0dce 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ParcelableKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ParcelableKeyRing.java @@ -32,24 +32,39 @@ public class ParcelableKeyRing implements Parcelable { public final String mExpectedFingerprint; public final String mKeyIdHex; public final String mKeybaseName; + public final String mFbUsername; public ParcelableKeyRing(byte[] bytes) { + this(null, bytes, false); + } + + /** + * @param disAmbiguator useless parameter intended to distinguish this overloaded constructor + * for when null is passed as first two arguments + */ + public ParcelableKeyRing(String expectedFingerprint, byte[] bytes, boolean disAmbiguator) { mBytes = bytes; - mExpectedFingerprint = null; + mExpectedFingerprint = expectedFingerprint; mKeyIdHex = null; mKeybaseName = null; + mFbUsername = null; } - public ParcelableKeyRing(String expectedFingerprint, byte[] bytes) { - mBytes = bytes; + + public ParcelableKeyRing(String expectedFingerprint, String keyIdHex) { + mBytes = null; mExpectedFingerprint = expectedFingerprint; - mKeyIdHex = null; + mKeyIdHex = keyIdHex; mKeybaseName = null; + mFbUsername = null; } - public ParcelableKeyRing(String expectedFingerprint, String keyIdHex, String keybaseName) { + + public ParcelableKeyRing(String expectedFingerprint, String keyIdHex, String keybaseName, + String fbUsername) { mBytes = null; mExpectedFingerprint = expectedFingerprint; mKeyIdHex = keyIdHex; mKeybaseName = keybaseName; + mFbUsername = fbUsername; } private ParcelableKeyRing(Parcel source) { @@ -58,6 +73,7 @@ public class ParcelableKeyRing implements Parcelable { mExpectedFingerprint = source.readString(); mKeyIdHex = source.readString(); mKeybaseName = source.readString(); + mFbUsername = source.readString(); } public void writeToParcel(Parcel dest, int flags) { @@ -65,6 +81,7 @@ public class ParcelableKeyRing implements Parcelable { dest.writeString(mExpectedFingerprint); dest.writeString(mKeyIdHex); dest.writeString(mKeybaseName); + dest.writeString(mFbUsername); } public static final Creator<ParcelableKeyRing> CREATOR = new Creator<ParcelableKeyRing>() { |