From 3b04636f5daf3d171449296a5d9a67440abfbf75 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Fri, 20 Mar 2015 02:27:05 +0100 Subject: support yubikeys in (some) edit key operations --- .../keychain/operations/CertifyOperation.java | 6 +- .../keychain/operations/EditKeyOperation.java | 11 +- .../operations/results/InputPendingResult.java | 33 +-- .../operations/results/OperationResult.java | 3 +- .../operations/results/PgpEditKeyResult.java | 9 +- .../keychain/pgp/CanonicalizedSecretKey.java | 8 +- .../keychain/pgp/PgpKeyOperation.java | 312 ++++++++++++++------- .../keychain/service/CertifyActionsParcel.java | 4 - .../keychain/service/KeychainIntentService.java | 6 +- .../keychain/service/input/CryptoInputParcel.java | 19 +- .../service/input/RequiredInputParcel.java | 4 + .../keychain/ui/CryptoOperationFragment.java | 12 +- .../keychain/ui/EditKeyFragment.java | 86 ++---- .../keychain/ui/EncryptActivity.java | 6 +- .../keychain/ui/NfcOperationActivity.java | 10 +- .../ui/dialog/SetPassphraseDialogFragment.java | 12 +- OpenKeychain/src/main/res/values/strings.xml | 3 +- 17 files changed, 296 insertions(+), 248 deletions(-) (limited to 'OpenKeychain/src') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/CertifyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/CertifyOperation.java index cc4ca88cb..bcdbf5f67 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/CertifyOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/CertifyOperation.java @@ -83,7 +83,7 @@ public class CertifyOperation extends BaseOperation { } // certification is always with the master key id, so use that one - String passphrase = parcel.mCryptoInput.getPassphrase(); + char[] passphrase = parcel.mCryptoInput.getPassphrase(); if (!certificationKey.unlock(passphrase)) { log.add(LogType.MSG_CRT_ERROR_UNLOCK, 2); @@ -103,7 +103,7 @@ public class CertifyOperation extends BaseOperation { int certifyOk = 0, certifyError = 0, uploadOk = 0, uploadError = 0; - NfcSignOperationsBuilder allRequiredInput = new NfcSignOperationsBuilder(parcel.getSignatureTime()); + NfcSignOperationsBuilder allRequiredInput = new NfcSignOperationsBuilder(parcel.mCryptoInput.getSignatureTime()); // Work through all requested certifications for (CertifyAction action : parcel.mCertifyActions) { @@ -127,7 +127,7 @@ public class CertifyOperation extends BaseOperation { PgpCertifyOperation op = new PgpCertifyOperation(); PgpCertifyResult result = op.certify(certificationKey, publicRing, - log, 2, action, parcel.getSignatureData(), parcel.getSignatureTime()); + log, 2, action, parcel.getSignatureData(), parcel.mCryptoInput.getSignatureTime()); if (!result.success()) { certifyError += 1; 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 bcd842dd0..a03664091 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/EditKeyOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/EditKeyOperation.java @@ -21,6 +21,7 @@ import android.content.Context; 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; import org.sufficientlysecure.keychain.operations.results.PgpEditKeyResult; @@ -34,6 +35,7 @@ import org.sufficientlysecure.keychain.provider.ProviderHelper.NotFoundException import org.sufficientlysecure.keychain.service.ContactSyncAdapterService; import org.sufficientlysecure.keychain.service.PassphraseCacheService; import org.sufficientlysecure.keychain.service.SaveKeyringParcel; +import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.util.ProgressScaler; @@ -55,7 +57,7 @@ public class EditKeyOperation extends BaseOperation { super(context, providerHelper, progressable, cancelled); } - public EditKeyResult execute(SaveKeyringParcel saveParcel, String passphrase) { + public OperationResult execute(SaveKeyringParcel saveParcel, CryptoInputParcel cryptoInput) { OperationLog log = new OperationLog(); log.add(LogType.MSG_ED, 0); @@ -69,7 +71,7 @@ public class EditKeyOperation extends BaseOperation { PgpEditKeyResult modifyResult; { PgpKeyOperation keyOperations = - new PgpKeyOperation(new ProgressScaler(mProgressable, 10, 60, 100), mCancelled); + new PgpKeyOperation(new ProgressScaler(mProgressable, 10, 60, 100), mCancelled, cryptoInput); // If a key id is specified, fetch and edit if (saveParcel.mMasterKeyId != null) { @@ -80,7 +82,10 @@ public class EditKeyOperation extends BaseOperation { CanonicalizedSecretKeyRing secRing = mProviderHelper.getCanonicalizedSecretKeyRing(saveParcel.mMasterKeyId); - modifyResult = keyOperations.modifySecretKeyRing(secRing, saveParcel, passphrase); + modifyResult = keyOperations.modifySecretKeyRing(secRing, saveParcel); + if (modifyResult.isPending()) { + return modifyResult; + } } catch (NotFoundException e) { log.add(LogType.MSG_ED_ERROR_KEY_NOT_FOUND, 2); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/InputPendingResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/InputPendingResult.java index 07d42c456..e0ba28fbe 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/InputPendingResult.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/InputPendingResult.java @@ -11,66 +11,35 @@ public class InputPendingResult extends OperationResult { // the fourth bit indicates a "data pending" result! (it's also a form of non-success) public static final int RESULT_PENDING = RESULT_ERROR + 8; - public static final int RESULT_PENDING_PASSPHRASE = RESULT_PENDING + 16; - public static final int RESULT_PENDING_NFC = RESULT_PENDING + 32; - final RequiredInputParcel mRequiredInput; - final Long mKeyIdPassphraseNeeded; public InputPendingResult(int result, OperationLog log) { super(result, log); mRequiredInput = null; - mKeyIdPassphraseNeeded = null; } public InputPendingResult(OperationLog log, RequiredInputParcel requiredInput) { - super(RESULT_PENDING_NFC, log); + super(RESULT_PENDING, log); mRequiredInput = requiredInput; - mKeyIdPassphraseNeeded = null; - } - - public InputPendingResult(OperationLog log, long keyIdPassphraseNeeded) { - super(RESULT_PENDING_PASSPHRASE, log); - mRequiredInput = null; - mKeyIdPassphraseNeeded = keyIdPassphraseNeeded; } public InputPendingResult(Parcel source) { super(source); mRequiredInput = source.readParcelable(getClass().getClassLoader()); - mKeyIdPassphraseNeeded = source.readInt() != 0 ? source.readLong() : null; } @Override public void writeToParcel(Parcel dest, int flags) { super.writeToParcel(dest, flags); dest.writeParcelable(mRequiredInput, 0); - if (mKeyIdPassphraseNeeded != null) { - dest.writeInt(1); - dest.writeLong(mKeyIdPassphraseNeeded); - } else { - dest.writeInt(0); - } } public boolean isPending() { return (mResult & RESULT_PENDING) == RESULT_PENDING; } - public boolean isNfcPending() { - return (mResult & RESULT_PENDING_NFC) == RESULT_PENDING_NFC; - } - - public boolean isPassphrasePending() { - return (mResult & RESULT_PENDING_PASSPHRASE) == RESULT_PENDING_PASSPHRASE; - } - public RequiredInputParcel getRequiredInputParcel() { return mRequiredInput; } - public long getPassphraseKeyId() { - return mKeyIdPassphraseNeeded; - } - } 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 989fb395e..25abab25b 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 @@ -512,6 +512,7 @@ public abstract class OperationResult implements Parcelable { // secret key modify MSG_MF (LogLevel.START, R.string.msg_mr), + MSG_MF_DIVERT (LogLevel.DEBUG, R.string.msg_mf_divert), MSG_MF_ERROR_DIVERT_SERIAL (LogLevel.ERROR, R.string.msg_mf_error_divert_serial), MSG_MF_ERROR_ENCODE (LogLevel.ERROR, R.string.msg_mf_error_encode), MSG_MF_ERROR_FINGERPRINT (LogLevel.ERROR, R.string.msg_mf_error_fingerprint), @@ -529,6 +530,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_INPUT_REQUIRED (LogLevel.OK, R.string.msg_mf_input_required), 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), @@ -596,7 +598,6 @@ public abstract class OperationResult implements Parcelable { MSG_PR_SUCCESS (LogLevel.OK, R.string.msg_pr_success), // messages used in UI code - MSG_EK_ERROR_DIVERT (LogLevel.ERROR, R.string.msg_ek_error_divert), MSG_EK_ERROR_DUMMY (LogLevel.ERROR, R.string.msg_ek_error_dummy), MSG_EK_ERROR_NOT_FOUND (LogLevel.ERROR, R.string.msg_ek_error_not_found), diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/PgpEditKeyResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/PgpEditKeyResult.java index 611353ac9..38edbf6ee 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/PgpEditKeyResult.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/PgpEditKeyResult.java @@ -22,8 +22,10 @@ import android.os.Parcel; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.pgp.UncachedKeyRing; +import org.sufficientlysecure.keychain.service.input.RequiredInputParcel; -public class PgpEditKeyResult extends OperationResult { + +public class PgpEditKeyResult extends InputPendingResult { private transient UncachedKeyRing mRing; public final long mRingMasterKeyId; @@ -35,6 +37,11 @@ public class PgpEditKeyResult extends OperationResult { mRingMasterKeyId = ring != null ? ring.getMasterKeyId() : Constants.key.none; } + public PgpEditKeyResult(OperationLog log, RequiredInputParcel requiredInput) { + super(log, requiredInput); + mRingMasterKeyId = Constants.key.none; + } + public UncachedKeyRing getRing() { return mRing; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java index c4d0d488e..36ab9dc1d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java @@ -149,10 +149,14 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey { } + public boolean unlock(String passphrase) throws PgpGeneralException { + return unlock(passphrase.toCharArray()); + } + /** * Returns true on right passphrase */ - public boolean unlock(String passphrase) throws PgpGeneralException { + public boolean unlock(char[] passphrase) throws PgpGeneralException { // handle keys on OpenPGP cards like they were unlocked if (mSecretKey.getS2K() != null && mSecretKey.getS2K().getType() == S2K.GNU_DUMMY_S2K @@ -164,7 +168,7 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey { // try to extract keys using the passphrase try { PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( - Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray()); + Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase); mPrivateKey = mSecretKey.extractPrivateKey(keyDecryptor); mPrivateKeyState = PRIVATE_KEY_STATE_UNLOCKED; } catch (PGPException e) { 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 8fb5392e3..fb9edb1cd 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java @@ -18,7 +18,7 @@ package org.sufficientlysecure.keychain.pgp; -import org.spongycastle.bcpg.HashAlgorithmTags; +import org.spongycastle.bcpg.S2K; import org.spongycastle.bcpg.sig.Features; import org.spongycastle.bcpg.sig.KeyFlags; import org.spongycastle.jce.spec.ElGamalParameterSpec; @@ -36,6 +36,7 @@ import org.spongycastle.openpgp.PGPUserAttributeSubpacketVector; import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor; import org.spongycastle.openpgp.operator.PBESecretKeyEncryptor; import org.spongycastle.openpgp.operator.PGPContentSignerBuilder; +import org.spongycastle.openpgp.operator.PGPDataDecryptor; import org.spongycastle.openpgp.operator.PGPDigestCalculator; import org.spongycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator; import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder; @@ -43,6 +44,8 @@ import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBu import org.spongycastle.openpgp.operator.jcajce.JcaPGPKeyPair; 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.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.operations.results.OperationResult; @@ -54,6 +57,9 @@ import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm; import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel; import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Curve; 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.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.util.IterableIterator; import org.sufficientlysecure.keychain.util.Log; @@ -85,9 +91,13 @@ import java.util.concurrent.atomic.AtomicBoolean; * This indicator may be null. */ public class PgpKeyOperation { + private Stack mProgress; private AtomicBoolean mCancelled; + NfcSignOperationsBuilder mNfcSignOps; + private CryptoInputParcel mCryptoInput; + public PgpKeyOperation(Progressable progress) { super(); if (progress != null) { @@ -96,9 +106,11 @@ public class PgpKeyOperation { } } - public PgpKeyOperation(Progressable progress, AtomicBoolean cancelled) { + public PgpKeyOperation(Progressable progress, AtomicBoolean cancelled, CryptoInputParcel cryptoInput) { this(progress); mCancelled = cancelled; + mNfcSignOps = new NfcSignOperationsBuilder(cryptoInput.getSignatureTime()); + mCryptoInput = cryptoInput; } private boolean checkCancelled() { @@ -316,7 +328,8 @@ public class PgpKeyOperation { masterSecretKey.getEncoded(), new JcaKeyFingerprintCalculator()); subProgressPush(50, 100); - return internal(sKR, masterSecretKey, add.mFlags, add.mExpiry, saveParcel, "", log); + CryptoInputParcel cryptoInput = new CryptoInputParcel(new Date(), ""); + return internal(sKR, masterSecretKey, add.mFlags, add.mExpiry, saveParcel, log); } catch (PGPException e) { log.add(LogType.MSG_CR_ERROR_INTERNAL_PGP, indent); @@ -347,8 +360,8 @@ public class PgpKeyOperation { * namely stripping of subkeys and changing the protection mode of dummy keys. * */ - public PgpEditKeyResult modifySecretKeyRing(CanonicalizedSecretKeyRing wsKR, SaveKeyringParcel saveParcel, - String passphrase) { + public PgpEditKeyResult modifySecretKeyRing(CanonicalizedSecretKeyRing wsKR, + SaveKeyringParcel saveParcel) { OperationLog log = new OperationLog(); int indent = 0; @@ -386,11 +399,16 @@ public class PgpKeyOperation { return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null); } - // If we have no passphrase, only allow restricted operation - if (passphrase == null) { + if (saveParcel.isRestrictedOnly()) { return internalRestricted(sKR, saveParcel, log); } + // Do we require a passphrase? If so, pass it along + if (!isDivertToCard(masterSecretKey) && !mCryptoInput.hasPassphrase()) { + return new PgpEditKeyResult(log, RequiredInputParcel.createRequiredPassphrase( + masterSecretKey.getKeyID(), mCryptoInput.getSignatureTime())); + } + // read masterKeyFlags, and use the same as before. // since this is the master key, this contains at least CERTIFY_OTHER PGPPublicKey masterPublicKey = masterSecretKey.getPublicKey(); @@ -398,13 +416,13 @@ public class PgpKeyOperation { Date expiryTime = wsKR.getPublicKey().getExpiryTime(); long masterKeyExpiry = expiryTime != null ? expiryTime.getTime() / 1000 : 0L; - return internal(sKR, masterSecretKey, masterKeyFlags, masterKeyExpiry, saveParcel, passphrase, log); + return internal(sKR, masterSecretKey, masterKeyFlags, masterKeyExpiry, saveParcel, log); } private PgpEditKeyResult internal(PGPSecretKeyRing sKR, PGPSecretKey masterSecretKey, int masterKeyFlags, long masterKeyExpiry, - SaveKeyringParcel saveParcel, String passphrase, + SaveKeyringParcel saveParcel, OperationLog log) { int indent = 1; @@ -413,18 +431,25 @@ public class PgpKeyOperation { PGPPublicKey masterPublicKey = masterSecretKey.getPublicKey(); - // 1. Unlock private key - progress(R.string.progress_modify_unlock, 10); - log.add(LogType.MSG_MF_UNLOCK, indent); PGPPrivateKey masterPrivateKey; - { - try { - PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( - Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray()); - masterPrivateKey = masterSecretKey.extractPrivateKey(keyDecryptor); - } catch (PGPException e) { - log.add(LogType.MSG_MF_UNLOCK_ERROR, indent + 1); - return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null); + + if (isDivertToCard(masterSecretKey)) { + masterPrivateKey = null; + log.add(LogType.MSG_MF_DIVERT, indent); + } else { + + // 1. Unlock private key + progress(R.string.progress_modify_unlock, 10); + log.add(LogType.MSG_MF_UNLOCK, indent); + { + try { + PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( + Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(mCryptoInput.getPassphrase()); + masterPrivateKey = masterSecretKey.extractPrivateKey(keyDecryptor); + } catch (PGPException e) { + log.add(LogType.MSG_MF_UNLOCK_ERROR, indent + 1); + return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null); + } } } @@ -479,9 +504,16 @@ public class PgpKeyOperation { boolean isPrimary = saveParcel.mChangePrimaryUserId != null && userId.equals(saveParcel.mChangePrimaryUserId); // generate and add new certificate - PGPSignature cert = generateUserIdSignature(masterPrivateKey, - masterPublicKey, userId, isPrimary, masterKeyFlags, masterKeyExpiry); - modifiedPublicKey = PGPPublicKey.addCertification(modifiedPublicKey, userId, cert); + try { + PGPSignature cert = generateUserIdSignature( + getSignatureGenerator(masterSecretKey, mCryptoInput), + mCryptoInput.getSignatureTime(), + masterPrivateKey, masterPublicKey, userId, + isPrimary, masterKeyFlags, masterKeyExpiry); + modifiedPublicKey = PGPPublicKey.addCertification(modifiedPublicKey, userId, cert); + } catch (NfcInteractionNeeded e) { + mNfcSignOps.addHash(e.hashToSign, e.hashAlgo); + } } subProgressPop(); @@ -508,9 +540,15 @@ public class PgpKeyOperation { PGPUserAttributeSubpacketVector vector = attribute.getVector(); // generate and add new certificate - PGPSignature cert = generateUserAttributeSignature(masterPrivateKey, - masterPublicKey, vector); - modifiedPublicKey = PGPPublicKey.addCertification(modifiedPublicKey, vector, cert); + try { + PGPSignature cert = generateUserAttributeSignature( + getSignatureGenerator(masterSecretKey, mCryptoInput), + mCryptoInput.getSignatureTime(), + masterPrivateKey, masterPublicKey, vector); + modifiedPublicKey = PGPPublicKey.addCertification(modifiedPublicKey, vector, cert); + } catch (NfcInteractionNeeded e) { + mNfcSignOps.addHash(e.hashToSign, e.hashAlgo); + } } subProgressPop(); @@ -538,9 +576,15 @@ public class PgpKeyOperation { // a duplicate revocation will be removed during canonicalization, so no need to // take care of that here. - PGPSignature cert = generateRevocationSignature(masterPrivateKey, - masterPublicKey, userId); - modifiedPublicKey = PGPPublicKey.addCertification(modifiedPublicKey, userId, cert); + try { + PGPSignature cert = generateRevocationSignature( + getSignatureGenerator(masterSecretKey, mCryptoInput), + mCryptoInput.getSignatureTime(), + masterPrivateKey, masterPublicKey, userId); + modifiedPublicKey = PGPPublicKey.addCertification(modifiedPublicKey, userId, cert); + } catch (NfcInteractionNeeded e) { + mNfcSignOps.addHash(e.hashToSign, e.hashAlgo); + } } subProgressPop(); @@ -610,11 +654,18 @@ public class PgpKeyOperation { log.add(LogType.MSG_MF_PRIMARY_REPLACE_OLD, indent); modifiedPublicKey = PGPPublicKey.removeCertification( modifiedPublicKey, userId, currentCert); - PGPSignature newCert = generateUserIdSignature( - masterPrivateKey, masterPublicKey, userId, false, - masterKeyFlags, masterKeyExpiry); - modifiedPublicKey = PGPPublicKey.addCertification( - modifiedPublicKey, userId, newCert); + try { + PGPSignature newCert = generateUserIdSignature( + getSignatureGenerator(masterSecretKey, mCryptoInput), + mCryptoInput.getSignatureTime(), + masterPrivateKey, masterPublicKey, userId, false, + masterKeyFlags, masterKeyExpiry); + modifiedPublicKey = PGPPublicKey.addCertification( + modifiedPublicKey, userId, newCert); + } catch (NfcInteractionNeeded e) { + mNfcSignOps.addHash(e.hashToSign, e.hashAlgo); + } + continue; } @@ -626,11 +677,17 @@ public class PgpKeyOperation { log.add(LogType.MSG_MF_PRIMARY_NEW, indent); modifiedPublicKey = PGPPublicKey.removeCertification( modifiedPublicKey, userId, currentCert); - PGPSignature newCert = generateUserIdSignature( - masterPrivateKey, masterPublicKey, userId, true, - masterKeyFlags, masterKeyExpiry); - modifiedPublicKey = PGPPublicKey.addCertification( - modifiedPublicKey, userId, newCert); + try { + PGPSignature newCert = generateUserIdSignature( + getSignatureGenerator(masterSecretKey, mCryptoInput), + mCryptoInput.getSignatureTime(), + masterPrivateKey, masterPublicKey, userId, true, + masterKeyFlags, masterKeyExpiry); + modifiedPublicKey = PGPPublicKey.addCertification( + modifiedPublicKey, userId, newCert); + } catch (NfcInteractionNeeded e) { + mNfcSignOps.addHash(e.hashToSign, e.hashAlgo); + } ok = true; } @@ -717,7 +774,8 @@ public class PgpKeyOperation { } PGPPublicKey pKey = - updateMasterCertificates(masterPrivateKey, masterPublicKey, + updateMasterCertificates( + masterSecretKey, masterPrivateKey, masterPublicKey, flags, expiry, indent, log); if (pKey == null) { // error log entry has already been added by updateMasterCertificates itself @@ -755,9 +813,16 @@ public class PgpKeyOperation { pKey = PGPPublicKey.removeCertification(pKey, sig); } + PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder() + .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build( + mCryptoInput.getPassphrase()); + PGPPrivateKey subPrivateKey = sKey.extractPrivateKey(keyDecryptor); + PGPSignature sig = generateSubkeyBindingSignature( + getSignatureGenerator(masterSecretKey, mCryptoInput), + mCryptoInput.getSignatureTime(), + masterPublicKey, masterPrivateKey, subPrivateKey, pKey, flags, expiry); + // generate and add new signature - PGPSignature sig = generateSubkeyBindingSignature(masterPublicKey, masterPrivateKey, - sKey, pKey, flags, expiry, passphrase); pKey = PGPPublicKey.addCertification(pKey, sig); sKR = PGPSecretKeyRing.insertSecretKey(sKR, PGPSecretKey.replacePublicKey(sKey, pKey)); } @@ -781,10 +846,17 @@ public class PgpKeyOperation { PGPPublicKey pKey = sKey.getPublicKey(); // generate and add new signature - PGPSignature sig = generateRevocationSignature(masterPublicKey, masterPrivateKey, pKey); - - pKey = PGPPublicKey.addCertification(pKey, sig); - sKR = PGPSecretKeyRing.insertSecretKey(sKR, PGPSecretKey.replacePublicKey(sKey, pKey)); + try { + PGPSignature sig = generateRevocationSignature( + getSignatureGenerator(masterSecretKey, mCryptoInput), + mCryptoInput.getSignatureTime(), + masterPublicKey, masterPrivateKey, pKey); + + pKey = PGPPublicKey.addCertification(pKey, sig); + sKR = PGPSecretKeyRing.insertSecretKey(sKR, PGPSecretKey.replacePublicKey(sKey, pKey)); + } catch (NfcInteractionNeeded e) { + mNfcSignOps.addHash(e.hashToSign, e.hashAlgo); + } } subProgressPop(); @@ -827,19 +899,29 @@ public class PgpKeyOperation { // add subkey binding signature (making this a sub rather than master key) PGPPublicKey pKey = keyPair.getPublicKey(); - PGPSignature cert = generateSubkeyBindingSignature( - masterPublicKey, masterPrivateKey, keyPair.getPrivateKey(), pKey, - add.mFlags, add.mExpiry); - pKey = PGPPublicKey.addSubkeyBindingCertification(pKey, cert); + try { + PGPSignature cert = generateSubkeyBindingSignature( + getSignatureGenerator(masterSecretKey, mCryptoInput), + mCryptoInput.getSignatureTime(), + masterPublicKey, masterPrivateKey, keyPair.getPrivateKey(), pKey, + add.mFlags, add.mExpiry); + pKey = PGPPublicKey.addSubkeyBindingCertification(pKey, cert); + } catch (NfcInteractionNeeded e) { + mNfcSignOps.addHash(e.hashToSign, e.hashAlgo); + } PGPSecretKey sKey; { + char[] passphrase = mCryptoInput.getPassphrase(); + if (passphrase == null) { + passphrase = new char[] { }; + } // Build key encrypter and decrypter based on passphrase PGPDigestCalculator encryptorHashCalc = new JcaPGPDigestCalculatorProviderBuilder() .build().get(PgpConstants.SECRET_KEY_ENCRYPTOR_HASH_ALGO); PBESecretKeyEncryptor keyEncryptor = new JcePBESecretKeyEncryptorBuilder( PgpConstants.SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO, encryptorHashCalc, PgpConstants.SECRET_KEY_ENCRYPTOR_S2K_COUNT) - .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray()); + .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase); PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder() .build().get(PgpConstants.SECRET_KEY_SIGNATURE_CHECKSUM_HASH_ALGO); @@ -867,7 +949,7 @@ public class PgpKeyOperation { indent += 1; sKR = applyNewUnlock(sKR, masterPublicKey, masterPrivateKey, - passphrase, saveParcel.mNewUnlock, log, indent); + mCryptoInput.getPassphrase(), saveParcel.mNewUnlock, log, indent); if (sKR == null) { // The error has been logged above, just return a bad state return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null); @@ -891,6 +973,12 @@ public class PgpKeyOperation { } progress(R.string.progress_done, 100); + + if (!mNfcSignOps.isEmpty()) { + log.add(LogType.MSG_MF_INPUT_REQUIRED, indent); + return new PgpEditKeyResult(log, mNfcSignOps.build()); + } + log.add(LogType.MSG_MF_SUCCESS, indent); return new PgpEditKeyResult(OperationResult.RESULT_OK, log, new UncachedKeyRing(sKR)); @@ -967,7 +1055,7 @@ public class PgpKeyOperation { PGPSecretKeyRing sKR, PGPPublicKey masterPublicKey, PGPPrivateKey masterPrivateKey, - String passphrase, + char[] passphrase, ChangeUnlockParcel newUnlock, OperationLog log, int indent) throws PGPException { @@ -1051,14 +1139,14 @@ public class PgpKeyOperation { private static PGPSecretKeyRing applyNewPassphrase( PGPSecretKeyRing sKR, PGPPublicKey masterPublicKey, - String passphrase, + char[] passphrase, String newPassphrase, OperationLog log, int indent) throws PGPException { PGPDigestCalculator encryptorHashCalc = new JcaPGPDigestCalculatorProviderBuilder().build() .get(PgpConstants.SECRET_KEY_ENCRYPTOR_HASH_ALGO); PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( - Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray()); + Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase); // Build key encryptor based on new passphrase PBESecretKeyEncryptor keyEncryptorNew = new JcePBESecretKeyEncryptorBuilder( PgpConstants.SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO, encryptorHashCalc, @@ -1115,8 +1203,9 @@ public class PgpKeyOperation { } /** Update all (non-revoked) uid signatures with new flags and expiry time. */ - private static PGPPublicKey updateMasterCertificates( - PGPPrivateKey masterPrivateKey, PGPPublicKey masterPublicKey, + private PGPPublicKey updateMasterCertificates( + PGPSecretKey masterSecretKey, PGPPrivateKey masterPrivateKey, + PGPPublicKey masterPublicKey, int flags, long expiry, int indent, OperationLog log) throws PGPException, IOException, SignatureException { @@ -1172,10 +1261,16 @@ public class PgpKeyOperation { currentCert.getHashedSubPackets().isPrimaryUserID(); modifiedPublicKey = PGPPublicKey.removeCertification( modifiedPublicKey, userId, currentCert); - PGPSignature newCert = generateUserIdSignature( - masterPrivateKey, masterPublicKey, userId, isPrimary, flags, expiry); - modifiedPublicKey = PGPPublicKey.addCertification( - modifiedPublicKey, userId, newCert); + try { + PGPSignature newCert = generateUserIdSignature( + getSignatureGenerator(masterSecretKey, mCryptoInput), + mCryptoInput.getSignatureTime(), + masterPrivateKey, masterPublicKey, userId, isPrimary, flags, expiry); + modifiedPublicKey = PGPPublicKey.addCertification( + modifiedPublicKey, userId, newCert); + } catch (NfcInteractionNeeded e) { + mNfcSignOps.addHash(e.hashToSign, e.hashAlgo); + } ok = true; } @@ -1190,15 +1285,37 @@ public class PgpKeyOperation { } - private static PGPSignature generateUserIdSignature( + private static PGPSignatureGenerator getSignatureGenerator( + PGPSecretKey secretKey, CryptoInputParcel cryptoInput) { + + PGPContentSignerBuilder builder; + + S2K s2k = secretKey.getS2K(); + if (s2k != null && s2k.getType() == S2K.GNU_DUMMY_S2K + && s2k.getProtectionMode() == S2K.GNU_PROTECTION_MODE_DIVERT_TO_CARD) { + // use synchronous "NFC based" SignerBuilder + builder = new NfcSyncPGPContentSignerBuilder( + secretKey.getPublicKey().getAlgorithm(), + PgpConstants.SECRET_KEY_SIGNATURE_HASH_ALGO, + secretKey.getKeyID(), cryptoInput.getCryptoData()) + .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); + } else { + // content signer based on signing key algorithm and chosen hash algorithm + builder = new JcaPGPContentSignerBuilder( + secretKey.getPublicKey().getAlgorithm(), + PgpConstants.SECRET_KEY_SIGNATURE_HASH_ALGO) + .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); + } + + return new PGPSignatureGenerator(builder); + + } + + private PGPSignature generateUserIdSignature( + PGPSignatureGenerator sGen, Date creationTime, PGPPrivateKey masterPrivateKey, PGPPublicKey pKey, String userId, boolean primary, int flags, long expiry) throws IOException, PGPException, SignatureException { - PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder( - masterPrivateKey.getPublicKeyPacket().getAlgorithm(), - PgpConstants.SECRET_KEY_SIGNATURE_HASH_ALGO) - .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); - PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder); PGPSignatureSubpacketGenerator hashedPacketsGen = new PGPSignatureSubpacketGenerator(); { @@ -1222,7 +1339,7 @@ public class PgpKeyOperation { hashedPacketsGen.setPrimaryUserID(false, primary); /* critical subpackets: we consider those important for a modern pgp implementation */ - hashedPacketsGen.setSignatureCreationTime(true, new Date()); + hashedPacketsGen.setSignatureCreationTime(true, creationTime); // Request that senders add the MDC to the message (authenticate unsigned messages) hashedPacketsGen.setFeature(true, Features.FEATURE_MODIFICATION_DETECTION); hashedPacketsGen.setKeyFlags(true, flags); @@ -1238,19 +1355,15 @@ public class PgpKeyOperation { } private static PGPSignature generateUserAttributeSignature( + PGPSignatureGenerator sGen, Date creationTime, PGPPrivateKey masterPrivateKey, PGPPublicKey pKey, PGPUserAttributeSubpacketVector vector) throws IOException, PGPException, SignatureException { - PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder( - masterPrivateKey.getPublicKeyPacket().getAlgorithm(), - PgpConstants.SECRET_KEY_SIGNATURE_HASH_ALGO) - .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); - PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder); PGPSignatureSubpacketGenerator hashedPacketsGen = new PGPSignatureSubpacketGenerator(); { /* critical subpackets: we consider those important for a modern pgp implementation */ - hashedPacketsGen.setSignatureCreationTime(true, new Date()); + hashedPacketsGen.setSignatureCreationTime(true, creationTime); } sGen.setHashedSubpackets(hashedPacketsGen.generate()); @@ -1259,29 +1372,24 @@ public class PgpKeyOperation { } private static PGPSignature generateRevocationSignature( + PGPSignatureGenerator sGen, Date creationTime, PGPPrivateKey masterPrivateKey, PGPPublicKey pKey, String userId) + throws IOException, PGPException, SignatureException { - PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder( - masterPrivateKey.getPublicKeyPacket().getAlgorithm(), - PgpConstants.SECRET_KEY_SIGNATURE_HASH_ALGO) - .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); - PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder); PGPSignatureSubpacketGenerator subHashedPacketsGen = new PGPSignatureSubpacketGenerator(); - subHashedPacketsGen.setSignatureCreationTime(true, new Date()); + subHashedPacketsGen.setSignatureCreationTime(true, creationTime); sGen.setHashedSubpackets(subHashedPacketsGen.generate()); sGen.init(PGPSignature.CERTIFICATION_REVOCATION, masterPrivateKey); return sGen.generateCertification(userId, pKey); } private static PGPSignature generateRevocationSignature( + PGPSignatureGenerator sGen, Date creationTime, PGPPublicKey masterPublicKey, PGPPrivateKey masterPrivateKey, PGPPublicKey pKey) throws IOException, PGPException, SignatureException { - PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder( - masterPublicKey.getAlgorithm(), PgpConstants.SECRET_KEY_SIGNATURE_HASH_ALGO) - .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); - PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder); + PGPSignatureSubpacketGenerator subHashedPacketsGen = new PGPSignatureSubpacketGenerator(); - subHashedPacketsGen.setSignatureCreationTime(true, new Date()); + subHashedPacketsGen.setSignatureCreationTime(true, creationTime); sGen.setHashedSubpackets(subHashedPacketsGen.generate()); // Generate key revocation or subkey revocation, depending on master/subkey-ness if (masterPublicKey.getKeyID() == pKey.getKeyID()) { @@ -1293,26 +1401,12 @@ public class PgpKeyOperation { } } - private static PGPSignature generateSubkeyBindingSignature( - PGPPublicKey masterPublicKey, PGPPrivateKey masterPrivateKey, - PGPSecretKey sKey, PGPPublicKey pKey, int flags, long expiry, String passphrase) - throws IOException, PGPException, SignatureException { - PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder() - .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build( - passphrase.toCharArray()); - PGPPrivateKey subPrivateKey = sKey.extractPrivateKey(keyDecryptor); - return generateSubkeyBindingSignature(masterPublicKey, masterPrivateKey, subPrivateKey, - pKey, flags, expiry); - } - static PGPSignature generateSubkeyBindingSignature( + PGPSignatureGenerator sGen, Date creationTime, PGPPublicKey masterPublicKey, PGPPrivateKey masterPrivateKey, PGPPrivateKey subPrivateKey, PGPPublicKey pKey, int flags, long expiry) throws IOException, PGPException, SignatureException { - // date for signing - Date creationTime = new Date(); - PGPSignatureSubpacketGenerator unhashedPacketsGen = new PGPSignatureSubpacketGenerator(); // If this key can sign, we need a primary key binding signature @@ -1323,10 +1417,10 @@ public class PgpKeyOperation { PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder( pKey.getAlgorithm(), PgpConstants.SECRET_KEY_SIGNATURE_HASH_ALGO) .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); - PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder); - sGen.init(PGPSignature.PRIMARYKEY_BINDING, subPrivateKey); - sGen.setHashedSubpackets(subHashedPacketsGen.generate()); - PGPSignature certification = sGen.generateCertification(masterPublicKey, pKey); + PGPSignatureGenerator subSigGen = new PGPSignatureGenerator(signerBuilder); + subSigGen.init(PGPSignature.PRIMARYKEY_BINDING, subPrivateKey); + subSigGen.setHashedSubpackets(subHashedPacketsGen.generate()); + PGPSignature certification = subSigGen.generateCertification(masterPublicKey, pKey); unhashedPacketsGen.setEmbeddedSignature(true, certification); } @@ -1341,10 +1435,6 @@ public class PgpKeyOperation { } } - PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder( - masterPublicKey.getAlgorithm(), PgpConstants.SECRET_KEY_SIGNATURE_HASH_ALGO) - .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); - PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder); sGen.init(PGPSignature.SUBKEY_BINDING, masterPrivateKey); sGen.setHashedSubpackets(hashedPacketsGen.generate()); sGen.setUnhashedSubpackets(unhashedPacketsGen.generate()); @@ -1371,4 +1461,10 @@ public class PgpKeyOperation { return flags; } + private static boolean isDivertToCard(PGPSecretKey secretKey) { + S2K s2k = secretKey.getS2K(); + return s2k.getType() == S2K.GNU_DUMMY_S2K + && s2k.getProtectionMode() == S2K.GNU_PROTECTION_MODE_DIVERT_TO_CARD; + } + } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CertifyActionsParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CertifyActionsParcel.java index 63a7bf371..405a6a24b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CertifyActionsParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CertifyActionsParcel.java @@ -76,10 +76,6 @@ public class CertifyActionsParcel implements Parcelable { return mCryptoInput.getCryptoData(); } - public Date getSignatureTime() { - return mCryptoInput.getSignatureTime(); - } - public static final Creator CREATOR = new Creator() { public CertifyActionsParcel createFromParcel(final Parcel source) { return new CertifyActionsParcel(source); 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 bf6a62782..6535cb404 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java @@ -61,6 +61,7 @@ import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralMsgIdException; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler.MessageStatus; +import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; import org.sufficientlysecure.keychain.util.FileHelper; import org.sufficientlysecure.keychain.util.InputData; import org.sufficientlysecure.keychain.util.Log; @@ -160,6 +161,7 @@ public class KeychainIntentService extends IntentService implements Progressable // save keyring public static final String EDIT_KEYRING_PARCEL = "save_parcel"; public static final String EDIT_KEYRING_PASSPHRASE = "passphrase"; + public static final String EXTRA_CRYPTO_INPUT = "crypto_input"; // delete keyring(s) public static final String DELETE_KEY_LIST = "delete_list"; @@ -469,11 +471,11 @@ public class KeychainIntentService extends IntentService implements Progressable // Input SaveKeyringParcel saveParcel = data.getParcelable(EDIT_KEYRING_PARCEL); - String passphrase = data.getString(EDIT_KEYRING_PASSPHRASE); + CryptoInputParcel cryptoInput = data.getParcelable(EXTRA_CRYPTO_INPUT); // Operation EditKeyOperation op = new EditKeyOperation(this, providerHelper, this, mActionCanceled); - EditKeyResult result = op.execute(saveParcel, passphrase); + OperationResult result = op.execute(saveParcel, cryptoInput); // Result sendMessageToHandler(MessageStatus.OKAY, result); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/CryptoInputParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/CryptoInputParcel.java index 7cc5069c8..836568533 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/CryptoInputParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/CryptoInputParcel.java @@ -81,8 +81,8 @@ public class CryptoInputParcel implements Parcelable { return mPassphrase != null; } - public String getPassphrase() { - return mPassphrase; + public char[] getPassphrase() { + return mPassphrase == null ? null : mPassphrase.toCharArray(); } public static final Creator CREATOR = new Creator() { @@ -95,4 +95,19 @@ public class CryptoInputParcel implements Parcelable { } }; + @Override + public String toString() { + StringBuilder b = new StringBuilder(); + b.append("CryptoInput: { "); + b.append(mSignatureTime).append(" "); + if (mPassphrase != null) { + b.append("passphrase"); + } + if (mCryptoData != null) { + b.append(mCryptoData.size()); + b.append(" hashes "); + } + b.append("}"); + return b.toString(); + } } 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 a4df604be..3d91812eb 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 @@ -15,9 +15,13 @@ public class RequiredInputParcel implements Parcelable { } public Date mSignatureTime; + public final RequiredInputType mType; + + public String mNfcPin = "123456"; public final byte[][] mInputHashes; public final int[] mSignAlgos; + private Long mSubKeyId; private RequiredInputParcel(RequiredInputType type, byte[][] inputHashes, diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CryptoOperationFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CryptoOperationFragment.java index 585a9433a..6b67d1db8 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CryptoOperationFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CryptoOperationFragment.java @@ -9,6 +9,7 @@ import android.support.v4.app.Fragment; import org.sufficientlysecure.keychain.operations.results.CertifyResult; import org.sufficientlysecure.keychain.operations.results.InputPendingResult; +import org.sufficientlysecure.keychain.operations.results.OperationResult; import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler.MessageStatus; import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; import org.sufficientlysecure.keychain.service.input.RequiredInputParcel; @@ -25,7 +26,6 @@ public abstract class CryptoOperationFragment extends Fragment { case NFC_DECRYPT: case NFC_SIGN: { Intent intent = new Intent(getActivity(), NfcOperationActivity.class); - intent.putExtra(NfcOperationActivity.EXTRA_PIN, "123456"); intent.putExtra(NfcOperationActivity.EXTRA_REQUIRED_INPUT, requiredInput); startActivityForResult(intent, REQUEST_CODE_NFC); return; @@ -76,10 +76,14 @@ public abstract class CryptoOperationFragment extends Fragment { if (message.arg1 == MessageStatus.OKAY.ordinal()) { Bundle data = message.getData(); - InputPendingResult result = data.getParcelable(CertifyResult.EXTRA_RESULT); + OperationResult result = data.getParcelable(CertifyResult.EXTRA_RESULT); + if (result == null || ! (result instanceof InputPendingResult)) { + return false; + } - if (result != null && result.isPending()) { - RequiredInputParcel requiredInput = result.getRequiredInputParcel(); + InputPendingResult pendingResult = (InputPendingResult) result; + if (pendingResult.isPending()) { + RequiredInputParcel requiredInput = pendingResult.getRequiredInputParcel(); initiateInputActivity(requiredInput); return true; } 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 8d16fe47e..9f2fa54f8 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java @@ -17,6 +17,8 @@ package org.sufficientlysecure.keychain.ui; +import java.util.Date; + import android.app.Activity; import android.app.ProgressDialog; import android.content.Intent; @@ -51,10 +53,10 @@ import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.provider.ProviderHelper.NotFoundException; import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; -import org.sufficientlysecure.keychain.service.PassphraseCacheService; import org.sufficientlysecure.keychain.service.SaveKeyringParcel; import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel; import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyChange; +import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; import org.sufficientlysecure.keychain.ui.adapter.SubkeysAdapter; import org.sufficientlysecure.keychain.ui.adapter.SubkeysAddedAdapter; import org.sufficientlysecure.keychain.ui.adapter.UserIdsAdapter; @@ -68,14 +70,12 @@ import org.sufficientlysecure.keychain.ui.dialog.SetPassphraseDialogFragment; import org.sufficientlysecure.keychain.ui.util.Notify; import org.sufficientlysecure.keychain.util.Log; -public class EditKeyFragment extends LoaderFragment implements +public class EditKeyFragment extends CryptoOperationFragment implements LoaderManager.LoaderCallbacks { public static final String ARG_DATA_URI = "uri"; public static final String ARG_SAVE_KEYRING_PARCEL = "save_keyring_parcel"; - public static final int REQUEST_CODE_PASSPHRASE = 0x00008001; - private ListView mUserIdsList; private ListView mSubkeysList; private ListView mUserIdsAddedList; @@ -100,7 +100,6 @@ public class EditKeyFragment extends LoaderFragment implements private SaveKeyringParcel mSaveKeyringParcel; private String mPrimaryUserId; - private String mCurrentPassphrase; /** * Creates new instance of this fragment @@ -129,8 +128,7 @@ public class EditKeyFragment extends LoaderFragment implements @Override public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) { - View root = super.onCreateView(inflater, superContainer, savedInstanceState); - View view = inflater.inflate(R.layout.edit_key_fragment, getContainer()); + View view = inflater.inflate(R.layout.edit_key_fragment, null); mUserIdsList = (ListView) view.findViewById(R.id.edit_key_user_ids); mSubkeysList = (ListView) view.findViewById(R.id.edit_key_keys); @@ -140,7 +138,7 @@ public class EditKeyFragment extends LoaderFragment implements mAddUserId = view.findViewById(R.id.edit_key_action_add_user_id); mAddSubkey = view.findViewById(R.id.edit_key_action_add_key); - return root; + return view; } @Override @@ -155,7 +153,7 @@ public class EditKeyFragment extends LoaderFragment implements if (mDataUri == null) { returnKeyringParcel(); } else { - saveInDatabase(mCurrentPassphrase); + cryptoOperation(new CryptoInputParcel(new Date())); } } }, new OnClickListener() { @@ -185,18 +183,12 @@ public class EditKeyFragment extends LoaderFragment implements private void loadSaveKeyringParcel(SaveKeyringParcel saveKeyringParcel) { mSaveKeyringParcel = saveKeyringParcel; mPrimaryUserId = saveKeyringParcel.mChangePrimaryUserId; - if (saveKeyringParcel.mNewUnlock != null) { - mCurrentPassphrase = saveKeyringParcel.mNewUnlock.mNewPassphrase; - } mUserIdsAddedAdapter = new UserIdsAddedAdapter(getActivity(), mSaveKeyringParcel.mAddUserIds, true); mUserIdsAddedList.setAdapter(mUserIdsAddedAdapter); mSubkeysAddedAdapter = new SubkeysAddedAdapter(getActivity(), mSaveKeyringParcel.mAddSubKeys, true); mSubkeysAddedList.setAdapter(mSubkeysAddedAdapter); - - // show directly - setContentShown(true); } private void loadData(Uri dataUri) { @@ -216,9 +208,6 @@ public class EditKeyFragment extends LoaderFragment implements case GNU_DUMMY: finishWithError(LogType.MSG_EK_ERROR_DUMMY); return; - case DIVERT_TO_CARD: - finishWithError(LogType.MSG_EK_ERROR_DIVERT); - break; } mSaveKeyringParcel = new SaveKeyringParcel(masterKeyId, keyRing.getFingerprint()); @@ -229,24 +218,10 @@ public class EditKeyFragment extends LoaderFragment implements return; } - try { - mCurrentPassphrase = PassphraseCacheService.getCachedPassphrase(getActivity(), - mSaveKeyringParcel.mMasterKeyId, mSaveKeyringParcel.mMasterKeyId); - } catch (PassphraseCacheService.KeyNotFoundException e) { - finishWithError(LogType.MSG_EK_ERROR_NOT_FOUND); - return; - } - - if (mCurrentPassphrase == null) { - Intent intent = new Intent(getActivity(), PassphraseDialogActivity.class); - intent.putExtra(PassphraseDialogActivity.EXTRA_SUBKEY_ID, mSaveKeyringParcel.mMasterKeyId); - startActivityForResult(intent, REQUEST_CODE_PASSPHRASE); - } else { - // Prepare the loaders. Either re-connect with an existing ones, - // or start new ones. - getLoaderManager().initLoader(LOADER_ID_USER_IDS, null, EditKeyFragment.this); - getLoaderManager().initLoader(LOADER_ID_SUBKEYS, null, EditKeyFragment.this); - } + // Prepare the loaders. Either re-connect with an existing ones, + // or start new ones. + getLoaderManager().initLoader(LOADER_ID_USER_IDS, null, EditKeyFragment.this); + getLoaderManager().initLoader(LOADER_ID_SUBKEYS, null, EditKeyFragment.this); mUserIdsAdapter = new UserIdsAdapter(getActivity(), null, 0, mSaveKeyringParcel); mUserIdsList.setAdapter(mUserIdsAdapter); @@ -262,28 +237,6 @@ public class EditKeyFragment extends LoaderFragment implements mSubkeysAddedList.setAdapter(mSubkeysAddedAdapter); } - @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) { - switch (requestCode) { - case REQUEST_CODE_PASSPHRASE: { - if (resultCode == Activity.RESULT_OK && data != null) { - mCurrentPassphrase = data.getStringExtra(PassphraseDialogActivity.MESSAGE_DATA_PASSPHRASE); - // Prepare the loaders. Either re-connect with an existing ones, - // or start new ones. - getLoaderManager().initLoader(LOADER_ID_USER_IDS, null, EditKeyFragment.this); - getLoaderManager().initLoader(LOADER_ID_SUBKEYS, null, EditKeyFragment.this); - } else { - getActivity().finish(); - } - return; - } - - default: { - super.onActivityResult(requestCode, resultCode, data); - } - } - } - private void initView() { mChangePassphrase.setOnClickListener(new View.OnClickListener() { @Override @@ -322,7 +275,6 @@ public class EditKeyFragment extends LoaderFragment implements } public Loader onCreateLoader(int id, Bundle args) { - setContentShown(false); switch (id) { case LOADER_ID_USER_IDS: { @@ -355,7 +307,6 @@ public class EditKeyFragment extends LoaderFragment implements break; } - setContentShown(true); } /** @@ -397,7 +348,7 @@ public class EditKeyFragment extends LoaderFragment implements Messenger messenger = new Messenger(returnHandler); SetPassphraseDialogFragment setPassphraseDialog = SetPassphraseDialogFragment.newInstance( - messenger, mCurrentPassphrase, R.string.title_change_passphrase); + messenger, R.string.title_change_passphrase); setPassphraseDialog.show(getActivity().getSupportFragmentManager(), "setPassphraseDialog"); } @@ -593,8 +544,11 @@ public class EditKeyFragment extends LoaderFragment implements getActivity().finish(); } - private void saveInDatabase(String passphrase) { - Log.d(Constants.TAG, "mSaveKeyringParcel:\n" + mSaveKeyringParcel.toString()); + @Override + protected void cryptoOperation(CryptoInputParcel cryptoInput) { + + Log.d(Constants.TAG, "cryptoInput:\n" + cryptoInput); + Log.d(Constants.TAG, "mSaveKeyringParcel:\n" + mSaveKeyringParcel); KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler( getActivity(), @@ -605,6 +559,10 @@ public class EditKeyFragment extends LoaderFragment implements // handle messages by standard KeychainIntentServiceHandler first super.handleMessage(message); + if (handlePendingMessage(message)) { + return; + } + if (message.arg1 == MessageStatus.OKAY.ordinal()) { // get returned data bundle @@ -640,7 +598,7 @@ public class EditKeyFragment extends LoaderFragment implements // fill values for this action Bundle data = new Bundle(); - data.putString(KeychainIntentService.EDIT_KEYRING_PASSPHRASE, passphrase); + data.putParcelable(KeychainIntentService.EXTRA_CRYPTO_INPUT, cryptoInput); data.putParcelable(KeychainIntentService.EDIT_KEYRING_PARCEL, mSaveKeyringParcel); intent.putExtra(KeychainIntentService.EXTRA_DATA, data); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java index f9b8e2ab6..66d1ad5a6 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java @@ -62,10 +62,9 @@ public abstract class EncryptActivity extends BaseActivity { startActivityForResult(intent, REQUEST_CODE_PASSPHRASE); } - protected void startNfcSign(long keyId, String pin, RequiredInputParcel nfcOps) { + protected void startNfcSign(long keyId, RequiredInputParcel nfcOps) { Intent intent = new Intent(this, NfcOperationActivity.class); - intent.putExtra(NfcOperationActivity.EXTRA_PIN, pin); intent.putExtra(NfcOperationActivity.EXTRA_REQUIRED_INPUT, nfcOps); // TODO respect keyid(?) @@ -148,8 +147,7 @@ public abstract class EncryptActivity extends BaseActivity { pgpResult.getNfcHash(), pgpResult.getNfcAlgo(), input.getSignatureTime()); - startNfcSign(pgpResult.getNfcKeyId(), - pgpResult.getNfcPassphrase(), parcel); + startNfcSign(pgpResult.getNfcKeyId(), parcel); } else { throw new RuntimeException("Unhandled pending result!"); 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 6a83c2361..69467daac 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcOperationActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcOperationActivity.java @@ -40,7 +40,6 @@ import java.nio.ByteBuffer; @TargetApi(Build.VERSION_CODES.GINGERBREAD_MR1) public class NfcOperationActivity extends BaseActivity { - public static final String EXTRA_PIN = "pin"; public static final String EXTRA_REQUIRED_INPUT = "required_input"; public static final String RESULT_DATA = "result_data"; @@ -50,8 +49,6 @@ public class NfcOperationActivity extends BaseActivity { private NfcAdapter mNfcAdapter; private IsoDep mIsoDep; - private String mPin; - RequiredInputParcel mNfcOperations; @Override @@ -70,7 +67,6 @@ public class NfcOperationActivity extends BaseActivity { Bundle data = intent.getExtras(); mNfcOperations = data.getParcelable(EXTRA_REQUIRED_INPUT); - mPin = data.getString(EXTRA_PIN); } @@ -161,14 +157,16 @@ public class NfcOperationActivity extends BaseActivity { return; } + String pin = mNfcOperations.mNfcPin; + // Command APDU for VERIFY command (page 32) String login = "00" // CLA + "20" // INS + "00" // P1 + "82" // P2 (PW1) - + String.format("%02x", mPin.length()) // Lc - + Hex.toHexString(mPin.getBytes()); + + String.format("%02x", pin.length()) // Lc + + Hex.toHexString(pin.getBytes()); if ( ! card(login).equals(accepted)) { // login toast("Wrong PIN!"); setResult(RESULT_CANCELED); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java index b34dc2edc..dc8d8f303 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java @@ -49,7 +49,6 @@ import org.sufficientlysecure.keychain.util.Log; public class SetPassphraseDialogFragment extends DialogFragment implements OnEditorActionListener { private static final String ARG_MESSENGER = "messenger"; private static final String ARG_TITLE = "title"; - private static final String ARG_OLD_PASSPHRASE = "old_passphrase"; public static final int MESSAGE_OKAY = 1; @@ -67,12 +66,11 @@ public class SetPassphraseDialogFragment extends DialogFragment implements OnEdi * @param messenger to communicate back after setting the passphrase * @return */ - public static SetPassphraseDialogFragment newInstance(Messenger messenger, String oldPassphrase, int title) { + public static SetPassphraseDialogFragment newInstance(Messenger messenger, int title) { SetPassphraseDialogFragment frag = new SetPassphraseDialogFragment(); Bundle args = new Bundle(); args.putInt(ARG_TITLE, title); args.putParcelable(ARG_MESSENGER, messenger); - args.putString(ARG_OLD_PASSPHRASE, oldPassphrase); frag.setArguments(args); @@ -88,7 +86,6 @@ public class SetPassphraseDialogFragment extends DialogFragment implements OnEdi int title = getArguments().getInt(ARG_TITLE); mMessenger = getArguments().getParcelable(ARG_MESSENGER); - String oldPassphrase = getArguments().getString(ARG_OLD_PASSPHRASE); CustomAlertDialogBuilder alert = new CustomAlertDialogBuilder(activity); @@ -102,13 +99,6 @@ public class SetPassphraseDialogFragment extends DialogFragment implements OnEdi mPassphraseAgainEditText = (EditText) view.findViewById(R.id.passphrase_passphrase_again); mNoPassphraseCheckBox = (CheckBox) view.findViewById(R.id.passphrase_no_passphrase); - - if (TextUtils.isEmpty(oldPassphrase)) { - mNoPassphraseCheckBox.setChecked(true); - mPassphraseEditText.setEnabled(false); - mPassphraseAgainEditText.setEnabled(false); - } - mNoPassphraseCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index 2eb167c8c..e8bcbcc31 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -897,6 +897,7 @@ "Modifying keyring %s" + "Will divert to card/nfc for crypto operations" "The serial number of a divert-to-card key must be 16 bytes! This is a programming error, please file a bug report!" "Encoding exception!" "Actual key fingerprint does not match the expected one!" @@ -911,6 +912,7 @@ "Fatal error decrypting master key! This is likely a programming error, please file a bug report!" "Internal OpenPGP error!" "Signature exception!" + "Diverting to card/nfc for crypto operations" "Modifying master certifications" "Adding empty notation packet" "Adding PIN notation packet" @@ -987,7 +989,6 @@ "Key successfully promoted" - "Editing of NFC keys is not (yet) supported!" "Cannot edit keyring with stripped master key!" "Key not found!" -- cgit v1.2.3