aboutsummaryrefslogtreecommitdiffstats
path: root/OpenKeychain/src/main/java/org
diff options
context:
space:
mode:
authorJoey Castillo <jose.castillo@gmail.com>2015-05-13 05:33:28 -0400
committerJoey Castillo <jose.castillo@gmail.com>2015-05-13 14:36:30 -0400
commitd21fb7733697a8f947604dbd1d6c608f5b2a21d5 (patch)
tree1ae3348b7047b351a598dd71b8d5e4e8ffed74dd /OpenKeychain/src/main/java/org
parenta0107afd3efefb152b5691b04c6343a4382fcd6a (diff)
downloadopen-keychain-d21fb7733697a8f947604dbd1d6c608f5b2a21d5.tar.gz
open-keychain-d21fb7733697a8f947604dbd1d6c608f5b2a21d5.tar.bz2
open-keychain-d21fb7733697a8f947604dbd1d6c608f5b2a21d5.zip
Moving keytocard process into PgpKeyOperation.
Diffstat (limited to 'OpenKeychain/src/main/java/org')
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/EditKeyOperation.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/NfcKeyToCardOperation.java93
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/NfcKeyToCardResult.java51
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java3
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java105
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java20
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java3
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/RequiredInputParcel.java50
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java77
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcOperationActivity.java72
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAdapter.java24
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseNfcActivity.java26
12 files changed, 236 insertions, 290 deletions
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/EditKeyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/EditKeyOperation.java
index 4072d91c5..152585040 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/EditKeyOperation.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/EditKeyOperation.java
@@ -83,7 +83,7 @@ public class EditKeyOperation extends BaseOperation {
CanonicalizedSecretKeyRing secRing =
mProviderHelper.getCanonicalizedSecretKeyRing(saveParcel.mMasterKeyId);
- modifyResult = keyOperations.modifySecretKeyRing(secRing, cryptoInput, saveParcel);
+ modifyResult = keyOperations.modifySecretKeyRing(secRing, cryptoInput, saveParcel, log);
if (modifyResult.isPending()) {
return modifyResult;
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/NfcKeyToCardOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/NfcKeyToCardOperation.java
deleted file mode 100644
index 36f31e20e..000000000
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/NfcKeyToCardOperation.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2015 Joey Castillo <joey@joeycastillo.com>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package org.sufficientlysecure.keychain.operations;
-
-import android.content.Context;
-
-import org.spongycastle.bcpg.PublicKeyAlgorithmTags;
-import org.sufficientlysecure.keychain.operations.results.NfcKeyToCardResult;
-import org.sufficientlysecure.keychain.operations.results.OperationResult;
-import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey;
-import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing;
-import org.sufficientlysecure.keychain.pgp.Progressable;
-import org.sufficientlysecure.keychain.provider.ProviderHelper;
-import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
-import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
-
-public class NfcKeyToCardOperation extends BaseOperation {
- public NfcKeyToCardOperation(Context context, ProviderHelper providerHelper, Progressable progressable) {
- super(context, providerHelper, progressable);
- }
-
- public NfcKeyToCardResult execute(long subKeyId) {
- OperationResult.OperationLog log = new OperationResult.OperationLog();
- int indent = 0;
- long masterKeyId;
-
- try {
- // fetch the indicated master key id
- masterKeyId = mProviderHelper.getMasterKeyId(subKeyId);
- CanonicalizedSecretKeyRing keyRing =
- mProviderHelper.getCanonicalizedSecretKeyRing(masterKeyId);
-
- log.add(OperationResult.LogType.MSG_KC_SECRET, indent,
- KeyFormattingUtils.convertKeyIdToHex(masterKeyId));
-
- // fetch the specific subkey
- CanonicalizedSecretKey subKey = keyRing.getSecretKey(subKeyId);
-
- // Key algorithm must be RSA
- int algorithm = subKey.getAlgorithm();
- if (algorithm != PublicKeyAlgorithmTags.RSA_ENCRYPT &&
- algorithm != PublicKeyAlgorithmTags.RSA_SIGN &&
- algorithm != PublicKeyAlgorithmTags.RSA_GENERAL) {
- log.add(OperationResult.LogType.MSG_K2C_ERROR_BAD_ALGO, indent + 1);
- return new NfcKeyToCardResult(NfcKeyToCardResult.RESULT_ERROR, log);
- }
-
- // Key size must be 2048
- int keySize = subKey.getBitStrength();
- if (keySize != 2048) {
- log.add(OperationResult.LogType.MSG_K2C_ERROR_BAD_SIZE, indent + 1);
- return new NfcKeyToCardResult(NfcKeyToCardResult.RESULT_ERROR, log);
- }
-
- // Secret key parts must be available
- CanonicalizedSecretKey.SecretKeyType type = subKey.getSecretKeyType();
- if (type == CanonicalizedSecretKey.SecretKeyType.DIVERT_TO_CARD ||
- type == CanonicalizedSecretKey.SecretKeyType.GNU_DUMMY) {
- log.add(OperationResult.LogType.MSG_K2C_ERROR_BAD_STRIPPED, indent + 1);
- return new NfcKeyToCardResult(NfcKeyToCardResult.RESULT_ERROR, log);
- }
-
- if (type == CanonicalizedSecretKey.SecretKeyType.PIN ||
- type == CanonicalizedSecretKey.SecretKeyType.PATTERN ||
- type == CanonicalizedSecretKey.SecretKeyType.PASSPHRASE ||
- type == CanonicalizedSecretKey.SecretKeyType.PASSPHRASE_EMPTY) {
- log.add(OperationResult.LogType.MSG_PSE_PENDING_NFC, indent);
- return new NfcKeyToCardResult(log, RequiredInputParcel
- .createNfcKeyToCardOperation(masterKeyId, subKeyId));
- }
-
- throw new AssertionError("Unhandled SecretKeyType! (should not happen)");
- } catch (ProviderHelper.NotFoundException e) {
- log.add(OperationResult.LogType.MSG_PSE_ERROR_UNLOCK, indent);
- return new NfcKeyToCardResult(NfcKeyToCardResult.RESULT_ERROR, log);
- }
- }
-}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/NfcKeyToCardResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/NfcKeyToCardResult.java
deleted file mode 100644
index 93899bbbe..000000000
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/NfcKeyToCardResult.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2015 Joey Castillo <joey@joeycastillo.com>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package org.sufficientlysecure.keychain.operations.results;
-
-import android.os.Parcel;
-
-import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
-
-public class NfcKeyToCardResult extends InputPendingResult {
- public NfcKeyToCardResult(int result, OperationLog log) {
- super(result, log);
- }
-
- public NfcKeyToCardResult(OperationLog log, RequiredInputParcel requiredInput) {
- super(log, requiredInput);
- }
-
- public NfcKeyToCardResult(Parcel source) {
- super(source);
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- super.writeToParcel(dest, flags);
- }
-
- public static Creator<NfcKeyToCardResult> CREATOR = new Creator<NfcKeyToCardResult>() {
- public NfcKeyToCardResult createFromParcel(final Parcel source) {
- return new NfcKeyToCardResult(source);
- }
-
- public NfcKeyToCardResult[] newArray(final int size) {
- return new NfcKeyToCardResult[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 4c40b0e5b..065ca710c 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
@@ -494,6 +494,7 @@ public abstract class OperationResult implements Parcelable {
MSG_MF_ERROR_REVOKED_PRIMARY (LogLevel.ERROR, R.string.msg_mf_error_revoked_primary),
MSG_MF_ERROR_SIG (LogLevel.ERROR, R.string.msg_mf_error_sig),
MSG_MF_ERROR_SUBKEY_MISSING(LogLevel.ERROR, R.string.msg_mf_error_subkey_missing),
+ MSG_MF_ERROR_CONFLICTING_NFC_COMMANDS(LogLevel.ERROR, R.string.msg_mf_error_conflicting_nfc_commands),
MSG_MF_MASTER (LogLevel.DEBUG, R.string.msg_mf_master),
MSG_MF_NOTATION_PIN (LogLevel.DEBUG, R.string.msg_mf_notation_pin),
MSG_MF_NOTATION_EMPTY (LogLevel.DEBUG, R.string.msg_mf_notation_empty),
@@ -511,6 +512,8 @@ public abstract class OperationResult implements Parcelable {
MSG_MF_SUBKEY_NEW (LogLevel.INFO, R.string.msg_mf_subkey_new),
MSG_MF_SUBKEY_REVOKE (LogLevel.INFO, R.string.msg_mf_subkey_revoke),
MSG_MF_SUBKEY_STRIP (LogLevel.INFO, R.string.msg_mf_subkey_strip),
+ MSG_MF_KEYTOCARD_START (LogLevel.INFO, R.string.msg_mf_keytocard_start),
+ MSG_MF_KEYTOCARD_FINISH (LogLevel.OK, R.string.msg_mf_keytocard_finish),
MSG_MF_SUCCESS (LogLevel.OK, R.string.msg_mf_success),
MSG_MF_UID_ADD (LogLevel.INFO, R.string.msg_mf_uid_add),
MSG_MF_UID_PRIMARY (LogLevel.INFO, R.string.msg_mf_uid_primary),
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
index 89db378a9..93c479d5e 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
@@ -18,6 +18,7 @@
package org.sufficientlysecure.keychain.pgp;
+import org.spongycastle.bcpg.PublicKeyAlgorithmTags;
import org.spongycastle.bcpg.S2K;
import org.spongycastle.bcpg.sig.Features;
import org.spongycastle.bcpg.sig.KeyFlags;
@@ -45,8 +46,10 @@ import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyEncryptorBuilder;
import org.spongycastle.openpgp.operator.jcajce.NfcSyncPGPContentSignerBuilder;
import org.spongycastle.openpgp.operator.jcajce.NfcSyncPGPContentSignerBuilder.NfcInteractionNeeded;
+import org.spongycastle.util.encoders.Hex;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.operations.results.EditKeyResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
@@ -59,6 +62,7 @@ import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyAdd;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel.NfcSignOperationsBuilder;
+import org.sufficientlysecure.keychain.service.input.RequiredInputParcel.NfcKeyToCardOperationsBuilder;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.Log;
@@ -68,6 +72,7 @@ import org.sufficientlysecure.keychain.util.ProgressScaler;
import java.io.IOException;
import java.math.BigInteger;
+import java.nio.ByteBuffer;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
@@ -356,10 +361,16 @@ public class PgpKeyOperation {
*
*/
public PgpEditKeyResult modifySecretKeyRing(CanonicalizedSecretKeyRing wsKR,
+ CryptoInputParcel cryptoInput,
+ SaveKeyringParcel saveParcel) {
+ return modifySecretKeyRing(wsKR, cryptoInput, saveParcel, new OperationLog());
+ }
+
+ public PgpEditKeyResult modifySecretKeyRing(CanonicalizedSecretKeyRing wsKR,
CryptoInputParcel cryptoInput,
- SaveKeyringParcel saveParcel) {
+ SaveKeyringParcel saveParcel,
+ OperationLog log) {
- OperationLog log = new OperationLog();
int indent = 0;
/*
@@ -400,6 +411,21 @@ public class PgpKeyOperation {
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
}
+ for(SaveKeyringParcel.SubkeyChange change : saveParcel.mChangeSubKeys) {
+ if (change.mDummyDivert != null && change.mDummyDivert.length == 0) {
+ // If this is a keytocard operation, see if it was completed: look for a hash
+ // matching the given subkey ID in cryptoData.
+ byte[] subKeyId = new byte[8];
+ ByteBuffer buf = ByteBuffer.wrap(subKeyId);
+ buf.putLong(change.mKeyId).rewind();
+
+ byte[] serialNumber = cryptoInput.getCryptoData().get(buf);
+ if (serialNumber != null) {
+ change.mDummyDivert = serialNumber;
+ }
+ }
+ }
+
if (isDummy(masterSecretKey) || saveParcel.isRestrictedOnly()) {
log.add(LogType.MSG_MF_RESTRICTED_MODE, indent);
return internalRestricted(sKR, saveParcel, log);
@@ -435,6 +461,8 @@ public class PgpKeyOperation {
NfcSignOperationsBuilder nfcSignOps = new NfcSignOperationsBuilder(
cryptoInput.getSignatureTime(), masterSecretKey.getKeyID(),
masterSecretKey.getKeyID());
+ NfcKeyToCardOperationsBuilder nfcKeyToCardOps = new NfcKeyToCardOperationsBuilder(
+ masterSecretKey.getKeyID());
progress(R.string.progress_modify, 0);
@@ -743,22 +771,37 @@ public class PgpKeyOperation {
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
}
- if (change.mDummyStrip || change.mDummyDivert != null) {
+ if (change.mDummyStrip) {
// IT'S DANGEROUS~
// no really, it is. this operation irrevocably removes the private key data from the key
- if (change.mDummyStrip) {
- sKey = PGPSecretKey.constructGnuDummyKey(sKey.getPublicKey());
- } else {
- // the serial number must be 16 bytes in length
- if (change.mDummyDivert.length != 16) {
- log.add(LogType.MSG_MF_ERROR_DIVERT_SERIAL,
- indent + 1, KeyFormattingUtils.convertKeyIdToHex(change.mKeyId));
- return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
+ sKey = PGPSecretKey.constructGnuDummyKey(sKey.getPublicKey());
+ sKR = PGPSecretKeyRing.insertSecretKey(sKR, sKey);
+ } else if (change.mDummyDivert != null) {
+ if (change.mDummyDivert.length == 0) {
+ // If serial number is 0 length, we're moving the key to a card.
+ if (checkSmartCardCompatibility(sKey, log, indent + 1)) {
+ log.add(LogType.MSG_MF_KEYTOCARD_START, indent + 1,
+ KeyFormattingUtils.convertKeyIdToHex(change.mKeyId));
+ nfcKeyToCardOps.addSubkey(change.mKeyId);
+ } else {
+ return new PgpEditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
+ } else if (change.mDummyDivert.length == 16) {
+ // If serial number is 16 bytes long, we're associating the key with a card.
+ log.add(LogType.MSG_MF_KEYTOCARD_FINISH, indent + 1,
+ KeyFormattingUtils.convertKeyIdToHex(change.mKeyId),
+ Hex.toHexString(change.mDummyDivert, 8, 6));
+ sKey = PGPSecretKey.constructGnuDummyKey(sKey.getPublicKey(), change.mDummyDivert);
+ sKR = PGPSecretKeyRing.insertSecretKey(sKR, sKey);
+ } else {
+ log.add(LogType.MSG_MF_ERROR_DIVERT_SERIAL,
+ indent + 1, KeyFormattingUtils.convertKeyIdToHex(change.mKeyId));
+ return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
}
- sKR = PGPSecretKeyRing.insertSecretKey(sKR, sKey);
}
+
+
// This doesn't concern us any further
if (!change.mRecertify && (change.mExpiry == null && change.mFlags == null)) {
continue;
@@ -980,11 +1023,21 @@ public class PgpKeyOperation {
progress(R.string.progress_done, 100);
+ if (!nfcSignOps.isEmpty() && !nfcKeyToCardOps.isEmpty()) {
+ log.add(LogType.MSG_MF_ERROR_CONFLICTING_NFC_COMMANDS, indent+1);
+ return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
+ }
+
if (!nfcSignOps.isEmpty()) {
log.add(LogType.MSG_MF_REQUIRE_DIVERT, indent);
return new PgpEditKeyResult(log, nfcSignOps.build());
}
+ if (!nfcKeyToCardOps.isEmpty()) {
+ log.add(LogType.MSG_MF_REQUIRE_DIVERT, indent);
+ return new PgpEditKeyResult(log, nfcKeyToCardOps.build());
+ }
+
log.add(LogType.MSG_MF_SUCCESS, indent);
return new PgpEditKeyResult(OperationResult.RESULT_OK, log, new UncachedKeyRing(sKR));
@@ -1042,6 +1095,9 @@ public class PgpKeyOperation {
indent + 1, KeyFormattingUtils.convertKeyIdToHex(change.mKeyId));
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
}
+ log.add(LogType.MSG_MF_KEYTOCARD_FINISH, indent + 1,
+ KeyFormattingUtils.convertKeyIdToHex(change.mKeyId),
+ Hex.toHexString(change.mDummyDivert, 8, 6));
sKey = PGPSecretKey.constructGnuDummyKey(sKey.getPublicKey(), change.mDummyDivert);
}
sKR = PGPSecretKeyRing.insertSecretKey(sKR, sKey);
@@ -1481,4 +1537,29 @@ public class PgpKeyOperation {
&& s2k.getProtectionMode() == S2K.GNU_PROTECTION_MODE_DIVERT_TO_CARD;
}
+ private static boolean checkSmartCardCompatibility(PGPSecretKey key, OperationLog log, int indent) {
+ PGPPublicKey publicKey = key.getPublicKey();
+ int algorithm = publicKey.getAlgorithm();
+ if (algorithm != PublicKeyAlgorithmTags.RSA_ENCRYPT &&
+ algorithm != PublicKeyAlgorithmTags.RSA_SIGN &&
+ algorithm != PublicKeyAlgorithmTags.RSA_GENERAL) {
+ log.add(OperationResult.LogType.MSG_K2C_ERROR_BAD_ALGO, indent + 1);
+ return true;
+ }
+
+ // Key size must be 2048
+ int keySize = publicKey.getBitStrength();
+ if (keySize != 2048) {
+ log.add(OperationResult.LogType.MSG_K2C_ERROR_BAD_SIZE, indent + 1);
+ return false;
+ }
+
+ // Secret key parts must be available
+ if (isDivertToCard(key) || isDummy(key)) {
+ log.add(OperationResult.LogType.MSG_K2C_ERROR_BAD_STRIPPED, indent + 1);
+ return false;
+ }
+
+ return true;
+ }
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java
index 0fad22e4a..e0509ac9b 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java
@@ -40,7 +40,6 @@ import org.sufficientlysecure.keychain.operations.CertifyOperation;
import org.sufficientlysecure.keychain.operations.DeleteOperation;
import org.sufficientlysecure.keychain.operations.EditKeyOperation;
import org.sufficientlysecure.keychain.operations.ImportExportOperation;
-import org.sufficientlysecure.keychain.operations.NfcKeyToCardOperation;
import org.sufficientlysecure.keychain.operations.PromoteKeyOperation;
import org.sufficientlysecure.keychain.operations.SignEncryptOperation;
import org.sufficientlysecure.keychain.operations.results.CertifyResult;
@@ -49,7 +48,6 @@ import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
import org.sufficientlysecure.keychain.operations.results.DeleteResult;
import org.sufficientlysecure.keychain.operations.results.ExportResult;
import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
-import org.sufficientlysecure.keychain.operations.results.NfcKeyToCardResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
import org.sufficientlysecure.keychain.operations.results.PromoteKeyResult;
@@ -113,8 +111,6 @@ public class KeychainIntentService extends IntentService implements Progressable
public static final String ACTION_IMPORT_KEYRING = Constants.INTENT_PREFIX + "IMPORT_KEYRING";
public static final String ACTION_EXPORT_KEYRING = Constants.INTENT_PREFIX + "EXPORT_KEYRING";
- public static final String ACTION_NFC_KEYTOCARD = Constants.INTENT_PREFIX + "NFC_KEYTOCARD";
-
public static final String ACTION_UPLOAD_KEYRING = Constants.INTENT_PREFIX + "UPLOAD_KEYRING";
public static final String ACTION_CERTIFY_KEYRING = Constants.INTENT_PREFIX + "SIGN_KEYRING";
@@ -180,9 +176,6 @@ public class KeychainIntentService extends IntentService implements Progressable
public static final String EXPORT_ALL = "export_all";
public static final String EXPORT_KEY_RING_MASTER_KEY_ID = "export_key_ring_id";
- // NFC export key to card
- public static final String NFC_KEYTOCARD_SUBKEY_ID = "nfc_keytocard_subkey_id";
-
// upload key
public static final String UPLOAD_KEY_SERVER = "upload_key_server";
@@ -539,19 +532,6 @@ public class KeychainIntentService extends IntentService implements Progressable
break;
}
- case ACTION_NFC_KEYTOCARD: {
- // Input
- long subKeyId = data.getLong(NFC_KEYTOCARD_SUBKEY_ID);
-
- // Operation
- NfcKeyToCardOperation exportOp = new NfcKeyToCardOperation(this, providerHelper, this);
- NfcKeyToCardResult result = exportOp.execute(subKeyId);
-
- // Result
- sendMessageToHandler(MessageStatus.OKAY, result);
-
- break;
- }
case ACTION_SIGN_ENCRYPT: {
// Input
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java
index 2e0524141..6064c28fb 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java
@@ -95,7 +95,8 @@ public class SaveKeyringParcel implements Parcelable {
}
for (SubkeyChange change : mChangeSubKeys) {
- if (change.mRecertify || change.mFlags != null || change.mExpiry != null) {
+ if (change.mRecertify || change.mFlags != null || change.mExpiry != null ||
+ (change.mDummyDivert != null && change.mDummyDivert.length == 0)) {
return false;
}
}
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 50fb35e90..727f1638c 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,5 +1,6 @@
package org.sufficientlysecure.keychain.service.input;
+import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
@@ -7,8 +8,6 @@ import java.util.Date;
import android.os.Parcel;
import android.os.Parcelable;
-import org.sufficientlysecure.keychain.Constants.key;
-
public class RequiredInputParcel implements Parcelable {
@@ -87,11 +86,6 @@ public class RequiredInputParcel implements Parcelable {
new byte[][] { inputHash }, null, null, null, subKeyId);
}
- public static RequiredInputParcel createNfcKeyToCardOperation(long masterKeyId, long subKeyId) {
- return new RequiredInputParcel(RequiredInputType.NFC_KEYTOCARD, null, null, null,
- masterKeyId, subKeyId);
- }
-
public static RequiredInputParcel createRequiredSignPassphrase(
long masterKeyId, long subKeyId, Date signatureTime) {
return new RequiredInputParcel(RequiredInputType.PASSPHRASE,
@@ -216,4 +210,46 @@ public class RequiredInputParcel implements Parcelable {
}
+ public static class NfcKeyToCardOperationsBuilder {
+ ArrayList<byte[]> mSubkeysToExport = new ArrayList<>();
+ Long mMasterKeyId;
+
+ public NfcKeyToCardOperationsBuilder(Long masterKeyId) {
+ mMasterKeyId = masterKeyId;
+ }
+
+ public RequiredInputParcel build() {
+ byte[][] inputHashes = new byte[mSubkeysToExport.size()][];
+ mSubkeysToExport.toArray(inputHashes);
+ ByteBuffer buf = ByteBuffer.wrap(mSubkeysToExport.get(0));
+
+ // We need to pass in a subkey here...
+ return new RequiredInputParcel(RequiredInputType.NFC_KEYTOCARD,
+ inputHashes, null, null, mMasterKeyId, buf.getLong());
+ }
+
+ public void addSubkey(long subkeyId) {
+ byte[] subKeyId = new byte[8];
+ ByteBuffer buf = ByteBuffer.wrap(subKeyId);
+ buf.putLong(subkeyId).rewind();
+ mSubkeysToExport.add(subKeyId);
+ }
+
+ public void addAll(RequiredInputParcel input) {
+ if (!mMasterKeyId.equals(input.mMasterKeyId)) {
+ throw new AssertionError("Master keys must match, this is a programming error!");
+ }
+ if (input.mType != RequiredInputType.NFC_KEYTOCARD) {
+ throw new AssertionError("Operation types must match, this is a programming error!");
+ }
+
+ Collections.addAll(mSubkeysToExport, input.mInputHashes);
+ }
+
+ public boolean isEmpty() {
+ return mSubkeysToExport.isEmpty();
+ }
+
+ }
+
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java
index 3b342bec0..bc2fbff76 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java
@@ -35,6 +35,7 @@ import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ListView;
+import android.widget.Toast;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
@@ -42,6 +43,7 @@ import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
import org.sufficientlysecure.keychain.operations.results.SingletonResult;
+import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey;
import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
@@ -418,6 +420,13 @@ public class EditKeyFragment extends CryptoOperationFragment implements
}
break;
case EditSubkeyDialogFragment.MESSAGE_STRIP: {
+ CanonicalizedSecretKey.SecretKeyType secretKeyType =
+ mSubkeysAdapter.getSecretKeyType(position);
+ if (secretKeyType == CanonicalizedSecretKey.SecretKeyType.GNU_DUMMY) {
+ // Key is already stripped; this is a no-op.
+ break;
+ }
+
SubkeyChange change = mSaveKeyringParcel.getSubkeyChange(keyId);
if (change == null) {
mSaveKeyringParcel.mChangeSubKeys.add(new SubkeyChange(keyId, true, null));
@@ -425,47 +434,39 @@ public class EditKeyFragment extends CryptoOperationFragment implements
}
// toggle
change.mDummyStrip = !change.mDummyStrip;
+ if (change.mDummyStrip && change.mDummyDivert != null) {
+ // User had chosen to divert key, but now wants to strip it instead.
+ change.mDummyDivert = null;
+ }
break;
}
case EditSubkeyDialogFragment.MESSAGE_KEYTOCARD: {
+ CanonicalizedSecretKey.SecretKeyType secretKeyType =
+ mSubkeysAdapter.getSecretKeyType(position);
+ if (secretKeyType == CanonicalizedSecretKey.SecretKeyType.DIVERT_TO_CARD ||
+ secretKeyType == CanonicalizedSecretKey.SecretKeyType.GNU_DUMMY) {
+ Toast.makeText(EditKeyFragment.this.getActivity(),
+ R.string.edit_key_error_bad_nfc_stripped, Toast.LENGTH_SHORT)
+ .show();
+ break;
+ }
+
SubkeyChange change;
change = mSaveKeyringParcel.getSubkeyChange(keyId);
if (change == null) {
mSaveKeyringParcel.mChangeSubKeys.add(
- new SubkeyChange(keyId, false, new byte[0])
+ new SubkeyChange(keyId, false, null)
);
+ change = mSaveKeyringParcel.getSubkeyChange(keyId);
+ }
+ // toggle
+ if (change.mDummyDivert == null) {
+ change.mDummyDivert = new byte[0];
+ // If user had chosen to strip key, we cancel that action now.
+ change.mDummyStrip = false;
+ } else {
+ change.mDummyDivert = null;
}
- final Bundle data = new Bundle();
- data.putLong(KeychainIntentService.NFC_KEYTOCARD_SUBKEY_ID, keyId);
- Intent intent = new Intent(EditKeyFragment.this.getActivity(),
- KeychainIntentService.class);
- intent.setAction(KeychainIntentService.ACTION_NFC_KEYTOCARD);
- intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
-
- ServiceProgressHandler serviceHandler = new ServiceProgressHandler(
- getActivity(),
- getString(R.string.progress_exporting),
- ProgressDialog.STYLE_HORIZONTAL,
- ProgressDialogFragment.ServiceType.KEYCHAIN_INTENT) {
- public void handleMessage(Message message) {
- super.handleMessage(message);
- if (EditKeyFragment.this.handlePendingMessage(message)) {
- return;
- }
- Bundle data = message.getData();
- OperationResult result = data.getParcelable(OperationResult.EXTRA_RESULT);
- if (result.getResult() == OperationResult.RESULT_ERROR) {
- result.createNotify(getActivity()).show();
- }
-
- }
- };
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(serviceHandler);
- intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
- serviceHandler.showProgressDialog(getActivity());
-
- getActivity().startService(intent);
break;
}
}
@@ -637,18 +638,6 @@ public class EditKeyFragment extends CryptoOperationFragment implements
Intent intent = new Intent(getActivity(), KeychainIntentService.class);
intent.setAction(KeychainIntentService.ACTION_EDIT_KEYRING);
- for (SubkeyChange change : mSaveKeyringParcel.mChangeSubKeys) {
- if(change.mDummyDivert != null) {
- // Convert long key ID to byte buffer
- byte[] subKeyId = new byte[8];
- ByteBuffer buf = ByteBuffer.wrap(subKeyId);
- buf.putLong(change.mKeyId).rewind();
-
- byte[] cardSerial = cryptoInput.getCryptoData().get(buf);
- change.mDummyDivert = cardSerial;
- }
- }
-
// fill values for this action
Bundle data = new Bundle();
data.putParcelable(KeychainIntentService.EXTRA_CRYPTO_INPUT, cryptoInput);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcOperationActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcOperationActivity.java
index 1a618329d..bed846dd3 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcOperationActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcOperationActivity.java
@@ -23,6 +23,7 @@ import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
import org.sufficientlysecure.keychain.ui.base.BaseNfcActivity;
import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.Passphrase;
import org.sufficientlysecure.keychain.util.Preferences;
import java.io.IOException;
@@ -59,9 +60,7 @@ public class NfcOperationActivity extends BaseNfcActivity {
mRequiredInput = data.getParcelable(EXTRA_REQUIRED_INPUT);
mServiceIntent = data.getParcelable(EXTRA_SERVICE_INTENT);
- if (mRequiredInput.mType == RequiredInputParcel.RequiredInputType.NFC_KEYTOCARD) {
- obtainKeyExportPassphrase(RequiredInputParcel.createRequiredPassphrase(mRequiredInput));
- } else {
+ if (mRequiredInput.mType != RequiredInputParcel.RequiredInputType.NFC_KEYTOCARD) {
obtainYubiKeyPin(RequiredInputParcel.createRequiredPassphrase(mRequiredInput));
}
}
@@ -99,38 +98,51 @@ public class NfcOperationActivity extends BaseNfcActivity {
CanonicalizedSecretKeyRing secretKeyRing;
try {
secretKeyRing = providerHelper.getCanonicalizedSecretKeyRing(
- KeychainContract.KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(mRequiredInput.getSubKeyId())
+ KeychainContract.KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(mRequiredInput.getMasterKeyId())
);
} catch (ProviderHelper.NotFoundException e) {
throw new IOException("Couldn't find subkey for key to card operation.");
}
- CanonicalizedSecretKey key = secretKeyRing.getSecretKey(mRequiredInput.getSubKeyId());
-
- long keyGenerationTimestampMillis = key.getCreationTime().getTime();
- long keyGenerationTimestamp = keyGenerationTimestampMillis / 1000;
- byte[] timestampBytes = ByteBuffer.allocate(4).putInt((int) keyGenerationTimestamp).array();
- byte[] cardSerialNumber = Arrays.copyOf(nfcGetAid(), 16);
-
- if (key.canSign() || key.canCertify()) {
- nfcPutKey(0xB6, key);
- nfcPutData(0xCE, timestampBytes);
- nfcPutData(0xC7, key.getFingerprint());
- } else if (key.canEncrypt()) {
- nfcPutKey(0xB8, key);
- nfcPutData(0xCF, timestampBytes);
- nfcPutData(0xC8, key.getFingerprint());
- } else if (key.canAuthenticate()) {
- nfcPutKey(0xA4, key);
- nfcPutData(0xD0, timestampBytes);
- nfcPutData(0xC9, key.getFingerprint());
- } else {
- throw new IOException("Inappropriate key flags for smart card key.");
- }
- byte[] subKeyId = new byte[8];
- ByteBuffer buf = ByteBuffer.wrap(subKeyId);
- buf.putLong(mRequiredInput.getSubKeyId());
- inputParcel.addCryptoData(subKeyId, cardSerialNumber);
+ // Note: we're abusing mInputHashes to hold the subkey IDs we need to export.
+ for (int i = 0; i < mRequiredInput.mInputHashes.length; i++) {
+ byte[] subkeyBytes = mRequiredInput.mInputHashes[i];
+ ByteBuffer buf = ByteBuffer.wrap(subkeyBytes);
+ long subkeyId = buf.getLong();
+
+ CanonicalizedSecretKey key = secretKeyRing.getSecretKey(subkeyId);
+
+ long keyGenerationTimestampMillis = key.getCreationTime().getTime();
+ long keyGenerationTimestamp = keyGenerationTimestampMillis / 1000;
+ byte[] timestampBytes = ByteBuffer.allocate(4).putInt((int) keyGenerationTimestamp).array();
+ byte[] cardSerialNumber = Arrays.copyOf(nfcGetAid(), 16);
+
+ Passphrase passphrase;
+ try {
+ passphrase = PassphraseCacheService.getCachedPassphrase(this,
+ mRequiredInput.getMasterKeyId(), mRequiredInput.getSubKeyId());
+ } catch (PassphraseCacheService.KeyNotFoundException e) {
+ throw new IOException("Unable to get cached passphrase!");
+ }
+
+ if (key.canSign() || key.canCertify()) {
+ nfcPutKey(0xB6, key, passphrase);
+ nfcPutData(0xCE, timestampBytes);
+ nfcPutData(0xC7, key.getFingerprint());
+ } else if (key.canEncrypt()) {
+ nfcPutKey(0xB8, key, passphrase);
+ nfcPutData(0xCF, timestampBytes);
+ nfcPutData(0xC8, key.getFingerprint());
+ } else if (key.canAuthenticate()) {
+ nfcPutKey(0xA4, key, passphrase);
+ nfcPutData(0xD0, timestampBytes);
+ nfcPutData(0xC9, key.getFingerprint());
+ } else {
+ throw new IOException("Inappropriate key flags for smart card key.");
+ }
+
+ inputParcel.addCryptoData(subkeyBytes, cardSerialNumber);
+ }
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAdapter.java
index db0a930ed..dfa5a39fb 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAdapter.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAdapter.java
@@ -179,13 +179,23 @@ public class SubkeysAdapter extends CursorAdapter {
? mSaveKeyringParcel.getSubkeyChange(keyId)
: null;
- if (change != null && change.mDummyStrip) {
- algorithmStr.append(", ");
- final SpannableString boldStripped = new SpannableString(
- context.getString(R.string.key_stripped)
- );
- boldStripped.setSpan(new StyleSpan(Typeface.BOLD), 0, boldStripped.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- algorithmStr.append(boldStripped);
+ if (change != null && (change.mDummyStrip || change.mDummyDivert != null)) {
+ if (change.mDummyStrip) {
+ algorithmStr.append(", ");
+ final SpannableString boldStripped = new SpannableString(
+ context.getString(R.string.key_stripped)
+ );
+ boldStripped.setSpan(new StyleSpan(Typeface.BOLD), 0, boldStripped.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ algorithmStr.append(boldStripped);
+ }
+ if (change.mDummyDivert != null) {
+ algorithmStr.append(", ");
+ final SpannableString boldDivert = new SpannableString(
+ context.getString(R.string.key_divert)
+ );
+ boldDivert.setSpan(new StyleSpan(Typeface.BOLD), 0, boldDivert.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ algorithmStr.append(boldDivert);
+ }
} else {
switch (SecretKeyType.fromNum(cursor.getInt(INDEX_HAS_SECRET))) {
case GNU_DUMMY:
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseNfcActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseNfcActivity.java
index 4506fe3c1..686c86b3a 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseNfcActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseNfcActivity.java
@@ -60,11 +60,9 @@ import org.sufficientlysecure.keychain.util.Preferences;
public abstract class BaseNfcActivity extends BaseActivity {
public static final int REQUEST_CODE_PIN = 1;
- public static final int REQUEST_CODE_PASSPHRASE = 2;
protected Passphrase mPin;
protected Passphrase mAdminPin;
- protected Passphrase mPassphrase; // For key export
protected boolean mPw1ValidForMultipleSignatures;
protected boolean mPw1ValidatedForSignature;
protected boolean mPw1ValidatedForDecrypt; // Mode 82 does other things; consider renaming?
@@ -136,15 +134,6 @@ public abstract class BaseNfcActivity extends BaseActivity {
enableNfcForegroundDispatch();
}
- protected void obtainKeyExportPassphrase(RequiredInputParcel requiredInput) {
-
- Intent intent = new Intent(this, PassphraseDialogActivity.class);
- intent.putExtra(PassphraseDialogActivity.EXTRA_REQUIRED_INPUT,
- RequiredInputParcel.createRequiredPassphrase(requiredInput));
- startActivityForResult(intent, REQUEST_CODE_PASSPHRASE);
-
- }
-
protected void obtainYubiKeyPin(RequiredInputParcel requiredInput) {
Preferences prefs = Preferences.getPreferences(this);
@@ -178,17 +167,6 @@ public abstract class BaseNfcActivity extends BaseActivity {
break;
}
- case REQUEST_CODE_PASSPHRASE: {
- if (resultCode != Activity.RESULT_OK) {
- setResult(resultCode);
- finish();
- return;
- }
- CryptoInputParcel input = data.getParcelableExtra(PassphraseDialogActivity.RESULT_CRYPTO_INPUT);
- mPassphrase = input.getPassphrase();
- break;
- }
-
default:
super.onActivityResult(requestCode, resultCode, data);
}
@@ -627,7 +605,7 @@ public abstract class BaseNfcActivity extends BaseActivity {
* 0xB8: Decipherment Key
* 0xA4: Authentication Key
*/
- public void nfcPutKey(int slot, CanonicalizedSecretKey secretKey)
+ public void nfcPutKey(int slot, CanonicalizedSecretKey secretKey, Passphrase passphrase)
throws IOException {
if (slot != 0xB6 && slot != 0xB8 && slot != 0xA4) {
throw new IOException("Invalid key slot");
@@ -635,7 +613,7 @@ public abstract class BaseNfcActivity extends BaseActivity {
RSAPrivateCrtKey crtSecretKey = null;
try {
- secretKey.unlock(mPassphrase);
+ secretKey.unlock(passphrase);
crtSecretKey = secretKey.getCrtSecretKey();
} catch (PgpGeneralException e) {
throw new IOException(e.getMessage());