diff --git a/OpenKeychain/src/main/AndroidManifest.xml b/OpenKeychain/src/main/AndroidManifest.xml
index d3bf9c3d5..a76982975 100644
--- a/OpenKeychain/src/main/AndroidManifest.xml
+++ b/OpenKeychain/src/main/AndroidManifest.xml
@@ -806,6 +806,13 @@
android:taskAffinity=":Nfc"
android:theme="@style/Theme.Keychain.Light.Dialog" />
+
+
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/DeleteOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/DeleteOperation.java
index b111efa5b..2932767b1 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/DeleteOperation.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/DeleteOperation.java
@@ -21,6 +21,7 @@ package org.sufficientlysecure.keychain.operations;
import android.content.Context;
import android.support.annotation.NonNull;
+import org.sufficientlysecure.keychain.BuildConfig;
import org.sufficientlysecure.keychain.operations.results.ConsolidateResult;
import org.sufficientlysecure.keychain.operations.results.DeleteResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
@@ -91,7 +92,7 @@ public class DeleteOperation extends BaseReadWriteOperation
}
}
- if (isSecret && success > 0) {
+ if (!BuildConfig.DEBUG && isSecret && success > 0) {
log.add(LogType.MSG_DEL_CONSOLIDATE, 1);
ConsolidateResult sub = mKeyWritableRepository.consolidateDatabaseStep1(mProgressable);
log.add(sub, 2);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/PromoteKeyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/PromoteKeyOperation.java
index 22b96db6a..80a4c5862 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/PromoteKeyOperation.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/PromoteKeyOperation.java
@@ -18,6 +18,8 @@
package org.sufficientlysecure.keychain.operations;
+import java.util.Arrays;
+import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import android.content.Context;
@@ -70,25 +72,24 @@ public class PromoteKeyOperation extends BaseReadWriteOperation fingerprints = promoteKeyringParcel.getFingerprints();
+ if (fingerprints == null) {
log.add(LogType.MSG_PR_ALL, 1);
} else {
// sort for binary search
for (CanonicalizedPublicKey key : pubRing.publicKeyIterator()) {
long subKeyId = key.getKeyId();
- if (naiveIndexOf(subKeyIds, subKeyId) != null) {
- log.add(LogType.MSG_PR_SUBKEY_MATCH, 1,
- KeyFormattingUtils.convertKeyIdToHex(subKeyId));
+
+ if (naiveArraySearch(fingerprints, key.getFingerprint())) {
+ log.add(LogType.MSG_PR_SUBKEY_MATCH, 1, KeyFormattingUtils.convertKeyIdToHex(subKeyId));
} else {
- log.add(LogType.MSG_PR_SUBKEY_NOMATCH, 1,
- KeyFormattingUtils.convertKeyIdToHex(subKeyId));
+ log.add(LogType.MSG_PR_SUBKEY_NOMATCH, 1, KeyFormattingUtils.convertKeyIdToHex(subKeyId));
}
}
}
// create divert-to-card secret key from public key
- promotedRing = pubRing.createDivertSecretRing(promoteKeyringParcel.getCardAid(), subKeyIds);
+ promotedRing = pubRing.createDivertSecretRing(promoteKeyringParcel.getCardAid(), fingerprints);
} catch (NotFoundException e) {
log.add(LogType.MSG_PR_ERROR_KEY_NOT_FOUND, 2);
@@ -128,13 +129,13 @@ public class PromoteKeyOperation extends BaseReadWriteOperation searchElements, byte[] needle) {
+ for (byte[] searchElement : searchElements) {
+ if (Arrays.equals(needle, searchElement)) {
+ return true;
}
}
- return null;
+ return false;
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/GenericOperationResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/GenericOperationResult.java
new file mode 100644
index 000000000..2ae0e5479
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/GenericOperationResult.java
@@ -0,0 +1,25 @@
+package org.sufficientlysecure.keychain.operations.results;
+
+
+import android.os.Parcel;
+
+
+public class GenericOperationResult extends OperationResult {
+ public GenericOperationResult(int result, OperationLog log) {
+ super(result, log);
+ }
+
+ public GenericOperationResult(Parcel source) {
+ super(source);
+ }
+
+ public static final Creator CREATOR = new Creator() {
+ public GenericOperationResult createFromParcel(final Parcel source) {
+ return new GenericOperationResult(source);
+ }
+
+ public GenericOperationResult[] newArray(final int size) {
+ return new GenericOperationResult[size];
+ }
+ };
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java
index f844acb0d..4c3266017 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java
@@ -905,6 +905,37 @@ public abstract class OperationResult implements Parcelable {
MSG_BENCH_S2K_100MS_ITS (LogLevel.INFO, R.string.msg_bench_s2k_100ms_its),
MSG_BENCH_SUCCESS (LogLevel.OK, R.string.msg_bench_success),
+ MSG_RET_CURI_ERROR_IO (LogLevel.ERROR, R.string.msg_ret_curi_error_io),
+ MSG_RET_CURI_ERROR_NO_MATCH (LogLevel.ERROR, R.string.msg_ret_curi_error_no_match),
+ MSG_RET_CURI_ERROR_NOT_FOUND (LogLevel.ERROR, R.string.msg_ret_curi_error_not_found),
+ MSG_RET_CURI_FOUND (LogLevel.DEBUG, R.string.msg_ret_curi_found),
+ MSG_RET_CURI_MISMATCH (LogLevel.ERROR, R.string.msg_ret_curi_mismatch),
+ MSG_RET_CURI_OK (LogLevel.OK, R.string.msg_ret_curi_ok),
+ MSG_RET_CURI_OPEN (LogLevel.DEBUG, R.string.msg_ret_curi_open),
+ MSG_RET_CURI_START (LogLevel.START, R.string.msg_ret_curi_start),
+ MSG_RET_KS_ERROR_NOT_FOUND (LogLevel.ERROR, R.string.msg_ret_ks_error_not_found),
+ MSG_RET_KS_ERROR (LogLevel.ERROR, R.string.msg_ret_ks_error),
+ MSG_RET_KS_FP_MATCH (LogLevel.DEBUG, R.string.msg_ret_ks_fp_match),
+ MSG_RET_KS_FP_MISMATCH (LogLevel.ERROR, R.string.msg_ret_ks_fp_mismatch),
+ MSG_RET_KS_OK (LogLevel.OK, R.string.msg_ret_ks_ok),
+ MSG_RET_KS_START (LogLevel.START, R.string.msg_ret_ks_start),
+ MSG_RET_LOCAL_SEARCH(LogLevel.DEBUG, R.string.msg_ret_local_search),
+ MSG_RET_LOCAL_FP_MATCH (LogLevel.DEBUG, R.string.msg_ret_local_fp_match),
+ MSG_RET_LOCAL_FP_MISMATCH (LogLevel.ERROR, R.string.msg_ret_local_fp_mismatch),
+ MSG_RET_LOCAL_NOT_FOUND (LogLevel.DEBUG, R.string.msg_ret_local_not_found),
+ MSG_RET_LOCAL_NONE_FOUND (LogLevel.ERROR, R.string.msg_ret_local_none_found),
+ MSG_RET_LOCAL_OK (LogLevel.OK, R.string.msg_ret_local_ok),
+ MSG_RET_LOCAL_SECRET (LogLevel.INFO, R.string.msg_ret_local_secret),
+ MSG_RET_LOCAL_START (LogLevel.START, R.string.msg_ret_local_start),
+ MSG_RET_URI_ERROR_NO_MATCH(LogLevel.ERROR, R.string.msg_ret_uri_error_no_match),
+ MSG_RET_URI_ERROR_FETCH (LogLevel.ERROR, R.string.msg_ret_uri_error_fetch),
+ MSG_RET_URI_ERROR_PARSE (LogLevel.ERROR, R.string.msg_ret_uri_error_parse),
+ MSG_RET_URI_FETCHING (LogLevel.DEBUG, R.string.msg_ret_uri_fetching),
+ MSG_RET_URI_OK (LogLevel.OK, R.string.msg_ret_uri_ok),
+ MSG_RET_URI_START (LogLevel.START, R.string.msg_ret_uri_start),
+ MSG_RET_URI_NULL (LogLevel.ERROR, R.string.msg_ret_uri_null),
+ MSG_RET_URI_TEST (LogLevel.DEBUG, R.string.msg_ret_uri_test),
+
;
public final int mMsgId;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKeyRing.java
index 93b819af6..05413991f 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKeyRing.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKeyRing.java
@@ -20,7 +20,9 @@ package org.sufficientlysecure.keychain.pgp;
import java.io.IOException;
+import java.util.Arrays;
import java.util.Iterator;
+import java.util.List;
import java.util.NoSuchElementException;
import android.support.annotation.Nullable;
@@ -32,6 +34,7 @@ import org.bouncycastle.openpgp.PGPSecretKey;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator;
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
+import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.IterableIterator;
@@ -153,19 +156,19 @@ public class CanonicalizedPublicKeyRing extends CanonicalizedKeyRing {
}
/** Create a dummy secret ring from this key */
- public UncachedKeyRing createDivertSecretRing (byte[] cardAid, long[] subKeyIds) {
+ public UncachedKeyRing createDivertSecretRing(byte[] cardAid, List subKeyFingerprints) {
PGPSecretKeyRing secRing = PGPSecretKeyRing.constructDummyFromPublic(getRing(), cardAid);
- if (subKeyIds == null) {
+ if (subKeyFingerprints == null) {
return new UncachedKeyRing(secRing);
}
// if only specific subkeys should be promoted, construct a
// stripped dummy, then move divert-to-card keys over
PGPSecretKeyRing newRing = PGPSecretKeyRing.constructDummyFromPublic(getRing());
- for (long subKeyId : subKeyIds) {
- PGPSecretKey key = secRing.getSecretKey(subKeyId);
- if (key != null) {
+ for (byte[] subKeyFingerprint : subKeyFingerprints) {
+ PGPSecretKey key = secRing.getSecretKey(KeyFormattingUtils.convertFingerprintToKeyId(subKeyFingerprint));
+ if (key != null && Arrays.equals(subKeyFingerprint, key.getPublicKey().getFingerprint())) {
newRing = PGPSecretKeyRing.insertSecretKey(newRing, key);
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java
index 850c24640..d535f2338 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java
@@ -19,6 +19,7 @@
package org.sufficientlysecure.keychain.pgp;
+import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -31,6 +32,7 @@ import java.util.Comparator;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.List;
import java.util.Set;
import java.util.TimeZone;
import java.util.TreeSet;
@@ -170,7 +172,24 @@ public class UncachedKeyRing {
}
- public static IteratorWithIOThrow fromStream(final InputStream stream) {
+ public boolean containsKeyWithAnyFingerprint(List expectedFingerprints) {
+ Iterator publicKeys = getPublicKeys();
+
+ while (publicKeys.hasNext()) {
+ UncachedPublicKey publicKey = publicKeys.next();
+
+ for (byte[] expectedFingerprint : expectedFingerprints) {
+ if (Arrays.equals(expectedFingerprint, publicKey.getFingerprint())) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ public static IteratorWithIOThrow fromStream(InputStream rawStream) {
+ final InputStream stream = rawStream.markSupported() ? rawStream: new BufferedInputStream(rawStream);
return new IteratorWithIOThrow() {
@@ -183,9 +202,15 @@ public class UncachedKeyRing {
}
try {
- while (stream.available() > 0) {
+ while (true) {
// if there are no objects left from the last factory, create a new one
if (mObjectFactory == null) {
+ stream.mark(1);
+ if (stream.read() == -1) {
+ break;
+ }
+ stream.reset();
+
InputStream in = PGPUtil.getDecoderStream(stream);
mObjectFactory = new PGPObjectFactory(in, new JcaKeyFingerprintCalculator());
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/CryptoInputParcelCacheService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/CryptoInputParcelCacheService.java
index c573aba9b..2a0b3c1fc 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/CryptoInputParcelCacheService.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/CryptoInputParcelCacheService.java
@@ -76,6 +76,7 @@ public class CryptoInputParcelCacheService extends Service {
data.setExtrasClassLoader(CryptoInputParcelCacheService.class.getClassLoader());
// And write out the UUID most and least significant bits.
+ data.setExtrasClassLoader(CryptoInputParcelCacheService.class.getClassLoader());
data.putExtra(OpenPgpApi.EXTRA_CALL_UUID1, mTicket.getMostSignificantBits());
data.putExtra(OpenPgpApi.EXTRA_CALL_UUID2, mTicket.getLeastSignificantBits());
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenHelper.java
index 5b687f488..9802d182b 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenHelper.java
@@ -217,6 +217,28 @@ public class SecurityTokenHelper {
}
+ public void resetPin(String newPinStr) throws IOException {
+ if (!mPw3Validated) {
+ verifyPin(0x83); // (Verify PW1 with mode 82 for decryption)
+ }
+
+ byte[] newPin = newPinStr.getBytes();
+
+ final int MAX_PW1_LENGTH_INDEX = 1;
+ byte[] pwStatusBytes = getPwStatusBytes();
+ if (newPin.length < 6 || newPin.length > pwStatusBytes[MAX_PW1_LENGTH_INDEX]) {
+ throw new IOException("Invalid PIN length");
+ }
+
+ // Command APDU for RESET RETRY COUNTER command (page 33)
+ CommandAPDU changePin = new CommandAPDU(0x00, 0x2C, 0x02, 0x81, newPin);
+ ResponseAPDU response = communicate(changePin);
+
+ if (response.getSW() != APDU_SW_SUCCESS) {
+ throw new CardException("Failed to change PIN", response.getSW());
+ }
+ }
+
/**
* Modifies the user's PW1 or PW3. Before sending, the new PIN will be validated for
* conformance to the token's requirements for key length.
@@ -587,6 +609,11 @@ public class SecurityTokenHelper {
return getData(0x00, 0x4F);
}
+ public String getUrl() throws IOException {
+ byte[] data = getData(0x5F, 0x50);
+ return new String(data).trim();
+ }
+
public String getUserId() throws IOException {
return getHolderName(getData(0x00, 0x65));
}
@@ -951,6 +978,24 @@ public class SecurityTokenHelper {
return mOpenPgpCapabilities;
}
+ public SecurityTokenInfo getTokenInfo() throws IOException {
+ byte[] rawFingerprints = getFingerprints();
+
+ byte[][] fingerprints = new byte[rawFingerprints.length / 20][];
+ ByteBuffer buf = ByteBuffer.wrap(rawFingerprints);
+ for (int i = 0; i < rawFingerprints.length / 20; i++) {
+ fingerprints[i] = new byte[20];
+ buf.get(fingerprints[i]);
+ }
+
+ byte[] aid = getAid();
+ String userId = getUserId();
+ String url = getUrl();
+ byte[] pwInfo = getPwStatusBytes();
+
+ return SecurityTokenInfo.create(fingerprints, aid, userId, url, pwInfo[4], pwInfo[6]);
+ }
+
private static class LazyHolder {
private static final SecurityTokenHelper SECURITY_TOKEN_HELPER = new SecurityTokenHelper();
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenInfo.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenInfo.java
new file mode 100644
index 000000000..9a2cb6b37
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenInfo.java
@@ -0,0 +1,82 @@
+package org.sufficientlysecure.keychain.securitytoken;
+
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import android.os.Parcelable;
+import android.support.annotation.Nullable;
+
+import com.google.auto.value.AutoValue;
+import org.bouncycastle.util.encoders.Hex;
+import org.sufficientlysecure.keychain.BuildConfig;
+import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
+
+
+@AutoValue
+public abstract class SecurityTokenInfo implements Parcelable {
+ private static final byte[] EMPTY_ARRAY = new byte[20];
+
+ public abstract List getFingerprints();
+ @Nullable
+ public abstract byte[] getAid();
+ @Nullable
+ public abstract String getUserId();
+ @Nullable
+ public abstract String getUrl();
+ public abstract int getVerifyRetries();
+ public abstract int getVerifyAdminRetries();
+
+ public boolean isEmpty() {
+ return getFingerprints().isEmpty();
+ }
+
+ public static SecurityTokenInfo create(byte[][] fingerprints, byte[] aid, String userId, String url,
+ int verifyRetries, int verifyAdminRetries) {
+ ArrayList fingerprintList = new ArrayList<>(fingerprints.length);
+ for (byte[] fingerprint : fingerprints) {
+ if (!Arrays.equals(EMPTY_ARRAY, fingerprint)) {
+ fingerprintList.add(fingerprint);
+ }
+ }
+ return new AutoValue_SecurityTokenInfo(fingerprintList, aid, userId, url, verifyRetries, verifyAdminRetries);
+ }
+
+ public static SecurityTokenInfo newInstanceDebugKeyserver() {
+ if (!BuildConfig.DEBUG) {
+ throw new UnsupportedOperationException("This operation is only available in debug builds!");
+ }
+ return SecurityTokenInfo.create(
+ new byte[][] { KeyFormattingUtils.convertFingerprintHexFingerprint("1efdb4845ca242ca6977fddb1f788094fd3b430a") },
+ Hex.decode("010203040506"), "yubinu2@mugenguild.com", null, 3, 3);
+ }
+
+ public static SecurityTokenInfo newInstanceDebugUri() {
+ if (!BuildConfig.DEBUG) {
+ throw new UnsupportedOperationException("This operation is only available in debug builds!");
+ }
+ return SecurityTokenInfo.create(
+ new byte[][] { KeyFormattingUtils.convertFingerprintHexFingerprint("4700BA1AC417ABEF3CC7765AD686905837779C3E") },
+ Hex.decode("010203040506"), "yubinu2@mugenguild.com", "http://valodim.stratum0.net/mryubinu2.asc", 3, 3);
+ }
+
+ public static SecurityTokenInfo newInstanceDebugLocked() {
+ if (!BuildConfig.DEBUG) {
+ throw new UnsupportedOperationException("This operation is only available in debug builds!");
+ }
+ return SecurityTokenInfo.create(
+ new byte[][] { KeyFormattingUtils.convertFingerprintHexFingerprint("4700BA1AC417ABEF3CC7765AD686905837779C3E") },
+ Hex.decode("010203040506"), "yubinu2@mugenguild.com", "http://valodim.stratum0.net/mryubinu2.asc", 0, 3);
+ }
+
+ public static SecurityTokenInfo newInstanceDebugLockedHard() {
+ if (!BuildConfig.DEBUG) {
+ throw new UnsupportedOperationException("This operation is only available in debug builds!");
+ }
+ return SecurityTokenInfo.create(
+ new byte[][] { KeyFormattingUtils.convertFingerprintHexFingerprint("4700BA1AC417ABEF3CC7765AD686905837779C3E") },
+ Hex.decode("010203040506"), "yubinu2@mugenguild.com", "http://valodim.stratum0.net/mryubinu2.asc", 0, 0);
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ImportKeyringParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ImportKeyringParcel.java
index e7a0e2d51..7cf0bc2c7 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ImportKeyringParcel.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ImportKeyringParcel.java
@@ -19,7 +19,6 @@
package org.sufficientlysecure.keychain.service;
-import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PromoteKeyringParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PromoteKeyringParcel.java
index c62815c7b..efa835531 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PromoteKeyringParcel.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PromoteKeyringParcel.java
@@ -20,6 +20,8 @@
package org.sufficientlysecure.keychain.service;
+import java.util.List;
+
import android.os.Parcelable;
import android.support.annotation.Nullable;
@@ -34,10 +36,10 @@ public abstract class PromoteKeyringParcel implements Parcelable {
public abstract byte[] getCardAid();
@Nullable
@SuppressWarnings("mutable")
- public abstract long[] getSubKeyIds();
+ public abstract List getFingerprints();
public static PromoteKeyringParcel createPromoteKeyringParcel(long keyRingId, byte[] cardAid,
- @Nullable long[] subKeyIds) {
- return new AutoValue_PromoteKeyringParcel(keyRingId, cardAid, subKeyIds);
+ @Nullable List fingerprints) {
+ return new AutoValue_PromoteKeyringParcel(keyRingId, cardAid, fingerprints);
}
}
\ No newline at end of file
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/RequiredInputParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/RequiredInputParcel.java
index 84c139d0b..4a0fe8069 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/RequiredInputParcel.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/RequiredInputParcel.java
@@ -1,21 +1,22 @@
package org.sufficientlysecure.keychain.service.input;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import org.sufficientlysecure.keychain.util.Passphrase;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import org.sufficientlysecure.keychain.util.Passphrase;
+
public class RequiredInputParcel implements Parcelable {
public enum RequiredInputType {
PASSPHRASE, PASSPHRASE_SYMMETRIC, BACKUP_CODE, SECURITY_TOKEN_SIGN, SECURITY_TOKEN_DECRYPT,
- SECURITY_TOKEN_MOVE_KEY_TO_CARD, SECURITY_TOKEN_RESET_CARD, ENABLE_ORBOT, UPLOAD_FAIL_RETRY,
+ SECURITY_TOKEN_MOVE_KEY_TO_CARD, SECURITY_TOKEN_RESET_CARD, ENABLE_ORBOT, UPLOAD_FAIL_RETRY
}
public Date mSignatureTime;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/SecurityTokenChangePinParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/SecurityTokenChangePinParcel.java
new file mode 100644
index 000000000..2d9bf7c85
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/SecurityTokenChangePinParcel.java
@@ -0,0 +1,18 @@
+package org.sufficientlysecure.keychain.service.input;
+
+
+import android.os.Parcelable;
+
+import com.google.auto.value.AutoValue;
+
+
+@AutoValue
+public abstract class SecurityTokenChangePinParcel implements Parcelable {
+ public abstract String getAdminPin();
+ public abstract String getNewPin();
+
+ public static SecurityTokenChangePinParcel createSecurityTokenUnlock(String adminPin, String newPin) {
+ return new AutoValue_SecurityTokenChangePinParcel(adminPin, newPin);
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java
index e64d846f6..35b5816b0 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java
@@ -31,14 +31,10 @@ import android.support.v4.app.FragmentTransaction;
import android.support.v4.app.TaskStackBuilder;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
-import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
-import org.sufficientlysecure.keychain.provider.KeyRepository;
-import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.securitytoken.KeyFormat;
+import org.sufficientlysecure.keychain.securitytoken.SecurityTokenInfo;
import org.sufficientlysecure.keychain.ui.base.BaseSecurityTokenActivity;
-import org.sufficientlysecure.keychain.ui.keyview.ViewKeyActivity;
-import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
+import org.sufficientlysecure.keychain.ui.token.ManageSecurityTokenFragment;
import org.sufficientlysecure.keychain.util.Passphrase;
import org.sufficientlysecure.keychain.util.Preferences;
@@ -53,9 +49,7 @@ public class CreateKeyActivity extends BaseSecurityTokenActivity {
public static final String EXTRA_SECURITY_TOKEN_PIN = "yubi_key_pin";
public static final String EXTRA_SECURITY_TOKEN_ADMIN_PIN = "yubi_key_admin_pin";
- public static final String EXTRA_SECURITY_TOKEN_USER_ID = "nfc_user_id";
- public static final String EXTRA_SECURITY_TOKEN_AID = "nfc_aid";
- public static final String EXTRA_SECURITY_FINGERPRINTS = "nfc_fingerprints";
+ public static final String EXTRA_SECURITY_TOKEN_INFO = "token_info";
public static final String FRAGMENT_TAG = "currentFragment";
@@ -73,10 +67,7 @@ public class CreateKeyActivity extends BaseSecurityTokenActivity {
Fragment mCurrentFragment;
-
- byte[] mScannedFingerprints;
- byte[] mSecurityTokenAid;
- String mSecurityTokenUserId;
+ SecurityTokenInfo tokenInfo;
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -103,7 +94,6 @@ public class CreateKeyActivity extends BaseSecurityTokenActivity {
mPassphrase = savedInstanceState.getParcelable(EXTRA_PASSPHRASE);
mFirstTime = savedInstanceState.getBoolean(EXTRA_FIRST_TIME);
mCreateSecurityToken = savedInstanceState.getBoolean(EXTRA_CREATE_SECURITY_TOKEN);
- mSecurityTokenAid = savedInstanceState.getByteArray(EXTRA_SECURITY_TOKEN_AID);
mSecurityTokenPin = savedInstanceState.getParcelable(EXTRA_SECURITY_TOKEN_PIN);
mSecurityTokenAdminPin = savedInstanceState.getParcelable(EXTRA_SECURITY_TOKEN_ADMIN_PIN);
@@ -117,22 +107,12 @@ public class CreateKeyActivity extends BaseSecurityTokenActivity {
mFirstTime = intent.getBooleanExtra(EXTRA_FIRST_TIME, false);
mCreateSecurityToken = intent.getBooleanExtra(EXTRA_CREATE_SECURITY_TOKEN, false);
- if (intent.hasExtra(EXTRA_SECURITY_FINGERPRINTS)) {
- byte[] nfcFingerprints = intent.getByteArrayExtra(EXTRA_SECURITY_FINGERPRINTS);
- String nfcUserId = intent.getStringExtra(EXTRA_SECURITY_TOKEN_USER_ID);
- byte[] nfcAid = intent.getByteArrayExtra(EXTRA_SECURITY_TOKEN_AID);
+ if (intent.hasExtra(EXTRA_SECURITY_TOKEN_INFO)) {
+ SecurityTokenInfo tokenInfo = intent.getParcelableExtra(EXTRA_SECURITY_TOKEN_INFO);
- if (containsKeys(nfcFingerprints)) {
- Fragment frag = CreateSecurityTokenImportResetFragment.newInstance(
- nfcFingerprints, nfcAid, nfcUserId);
- loadFragment(frag, FragAction.START);
-
- setTitle(R.string.title_import_keys);
- } else {
- Fragment frag = CreateSecurityTokenBlankFragment.newInstance(nfcAid);
- loadFragment(frag, FragAction.START);
- setTitle(R.string.title_manage_my_keys);
- }
+ Fragment frag = ManageSecurityTokenFragment.newInstance(tokenInfo);
+ loadFragment(frag, FragAction.START);
+ setTitle(R.string.title_manage_my_keys);
// done
return;
@@ -159,9 +139,7 @@ public class CreateKeyActivity extends BaseSecurityTokenActivity {
return;
}
- mScannedFingerprints = mSecurityTokenHelper.getFingerprints();
- mSecurityTokenAid = mSecurityTokenHelper.getAid();
- mSecurityTokenUserId = mSecurityTokenHelper.getUserId();
+ tokenInfo = mSecurityTokenHelper.getTokenInfo();
}
@Override
@@ -179,45 +157,14 @@ public class CreateKeyActivity extends BaseSecurityTokenActivity {
CreateSecurityTokenWaitFragment.sDisableFragmentAnimations = false;
}
- if (containsKeys(mScannedFingerprints)) {
- try {
- long masterKeyId = KeyFormattingUtils.getKeyIdFromFingerprint(mScannedFingerprints);
- CachedPublicKeyRing ring = KeyRepository.createDatabaseInteractor(this).getCachedPublicKeyRing(masterKeyId);
- ring.getMasterKeyId();
-
- Intent intent = new Intent(this, ViewKeyActivity.class);
- intent.setData(KeyRings.buildGenericKeyRingUri(masterKeyId));
- intent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_AID, mSecurityTokenAid);
- intent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_USER_ID, mSecurityTokenUserId);
- intent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_FINGERPRINTS, mScannedFingerprints);
- startActivity(intent);
- finish();
-
- } catch (PgpKeyNotFoundException e) {
- Fragment frag = CreateSecurityTokenImportResetFragment.newInstance(
- mScannedFingerprints, mSecurityTokenAid, mSecurityTokenUserId);
- loadFragment(frag, FragAction.TO_RIGHT);
- }
+ Fragment frag = ManageSecurityTokenFragment.newInstance(tokenInfo);
+ if (mCurrentFragment instanceof ManageSecurityTokenFragment) {
+ loadFragment(frag, FragAction.REPLACE);
} else {
- Fragment frag = CreateSecurityTokenBlankFragment.newInstance(mSecurityTokenAid);
loadFragment(frag, FragAction.TO_RIGHT);
}
}
- private boolean containsKeys(byte[] scannedFingerprints) {
- if (scannedFingerprints == null) {
- return false;
- }
-
- // If all fingerprint bytes are 0, the card contains no keys.
- for (byte b : scannedFingerprints) {
- if (b != 0) {
- return true;
- }
- }
- return false;
- }
-
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
@@ -228,7 +175,6 @@ public class CreateKeyActivity extends BaseSecurityTokenActivity {
outState.putParcelable(EXTRA_PASSPHRASE, mPassphrase);
outState.putBoolean(EXTRA_FIRST_TIME, mFirstTime);
outState.putBoolean(EXTRA_CREATE_SECURITY_TOKEN, mCreateSecurityToken);
- outState.putByteArray(EXTRA_SECURITY_TOKEN_AID, mSecurityTokenAid);
outState.putParcelable(EXTRA_SECURITY_TOKEN_PIN, mSecurityTokenPin);
outState.putParcelable(EXTRA_SECURITY_TOKEN_ADMIN_PIN, mSecurityTokenAdminPin);
}
@@ -238,10 +184,19 @@ public class CreateKeyActivity extends BaseSecurityTokenActivity {
setContentView(R.layout.create_key_activity);
}
+ public void startCreateKeyForSecurityToken(SecurityTokenInfo tokenInfo) {
+ mCreateSecurityToken = true;
+ this.tokenInfo = tokenInfo;
+
+ CreateKeyNameFragment frag = CreateKeyNameFragment.newInstance();
+ loadFragment(frag, FragAction.TO_RIGHT);
+ }
+
public enum FragAction {
START,
TO_RIGHT,
- TO_LEFT
+ TO_LEFT,
+ REPLACE
}
public void loadFragment(Fragment fragment, FragAction action) {
@@ -259,6 +214,10 @@ public class CreateKeyActivity extends BaseSecurityTokenActivity {
case TO_LEFT:
getSupportFragmentManager().popBackStackImmediate();
break;
+ case REPLACE:
+ transaction.replace(R.id.create_key_fragment_container, fragment, FRAGMENT_TAG)
+ .commit();
+ break;
case TO_RIGHT:
transaction.setCustomAnimations(R.anim.frag_slide_in_from_right, R.anim.frag_slide_out_to_left,
R.anim.frag_slide_in_from_left, R.anim.frag_slide_out_to_right);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateSecurityTokenAlgorithmFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateSecurityTokenAlgorithmFragment.java
index 1079229b4..0c3985eff 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateSecurityTokenAlgorithmFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateSecurityTokenAlgorithmFragment.java
@@ -100,7 +100,7 @@ public class CreateSecurityTokenAlgorithmFragment extends Fragment {
choices.add(new Choice<>(SupportedKeyType.RSA_4096, getResources().getString(
R.string.rsa_4096), getResources().getString(R.string.rsa_4096_description_html)));
- final double version = SecurityTokenHelper.parseOpenPgpVersion(mCreateKeyActivity.mSecurityTokenAid);
+ final double version = SecurityTokenHelper.parseOpenPgpVersion(mCreateKeyActivity.tokenInfo.getAid());
if (version >= 3.0) {
choices.add(new Choice<>(SupportedKeyType.ECC_P256, getResources().getString(
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateSecurityTokenBlankFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateSecurityTokenBlankFragment.java
deleted file mode 100644
index 61a069e9d..000000000
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateSecurityTokenBlankFragment.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (C) 2014 Dominik Schürmann
- *
- * 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 .
- */
-
-package org.sufficientlysecure.keychain.ui;
-
-import android.app.Activity;
-import android.content.Context;
-import android.os.Bundle;
-import android.support.v4.app.Fragment;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction;
-
-public class CreateSecurityTokenBlankFragment extends Fragment {
-
- CreateKeyActivity mCreateKeyActivity;
- View mBackButton;
- View mNextButton;
-
- private byte[] mAid;
-
- /**
- * Creates new instance of this fragment
- */
- public static CreateSecurityTokenBlankFragment newInstance(byte[] aid) {
- CreateSecurityTokenBlankFragment frag = new CreateSecurityTokenBlankFragment();
-
- Bundle args = new Bundle();
-
- frag.mAid = aid;
- frag.setArguments(args);
-
- return frag;
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- View view = inflater.inflate(R.layout.create_yubi_key_blank_fragment, container, false);
-
- mBackButton = view.findViewById(R.id.create_key_back_button);
- mNextButton = view.findViewById(R.id.create_key_next_button);
-
- mBackButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- if (getFragmentManager().getBackStackEntryCount() == 0) {
- getActivity().setResult(Activity.RESULT_CANCELED);
- getActivity().finish();
- } else {
- mCreateKeyActivity.loadFragment(null, FragAction.TO_LEFT);
- }
- }
- });
- mNextButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- nextClicked();
- }
- });
-
- return view;
- }
-
- @Override
- public void onAttach(Context context) {
- super.onAttach(context);
- mCreateKeyActivity = (CreateKeyActivity) getActivity();
- }
-
- private void nextClicked() {
- mCreateKeyActivity.mCreateSecurityToken = true;
- mCreateKeyActivity.mSecurityTokenAid = mAid;
-
- CreateKeyNameFragment frag = CreateKeyNameFragment.newInstance();
- mCreateKeyActivity.loadFragment(frag, FragAction.TO_RIGHT);
- }
-
-}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateSecurityTokenImportResetFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateSecurityTokenImportResetFragment.java
deleted file mode 100644
index 41cf0786c..000000000
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateSecurityTokenImportResetFragment.java
+++ /dev/null
@@ -1,305 +0,0 @@
-/*
- * Copyright (C) 2014 Dominik Schürmann
- *
- * 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 .
- */
-
-package org.sufficientlysecure.keychain.ui;
-
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.support.v4.app.Fragment;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.CompoundButton;
-import android.widget.RadioButton;
-import android.widget.TextView;
-
-import org.bouncycastle.util.encoders.Hex;
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
-import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
-import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
-import org.sufficientlysecure.keychain.securitytoken.SecurityTokenHelper;
-import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
-import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
-import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
-import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction;
-import org.sufficientlysecure.keychain.ui.CreateKeyActivity.SecurityTokenListenerFragment;
-import org.sufficientlysecure.keychain.ui.base.QueueingCryptoOperationFragment;
-import org.sufficientlysecure.keychain.ui.keyview.ViewKeyActivity;
-import org.sufficientlysecure.keychain.keyimport.HkpKeyserverAddress;
-import org.sufficientlysecure.keychain.util.Preferences;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.util.ArrayList;
-
-
-public class CreateSecurityTokenImportResetFragment
- extends QueueingCryptoOperationFragment
- implements SecurityTokenListenerFragment {
-
- private static final int REQUEST_CODE_RESET = 0x00005001;
-
- private static final String ARG_FINGERPRINTS = "fingerprint";
- public static final String ARG_AID = "aid";
- public static final String ARG_USER_ID = "user_ids";
-
- CreateKeyActivity mCreateKeyActivity;
-
- private byte[] mTokenFingerprints;
- private byte[] mTokenAid;
- private double mTokenVersion;
- private String mTokenUserId;
- private byte[] mTokenFingerprint;
- private TextView vSerNo;
- private TextView vUserId;
- private TextView mNextButton;
- private RadioButton mRadioImport;
- private RadioButton mRadioFile;
- private RadioButton mRadioReset;
- private View mResetWarning;
-
- // for CryptoOperationFragment key import
- private HkpKeyserverAddress mKeyserver;
- private ArrayList mKeyList;
-
- public static Fragment newInstance(byte[] scannedFingerprints, byte[] nfcAid, String userId) {
-
- CreateSecurityTokenImportResetFragment frag = new CreateSecurityTokenImportResetFragment();
-
- Bundle args = new Bundle();
- args.putByteArray(ARG_FINGERPRINTS, scannedFingerprints);
- args.putByteArray(ARG_AID, nfcAid);
- args.putString(ARG_USER_ID, userId);
- frag.setArguments(args);
-
- return frag;
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- Bundle args = savedInstanceState != null ? savedInstanceState : getArguments();
-
- mTokenFingerprints = args.getByteArray(ARG_FINGERPRINTS);
- mTokenAid = args.getByteArray(ARG_AID);
- mTokenUserId = args.getString(ARG_USER_ID);
-
- byte[] fp = new byte[20];
- ByteBuffer.wrap(fp).put(mTokenFingerprints, 0, 20);
- mTokenFingerprint = fp;
-
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- View view = inflater.inflate(R.layout.create_security_token_import_reset_fragment, container, false);
-
- vSerNo = (TextView) view.findViewById(R.id.token_serno);
- vUserId = (TextView) view.findViewById(R.id.token_userid);
- mNextButton = (TextView) view.findViewById(R.id.create_key_next_button);
- mRadioImport = (RadioButton) view.findViewById(R.id.token_decision_import);
- mRadioFile = (RadioButton) view.findViewById(R.id.token_decision_file);
- mRadioReset = (RadioButton) view.findViewById(R.id.token_decision_reset);
- mResetWarning = view.findViewById(R.id.token_import_reset_warning);
-
- View mBackButton = view.findViewById(R.id.create_key_back_button);
- mBackButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- if (getFragmentManager().getBackStackEntryCount() == 0) {
- getActivity().setResult(Activity.RESULT_CANCELED);
- getActivity().finish();
- } else {
- mCreateKeyActivity.loadFragment(null, FragAction.TO_LEFT);
- }
- }
- });
-
- mNextButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- if (mRadioReset.isChecked()) {
- resetCard();
- } else if (mRadioImport.isChecked()){
- importKey();
- } else {
- importFile();
- }
- }
- });
-
- mRadioImport.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
- @Override
- public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
- if (isChecked) {
- mNextButton.setText(R.string.btn_import);
- mNextButton.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.ic_key_plus_grey600_24dp, 0);
- mNextButton.setVisibility(View.VISIBLE);
- mResetWarning.setVisibility(View.GONE);
- }
- }
- });
- mRadioFile.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
- @Override
- public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
- if (isChecked) {
- mNextButton.setText(R.string.key_list_fab_import);
- mNextButton.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.ic_folder_grey_24dp, 0);
- mNextButton.setVisibility(View.VISIBLE);
- mResetWarning.setVisibility(View.GONE);
- }
- }
- });
- mRadioReset.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
- @Override
- public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
- if (isChecked) {
- mNextButton.setText(R.string.btn_reset);
- mNextButton.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.ic_close_grey_24dp, 0);
- mNextButton.setVisibility(View.VISIBLE);
- mResetWarning.setVisibility(View.VISIBLE);
- }
- }
- });
-
- setData();
-
-
- return view;
- }
-
- @Override
- public void onSaveInstanceState(Bundle args) {
- super.onSaveInstanceState(args);
-
- args.putByteArray(ARG_FINGERPRINTS, mTokenFingerprints);
- args.putByteArray(ARG_AID, mTokenAid);
- args.putString(ARG_USER_ID, mTokenUserId);
- }
-
- @Override
- public void onAttach(Context context) {
- super.onAttach(context);
- mCreateKeyActivity = (CreateKeyActivity) getActivity();
- }
-
- public void setData() {
- String serno = Hex.toHexString(mTokenAid, 10, 4);
- vSerNo.setText(getString(R.string.security_token_serial_no, serno));
-
- if (!mTokenUserId.isEmpty()) {
- vUserId.setText(getString(R.string.security_token_key_holder, mTokenUserId));
- } else {
- vUserId.setText(getString(R.string.security_token_key_holder_not_set));
- }
- }
-
- public void importKey() {
- ArrayList keyList = new ArrayList<>();
- keyList.add(ParcelableKeyRing.createFromReference(mTokenFingerprint, null, null, null));
- mKeyList = keyList;
-
- mKeyserver = Preferences.getPreferences(getActivity()).getPreferredKeyserver();
-
- super.setProgressMessageResource(R.string.progress_importing);
-
- super.cryptoOperation();
- }
-
- public void importFile() {
- Intent intent = new Intent(getActivity(), ImportKeysActivity.class);
- intent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_FILE);
- startActivity(intent);
- }
-
- public void resetCard() {
- Intent intent = new Intent(getActivity(), SecurityTokenOperationActivity.class);
- RequiredInputParcel resetP = RequiredInputParcel.createSecurityTokenReset();
- intent.putExtra(SecurityTokenOperationActivity.EXTRA_REQUIRED_INPUT, resetP);
- intent.putExtra(SecurityTokenOperationActivity.EXTRA_CRYPTO_INPUT, CryptoInputParcel.createCryptoInputParcel());
- startActivityForResult(intent, REQUEST_CODE_RESET);
- }
-
- @Override
- public void onActivityResult(int requestCode, int resultCode, Intent data) {
- if (requestCode == REQUEST_CODE_RESET && resultCode == Activity.RESULT_OK) {
- mCreateKeyActivity.loadFragment(null, FragAction.TO_LEFT);
- return;
- }
-
- super.onActivityResult(requestCode, resultCode, data);
- }
-
- @Override
- public void doSecurityTokenInBackground() throws IOException {
-
- mTokenFingerprints = mCreateKeyActivity.getSecurityTokenHelper().getFingerprints();
- mTokenAid = mCreateKeyActivity.getSecurityTokenHelper().getAid();
- mTokenVersion = SecurityTokenHelper.parseOpenPgpVersion(mTokenAid);
- mTokenUserId = mCreateKeyActivity.getSecurityTokenHelper().getUserId();
-
- byte[] fp = new byte[20];
- ByteBuffer.wrap(fp).put(mTokenFingerprints, 0, 20);
- mTokenFingerprint = fp;
- }
-
- @Override
- public void onSecurityTokenPostExecute() {
-
- setData();
-
- }
-
- @Override
- public ImportKeyringParcel createOperationInput() {
- return ImportKeyringParcel.createImportKeyringParcel(mKeyList, mKeyserver);
- }
-
- @Override
- public void onQueuedOperationSuccess(ImportKeyResult result) {
- long[] masterKeyIds = result.getImportedMasterKeyIds();
- if (masterKeyIds.length == 0) {
- super.onCryptoOperationError(result);
- return;
- }
-
- // null-protected from Queueing*Fragment
- Activity activity = getActivity();
-
- Intent viewKeyIntent = new Intent(activity, ViewKeyActivity.class);
- // use the imported masterKeyId, not the one from the token, because
- // that one might* just have been a subkey of the imported key
- viewKeyIntent.setData(KeyRings.buildGenericKeyRingUri(masterKeyIds[0]));
- viewKeyIntent.putExtra(ViewKeyActivity.EXTRA_DISPLAY_RESULT, result);
- viewKeyIntent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_AID, mTokenAid);
- viewKeyIntent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_VERSION, mTokenVersion);
- viewKeyIntent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_USER_ID, mTokenUserId);
- viewKeyIntent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_FINGERPRINTS, mTokenFingerprints);
-
- if (activity instanceof CreateKeyActivity) {
- ((CreateKeyActivity) activity).finishWithFirstTimeHandling(viewKeyIntent);
- } else {
- activity.startActivity(viewKeyIntent);
- activity.finish();
- }
- }
-}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateSecurityTokenPinFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateSecurityTokenPinFragment.java
index f100ee8bc..4e4e955b3 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateSecurityTokenPinFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateSecurityTokenPinFragment.java
@@ -201,7 +201,7 @@ public class CreateSecurityTokenPinFragment extends Fragment {
mCreateKeyActivity.mSecurityTokenPin = new Passphrase(mPin.getText().toString());
- final double version = SecurityTokenHelper.parseOpenPgpVersion(mCreateKeyActivity.mSecurityTokenAid);
+ final double version = SecurityTokenHelper.parseOpenPgpVersion(mCreateKeyActivity.tokenInfo.getAid());
Fragment frag;
if (version >= 3.0) {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateSecurityTokenWaitFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateSecurityTokenWaitFragment.java
index 782502741..022dc0f2b 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateSecurityTokenWaitFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateSecurityTokenWaitFragment.java
@@ -17,18 +17,26 @@
package org.sufficientlysecure.keychain.ui;
+
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
+import org.sufficientlysecure.keychain.BuildConfig;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.securitytoken.SecurityTokenInfo;
import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction;
import org.sufficientlysecure.keychain.ui.base.BaseSecurityTokenActivity;
+import org.sufficientlysecure.keychain.ui.token.ManageSecurityTokenFragment;
+
public class CreateSecurityTokenWaitFragment extends Fragment {
@@ -44,6 +52,36 @@ public class CreateSecurityTokenWaitFragment extends Fragment {
if (this.getActivity() instanceof BaseSecurityTokenActivity) {
((BaseSecurityTokenActivity) this.getActivity()).checkDeviceConnection();
}
+
+ setHasOptionsMenu(BuildConfig.DEBUG);
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ inflater.inflate(R.menu.token_debug, menu);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.menu_token_debug_uri:
+ mCreateKeyActivity.loadFragment(ManageSecurityTokenFragment.newInstance(
+ SecurityTokenInfo.newInstanceDebugUri()), FragAction.TO_RIGHT);
+ break;
+ case R.id.menu_token_debug_keyserver:
+ mCreateKeyActivity.loadFragment(ManageSecurityTokenFragment.newInstance(
+ SecurityTokenInfo.newInstanceDebugKeyserver()), FragAction.TO_RIGHT);
+ break;
+ case R.id.menu_token_debug_locked:
+ mCreateKeyActivity.loadFragment(ManageSecurityTokenFragment.newInstance(
+ SecurityTokenInfo.newInstanceDebugLocked()), FragAction.TO_RIGHT);
+ break;
+ case R.id.menu_token_debug_locked_hard:
+ mCreateKeyActivity.loadFragment(ManageSecurityTokenFragment.newInstance(
+ SecurityTokenInfo.newInstanceDebugLockedHard()), FragAction.TO_RIGHT);
+ break;
+ }
+ return super.onOptionsItemSelected(item);
}
@Override
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SecurityTokenChangePinOperationActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SecurityTokenChangePinOperationActivity.java
new file mode 100644
index 000000000..c38f667de
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SecurityTokenChangePinOperationActivity.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2017 Vincent Breitmoser
+ *
+ * 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 .
+ */
+
+package org.sufficientlysecure.keychain.ui;
+
+
+import java.io.IOException;
+
+import android.content.Intent;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.Button;
+import android.widget.TextView;
+import android.widget.ViewAnimator;
+
+import nordpol.android.NfcGuideView;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.securitytoken.SecurityTokenInfo;
+import org.sufficientlysecure.keychain.service.input.SecurityTokenChangePinParcel;
+import org.sufficientlysecure.keychain.ui.base.BaseSecurityTokenActivity;
+import org.sufficientlysecure.keychain.ui.util.ThemeChanger;
+import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.OrientationUtils;
+import org.sufficientlysecure.keychain.util.Passphrase;
+
+
+/**
+ * This class provides a communication interface to OpenPGP applications on ISO SmartCard compliant
+ * NFC devices.
+ * For the full specs, see http://g10code.com/docs/openpgp-card-2.0.pdf
+ */
+public class SecurityTokenChangePinOperationActivity extends BaseSecurityTokenActivity {
+ public static final String EXTRA_CHANGE_PIN_PARCEL = "change_pin_parcel";
+
+ public static final String RESULT_TOKEN_INFO = "token_info";
+
+ public ViewAnimator vAnimator;
+ public TextView vErrorText;
+ private TextView vErrorTextPin;
+ public Button vErrorTryAgainButton;
+ public NfcGuideView nfcGuideView;
+
+ private SecurityTokenChangePinParcel changePinInput;
+
+ private SecurityTokenInfo resultTokenInfo;
+
+ @Override
+ protected void initTheme() {
+ mThemeChanger = new ThemeChanger(this);
+ mThemeChanger.setThemes(R.style.Theme_Keychain_Light_Dialog,
+ R.style.Theme_Keychain_Dark_Dialog);
+ mThemeChanger.changeTheme();
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Log.d(Constants.TAG, "NfcOperationActivity.onCreate");
+
+ nfcGuideView = (NfcGuideView) findViewById(R.id.nfc_guide_view);
+
+ // prevent annoying orientation changes while fumbling with the device
+ OrientationUtils.lockOrientation(this);
+ // prevent close when touching outside of the dialog (happens easily when fumbling with the device)
+ setFinishOnTouchOutside(false);
+ // keep screen on
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+
+ setTitle(R.string.security_token_nfc_text);
+
+ vAnimator = (ViewAnimator) findViewById(R.id.view_animator);
+ vAnimator.setDisplayedChild(0);
+
+ nfcGuideView.setCurrentStatus(NfcGuideView.NfcGuideViewStatus.STARTING_POSITION);
+
+ vErrorText = (TextView) findViewById(R.id.security_token_activity_3_error_text);
+ vErrorTextPin = (TextView) findViewById(R.id.security_token_activity_4_error_text);
+ vErrorTryAgainButton = (Button) findViewById(R.id.security_token_activity_3_error_try_again);
+ vErrorTryAgainButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ resumeTagHandling();
+
+ vAnimator.setDisplayedChild(0);
+
+ nfcGuideView.setVisibility(View.VISIBLE);
+ nfcGuideView.setCurrentStatus(NfcGuideView.NfcGuideViewStatus.STARTING_POSITION);
+ }
+ });
+ Button vCancel = (Button) findViewById(R.id.security_token_activity_0_cancel);
+ vCancel.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ setResult(RESULT_CANCELED);
+ finish();
+ }
+ });
+ findViewById(R.id.security_token_activity_4_back).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent result = new Intent();
+ result.putExtra(RESULT_TOKEN_INFO, resultTokenInfo);
+ setResult(RESULT_CANCELED, result);
+ finish();
+ }
+ });
+
+ changePinInput = getIntent().getParcelableExtra(EXTRA_CHANGE_PIN_PARCEL);
+ }
+
+ @Override
+ protected void initLayout() {
+ setContentView(R.layout.security_token_operation_activity);
+ }
+
+ @Override
+ public void onSecurityTokenPreExecute() {
+ // start with indeterminate progress
+ vAnimator.setDisplayedChild(1);
+ nfcGuideView.setCurrentStatus(NfcGuideView.NfcGuideViewStatus.TRANSFERRING);
+ }
+
+ @Override
+ protected void doSecurityTokenInBackground() throws IOException {
+ mSecurityTokenHelper.setAdminPin(new Passphrase(changePinInput.getAdminPin()));
+ mSecurityTokenHelper.resetPin(changePinInput.getNewPin());
+
+ resultTokenInfo = mSecurityTokenHelper.getTokenInfo();
+ }
+
+ @Override
+ protected final void onSecurityTokenPostExecute() {
+ Intent result = new Intent();
+ result.putExtra(RESULT_TOKEN_INFO, resultTokenInfo);
+ setResult(RESULT_OK, result);
+
+ // show finish
+ vAnimator.setDisplayedChild(2);
+
+ nfcGuideView.setCurrentStatus(NfcGuideView.NfcGuideViewStatus.DONE);
+
+ if (mSecurityTokenHelper.isPersistentConnectionAllowed()) {
+ // Just close
+ finish();
+ } else {
+ mSecurityTokenHelper.clearSecureMessaging();
+ new AsyncTask() {
+ @Override
+ protected Void doInBackground(Void... params) {
+ // check all 200ms if Security Token has been taken away
+ while (true) {
+ if (isSecurityTokenConnected()) {
+ try {
+ Thread.sleep(200);
+ } catch (InterruptedException ignored) {
+ }
+ } else {
+ return null;
+ }
+ }
+ }
+
+ @Override
+ protected void onPostExecute(Void result) {
+ super.onPostExecute(result);
+ finish();
+ }
+ }.execute();
+ }
+ }
+
+ @Override
+ protected void onSecurityTokenError(String error) {
+ pauseTagHandling();
+
+ vErrorText.setText(error + "\n\n" + getString(R.string.security_token_nfc_try_again_text));
+ vAnimator.setDisplayedChild(3);
+
+ nfcGuideView.setVisibility(View.GONE);
+ }
+
+ @Override
+ public void onSecurityTokenPinError(String error, SecurityTokenInfo tokeninfo) {
+ resultTokenInfo = tokeninfo;
+
+ pauseTagHandling();
+
+ vErrorTextPin.setText(error);
+ vAnimator.setDisplayedChild(4);
+
+ nfcGuideView.setVisibility(View.GONE);
+ }
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SecurityTokenOperationActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SecurityTokenOperationActivity.java
index ceea84ee8..09dfa51cc 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SecurityTokenOperationActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SecurityTokenOperationActivity.java
@@ -44,6 +44,7 @@ import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing;
import org.sufficientlysecure.keychain.provider.KeyRepository;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.securitytoken.KeyType;
+import org.sufficientlysecure.keychain.securitytoken.SecurityTokenInfo;
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
@@ -65,6 +66,7 @@ public class SecurityTokenOperationActivity extends BaseSecurityTokenActivity {
public static final String EXTRA_CRYPTO_INPUT = "crypto_input";
public static final String RESULT_CRYPTO_INPUT = "result_data";
+ public static final String RESULT_TOKEN_INFO = "token_info";
public ViewAnimator vAnimator;
public TextView vErrorText;
@@ -74,6 +76,7 @@ public class SecurityTokenOperationActivity extends BaseSecurityTokenActivity {
private RequiredInputParcel mRequiredInput;
private CryptoInputParcel mInputParcel;
+ private SecurityTokenInfo mResultTokenInfo;
@Override
protected void initTheme() {
@@ -277,6 +280,7 @@ public class SecurityTokenOperationActivity extends BaseSecurityTokenActivity {
}
case SECURITY_TOKEN_RESET_CARD: {
mSecurityTokenHelper.resetAndWipeToken();
+ mResultTokenInfo = mSecurityTokenHelper.getTokenInfo();
break;
}
@@ -334,6 +338,9 @@ public class SecurityTokenOperationActivity extends BaseSecurityTokenActivity {
Intent result = new Intent();
// send back the CryptoInputParcel we received
result.putExtra(RESULT_CRYPTO_INPUT, inputParcel);
+ if (mResultTokenInfo != null) {
+ result.putExtra(RESULT_TOKEN_INFO, mResultTokenInfo);
+ }
setResult(RESULT_OK, result);
}
@@ -348,7 +355,7 @@ public class SecurityTokenOperationActivity extends BaseSecurityTokenActivity {
}
@Override
- public void onSecurityTokenPinError(String error) {
+ public void onSecurityTokenPinError(String error, SecurityTokenInfo tokeninfo) {
onSecurityTokenError(error);
// clear (invalid) passphrase
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeySecurityTokenFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeySecurityTokenFragment.java
deleted file mode 100644
index bbea3973b..000000000
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeySecurityTokenFragment.java
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * Copyright (C) 2015 Dominik Schürmann
- * Copyright (C) 2015 Vincent Breitmoser
- *
- * 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 .
- */
-
-package org.sufficientlysecure.keychain.ui;
-
-import java.nio.ByteBuffer;
-import java.util.Arrays;
-
-import android.database.Cursor;
-import android.os.Bundle;
-import android.support.v4.app.LoaderManager.LoaderCallbacks;
-import android.support.v4.content.CursorLoader;
-import android.support.v4.content.Loader;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.widget.Button;
-import android.widget.TextView;
-
-import org.bouncycastle.util.encoders.Hex;
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.operations.results.PromoteKeyResult;
-import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType;
-import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
-import org.sufficientlysecure.keychain.service.PromoteKeyringParcel;
-import org.sufficientlysecure.keychain.ui.base.QueueingCryptoOperationFragment;
-import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
-
-public class ViewKeySecurityTokenFragment
- extends QueueingCryptoOperationFragment
- implements LoaderCallbacks {
-
- public static final String ARG_MASTER_KEY_ID = "master_key_id";
- public static final String ARG_FINGERPRINT = "fingerprint";
- public static final String ARG_USER_ID = "user_id";
- public static final String ARG_CARD_AID = "aid";
- public static final String ARG_CARD_VERSION = "version";
-
- private byte[][] mFingerprints;
- private String mUserId;
- private byte[] mCardAid;
- private double mCardVersion;
- private long mMasterKeyId;
- private long[] mSubKeyIds;
-
- private Button vButton;
- private TextView vStatus;
-
- public static ViewKeySecurityTokenFragment newInstance(long masterKeyId,
- byte[] fingerprints, String userId, byte[] aid, double version) {
- ViewKeySecurityTokenFragment frag = new ViewKeySecurityTokenFragment();
-
- Bundle args = new Bundle();
- args.putLong(ARG_MASTER_KEY_ID, masterKeyId);
- args.putByteArray(ARG_FINGERPRINT, fingerprints);
- args.putString(ARG_USER_ID, userId);
- args.putByteArray(ARG_CARD_AID, aid);
- args.putDouble(ARG_CARD_VERSION, version);
- frag.setArguments(args);
-
- return frag;
- }
-
- public ViewKeySecurityTokenFragment() {
- super(null);
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- Bundle args = getArguments();
- ByteBuffer buf = ByteBuffer.wrap(args.getByteArray(ARG_FINGERPRINT));
- mFingerprints = new byte[buf.remaining()/20][];
- for (int i = 0; i < mFingerprints.length; i++) {
- mFingerprints[i] = new byte[20];
- buf.get(mFingerprints[i]);
- }
- mUserId = args.getString(ARG_USER_ID);
- mCardAid = args.getByteArray(ARG_CARD_AID);
- mCardVersion = args.getDouble(ARG_CARD_VERSION);
-
- mMasterKeyId = args.getLong(ARG_MASTER_KEY_ID);
-
- getLoaderManager().initLoader(0, null, this);
-
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) {
- View view = inflater.inflate(R.layout.view_key_security_token, null);
-
- TextView vSerNo = (TextView) view.findViewById(R.id.token_serno);
- TextView vUserId = (TextView) view.findViewById(R.id.token_userid);
-
- String serno = Hex.toHexString(mCardAid, 10, 4);
- vSerNo.setText(getString(R.string.security_token_serial_no, serno));
-
- if (!mUserId.isEmpty()) {
- vUserId.setText(getString(R.string.security_token_key_holder, mUserId));
- } else {
- vUserId.setText(getString(R.string.security_token_key_holder_not_set));
- }
-
- vButton = (Button) view.findViewById(R.id.button_bind);
- vButton.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- promoteToSecretKey();
- }
- });
-
- vStatus = (TextView) view.findViewById(R.id.token_status);
-
- return view;
- }
-
- public void promoteToSecretKey() {
- long[] subKeyIds = new long[mFingerprints.length];
- for (int i = 0; i < subKeyIds.length; i++) {
- subKeyIds[i] = KeyFormattingUtils.getKeyIdFromFingerprint(mFingerprints[i]);
- }
-
- // mMasterKeyId and mCardAid are already set
- mSubKeyIds = subKeyIds;
-
- cryptoOperation();
- }
-
- public static final String[] PROJECTION = new String[]{
- Keys._ID,
- Keys.KEY_ID,
- Keys.RANK,
- Keys.HAS_SECRET,
- Keys.FINGERPRINT
- };
- // private static final int INDEX_KEY_ID = 1;
- // private static final int INDEX_RANK = 2;
- private static final int INDEX_HAS_SECRET = 3;
- private static final int INDEX_FINGERPRINT = 4;
-
- @Override
- public Loader onCreateLoader(int id, Bundle args) {
- return new CursorLoader(getActivity(), Keys.buildKeysUri(mMasterKeyId),
- PROJECTION, null, null, null);
- }
-
- @Override
- public void onLoadFinished(Loader loader, Cursor data) {
- if (!data.moveToFirst()) {
- // wut?
- return;
- }
-
- boolean allBound = true;
- boolean noneBound = true;
-
- do {
- SecretKeyType keyType = SecretKeyType.fromNum(data.getInt(INDEX_HAS_SECRET));
- byte[] fingerprint = data.getBlob(INDEX_FINGERPRINT);
- Integer index = naiveIndexOf(mFingerprints, fingerprint);
- if (index == null) {
- continue;
- }
- if (keyType == SecretKeyType.DIVERT_TO_CARD) {
- noneBound = false;
- } else {
- allBound = false;
- }
- } while (data.moveToNext());
-
- if (allBound) {
- vButton.setVisibility(View.GONE);
- vStatus.setText(R.string.security_token_status_bound);
- } else {
- vButton.setVisibility(View.VISIBLE);
- vStatus.setText(noneBound
- ? R.string.security_token_status_unbound
- : R.string.security_token_status_partly);
- }
-
- }
-
- static private Integer naiveIndexOf(byte[][] haystack, byte[] needle) {
- for (int i = 0; i < haystack.length; i++) {
- if (Arrays.equals(needle, haystack[i])) {
- return i;
- }
- }
- return null;
- }
-
- @Override
- public void onLoaderReset(Loader loader) {
-
- }
-
- @Override
- public PromoteKeyringParcel createOperationInput() {
- return PromoteKeyringParcel.createPromoteKeyringParcel(mMasterKeyId, mCardAid, mSubKeyIds);
- }
-
- @Override
- public void onQueuedOperationSuccess(PromoteKeyResult result) {
- result.createNotify(getActivity()).show();
- }
-
-}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseSecurityTokenActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseSecurityTokenActivity.java
index 55ed00358..db7153e3d 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseSecurityTokenActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseSecurityTokenActivity.java
@@ -41,13 +41,10 @@ import nordpol.android.TagDispatcher;
import nordpol.android.TagDispatcherBuilder;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
-import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
-import org.sufficientlysecure.keychain.provider.KeyRepository;
-import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.securitytoken.CardException;
import org.sufficientlysecure.keychain.securitytoken.NfcTransport;
import org.sufficientlysecure.keychain.securitytoken.SecurityTokenHelper;
+import org.sufficientlysecure.keychain.securitytoken.SecurityTokenInfo;
import org.sufficientlysecure.keychain.securitytoken.Transport;
import org.sufficientlysecure.keychain.securitytoken.UsbConnectionDispatcher;
import org.sufficientlysecure.keychain.securitytoken.usb.UsbTransport;
@@ -56,10 +53,8 @@ import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
import org.sufficientlysecure.keychain.ui.CreateKeyActivity;
import org.sufficientlysecure.keychain.ui.PassphraseDialogActivity;
-import org.sufficientlysecure.keychain.ui.keyview.ViewKeyActivity;
import org.sufficientlysecure.keychain.ui.dialog.FidesmoInstallDialog;
import org.sufficientlysecure.keychain.ui.dialog.FidesmoPgpInstallDialog;
-import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.ui.util.Notify.Style;
import org.sufficientlysecure.keychain.util.Log;
@@ -78,9 +73,7 @@ public abstract class BaseSecurityTokenActivity extends BaseActivity
protected UsbConnectionDispatcher mUsbDispatcher;
private boolean mTagHandlingEnabled;
- private byte[] mSecurityTokenFingerprints;
- private String mSecurityTokenUserId;
- private byte[] mSecurityTokenAid;
+ private SecurityTokenInfo tokenInfo;
/**
* Override to change UI before SecurityToken handling (UI thread)
@@ -92,36 +85,17 @@ public abstract class BaseSecurityTokenActivity extends BaseActivity
* Override to implement SecurityToken operations (background thread)
*/
protected void doSecurityTokenInBackground() throws IOException {
- mSecurityTokenAid = mSecurityTokenHelper.getAid();
- mSecurityTokenFingerprints = mSecurityTokenHelper.getFingerprints();
- mSecurityTokenUserId = mSecurityTokenHelper.getUserId();
+ tokenInfo = mSecurityTokenHelper.getTokenInfo();
+ Log.d(Constants.TAG, "Security Token: " + tokenInfo);
}
/**
* Override to handle result of SecurityToken operations (UI thread)
*/
protected void onSecurityTokenPostExecute() {
-
- final long subKeyId = KeyFormattingUtils.getKeyIdFromFingerprint(mSecurityTokenFingerprints);
-
- try {
- CachedPublicKeyRing ring = KeyRepository.createDatabaseInteractor(this).getCachedPublicKeyRing(
- KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(subKeyId));
- long masterKeyId = ring.getMasterKeyId();
-
- Intent intent = new Intent(this, ViewKeyActivity.class);
- intent.setData(KeyRings.buildGenericKeyRingUri(masterKeyId));
- intent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_AID, mSecurityTokenAid);
- intent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_USER_ID, mSecurityTokenUserId);
- intent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_FINGERPRINTS, mSecurityTokenFingerprints);
- startActivity(intent);
- } catch (PgpKeyNotFoundException e) {
- Intent intent = new Intent(this, CreateKeyActivity.class);
- intent.putExtra(CreateKeyActivity.EXTRA_SECURITY_TOKEN_AID, mSecurityTokenAid);
- intent.putExtra(CreateKeyActivity.EXTRA_SECURITY_TOKEN_USER_ID, mSecurityTokenUserId);
- intent.putExtra(CreateKeyActivity.EXTRA_SECURITY_FINGERPRINTS, mSecurityTokenFingerprints);
- startActivity(intent);
- }
+ Intent intent = new Intent(this, CreateKeyActivity.class);
+ intent.putExtra(CreateKeyActivity.EXTRA_SECURITY_TOKEN_INFO, tokenInfo);
+ startActivity(intent);
}
/**
@@ -134,7 +108,7 @@ public abstract class BaseSecurityTokenActivity extends BaseActivity
/**
* Override to do something when PIN is wrong, e.g., clear passphrases (UI thread)
*/
- protected void onSecurityTokenPinError(String error) {
+ protected void onSecurityTokenPinError(String error, SecurityTokenInfo tokeninfo) {
onSecurityTokenError(error);
}
@@ -268,8 +242,16 @@ public abstract class BaseSecurityTokenActivity extends BaseActivity
// https://github.com/Yubico/ykneo-openpgp/commit/90c2b91e86fb0e43ee234dd258834e75e3416410
if ((status & (short) 0xFFF0) == 0x63C0) {
int tries = status & 0x000F;
+
+ SecurityTokenInfo tokeninfo = null;
+ try {
+ tokeninfo = mSecurityTokenHelper.getTokenInfo();
+ } catch (IOException e2) {
+ // don't care
+ }
// hook to do something different when PIN is wrong
- onSecurityTokenPinError(getResources().getQuantityString(R.plurals.security_token_error_pin, tries, tries));
+ onSecurityTokenPinError(
+ getResources().getQuantityString(R.plurals.security_token_error_pin, tries, tries), tokeninfo);
return;
}
@@ -282,8 +264,15 @@ public abstract class BaseSecurityTokenActivity extends BaseActivity
PW not checked (command not allowed), Secure messaging incorrect (checksum and/or cryptogram) */
// NOTE: Used in ykneo-openpgp >= 1.0.11 for wrong PIN
case 0x6982: {
+ SecurityTokenInfo tokeninfo = null;
+ try {
+ tokeninfo = mSecurityTokenHelper.getTokenInfo();
+ } catch (IOException e2) {
+ // don't care
+ }
+
// hook to do something different when PIN is wrong
- onSecurityTokenPinError(getString(R.string.security_token_error_security_not_satisfied));
+ onSecurityTokenPinError(getString(R.string.security_token_error_security_not_satisfied), tokeninfo);
break;
}
/* OpenPGP Card Spec: Selected file in termination state */
@@ -296,14 +285,14 @@ public abstract class BaseSecurityTokenActivity extends BaseActivity
// https://github.com/Yubico/ykneo-openpgp/commit/b49ce8241917e7c087a4dab7b2c755420ff4500f
case 0x6700: {
// hook to do something different when PIN is wrong
- onSecurityTokenPinError(getString(R.string.security_token_error_wrong_length));
+ onSecurityTokenPinError(getString(R.string.security_token_error_wrong_length), null);
break;
}
/* OpenPGP Card Spec: Incorrect parameters in the data field */
// NOTE: Used in ykneo-openpgp >= 1.0.11 for too short PIN
case 0x6A80: {
// hook to do something different when PIN is wrong
- onSecurityTokenPinError(getString(R.string.security_token_error_bad_data));
+ onSecurityTokenPinError(getString(R.string.security_token_error_bad_data), null);
break;
}
/* OpenPGP Card Spec: Authentication method blocked, PW blocked (error counter zero) */
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CryptoOperationHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CryptoOperationHelper.java
index f7316d811..b2c8fd853 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CryptoOperationHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CryptoOperationHelper.java
@@ -25,9 +25,11 @@ import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Intent;
import android.os.Bundle;
+import android.os.Handler;
import android.os.Message;
import android.os.Messenger;
import android.os.Parcelable;
+import android.os.SystemClock;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
@@ -55,6 +57,8 @@ import org.sufficientlysecure.keychain.util.Log;
*/
public class CryptoOperationHelper {
+ private long operationStartTime;
+
public interface Callback {
T createOperationInput();
@@ -67,6 +71,19 @@ public class CryptoOperationHelper
+ implements Callback {
+ @Override
+ public void onCryptoOperationCancelled() {
+ throw new UnsupportedOperationException("Unexpectedly cancelled operation!!");
+ }
+
+ @Override
+ public boolean onCryptoSetProgress(String msg, int progress, int max) {
+ return false;
+ }
+ }
+
// request codes from CryptoOperationHelper are created essentially
// a static property, used to identify requestCodes meant for this
// particular helper. a request code looks as follows:
@@ -85,6 +102,7 @@ public class CryptoOperationHelper minimumOperationDelay) {
+ returnResultToCallback(result);
+ return;
+ }
+
+ new Handler().postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ returnResultToCallback(result);
+ }
+ }, minimumOperationDelay - elapsedTime);
+ }
+
+ private void returnResultToCallback(OperationResult result) {
try {
if (result.success()) {
// noinspection unchecked, because type erasure :(
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/ViewKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/ViewKeyActivity.java
index 514e1231a..c60d0908e 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/ViewKeyActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/ViewKeyActivity.java
@@ -19,7 +19,6 @@
package org.sufficientlysecure.keychain.ui.keyview;
-import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -76,7 +75,6 @@ import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType;
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;
import org.sufficientlysecure.keychain.provider.KeychainContract;
@@ -87,7 +85,6 @@ import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
import org.sufficientlysecure.keychain.ui.BackupActivity;
import org.sufficientlysecure.keychain.ui.CertifyFingerprintActivity;
import org.sufficientlysecure.keychain.ui.CertifyKeyActivity;
-import org.sufficientlysecure.keychain.ui.CreateKeyActivity;
import org.sufficientlysecure.keychain.ui.DeleteKeyDialogActivity;
import org.sufficientlysecure.keychain.ui.EncryptFilesActivity;
import org.sufficientlysecure.keychain.ui.EncryptTextActivity;
@@ -98,7 +95,6 @@ import org.sufficientlysecure.keychain.ui.QrCodeViewActivity;
import org.sufficientlysecure.keychain.ui.SafeSlingerActivity;
import org.sufficientlysecure.keychain.ui.ViewKeyAdvActivity;
import org.sufficientlysecure.keychain.ui.ViewKeyKeybaseFragment;
-import org.sufficientlysecure.keychain.ui.ViewKeySecurityTokenFragment;
import org.sufficientlysecure.keychain.ui.base.BaseSecurityTokenActivity;
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
import org.sufficientlysecure.keychain.ui.dialog.SetPassphraseDialogFragment;
@@ -107,7 +103,6 @@ import org.sufficientlysecure.keychain.ui.util.FormattingUtils;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.State;
import org.sufficientlysecure.keychain.ui.util.Notify;
-import org.sufficientlysecure.keychain.ui.util.Notify.ActionListener;
import org.sufficientlysecure.keychain.ui.util.Notify.Style;
import org.sufficientlysecure.keychain.ui.util.QrCodeUtils;
import org.sufficientlysecure.keychain.util.ContactHelper;
@@ -119,12 +114,6 @@ import org.sufficientlysecure.keychain.util.Preferences;
public class ViewKeyActivity extends BaseSecurityTokenActivity implements
LoaderManager.LoaderCallbacks,
CryptoOperationHelper.Callback {
-
- public static final String EXTRA_SECURITY_TOKEN_USER_ID = "security_token_user_id";
- public static final String EXTRA_SECURITY_TOKEN_AID = "security_token_aid";
- public static final String EXTRA_SECURITY_TOKEN_VERSION = "security_token_version";
- public static final String EXTRA_SECURITY_TOKEN_FINGERPRINTS = "security_token_fingerprints";
-
@Retention(RetentionPolicy.SOURCE)
@IntDef({REQUEST_QR_FINGERPRINT, REQUEST_BACKUP, REQUEST_CERTIFY, REQUEST_DELETE})
private @interface RequestType {
@@ -173,8 +162,6 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity implements
private boolean mIsSecure = true;
private boolean mIsExpired = false;
- private boolean mShowSecurityTokenAfterCreation = false;
-
private MenuItem mRefreshItem;
private boolean mIsRefreshing;
private Animation mRotate, mRotateSpin;
@@ -183,11 +170,6 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity implements
private long mMasterKeyId;
private byte[] mFingerprint;
- private byte[] mSecurityTokenFingerprints;
- private String mSecurityTokenUserId;
- private byte[] mSecurityTokenAid;
- private double mSecurityTokenVersion;
-
@SuppressLint("InflateParams")
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -334,11 +316,6 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity implements
.replace(R.id.view_key_keybase_fragment, keybaseFrag)
.commit();
}
-
- // need to postpone loading of the security token fragment until after mMasterKeyId
- // is available, but we mark here that this should be done
- mShowSecurityTokenAfterCreation = true;
-
}
@Override
@@ -641,89 +618,10 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity implements
}
- @Override
- protected void doSecurityTokenInBackground() throws IOException {
-
- mSecurityTokenFingerprints = mSecurityTokenHelper.getFingerprints();
- mSecurityTokenUserId = mSecurityTokenHelper.getUserId();
- mSecurityTokenAid = mSecurityTokenHelper.getAid();
- }
-
@Override
protected void onSecurityTokenPostExecute() {
-
- long tokenId = KeyFormattingUtils.getKeyIdFromFingerprint(mSecurityTokenFingerprints);
-
- try {
-
- // if the security token matches a subkey in any key
- CachedPublicKeyRing ring = mKeyRepository.getCachedPublicKeyRing(
- KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(tokenId));
- byte[] candidateFp = ring.getFingerprint();
-
- // if the master key of that key matches this one, just show the token dialog
- if (Arrays.equals(candidateFp, mFingerprint)) {
- showSecurityTokenFragment(
- mSecurityTokenFingerprints, mSecurityTokenUserId, mSecurityTokenAid, mSecurityTokenVersion);
- return;
- }
-
- // otherwise, offer to go to that key
- final long masterKeyId = KeyFormattingUtils.getKeyIdFromFingerprint(candidateFp);
- Notify.create(this, R.string.snack_security_token_other, Notify.LENGTH_LONG,
- Style.WARN, new ActionListener() {
- @Override
- public void onAction() {
- Intent intent = new Intent(
- ViewKeyActivity.this, ViewKeyActivity.class);
- intent.setData(KeyRings.buildGenericKeyRingUri(masterKeyId));
- intent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_AID, mSecurityTokenAid);
- intent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_VERSION, mSecurityTokenVersion);
- intent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_USER_ID, mSecurityTokenUserId);
- intent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_FINGERPRINTS, mSecurityTokenFingerprints);
- startActivity(intent);
- finish();
- }
- }, R.string.snack_security_token_view).show();
- // and if it's not found, offer import
- } catch (PgpKeyNotFoundException e) {
- Notify.create(this, R.string.snack_security_token_other, Notify.LENGTH_LONG,
- Style.WARN, new ActionListener() {
- @Override
- public void onAction() {
- Intent intent = new Intent(
- ViewKeyActivity.this, CreateKeyActivity.class);
- intent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_AID, mSecurityTokenAid);
- intent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_VERSION, mSecurityTokenVersion);
- intent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_USER_ID, mSecurityTokenUserId);
- intent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_FINGERPRINTS, mSecurityTokenFingerprints);
- startActivity(intent);
- finish();
- }
- }, R.string.snack_security_token_import).show();
- }
- }
-
- public void showSecurityTokenFragment(
- final byte[] tokenFingerprints, final String tokenUserId, final byte[] tokenAid, final double tokenVersion) {
-
- new Handler().post(new Runnable() {
- @Override
- public void run() {
- ViewKeySecurityTokenFragment frag = ViewKeySecurityTokenFragment.newInstance(
- mMasterKeyId, tokenFingerprints, tokenUserId, tokenAid, tokenVersion);
-
- FragmentManager manager = getSupportFragmentManager();
-
- manager.popBackStack("security_token", FragmentManager.POP_BACK_STACK_INCLUSIVE);
- manager.beginTransaction()
- .addToBackStack("security_token")
- .replace(R.id.view_key_fragment, frag)
- // if this is called while the activity wasn't resumed, just forget it happened
- .commitAllowingStateLoss();
- }
- });
-
+ super.onSecurityTokenPostExecute();
+ finish();
}
public void showMainFragment() {
@@ -927,17 +825,6 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity implements
// queue showing of the main fragment
showMainFragment();
- // if it wasn't shown yet, display token fragment
- if (mShowSecurityTokenAfterCreation && getIntent().hasExtra(EXTRA_SECURITY_TOKEN_AID)) {
- mShowSecurityTokenAfterCreation = false;
- Intent intent = getIntent();
- byte[] tokenFingerprints = intent.getByteArrayExtra(EXTRA_SECURITY_TOKEN_FINGERPRINTS);
- String tokenUserId = intent.getStringExtra(EXTRA_SECURITY_TOKEN_USER_ID);
- byte[] tokenAid = intent.getByteArrayExtra(EXTRA_SECURITY_TOKEN_AID);
- double tokenVersion = intent.getDoubleExtra(EXTRA_SECURITY_TOKEN_VERSION, 2.0);
- showSecurityTokenFragment(tokenFingerprints, tokenUserId, tokenAid, tokenVersion);
- }
-
// if the refresh animation isn't playing
if (!mRotate.hasStarted() && !mRotateSpin.hasStarted()) {
// re-create options menu based on mIsSecret, mIsVerified
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/loader/IdentityLoader.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/loader/IdentityLoader.java
index a14287811..a6b8a137c 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/loader/IdentityLoader.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/loader/IdentityLoader.java
@@ -151,7 +151,8 @@ public class IdentityLoader extends AsyncTaskLoader> {
private Intent getTrustIdActivityIntentIfResolvable(String packageName, String autocryptPeer) {
Intent intent = new Intent();
- intent.setAction(packageName + ".AUTOCRYPT_PEER_ACTION");
+ intent.setAction("org.autocrypt.PEER_ACTION");
+ intent.setPackage(packageName);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(OpenPgpApi.EXTRA_AUTOCRYPT_PEER_ID, autocryptPeer);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/token/ChangePinDialogHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/token/ChangePinDialogHelper.java
new file mode 100644
index 000000000..0db53ca2c
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/token/ChangePinDialogHelper.java
@@ -0,0 +1,76 @@
+package org.sufficientlysecure.keychain.ui.token;
+
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnShowListener;
+import android.support.annotation.CheckResult;
+import android.support.v7.app.AlertDialog;
+import android.support.v7.app.AlertDialog.Builder;
+import android.view.ContextThemeWrapper;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.EditText;
+
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.ui.token.ManageSecurityTokenContract.ManageSecurityTokenMvpPresenter;
+import org.sufficientlysecure.keychain.ui.util.ThemeChanger;
+
+
+class ChangePinDialogHelper {
+ @CheckResult
+ static AlertDialog createAdminPinDialog(Context context, final ManageSecurityTokenMvpPresenter presenter) {
+ ContextThemeWrapper themedContext = ThemeChanger.getDialogThemeWrapper(context);
+
+ @SuppressLint("InflateParams") // it's a dialog, no root element
+ View view = LayoutInflater.from(themedContext).inflate(R.layout.admin_pin_dialog, null, false);
+ final EditText adminPin = (EditText) view.findViewById(R.id.admin_pin_current);
+ final EditText newPin = (EditText) view.findViewById(R.id.pin_new);
+ final EditText newPinRepeat = (EditText) view.findViewById(R.id.pin_new_repeat);
+
+ AlertDialog dialog = new Builder(themedContext)
+ .setView(view)
+ .setNegativeButton(R.string.button_cancel, null)
+ .setPositiveButton(R.string.token_unlock_ok, null).create();
+ dialog.setOnShowListener(new OnShowListener() {
+ @Override
+ public void onShow(final DialogInterface dialog) {
+ ((AlertDialog) dialog).getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ checkAndHandleInput(adminPin, newPin, newPinRepeat, dialog, presenter);
+ }
+ });
+ }
+ });
+
+ return dialog;
+ }
+
+ private static void checkAndHandleInput(EditText adminPinView, EditText newPinView, EditText newPinRepeatView,
+ DialogInterface dialog, ManageSecurityTokenMvpPresenter presenter) {
+ String adminPin = adminPinView.getText().toString();
+ String newPin = newPinView.getText().toString();
+ String newPinRepeat = newPinRepeatView.getText().toString();
+
+ if (adminPin.length() < 8) {
+ adminPinView.setError(adminPinView.getContext().getString(R.string.token_error_admin_min8));
+ return;
+ }
+
+ if (newPin.length() < 6) {
+ newPinView.setError(newPinView.getContext().getString(R.string.token_error_pin_min6));
+ return;
+ }
+
+ if (!newPin.equals(newPinRepeat)) {
+ newPinRepeatView.setError(newPinRepeatView.getContext().getString(R.string.token_error_pin_repeat));
+ return;
+ }
+
+ dialog.dismiss();
+ presenter.onInputAdminPin(adminPin, newPin);
+ }
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/token/ManageSecurityTokenContract.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/token/ManageSecurityTokenContract.java
new file mode 100644
index 000000000..f5f481930
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/token/ManageSecurityTokenContract.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2017 Vincent Breitmoser
+ *
+ * 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 .
+ */
+
+package org.sufficientlysecure.keychain.ui.token;
+
+
+import java.util.List;
+
+import android.net.Uri;
+
+import org.sufficientlysecure.keychain.operations.results.OperationResult;
+import org.sufficientlysecure.keychain.securitytoken.SecurityTokenInfo;
+import org.sufficientlysecure.keychain.ui.token.ManageSecurityTokenFragment.StatusLine;
+
+
+class ManageSecurityTokenContract {
+ interface ManageSecurityTokenMvpPresenter {
+ void setView(ManageSecurityTokenMvpView createSecurityTokenImportFragment);
+ void onActivityCreated();
+
+ void onClickRetry();
+ void onClickViewKey();
+ void onMenuClickViewLog();
+
+ void onClickImport();
+ void onImportSuccess(OperationResult result);
+ void onImportError(OperationResult result);
+
+ void onPromoteSuccess(OperationResult result);
+ void onPromoteError(OperationResult result);
+
+
+ void onSecurityTokenChangePinSuccess(SecurityTokenInfo tokenInfo);
+
+ void onSecurityTokenChangePinCanceled(SecurityTokenInfo tokenInfo);
+
+ void onClickLoadFile();
+ void onFileSelected(Uri fileUri);
+ void onStoragePermissionGranted();
+ void onStoragePermissionDenied();
+
+ void onClickResetToken();
+ void onClickConfirmReset();
+ void onSecurityTokenResetSuccess(SecurityTokenInfo tokenInfo);
+ void onSecurityTokenResetCanceled(SecurityTokenInfo tokenInfo);
+
+ void onClickSetupToken();
+
+ void onClickUnlockToken();
+ void onMenuClickChangePin();
+ void onInputAdminPin(String adminPin, String newPin);
+
+ void onClickUnlockTokenImpossible();
+ }
+
+ interface ManageSecurityTokenMvpView {
+ void statusLineAdd(StatusLine statusLine);
+ void statusLineOk();
+ void statusLineError();
+ void resetStatusLines();
+
+ void showActionImport();
+ void showActionViewKey();
+ void showActionRetryOrFromFile();
+ void showActionLocked(int unlockAttempts);
+ void showActionEmptyToken();
+ void hideAction();
+
+ void operationImportKey(byte[] importKeyData);
+ void operationPromote(long masterKeyId, byte[] cardAid, List fingerprints);
+ void operationResetSecurityToken();
+ void operationChangePinSecurityToken(String adminPin, String newPin);
+
+ void finishAndShowKey(long masterKeyId);
+
+ void showFileSelectDialog();
+ void showConfirmResetDialog();
+ void showAdminPinDialog();
+ void startCreateKeyForToken(SecurityTokenInfo tokenInfo);
+
+ void showDisplayLogActivity(OperationResult result);
+
+ void requestStoragePermission();
+
+ void showErrorCannotUnlock();
+ }
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/token/ManageSecurityTokenFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/token/ManageSecurityTokenFragment.java
new file mode 100644
index 000000000..aff9e9810
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/token/ManageSecurityTokenFragment.java
@@ -0,0 +1,513 @@
+/*
+ * Copyright (C) 2017 Vincent Breitmoser
+ *
+ * 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 .
+ */
+
+package org.sufficientlysecure.keychain.ui.token;
+
+
+import java.util.List;
+
+import android.Manifest;
+import android.annotation.TargetApi;
+import android.app.Activity;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.Build.VERSION_CODES;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.StringRes;
+import android.support.v4.app.Fragment;
+import android.support.v7.app.AlertDialog;
+import android.support.v7.app.AlertDialog.Builder;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
+import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
+import org.sufficientlysecure.keychain.operations.results.OperationResult;
+import org.sufficientlysecure.keychain.operations.results.PromoteKeyResult;
+import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
+import org.sufficientlysecure.keychain.securitytoken.SecurityTokenInfo;
+import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
+import org.sufficientlysecure.keychain.service.PromoteKeyringParcel;
+import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
+import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
+import org.sufficientlysecure.keychain.service.input.SecurityTokenChangePinParcel;
+import org.sufficientlysecure.keychain.ui.CreateKeyActivity;
+import org.sufficientlysecure.keychain.ui.LogDisplayActivity;
+import org.sufficientlysecure.keychain.ui.LogDisplayFragment;
+import org.sufficientlysecure.keychain.ui.SecurityTokenOperationActivity;
+import org.sufficientlysecure.keychain.ui.SecurityTokenChangePinOperationActivity;
+import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
+import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper.AbstractCallback;
+import org.sufficientlysecure.keychain.ui.keyview.ViewKeyActivity;
+import org.sufficientlysecure.keychain.ui.token.ManageSecurityTokenContract.ManageSecurityTokenMvpPresenter;
+import org.sufficientlysecure.keychain.ui.token.ManageSecurityTokenContract.ManageSecurityTokenMvpView;
+import org.sufficientlysecure.keychain.ui.util.Notify;
+import org.sufficientlysecure.keychain.ui.util.Notify.Style;
+import org.sufficientlysecure.keychain.ui.util.ThemeChanger;
+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 ManageSecurityTokenFragment extends Fragment implements ManageSecurityTokenMvpView, OnClickListener {
+ private static final String ARG_TOKEN_INFO = "token_info";
+ public static final int REQUEST_CODE_OPEN_FILE = 0;
+ public static final int REQUEST_CODE_RESET = 1;
+ public static final int REQUEST_CODE_CHANGE_PIN = 2;
+ public static final int PERMISSION_READ_STORAGE = 0;
+
+ ManageSecurityTokenMvpPresenter presenter;
+ private ViewGroup statusLayoutGroup;
+ private ToolableViewAnimator actionAnimator;
+ private TextView unlockSubtitle;
+
+ ImportKeyringParcel currentImportKeyringParcel;
+ PromoteKeyringParcel currentPromoteKeyringParcel;
+ private LayoutInflater layoutInflater;
+ private StatusIndicator latestStatusIndicator;
+
+ public static Fragment newInstance(SecurityTokenInfo tokenInfo) {
+ ManageSecurityTokenFragment frag = new ManageSecurityTokenFragment();
+
+ Bundle args = new Bundle();
+ args.putParcelable(ARG_TOKEN_INFO, tokenInfo);
+ frag.setArguments(args);
+
+ return frag;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ Bundle args = getArguments();
+ SecurityTokenInfo tokenInfo = args.getParcelable(ARG_TOKEN_INFO);
+
+ presenter = new ManageSecurityTokenPresenter(getContext(), getLoaderManager(), tokenInfo);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ this.layoutInflater = inflater;
+ View view = inflater.inflate(R.layout.create_security_token_import_fragment, container, false);
+
+ statusLayoutGroup = (ViewGroup) view.findViewById(R.id.status_indicator_layout);
+ actionAnimator = (ToolableViewAnimator) view.findViewById(R.id.action_animator);
+ unlockSubtitle = (TextView) view.findViewById(R.id.button_unlock_subtitle);
+
+ view.findViewById(R.id.button_import).setOnClickListener(this);
+ view.findViewById(R.id.button_view_key).setOnClickListener(this);
+ view.findViewById(R.id.button_retry).setOnClickListener(this);
+ 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_reset_token_4).setOnClickListener(this);
+ view.findViewById(R.id.button_reset_token_5).setOnClickListener(this);
+ view.findViewById(R.id.button_unlock).setOnClickListener(this);
+ view.findViewById(R.id.button_unlock_impossible).setOnClickListener(this);
+ view.findViewById(R.id.button_load_file).setOnClickListener(this);
+ view.findViewById(R.id.button_setup).setOnClickListener(this);
+
+ setHasOptionsMenu(true);
+
+ presenter.setView(this);
+
+ return view;
+ }
+
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+
+ presenter.setView(null);
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ inflater.inflate(R.menu.token_setup, menu);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.view_log: {
+ presenter.onMenuClickViewLog();
+ return true;
+ }
+ case R.id.change_pin: {
+ presenter.onMenuClickChangePin();
+ return true;
+ }
+ default: {
+ return super.onOptionsItemSelected(item);
+ }
+ }
+ }
+
+ @Override
+ public void onActivityCreated(@Nullable Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ presenter.onActivityCreated();
+ }
+
+ @Override
+ public void finishAndShowKey(long masterKeyId) {
+ Activity activity = getActivity();
+
+ Intent viewKeyIntent = new Intent(activity, ViewKeyActivity.class);
+ // use the imported masterKeyId, not the one from the token, because
+ // that one might* just have been a subkey of the imported key
+ viewKeyIntent.setData(KeyRings.buildGenericKeyRingUri(masterKeyId));
+
+ if (activity instanceof CreateKeyActivity) {
+ ((CreateKeyActivity) activity).finishWithFirstTimeHandling(viewKeyIntent);
+ } else {
+ activity.startActivity(viewKeyIntent);
+ activity.finish();
+ }
+ }
+
+ @Override
+ public void statusLineAdd(StatusLine statusLine) {
+ if (latestStatusIndicator != null) {
+ throw new IllegalStateException("Cannot set next status line before completing previous!");
+ }
+
+ View line = layoutInflater.inflate(R.layout.status_indicator_line, statusLayoutGroup, false);
+
+ latestStatusIndicator = (StatusIndicator) line.findViewById(R.id.status_indicator);
+ latestStatusIndicator.setDisplayedChild(Status.PROGRESS);
+ TextView latestStatusText = (TextView) line.findViewById(R.id.status_text);
+ latestStatusText.setText(statusLine.stringRes);
+
+ statusLayoutGroup.addView(line);
+ }
+
+ @Override
+ public void statusLineOk() {
+ latestStatusIndicator.setDisplayedChild(Status.OK);
+ latestStatusIndicator = null;
+ }
+
+ @Override
+ public void statusLineError() {
+ latestStatusIndicator.setDisplayedChild(Status.ERROR);
+ latestStatusIndicator = null;
+ }
+
+ @Override
+ public void resetStatusLines() {
+ latestStatusIndicator = null;
+ statusLayoutGroup.removeAllViews();
+ }
+
+ @Override
+ public void showActionImport() {
+ actionAnimator.setDisplayedChildId(R.id.token_layout_import);
+ }
+
+ @Override
+ public void showActionViewKey() {
+ actionAnimator.setDisplayedChildId(R.id.token_layout_ok);
+ }
+
+ @Override
+ public void showActionRetryOrFromFile() {
+ actionAnimator.setDisplayedChildId(R.id.token_layout_not_found);
+ }
+
+ @Override
+ public void showActionLocked(int attemptsLeft) {
+ if (attemptsLeft > 0) {
+ actionAnimator.setDisplayedChildId(R.id.token_layout_locked);
+
+ String unlockAttemptsText = getResources().getQuantityString(
+ R.plurals.token_unlock_attempts, attemptsLeft, attemptsLeft);
+ unlockSubtitle.setText(unlockAttemptsText);
+ } else {
+ actionAnimator.setDisplayedChildId(R.id.token_layout_locked_hard);
+ }
+ }
+
+ @Override
+ public void showActionEmptyToken() {
+ actionAnimator.setDisplayedChildId(R.id.token_layout_empty);
+ }
+
+ @Override
+ public void hideAction() {
+ actionAnimator.setDisplayedChild(0);
+ }
+
+ @Override
+ public void operationImportKey(byte[] importKeyData) {
+ if (currentImportKeyringParcel != null) {
+ throw new IllegalStateException("Cannot trigger import operation twice!");
+ }
+
+ currentImportKeyringParcel =
+ ImportKeyringParcel.createImportKeyringParcel(ParcelableKeyRing.createFromEncodedBytes(importKeyData));
+ cryptoImportOperationHelper.setOperationMinimumDelay(1000L);
+ cryptoImportOperationHelper.cryptoOperation();
+ }
+
+ @Override
+ public void operationPromote(long masterKeyId, byte[] cardAid, List fingerprints) {
+ if (currentImportKeyringParcel != null) {
+ throw new IllegalStateException("Cannot trigger import operation twice!");
+ }
+
+ currentPromoteKeyringParcel = PromoteKeyringParcel.createPromoteKeyringParcel(
+ masterKeyId, cardAid, fingerprints);
+ cryptoPromoteOperationHelper.setOperationMinimumDelay(1000L);
+ cryptoPromoteOperationHelper.cryptoOperation();
+ }
+
+ @Override
+ public void operationResetSecurityToken() {
+ Intent intent = new Intent(getActivity(), SecurityTokenOperationActivity.class);
+ RequiredInputParcel resetP = RequiredInputParcel.createSecurityTokenReset();
+ intent.putExtra(SecurityTokenOperationActivity.EXTRA_REQUIRED_INPUT, resetP);
+ intent.putExtra(SecurityTokenOperationActivity.EXTRA_CRYPTO_INPUT, CryptoInputParcel.createCryptoInputParcel());
+ startActivityForResult(intent, REQUEST_CODE_RESET);
+ }
+
+ @Override
+ public void operationChangePinSecurityToken(String adminPin, String newPin) {
+ Intent intent = new Intent(getActivity(), SecurityTokenChangePinOperationActivity.class);
+ SecurityTokenChangePinParcel changePinParcel =
+ SecurityTokenChangePinParcel.createSecurityTokenUnlock(adminPin, newPin);
+ intent.putExtra(SecurityTokenChangePinOperationActivity.EXTRA_CHANGE_PIN_PARCEL, changePinParcel);
+ startActivityForResult(intent, REQUEST_CODE_CHANGE_PIN);
+ }
+
+ @Override
+ public void showFileSelectDialog() {
+ FileHelper.openDocument(this, null, "*/*", false, REQUEST_CODE_OPEN_FILE);
+ }
+
+ @Override
+ public void showConfirmResetDialog() {
+ new Builder(ThemeChanger.getDialogThemeWrapper(getContext()))
+ .setTitle(R.string.token_reset_confirm_title)
+ .setMessage(R.string.token_reset_confirm_message)
+ .setNegativeButton(R.string.button_cancel, null)
+ .setPositiveButton(R.string.token_reset_confirm_ok, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ presenter.onClickConfirmReset();
+ }
+ }).show();
+ }
+
+ @Override
+ public void showAdminPinDialog() {
+ AlertDialog adminPinDialog = ChangePinDialogHelper.createAdminPinDialog(getContext(), presenter);
+
+ adminPinDialog.show();
+ }
+
+ @Override
+ public void startCreateKeyForToken(SecurityTokenInfo tokenInfo) {
+ CreateKeyActivity activity = (CreateKeyActivity) getActivity();
+ activity.startCreateKeyForSecurityToken(tokenInfo);
+ }
+
+ @Override
+ public void showErrorCannotUnlock() {
+ Notify.create(getActivity(), R.string.token_error_locked_indefinitely, Style.ERROR).show();
+ }
+
+ @Override
+ public void showDisplayLogActivity(OperationResult result) {
+ Intent intent = new Intent(getActivity(), LogDisplayActivity.class);
+ intent.putExtra(LogDisplayFragment.EXTRA_RESULT, result);
+ startActivity(intent);
+ }
+
+ @TargetApi(VERSION_CODES.JELLY_BEAN)
+ @Override
+ public void requestStoragePermission() {
+ requestPermissions(new String[] { Manifest.permission.READ_EXTERNAL_STORAGE }, PERMISSION_READ_STORAGE);
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
+ @NonNull int[] grantResults) {
+ if (requestCode != PERMISSION_READ_STORAGE) {
+ return;
+ }
+
+ boolean permissionWasGranted = grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED;
+ if (permissionWasGranted) {
+ presenter.onStoragePermissionGranted();
+ } else {
+ presenter.onStoragePermissionDenied();
+ }
+ }
+
+ @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;
+ }
+ case REQUEST_CODE_RESET: {
+ SecurityTokenInfo tokenInfo = data == null ? null :
+ data.getParcelableExtra(SecurityTokenOperationActivity.RESULT_TOKEN_INFO);
+ if (resultCode == Activity.RESULT_OK) {
+ presenter.onSecurityTokenResetSuccess(tokenInfo);
+ } else {
+ presenter.onSecurityTokenResetCanceled(tokenInfo);
+ }
+ break;
+ }
+ case REQUEST_CODE_CHANGE_PIN: {
+ SecurityTokenInfo tokenInfo = data == null ? null :
+ data.getParcelableExtra(SecurityTokenOperationActivity.RESULT_TOKEN_INFO);
+ if (resultCode == Activity.RESULT_OK) {
+ presenter.onSecurityTokenChangePinSuccess(tokenInfo);
+ } else {
+ presenter.onSecurityTokenChangePinCanceled(tokenInfo);
+ }
+ break;
+ }
+ default: {
+ super.onActivityResult(requestCode, resultCode, data);
+ }
+ }
+ }
+
+ @Override
+ public void onClick(View v) {
+ switch (v.getId()) {
+ case R.id.button_import: {
+ presenter.onClickImport();
+ break;
+ }
+ case R.id.button_retry: {
+ presenter.onClickRetry();
+ break;
+ }
+ case R.id.button_view_key: {
+ presenter.onClickViewKey();
+ break;
+ }
+ case R.id.button_load_file: {
+ presenter.onClickLoadFile();
+ break;
+ }
+ case R.id.button_reset_token_1:
+ case R.id.button_reset_token_2:
+ case R.id.button_reset_token_3:
+ case R.id.button_reset_token_4:
+ case R.id.button_reset_token_5: {
+ presenter.onClickResetToken();
+ break;
+ }
+
+ case R.id.button_unlock: {
+ presenter.onClickUnlockToken();
+ break;
+ }
+ case R.id.button_unlock_impossible: {
+ presenter.onClickUnlockTokenImpossible();
+ break;
+ }
+
+ case R.id.button_setup: {
+ presenter.onClickSetupToken();
+ break;
+ }
+ }
+ }
+
+ CryptoOperationHelper cryptoImportOperationHelper =
+ new CryptoOperationHelper<>(0, this, new AbstractCallback() {
+ @Override
+ public ImportKeyringParcel createOperationInput() {
+ return currentImportKeyringParcel;
+ }
+
+ @Override
+ public void onCryptoOperationSuccess(ImportKeyResult result) {
+ currentImportKeyringParcel = null;
+ presenter.onImportSuccess(result);
+ }
+
+ @Override
+ public void onCryptoOperationError(ImportKeyResult result) {
+ currentImportKeyringParcel = null;
+ presenter.onImportError(result);
+ }
+ }, null);
+
+ CryptoOperationHelper cryptoPromoteOperationHelper =
+ new CryptoOperationHelper<>(1, this, new AbstractCallback() {
+ @Override
+ public PromoteKeyringParcel createOperationInput() {
+ return currentPromoteKeyringParcel;
+ }
+
+ @Override
+ public void onCryptoOperationSuccess(PromoteKeyResult result) {
+ currentPromoteKeyringParcel = null;
+ presenter.onPromoteSuccess(result);
+ }
+
+ @Override
+ public void onCryptoOperationError(PromoteKeyResult result) {
+ currentPromoteKeyringParcel = null;
+ presenter.onPromoteError(result);
+ }
+ }, null);
+
+ enum StatusLine {
+ CHECK_KEY (R.string.status_check_key),
+ SEARCH_LOCAL (R.string.status_search_local),
+ SEARCH_URI (R.string.status_search_uri),
+ 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),
+ SEARCH_CONTENT_URI (R.string.status_content_uri);
+
+ @StringRes
+ private int stringRes;
+
+ StatusLine(@StringRes int stringRes) {
+ this.stringRes = stringRes;
+ }
+ }
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/token/ManageSecurityTokenPresenter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/token/ManageSecurityTokenPresenter.java
new file mode 100644
index 000000000..f51fee3c5
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/token/ManageSecurityTokenPresenter.java
@@ -0,0 +1,425 @@
+/*
+ * Copyright (C) 2017 Vincent Breitmoser
+ *
+ * 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 .
+ */
+
+package org.sufficientlysecure.keychain.ui.token;
+
+
+import android.content.Context;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.support.v4.app.LoaderManager;
+import android.support.v4.app.LoaderManager.LoaderCallbacks;
+import android.support.v4.content.Loader;
+
+import org.sufficientlysecure.keychain.operations.results.GenericOperationResult;
+import org.sufficientlysecure.keychain.operations.results.OperationResult;
+import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
+import org.sufficientlysecure.keychain.securitytoken.SecurityTokenInfo;
+import org.sufficientlysecure.keychain.ui.token.ManageSecurityTokenContract.ManageSecurityTokenMvpPresenter;
+import org.sufficientlysecure.keychain.ui.token.ManageSecurityTokenContract.ManageSecurityTokenMvpView;
+import org.sufficientlysecure.keychain.ui.token.ManageSecurityTokenFragment.StatusLine;
+import org.sufficientlysecure.keychain.ui.token.PublicKeyRetrievalLoader.ContentUriRetrievalLoader;
+import org.sufficientlysecure.keychain.ui.token.PublicKeyRetrievalLoader.KeyRetrievalResult;
+import org.sufficientlysecure.keychain.ui.token.PublicKeyRetrievalLoader.KeyserverRetrievalLoader;
+import org.sufficientlysecure.keychain.ui.token.PublicKeyRetrievalLoader.LocalKeyLookupLoader;
+import org.sufficientlysecure.keychain.ui.token.PublicKeyRetrievalLoader.UriKeyRetrievalLoader;
+import org.sufficientlysecure.keychain.ui.util.PermissionsUtil;
+
+
+class ManageSecurityTokenPresenter implements ManageSecurityTokenMvpPresenter {
+ 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 final Context context;
+ private final LoaderManager loaderManager;
+
+ private SecurityTokenInfo tokenInfo;
+
+
+ private ManageSecurityTokenMvpView view;
+
+ private boolean checkedKeyStatus;
+ private boolean searchedLocally;
+ private boolean searchedAtUri;
+ private boolean searchedKeyservers;
+
+ private byte[] importKeyData;
+ private Long masterKeyId;
+
+ private OperationLog log;
+ private Uri selectedContentUri;
+
+ ManageSecurityTokenPresenter(Context context, LoaderManager loaderManager, SecurityTokenInfo tokenInfo) {
+ this.context = context.getApplicationContext();
+ this.loaderManager = loaderManager;
+ this.tokenInfo = tokenInfo;
+
+ this.log = new OperationLog();
+ }
+
+ @Override
+ public void setView(ManageSecurityTokenMvpView view) {
+ this.view = view;
+ }
+
+ @Override
+ public void onActivityCreated() {
+ if (!checkedKeyStatus || !searchedLocally || !searchedAtUri || !searchedKeyservers) {
+ continueSearch();
+ }
+ }
+
+ private void continueSearchAfterError() {
+ view.statusLineError();
+ continueSearch();
+ }
+
+ private void resetAndContinueSearch() {
+ checkedKeyStatus = false;
+ searchedLocally = false;
+ searchedAtUri = false;
+ searchedKeyservers = false;
+
+ view.hideAction();
+ view.resetStatusLines();
+ continueSearch();
+ }
+
+ private void continueSearch() {
+ if (!checkedKeyStatus) {
+ boolean keyIsLocked = tokenInfo.getVerifyRetries() == 0;
+ boolean keyIsEmpty = tokenInfo.isEmpty();
+ if (keyIsLocked || keyIsEmpty) {
+ // the "checking key status" is fake: we only do it if we already know the key is locked
+ view.statusLineAdd(StatusLine.CHECK_KEY);
+ delayPerformKeyCheck();
+ return;
+ } else {
+ checkedKeyStatus = true;
+ }
+ }
+
+ if (!searchedLocally) {
+ view.statusLineAdd(StatusLine.SEARCH_LOCAL);
+ loaderManager.restartLoader(LOADER_LOCAL, null, loaderCallbacks);
+ return;
+ }
+
+ if (!searchedAtUri) {
+ view.statusLineAdd(StatusLine.SEARCH_URI);
+ loaderManager.restartLoader(LOADER_URI, null, loaderCallbacks);
+ return;
+ }
+
+ if (!searchedKeyservers) {
+ view.statusLineAdd(StatusLine.SEARCH_KEYSERVER);
+ loaderManager.restartLoader(LOADER_KEYSERVER, null, loaderCallbacks);
+ return;
+ }
+
+ view.showActionRetryOrFromFile();
+ }
+
+ private void delayPerformKeyCheck() {
+ new Handler().postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ performKeyCheck();
+ }
+ }, 1000);
+ }
+
+ private void performKeyCheck() {
+ boolean keyIsEmpty = tokenInfo.isEmpty();
+ if (keyIsEmpty) {
+ view.statusLineOk();
+
+ view.showActionEmptyToken();
+ return;
+ }
+
+ boolean keyIsLocked = tokenInfo.getVerifyRetries() == 0;
+ if (keyIsLocked) {
+ view.statusLineError();
+
+ int unlockAttemptsLeft = tokenInfo.getVerifyAdminRetries();
+ view.showActionLocked(unlockAttemptsLeft);
+ return;
+ }
+
+ view.statusLineOk();
+
+ checkedKeyStatus = true;
+ continueSearch();
+ }
+
+ @Override
+ public void onClickUnlockToken() {
+ view.showAdminPinDialog();
+ }
+
+ @Override
+ public void onMenuClickChangePin() {
+ if (!checkedKeyStatus) {
+ return;
+ }
+
+ if (tokenInfo.getVerifyAdminRetries() == 0) {
+ view.showErrorCannotUnlock();
+ return;
+ }
+
+ view.showAdminPinDialog();
+ }
+
+ @Override
+ public void onInputAdminPin(String adminPin, String newPin) {
+ view.operationChangePinSecurityToken(adminPin, newPin);
+ }
+
+ @Override
+ public void onClickUnlockTokenImpossible() {
+ view.showErrorCannotUnlock();
+ }
+
+ private LoaderCallbacks loaderCallbacks = new LoaderCallbacks() {
+ @Override
+ public Loader onCreateLoader(int id, Bundle args) {
+ switch (id) {
+ case LOADER_LOCAL:
+ return new LocalKeyLookupLoader(context, tokenInfo.getFingerprints());
+ case LOADER_URI:
+ return new UriKeyRetrievalLoader(context, tokenInfo.getUrl(), tokenInfo.getFingerprints());
+ case LOADER_KEYSERVER:
+ return new KeyserverRetrievalLoader(context, tokenInfo.getFingerprints());
+ case LOADER_CONTENT_URI:
+ return new ContentUriRetrievalLoader(context, tokenInfo.getFingerprints(),
+ args.getParcelable(ARG_CONTENT_URI));
+ }
+ throw new IllegalArgumentException("called with unknown loader id!");
+ }
+
+ @Override
+ public void onLoadFinished(Loader loader, KeyRetrievalResult data) {
+ if (view == null) {
+ return;
+ }
+
+ switch (loader.getId()) {
+ case LOADER_LOCAL: {
+ searchedLocally = true;
+ break;
+ }
+ case LOADER_URI: {
+ searchedAtUri = true;
+ break;
+ }
+ case LOADER_KEYSERVER: {
+ searchedKeyservers = true;
+ break;
+ }
+ case LOADER_CONTENT_URI: {
+ // nothing to do here
+ break;
+ }
+ default: {
+ throw new IllegalArgumentException("called with unknown loader id!");
+ }
+ }
+
+ log.add(data.getOperationResult(), 0);
+
+ if (data.isSuccess()) {
+ processResult(data);
+ } else {
+ continueSearchAfterError();
+ }
+ }
+
+ @Override
+ public void onLoaderReset(Loader loader) {
+
+ }
+ };
+
+ private void processResult(KeyRetrievalResult result) {
+ view.statusLineOk();
+
+ byte[] importKeyData = result.getKeyData();
+ Long masterKeyId = result.getMasterKeyId();
+ if (importKeyData != null && masterKeyId != null) {
+ view.showActionImport();
+ this.importKeyData = importKeyData;
+ this.masterKeyId = masterKeyId;
+ return;
+ }
+
+ if (masterKeyId != null) {
+ this.masterKeyId = masterKeyId;
+ view.statusLineAdd(StatusLine.TOKEN_CHECK);
+
+ promoteKeyWithTokenInfo(masterKeyId);
+ return;
+ }
+
+ throw new IllegalArgumentException("Method can only be called with successful result!");
+ }
+
+ private void promoteKeyWithTokenInfo(Long masterKeyId) {
+ view.operationPromote(masterKeyId, tokenInfo.getAid(), tokenInfo.getFingerprints());
+ }
+
+ @Override
+ public void onClickImport() {
+ view.statusLineAdd(StatusLine.IMPORT);
+ view.hideAction();
+ view.operationImportKey(importKeyData);
+ }
+
+ @Override
+ public void onImportSuccess(OperationResult result) {
+ log.add(result, 0);
+
+ view.statusLineOk();
+ view.statusLineAdd(StatusLine.TOKEN_PROMOTE);
+ promoteKeyWithTokenInfo(masterKeyId);
+ }
+
+ @Override
+ public void onImportError(OperationResult result) {
+ log.add(result, 0);
+
+ view.statusLineError();
+ }
+
+ @Override
+ public void onPromoteSuccess(OperationResult result) {
+ log.add(result, 0);
+
+ view.statusLineOk();
+ view.showActionViewKey();
+ }
+
+ @Override
+ public void onPromoteError(OperationResult result) {
+ log.add(result, 0);
+
+ view.statusLineError();
+ }
+
+ @Override
+ public void onClickRetry() {
+ resetAndContinueSearch();
+ }
+
+ @Override
+ public void onClickViewKey() {
+ view.finishAndShowKey(masterKeyId);
+ }
+
+ @Override
+ public void onClickResetToken() {
+ view.showConfirmResetDialog();
+ }
+
+ @Override
+ public void onClickConfirmReset() {
+ view.operationResetSecurityToken();
+ }
+
+ @Override
+ public void onSecurityTokenResetSuccess(SecurityTokenInfo tokenInfo) {
+ this.tokenInfo = tokenInfo;
+ resetAndContinueSearch();
+ }
+
+ @Override
+ public void onSecurityTokenResetCanceled(SecurityTokenInfo tokenInfo) {
+ if (tokenInfo != null) {
+ this.tokenInfo = tokenInfo;
+ resetAndContinueSearch();
+ }
+ }
+
+ @Override
+ public void onClickSetupToken() {
+ view.startCreateKeyForToken(tokenInfo);
+ }
+
+ @Override
+ public void onSecurityTokenChangePinSuccess(SecurityTokenInfo tokenInfo) {
+ this.tokenInfo = tokenInfo;
+ resetAndContinueSearch();
+ }
+
+ @Override
+ public void onSecurityTokenChangePinCanceled(SecurityTokenInfo tokenInfo) {
+ if (tokenInfo != null) {
+ this.tokenInfo = tokenInfo;
+ resetAndContinueSearch();
+ }
+ }
+
+ @Override
+ public void onClickLoadFile() {
+ view.showFileSelectDialog();
+ }
+
+ @Override
+ public void onFileSelected(Uri contentUri) {
+ boolean hasReadPermission = PermissionsUtil.checkReadPermission(context, contentUri);
+ if (!hasReadPermission) {
+ selectedContentUri = contentUri;
+ view.requestStoragePermission();
+ return;
+ }
+
+ startLoadingFile(contentUri);
+ }
+
+ private void startLoadingFile(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);
+ }
+
+ @Override
+ public void onStoragePermissionGranted() {
+ Uri contentUri = selectedContentUri;
+ selectedContentUri = null;
+ startLoadingFile(contentUri);
+ }
+
+ @Override
+ public void onStoragePermissionDenied() {
+ selectedContentUri = null;
+ }
+
+ @Override
+ public void onMenuClickViewLog() {
+ OperationResult result = new GenericOperationResult(GenericOperationResult.RESULT_OK, log);
+ view.showDisplayLogActivity(result);
+ }
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/token/PublicKeyRetrievalLoader.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/token/PublicKeyRetrievalLoader.java
new file mode 100644
index 000000000..fb4d0d35c
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/token/PublicKeyRetrievalLoader.java
@@ -0,0 +1,359 @@
+/*
+ * Copyright (C) 2017 Vincent Breitmoser
+ *
+ * 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 .
+ */
+
+package org.sufficientlysecure.keychain.ui.token;
+
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+
+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.text.TextUtils;
+import android.util.Log;
+
+import com.google.auto.value.AutoValue;
+import okhttp3.Call;
+import okhttp3.HttpUrl;
+import okhttp3.Request.Builder;
+import okhttp3.Response;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.keyimport.HkpKeyserverAddress;
+import org.sufficientlysecure.keychain.keyimport.HkpKeyserverClient;
+import org.sufficientlysecure.keychain.keyimport.KeyserverClient.QueryFailedException;
+import org.sufficientlysecure.keychain.keyimport.KeyserverClient.QueryNotFoundException;
+import org.sufficientlysecure.keychain.network.OkHttpClientFactory;
+import org.sufficientlysecure.keychain.operations.results.GenericOperationResult;
+import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
+import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
+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;
+import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
+import org.sufficientlysecure.keychain.ui.token.PublicKeyRetrievalLoader.KeyRetrievalResult;
+import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
+import org.sufficientlysecure.keychain.util.ParcelableProxy;
+import org.sufficientlysecure.keychain.util.Preferences;
+
+
+public abstract class PublicKeyRetrievalLoader extends AsyncTaskLoader {
+ private static final long MIN_OPERATION_TIME_MILLIS = 1500;
+
+
+ private KeyRetrievalResult cachedResult;
+
+ protected final List fingerprints;
+
+
+ private PublicKeyRetrievalLoader(Context context, List fingerprints) {
+ super(context);
+
+ this.fingerprints = fingerprints;
+ }
+
+ @Override
+ protected KeyRetrievalResult onLoadInBackground() {
+ long startTime = SystemClock.elapsedRealtime();
+
+ KeyRetrievalResult keyRetrievalResult = super.onLoadInBackground();
+
+ try {
+ long elapsedTime = SystemClock.elapsedRealtime() - startTime;
+ if (elapsedTime < MIN_OPERATION_TIME_MILLIS) {
+ Thread.sleep(MIN_OPERATION_TIME_MILLIS - elapsedTime);
+ }
+ } catch (InterruptedException e) {
+ // nvm
+ }
+
+ return keyRetrievalResult;
+ }
+
+ static class LocalKeyLookupLoader extends PublicKeyRetrievalLoader {
+ private final KeyRepository keyRepository;
+
+ LocalKeyLookupLoader(Context context, List fingerprints) {
+ super(context, fingerprints);
+
+ this.keyRepository = KeyRepository.createDatabaseInteractor(context);
+ }
+
+ @Override
+ public KeyRetrievalResult loadInBackground() {
+ OperationLog log = new OperationLog();
+ log.add(LogType.MSG_RET_LOCAL_START, 0);
+
+ for (byte[] fingerprint : fingerprints) {
+ long keyId = KeyFormattingUtils.getKeyIdFromFingerprint(fingerprint);
+ if (keyId == 0L) {
+ continue;
+ }
+
+ log.add(LogType.MSG_RET_LOCAL_SEARCH, 1, KeyFormattingUtils.convertKeyIdToHex(keyId));
+ try {
+ CachedPublicKeyRing cachedPublicKeyRing = keyRepository.getCachedPublicKeyRing(
+ KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(keyId)
+ );
+
+ long masterKeyId = cachedPublicKeyRing.getMasterKeyId();
+ // TODO check fingerprint
+ // if (!Arrays.equals(fingerprints, cachedPublicKeyRing.getFingerprint())) {
+ // log.add(LogType.MSG_RET_LOCAL_FP_MISMATCH, 1);
+ // return KeyRetrievalResult.createWithError(log);
+ // } else {
+ // log.add(LogType.MSG_RET_LOCAL_FP_MATCH, 1);
+ // }
+
+ switch (cachedPublicKeyRing.getSecretKeyType(keyId)) {
+ case PASSPHRASE:
+ case PASSPHRASE_EMPTY: {
+ log.add(LogType.MSG_RET_LOCAL_SECRET, 1);
+ log.add(LogType.MSG_RET_LOCAL_OK, 1);
+ return KeyRetrievalResult.createWithMasterKeyIdAndSecretAvailable(log, masterKeyId);
+ }
+
+ case GNU_DUMMY:
+ case DIVERT_TO_CARD:
+ case UNAVAILABLE: {
+ log.add(LogType.MSG_RET_LOCAL_OK, 1);
+ return KeyRetrievalResult.createWithMasterKeyId(log, masterKeyId);
+ }
+
+ default: {
+ throw new IllegalStateException("Unhandled SecretKeyType!");
+ }
+ }
+ } catch (PgpKeyNotFoundException | NotFoundException e) {
+ log.add(LogType.MSG_RET_LOCAL_NOT_FOUND, 2);
+ }
+ }
+
+ log.add(LogType.MSG_RET_LOCAL_NONE_FOUND, 1);
+ return KeyRetrievalResult.createWithError(log);
+ }
+ }
+
+ static class UriKeyRetrievalLoader extends PublicKeyRetrievalLoader {
+ private final String tokenUri;
+
+ UriKeyRetrievalLoader(Context context, String tokenUri, List fingerprints) {
+ super(context, fingerprints);
+
+ this.tokenUri = tokenUri;
+ }
+
+ @Override
+ public KeyRetrievalResult loadInBackground() {
+ OperationLog log = new OperationLog();
+
+ try {
+ log.add(LogType.MSG_RET_URI_START, 0);
+ if (TextUtils.isEmpty(tokenUri)) {
+ log.add(LogType.MSG_RET_URI_NULL, 1);
+ return KeyRetrievalResult.createWithError(log);
+ }
+
+ log.add(LogType.MSG_RET_URI_FETCHING, 1, tokenUri);
+
+ HttpUrl httpUrl = HttpUrl.parse(tokenUri);
+ if (httpUrl == null) {
+ log.add(LogType.MSG_RET_URI_ERROR_PARSE, 1);
+ return KeyRetrievalResult.createWithError(log);
+ }
+
+ Call call = OkHttpClientFactory.getSimpleClient().newCall(new Builder().url(httpUrl).build());
+ Response execute = call.execute();
+ if (!execute.isSuccessful()) {
+ log.add(LogType.MSG_RET_URI_ERROR_FETCH, 1);
+ }
+
+ IteratorWithIOThrow uncachedKeyRingIterator = UncachedKeyRing.fromStream(
+ execute.body().byteStream());
+ while (uncachedKeyRingIterator.hasNext()) {
+ UncachedKeyRing keyRing = uncachedKeyRingIterator.next();
+ log.add(LogType.MSG_RET_URI_TEST, 1, KeyFormattingUtils.convertKeyIdToHex(keyRing.getMasterKeyId()));
+ if (keyRing.containsKeyWithAnyFingerprint(fingerprints)) {
+ log.add(LogType.MSG_RET_URI_OK, 1);
+ return KeyRetrievalResult.createWithKeyringdata(log, keyRing.getMasterKeyId(), keyRing.getEncoded());
+ }
+ }
+
+ log.add(LogType.MSG_RET_URI_ERROR_NO_MATCH, 1);
+ } catch (IOException e) {
+ log.add(LogType.MSG_RET_URI_ERROR_FETCH, 1);
+ Log.e(Constants.TAG, "error retrieving key from uri", e);
+ }
+
+ return KeyRetrievalResult.createWithError(log);
+ }
+ }
+
+ static class KeyserverRetrievalLoader extends PublicKeyRetrievalLoader {
+ KeyserverRetrievalLoader(Context context, List fingerprints) {
+ super(context, fingerprints);
+ }
+
+ @Override
+ public KeyRetrievalResult loadInBackground() {
+ OperationLog log = new OperationLog();
+
+ HkpKeyserverAddress preferredKeyserver = Preferences.getPreferences(getContext()).getPreferredKeyserver();
+ ParcelableProxy parcelableProxy = Preferences.getPreferences(getContext()).getParcelableProxy();
+
+ HkpKeyserverClient keyserverClient = HkpKeyserverClient.fromHkpKeyserverAddress(preferredKeyserver);
+
+ try {
+ log.add(LogType.MSG_RET_KS_START, 0);
+
+ String keyString = keyserverClient.get(
+ "0x" + KeyFormattingUtils.convertFingerprintToHex(fingerprints.get(0)), parcelableProxy);
+ UncachedKeyRing keyRing = UncachedKeyRing.decodeFromData(keyString.getBytes());
+
+ if (!keyRing.containsKeyWithAnyFingerprint(fingerprints)) {
+ log.add(LogType.MSG_RET_KS_FP_MISMATCH, 1);
+ return KeyRetrievalResult.createWithError(log);
+ } else {
+ log.add(LogType.MSG_RET_KS_FP_MATCH, 1);
+ }
+
+ log.add(LogType.MSG_RET_KS_OK, 1);
+ return KeyRetrievalResult.createWithKeyringdata(log, keyRing.getMasterKeyId(), keyRing.getEncoded());
+ } catch (QueryNotFoundException e) {
+ log.add(LogType.MSG_RET_KS_ERROR_NOT_FOUND, 1);
+ } catch (QueryFailedException | IOException | PgpGeneralException e) {
+ log.add(LogType.MSG_RET_KS_ERROR, 1);
+ Log.e(Constants.TAG, "error retrieving key from keyserver", e);
+ }
+
+ return KeyRetrievalResult.createWithError(log);
+ }
+ }
+
+ static class ContentUriRetrievalLoader extends PublicKeyRetrievalLoader {
+ private final ContentResolver contentResolver;
+ private final Uri uri;
+
+ ContentUriRetrievalLoader(Context context, List fingerprints, Uri uri) {
+ super(context, fingerprints);
+
+ this.uri = uri;
+ this.contentResolver = context.getContentResolver();
+ }
+
+ @Override
+ public KeyRetrievalResult loadInBackground() {
+ OperationLog log = new OperationLog();
+
+ try {
+ log.add(LogType.MSG_RET_CURI_START, 0);
+
+ log.add(LogType.MSG_RET_CURI_OPEN, 1, uri.toString());
+ InputStream is = contentResolver.openInputStream(uri);
+ if (is == null) {
+ log.add(LogType.MSG_RET_CURI_ERROR_NOT_FOUND, 1);
+ return KeyRetrievalResult.createWithError(log);
+ }
+
+ IteratorWithIOThrow uncachedKeyRingIterator = UncachedKeyRing.fromStream(is);
+ while (uncachedKeyRingIterator.hasNext()) {
+ UncachedKeyRing keyRing = uncachedKeyRingIterator.next();
+ log.add(LogType.MSG_RET_CURI_FOUND, 1, KeyFormattingUtils.convertKeyIdToHex(keyRing.getMasterKeyId()));
+ if (keyRing.containsKeyWithAnyFingerprint(fingerprints)) {
+ log.add(LogType.MSG_RET_CURI_OK, 1);
+ return KeyRetrievalResult.createWithKeyringdata(log, keyRing.getMasterKeyId(), keyRing.getEncoded());
+ } else {
+ log.add(LogType.MSG_RET_CURI_MISMATCH, 1);
+ }
+ }
+ log.add(LogType.MSG_RET_CURI_ERROR_NO_MATCH, 1);
+ } catch (IOException e) {
+ Log.e(Constants.TAG, "error reading keyring from file", e);
+ log.add(LogType.MSG_RET_CURI_ERROR_IO, 1);
+ }
+
+ return KeyRetrievalResult.createWithError(log);
+ }
+ }
+
+ @Override
+ public void deliverResult(KeyRetrievalResult result) {
+ cachedResult = result;
+
+ if (isStarted()) {
+ super.deliverResult(result);
+ }
+ }
+
+ @Override
+ protected void onStartLoading() {
+ if (cachedResult != null) {
+ deliverResult(cachedResult);
+ }
+
+ if (takeContentChanged() || cachedResult == null) {
+ forceLoad();
+ }
+ }
+
+ @AutoValue
+ static abstract class KeyRetrievalResult {
+ abstract GenericOperationResult getOperationResult();
+
+ @Nullable
+ abstract Long getMasterKeyId();
+ @Nullable
+ abstract byte[] getKeyData();
+ abstract boolean isSecretKeyAvailable();
+
+ boolean isSuccess() {
+ return getMasterKeyId() != null || getKeyData() != null;
+ }
+
+ static KeyRetrievalResult createWithError(OperationLog log) {
+ return new AutoValue_PublicKeyRetrievalLoader_KeyRetrievalResult(
+ new GenericOperationResult(GenericOperationResult.RESULT_ERROR, log),
+ null, null, false);
+ }
+
+ static KeyRetrievalResult createWithKeyringdata(OperationLog log, long masterKeyId, byte[] keyringData) {
+ return new AutoValue_PublicKeyRetrievalLoader_KeyRetrievalResult(
+ new GenericOperationResult(GenericOperationResult.RESULT_OK, log),
+ masterKeyId, keyringData, false);
+ }
+
+ static KeyRetrievalResult createWithMasterKeyIdAndSecretAvailable(OperationLog log, long masterKeyId) {
+ return new AutoValue_PublicKeyRetrievalLoader_KeyRetrievalResult(
+ new GenericOperationResult(GenericOperationResult.RESULT_OK, log),
+ masterKeyId, null, true);
+ }
+
+ static KeyRetrievalResult createWithMasterKeyId(OperationLog log, long masterKeyId) {
+ return new AutoValue_PublicKeyRetrievalLoader_KeyRetrievalResult(
+ new GenericOperationResult(GenericOperationResult.RESULT_OK, log),
+ masterKeyId, null, false);
+ }
+ }
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/PermissionsUtil.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/PermissionsUtil.java
index 435240859..0a004746f 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/PermissionsUtil.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/PermissionsUtil.java
@@ -62,7 +62,7 @@ public class PermissionsUtil {
return result;
}
- private static boolean checkReadPermission(Context context, Uri uri) {
+ public static boolean checkReadPermission(Context context, Uri uri) {
if (!ContentResolver.SCHEME_FILE.equals(uri.getScheme())) {
return true;
}
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/ic_bomb_24dp.png b/OpenKeychain/src/main/res/drawable-hdpi/ic_bomb_24dp.png
new file mode 100644
index 000000000..0f05efecb
Binary files /dev/null and b/OpenKeychain/src/main/res/drawable-hdpi/ic_bomb_24dp.png differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/ic_bomb_24dp.png b/OpenKeychain/src/main/res/drawable-mdpi/ic_bomb_24dp.png
new file mode 100644
index 000000000..b1e3b159f
Binary files /dev/null and b/OpenKeychain/src/main/res/drawable-mdpi/ic_bomb_24dp.png differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/ic_bomb_24dp.png b/OpenKeychain/src/main/res/drawable-xhdpi/ic_bomb_24dp.png
new file mode 100644
index 000000000..5eee46118
Binary files /dev/null and b/OpenKeychain/src/main/res/drawable-xhdpi/ic_bomb_24dp.png differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_bomb_24dp.png b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_bomb_24dp.png
new file mode 100644
index 000000000..a09970b17
Binary files /dev/null and b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_bomb_24dp.png differ
diff --git a/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_bomb_24dp.png b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_bomb_24dp.png
new file mode 100644
index 000000000..1e2a035c1
Binary files /dev/null and b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_bomb_24dp.png differ
diff --git a/OpenKeychain/src/main/res/layout/admin_pin_dialog.xml b/OpenKeychain/src/main/res/layout/admin_pin_dialog.xml
new file mode 100644
index 000000000..c2909cf4c
--- /dev/null
+++ b/OpenKeychain/src/main/res/layout/admin_pin_dialog.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OpenKeychain/src/main/res/layout/create_security_token_import_fragment.xml b/OpenKeychain/src/main/res/layout/create_security_token_import_fragment.xml
new file mode 100644
index 000000000..9a35ad763
--- /dev/null
+++ b/OpenKeychain/src/main/res/layout/create_security_token_import_fragment.xml
@@ -0,0 +1,377 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OpenKeychain/src/main/res/layout/create_security_token_import_reset_fragment.xml b/OpenKeychain/src/main/res/layout/create_security_token_import_reset_fragment.xml
index 8fd7b1479..6fbda4468 100644
--- a/OpenKeychain/src/main/res/layout/create_security_token_import_reset_fragment.xml
+++ b/OpenKeychain/src/main/res/layout/create_security_token_import_reset_fragment.xml
@@ -80,14 +80,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
- android:text="@string/security_token_import_radio" />
-
-
+ android:text="@string/security_token_radio_use_existing" />
+ android:text="@string/security_token_radio_reset" />
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/OpenKeychain/src/main/res/layout/fake_dialog.xml b/OpenKeychain/src/main/res/layout/fake_dialog.xml
new file mode 100644
index 000000000..19bfb96a6
--- /dev/null
+++ b/OpenKeychain/src/main/res/layout/fake_dialog.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OpenKeychain/src/main/res/layout/security_token_operation_activity_toolable_view_animator.xml b/OpenKeychain/src/main/res/layout/security_token_operation_activity_toolable_view_animator.xml
index 35ddf398b..e3a0ed6b0 100644
--- a/OpenKeychain/src/main/res/layout/security_token_operation_activity_toolable_view_animator.xml
+++ b/OpenKeychain/src/main/res/layout/security_token_operation_activity_toolable_view_animator.xml
@@ -10,7 +10,7 @@
android:measureAllChildren="false"
android:minHeight="?listPreferredItemHeightSmall"
android:outAnimation="@anim/fade_out"
- custom:initialView="3"
+ custom:initialView="4"
tools:showIn="@layout/security_token_operation_activity">
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/OpenKeychain/src/main/res/layout/status_indicator_line.xml b/OpenKeychain/src/main/res/layout/status_indicator_line.xml
new file mode 100644
index 000000000..5d8ab1d9f
--- /dev/null
+++ b/OpenKeychain/src/main/res/layout/status_indicator_line.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OpenKeychain/src/main/res/layout/view_key_security_token.xml b/OpenKeychain/src/main/res/layout/view_key_security_token.xml
deleted file mode 100644
index 27fa3d53c..000000000
--- a/OpenKeychain/src/main/res/layout/view_key_security_token.xml
+++ /dev/null
@@ -1,103 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/OpenKeychain/src/main/res/menu/token_debug.xml b/OpenKeychain/src/main/res/menu/token_debug.xml
new file mode 100644
index 000000000..4f7c8187f
--- /dev/null
+++ b/OpenKeychain/src/main/res/menu/token_debug.xml
@@ -0,0 +1,19 @@
+
+
\ No newline at end of file
diff --git a/OpenKeychain/src/main/res/menu/token_setup.xml b/OpenKeychain/src/main/res/menu/token_setup.xml
new file mode 100644
index 000000000..041ae63a8
--- /dev/null
+++ b/OpenKeychain/src/main/res/menu/token_setup.xml
@@ -0,0 +1,14 @@
+
+
\ No newline at end of file
diff --git a/OpenKeychain/src/main/res/values-de/strings.xml b/OpenKeychain/src/main/res/values-de/strings.xml
index 1a9eee7c6..094154811 100644
--- a/OpenKeychain/src/main/res/values-de/strings.xml
+++ b/OpenKeychain/src/main/res/values-de/strings.xml
@@ -1458,9 +1458,9 @@
Halte den Security-Token gegen die Rückseite deines Geräts.
Dieses Sicherheits-Token enthält bereits einen Schlüssel. Um es zu benutzen, benötigen wir zusätzliche Schlüsselinformationen. Diese Informationen können auf einem Keyserver gesucht oder aus einer Datei importiert werden.
Zurücksetzen
- Suche Schlüsselinformation auf Schlüsselserver
+ Suche Schlüsselinformation auf Schlüsselserver
Importiere Schlüsselinformation von Datei
- Security-Token zurücksetzen
+ Security-Token zurücksetzen
Das Zurücksetzen des Security-Tokens zerstört die darauf gespeicherten Schlüssel vollständig. Mit diesen Schlüssel verschlüsselte Nachrichten/Dateien können danach nicht mehr entschlüsselt werden!
Es ist ein anderer Schlüssel auf dem Security-Token gespeichert!
Fehler: %s
diff --git a/OpenKeychain/src/main/res/values-es/strings.xml b/OpenKeychain/src/main/res/values-es/strings.xml
index 113155569..db40c1c89 100644
--- a/OpenKeychain/src/main/res/values-es/strings.xml
+++ b/OpenKeychain/src/main/res/values-es/strings.xml
@@ -1475,9 +1475,9 @@
Sostenga el token de seguridad contra la parte trasera de su dispositivo.
Este token de seguridad ya contiene una clave. Para usarla, necesitamos información adicional de la clave. Esta información se puede buscar en un servidor de claves o se puede importar de un fichero.
Reiniciar
- Buscar información de la clave en el servidor de claves
+ Buscar información de la clave en el servidor de claves
Importar información de la clave desde un fichero
- Reinicializar token de seguridad
+ Reinicializar token de seguridad
Reinicializar el token de seguridad destruirá por completo las claves que almacene. ¡Acto seguido no podrá descifrar los mensajes/ficheros cifrados con esta clave!
¡La clave almacenada en el token de seguridad es diferente!
Error: %s
diff --git a/OpenKeychain/src/main/res/values-eu/strings.xml b/OpenKeychain/src/main/res/values-eu/strings.xml
index b23e8d04d..e0573b5a4 100644
--- a/OpenKeychain/src/main/res/values-eu/strings.xml
+++ b/OpenKeychain/src/main/res/values-eu/strings.xml
@@ -1291,7 +1291,7 @@
Serie Zbk: %s
]]>
Berrezarri
- Berrezarri Segurtasun Lekukoa
+ Berrezarri Segurtasun Lekukoa
Akatsa: %s
- PIN okerra!\n%d saiakera gelditzen da.
diff --git a/OpenKeychain/src/main/res/values-fr/strings.xml b/OpenKeychain/src/main/res/values-fr/strings.xml
index 8b131910e..0e6d57bf4 100644
--- a/OpenKeychain/src/main/res/values-fr/strings.xml
+++ b/OpenKeychain/src/main/res/values-fr/strings.xml
@@ -1475,9 +1475,9 @@
Tenir le jeton de sécurité contre le dos de votre appareil.
Ce jeton de sécurité contient déjà une clé. Des informations supplémentaires sont requises pour l\'utiliser. Elles peuvent être recherchées sur un serveur de clés ou importer d\'un fichier.
Réinitialiser
- Rechercher les informations de la clé sur le serveur de clés
+ Rechercher les informations de la clé sur le serveur de clés
Importer les informations de la clé d\'un fichier
- Réinitialiser le jeton de sécurité
+ Réinitialiser le jeton de sécurité
La réinitialisation du jeton de sécurité détruit complètement les clés qu\'il contient. Par la suite, vous ne pourrez plus déchiffrer les messages/les fichiers chiffrés avec cette clé !
Une clé différente est stockée sur le jeton de sécurité !
Erreur : %s
diff --git a/OpenKeychain/src/main/res/values-ja/strings.xml b/OpenKeychain/src/main/res/values-ja/strings.xml
index 11ff59774..d770ef0f1 100644
--- a/OpenKeychain/src/main/res/values-ja/strings.xml
+++ b/OpenKeychain/src/main/res/values-ja/strings.xml
@@ -1446,9 +1446,9 @@
あなたのデバイスの背面にセキュリティトークンを保持してください。
このセキュリティトークンにはすでに鍵が含まれています。 使用するには、追加の鍵の情報が必要です。 この情報は、鍵サーバ上で検索することや、ファイルからインポートすることができます。
リセット
- 鍵サーバ上で鍵情報を検索
+ 鍵サーバ上で鍵情報を検索
ファイルから鍵情報をインポート
- セキュリティトークンのリセット
+ セキュリティトークンのリセット
セキュリティトークンをリセットすると、その上の鍵を完全に破壊します。その後は、この鍵で暗号化されたメッセージ/ファイルを復号化することができなくなります!
違う鍵がセキュリティトークンに格納されています!
エラー: %s
diff --git a/OpenKeychain/src/main/res/values-nb/strings.xml b/OpenKeychain/src/main/res/values-nb/strings.xml
index 74292615a..73e196728 100644
--- a/OpenKeychain/src/main/res/values-nb/strings.xml
+++ b/OpenKeychain/src/main/res/values-nb/strings.xml
@@ -580,7 +580,7 @@
Innstillinger
Vis
Tilbakestill
- Tilbakestill sikkerhetssymbol
+ Tilbakestill sikkerhetssymbol
Feil: %s
Ukjent feil
Prøv igjen
diff --git a/OpenKeychain/src/main/res/values-nl/strings.xml b/OpenKeychain/src/main/res/values-nl/strings.xml
index 76626cbd6..41e17a55d 100644
--- a/OpenKeychain/src/main/res/values-nl/strings.xml
+++ b/OpenKeychain/src/main/res/values-nl/strings.xml
@@ -1476,9 +1476,9 @@
Hou het Security Token tegen de achterkant van je apparaat.
Dit Security Token bevat al een sleutel. Om deze te gebruiken hebben we extra sleutelinformatie nodig. Deze informatie kan gevonden worden op een sleutelserver, of geïmporteerd uit een bestand.
Reset
- Zoek sleutelinformatie op sleutelserver
+ Zoek sleutelinformatie op sleutelserver
Importeer sleutelinformatie uit bestand
- Reset Security Token
+ Reset Security Token
Het opnieuw instellen van het Security Token maakt alle sleutels op het token ongedaan. Het is daarna niet meer mogelijk om berichten/bestanden te ontsleutelen met deze sleutel!
Andere sleutel opgeslagen op Security Token!
Error: %s
diff --git a/OpenKeychain/src/main/res/values-pt-rBR/strings.xml b/OpenKeychain/src/main/res/values-pt-rBR/strings.xml
index baa802fbf..b9298194a 100644
--- a/OpenKeychain/src/main/res/values-pt-rBR/strings.xml
+++ b/OpenKeychain/src/main/res/values-pt-rBR/strings.xml
@@ -1390,7 +1390,7 @@
O Token de Segurança coincide, parcialmente associado à chave
Segure o Token de Segurança contra as costas do seu dispositivo.
Redefinir
- Redefinir Token de Segurança
+ Redefinir Token de Segurança
Resetar o Token de Segurança destrói completamente as chaves nele. Após isto, você não será capaz de decriptar mensagens/arquivos encruiptados com estas chaves!
Uma chave diferente está armazenada no Token de Segurança!
Erro: %s
diff --git a/OpenKeychain/src/main/res/values-ru/strings.xml b/OpenKeychain/src/main/res/values-ru/strings.xml
index 34106c125..b1b917f5f 100644
--- a/OpenKeychain/src/main/res/values-ru/strings.xml
+++ b/OpenKeychain/src/main/res/values-ru/strings.xml
@@ -1544,9 +1544,9 @@
Держите токен безопасности возле задней части вашего устройства.
Этот токен безопасности уже содержит ключ. Чтобы его использовать, нужна дополнительная информация о ключе. Её можно найти на сервере ключей или импортировать из файла.
Сброс
- Найти информацию о ключе на сервере ключей
+ Найти информацию о ключе на сервере ключей
Импортировать информацию о ключе из файла
- Сбросить токен безопасности
+ Сбросить токен безопасности
Сброс токена безопасности полностью уничтожает ключи на нём. После этого вы не сможете расшифровать сообщения или файлы, зашифрованные с помощью данных ключей!
На токене безопасности хранится другой ключ!
Ошибка: %s
diff --git a/OpenKeychain/src/main/res/values-uk/strings.xml b/OpenKeychain/src/main/res/values-uk/strings.xml
index 3a74d54c3..25b82089b 100644
--- a/OpenKeychain/src/main/res/values-uk/strings.xml
+++ b/OpenKeychain/src/main/res/values-uk/strings.xml
@@ -1512,9 +1512,9 @@
Тримайте маркер безпеки навпроти зворотнього боку Вашого пристрою.
Маркер безпеки вже містить ключ. Щоб використовувати його, нам потрібна додаткова інформація. Ця інформація може бути знайдена на сервері ключів чи імпортована з файлу.
Скинути
- Пошук інформації про ключ на сервері ключів
+ Пошук інформації про ключ на сервері ключів
Імпорт інформації про ключ з файлу
- Очистити маркер безпеки
+ Очистити маркер безпеки
Скидання маркера безпеки повністю знищить всі ключі на ньому. Ви не зможете розшифрувати повідомлення/файли зашифровані цим ключем.
Різні ключі збережено на маркері безпеки!
Помилка: %s
diff --git a/OpenKeychain/src/main/res/values-zh/strings.xml b/OpenKeychain/src/main/res/values-zh/strings.xml
index a035f73cd..2f709891d 100644
--- a/OpenKeychain/src/main/res/values-zh/strings.xml
+++ b/OpenKeychain/src/main/res/values-zh/strings.xml
@@ -1359,7 +1359,7 @@
安全信息匹配并且已部分绑定到密钥
保持安全令牌在您的手机背部
重置
- 重置安全令牌
+ 重置安全令牌
重置安全令牌将完全摧毁其内部的密钥。之后您将无法使用该密钥加解密消息或文件!
安全令牌中存有不同的密钥!
错误: %s
diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml
index efa724e27..47d2ed937 100644
--- a/OpenKeychain/src/main/res/values/strings.xml
+++ b/OpenKeychain/src/main/res/values/strings.xml
@@ -1593,11 +1593,10 @@
"Security Token matches, can be bound to key"
"Security Token matches, partly bound to key"
"Hold Security Token against the back of your device."
- "This Security Token already contains a key. To use it, we need additional key information. This information can be searched for on a keyserver or imported from a file."
+ "This Security Token already contains a key."
"Reset"
- "Search key information on keyserver"
- "Import key information from file"
- "Reset Security Token"
+ "Use existing key"
+ "Reset token"
"Resetting the Security Token completely destroys the keys on it. Afterwards, you will not be able to decrypt messages/files encrypted with this key!"
Different key stored on Security Token!
"Error: %s"
@@ -1919,4 +1918,77 @@
"Make sure you are on the same network, then scan again."
"Make sure you are on the "%s" network, then scan again."
+ Checking key status…
+ Searching in key list…
+ Searching at token Uri…
+ Searching on keyservers…
+ Importing key…
+ Setting up key…
+ Checking key setup…
+ Reading file…
+
+ Reset Security Token?
+ This will irrecoverably delete the key stored on this Security Token. You will no longer be able to use this key for decryption! Are you sure?
+ Reset
+ Gathering information for Security Token…
+ Key not found!
+ Key found!
+ Ready for use!
+ Token is empty
+ Retry Search
+ Load from File
+ Reset Security Token
+ Import
+ View Key
+ "Set up with new key"
+ Unlock using admin pin
+ "No unlock attempts left"
+
+ - "1 attempt left"
+ - "%d attempts left"
+
+ Too many reset attempts. Token is locked irrecoverably!
+
+ Change Pin
+ View Log
+
+ Unlock
+ Admin Pin
+ New Pin
+ New Pin (repeat)
+ Admin pin must be 8 characters or longer!
+ New pin must be 6 characters or longer!
+ Doesn\'t match pin!
+
+ "Error reading data!"
+ "No matching key found"
+ "Couldn't open file!"
+ "Found key: %s"
+ "Key doesn't match"
+ "Key found"
+ "Opening Uri: %s"
+ "Loading key from file or document…"
+ "Unknown error searching for key!"
+ "Key not found"
+ "Retrieved key's fingerprint matches"
+ "Retrieved key's fingerprint doesn't match!"
+ "Key found"
+ "Looking for key on keyservers…"
+ "Searching for key: %s"
+ "Local key's fingerprint matches"
+ "Local key's fingerprint doesn't match!"
+ "Key not found"
+ "No matching key found"
+ "Key found"
+ "Local key contains secret key material"
+ "Looking for key in local key list…"
+ "Unknown error fetching Uri!"
+ "Token Uri is malformed!"
+ "No matching key found at Uri"
+ "Fetching Uri: %s"
+ "Key found"
+ "Looking for key at token Uri…"
+ "No Uri saved on Security Token"
+ "Checking if found key matches: %s"
+
diff --git a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/operations/PromoteKeyOperationTest.java b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/operations/PromoteKeyOperationTest.java
index d9b3f591b..e116d26ce 100644
--- a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/operations/PromoteKeyOperationTest.java
+++ b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/operations/PromoteKeyOperationTest.java
@@ -20,6 +20,7 @@ package org.sufficientlysecure.keychain.operations;
import java.io.PrintStream;
import java.security.Security;
+import java.util.Arrays;
import java.util.Iterator;
import org.bouncycastle.bcpg.sig.KeyFlags;
@@ -164,9 +165,8 @@ public class PromoteKeyOperationTest {
long keyId = KeyringTestingHelper.getSubkeyId(mStaticRing, 1);
PromoteKeyResult result = op.execute(
- PromoteKeyringParcel.createPromoteKeyringParcel(mStaticRing.getMasterKeyId(), aid, new long[] {
- keyId
- }), null);
+ PromoteKeyringParcel.createPromoteKeyringParcel(mStaticRing.getMasterKeyId(), aid,
+ Arrays.asList(mStaticRing.getPublicKey(keyId).getFingerprint())), null);
Assert.assertTrue("promotion must succeed", result.success());
diff --git a/graphics/drawables/ic_bomb.svg b/graphics/drawables/ic_bomb.svg
new file mode 100644
index 000000000..c92329f1f
--- /dev/null
+++ b/graphics/drawables/ic_bomb.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/graphics/update-drawables.sh b/graphics/update-drawables.sh
index 83b4bad47..6e28a57f0 100755
--- a/graphics/update-drawables.sh
+++ b/graphics/update-drawables.sh
@@ -22,7 +22,7 @@ SRC_DIR=./drawables/
#inkscape -w 512 -h 512 -e "$PLAY_DIR/$NAME.png" $NAME.svg
-for NAME in "ic_live_help" "ic_send" "ic_cloud_unknown" "ic_cloud_off" "ic_wifi_lock" "broken_heart" "ic_cloud_search" "ic_action_encrypt_file" "ic_action_encrypt_text" "ic_action_verified_cutout" "ic_action_encrypt_copy" "ic_action_encrypt_paste" "ic_action_encrypt_save" "ic_action_encrypt_share" "status_lock_closed" "status_lock_error" "status_lock_open" "status_signature_expired_cutout" "status_signature_invalid_cutout" "status_signature_revoked_cutout" "status_signature_unknown_cutout" "status_signature_unverified_cutout" "status_signature_verified_cutout" "key_flag_authenticate" "key_flag_certify" "key_flag_encrypt" "key_flag_sign" "yubi_icon" "ic_stat_notify" "status_signature_verified_inner" "link" "octo_link"
+for NAME in "ic_bomb" "ic_live_help" "ic_send" "ic_cloud_unknown" "ic_cloud_off" "ic_wifi_lock" "broken_heart" "ic_cloud_search" "ic_action_encrypt_file" "ic_action_encrypt_text" "ic_action_verified_cutout" "ic_action_encrypt_copy" "ic_action_encrypt_paste" "ic_action_encrypt_save" "ic_action_encrypt_share" "status_lock_closed" "status_lock_error" "status_lock_open" "status_signature_expired_cutout" "status_signature_invalid_cutout" "status_signature_revoked_cutout" "status_signature_unknown_cutout" "status_signature_unverified_cutout" "status_signature_verified_cutout" "key_flag_authenticate" "key_flag_certify" "key_flag_encrypt" "key_flag_sign" "yubi_icon" "ic_stat_notify" "status_signature_verified_inner" "link" "octo_link"
do
echo $NAME
inkscape -w 24 -h 24 -e "$MDPI_DIR/${NAME}_24dp.png" "$SRC_DIR/$NAME.svg"