Moving keytocard process into PgpKeyOperation.

This commit is contained in:
Joey Castillo 2015-05-13 05:33:28 -04:00
parent a0107afd3e
commit d21fb77336
13 changed files with 239 additions and 290 deletions

View File

@ -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;
}

View File

@ -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);
}
}
}

View File

@ -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];
}
};
}

View File

@ -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),

View File

@ -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) {
CryptoInputParcel cryptoInput,
SaveKeyringParcel saveParcel) {
return modifySecretKeyRing(wsKR, cryptoInput, saveParcel, new OperationLog());
}
public PgpEditKeyResult modifySecretKeyRing(CanonicalizedSecretKeyRing wsKR,
CryptoInputParcel cryptoInput,
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);
}
}
// 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;
}
}

View File

@ -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

View File

@ -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;
}
}

View File

@ -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();
}
}
}

View File

@ -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);

View File

@ -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);
// 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();
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.");
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);
}
byte[] subKeyId = new byte[8];
ByteBuffer buf = ByteBuffer.wrap(subKeyId);
buf.putLong(mRequiredInput.getSubKeyId());
inputParcel.addCryptoData(subKeyId, cardSerialNumber);
}
}

View File

@ -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:

View File

@ -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());

View File

@ -928,6 +928,7 @@
<string name="msg_mf_error_pgp">"Internal OpenPGP error!"</string>
<string name="msg_mf_error_sig">"Signature exception!"</string>
<string name="msg_mf_error_subkey_missing">"Tried to operate on missing subkey %s!"</string>
<string name="msg_mf_error_conflicting_nfc_commands">"Cannot move key to card in same operation that creates an on-card signature."</string>
<string name="msg_mf_master">"Modifying master certifications"</string>
<string name="msg_mf_notation_empty">"Adding empty notation packet"</string>
<string name="msg_mf_notation_pin">"Adding PIN notation packet"</string>
@ -946,6 +947,8 @@
<string name="msg_mf_error_past_expiry">"Expiry date cannot be in the past!"</string>
<string name="msg_mf_subkey_revoke">"Revoking subkey %s"</string>
<string name="msg_mf_subkey_strip">"Stripping subkey %s"</string>
<string name="msg_mf_keytocard_start">"Moving subkey %s to smart card"</string>
<string name="msg_mf_keytocard_finish">"Moved %1$s to card %2$s"</string>
<string name="msg_mf_success">"Keyring successfully modified"</string>
<string name="msg_mf_uid_add">"Adding user ID %s"</string>
<string name="msg_mf_uid_primary">"Changing primary user ID to %s"</string>