Merge remote-tracking branch 'origin/master' into encrypted-export

This commit is contained in:
Vincent Breitmoser
2015-09-28 18:27:29 +02:00
71 changed files with 2111 additions and 756 deletions

View File

@@ -103,8 +103,7 @@ public class EmailKeyHelper {
}
}
}
} catch (Keyserver.QueryFailedException ignored) {
} catch (Keyserver.QueryNeedsRepairException ignored) {
} catch (Keyserver.CloudSearchFailureException ignored) {
}
return new ArrayList<>(keys);
}

View File

@@ -69,13 +69,14 @@ public class ExportHelper
: R.string.specify_backup_dest_single);
}
FileHelper.saveDocumentDialog(new FileHelper.FileDialogCallback() {
@Override
public void onFileSelected(File file, boolean checked) {
mExportFile = file;
exportKeys(masterKeyId == null ? null : new long[] { masterKeyId }, exportSecret);
}
}, mActivity.getSupportFragmentManager(), title, message, exportFile, null);
// TODO: for valodim
// FileHelper.saveDocumentDialog(new FileHelper.FileDialogCallback() {
// @Override
// public void onFileSelected(File file, boolean checked) {
// mExportFile = file;
// exportKeys(masterKeyId == null ? null : new long[] { masterKeyId }, exportSecret);
// }
// }, mActivity.getSupportFragmentManager(), title, message, exportFile, null);
}
// TODO: If ExportHelper requires pending data (see CryptoOPerationHelper), activities using

View File

@@ -18,7 +18,6 @@
package org.sufficientlysecure.keychain.util;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.ContentResolver;
import android.content.Context;
@@ -30,20 +29,13 @@ import android.net.Uri;
import android.os.Build;
import android.os.Build.VERSION_CODES;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.os.Messenger;
import android.provider.DocumentsContract;
import android.provider.OpenableColumns;
import android.support.annotation.StringRes;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.widget.Toast;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;
import org.sufficientlysecure.keychain.ui.dialog.FileDialogFragment;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
@@ -82,50 +74,24 @@ import java.text.DecimalFormat;
public class FileHelper {
public static void openDocument(Fragment fragment, Uri last, String mimeType, boolean multiple, int requestCode) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
openDocumentPreKitKat(fragment, last, mimeType, multiple, requestCode);
} else {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
openDocumentKitKat(fragment, mimeType, multiple, requestCode);
}
}
public static void saveDocument(Fragment fragment, String targetName, Uri inputUri,
@StringRes int title, @StringRes int message, int requestCode) {
saveDocument(fragment, targetName, inputUri, "*/*", title, message, requestCode);
}
public static void saveDocument(Fragment fragment, String targetName, Uri inputUri, String mimeType,
@StringRes int title, @StringRes int message, int requestCode) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
saveDocumentDialog(fragment, targetName, inputUri, title, message, requestCode);
} else {
saveDocumentKitKat(fragment, mimeType, targetName, requestCode);
openDocumentPreKitKat(fragment, last, mimeType, multiple, requestCode);
}
}
public static void saveDocumentDialog(final Fragment fragment, String targetName, Uri inputUri,
@StringRes int title, @StringRes int message, final int requestCode) {
saveDocumentDialog(fragment, targetName, inputUri, title, message, new FileDialogCallback() {
// is this a good idea? seems hacky...
@Override
public void onFileSelected(File file, boolean checked) {
Intent intent = new Intent();
intent.setData(Uri.fromFile(file));
fragment.onActivityResult(requestCode, Activity.RESULT_OK, intent);
}
});
public static void saveDocument(Fragment fragment, String targetName, int requestCode) {
saveDocument(fragment, targetName, "*/*", requestCode);
}
public static void saveDocumentDialog(final Fragment fragment, String targetName, Uri inputUri,
@StringRes int title, @StringRes int message, FileDialogCallback callback) {
File file = inputUri == null ? null : new File(inputUri.getPath());
File parentDir = file != null && file.exists() ? file.getParentFile() : Constants.Path.APP_DIR;
File targetFile = new File(parentDir, targetName);
saveDocumentDialog(callback, fragment.getActivity().getSupportFragmentManager(),
fragment.getString(title), fragment.getString(message), targetFile, null);
public static void saveDocument(Fragment fragment, String targetName, String mimeType,
int requestCode) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
saveDocumentKitKat(fragment, mimeType, targetName, requestCode);
} else {
throw new RuntimeException("saveDocument does not support Android < 4.4!");
}
}
/** Opens the preferred installed file manager on Android and shows a toast
@@ -172,36 +138,6 @@ public class FileHelper {
fragment.startActivityForResult(intent, requestCode);
}
public static void saveDocumentDialog(
final FileDialogCallback callback, final FragmentManager fragmentManager,
final String title, final String message, final File defaultFile,
final String checkMsg) {
// Message is received after file is selected
Handler returnHandler = new Handler() {
@Override
public void handleMessage(Message message) {
if (message.what == FileDialogFragment.MESSAGE_OKAY) {
callback.onFileSelected(
new File(message.getData().getString(FileDialogFragment.MESSAGE_DATA_FILE)),
message.getData().getBoolean(FileDialogFragment.MESSAGE_DATA_CHECKED));
}
}
};
// Create a new Messenger for the communication back
final Messenger messenger = new Messenger(returnHandler);
DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(new Runnable() {
@Override
public void run() {
FileDialogFragment fileDialog = FileDialogFragment.newInstance(messenger, title, message,
defaultFile, checkMsg);
fileDialog.show(fragmentManager, "fileDialog");
}
});
}
public static String getFilename(Context context, Uri uri) {
String filename = null;
try {

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
*
* 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.util;

View File

@@ -141,6 +141,10 @@ public class NfcHelper {
}
protected void onPostExecute(Void unused) {
if (mActivity.isFinishing()) {
return;
}
// Register callback to set NDEF message
mNfcAdapter.setNdefPushMessageCallback(mNdefCallback,
mActivity);

View File

@@ -0,0 +1,79 @@
/*
* Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
*
* 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.util;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.OkUrlFactory;
import com.textuality.keybase.lib.KeybaseUrlConnectionClient;
import org.sufficientlysecure.keychain.Constants;
import java.io.IOException;
import java.net.Proxy;
import java.net.URL;
import java.net.URLConnection;
import java.util.concurrent.TimeUnit;
/**
* Wrapper for Keybase Lib
*/
public class OkHttpKeybaseClient implements KeybaseUrlConnectionClient {
private final OkUrlFactory factory;
private static OkUrlFactory generateUrlFactory() {
OkHttpClient client = new OkHttpClient();
return new OkUrlFactory(client);
}
public OkHttpKeybaseClient() {
factory = generateUrlFactory();
}
@Override
public URLConnection openConnection(URL url) throws IOException {
return openConnection(url, null);
}
@Override
public URLConnection openConnection(URL url, Proxy proxy) throws IOException {
if (proxy != null) {
factory.client().setProxy(proxy);
factory.client().setConnectTimeout(30000, TimeUnit.MILLISECONDS);
factory.client().setReadTimeout(40000, TimeUnit.MILLISECONDS);
} else {
factory.client().setConnectTimeout(5000, TimeUnit.MILLISECONDS);
factory.client().setReadTimeout(25000, TimeUnit.MILLISECONDS);
}
factory.client().setFollowSslRedirects(false);
// forced the usage of keybase.io pinned certificate
try {
if (!TlsHelper.usePinnedCertificateIfAvailable(factory.client(), url)) {
throw new IOException("no pinned certificate found for URL!");
}
} catch (TlsHelper.TlsHelperException e) {
Log.e(Constants.TAG, "TlsHelper failed", e);
throw new IOException("TlsHelper failed");
}
return factory.open(url);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2013-2014 Dominik Schürmann <dominik@dominikschuermann.de>
* Copyright (C) 2013-2015 Dominik Schürmann <dominik@dominikschuermann.de>
*
* 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
@@ -20,6 +20,7 @@ package org.sufficientlysecure.keychain.util;
import android.content.res.AssetManager;
import com.squareup.okhttp.OkHttpClient;
import org.sufficientlysecure.keychain.Constants;
import java.io.ByteArrayInputStream;
@@ -37,7 +38,6 @@ import java.security.cert.CertificateFactory;
import java.util.HashMap;
import java.util.Map;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
@@ -49,15 +49,14 @@ public class TlsHelper {
}
}
private static Map<String, byte[]> sStaticCA = new HashMap<>();
private static Map<String, byte[]> sPinnedCertificates = new HashMap<>();
public static void addStaticCA(String domain, byte[] certificate) {
sStaticCA.put(domain, certificate);
}
public static void addStaticCA(String domain, AssetManager assetManager, String name) {
/**
* Add certificate from assets to pinned certificate map.
*/
public static void addPinnedCertificate(String host, AssetManager assetManager, String cerFilename) {
try {
InputStream is = assetManager.open(name);
InputStream is = assetManager.open(cerFilename);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int reads = is.read();
@@ -68,27 +67,36 @@ public class TlsHelper {
is.close();
addStaticCA(domain, baos.toByteArray());
sPinnedCertificates.put(host, baos.toByteArray());
} catch (IOException e) {
Log.w(Constants.TAG, e);
}
}
public static void pinCertificateIfNecessary(OkHttpClient client, URL url) throws TlsHelperException, IOException {
/**
* Use pinned certificate for OkHttpClient if we have one.
*
* @return true, if certificate is available, false if not
* @throws TlsHelperException
* @throws IOException
*/
public static boolean usePinnedCertificateIfAvailable(OkHttpClient client, URL url) throws TlsHelperException, IOException {
if (url.getProtocol().equals("https")) {
for (String domain : sStaticCA.keySet()) {
if (url.getHost().endsWith(domain)) {
pinCertificate(sStaticCA.get(domain), client);
// use certificate PIN from assets if we have one
for (String host : sPinnedCertificates.keySet()) {
if (url.getHost().endsWith(host)) {
pinCertificate(sPinnedCertificates.get(host), client);
return true;
}
}
}
return false;
}
/**
* Modifies the client to accept only requests with a given certificate. Applies to all URLs requested by the
* client.
* Therefore a client that is pinned this way should be used to only make requests to URLs with passed certificate.
* TODO: Refactor - More like SSH StrictHostKeyChecking than pinning?
*
* @param certificate certificate to pin
* @param client OkHttpClient to enforce pinning on
@@ -97,8 +105,10 @@ public class TlsHelper {
*/
private static void pinCertificate(byte[] certificate, OkHttpClient client)
throws TlsHelperException, IOException {
// We don't use OkHttp's CertificatePinner since it depends on a TrustManager to verify it too. Refer to
// note at end of description: http://square.github.io/okhttp/javadoc/com/squareup/okhttp/CertificatePinner.html
// We don't use OkHttp's CertificatePinner since it can not be used to pin self-signed
// certificate if such certificate is not accepted by TrustManager.
// (Refer to note at end of description:
// http://square.github.io/okhttp/javadoc/com/squareup/okhttp/CertificatePinner.html )
// Creating our own TrustManager that trusts only our certificate eliminates the need for certificate pinning
try {
// Load CA
@@ -126,42 +136,4 @@ public class TlsHelper {
}
}
/**
* Opens a Connection that will only accept certificates signed with a specific CA and skips common name check.
* This is required for some distributed Keyserver networks like sks-keyservers.net
*
* @param certificate The X.509 certificate used to sign the servers certificate
* @param url Connection target
*/
public static HttpsURLConnection openCAConnection(byte[] certificate, URL url)
throws TlsHelperException, IOException {
try {
// Load CA
CertificateFactory cf = CertificateFactory.getInstance("X.509");
Certificate ca = cf.generateCertificate(new ByteArrayInputStream(certificate));
// 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());
return urlConnection;
} catch (CertificateException | KeyManagementException | KeyStoreException | NoSuchAlgorithmException e) {
throw new TlsHelperException(e);
}
}
}