token-import: load from file

This commit is contained in:
Vincent Breitmoser
2017-09-05 02:23:53 +02:00
parent af7d36c038
commit bc3031c9eb
4 changed files with 107 additions and 7 deletions

View File

@@ -20,6 +20,7 @@ package org.sufficientlysecure.keychain.ui;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.annotation.StringRes;
@@ -48,6 +49,7 @@ import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.ui.widget.StatusIndicator;
import org.sufficientlysecure.keychain.ui.widget.StatusIndicator.Status;
import org.sufficientlysecure.keychain.ui.widget.ToolableViewAnimator;
import org.sufficientlysecure.keychain.util.FileHelper;
public class CreateSecurityTokenImportFragment extends Fragment implements CreateSecurityTokenImportMvpView,
@@ -56,6 +58,7 @@ public class CreateSecurityTokenImportFragment extends Fragment implements Creat
private static final String ARG_AID = "aid";
private static final String ARG_USER_ID = "user_ids";
private static final String ARG_URL = "key_uri";
public static final int REQUEST_CODE_OPEN_FILE = 0;
CreateSecurityTokenImportPresenter presenter;
private ViewGroup statusLayoutGroup;
@@ -114,6 +117,7 @@ public class CreateSecurityTokenImportFragment extends Fragment implements Creat
view.findViewById(R.id.button_reset_token_1).setOnClickListener(this);
view.findViewById(R.id.button_reset_token_2).setOnClickListener(this);
view.findViewById(R.id.button_reset_token_3).setOnClickListener(this);
view.findViewById(R.id.button_load_file).setOnClickListener(this);
setHasOptionsMenu(true);
@@ -228,6 +232,27 @@ public class CreateSecurityTokenImportFragment extends Fragment implements Creat
cryptoPromoteOperationHelper.cryptoOperation();
}
@Override
public void showFileSelectDialog() {
FileHelper.openDocument(this, null, "*/*", false, REQUEST_CODE_OPEN_FILE);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case REQUEST_CODE_OPEN_FILE: {
if (resultCode == Activity.RESULT_OK && data != null && data.getData() != null) {
Uri fileUri = data.getData();
presenter.onFileSelected(fileUri);
}
break;
}
default: {
super.onActivityResult(requestCode, resultCode, data);
}
}
}
@Override
public void onClick(View v) {
switch (v.getId()) {
@@ -243,6 +268,9 @@ public class CreateSecurityTokenImportFragment extends Fragment implements Creat
presenter.onClickViewKey();
break;
}
case R.id.button_load_file: {
presenter.onClickLoadFile();
}
case R.id.button_reset_token_1:
case R.id.button_reset_token_2:
case R.id.button_reset_token_3: {
@@ -298,7 +326,8 @@ public class CreateSecurityTokenImportFragment extends Fragment implements Creat
SEARCH_KEYSERVER (R.string.status_search_keyserver),
IMPORT (R.string.status_import),
TOKEN_PROMOTE(R.string.status_token_promote),
TOKEN_CHECK (R.string.status_token_check);
TOKEN_CHECK (R.string.status_token_check),
SEARCH_CONTENT_URI (R.string.status_content_uri);
@StringRes
private int stringRes;

View File

@@ -19,13 +19,14 @@ package org.sufficientlysecure.keychain.ui;
import android.content.Context;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.LoaderManager;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.Loader;
import org.sufficientlysecure.keychain.provider.KeyRepository;
import org.sufficientlysecure.keychain.ui.CreateSecurityTokenImportFragment.StatusLine;
import org.sufficientlysecure.keychain.ui.PublicKeyRetrievalLoader.ContentUriRetrievalLoader;
import org.sufficientlysecure.keychain.ui.PublicKeyRetrievalLoader.KeyRetrievalResult;
import org.sufficientlysecure.keychain.ui.PublicKeyRetrievalLoader.KeyserverRetrievalLoader;
import org.sufficientlysecure.keychain.ui.PublicKeyRetrievalLoader.LocalKeyLookupLoader;
@@ -36,6 +37,8 @@ class CreateSecurityTokenImportPresenter {
private static final int LOADER_LOCAL = 0;
private static final int LOADER_URI = 1;
private static final int LOADER_KEYSERVER = 2;
private static final int LOADER_CONTENT_URI = 3;
private static final String ARG_CONTENT_URI = "content_uri";
private Context context;
@@ -119,6 +122,9 @@ class CreateSecurityTokenImportPresenter {
return new UriKeyRetrievalLoader(context, tokenUrl, tokenFingerprints);
case LOADER_KEYSERVER:
return new KeyserverRetrievalLoader(context, tokenFingerprints[0]);
case LOADER_CONTENT_URI:
return new ContentUriRetrievalLoader(context, tokenFingerprints[0],
args.<Uri>getParcelable(ARG_CONTENT_URI));
}
throw new IllegalArgumentException("called with unknown loader id!");
}
@@ -138,6 +144,10 @@ class CreateSecurityTokenImportPresenter {
searchedKeyservers = true;
break;
}
case LOADER_CONTENT_URI: {
// nothing to do here
break;
}
default: {
throw new IllegalArgumentException("called with unknown loader id!");
}
@@ -218,7 +228,20 @@ class CreateSecurityTokenImportPresenter {
}
public void onClickResetToken() {
// TODO
}
void onClickLoadFile() {
view.showFileSelectDialog();
}
void onFileSelected(Uri contentUri) {
view.resetStatusLines();
view.statusLineAdd(StatusLine.SEARCH_CONTENT_URI);
Bundle args = new Bundle();
args.putParcelable(ARG_CONTENT_URI, contentUri);
loaderManager.restartLoader(LOADER_CONTENT_URI, args, loaderCallbacks);
}
interface CreateSecurityTokenImportMvpView {
@@ -236,5 +259,7 @@ class CreateSecurityTokenImportPresenter {
void operationPromote(long masterKeyId, byte[] cardAid);
void finishAndShowKey(long masterKeyId);
void showFileSelectDialog();
}
}

View File

@@ -18,16 +18,21 @@
package org.sufficientlysecure.keychain.ui;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import android.content.ContentResolver;
import android.content.Context;
import android.net.Uri;
import android.os.SystemClock;
import android.support.annotation.Nullable;
import android.support.v4.content.AsyncTaskLoader;
import android.util.Log;
import com.google.auto.value.AutoValue;
import okhttp3.Call;
import okhttp3.Request.Builder;
import okhttp3.Response;
import org.sufficientlysecure.keychain.Constants;
@@ -35,10 +40,9 @@ import org.sufficientlysecure.keychain.keyimport.HkpKeyserverAddress;
import org.sufficientlysecure.keychain.keyimport.HkpKeyserverClient;
import org.sufficientlysecure.keychain.keyimport.KeyserverClient.QueryFailedException;
import org.sufficientlysecure.keychain.network.OkHttpClientFactory;
import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing;
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing.IteratorWithIOThrow;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
import org.sufficientlysecure.keychain.provider.KeyRepository;
import org.sufficientlysecure.keychain.provider.KeyRepository.NotFoundException;
@@ -66,7 +70,7 @@ public abstract class PublicKeyRetrievalLoader extends AsyncTaskLoader<KeyRetrie
KeyRetrievalResult keyRetrievalResult = super.onLoadInBackground();
try {
long elapsedTime = startTime - SystemClock.elapsedRealtime();
long elapsedTime = SystemClock.elapsedRealtime() - startTime;
if (elapsedTime < MIN_OPERATION_TIME_MILLIS) {
Thread.sleep(MIN_OPERATION_TIME_MILLIS - elapsedTime);
}
@@ -130,8 +134,8 @@ public abstract class PublicKeyRetrievalLoader extends AsyncTaskLoader<KeyRetrie
@Override
public KeyRetrievalResult loadInBackground() {
try {
Response execute =
OkHttpClientFactory.getSimpleClient().newCall(new Builder().url(yubikeyUri).build()).execute();
Call call = OkHttpClientFactory.getSimpleClient().newCall(new Builder().url(yubikeyUri).build());
Response execute = call.execute();
if (execute.isSuccessful()) {
UncachedKeyRing keyRing = UncachedKeyRing.decodeFromData(execute.body().bytes());
if (Arrays.equals(fingerprints[0], keyRing.getFingerprint())) {
@@ -162,6 +166,10 @@ public abstract class PublicKeyRetrievalLoader extends AsyncTaskLoader<KeyRetrie
HkpKeyserverClient keyserverClient = HkpKeyserverClient.fromHkpKeyserverAddress(preferredKeyserver);
if (true) {
return KeyRetrievalResult.createWithError();
}
try {
String keyString =
keyserverClient.get("0x" + KeyFormattingUtils.convertFingerprintToHex(fingerprint), parcelableProxy);
@@ -176,6 +184,43 @@ public abstract class PublicKeyRetrievalLoader extends AsyncTaskLoader<KeyRetrie
}
}
public static class ContentUriRetrievalLoader extends PublicKeyRetrievalLoader {
private final ContentResolver contentResolver;
private final byte[] fingerprint;
private final Uri uri;
public ContentUriRetrievalLoader(Context context, byte[] fingerprint, Uri uri) {
super(context);
this.fingerprint = fingerprint;
this.uri = uri;
this.contentResolver = context.getContentResolver();
}
@Override
public KeyRetrievalResult loadInBackground() {
try {
InputStream is = contentResolver.openInputStream(uri);
if (is == null) {
return KeyRetrievalResult.createWithError();
}
IteratorWithIOThrow<UncachedKeyRing> uncachedKeyRingIterator = UncachedKeyRing.fromStream(
new BufferedInputStream(is));
while (uncachedKeyRingIterator.hasNext()) {
UncachedKeyRing keyRing = uncachedKeyRingIterator.next();
if (Arrays.equals(fingerprint, keyRing.getFingerprint())) {
return KeyRetrievalResult.createWithKeyringdata(keyRing.getMasterKeyId(), keyRing.getEncoded());
}
}
} catch (IOException e) {
Log.e(Constants.TAG, "error retrieving key from keyserver", e);
}
return KeyRetrievalResult.createWithError();
}
}
@Override
public void deliverResult(KeyRetrievalResult result) {
cachedResult = result;

View File

@@ -1924,5 +1924,6 @@
<string name="status_import">Importing key…</string>
<string name="status_token_promote">Setting up key…</string>
<string name="status_token_check">Checking key setup…</string>
<string name="status_content_uri">Reading file…</string>
</resources>