tls-psk: actual import of keys

This commit is contained in:
Vincent Breitmoser
2017-05-30 19:06:28 +02:00
parent e44d668e27
commit 703603782f
7 changed files with 139 additions and 63 deletions

View File

@@ -124,7 +124,7 @@ public class KeyTransferInteractor {
serverSocket = (SSLServerSocket) sslContext.getServerSocketFactory().createServerSocket(port); serverSocket = (SSLServerSocket) sslContext.getServerSocketFactory().createServerSocket(port);
String presharedKeyEncoded = Base64.encodeToString(presharedKey, Base64.URL_SAFE | Base64.NO_PADDING); String presharedKeyEncoded = Base64.encodeToString(presharedKey, Base64.URL_SAFE | Base64.NO_PADDING);
String qrCodeData = presharedKeyEncoded + "@" + getIPAddress(true) + ":" + port; String qrCodeData = "pgp+transfer://" + presharedKeyEncoded + "@" + getIPAddress(true) + ":" + port;
invokeListener(CONNECTION_LISTENING, qrCodeData); invokeListener(CONNECTION_LISTENING, qrCodeData);
socket = serverSocket.accept(); socket = serverSocket.accept();
@@ -135,8 +135,8 @@ public class KeyTransferInteractor {
} }
handleOpenConnection(socket); handleOpenConnection(socket);
Log.d(Constants.TAG, "connection closed ok!");
} catch (IOException e) { } catch (IOException e) {
invokeListener(CONNECTION_LOST, null);
Log.e(Constants.TAG, "error!", e); Log.e(Constants.TAG, "error!", e);
} finally { } finally {
try { try {
@@ -176,13 +176,8 @@ public class KeyTransferInteractor {
socket.setSoTimeout(500); socket.setSoTimeout(500);
while (!isInterrupted() && socket.isConnected()) { while (!isInterrupted() && socket.isConnected()) {
sendDataIfAvailable(socket, outputStream); sendDataIfAvailable(socket, outputStream);
receiveDataIfAvailable(socket, bufferedReader);
boolean connectionClosed = receiveDataIfAvailable(socket, bufferedReader);
if (connectionClosed) {
break;
}
} }
Log.d(Constants.TAG, "disconnected"); Log.d(Constants.TAG, "disconnected");
invokeListener(CONNECTION_LOST, null); invokeListener(CONNECTION_LOST, null);
} }
@@ -196,6 +191,7 @@ public class KeyTransferInteractor {
} }
if (firstLine == null) { if (firstLine == null) {
invokeListener(CONNECTION_LOST, null);
return true; return true;
} }
@@ -204,7 +200,7 @@ public class KeyTransferInteractor {
socket.setSoTimeout(500); socket.setSoTimeout(500);
invokeListener(CONNECTION_RECEIVE_OK, receivedData); invokeListener(CONNECTION_RECEIVE_OK, receivedData);
return false; return true;
} }
private boolean sendDataIfAvailable(Socket socket, OutputStream outputStream) throws IOException { private boolean sendDataIfAvailable(Socket socket, OutputStream outputStream) throws IOException {

View File

@@ -22,6 +22,7 @@ package org.sufficientlysecure.keychain.operations;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Iterator; import java.util.Iterator;
import java.util.List;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService; import java.util.concurrent.ExecutorCompletionService;
@@ -477,7 +478,7 @@ public class ImportOperation extends BaseReadWriteOperation<ImportKeyringParcel>
@NonNull @NonNull
@Override @Override
public ImportKeyResult execute(ImportKeyringParcel importInput, CryptoInputParcel cryptoInput) { public ImportKeyResult execute(ImportKeyringParcel importInput, CryptoInputParcel cryptoInput) {
ArrayList<ParcelableKeyRing> keyList = importInput.getKeyList(); List<ParcelableKeyRing> keyList = importInput.getKeyList();
HkpKeyserverAddress keyServer = importInput.getKeyserver(); HkpKeyserverAddress keyServer = importInput.getKeyserver();
boolean skipSave = importInput.isSkipSave(); boolean skipSave = importInput.isSkipSave();
@@ -510,7 +511,7 @@ public class ImportOperation extends BaseReadWriteOperation<ImportKeyringParcel>
} }
@NonNull @NonNull
private ImportKeyResult multiThreadedKeyImport(ArrayList<ParcelableKeyRing> keyList, private ImportKeyResult multiThreadedKeyImport(List<ParcelableKeyRing> keyList,
final HkpKeyserverAddress keyServer, final ParcelableProxy proxy, final HkpKeyserverAddress keyServer, final ParcelableProxy proxy,
final boolean skipSave) { final boolean skipSave) {
Log.d(Constants.TAG, "Multi-threaded key import starting"); Log.d(Constants.TAG, "Multi-threaded key import starting");

View File

@@ -20,6 +20,8 @@ package org.sufficientlysecure.keychain.service;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import android.os.Parcelable; import android.os.Parcelable;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
@@ -31,21 +33,25 @@ import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
@AutoValue @AutoValue
public abstract class ImportKeyringParcel implements Parcelable { public abstract class ImportKeyringParcel implements Parcelable {
@Nullable // If null, keys are expected to be read from a cache file in ImportExportOperations @Nullable // If null, keys are expected to be read from a cache file in ImportExportOperations
public abstract ArrayList<ParcelableKeyRing> getKeyList(); public abstract List<ParcelableKeyRing> getKeyList();
@Nullable // must be set if keys are to be imported from a keyserver @Nullable // must be set if keys are to be imported from a keyserver
public abstract HkpKeyserverAddress getKeyserver(); public abstract HkpKeyserverAddress getKeyserver();
public abstract boolean isSkipSave(); public abstract boolean isSkipSave();
public static ImportKeyringParcel createImportKeyringParcel(ArrayList<ParcelableKeyRing> keyList, public static ImportKeyringParcel createImportKeyringParcel(List<ParcelableKeyRing> keyList,
HkpKeyserverAddress keyserver) { HkpKeyserverAddress keyserver) {
return new AutoValue_ImportKeyringParcel(keyList, keyserver, false); return new AutoValue_ImportKeyringParcel(keyList, keyserver, false);
} }
public static ImportKeyringParcel createWithSkipSave(ArrayList<ParcelableKeyRing> keyList, public static ImportKeyringParcel createWithSkipSave(List<ParcelableKeyRing> keyList,
HkpKeyserverAddress keyserver) { HkpKeyserverAddress keyserver) {
return new AutoValue_ImportKeyringParcel(keyList, keyserver, true); return new AutoValue_ImportKeyringParcel(keyList, keyserver, true);
} }
public static ImportKeyringParcel createImportKeyringParcel(ParcelableKeyRing key) {
return new AutoValue_ImportKeyringParcel(Collections.singletonList(key), null, false);
}
public static ImportKeyringParcel createFromFileCacheWithSkipSave() { public static ImportKeyringParcel createFromFileCacheWithSkipSave() {
return new AutoValue_ImportKeyringParcel(null, null, true); return new AutoValue_ImportKeyringParcel(null, null, true);
} }

View File

@@ -27,6 +27,7 @@ import android.net.Uri;
import android.os.Build.VERSION_CODES; import android.os.Build.VERSION_CODES;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.Parcelable;
import android.support.annotation.RequiresApi; import android.support.annotation.RequiresApi;
import android.support.v4.app.LoaderManager; import android.support.v4.app.LoaderManager;
import android.support.v4.app.LoaderManager.LoaderCallbacks; import android.support.v4.app.LoaderManager.LoaderCallbacks;
@@ -37,12 +38,18 @@ import android.view.LayoutInflater;
import org.openintents.openpgp.util.OpenPgpUtils; import org.openintents.openpgp.util.OpenPgpUtils;
import org.openintents.openpgp.util.OpenPgpUtils.UserId; import org.openintents.openpgp.util.OpenPgpUtils.UserId;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
import org.sufficientlysecure.keychain.network.KeyTransferInteractor; import org.sufficientlysecure.keychain.network.KeyTransferInteractor;
import org.sufficientlysecure.keychain.network.KeyTransferInteractor.KeyTransferCallback; import org.sufficientlysecure.keychain.network.KeyTransferInteractor.KeyTransferCallback;
import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing; import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.KeyRepository; import org.sufficientlysecure.keychain.provider.KeyRepository;
import org.sufficientlysecure.keychain.provider.KeyRepository.NotFoundException; import org.sufficientlysecure.keychain.provider.KeyRepository.NotFoundException;
import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper.Callback;
import org.sufficientlysecure.keychain.ui.transfer.loader.SecretKeyLoader.SecretKeyItem; import org.sufficientlysecure.keychain.ui.transfer.loader.SecretKeyLoader.SecretKeyItem;
import org.sufficientlysecure.keychain.ui.transfer.view.ReceivedSecretKeyList.OnClickImportKeyListener; import org.sufficientlysecure.keychain.ui.transfer.view.ReceivedSecretKeyList.OnClickImportKeyListener;
import org.sufficientlysecure.keychain.ui.transfer.view.ReceivedSecretKeyList.ReceivedKeyAdapter; import org.sufficientlysecure.keychain.ui.transfer.view.ReceivedSecretKeyList.ReceivedKeyAdapter;
@@ -61,11 +68,14 @@ public class TransferPresenter implements KeyTransferCallback, LoaderCallbacks<L
private final LoaderManager loaderManager; private final LoaderManager loaderManager;
private final int loaderId; private final int loaderId;
private KeyTransferInteractor keyTransferClientInteractor;
private KeyTransferInteractor keyTransferServerInteractor;
private final TransferKeyAdapter secretKeyAdapter; private final TransferKeyAdapter secretKeyAdapter;
private final ReceivedKeyAdapter receivedKeyAdapter; private final ReceivedKeyAdapter receivedKeyAdapter;
private KeyTransferInteractor keyTransferClientInteractor;
private KeyTransferInteractor keyTransferServerInteractor;
private boolean wasConnected = false;
public TransferPresenter(Context context, LoaderManager loaderManager, int loaderId, TransferMvpView view) { public TransferPresenter(Context context, LoaderManager loaderManager, int loaderId, TransferMvpView view) {
this.context = context; this.context = context;
this.view = view; this.view = view;
@@ -83,7 +93,7 @@ public class TransferPresenter implements KeyTransferCallback, LoaderCallbacks<L
public void onUiStart() { public void onUiStart() {
loaderManager.restartLoader(loaderId, null, this); loaderManager.restartLoader(loaderId, null, this);
if (keyTransferServerInteractor == null && keyTransferClientInteractor == null) { if (keyTransferServerInteractor == null && keyTransferClientInteractor == null && !wasConnected) {
connectionStartListen(); connectionStartListen();
} }
} }
@@ -115,26 +125,72 @@ public class TransferPresenter implements KeyTransferCallback, LoaderCallbacks<L
} }
@Override @Override
public void onUiClickImportKey(String keyData) { public void onUiClickImportKey(final long masterKeyId, String keyData) {
receivedKeyAdapter.focusItem(masterKeyId);
final ImportKeyringParcel importKeyringParcel = ImportKeyringParcel.createImportKeyringParcel(
ParcelableKeyRing.createFromEncodedBytes(keyData.getBytes()));
CryptoOperationHelper<ImportKeyringParcel,ImportKeyResult> op =
view.createCryptoOperationHelper(new Callback<ImportKeyringParcel,ImportKeyResult>() {
@Override
public ImportKeyringParcel createOperationInput() {
return importKeyringParcel;
}
@Override
public void onCryptoOperationSuccess(ImportKeyResult result) {
receivedKeyAdapter.focusItem(null);
receivedKeyAdapter.addToFinishedItems(masterKeyId);
view.releaseCryptoOperationHelper();
view.showResultNotification(result);
}
@Override
public void onCryptoOperationCancelled() {
view.releaseCryptoOperationHelper();
receivedKeyAdapter.focusItem(null);
}
@Override
public void onCryptoOperationError(ImportKeyResult result) {
receivedKeyAdapter.focusItem(null);
view.releaseCryptoOperationHelper();
view.showResultNotification(result);
}
@Override
public boolean onCryptoSetProgress(String msg, int progress, int max) {
return false;
}
});
op.cryptoOperation();
} }
@Override @Override
public void onServerStarted(String qrCodeData) { public void onServerStarted(String qrCodeData) {
Bitmap qrCodeBitmap = QrCodeUtils.getQRCodeBitmap(Uri.parse("pgp+transfer://" + qrCodeData)); Bitmap qrCodeBitmap = QrCodeUtils.getQRCodeBitmap(Uri.parse(qrCodeData));
view.setQrImage(qrCodeBitmap); view.setQrImage(qrCodeBitmap);
} }
@Override @Override
public void onConnectionEstablished(String otherName) { public void onConnectionEstablished(String otherName) {
wasConnected = true;
secretKeyAdapter.clearFinishedItems(); secretKeyAdapter.clearFinishedItems();
view.showConnectionEstablished(otherName); view.showConnectionEstablished(otherName);
} }
@Override @Override
public void onConnectionLost() { public void onConnectionLost() {
connectionStartListen(); if (!wasConnected) {
// display connection error?
connectionStartListen();
view.showErrorConnectionFailed();
}
// TODO handle error?
} }
@Override @Override
@@ -152,7 +208,8 @@ public class TransferPresenter implements KeyTransferCallback, LoaderCallbacks<L
uncachedKeyRing.getCreationTime(), userId.name, userId.email); uncachedKeyRing.getCreationTime(), userId.name, userId.email);
receivedKeyAdapter.addItem(receivedKeyItem); receivedKeyAdapter.addItem(receivedKeyItem);
} catch (PgpGeneralException | IOException e) { } catch (PgpGeneralException | IOException e) {
e.printStackTrace(); Log.e(Constants.TAG, "error parsing incoming key", e);
view.showErrorBadKey();
} }
} }
@@ -231,7 +288,15 @@ public class TransferPresenter implements KeyTransferCallback, LoaderCallbacks<L
void scanQrCode(); void scanQrCode();
void setQrImage(Bitmap qrCode); void setQrImage(Bitmap qrCode);
void releaseCryptoOperationHelper();
void showErrorBadKey();
void showErrorConnectionFailed();
void showResultNotification(ImportKeyResult result);
void setSecretKeyAdapter(Adapter adapter); void setSecretKeyAdapter(Adapter adapter);
void setReceivedKeyAdapter(Adapter secretKeyAdapter); void setReceivedKeyAdapter(Adapter secretKeyAdapter);
<T extends Parcelable, S extends OperationResult> CryptoOperationHelper<T,S> createCryptoOperationHelper(Callback<T, S> callback);
} }
} }

View File

@@ -87,6 +87,11 @@ public class ReceivedSecretKeyList extends RecyclerView {
return data.get(position).masterKeyId; return data.get(position).masterKeyId;
} }
public void addToFinishedItems(long masterKeyId) {
finishedItems.add(masterKeyId);
// doeesn't notify, because it's non-trivial and this is called in conjunction with other refreshing things!
}
public void focusItem(Long masterKeyId) { public void focusItem(Long masterKeyId) {
focusedMasterKeyId = masterKeyId; focusedMasterKeyId = masterKeyId;
notifyItemRangeChanged(0, getItemCount()); notifyItemRangeChanged(0, getItemCount());
@@ -154,7 +159,7 @@ public class ReceivedSecretKeyList extends RecyclerView {
vImportButton.setOnClickListener(new OnClickListener() { vImportButton.setOnClickListener(new OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
onClickReceiveKeyListener.onUiClickImportKey(item.keyData); onClickReceiveKeyListener.onUiClickImportKey(item.masterKeyId, item.keyData);
} }
}); });
} else { } else {
@@ -164,7 +169,7 @@ public class ReceivedSecretKeyList extends RecyclerView {
} }
public interface OnClickImportKeyListener { public interface OnClickImportKeyListener {
void onUiClickImportKey(String keyData); void onUiClickImportKey(long masterKeyId, String keyData);
} }
public static class ReceivedKeyItem { public static class ReceivedKeyItem {

View File

@@ -18,16 +18,12 @@
package org.sufficientlysecure.keychain.ui.transfer.view; package org.sufficientlysecure.keychain.ui.transfer.view;
import java.util.Timer;
import java.util.TimerTask;
import android.app.Activity; import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Intent; import android.content.Intent;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.os.Build.VERSION_CODES; import android.os.Build.VERSION_CODES;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Parcelable;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.annotation.RequiresApi; import android.support.annotation.RequiresApi;
import android.support.v4.app.Fragment; import android.support.v4.app.Fragment;
@@ -44,8 +40,12 @@ import android.widget.ViewAnimator;
import com.google.zxing.client.android.Intents; import com.google.zxing.client.android.Intents;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
import org.sufficientlysecure.keychain.ui.QrCodeCaptureActivity; import org.sufficientlysecure.keychain.ui.QrCodeCaptureActivity;
import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment; import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper.Callback;
import org.sufficientlysecure.keychain.ui.transfer.presenter.TransferPresenter; import org.sufficientlysecure.keychain.ui.transfer.presenter.TransferPresenter;
import org.sufficientlysecure.keychain.ui.transfer.presenter.TransferPresenter.TransferMvpView; import org.sufficientlysecure.keychain.ui.transfer.presenter.TransferPresenter.TransferMvpView;
@@ -54,8 +54,7 @@ import org.sufficientlysecure.keychain.ui.transfer.presenter.TransferPresenter.T
public class TransferFragment extends Fragment implements TransferMvpView { public class TransferFragment extends Fragment implements TransferMvpView {
public static final int VIEW_WAITING = 0; public static final int VIEW_WAITING = 0;
public static final int VIEW_CONNECTED = 1; public static final int VIEW_CONNECTED = 1;
public static final int VIEW_SEND_OK = 2; public static final int VIEW_RECEIVING = 2;
public static final int VIEW_RECEIVING = 3;
public static final int REQUEST_CODE_SCAN = 1; public static final int REQUEST_CODE_SCAN = 1;
public static final int LOADER_ID = 1; public static final int LOADER_ID = 1;
@@ -68,6 +67,8 @@ public class TransferFragment extends Fragment implements TransferMvpView {
private RecyclerView vTransferKeyList; private RecyclerView vTransferKeyList;
private RecyclerView vReceivedKeyList; private RecyclerView vReceivedKeyList;
private CryptoOperationHelper currentCryptoOperationHelper;
@Override @Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
@@ -90,15 +91,9 @@ public class TransferFragment extends Fragment implements TransferMvpView {
} }
}); });
return view;
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
presenter = new TransferPresenter(getContext(), getLoaderManager(), LOADER_ID, this); presenter = new TransferPresenter(getContext(), getLoaderManager(), LOADER_ID, this);
return view;
} }
@Override @Override
@@ -162,8 +157,40 @@ public class TransferFragment extends Fragment implements TransferMvpView {
vReceivedKeyList.setAdapter(adapter); vReceivedKeyList.setAdapter(adapter);
} }
@Override
public <T extends Parcelable, S extends OperationResult> CryptoOperationHelper<T,S> createCryptoOperationHelper(Callback<T, S> callback) {
CryptoOperationHelper<T,S> cryptoOperationHelper = new CryptoOperationHelper<>(1, this, callback, null);
currentCryptoOperationHelper = cryptoOperationHelper;
return cryptoOperationHelper;
}
@Override
public void releaseCryptoOperationHelper() {
currentCryptoOperationHelper = null;
}
@Override
public void showErrorBadKey() {
}
@Override
public void showErrorConnectionFailed() {
}
@Override
public void showResultNotification(ImportKeyResult result) {
result.createNotify(getActivity()).show(this);
}
@Override @Override
public void onActivityResult(int requestCode, int resultCode, Intent data) { public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (currentCryptoOperationHelper != null &&
currentCryptoOperationHelper.handleActivityResult(requestCode, resultCode, data)) {
return;
}
switch (requestCode) { switch (requestCode) {
case REQUEST_CODE_SCAN: case REQUEST_CODE_SCAN:
if (resultCode == Activity.RESULT_OK) { if (resultCode == Activity.RESULT_OK) {

View File

@@ -87,30 +87,6 @@
</LinearLayout> </LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center_vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="32dp"
android:layout_marginBottom="32dp"
android:text="Key transfer ok!"
android:textAppearance="?android:attr/textAppearanceMedium" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="Finish"
android:textAppearance="?android:attr/textAppearanceMedium"
/>
</LinearLayout>
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"