tls-psk: working version, with fake progress too!

This commit is contained in:
Vincent Breitmoser
2017-05-30 14:39:31 +02:00
parent 747feaa100
commit 0c3a247d9e
5 changed files with 293 additions and 134 deletions

View File

@@ -22,6 +22,7 @@ import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.Socket;
@@ -53,9 +54,11 @@ import org.sufficientlysecure.keychain.util.Log;
@RequiresApi(api = VERSION_CODES.LOLLIPOP)
public class KeyTransferInteractor {
private static final int SHOW_CONNECTION_DETAILS = 1;
private static final int CONNECTION_LISTENING = 1;
private static final int CONNECTION_ESTABLISHED = 2;
private static final int CONNECTION_LOST = 3;
private static final int CONNECTION_SEND_OK = 3;
private static final int CONNECTION_RECEIVE_OK = 4;
private static final int CONNECTION_LOST = 5;
private TransferThread transferThread;
@@ -121,7 +124,7 @@ public class KeyTransferInteractor {
String presharedKeyEncoded = Base64.encodeToString(presharedKey, Base64.URL_SAFE | Base64.NO_PADDING);
String qrCodeData = presharedKeyEncoded + "@" + getIPAddress(true) + ":" + port;
invokeListener(SHOW_CONNECTION_DETAILS, qrCodeData);
invokeListener(CONNECTION_LISTENING, qrCodeData);
socket = serverSocket.accept();
invokeListener(CONNECTION_ESTABLISHED, socket.getInetAddress().toString());
@@ -131,6 +134,7 @@ public class KeyTransferInteractor {
}
handleOpenConnection(socket);
Log.d(Constants.TAG, "connection closed ok!");
} catch (IOException e) {
Log.e(Constants.TAG, "error!", e);
} finally {
@@ -164,33 +168,70 @@ public class KeyTransferInteractor {
}
private void handleOpenConnection(Socket socket) throws IOException {
socket.setSoTimeout(500);
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
socket.setSoTimeout(500);
while (!isInterrupted() && socket.isConnected()) {
if (dataToSend != null) {
BufferedOutputStream bufferedOutputStream =
new BufferedOutputStream(socket.getOutputStream());
bufferedOutputStream.write(dataToSend);
bufferedOutputStream.close();
dataToSend = null;
break;
if (sendDataIfAvailable(socket)) {
return;
}
try {
String line = bufferedReader.readLine();
if (line == null) {
Log.d(Constants.TAG, "eof");
break;
}
Log.d(Constants.TAG, "got line: " + line);
} catch (SocketTimeoutException e) {
// ignore
if (receiveDataIfAvailable(socket, bufferedReader)) {
return;
}
}
Log.d(Constants.TAG, "disconnected");
invokeListener(CONNECTION_LOST, null);
}
private boolean receiveDataIfAvailable(Socket socket, BufferedReader bufferedReader) throws IOException {
String firstLine;
try {
firstLine = bufferedReader.readLine();
} catch (SocketTimeoutException e) {
return false;
}
if (firstLine == null) {
invokeListener(CONNECTION_LOST, null);
return true;
}
socket.setSoTimeout(2000);
String receivedData = receiveAfterFirstLineAndClose(bufferedReader, firstLine);
invokeListener(CONNECTION_RECEIVE_OK, receivedData);
return true;
}
private boolean sendDataIfAvailable(Socket socket) throws IOException {
if (dataToSend != null) {
byte[] data = dataToSend;
dataToSend = null;
socket.setSoTimeout(2000);
sendDataAndClose(socket.getOutputStream(), data);
invokeListener(CONNECTION_SEND_OK, null);
return true;
}
return false;
}
private String receiveAfterFirstLineAndClose(BufferedReader bufferedReader, String line) throws IOException {
StringBuilder builder = new StringBuilder();
do {
builder.append(line);
line = bufferedReader.readLine();
} while (line != null);
return builder.toString();
}
private void sendDataAndClose(OutputStream outputStream, byte[] data) throws IOException {
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream);
bufferedOutputStream.write(data);
bufferedOutputStream.close();
}
private void invokeListener(final int method, final String arg) {
if (handler == null) {
return;
@@ -203,12 +244,18 @@ public class KeyTransferInteractor {
return;
}
switch (method) {
case SHOW_CONNECTION_DETAILS:
case CONNECTION_LISTENING:
callback.onServerStarted(arg);
break;
case CONNECTION_ESTABLISHED:
callback.onConnectionEstablished(arg);
break;
case CONNECTION_RECEIVE_OK:
callback.onDataReceivedOk(arg);
break;
case CONNECTION_SEND_OK:
callback.onDataSentOk(arg);
break;
case CONNECTION_LOST:
callback.onConnectionLost();
}
@@ -218,7 +265,7 @@ public class KeyTransferInteractor {
handler.post(runnable);
}
public synchronized void sendDataAndClose(byte[] dataToSend) {
synchronized void sendDataAndClose(byte[] dataToSend) {
this.dataToSend = dataToSend;
}
@@ -258,6 +305,9 @@ public class KeyTransferInteractor {
void onServerStarted(String qrCodeData);
void onConnectionEstablished(String otherName);
void onConnectionLost();
void onDataReceivedOk(String receivedData);
void onDataSentOk(String arg);
}
/**
@@ -293,7 +343,8 @@ public class KeyTransferInteractor {
}
}
} catch (Exception ex) {
} // for now eat exceptions
// ignore
}
return "";
}

View File

@@ -33,6 +33,7 @@ import android.support.v4.content.Loader;
import android.support.v7.widget.RecyclerView.Adapter;
import android.view.LayoutInflater;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.network.KeyTransferInteractor;
import org.sufficientlysecure.keychain.network.KeyTransferInteractor.KeyTransferCallback;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
@@ -42,6 +43,7 @@ import org.sufficientlysecure.keychain.ui.transfer.loader.SecretKeyLoader.Secret
import org.sufficientlysecure.keychain.ui.transfer.view.TransferSecretKeyList.OnClickTransferKeyListener;
import org.sufficientlysecure.keychain.ui.transfer.view.TransferSecretKeyList.TransferKeyAdapter;
import org.sufficientlysecure.keychain.ui.util.QrCodeUtils;
import org.sufficientlysecure.keychain.util.Log;
@RequiresApi(api = VERSION_CODES.LOLLIPOP)
@@ -66,43 +68,45 @@ public class TransferPresenter implements KeyTransferCallback, LoaderCallbacks<L
view.setSecretKeyAdapter(secretKeyAdapter);
}
public void onStart() {
public void onUiStart() {
loaderManager.restartLoader(loaderId, null, this);
startServer();
if (keyTransferServerInteractor == null && keyTransferClientInteractor == null) {
connectionStartListen();
}
}
public void onStop() {
clearConnections();
public void onUiStop() {
connectionClear();
}
public void onClickScan() {
clearConnections();
public void onUiClickScan() {
connectionClear();
view.scanQrCode();
}
public void onQrCodeScanned(String qrCodeContent) {
keyTransferClientInteractor = new KeyTransferInteractor();
keyTransferClientInteractor.connectToServer(qrCodeContent, this);
public void onUiQrCodeScanned(String qrCodeContent) {
connectionStartConnect(qrCodeContent);
}
private void clearConnections() {
if (keyTransferServerInteractor != null) {
keyTransferServerInteractor.closeConnection();
keyTransferServerInteractor = null;
}
if (keyTransferClientInteractor != null) {
keyTransferClientInteractor.closeConnection();
keyTransferClientInteractor = null;
@Override
public void onUiClickTransferKey(long masterKeyId) {
try {
byte[] armoredSecretKey =
KeyRepository.createDatabaseInteractor(context).getSecretKeyRingAsArmoredData(masterKeyId);
connectionSend(armoredSecretKey);
} catch (IOException | NotFoundException | PgpGeneralException e) {
e.printStackTrace();
}
}
public void startServer() {
keyTransferServerInteractor = new KeyTransferInteractor();
keyTransferServerInteractor.startServer(this);
public void onUiFakeProgressFinished() {
view.showKeySentOk();
}
@Override
public void onServerStarted(String qrCodeData) {
Bitmap qrCodeBitmap = QrCodeUtils.getQRCodeBitmap(Uri.parse("pgp+transfer://" + qrCodeData));
@@ -116,10 +120,57 @@ public class TransferPresenter implements KeyTransferCallback, LoaderCallbacks<L
@Override
public void onConnectionLost() {
view.showWaitingForConnection();
startServer();
connectionStartListen();
}
@Override
public void onDataReceivedOk(String receivedData) {
Log.d(Constants.TAG, "received: " + receivedData);
}
@Override
public void onDataSentOk(String arg) {
Log.d(Constants.TAG, "data sent ok!");
view.showFakeSendProgressDialog();
}
private void connectionStartConnect(String qrCodeContent) {
connectionClear();
keyTransferClientInteractor = new KeyTransferInteractor();
keyTransferClientInteractor.connectToServer(qrCodeContent, this);
}
private void connectionStartListen() {
connectionClear();
keyTransferServerInteractor = new KeyTransferInteractor();
keyTransferServerInteractor.startServer(this);
view.showWaitingForConnection();
}
private void connectionClear() {
if (keyTransferServerInteractor != null) {
keyTransferServerInteractor.closeConnection();
keyTransferServerInteractor = null;
}
if (keyTransferClientInteractor != null) {
keyTransferClientInteractor.closeConnection();
keyTransferClientInteractor = null;
}
}
private void connectionSend(byte[] armoredSecretKey) {
if (keyTransferClientInteractor != null) {
keyTransferClientInteractor.sendData(armoredSecretKey);
} else if (keyTransferServerInteractor != null) {
keyTransferServerInteractor.sendData(armoredSecretKey);
}
}
@Override
public Loader<List<SecretKeyItem>> onCreateLoader(int id, Bundle args) {
return secretKeyAdapter.createLoader(context);
@@ -135,20 +186,6 @@ public class TransferPresenter implements KeyTransferCallback, LoaderCallbacks<L
secretKeyAdapter.setData(null);
}
@Override
public void onClickTransferKey(long masterKeyId) {
try {
byte[] armoredSecretKey =
KeyRepository.createDatabaseInteractor(context).getSecretKeyRingAsArmoredData(masterKeyId);
if (keyTransferClientInteractor != null) {
keyTransferClientInteractor.sendData(armoredSecretKey);
} else if (keyTransferServerInteractor != null) {
keyTransferServerInteractor.sendData(armoredSecretKey);
}
} catch (IOException | NotFoundException | PgpGeneralException e) {
e.printStackTrace();
}
}
public interface TransferMvpView {
void showWaitingForConnection();
@@ -158,5 +195,8 @@ public class TransferPresenter implements KeyTransferCallback, LoaderCallbacks<L
void setQrImage(Bitmap qrCode);
void setSecretKeyAdapter(Adapter adapter);
void showFakeSendProgressDialog();
void showKeySentOk();
}
}

View File

@@ -18,11 +18,16 @@
package org.sufficientlysecure.keychain.ui.transfer.view;
import java.util.Timer;
import java.util.TimerTask;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
import android.os.Handler;
import android.support.annotation.Nullable;
import android.support.annotation.RequiresApi;
import android.support.v4.app.Fragment;
@@ -40,6 +45,7 @@ import android.widget.ViewAnimator;
import com.google.zxing.client.android.Intents;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.ui.QrCodeCaptureActivity;
import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment;
import org.sufficientlysecure.keychain.ui.transfer.presenter.TransferPresenter;
import org.sufficientlysecure.keychain.ui.transfer.presenter.TransferPresenter.TransferMvpView;
@@ -48,6 +54,8 @@ import org.sufficientlysecure.keychain.ui.transfer.presenter.TransferPresenter.T
public class TransferFragment extends Fragment implements TransferMvpView {
public static final int VIEW_WAITING = 0;
public static final int VIEW_CONNECTED = 1;
public static final int VIEW_SEND_OK = 2;
public static final int REQUEST_CODE_SCAN = 1;
public static final int LOADER_ID = 1;
@@ -74,7 +82,7 @@ public class TransferFragment extends Fragment implements TransferMvpView {
@Override
public void onClick(View v) {
if (presenter != null) {
presenter.onClickScan();
presenter.onUiClickScan();
}
}
});
@@ -94,14 +102,14 @@ public class TransferFragment extends Fragment implements TransferMvpView {
public void onStart() {
super.onStart();
presenter.onStart();
presenter.onUiStart();
}
@Override
public void onStop() {
super.onStop();
presenter.onStop();
presenter.onUiStop();
}
@Override
@@ -111,8 +119,13 @@ public class TransferFragment extends Fragment implements TransferMvpView {
@Override
public void showConnectionEstablished(String hostname) {
vTransferAnimator.setDisplayedChild(VIEW_CONNECTED);
vConnectionStatusText.setText("Connected to: " + hostname);
vTransferAnimator.setDisplayedChild(VIEW_CONNECTED);
}
@Override
public void showKeySentOk() {
vTransferAnimator.setDisplayedChild(VIEW_SEND_OK);
}
@Override
@@ -141,13 +154,44 @@ public class TransferFragment extends Fragment implements TransferMvpView {
vTransferKeyList.setAdapter(adapter);
}
@Override
public void showFakeSendProgressDialog() {
final ProgressDialogFragment progressDialogFragment =
ProgressDialogFragment.newInstance("Sending key…", ProgressDialog.STYLE_HORIZONTAL, false);
progressDialogFragment.show(getFragmentManager(), "progress");
final Handler handler = new Handler();
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
int fakeProgress = 0;
@Override
public void run() {
fakeProgress += 6;
if (fakeProgress > 100) {
cancel();
progressDialogFragment.dismissAllowingStateLoss();
handler.post(new Runnable() {
@Override
public void run() {
presenter.onUiFakeProgressFinished();
}
});
return;
}
progressDialogFragment.setProgress(fakeProgress, 100);
}
}, 0, 100);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case REQUEST_CODE_SCAN:
if (resultCode == Activity.RESULT_OK) {
String qrCodeData = data.getStringExtra(Intents.Scan.RESULT);
presenter.onQrCodeScanned(qrCodeData);
presenter.onUiQrCodeScanned(qrCodeData);
}
break;
default:

View File

@@ -129,7 +129,7 @@ public class TransferSecretKeyList extends RecyclerView {
vSendButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
onClickTransferKeyListener.onClickTransferKey(item.masterKeyId);
onClickTransferKeyListener.onUiClickTransferKey(item.masterKeyId);
}
});
} else {
@@ -139,6 +139,6 @@ public class TransferSecretKeyList extends RecyclerView {
}
public interface OnClickTransferKeyListener {
void onClickTransferKey(long masterKeyId);
void onUiClickTransferKey(long masterKeyId);
}
}

View File

@@ -1,80 +1,80 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
<org.sufficientlysecure.keychain.ui.widget.ToolableViewAnimator
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:custom="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:custom="http://schemas.android.com/apk/res-auto">
android:id="@+id/transfer_animator"
android:inAnimation="@anim/fade_in_delayed"
android:outAnimation="@anim/fade_out"
custom:initialView="0">
<org.sufficientlysecure.keychain.ui.widget.ToolableViewAnimator
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/transfer_animator"
android:inAnimation="@anim/fade_in_delayed"
android:outAnimation="@anim/fade_out"
custom:initialView="01">
android:layout_height="match_parent"
android:gravity="center_vertical"
android:orientation="vertical"
android:padding="16dp">
<LinearLayout
android:layout_width="match_parent"
<ImageView
android:layout_width="250dp"
android:layout_height="250dp"
android:layout_gravity="center_horizontal"
android:layout_marginBottom="16dp"
android:id="@+id/qr_code_image"
tools:src="@drawable/ic_qrcode_white_24dp"
tools:tint="@color/md_black_1000"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingTop="16dp">
android:layout_gravity="center_horizontal"
android:layout_margin="8dp"
android:id="@+id/button_scan"
android:text="Scan"
android:drawableLeft="@drawable/ic_qrcode_white_24dp"
android:drawablePadding="8dp"
/>
<ImageView
android:layout_width="250dp"
android:layout_height="250dp"
android:layout_gravity="center_horizontal"
android:layout_margin="8dp"
android:id="@+id/qr_code_image"
tools:src="@drawable/ic_qrcode_white_24dp"
tools:tint="@color/md_black_1000"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_margin="8dp"
android:id="@+id/button_scan"
android:text="Scan"
android:drawableLeft="@drawable/ic_qrcode_white_24dp"
android:drawablePadding="8dp"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_margin="8dp"
android:text="Scan with either device to establish a secure connection for device setup."
android:textAppearance="?android:attr/textAppearanceMedium" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
android:layout_gravity="center_horizontal"
android:layout_margin="8dp"
android:text="Scan with either device to establish a secure connection for device setup."
android:textAppearance="?android:attr/textAppearanceMedium" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
android:id="@+id/connection_status"
android:text="Connected to: 123.456.123.123"
android:textAppearance="?android:attr/textAppearanceMedium" />
</LinearLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="Available Keys:"
android:textAppearance="?android:attr/textAppearanceMedium"
style="@style/SectionHeader"
/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
android:id="@+id/connection_status"
android:text="Connected to: 123.456.123.123"
android:textAppearance="?android:attr/textAppearanceMedium" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="Available Keys:"
android:textAppearance="?android:attr/textAppearanceMedium"
style="@style/SectionHeader"
/>
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<org.sufficientlysecure.keychain.ui.transfer.view.TransferSecretKeyList
android:layout_width="match_parent"
@@ -83,8 +83,32 @@
android:padding="16dp"
/>
</LinearLayout>
</ScrollView>
</org.sufficientlysecure.keychain.ui.widget.ToolableViewAnimator>
</LinearLayout>
</ScrollView>
<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>
</org.sufficientlysecure.keychain.ui.widget.ToolableViewAnimator>