diff options
Diffstat (limited to 'OpenKeychain/src/main/java/org')
32 files changed, 1730 insertions, 590 deletions
| diff --git a/OpenKeychain/src/main/java/org/spongycastle/openpgp/operator/jcajce/NfcSyncPGPContentSignerBuilder.java b/OpenKeychain/src/main/java/org/spongycastle/openpgp/operator/jcajce/NfcSyncPGPContentSignerBuilder.java index e0286ec15..0344b2173 100644 --- a/OpenKeychain/src/main/java/org/spongycastle/openpgp/operator/jcajce/NfcSyncPGPContentSignerBuilder.java +++ b/OpenKeychain/src/main/java/org/spongycastle/openpgp/operator/jcajce/NfcSyncPGPContentSignerBuilder.java @@ -14,8 +14,12 @@ import org.spongycastle.openpgp.operator.PGPContentSignerBuilder;  import org.spongycastle.openpgp.operator.PGPDigestCalculator;  import java.io.OutputStream; +import java.nio.ByteBuffer;  import java.security.Provider;  import java.util.Date; +import java.util.HashMap; +import java.util.Map; +  /**   * This class is based on JcaPGPContentSignerBuilder. @@ -31,31 +35,27 @@ public class NfcSyncPGPContentSignerBuilder      private int     keyAlgorithm;      private long    keyID; -    private byte[] signedHash; -    private Date creationTimestamp; +    private Map signedHashes;      public static class NfcInteractionNeeded extends RuntimeException      {          public byte[] hashToSign; -        public Date creationTimestamp;          public int hashAlgo; -        public NfcInteractionNeeded(byte[] hashToSign, int hashAlgo, Date creationTimestamp) +        public NfcInteractionNeeded(byte[] hashToSign, int hashAlgo)          {              super("NFC interaction required!");              this.hashToSign = hashToSign;              this.hashAlgo = hashAlgo; -            this.creationTimestamp = creationTimestamp;          }      } -    public NfcSyncPGPContentSignerBuilder(int keyAlgorithm, int hashAlgorithm, long keyID, byte[] signedHash, Date creationTimestamp) +    public NfcSyncPGPContentSignerBuilder(int keyAlgorithm, int hashAlgorithm, long keyID, Map signedHashes)      {          this.keyAlgorithm = keyAlgorithm;          this.hashAlgorithm = hashAlgorithm;          this.keyID = keyID; -        this.signedHash = signedHash; -        this.creationTimestamp = creationTimestamp; +        this.signedHashes = signedHashes;      }      public NfcSyncPGPContentSignerBuilder setProvider(Provider provider) @@ -125,14 +125,14 @@ public class NfcSyncPGPContentSignerBuilder              }              public byte[] getSignature() { -                if (signedHash != null) { -                    // we already have the signed hash from a previous execution, return this! -                    return signedHash; -                } else { -                    // catch this when signatureGenerator.generate() is executed and divert digest to card, -                    // when doing the operation again reuse creationTimestamp (this will be hashed) -                    throw new NfcInteractionNeeded(digestCalculator.getDigest(), getHashAlgorithm(), creationTimestamp); +                byte[] digest = digestCalculator.getDigest(); +                ByteBuffer buf = ByteBuffer.wrap(digest); +                if (signedHashes.containsKey(buf)) { +                    return (byte[]) signedHashes.get(buf);                  } +                // catch this when signatureGenerator.generate() is executed and divert digest to card, +                // when doing the operation again reuse creationTimestamp (this will be hashed) +                throw new NfcInteractionNeeded(digest, getHashAlgorithm());              }              public byte[] getDigest() 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 4ceb34722..eb2a3d33f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/CertifyOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/CertifyOperation.java @@ -28,8 +28,9 @@ import org.sufficientlysecure.keychain.operations.results.OperationResult.Operat  import org.sufficientlysecure.keychain.operations.results.SaveKeyringResult;  import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing;  import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey; -import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType;  import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing; +import org.sufficientlysecure.keychain.pgp.PgpCertifyOperation; +import org.sufficientlysecure.keychain.pgp.PgpCertifyOperation.PgpCertifyResult;  import org.sufficientlysecure.keychain.pgp.Progressable;  import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;  import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; @@ -38,6 +39,9 @@ import org.sufficientlysecure.keychain.provider.ProviderHelper.NotFoundException  import org.sufficientlysecure.keychain.service.CertifyActionsParcel;  import org.sufficientlysecure.keychain.service.CertifyActionsParcel.CertifyAction;  import org.sufficientlysecure.keychain.service.ContactSyncAdapterService; +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.Log;  import org.sufficientlysecure.keychain.util.Passphrase; @@ -60,7 +64,7 @@ public class CertifyOperation extends BaseOperation {          super(context, providerHelper, progressable, cancelled);      } -    public CertifyResult certify(CertifyActionsParcel parcel, String keyServerUri) { +    public CertifyResult certify(CertifyActionsParcel parcel, CryptoInputParcel cryptoInput, String keyServerUri) {          OperationLog log = new OperationLog();          log.add(LogType.MSG_CRT, 0); @@ -74,13 +78,14 @@ public class CertifyOperation extends BaseOperation {                      mProviderHelper.getCanonicalizedSecretKeyRing(parcel.mMasterKeyId);              log.add(LogType.MSG_CRT_UNLOCK, 1);              certificationKey = secretKeyRing.getSecretKey(); -            if (certificationKey.getSecretKeyType() == SecretKeyType.DIVERT_TO_CARD) { -                log.add(LogType.MSG_CRT_ERROR_DIVERT, 2); -                return new CertifyResult(CertifyResult.RESULT_ERROR, log); + +            if (!cryptoInput.hasPassphrase()) { +                return new CertifyResult(log, RequiredInputParcel.createRequiredPassphrase( +                        certificationKey.getKeyId(), certificationKey.getKeyId(), null));              }              // certification is always with the master key id, so use that one -            Passphrase passphrase = getCachedPassphrase(parcel.mMasterKeyId, parcel.mMasterKeyId); +            Passphrase passphrase = cryptoInput.getPassphrase();              if (!certificationKey.unlock(passphrase)) {                  log.add(LogType.MSG_CRT_ERROR_UNLOCK, 2); @@ -92,9 +97,6 @@ public class CertifyOperation extends BaseOperation {          } catch (NotFoundException e) {              log.add(LogType.MSG_CRT_ERROR_MASTER_NOT_FOUND, 2);              return new CertifyResult(CertifyResult.RESULT_ERROR, log); -        } catch (NoSecretKeyException e) { -            log.add(LogType.MSG_CRT_ERROR_MASTER_NOT_FOUND, 2); -            return new CertifyResult(CertifyResult.RESULT_ERROR, log);          }          ArrayList<UncachedKeyRing> certifiedKeys = new ArrayList<>(); @@ -103,6 +105,10 @@ public class CertifyOperation extends BaseOperation {          int certifyOk = 0, certifyError = 0, uploadOk = 0, uploadError = 0; +        NfcSignOperationsBuilder allRequiredInput = new NfcSignOperationsBuilder( +                cryptoInput.getSignatureTime(), certificationKey.getKeyId(), +                certificationKey.getKeyId()); +          // Work through all requested certifications          for (CertifyAction action : parcel.mCertifyActions) { @@ -123,28 +129,21 @@ public class CertifyOperation extends BaseOperation {                  CanonicalizedPublicKeyRing publicRing =                          mProviderHelper.getCanonicalizedPublicKeyRing(action.mMasterKeyId); -                UncachedKeyRing certifiedKey = null; -                if (action.mUserIds != null) { -                    log.add(LogType.MSG_CRT_CERTIFY_UIDS, 2, action.mUserIds.size(), -                            KeyFormattingUtils.convertKeyIdToHex(action.mMasterKeyId)); +                PgpCertifyOperation op = new PgpCertifyOperation(); +                PgpCertifyResult result = op.certify(certificationKey, publicRing, +                        log, 2, action, cryptoInput.getCryptoData(), cryptoInput.getSignatureTime()); -                    certifiedKey = certificationKey.certifyUserIds( -                            publicRing, action.mUserIds, null, null); +                if (!result.success()) { +                    certifyError += 1; +                    continue;                  } - -                if (action.mUserAttributes != null) { -                    log.add(LogType.MSG_CRT_CERTIFY_UATS, 2, action.mUserAttributes.size(), -                            KeyFormattingUtils.convertKeyIdToHex(action.mMasterKeyId)); - -                    certifiedKey = certificationKey.certifyUserAttributes( -                            publicRing, action.mUserAttributes, null, null); +                if (result.nfcInputRequired()) { +                    RequiredInputParcel requiredInput = result.getRequiredInput(); +                    allRequiredInput.addAll(requiredInput); +                    continue;                  } -                if (certifiedKey == null) { -                    certifyError += 1; -                    log.add(LogType.MSG_CRT_WARN_CERT_FAILED, 3); -                } -                certifiedKeys.add(certifiedKey); +                certifiedKeys.add(result.getCertifiedRing());              } catch (NotFoundException e) {                  certifyError += 1; @@ -153,6 +152,11 @@ public class CertifyOperation extends BaseOperation {          } +        if ( ! allRequiredInput.isEmpty()) { +            log.add(LogType.MSG_CRT_NFC_RETURN, 1); +            return new CertifyResult(log, allRequiredInput.build()); +        } +          log.add(LogType.MSG_CRT_SAVING, 1);          // Check if we were cancelled 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 a179b53ee..4072d91c5 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.Passphrase;  import org.sufficientlysecure.keychain.util.ProgressScaler; @@ -56,7 +58,7 @@ public class EditKeyOperation extends BaseOperation {          super(context, providerHelper, progressable, cancelled);      } -    public EditKeyResult execute(SaveKeyringParcel saveParcel, Passphrase passphrase) { +    public OperationResult execute(SaveKeyringParcel saveParcel, CryptoInputParcel cryptoInput) {          OperationLog log = new OperationLog();          log.add(LogType.MSG_ED, 0); @@ -81,7 +83,10 @@ public class EditKeyOperation extends BaseOperation {                      CanonicalizedSecretKeyRing secRing =                              mProviderHelper.getCanonicalizedSecretKeyRing(saveParcel.mMasterKeyId); -                    modifyResult = keyOperations.modifySecretKeyRing(secRing, saveParcel, passphrase); +                    modifyResult = keyOperations.modifySecretKeyRing(secRing, cryptoInput, 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/CertifyResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/CertifyResult.java index f56fe4bb9..0a0e63330 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/CertifyResult.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/CertifyResult.java @@ -23,6 +23,7 @@ import android.content.Intent;  import android.os.Parcel;  import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;  import org.sufficientlysecure.keychain.ui.LogDisplayActivity;  import org.sufficientlysecure.keychain.ui.LogDisplayFragment;  import org.sufficientlysecure.keychain.ui.util.Notify; @@ -30,16 +31,19 @@ import org.sufficientlysecure.keychain.ui.util.Notify.ActionListener;  import org.sufficientlysecure.keychain.ui.util.Notify.Showable;  import org.sufficientlysecure.keychain.ui.util.Notify.Style; -public class CertifyResult extends OperationResult { - +public class CertifyResult extends InputPendingResult {      int mCertifyOk, mCertifyError, mUploadOk, mUploadError;      public CertifyResult(int result, OperationLog log) {          super(result, log);      } +    public CertifyResult(OperationLog log, RequiredInputParcel requiredInput) { +        super(log, requiredInput); +    } +      public CertifyResult(int result, OperationLog log, int certifyOk, int certifyError, int uploadOk, int uploadError) { -        this(result, log); +        super(result, log);          mCertifyOk = certifyOk;          mCertifyError = certifyError;          mUploadOk = uploadOk; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/EditKeyResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/EditKeyResult.java index abcf575af..842b75c3b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/EditKeyResult.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/EditKeyResult.java @@ -31,13 +31,18 @@ public class EditKeyResult extends OperationResult {      public EditKeyResult(Parcel source) {          super(source); -        mMasterKeyId = source.readLong(); +        mMasterKeyId = source.readInt() != 0 ? source.readLong() : null;      }      @Override      public void writeToParcel(Parcel dest, int flags) {          super.writeToParcel(dest, flags); -        dest.writeLong(mMasterKeyId); +        if (mMasterKeyId != null) { +            dest.writeInt(1); +            dest.writeLong(mMasterKeyId); +        } else { +            dest.writeInt(0); +        }      }      public static Creator<EditKeyResult> CREATOR = new Creator<EditKeyResult>() { 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 new file mode 100644 index 000000000..e0ba28fbe --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/InputPendingResult.java @@ -0,0 +1,45 @@ +package org.sufficientlysecure.keychain.operations.results; + + +import android.os.Parcel; + +import org.sufficientlysecure.keychain.service.input.RequiredInputParcel; + + +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; + +    final RequiredInputParcel mRequiredInput; + +    public InputPendingResult(int result, OperationLog log) { +        super(result, log); +        mRequiredInput = null; +    } + +    public InputPendingResult(OperationLog log, RequiredInputParcel requiredInput) { +        super(RESULT_PENDING, log); +        mRequiredInput = requiredInput; +    } + +    public InputPendingResult(Parcel source) { +        super(source); +        mRequiredInput = source.readParcelable(getClass().getClassLoader()); +    } + +    @Override +    public void writeToParcel(Parcel dest, int flags) { +        super.writeToParcel(dest, flags); +        dest.writeParcelable(mRequiredInput, 0); +    } + +    public boolean isPending() { +        return (mResult & RESULT_PENDING) == RESULT_PENDING; +    } + +    public RequiredInputParcel getRequiredInputParcel() { +        return mRequiredInput; +    } + +} 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 f2a27b0fc..561b8f907 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 @@ -250,12 +250,20 @@ public abstract class OperationResult implements Parcelable {      public Showable createNotify(final Activity activity) { -        Log.d(Constants.TAG, "mLog.getLast()"+mLog.getLast()); -        Log.d(Constants.TAG, "mLog.getLast().mType"+mLog.getLast().mType); -        Log.d(Constants.TAG, "mLog.getLast().mType.getMsgId()"+mLog.getLast().mType.getMsgId()); -          // Take the last message as string -        int msgId = mLog.getLast().mType.getMsgId(); +        String logText; + +        LogEntryParcel entryParcel = mLog.getLast(); +        // special case: first parameter may be a quantity +        if (entryParcel.mParameters != null && entryParcel.mParameters.length > 0 +                && entryParcel.mParameters[0] instanceof Integer) { +            logText = activity.getResources().getQuantityString(entryParcel.mType.getMsgId(), +                    (Integer) entryParcel.mParameters[0], +                    entryParcel.mParameters); +        } else { +            logText = activity.getString(entryParcel.mType.getMsgId(), +                    entryParcel.mParameters); +        }          Style style; @@ -273,19 +281,19 @@ public abstract class OperationResult implements Parcelable {          }          if (getLog() == null || getLog().isEmpty()) { -            return Notify.create(activity, msgId, Notify.LENGTH_LONG, style); +            return Notify.create(activity, logText, Notify.LENGTH_LONG, style);          } -        return Notify.create(activity, msgId, Notify.LENGTH_LONG, style, -                new ActionListener() { -                    @Override -                    public void onAction() { -                        Intent intent = new Intent( -                                activity, LogDisplayActivity.class); -                        intent.putExtra(LogDisplayFragment.EXTRA_RESULT, OperationResult.this); -                        activity.startActivity(intent); -                    } -                }, R.string.view_log); +        return Notify.create(activity, logText, Notify.LENGTH_LONG, style, +            new ActionListener() { +                @Override +                public void onAction() { +                    Intent intent = new Intent( +                            activity, LogDisplayActivity.class); +                    intent.putExtra(LogDisplayFragment.EXTRA_RESULT, OperationResult.this); +                    activity.startActivity(intent); +                } +            }, R.string.view_log);      } @@ -512,6 +520,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), @@ -521,6 +530,7 @@ public abstract class OperationResult implements Parcelable {          MSG_MF_ERROR_NO_CERTIFY (LogLevel.ERROR, R.string.msg_cr_error_no_certify),          MSG_MF_ERROR_NOEXIST_PRIMARY (LogLevel.ERROR, R.string.msg_mf_error_noexist_primary),          MSG_MF_ERROR_NOEXIST_REVOKE (LogLevel.ERROR, R.string.msg_mf_error_noexist_revoke), +        MSG_MF_ERROR_NOOP (LogLevel.ERROR, R.string.msg_mf_error_noop),          MSG_MF_ERROR_NULL_EXPIRY (LogLevel.ERROR, R.string.msg_mf_error_null_expiry),          MSG_MF_ERROR_PASSPHRASE_MASTER(LogLevel.ERROR, R.string.msg_mf_error_passphrase_master),          MSG_MF_ERROR_PAST_EXPIRY(LogLevel.ERROR, R.string.msg_mf_error_past_expiry), @@ -538,6 +548,9 @@ public abstract class OperationResult implements Parcelable {          MSG_MF_PASSPHRASE_FAIL (LogLevel.WARN, R.string.msg_mf_passphrase_fail),          MSG_MF_PRIMARY_REPLACE_OLD (LogLevel.DEBUG, R.string.msg_mf_primary_replace_old),          MSG_MF_PRIMARY_NEW (LogLevel.DEBUG, R.string.msg_mf_primary_new), +        MSG_MF_RESTRICTED_MODE (LogLevel.INFO, R.string.msg_mf_restricted_mode), +        MSG_MF_REQUIRE_DIVERT (LogLevel.OK, R.string.msg_mf_require_divert), +        MSG_MF_REQUIRE_PASSPHRASE (LogLevel.OK, R.string.msg_mf_require_passphrase),          MSG_MF_SUBKEY_CHANGE (LogLevel.INFO, R.string.msg_mf_subkey_change),          MSG_MF_SUBKEY_NEW_ID (LogLevel.DEBUG, R.string.msg_mf_subkey_new_id),          MSG_MF_SUBKEY_NEW (LogLevel.INFO, R.string.msg_mf_subkey_new), @@ -596,7 +609,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), @@ -697,9 +709,9 @@ public abstract class OperationResult implements Parcelable {          MSG_CRT_ERROR_MASTER_NOT_FOUND (LogLevel.ERROR, R.string.msg_crt_error_master_not_found),          MSG_CRT_ERROR_NOTHING (LogLevel.ERROR, R.string.msg_crt_error_nothing),          MSG_CRT_ERROR_UNLOCK (LogLevel.ERROR, R.string.msg_crt_error_unlock), -        MSG_CRT_ERROR_DIVERT (LogLevel.ERROR, R.string.msg_crt_error_divert),          MSG_CRT (LogLevel.START, R.string.msg_crt),          MSG_CRT_MASTER_FETCH (LogLevel.DEBUG, R.string.msg_crt_master_fetch), +        MSG_CRT_NFC_RETURN (LogLevel.OK, R.string.msg_crt_nfc_return),          MSG_CRT_SAVE (LogLevel.DEBUG, R.string.msg_crt_save),          MSG_CRT_SAVING (LogLevel.DEBUG, R.string.msg_crt_saving),          MSG_CRT_SUCCESS (LogLevel.OK, R.string.msg_crt_success), 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/operations/results/PgpSignEncryptResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/PgpSignEncryptResult.java index cf40001b3..bda9893dd 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/PgpSignEncryptResult.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/PgpSignEncryptResult.java @@ -37,7 +37,6 @@ public class PgpSignEncryptResult extends OperationResult {      long mNfcKeyId;      byte[] mNfcHash;      int mNfcAlgo; -    Date mNfcTimestamp;      Passphrase mNfcPassphrase;      byte[] mDetachedSignature; @@ -49,11 +48,10 @@ public class PgpSignEncryptResult extends OperationResult {          mKeyIdPassphraseNeeded = keyIdPassphraseNeeded;      } -    public void setNfcData(long nfcKeyId, byte[] nfcHash, int nfcAlgo, Date nfcTimestamp, Passphrase passphrase) { +    public void setNfcData(long nfcKeyId, byte[] nfcHash, int nfcAlgo, Passphrase passphrase) {          mNfcKeyId = nfcKeyId;          mNfcHash = nfcHash;          mNfcAlgo = nfcAlgo; -        mNfcTimestamp = nfcTimestamp;          mNfcPassphrase = passphrase;      } @@ -73,10 +71,6 @@ public class PgpSignEncryptResult extends OperationResult {          return mNfcAlgo;      } -    public Date getNfcTimestamp() { -        return mNfcTimestamp; -    } -      public Passphrase getNfcPassphrase() {          return mNfcPassphrase;      } @@ -97,7 +91,6 @@ public class PgpSignEncryptResult extends OperationResult {          super(source);          mNfcHash = source.readInt() != 0 ? source.createByteArray() : null;          mNfcAlgo = source.readInt(); -        mNfcTimestamp = source.readInt() != 0 ? new Date(source.readLong()) : null;          mDetachedSignature = source.readInt() != 0 ? source.createByteArray() : null;      } @@ -114,12 +107,6 @@ public class PgpSignEncryptResult extends OperationResult {              dest.writeInt(0);          }          dest.writeInt(mNfcAlgo); -        if (mNfcTimestamp != null) { -            dest.writeInt(1); -            dest.writeLong(mNfcTimestamp.getTime()); -        } else { -            dest.writeInt(0); -        }          if (mDetachedSignature != null) {              dest.writeInt(1);              dest.writeByteArray(mDetachedSignature); @@ -138,4 +125,4 @@ public class PgpSignEncryptResult extends OperationResult {          }      }; -}
\ No newline at end of file +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/SignEncryptResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/SignEncryptResult.java index ed0de65b0..87483ade9 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/SignEncryptResult.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/SignEncryptResult.java @@ -21,6 +21,7 @@ import android.os.Parcel;  import java.util.ArrayList; +  public class SignEncryptResult extends OperationResult {      ArrayList<PgpSignEncryptResult> mResults; @@ -28,6 +29,7 @@ public class SignEncryptResult extends OperationResult {      public static final int RESULT_PENDING = RESULT_ERROR + 8; +      public PgpSignEncryptResult getPending() {          for (PgpSignEncryptResult sub : mResults) {              if (sub.isPending()) { 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 6ce77394c..21b6e551b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java @@ -43,10 +43,13 @@ import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;  import org.sufficientlysecure.keychain.util.Log;  import org.sufficientlysecure.keychain.util.Passphrase; +import java.nio.ByteBuffer;  import java.util.ArrayList;  import java.util.Date;  import java.util.HashMap;  import java.util.List; +import java.util.Map; +  /**   * Wrapper for a PGPSecretKey. @@ -184,13 +187,13 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {          return PgpConstants.sPreferredHashAlgorithms;      } -    private PGPContentSignerBuilder getContentSignerBuilder(int hashAlgo, byte[] nfcSignedHash, -                                                            Date nfcCreationTimestamp) { +    private PGPContentSignerBuilder getContentSignerBuilder(int hashAlgo, +            Map<ByteBuffer,byte[]> signedHashes) {          if (mPrivateKeyState == PRIVATE_KEY_STATE_DIVERT_TO_CARD) {              // use synchronous "NFC based" SignerBuilder              return new NfcSyncPGPContentSignerBuilder(                      mSecretKey.getPublicKey().getAlgorithm(), hashAlgo, -                    mSecretKey.getKeyID(), nfcSignedHash, nfcCreationTimestamp) +                    mSecretKey.getKeyID(), signedHashes)                      .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);          } else {              // content signer based on signing key algorithm and chosen hash algorithm @@ -200,29 +203,43 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {          }      } -    public PGPSignatureGenerator getSignatureGenerator(int hashAlgo, boolean cleartext, -                                                       byte[] nfcSignedHash, Date nfcCreationTimestamp) -            throws PgpGeneralException { +    public PGPSignatureGenerator getCertSignatureGenerator(Map<ByteBuffer, byte[]> signedHashes) { +        PGPContentSignerBuilder contentSignerBuilder = getContentSignerBuilder( +                PgpConstants.CERTIFY_HASH_ALGO, signedHashes); +          if (mPrivateKeyState == PRIVATE_KEY_STATE_LOCKED) {              throw new PrivateKeyNotUnlockedException();          } -        if (nfcSignedHash != null && nfcCreationTimestamp == null) { -            throw new PgpGeneralException("Got nfc hash without timestamp!!"); + +        PGPSignatureGenerator signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder); +        try { +            signatureGenerator.init(PGPSignature.DEFAULT_CERTIFICATION, mPrivateKey); +            return signatureGenerator; +        } catch (PGPException e) { +            Log.e(Constants.TAG, "signing error", e); +            return null; +        } +    } + +    public PGPSignatureGenerator getDataSignatureGenerator(int hashAlgo, boolean cleartext, +            Map<ByteBuffer, byte[]> signedHashes, Date creationTimestamp) +            throws PgpGeneralException { +        if (mPrivateKeyState == PRIVATE_KEY_STATE_LOCKED) { +            throw new PrivateKeyNotUnlockedException();          }          // We explicitly create a signature creation timestamp in this place.          // That way, we can inject an artificial one from outside, ie the one          // used in previous runs of this function. -        if (nfcCreationTimestamp == null) { +        if (creationTimestamp == null) {              // to sign using nfc PgpSignEncrypt is executed two times.              // the first time it stops to return the PendingIntent for nfc connection and signing the hash              // the second time the signed hash is used.              // to get the same hash we cache the timestamp for the second round! -            nfcCreationTimestamp = new Date(); +            creationTimestamp = new Date();          } -        PGPContentSignerBuilder contentSignerBuilder = getContentSignerBuilder(hashAlgo, -                nfcSignedHash, nfcCreationTimestamp); +        PGPContentSignerBuilder contentSignerBuilder = getContentSignerBuilder(hashAlgo, signedHashes);          int signatureType;          if (cleartext) { @@ -238,7 +255,7 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {              PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();              spGen.setSignerUserID(false, mRing.getPrimaryUserIdWithFallback()); -            spGen.setSignatureCreationTime(false, nfcCreationTimestamp); +            spGen.setSignatureCreationTime(false, creationTimestamp);              signatureGenerator.setHashedSubpackets(spGen.generate());              return signatureGenerator;          } catch (PgpKeyNotFoundException | PGPException e) { @@ -261,133 +278,6 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {          }      } -    /** -     * Certify the given pubkeyid with the given masterkeyid. -     * -     * @param publicKeyRing Keyring to add certification to. -     * @param userIds       User IDs to certify -     * @return A keyring with added certifications -     */ -    public UncachedKeyRing certifyUserIds(CanonicalizedPublicKeyRing publicKeyRing, List<String> userIds, -                                          byte[] nfcSignedHash, Date nfcCreationTimestamp) { -        if (mPrivateKeyState == PRIVATE_KEY_STATE_LOCKED) { -            throw new PrivateKeyNotUnlockedException(); -        } -        if (!isMasterKey()) { -            throw new AssertionError("tried to certify with non-master key, this is a programming error!"); -        } -        if (publicKeyRing.getMasterKeyId() == getKeyId()) { -            throw new AssertionError("key tried to self-certify, this is a programming error!"); -        } - -        // create a signatureGenerator from the supplied masterKeyId and passphrase -        PGPSignatureGenerator signatureGenerator; -        { -            PGPContentSignerBuilder contentSignerBuilder = getContentSignerBuilder( -                    PgpConstants.CERTIFY_HASH_ALGO, nfcSignedHash, nfcCreationTimestamp); - -            signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder); -            try { -                signatureGenerator.init(PGPSignature.DEFAULT_CERTIFICATION, mPrivateKey); -            } catch (PGPException e) { -                Log.e(Constants.TAG, "signing error", e); -                return null; -            } -        } - -        { // supply signatureGenerator with a SubpacketVector -            PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator(); -            if (nfcCreationTimestamp != null) { -                spGen.setSignatureCreationTime(false, nfcCreationTimestamp); -                Log.d(Constants.TAG, "For NFC: set sig creation time to " + nfcCreationTimestamp); -            } -            PGPSignatureSubpacketVector packetVector = spGen.generate(); -            signatureGenerator.setHashedSubpackets(packetVector); -        } - -        // get the master subkey (which we certify for) -        PGPPublicKey publicKey = publicKeyRing.getPublicKey().getPublicKey(); - -        // fetch public key ring, add the certification and return it -        try { -            for (String userId : userIds) { -                PGPSignature sig = signatureGenerator.generateCertification(userId, publicKey); -                publicKey = PGPPublicKey.addCertification(publicKey, userId, sig); -            } -        } catch (PGPException e) { -            Log.e(Constants.TAG, "signing error", e); -            return null; -        } - -        PGPPublicKeyRing ring = PGPPublicKeyRing.insertPublicKey(publicKeyRing.getRing(), publicKey); - -        return new UncachedKeyRing(ring); -    } - -    /** -     * Certify the given user attributes with the given masterkeyid. -     * -     * @param publicKeyRing Keyring to add certification to. -     * @param userAttributes       User IDs to certify, or all if null -     * @return A keyring with added certifications -     */ -    public UncachedKeyRing certifyUserAttributes(CanonicalizedPublicKeyRing publicKeyRing, -            List<WrappedUserAttribute> userAttributes, byte[] nfcSignedHash, Date nfcCreationTimestamp) { -        if (mPrivateKeyState == PRIVATE_KEY_STATE_LOCKED) { -            throw new PrivateKeyNotUnlockedException(); -        } -        if (!isMasterKey()) { -            throw new AssertionError("tried to certify with non-master key, this is a programming error!"); -        } -        if (publicKeyRing.getMasterKeyId() == getKeyId()) { -            throw new AssertionError("key tried to self-certify, this is a programming error!"); -        } - -        // create a signatureGenerator from the supplied masterKeyId and passphrase -        PGPSignatureGenerator signatureGenerator; -        { -            PGPContentSignerBuilder contentSignerBuilder = getContentSignerBuilder( -                    PgpConstants.CERTIFY_HASH_ALGO, nfcSignedHash, nfcCreationTimestamp); - -            signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder); -            try { -                signatureGenerator.init(PGPSignature.DEFAULT_CERTIFICATION, mPrivateKey); -            } catch (PGPException e) { -                Log.e(Constants.TAG, "signing error", e); -                return null; -            } -        } - -        { // supply signatureGenerator with a SubpacketVector -            PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator(); -            if (nfcCreationTimestamp != null) { -                spGen.setSignatureCreationTime(false, nfcCreationTimestamp); -                Log.d(Constants.TAG, "For NFC: set sig creation time to " + nfcCreationTimestamp); -            } -            PGPSignatureSubpacketVector packetVector = spGen.generate(); -            signatureGenerator.setHashedSubpackets(packetVector); -        } - -        // get the master subkey (which we certify for) -        PGPPublicKey publicKey = publicKeyRing.getPublicKey().getPublicKey(); - -        // fetch public key ring, add the certification and return it -        try { -            for (WrappedUserAttribute userAttribute : userAttributes) { -                PGPUserAttributeSubpacketVector vector = userAttribute.getVector(); -                PGPSignature sig = signatureGenerator.generateCertification(vector, publicKey); -                publicKey = PGPPublicKey.addCertification(publicKey, vector, sig); -            } -        } catch (PGPException e) { -            Log.e(Constants.TAG, "signing error", e); -            return null; -        } - -        PGPPublicKeyRing ring = PGPPublicKeyRing.insertPublicKey(publicKeyRing.getRing(), publicKey); - -        return new UncachedKeyRing(ring); -    } -      static class PrivateKeyNotUnlockedException extends RuntimeException {          // this exception is a programming error which happens when an operation which requires          // the private key is called without a previous call to unlock() @@ -402,4 +292,9 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {          return mPrivateKey;      } +    // HACK, for TESTING ONLY!! +    PGPSecretKey getSecretKey() { +        return mSecretKey; +    } +  } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpCertifyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpCertifyOperation.java new file mode 100644 index 000000000..90ec3053f --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpCertifyOperation.java @@ -0,0 +1,149 @@ +package org.sufficientlysecure.keychain.pgp; + + +import java.nio.ByteBuffer; +import java.util.Date; +import java.util.Map; + +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.PGPPublicKeyRing; +import org.spongycastle.openpgp.PGPSignature; +import org.spongycastle.openpgp.PGPSignatureGenerator; +import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator; +import org.spongycastle.openpgp.PGPSignatureSubpacketVector; +import org.spongycastle.openpgp.PGPUserAttributeSubpacketVector; +import org.spongycastle.openpgp.operator.jcajce.NfcSyncPGPContentSignerBuilder.NfcInteractionNeeded; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType; +import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; +import org.sufficientlysecure.keychain.service.CertifyActionsParcel.CertifyAction; +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.Log; + + +public class PgpCertifyOperation { + +    public PgpCertifyResult certify( +            CanonicalizedSecretKey secretKey, +            CanonicalizedPublicKeyRing publicRing, +            OperationLog log, +            int indent, +            CertifyAction action, +            Map<ByteBuffer,byte[]> signedHashes, +            Date creationTimestamp) { + +        if (!secretKey.isMasterKey()) { +            throw new AssertionError("tried to certify with non-master key, this is a programming error!"); +        } +        if (publicRing.getMasterKeyId() == secretKey.getKeyId()) { +            throw new AssertionError("key tried to self-certify, this is a programming error!"); +        } + +        // create a signatureGenerator from the supplied masterKeyId and passphrase +        PGPSignatureGenerator signatureGenerator = secretKey.getCertSignatureGenerator(signedHashes); + +        { // supply signatureGenerator with a SubpacketVector +            PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator(); +            if (creationTimestamp != null) { +                spGen.setSignatureCreationTime(false, creationTimestamp); +                Log.d(Constants.TAG, "For NFC: set sig creation time to " + creationTimestamp); +            } +            PGPSignatureSubpacketVector packetVector = spGen.generate(); +            signatureGenerator.setHashedSubpackets(packetVector); +        } + +        // get the master subkey (which we certify for) +        PGPPublicKey publicKey = publicRing.getPublicKey().getPublicKey(); + +        NfcSignOperationsBuilder requiredInput = new NfcSignOperationsBuilder(creationTimestamp, +                publicKey.getKeyID(), publicKey.getKeyID()); + +        try { +            if (action.mUserIds != null) { +                log.add(LogType.MSG_CRT_CERTIFY_UIDS, 2, action.mUserIds.size(), +                        KeyFormattingUtils.convertKeyIdToHex(action.mMasterKeyId)); + +                // fetch public key ring, add the certification and return it +                for (String userId : action.mUserIds) { +                    try { +                        PGPSignature sig = signatureGenerator.generateCertification(userId, publicKey); +                        publicKey = PGPPublicKey.addCertification(publicKey, userId, sig); +                    } catch (NfcInteractionNeeded e) { +                        requiredInput.addHash(e.hashToSign, e.hashAlgo); +                    } +                } + +            } + +            if (action.mUserAttributes != null) { +                log.add(LogType.MSG_CRT_CERTIFY_UATS, 2, action.mUserAttributes.size(), +                        KeyFormattingUtils.convertKeyIdToHex(action.mMasterKeyId)); + +                // fetch public key ring, add the certification and return it +                for (WrappedUserAttribute userAttribute : action.mUserAttributes) { +                    PGPUserAttributeSubpacketVector vector = userAttribute.getVector(); +                    try { +                        PGPSignature sig = signatureGenerator.generateCertification(vector, publicKey); +                        publicKey = PGPPublicKey.addCertification(publicKey, vector, sig); +                    } catch (NfcInteractionNeeded e) { +                        requiredInput.addHash(e.hashToSign, e.hashAlgo); +                    } +                } + +            } +        } catch (PGPException e) { +            Log.e(Constants.TAG, "signing error", e); +            return new PgpCertifyResult(); +        } + +        if (!requiredInput.isEmpty()) { +            return new PgpCertifyResult(requiredInput.build()); +        } + +        PGPPublicKeyRing ring = PGPPublicKeyRing.insertPublicKey(publicRing.getRing(), publicKey); +        return new PgpCertifyResult(new UncachedKeyRing(ring)); + +    } + +    public static class PgpCertifyResult { + +        final RequiredInputParcel mRequiredInput; +        final UncachedKeyRing mCertifiedRing; + +        PgpCertifyResult() { +            mRequiredInput = null; +            mCertifiedRing = null; +        } + +        PgpCertifyResult(RequiredInputParcel requiredInput) { +            mRequiredInput = requiredInput; +            mCertifiedRing = null; +        } + +        PgpCertifyResult(UncachedKeyRing certifiedRing) { +            mRequiredInput = null; +            mCertifiedRing = certifiedRing; +        } + +        public boolean success() { +            return mCertifiedRing != null || mRequiredInput != null; +        } + +        public boolean nfcInputRequired() { +            return mRequiredInput != null; +        } + +        public UncachedKeyRing getCertifiedRing() { +            return mCertifiedRing; +        } + +        public RequiredInputParcel getRequiredInput() { +            return mRequiredInput; +        } + +    } + +} 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 b3bf92364..f73bada06 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; @@ -43,6 +43,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 +56,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; @@ -86,6 +91,7 @@ import java.util.concurrent.atomic.AtomicBoolean;   * This indicator may be null.   */  public class PgpKeyOperation { +      private Stack<Progressable> mProgress;      private AtomicBoolean mCancelled; @@ -317,7 +323,8 @@ public class PgpKeyOperation {                      masterSecretKey.getEncoded(), new JcaKeyFingerprintCalculator());              subProgressPush(50, 100); -            return internal(sKR, masterSecretKey, add.mFlags, add.mExpiry, saveParcel, new Passphrase(), log); +            CryptoInputParcel cryptoInput = new CryptoInputParcel(new Date(), new Passphrase("")); +            return internal(sKR, masterSecretKey, add.mFlags, add.mExpiry, cryptoInput, saveParcel, log);          } catch (PGPException e) {              log.add(LogType.MSG_CR_ERROR_INTERNAL_PGP, indent); @@ -348,8 +355,9 @@ public class PgpKeyOperation {       * namely stripping of subkeys and changing the protection mode of dummy keys.       *       */ -    public PgpEditKeyResult modifySecretKeyRing(CanonicalizedSecretKeyRing wsKR, SaveKeyringParcel saveParcel, -                                                Passphrase passphrase) { +    public PgpEditKeyResult modifySecretKeyRing(CanonicalizedSecretKeyRing wsKR, +            CryptoInputParcel cryptoInput, +            SaveKeyringParcel saveParcel) {          OperationLog log = new OperationLog();          int indent = 0; @@ -387,11 +395,24 @@ 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.isEmpty()) { +            log.add(LogType.MSG_MF_ERROR_NOOP, indent); +            return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null); +        } + +        if (isDummy(masterSecretKey) || saveParcel.isRestrictedOnly()) { +            log.add(LogType.MSG_MF_RESTRICTED_MODE, indent);              return internalRestricted(sKR, saveParcel, log);          } +        // Do we require a passphrase? If so, pass it along +        if (!isDivertToCard(masterSecretKey) && !cryptoInput.hasPassphrase()) { +            log.add(LogType.MSG_MF_REQUIRE_PASSPHRASE, indent); +            return new PgpEditKeyResult(log, RequiredInputParcel.createRequiredPassphrase( +                    masterSecretKey.getKeyID(), masterSecretKey.getKeyID(), +                    cryptoInput.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(); @@ -399,33 +420,45 @@ 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, cryptoInput, saveParcel, log);      }      private PgpEditKeyResult internal(PGPSecretKeyRing sKR, PGPSecretKey masterSecretKey,                                       int masterKeyFlags, long masterKeyExpiry, -                                     SaveKeyringParcel saveParcel, Passphrase passphrase, +                                     CryptoInputParcel cryptoInput, +                                     SaveKeyringParcel saveParcel,                                       OperationLog log) {          int indent = 1; +        NfcSignOperationsBuilder nfcSignOps = new NfcSignOperationsBuilder( +                cryptoInput.getSignatureTime(), masterSecretKey.getKeyID(), +                masterSecretKey.getKeyID()); +          progress(R.string.progress_modify, 0);          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.getCharArray()); -                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(cryptoInput.getPassphrase().getCharArray()); +                    masterPrivateKey = masterSecretKey.extractPrivateKey(keyDecryptor); +                } catch (PGPException e) { +                    log.add(LogType.MSG_MF_UNLOCK_ERROR, indent + 1); +                    return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null); +                }              }          } @@ -449,7 +482,7 @@ public class PgpKeyOperation {                      String userId = saveParcel.mAddUserIds.get(i);                      log.add(LogType.MSG_MF_UID_ADD, indent, userId); -                    if (userId.equals("")) { +                    if ("".equals(userId)) {                          log.add(LogType.MSG_MF_UID_ERROR_EMPTY, indent + 1);                          return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);                      } @@ -480,9 +513,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, cryptoInput), +                                cryptoInput.getSignatureTime(), +                                masterPrivateKey, masterPublicKey, userId, +                                isPrimary, masterKeyFlags, masterKeyExpiry); +                        modifiedPublicKey = PGPPublicKey.addCertification(modifiedPublicKey, userId, cert); +                    } catch (NfcInteractionNeeded e) { +                        nfcSignOps.addHash(e.hashToSign, e.hashAlgo); +                    }                  }                  subProgressPop(); @@ -509,9 +549,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, cryptoInput), +                                cryptoInput.getSignatureTime(), +                                masterPrivateKey, masterPublicKey, vector); +                        modifiedPublicKey = PGPPublicKey.addCertification(modifiedPublicKey, vector, cert); +                    } catch (NfcInteractionNeeded e) { +                        nfcSignOps.addHash(e.hashToSign, e.hashAlgo); +                    }                  }                  subProgressPop(); @@ -539,9 +585,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, cryptoInput), +                                cryptoInput.getSignatureTime(), +                                masterPrivateKey, masterPublicKey, userId); +                        modifiedPublicKey = PGPPublicKey.addCertification(modifiedPublicKey, userId, cert); +                    } catch (NfcInteractionNeeded e) { +                        nfcSignOps.addHash(e.hashToSign, e.hashAlgo); +                    }                  }                  subProgressPop(); @@ -611,11 +663,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, cryptoInput), +                                        cryptoInput.getSignatureTime(), +                                        masterPrivateKey, masterPublicKey, userId, false, +                                        masterKeyFlags, masterKeyExpiry); +                                modifiedPublicKey = PGPPublicKey.addCertification( +                                        modifiedPublicKey, userId, newCert); +                            } catch (NfcInteractionNeeded e) { +                                nfcSignOps.addHash(e.hashToSign, e.hashAlgo); +                            } +                              continue;                          } @@ -627,11 +686,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, cryptoInput), +                                        cryptoInput.getSignatureTime(), +                                        masterPrivateKey, masterPublicKey, userId, true, +                                        masterKeyFlags, masterKeyExpiry); +                                modifiedPublicKey = PGPPublicKey.addCertification( +                                        modifiedPublicKey, userId, newCert); +                            } catch (NfcInteractionNeeded e) { +                                nfcSignOps.addHash(e.hashToSign, e.hashAlgo); +                            }                              ok = true;                          } @@ -718,8 +783,9 @@ public class PgpKeyOperation {                      }                      PGPPublicKey pKey = -                            updateMasterCertificates(masterPrivateKey, masterPublicKey, -                                    flags, expiry, indent, log); +                            updateMasterCertificates( +                                    masterSecretKey, masterPrivateKey, masterPublicKey, +                                    flags, expiry, cryptoInput,  nfcSignOps, indent, log);                      if (pKey == null) {                          // error log entry has already been added by updateMasterCertificates itself                          return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null); @@ -756,9 +822,16 @@ public class PgpKeyOperation {                      pKey = PGPPublicKey.removeCertification(pKey, sig);                  } +                PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder() +                        .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build( +                                cryptoInput.getPassphrase().getCharArray()); +                PGPPrivateKey subPrivateKey = sKey.extractPrivateKey(keyDecryptor); +                PGPSignature sig = generateSubkeyBindingSignature( +                        getSignatureGenerator(masterSecretKey, cryptoInput), +                        cryptoInput.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));              } @@ -782,10 +855,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, cryptoInput), +                            cryptoInput.getSignatureTime(), +                            masterPublicKey, masterPrivateKey, pKey); + +                    pKey = PGPPublicKey.addCertification(pKey, sig); +                    sKR = PGPSecretKeyRing.insertSecretKey(sKR, PGPSecretKey.replacePublicKey(sKey, pKey)); +                } catch (NfcInteractionNeeded e) { +                    nfcSignOps.addHash(e.hashToSign, e.hashAlgo); +                }              }              subProgressPop(); @@ -828,10 +908,16 @@ 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, cryptoInput), +                            cryptoInput.getSignatureTime(), +                            masterPublicKey, masterPrivateKey, keyPair.getPrivateKey(), pKey, +                            add.mFlags, add.mExpiry); +                    pKey = PGPPublicKey.addSubkeyBindingCertification(pKey, cert); +                } catch (NfcInteractionNeeded e) { +                    nfcSignOps.addHash(e.hashToSign, e.hashAlgo); +                }                  PGPSecretKey sKey; {                      // Build key encrypter and decrypter based on passphrase @@ -840,7 +926,8 @@ public class PgpKeyOperation {                      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.getCharArray()); +                            .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build( +                                    cryptoInput.getPassphrase().getCharArray());                      PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder()                              .build().get(PgpConstants.SECRET_KEY_SIGNATURE_CHECKSUM_HASH_ALGO); @@ -868,7 +955,7 @@ public class PgpKeyOperation {                  indent += 1;                  sKR = applyNewUnlock(sKR, masterPublicKey, masterPrivateKey, -                        passphrase, saveParcel.mNewUnlock, log, indent); +                        cryptoInput.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); @@ -892,6 +979,12 @@ public class PgpKeyOperation {          }          progress(R.string.progress_done, 100); + +        if (!nfcSignOps.isEmpty()) { +            log.add(LogType.MSG_MF_REQUIRE_DIVERT, indent); +            return new PgpEditKeyResult(log, nfcSignOps.build()); +        } +          log.add(LogType.MSG_MF_SUCCESS, indent);          return new PgpEditKeyResult(OperationResult.RESULT_OK, log, new UncachedKeyRing(sKR)); @@ -1064,8 +1157,7 @@ public class PgpKeyOperation {          PBESecretKeyEncryptor keyEncryptorNew = new JcePBESecretKeyEncryptorBuilder(                  PgpConstants.SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO, encryptorHashCalc,                  PgpConstants.SECRET_KEY_ENCRYPTOR_S2K_COUNT) -                .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build( -                        newPassphrase.getCharArray()); +                .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(newPassphrase.getCharArray());          // noinspection unchecked          for (PGPSecretKey sKey : new IterableIterator<PGPSecretKey>(sKR.getSecretKeys())) { @@ -1116,9 +1208,13 @@ public class PgpKeyOperation {      }      /** Update all (non-revoked) uid signatures with new flags and expiry time. */ -    private static PGPPublicKey updateMasterCertificates( -            PGPPrivateKey masterPrivateKey, PGPPublicKey masterPublicKey, -            int flags, long expiry, int indent, OperationLog log) +    private PGPPublicKey updateMasterCertificates( +            PGPSecretKey masterSecretKey, PGPPrivateKey masterPrivateKey, +            PGPPublicKey masterPublicKey, +            int flags, long expiry, +            CryptoInputParcel cryptoInput, +            NfcSignOperationsBuilder nfcSignOps, +            int indent, OperationLog log)              throws PGPException, IOException, SignatureException {          // keep track if we actually changed one @@ -1173,10 +1269,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, cryptoInput), +                        cryptoInput.getSignatureTime(), +                        masterPrivateKey, masterPublicKey, userId, isPrimary, flags, expiry); +                modifiedPublicKey = PGPPublicKey.addCertification( +                        modifiedPublicKey, userId, newCert); +            } catch (NfcInteractionNeeded e) { +                nfcSignOps.addHash(e.hashToSign, e.hashAlgo); +            }              ok = true;          } @@ -1191,15 +1293,37 @@ public class PgpKeyOperation {      } -    private static PGPSignature generateUserIdSignature( +    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();          { @@ -1223,7 +1347,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); @@ -1239,19 +1363,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()); @@ -1260,29 +1380,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()) { @@ -1294,26 +1409,12 @@ public class PgpKeyOperation {          }      } -    private static PGPSignature generateSubkeyBindingSignature( -            PGPPublicKey masterPublicKey, PGPPrivateKey masterPrivateKey, -            PGPSecretKey sKey, PGPPublicKey pKey, int flags, long expiry, Passphrase passphrase) -            throws IOException, PGPException, SignatureException { -        PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder() -                .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build( -                        passphrase.getCharArray()); -        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 @@ -1324,10 +1425,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);          } @@ -1342,10 +1443,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()); @@ -1372,4 +1469,16 @@ public class PgpKeyOperation {          return flags;      } +    private static boolean isDummy(PGPSecretKey secretKey) { +        S2K s2k = secretKey.getS2K(); +        return s2k.getType() == S2K.GNU_DUMMY_S2K +                && s2k.getProtectionMode() == S2K.GNU_PROTECTION_MODE_NO_PRIVATE_KEY; +    } + +    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/pgp/PgpSignEncryptInput.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptInputParcel.java index 4a920685a..d5f3cf964 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptInput.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptInputParcel.java @@ -20,11 +20,18 @@ package org.sufficientlysecure.keychain.pgp;  import org.spongycastle.bcpg.CompressionAlgorithmTags;  import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;  import org.sufficientlysecure.keychain.util.Passphrase; +import java.nio.ByteBuffer;  import java.util.Date; +import java.util.Map; -public class PgpSignEncryptInput { +import android.os.Parcel; +import android.os.Parcelable; + + +public class PgpSignEncryptInputParcel implements Parcelable {      protected String mVersionHeader = null;      protected boolean mEnableAsciiArmorOutput = false; @@ -37,13 +44,73 @@ public class PgpSignEncryptInput {      protected int mSignatureHashAlgorithm = PgpConstants.OpenKeychainHashAlgorithmTags.USE_PREFERRED;      protected Passphrase mSignaturePassphrase = null;      protected long mAdditionalEncryptId = Constants.key.none; -    protected byte[] mNfcSignedHash = null; -    protected Date mNfcCreationTimestamp = null;      protected boolean mFailOnMissingEncryptionKeyIds = false;      protected String mCharset;      protected boolean mCleartextSignature;      protected boolean mDetachedSignature = false;      protected boolean mHiddenRecipients = false; +    protected CryptoInputParcel mCryptoInput = new CryptoInputParcel(); + +    public PgpSignEncryptInputParcel() { + +    } + +    PgpSignEncryptInputParcel(Parcel source) { + +        ClassLoader loader = getClass().getClassLoader(); + +        // we do all of those here, so the PgpSignEncryptInput class doesn't have to be parcelable +        mVersionHeader = source.readString(); +        mEnableAsciiArmorOutput  = source.readInt() == 1; +        mCompressionId = source.readInt(); +        mEncryptionMasterKeyIds = source.createLongArray(); +        mSymmetricPassphrase = source.readParcelable(loader); +        mSymmetricEncryptionAlgorithm = source.readInt(); +        mSignatureMasterKeyId = source.readLong(); +        mSignatureSubKeyId = source.readInt() == 1 ? source.readLong() : null; +        mSignatureHashAlgorithm = source.readInt(); +        mSignaturePassphrase = source.readParcelable(loader); +        mAdditionalEncryptId = source.readLong(); +        mFailOnMissingEncryptionKeyIds = source.readInt() == 1; +        mCharset = source.readString(); +        mCleartextSignature = source.readInt() == 1; +        mDetachedSignature = source.readInt() == 1; +        mHiddenRecipients = source.readInt() == 1; + +        mCryptoInput = source.readParcelable(loader); +    } + +    @Override +    public int describeContents() { +        return 0; +    } + +    @Override +    public void writeToParcel(Parcel dest, int flags) { +        dest.writeString(mVersionHeader); +        dest.writeInt(mEnableAsciiArmorOutput ? 1 : 0); +        dest.writeInt(mCompressionId); +        dest.writeLongArray(mEncryptionMasterKeyIds); +        dest.writeParcelable(mSymmetricPassphrase, 0); +        dest.writeInt(mSymmetricEncryptionAlgorithm); +        dest.writeLong(mSignatureMasterKeyId); +        if (mSignatureSubKeyId != null) { +            dest.writeInt(1); +            dest.writeLong(mSignatureSubKeyId); +        } else { +            dest.writeInt(0); +        } +        dest.writeInt(mSignatureHashAlgorithm); +        dest.writeParcelable(mSignaturePassphrase, 0); +        dest.writeLong(mAdditionalEncryptId); +        dest.writeInt(mFailOnMissingEncryptionKeyIds ? 1 : 0); +        dest.writeString(mCharset); +        dest.writeInt(mCleartextSignature ? 1 : 0); +        dest.writeInt(mDetachedSignature ? 1 : 0); +        dest.writeInt(mHiddenRecipients ? 1 : 0); + +        dest.writeParcelable(mCryptoInput, 0); +    }      public String getCharset() {          return mCharset; @@ -57,19 +124,11 @@ public class PgpSignEncryptInput {          return mFailOnMissingEncryptionKeyIds;      } -    public Date getNfcCreationTimestamp() { -        return mNfcCreationTimestamp; -    } - -    public byte[] getNfcSignedHash() { -        return mNfcSignedHash; -    } -      public long getAdditionalEncryptId() {          return mAdditionalEncryptId;      } -    public PgpSignEncryptInput setAdditionalEncryptId(long additionalEncryptId) { +    public PgpSignEncryptInputParcel setAdditionalEncryptId(long additionalEncryptId) {          mAdditionalEncryptId = additionalEncryptId;          return this;      } @@ -78,7 +137,7 @@ public class PgpSignEncryptInput {          return mSignaturePassphrase;      } -    public PgpSignEncryptInput setSignaturePassphrase(Passphrase signaturePassphrase) { +    public PgpSignEncryptInputParcel  setSignaturePassphrase(Passphrase signaturePassphrase) {          mSignaturePassphrase = signaturePassphrase;          return this;      } @@ -87,7 +146,7 @@ public class PgpSignEncryptInput {          return mSignatureHashAlgorithm;      } -    public PgpSignEncryptInput setSignatureHashAlgorithm(int signatureHashAlgorithm) { +    public PgpSignEncryptInputParcel setSignatureHashAlgorithm(int signatureHashAlgorithm) {          mSignatureHashAlgorithm = signatureHashAlgorithm;          return this;      } @@ -96,7 +155,7 @@ public class PgpSignEncryptInput {          return mSignatureSubKeyId;      } -    public PgpSignEncryptInput setSignatureSubKeyId(long signatureSubKeyId) { +    public PgpSignEncryptInputParcel setSignatureSubKeyId(long signatureSubKeyId) {          mSignatureSubKeyId = signatureSubKeyId;          return this;      } @@ -105,7 +164,7 @@ public class PgpSignEncryptInput {          return mSignatureMasterKeyId;      } -    public PgpSignEncryptInput setSignatureMasterKeyId(long signatureMasterKeyId) { +    public PgpSignEncryptInputParcel setSignatureMasterKeyId(long signatureMasterKeyId) {          mSignatureMasterKeyId = signatureMasterKeyId;          return this;      } @@ -114,7 +173,7 @@ public class PgpSignEncryptInput {          return mSymmetricEncryptionAlgorithm;      } -    public PgpSignEncryptInput setSymmetricEncryptionAlgorithm(int symmetricEncryptionAlgorithm) { +    public PgpSignEncryptInputParcel setSymmetricEncryptionAlgorithm(int symmetricEncryptionAlgorithm) {          mSymmetricEncryptionAlgorithm = symmetricEncryptionAlgorithm;          return this;      } @@ -123,7 +182,7 @@ public class PgpSignEncryptInput {          return mSymmetricPassphrase;      } -    public PgpSignEncryptInput setSymmetricPassphrase(Passphrase symmetricPassphrase) { +    public PgpSignEncryptInputParcel setSymmetricPassphrase(Passphrase symmetricPassphrase) {          mSymmetricPassphrase = symmetricPassphrase;          return this;      } @@ -132,7 +191,7 @@ public class PgpSignEncryptInput {          return mEncryptionMasterKeyIds;      } -    public PgpSignEncryptInput setEncryptionMasterKeyIds(long[] encryptionMasterKeyIds) { +    public PgpSignEncryptInputParcel setEncryptionMasterKeyIds(long[] encryptionMasterKeyIds) {          mEncryptionMasterKeyIds = encryptionMasterKeyIds;          return this;      } @@ -141,7 +200,7 @@ public class PgpSignEncryptInput {          return mCompressionId;      } -    public PgpSignEncryptInput setCompressionId(int compressionId) { +    public PgpSignEncryptInputParcel setCompressionId(int compressionId) {          mCompressionId = compressionId;          return this;      } @@ -154,28 +213,22 @@ public class PgpSignEncryptInput {          return mVersionHeader;      } -    public PgpSignEncryptInput setVersionHeader(String versionHeader) { +    public PgpSignEncryptInputParcel setVersionHeader(String versionHeader) {          mVersionHeader = versionHeader;          return this;      } -    public PgpSignEncryptInput setEnableAsciiArmorOutput(boolean enableAsciiArmorOutput) { +    public PgpSignEncryptInputParcel setEnableAsciiArmorOutput(boolean enableAsciiArmorOutput) {          mEnableAsciiArmorOutput = enableAsciiArmorOutput;          return this;      } -    public PgpSignEncryptInput setFailOnMissingEncryptionKeyIds(boolean failOnMissingEncryptionKeyIds) { +    public PgpSignEncryptInputParcel setFailOnMissingEncryptionKeyIds(boolean failOnMissingEncryptionKeyIds) {          mFailOnMissingEncryptionKeyIds = failOnMissingEncryptionKeyIds;          return this;      } -    public PgpSignEncryptInput setNfcState(byte[] signedHash, Date creationTimestamp) { -        mNfcSignedHash = signedHash; -        mNfcCreationTimestamp = creationTimestamp; -        return this; -    } - -    public PgpSignEncryptInput setCleartextSignature(boolean cleartextSignature) { +    public PgpSignEncryptInputParcel setCleartextSignature(boolean cleartextSignature) {          this.mCleartextSignature = cleartextSignature;          return this;      } @@ -184,7 +237,7 @@ public class PgpSignEncryptInput {          return mCleartextSignature;      } -    public PgpSignEncryptInput setDetachedSignature(boolean detachedSignature) { +    public PgpSignEncryptInputParcel setDetachedSignature(boolean detachedSignature) {          this.mDetachedSignature = detachedSignature;          return this;      } @@ -193,7 +246,7 @@ public class PgpSignEncryptInput {          return mDetachedSignature;      } -    public PgpSignEncryptInput setHiddenRecipients(boolean hiddenRecipients) { +    public PgpSignEncryptInputParcel setHiddenRecipients(boolean hiddenRecipients) {          this.mHiddenRecipients = hiddenRecipients;          return this;      } @@ -201,5 +254,29 @@ public class PgpSignEncryptInput {      public boolean isHiddenRecipients() {          return mHiddenRecipients;      } + +    public PgpSignEncryptInputParcel setCryptoInput(CryptoInputParcel cryptoInput) { +        mCryptoInput = cryptoInput; +        return this; +    } + +    public Map<ByteBuffer, byte[]> getCryptoData() { +        return mCryptoInput.getCryptoData(); +    } + +    public Date getSignatureTime() { +        return mCryptoInput.getSignatureTime(); +    } + +    public static final Creator<PgpSignEncryptInputParcel> CREATOR = new Creator<PgpSignEncryptInputParcel>() { +        public PgpSignEncryptInputParcel createFromParcel(final Parcel source) { +            return new PgpSignEncryptInputParcel(source); +        } + +        public PgpSignEncryptInputParcel[] newArray(final int size) { +            return new PgpSignEncryptInputParcel[size]; +        } +    }; +  } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptOperation.java index bd3c31d3a..7e70b4571 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptOperation.java @@ -71,7 +71,7 @@ import java.util.concurrent.atomic.AtomicBoolean;   *   * For a high-level operation based on URIs, see SignEncryptOperation.   * - * @see org.sufficientlysecure.keychain.pgp.PgpSignEncryptInput + * @see PgpSignEncryptInputParcel   * @see org.sufficientlysecure.keychain.operations.results.PgpSignEncryptResult   * @see org.sufficientlysecure.keychain.operations.SignEncryptOperation   * @@ -99,7 +99,7 @@ public class PgpSignEncryptOperation extends BaseOperation {      /**       * Signs and/or encrypts data based on parameters of class       */ -    public PgpSignEncryptResult execute(PgpSignEncryptInput input, +    public PgpSignEncryptResult execute(PgpSignEncryptInputParcel input,                                       InputData inputData, OutputStream outputStream) {          int indent = 0; @@ -281,8 +281,9 @@ public class PgpSignEncryptOperation extends BaseOperation {              try {                  boolean cleartext = input.isCleartextSignature() && input.isEnableAsciiArmorOutput() && !enableEncryption; -                signatureGenerator = signingKey.getSignatureGenerator( -                        input.getSignatureHashAlgorithm(), cleartext, input.getNfcSignedHash(), input.getNfcCreationTimestamp()); +                signatureGenerator = signingKey.getDataSignatureGenerator( +                        input.getSignatureHashAlgorithm(), cleartext, +                        input.getCryptoData(), input.getSignatureTime());              } catch (PgpGeneralException e) {                  log.add(LogType.MSG_PSE_ERROR_NFC, indent);                  return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log); @@ -489,7 +490,8 @@ public class PgpSignEncryptOperation extends BaseOperation {                              new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_PENDING_NFC, log);                      // Note that the checked key here is the master key, not the signing key                      // (although these are always the same on Yubikeys) -                    result.setNfcData(input.getSignatureSubKeyId(), e.hashToSign, e.hashAlgo, e.creationTimestamp, input.getSignaturePassphrase()); +                    result.setNfcData(signingKey.getKeyId(), e.hashToSign, e.hashAlgo, +                            input.getSignaturePassphrase());                      Log.d(Constants.TAG, "e.hashToSign" + Hex.toHexString(e.hashToSign));                      return result;                  } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/SignEncryptParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/SignEncryptParcel.java index 975548c95..b178e9515 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/SignEncryptParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/SignEncryptParcel.java @@ -20,7 +20,6 @@ package org.sufficientlysecure.keychain.pgp;  import android.net.Uri;  import android.os.Parcel; -import android.os.Parcelable;  import org.sufficientlysecure.keychain.util.Passphrase; @@ -42,7 +41,7 @@ import java.util.List;   *   left, which will be returned in a byte array as part of the result parcel.   *   */ -public class SignEncryptParcel extends PgpSignEncryptInput implements Parcelable { +public class SignEncryptParcel extends PgpSignEncryptInputParcel {      public ArrayList<Uri> mInputUris = new ArrayList<>();      public ArrayList<Uri> mOutputUris = new ArrayList<>(); @@ -53,26 +52,7 @@ public class SignEncryptParcel extends PgpSignEncryptInput implements Parcelable      }      public SignEncryptParcel(Parcel src) { - -        // we do all of those here, so the PgpSignEncryptInput class doesn't have to be parcelable -        mVersionHeader = src.readString(); -        mEnableAsciiArmorOutput  = src.readInt() == 1; -        mCompressionId = src.readInt(); -        mEncryptionMasterKeyIds = src.createLongArray(); -        mSymmetricPassphrase = src.readParcelable(Passphrase.class.getClassLoader()); -        mSymmetricEncryptionAlgorithm = src.readInt(); -        mSignatureMasterKeyId = src.readLong(); -        mSignatureSubKeyId = src.readInt() == 1 ? src.readLong() : null; -        mSignatureHashAlgorithm = src.readInt(); -        mSignaturePassphrase = src.readParcelable(Passphrase.class.getClassLoader()); -        mAdditionalEncryptId = src.readLong(); -        mNfcSignedHash = src.createByteArray(); -        mNfcCreationTimestamp = src.readInt() == 1 ? new Date(src.readLong()) : null; -        mFailOnMissingEncryptionKeyIds = src.readInt() == 1; -        mCharset = src.readString(); -        mCleartextSignature = src.readInt() == 1; -        mDetachedSignature = src.readInt() == 1; -        mHiddenRecipients = src.readInt() == 1; +        super(src);          mInputUris = src.createTypedArrayList(Uri.CREATOR);          mOutputUris = src.createTypedArrayList(Uri.CREATOR); @@ -110,34 +90,7 @@ public class SignEncryptParcel extends PgpSignEncryptInput implements Parcelable      }      public void writeToParcel(Parcel dest, int flags) { -        dest.writeString(mVersionHeader); -        dest.writeInt(mEnableAsciiArmorOutput ? 1 : 0); -        dest.writeInt(mCompressionId); -        dest.writeLongArray(mEncryptionMasterKeyIds); -        dest.writeParcelable(mSymmetricPassphrase, flags); -        dest.writeInt(mSymmetricEncryptionAlgorithm); -        dest.writeLong(mSignatureMasterKeyId); -        if (mSignatureSubKeyId != null) { -            dest.writeInt(1); -            dest.writeLong(mSignatureSubKeyId); -        } else { -            dest.writeInt(0); -        } -        dest.writeInt(mSignatureHashAlgorithm); -        dest.writeParcelable(mSignaturePassphrase, flags); -        dest.writeLong(mAdditionalEncryptId); -        dest.writeByteArray(mNfcSignedHash); -        if (mNfcCreationTimestamp != null) { -            dest.writeInt(1); -            dest.writeLong(mNfcCreationTimestamp.getTime()); -        } else { -            dest.writeInt(0); -        } -        dest.writeInt(mFailOnMissingEncryptionKeyIds ? 1 : 0); -        dest.writeString(mCharset); -        dest.writeInt(mCleartextSignature ? 1 : 0); -        dest.writeInt(mDetachedSignature ? 1 : 0); -        dest.writeInt(mHiddenRecipients ? 1 : 0); +        super.writeToParcel(dest, flags);          dest.writeTypedList(mInputUris);          dest.writeTypedList(mOutputUris); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java index bd2866985..204af1b67 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java @@ -38,7 +38,7 @@ import org.sufficientlysecure.keychain.operations.results.OperationResult.LogEnt  import org.sufficientlysecure.keychain.operations.results.PgpSignEncryptResult;  import org.sufficientlysecure.keychain.pgp.PgpConstants;  import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify; -import org.sufficientlysecure.keychain.pgp.PgpSignEncryptInput; +import org.sufficientlysecure.keychain.pgp.PgpSignEncryptInputParcel;  import org.sufficientlysecure.keychain.pgp.PgpSignEncryptOperation;  import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;  import org.sufficientlysecure.keychain.provider.KeychainContract; @@ -48,6 +48,7 @@ import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables;  import org.sufficientlysecure.keychain.provider.ProviderHelper;  import org.sufficientlysecure.keychain.remote.ui.RemoteServiceActivity;  import org.sufficientlysecure.keychain.remote.ui.SelectSignKeyIdActivity; +import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;  import org.sufficientlysecure.keychain.ui.ImportKeysActivity;  import org.sufficientlysecure.keychain.ui.NfcActivity;  import org.sufficientlysecure.keychain.ui.PassphraseDialogActivity; @@ -264,11 +265,13 @@ public class OpenPgpService extends RemoteService {              }              // carefully: only set if timestamp exists -            Date nfcCreationDate = null; +            Date nfcCreationDate;              long nfcCreationTimestamp = data.getLongExtra(OpenPgpApi.EXTRA_NFC_SIG_CREATION_TIMESTAMP, -1);              Log.d(Constants.TAG, "nfcCreationTimestamp: " + nfcCreationTimestamp);              if (nfcCreationTimestamp != -1) {                  nfcCreationDate = new Date(nfcCreationTimestamp); +            } else { +                nfcCreationDate = new Date();              }              // Get Input- and OutputStream from ParcelFileDescriptor @@ -281,8 +284,11 @@ public class OpenPgpService extends RemoteService {              long inputLength = is.available();              InputData inputData = new InputData(is, inputLength); +            CryptoInputParcel cryptoInput = new CryptoInputParcel(nfcCreationDate); +            cryptoInput.addCryptoData(null, nfcSignedHash); // TODO fix +              // sign-only -            PgpSignEncryptInput pseInput = new PgpSignEncryptInput() +            PgpSignEncryptInputParcel pseInput = new PgpSignEncryptInputParcel()                      .setSignaturePassphrase(passphrase)                      .setEnableAsciiArmorOutput(asciiArmor)                      .setCleartextSignature(cleartextSign) @@ -290,7 +296,7 @@ public class OpenPgpService extends RemoteService {                      .setVersionHeader(null)                      .setSignatureHashAlgorithm(PgpConstants.OpenKeychainHashAlgorithmTags.USE_PREFERRED)                      .setSignatureMasterKeyId(signKeyId) -                    .setNfcState(nfcSignedHash, nfcCreationDate); +                    .setCryptoInput(cryptoInput);              // execute PGP operation!              PgpSignEncryptOperation pse = new PgpSignEncryptOperation(this, new ProviderHelper(getContext()), null); @@ -305,7 +311,7 @@ public class OpenPgpService extends RemoteService {                      // return PendingIntent to execute NFC activity                      // pass through the signature creation timestamp to be used again on second execution                      // of PgpSignEncrypt when we have the signed hash! -                    data.putExtra(OpenPgpApi.EXTRA_NFC_SIG_CREATION_TIMESTAMP, pgpResult.getNfcTimestamp().getTime()); +                    data.putExtra(OpenPgpApi.EXTRA_NFC_SIG_CREATION_TIMESTAMP, nfcCreationDate.getTime());                      // return PendingIntent to be executed by client                      Intent result = new Intent(); @@ -401,7 +407,7 @@ public class OpenPgpService extends RemoteService {              long inputLength = is.available();              InputData inputData = new InputData(is, inputLength, originalFilename); -            PgpSignEncryptInput pseInput = new PgpSignEncryptInput(); +            PgpSignEncryptInputParcel pseInput = new PgpSignEncryptInputParcel();              pseInput.setSignaturePassphrase(passphrase)                      .setEnableAsciiArmorOutput(asciiArmor)                      .setVersionHeader(null) @@ -425,16 +431,21 @@ public class OpenPgpService extends RemoteService {                  byte[] nfcSignedHash = data.getByteArrayExtra(OpenPgpApi.EXTRA_NFC_SIGNED_HASH);                  // carefully: only set if timestamp exists -                Date nfcCreationDate = null; +                Date nfcCreationDate;                  long nfcCreationTimestamp = data.getLongExtra(OpenPgpApi.EXTRA_NFC_SIG_CREATION_TIMESTAMP, -1);                  if (nfcCreationTimestamp != -1) {                      nfcCreationDate = new Date(nfcCreationTimestamp); +                } else { +                    nfcCreationDate = new Date();                  } +                CryptoInputParcel cryptoInput = new CryptoInputParcel(nfcCreationDate); +                cryptoInput.addCryptoData(null, nfcSignedHash); // TODO fix! +                  // sign and encrypt                  pseInput.setSignatureHashAlgorithm(PgpConstants.OpenKeychainHashAlgorithmTags.USE_PREFERRED)                          .setSignatureMasterKeyId(signKeyId) -                        .setNfcState(nfcSignedHash, nfcCreationDate) +                        .setCryptoInput(cryptoInput)                          .setAdditionalEncryptId(signKeyId); // add sign key for encryption              } @@ -452,7 +463,7 @@ public class OpenPgpService extends RemoteService {                      // return PendingIntent to execute NFC activity                      // pass through the signature creation timestamp to be used again on second execution                      // of PgpSignEncrypt when we have the signed hash! -                    data.putExtra(OpenPgpApi.EXTRA_NFC_SIG_CREATION_TIMESTAMP, pgpResult.getNfcTimestamp().getTime()); +                    data.putExtra(OpenPgpApi.EXTRA_NFC_SIG_CREATION_TIMESTAMP, 0L); // TODO fix                      // return PendingIntent to be executed by client                      Intent result = new Intent();                      result.putExtra(OpenPgpApi.RESULT_INTENT, 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 f4b941109..8721f4c0c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CertifyActionsParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CertifyActionsParcel.java @@ -22,9 +22,13 @@ import android.os.Parcel;  import android.os.Parcelable;  import java.io.Serializable; +import java.nio.ByteBuffer;  import java.util.ArrayList; +import java.util.Date; +import java.util.Map;  import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute; +import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;  /** 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 d5f13f7ce..ed6453e9d 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; @@ -161,6 +162,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"; @@ -252,11 +254,12 @@ public class KeychainIntentService extends IntentService implements Progressable                  // Input                  CertifyActionsParcel parcel = data.getParcelable(CERTIFY_PARCEL); +                CryptoInputParcel cryptoInput = data.getParcelable(EXTRA_CRYPTO_INPUT);                  String keyServerUri = data.getString(UPLOAD_KEY_SERVER);                  // Operation                  CertifyOperation op = new CertifyOperation(this, providerHelper, this, mActionCanceled); -                CertifyResult result = op.certify(parcel, keyServerUri); +                CertifyResult result = op.certify(parcel, cryptoInput, keyServerUri);                  // Result                  sendMessageToHandler(MessageStatus.OKAY, result); @@ -470,11 +473,11 @@ public class KeychainIntentService extends IntentService implements Progressable                  // Input                  SaveKeyringParcel saveParcel = data.getParcelable(EDIT_KEYRING_PARCEL); -                Passphrase passphrase = data.getParcelable(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/KeychainIntentServiceHandler.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentServiceHandler.java index 91a079a5d..794e660bd 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentServiceHandler.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentServiceHandler.java @@ -18,6 +18,7 @@  package org.sufficientlysecure.keychain.service;  import android.app.Activity; +import android.content.Intent;  import android.os.Bundle;  import android.os.Handler;  import android.os.Message; @@ -26,6 +27,7 @@ import android.support.v4.app.FragmentManager;  import org.sufficientlysecure.keychain.Constants;  import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.operations.results.CertifyResult;  import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment;  import org.sufficientlysecure.keychain.ui.util.Notify;  import org.sufficientlysecure.keychain.util.Log; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java index 9fd278c13..2e0524141 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java @@ -82,10 +82,14 @@ public class SaveKeyringParcel implements Parcelable {          mRevokeSubKeys = new ArrayList<>();      } +    public boolean isEmpty() { +        return isRestrictedOnly() && mChangeSubKeys.isEmpty(); +    } +      /** Returns true iff this parcel does not contain any operations which require a passphrase. */      public boolean isRestrictedOnly() {          if (mNewUnlock != null || !mAddUserIds.isEmpty() || !mAddUserAttribute.isEmpty() -                || !mAddSubKeys.isEmpty() || mChangePrimaryUserId != null || !mRevokeSubKeys .isEmpty() +                || !mAddSubKeys.isEmpty() || mChangePrimaryUserId != null || !mRevokeUserIds.isEmpty()                  || !mRevokeSubKeys.isEmpty()) {              return false;          } 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 new file mode 100644 index 000000000..21aacd1f0 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/CryptoInputParcel.java @@ -0,0 +1,125 @@ +package org.sufficientlysecure.keychain.service.input; + +import java.nio.ByteBuffer; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +import android.os.Parcel; +import android.os.Parcelable; + +import org.sufficientlysecure.keychain.util.Passphrase; + + +/** This is a base class for the input of crypto operations. + * + */ +public class CryptoInputParcel implements Parcelable { + +    final Date mSignatureTime; +    final Passphrase mPassphrase; + +    // this map contains both decrypted session keys and signed hashes to be +    // used in the crypto operation described by this parcel. +    private HashMap<ByteBuffer,byte[]> mCryptoData = new HashMap<>(); + +    public CryptoInputParcel() { +        mSignatureTime = new Date(); +        mPassphrase = null; +    } + +    public CryptoInputParcel(Date signatureTime, Passphrase passphrase) { +        mSignatureTime = signatureTime == null ? new Date() : signatureTime; +        mPassphrase = passphrase; +    } + +    public CryptoInputParcel(Passphrase passphrase) { +        mSignatureTime = new Date(); +        mPassphrase = passphrase; +    } + +    public CryptoInputParcel(Date signatureTime) { +        mSignatureTime = signatureTime == null ? new Date() : signatureTime; +        mPassphrase = null; +    } + +    protected CryptoInputParcel(Parcel source) { +        mSignatureTime = new Date(source.readLong()); +        mPassphrase = source.readParcelable(getClass().getClassLoader()); + +        { +            int count = source.readInt(); +            mCryptoData = new HashMap<>(count); +            for (int i = 0; i < count; i++) { +                byte[] key = source.createByteArray(); +                byte[] value = source.createByteArray(); +                mCryptoData.put(ByteBuffer.wrap(key), value); +            } +        } + +    } + +    @Override +    public int describeContents() { +        return 0; +    } + +    @Override +    public void writeToParcel(Parcel dest, int flags) { +        dest.writeLong(mSignatureTime.getTime()); +        dest.writeParcelable(mPassphrase, 0); + +        dest.writeInt(mCryptoData.size()); +        for (HashMap.Entry<ByteBuffer,byte[]> entry : mCryptoData.entrySet()) { +            dest.writeByteArray(entry.getKey().array()); +            dest.writeByteArray(entry.getValue()); +        } +    } + +    public void addCryptoData(byte[] hash, byte[] signedHash) { +        mCryptoData.put(ByteBuffer.wrap(hash), signedHash); +    } + +    public Map<ByteBuffer, byte[]> getCryptoData() { +        return Collections.unmodifiableMap(mCryptoData); +    } + +    public Date getSignatureTime() { +        return mSignatureTime; +    } + +    public boolean hasPassphrase() { +        return mPassphrase != null; +    } + +    public Passphrase getPassphrase() { +        return mPassphrase; +    } + +    public static final Creator<CryptoInputParcel> CREATOR = new Creator<CryptoInputParcel>() { +        public CryptoInputParcel createFromParcel(final Parcel source) { +            return new CryptoInputParcel(source); +        } + +        public CryptoInputParcel[] newArray(final int size) { +            return new CryptoInputParcel[size]; +        } +    }; + +    @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 new file mode 100644 index 000000000..d8d87114e --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/RequiredInputParcel.java @@ -0,0 +1,187 @@ +package org.sufficientlysecure.keychain.service.input; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; + +import android.os.Parcel; +import android.os.Parcelable; + + +public class RequiredInputParcel implements Parcelable { + +    public enum RequiredInputType { +        PASSPHRASE, NFC_SIGN, NFC_DECRYPT +    } + +    public Date mSignatureTime; + +    public final RequiredInputType mType; + +    public final byte[][] mInputHashes; +    public final int[] mSignAlgos; + +    private Long mMasterKeyId; +    private Long mSubKeyId; + +    private RequiredInputParcel(RequiredInputType type, byte[][] inputHashes, +            int[] signAlgos, Date signatureTime, Long masterKeyId, Long subKeyId) { +        mType = type; +        mInputHashes = inputHashes; +        mSignAlgos = signAlgos; +        mSignatureTime = signatureTime; +        mMasterKeyId = masterKeyId; +        mSubKeyId = subKeyId; +    } + +    public RequiredInputParcel(Parcel source) { +        mType = RequiredInputType.values()[source.readInt()]; + +        if (source.readInt() != 0) { +            int count = source.readInt(); +            mInputHashes = new byte[count][]; +            mSignAlgos = new int[count]; +            for (int i = 0; i < count; i++) { +                mInputHashes[i] = source.createByteArray(); +                mSignAlgos[i] = source.readInt(); +            } +        } else { +            mInputHashes = null; +            mSignAlgos = null; +        } + +        mSignatureTime = source.readInt() != 0 ? new Date(source.readLong()) : null; +        mMasterKeyId = source.readInt() != 0 ? source.readLong() : null; +        mSubKeyId = source.readInt() != 0 ? source.readLong() : null; + +    } + +    public long getSubKeyId() { +        return mSubKeyId; +    } + +    public static RequiredInputParcel createNfcSignOperation( +            byte[] inputHash, int signAlgo, Date signatureTime) { +        return new RequiredInputParcel(RequiredInputType.NFC_SIGN, +                new byte[][] { inputHash }, new int[] { signAlgo }, +                signatureTime, null, null); +    } + +    public static RequiredInputParcel createNfcDecryptOperation(byte[] inputHash) { +        return new RequiredInputParcel(RequiredInputType.NFC_DECRYPT, +                new byte[][] { inputHash }, null, null, null, null); +    } + +    public static RequiredInputParcel createRequiredPassphrase( +            long masterKeyId, long subKeyId, Date signatureTime) { +        return new RequiredInputParcel(RequiredInputType.PASSPHRASE, +                null, null, signatureTime, masterKeyId, subKeyId); +    } + +    public static RequiredInputParcel createRequiredPassphrase( +            RequiredInputParcel req) { +        return new RequiredInputParcel(RequiredInputType.PASSPHRASE, +                null, null, req.mSignatureTime, req.mMasterKeyId, req.mSubKeyId); +    } + + +    @Override +    public int describeContents() { +        return 0; +    } + +    @Override +    public void writeToParcel(Parcel dest, int flags) { +        dest.writeInt(mType.ordinal()); +        if (mInputHashes != null) { +            dest.writeInt(1); +            dest.writeInt(mInputHashes.length); +            for (int i = 0; i < mInputHashes.length; i++) { +                dest.writeByteArray(mInputHashes[i]); +                dest.writeInt(mSignAlgos[i]); +            } +        } else { +            dest.writeInt(0); +        } +        if (mSignatureTime != null) { +            dest.writeInt(1); +            dest.writeLong(mSignatureTime.getTime()); +        } else { +            dest.writeInt(0); +        } +        if (mMasterKeyId != null) { +            dest.writeInt(1); +            dest.writeLong(mMasterKeyId); +        } else { +            dest.writeInt(0); +        } +        if (mSubKeyId != null) { +            dest.writeInt(1); +            dest.writeLong(mSubKeyId); +        } else { +            dest.writeInt(0); +        } + +    } + +    public static final Creator<RequiredInputParcel> CREATOR = new Creator<RequiredInputParcel>() { +        public RequiredInputParcel createFromParcel(final Parcel source) { +            return new RequiredInputParcel(source); +        } + +        public RequiredInputParcel[] newArray(final int size) { +            return new RequiredInputParcel[size]; +        } +    }; + +    public static class NfcSignOperationsBuilder { +        Date mSignatureTime; +        ArrayList<Integer> mSignAlgos = new ArrayList<>(); +        ArrayList<byte[]> mInputHashes = new ArrayList<>(); +        long mMasterKeyId; +        long mSubKeyId; + +        public NfcSignOperationsBuilder(Date signatureTime, long masterKeyId, long subKeyId) { +            mSignatureTime = signatureTime; +            mMasterKeyId = masterKeyId; +            mSubKeyId = subKeyId; +        } + +        public RequiredInputParcel build() { +            byte[][] inputHashes = new byte[mInputHashes.size()][]; +            mInputHashes.toArray(inputHashes); +            int[] signAlgos = new int[mSignAlgos.size()]; +            for (int i = 0; i < mSignAlgos.size(); i++) { +                signAlgos[i] = mSignAlgos.get(i); +            } + +            return new RequiredInputParcel(RequiredInputType.NFC_SIGN, +                    inputHashes, signAlgos, mSignatureTime, mMasterKeyId, mSubKeyId); +        } + +        public void addHash(byte[] hash, int algo) { +            mInputHashes.add(hash); +            mSignAlgos.add(algo); +        } + +        public void addAll(RequiredInputParcel input) { +            if (!mSignatureTime.equals(input.mSignatureTime)) { +                throw new AssertionError("input times must match, this is a programming error!"); +            } +            if (input.mType != RequiredInputType.NFC_SIGN) { +                throw new AssertionError("operation types must match, this is a progrmming error!"); +            } + +            Collections.addAll(mInputHashes, input.mInputHashes); +            for (int signAlgo : input.mSignAlgos) { +                mSignAlgos.add(signAlgo); +            } +        } + +        public boolean isEmpty() { +            return mInputHashes.isEmpty(); +        } + +    } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java index b3738851c..b98fd90f1 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java @@ -56,7 +56,7 @@ import org.sufficientlysecure.keychain.service.CertifyActionsParcel;  import org.sufficientlysecure.keychain.service.CertifyActionsParcel.CertifyAction;  import org.sufficientlysecure.keychain.service.KeychainIntentService;  import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; -import org.sufficientlysecure.keychain.service.PassphraseCacheService; +import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;  import org.sufficientlysecure.keychain.ui.adapter.MultiUserIdsAdapter;  import org.sufficientlysecure.keychain.ui.util.Notify;  import org.sufficientlysecure.keychain.ui.widget.CertifyKeySpinner; @@ -68,10 +68,9 @@ import org.sufficientlysecure.keychain.util.Preferences;  import java.lang.reflect.Method;  import java.util.ArrayList; -public class CertifyKeyFragment extends LoaderFragment -        implements LoaderManager.LoaderCallbacks<Cursor> { -    public static final int REQUEST_CODE_PASSPHRASE = 0x00008001; +public class CertifyKeyFragment extends CryptoOperationFragment +        implements LoaderManager.LoaderCallbacks<Cursor> {      private CheckBox mUploadKeyCheckbox;      ListView mUserIds; @@ -101,9 +100,6 @@ public class CertifyKeyFragment extends LoaderFragment      public void onActivityCreated(Bundle savedInstanceState) {          super.onActivityCreated(savedInstanceState); -        // Start out with a progress indicator. -        setContentShown(false); -          mPubMasterKeyIds = getActivity().getIntent().getLongArrayExtra(CertifyKeyActivity.EXTRA_KEY_IDS);          if (mPubMasterKeyIds == null) {              Log.e(Constants.TAG, "List of key ids to certify missing!"); @@ -113,6 +109,7 @@ public class CertifyKeyFragment extends LoaderFragment          mPassthroughMessenger = getActivity().getIntent().getParcelableExtra(                  KeychainIntentService.EXTRA_MESSENGER); +        mPassthroughMessenger = null; // TODO remove, development hack          // preselect certify key id if given          long certifyKeyId = getActivity().getIntent().getLongExtra(CertifyKeyActivity.EXTRA_CERTIFY_KEY_ID, Constants.key.none); @@ -142,9 +139,7 @@ public class CertifyKeyFragment extends LoaderFragment      @Override      public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) { -        View root = super.onCreateView(inflater, superContainer, savedInstanceState); - -        View view = inflater.inflate(R.layout.certify_key_fragment, getContainer()); +        View view = inflater.inflate(R.layout.certify_key_fragment, null);          mCertifyKeySpinner = (CertifyKeySpinner) view.findViewById(R.id.certify_key_spinner);          mUploadKeyCheckbox = (CheckBox) view.findViewById(R.id.sign_key_upload_checkbox); @@ -172,7 +167,7 @@ public class CertifyKeyFragment extends LoaderFragment                      Notify.create(getActivity(), getString(R.string.select_key_to_certify),                              Notify.Style.ERROR).show();                  } else { -                    initiateCertifying(); +                    cryptoOperation(null);                  }              }          }); @@ -182,7 +177,7 @@ public class CertifyKeyFragment extends LoaderFragment              mUploadKeyCheckbox.setChecked(false);          } -        return root; +        return view;      }      @Override @@ -306,7 +301,6 @@ public class CertifyKeyFragment extends LoaderFragment          }          mUserIdsAdapter.swapCursor(matrix); -        setContentShown(true, isResumed());      }      @Override @@ -314,49 +308,8 @@ public class CertifyKeyFragment extends LoaderFragment          mUserIdsAdapter.swapCursor(null);      } -    /** -     * handles the UI bits of the signing process on the UI thread -     */ -    private void initiateCertifying() { -        // get the user's passphrase for this key (if required) -        Passphrase passphrase; -        try { -            passphrase = PassphraseCacheService.getCachedPassphrase(getActivity(), mSignMasterKeyId, mSignMasterKeyId); -        } catch (PassphraseCacheService.KeyNotFoundException e) { -            Log.e(Constants.TAG, "Key not found!", e); -            getActivity().finish(); -            return; -        } -        if (passphrase == null) { -            Intent intent = new Intent(getActivity(), PassphraseDialogActivity.class); -            intent.putExtra(PassphraseDialogActivity.EXTRA_SUBKEY_ID, mSignMasterKeyId); -            startActivityForResult(intent, REQUEST_CODE_PASSPHRASE); -            // bail out; need to wait until the user has entered the passphrase before trying again -        } else { -            startCertifying(); -        } -    } -      @Override -    public void onActivityResult(int requestCode, int resultCode, Intent data) { -        switch (requestCode) { -            case REQUEST_CODE_PASSPHRASE: { -                if (resultCode == Activity.RESULT_OK && data != null) { -                    startCertifying(); -                } -                return; -            } - -            default: { -                super.onActivityResult(requestCode, resultCode, data); -            } -        } -    } - -    /** -     * kicks off the actual signing process on a background thread -     */ -    private void startCertifying() { +    protected void cryptoOperation(CryptoInputParcel cryptoInput) {          // Bail out if there is not at least one user id selected          ArrayList<CertifyAction> certifyActions = mUserIdsAdapter.getSelectedCertifyActions();          if (certifyActions.isEmpty()) { @@ -371,6 +324,7 @@ public class CertifyKeyFragment extends LoaderFragment              CertifyActionsParcel parcel = new CertifyActionsParcel(mSignMasterKeyId);              parcel.mCertifyActions.addAll(certifyActions); +            data.putParcelable(KeychainIntentService.EXTRA_CRYPTO_INPUT, cryptoInput);              data.putParcelable(KeychainIntentService.CERTIFY_PARCEL, parcel);              if (mUploadKeyCheckbox.isChecked()) {                  String keyserver = Preferences.getPreferences(getActivity()).getPreferredKeyserver(); @@ -388,14 +342,21 @@ public class CertifyKeyFragment extends LoaderFragment          } else {              // Message is received after signing is done in KeychainIntentService -            KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(getActivity(), -                    getString(R.string.progress_certifying), ProgressDialog.STYLE_SPINNER, true) { +            KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler( +                    getActivity(), getString(R.string.progress_certifying), +                    ProgressDialog.STYLE_SPINNER, true) {                  public void handleMessage(Message message) { -                    // handle messages by standard KeychainIntentServiceHandler first +                    // handle messages by KeychainIntentCryptoServiceHandler first                      super.handleMessage(message); +                    // handle pending messages +                    if (handlePendingMessage(message)) { +                        return; +                    } +                      if (message.arg1 == MessageStatus.OKAY.ordinal()) {                          Bundle data = message.getData(); +                          CertifyResult result = data.getParcelable(CertifyResult.EXTRA_RESULT);                          Intent intent = new Intent(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CryptoOperationFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CryptoOperationFragment.java new file mode 100644 index 000000000..6b67d1db8 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CryptoOperationFragment.java @@ -0,0 +1,97 @@ +package org.sufficientlysecure.keychain.ui; + + +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; +import android.os.Message; +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; + + +public abstract class CryptoOperationFragment extends Fragment { + +    public static final int REQUEST_CODE_PASSPHRASE = 0x00008001; +    public static final int REQUEST_CODE_NFC = 0x00008002; + +    private void initiateInputActivity(RequiredInputParcel requiredInput) { + +        switch (requiredInput.mType) { +            case NFC_DECRYPT: +            case NFC_SIGN: { +                Intent intent = new Intent(getActivity(), NfcOperationActivity.class); +                intent.putExtra(NfcOperationActivity.EXTRA_REQUIRED_INPUT, requiredInput); +                startActivityForResult(intent, REQUEST_CODE_NFC); +                return; +            } + +            case PASSPHRASE: { +                Intent intent = new Intent(getActivity(), PassphraseDialogActivity.class); +                intent.putExtra(PassphraseDialogActivity.EXTRA_REQUIRED_INPUT, requiredInput); +                startActivityForResult(intent, REQUEST_CODE_PASSPHRASE); +                return; +            } +        } + +        throw new RuntimeException("Unhandled pending result!"); + +    } + +    @Override +    public void onActivityResult(int requestCode, int resultCode, Intent data) { +        switch (requestCode) { +            case REQUEST_CODE_PASSPHRASE: { +                if (resultCode == Activity.RESULT_OK && data != null) { +                    CryptoInputParcel cryptoInput = +                            data.getParcelableExtra(PassphraseDialogActivity.RESULT_DATA); +                    cryptoOperation(cryptoInput); +                } +                return; +            } + +            case REQUEST_CODE_NFC: { +                if (resultCode == Activity.RESULT_OK && data != null) { +                    CryptoInputParcel cryptoInput = +                            data.getParcelableExtra(NfcOperationActivity.RESULT_DATA); +                    cryptoOperation(cryptoInput); +                    return; +                } +                break; +            } + +            default: { +                super.onActivityResult(requestCode, resultCode, data); +            } +        } +    } + +    public boolean handlePendingMessage(Message message) { + +        if (message.arg1 == MessageStatus.OKAY.ordinal()) { +            Bundle data = message.getData(); + +            OperationResult result = data.getParcelable(CertifyResult.EXTRA_RESULT); +            if (result == null || ! (result instanceof InputPendingResult)) { +                return false; +            } + +            InputPendingResult pendingResult = (InputPendingResult) result; +            if (pendingResult.isPending()) { +                RequiredInputParcel requiredInput = pendingResult.getRequiredInputParcel(); +                initiateInputActivity(requiredInput); +                return true; +            } +        } + +        return false; +    } + +    protected abstract void cryptoOperation(CryptoInputParcel cryptoInput); + +} 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 61a02720e..13fba14e9 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; @@ -69,14 +71,13 @@ import org.sufficientlysecure.keychain.ui.util.Notify;  import org.sufficientlysecure.keychain.util.Log;  import org.sufficientlysecure.keychain.util.Passphrase; -public class EditKeyFragment extends LoaderFragment implements + +public class EditKeyFragment extends CryptoOperationFragment implements          LoaderManager.LoaderCallbacks<Cursor> {      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; @@ -101,7 +102,6 @@ public class EditKeyFragment extends LoaderFragment implements      private SaveKeyringParcel mSaveKeyringParcel;      private String mPrimaryUserId; -    private Passphrase mCurrentPassphrase;      /**       * Creates new instance of this fragment @@ -130,8 +130,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); @@ -141,7 +140,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 @@ -156,7 +155,7 @@ public class EditKeyFragment extends LoaderFragment implements                          if (mDataUri == null) {                              returnKeyringParcel();                          } else { -                            saveInDatabase(mCurrentPassphrase); +                            cryptoOperation(new CryptoInputParcel(new Date()));                          }                      }                  }, new OnClickListener() { @@ -186,18 +185,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) { @@ -217,9 +210,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()); @@ -230,24 +220,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); @@ -263,28 +239,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.getParcelableExtra(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 @@ -323,7 +277,6 @@ public class EditKeyFragment extends LoaderFragment implements      }      public Loader<Cursor> onCreateLoader(int id, Bundle args) { -        setContentShown(false);          switch (id) {              case LOADER_ID_USER_IDS: { @@ -356,7 +309,6 @@ public class EditKeyFragment extends LoaderFragment implements                  break;          } -        setContentShown(true);      }      /** @@ -398,7 +350,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");      } @@ -594,8 +546,11 @@ public class EditKeyFragment extends LoaderFragment implements          getActivity().finish();      } -    private void saveInDatabase(Passphrase 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(), @@ -606,6 +561,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 @@ -641,7 +600,7 @@ public class EditKeyFragment extends LoaderFragment implements          // fill values for this action          Bundle data = new Bundle(); -        data.putParcelable(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 5438f667c..b87dec8fc 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java @@ -25,16 +25,16 @@ import android.os.Message;  import android.os.Messenger;  import android.view.View; -import org.openintents.openpgp.util.OpenPgpApi;  import org.sufficientlysecure.keychain.R;  import org.sufficientlysecure.keychain.operations.results.PgpSignEncryptResult;  import org.sufficientlysecure.keychain.operations.results.SignEncryptResult;  import org.sufficientlysecure.keychain.pgp.SignEncryptParcel;  import org.sufficientlysecure.keychain.service.KeychainIntentService;  import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; +import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; +import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;  import org.sufficientlysecure.keychain.util.Passphrase; -import java.util.Date;  public abstract class EncryptActivity extends BaseActivity { @@ -43,8 +43,6 @@ public abstract class EncryptActivity extends BaseActivity {      // For NFC data      protected Passphrase mSigningKeyPassphrase = null; -    protected Date mNfcTimestamp = null; -    protected byte[] mNfcHash = null;      @Override      public void onCreate(Bundle savedInstanceState) { @@ -65,17 +63,11 @@ public abstract class EncryptActivity extends BaseActivity {          startActivityForResult(intent, REQUEST_CODE_PASSPHRASE);      } -    protected void startNfcSign(long keyId, Passphrase pin, byte[] hashToSign, int hashAlgo) { -        // build PendingIntent for Yubikey NFC operations -        Intent intent = new Intent(this, NfcActivity.class); -        intent.setAction(NfcActivity.ACTION_SIGN_HASH); +    protected void startNfcSign(long keyId, RequiredInputParcel nfcOps) { -        // pass params through to activity that it can be returned again later to repeat pgp operation -        intent.putExtra(NfcActivity.EXTRA_DATA, new Intent()); // not used, only relevant to OpenPgpService -        intent.putExtra(NfcActivity.EXTRA_KEY_ID, keyId); -        intent.putExtra(NfcActivity.EXTRA_PIN, pin); -        intent.putExtra(NfcActivity.EXTRA_NFC_HASH_TO_SIGN, hashToSign); -        intent.putExtra(NfcActivity.EXTRA_NFC_HASH_ALGO, hashAlgo); +        Intent intent = new Intent(this, NfcOperationActivity.class); +        intent.putExtra(NfcOperationActivity.EXTRA_REQUIRED_INPUT, nfcOps); +        // TODO respect keyid(?)          startActivityForResult(intent, REQUEST_CODE_NFC);      } @@ -94,8 +86,9 @@ public abstract class EncryptActivity extends BaseActivity {              case REQUEST_CODE_NFC: {                  if (resultCode == RESULT_OK && data != null) { -                    mNfcHash = data.getByteArrayExtra(OpenPgpApi.EXTRA_NFC_SIGNED_HASH); -                    startEncrypt(); +                    CryptoInputParcel cryptoInput = +                            data.getParcelableExtra(NfcOperationActivity.RESULT_DATA); +                    startEncrypt(cryptoInput);                      return;                  }                  break; @@ -109,6 +102,10 @@ public abstract class EncryptActivity extends BaseActivity {      }      public void startEncrypt() { +        startEncrypt(null); +    } + +    public void startEncrypt(CryptoInputParcel cryptoInput) {          if (!inputIsValid()) {              // Notify was created by inputIsValid.              return; @@ -118,8 +115,13 @@ public abstract class EncryptActivity extends BaseActivity {          Intent intent = new Intent(this, KeychainIntentService.class);          intent.setAction(KeychainIntentService.ACTION_SIGN_ENCRYPT); +        final SignEncryptParcel input = createEncryptBundle(); +        if (cryptoInput != null) { +            input.setCryptoInput(cryptoInput); +        } +          Bundle data = new Bundle(); -        data.putParcelable(KeychainIntentService.SIGN_ENCRYPT_PARCEL, createEncryptBundle()); +        data.putParcelable(KeychainIntentService.SIGN_ENCRYPT_PARCEL, input);          intent.putExtra(KeychainIntentService.EXTRA_DATA, data);          // Message is received after encrypting is done in KeychainIntentService @@ -142,9 +144,12 @@ public abstract class EncryptActivity extends BaseActivity {                          } else if ((pgpResult.getResult() & PgpSignEncryptResult.RESULT_PENDING_NFC) ==                                  PgpSignEncryptResult.RESULT_PENDING_NFC) { -                            mNfcTimestamp = pgpResult.getNfcTimestamp(); -                            startNfcSign(pgpResult.getNfcKeyId(), pgpResult.getNfcPassphrase(), -                                    pgpResult.getNfcHash(), pgpResult.getNfcAlgo()); +                            RequiredInputParcel parcel = RequiredInputParcel.createNfcSignOperation( +                                    pgpResult.getNfcHash(), +                                    pgpResult.getNfcAlgo(), +                                    input.getSignatureTime()); +                            startNfcSign(pgpResult.getNfcKeyId(), parcel); +                          } else {                              throw new RuntimeException("Unhandled pending result!");                          } @@ -159,8 +164,6 @@ public abstract class EncryptActivity extends BaseActivity {                      // no matter the result, reset parameters                      mSigningKeyPassphrase = null; -                    mNfcHash = null; -                    mNfcTimestamp = null;                  }              }          }; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesActivity.java index fe9b05226..ac54ebff6 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesActivity.java @@ -253,7 +253,6 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi              data.setEncryptionMasterKeyIds(mEncryptionKeyIds);              data.setSignatureMasterKeyId(mSigningKeyId);              data.setSignaturePassphrase(mSigningKeyPassphrase); -            data.setNfcState(mNfcHash, mNfcTimestamp);          }          return data;      } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextActivity.java index c800153ae..827418f08 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextActivity.java @@ -235,7 +235,6 @@ public class EncryptTextActivity extends EncryptActivity implements EncryptActiv              data.setEncryptionMasterKeyIds(mEncryptionKeyIds);              data.setSignatureMasterKeyId(mSigningKeyId);              data.setSignaturePassphrase(mSigningKeyPassphrase); -            data.setNfcState(mNfcHash, mNfcTimestamp);          }          return data;      } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcOperationActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcOperationActivity.java new file mode 100644 index 000000000..68eee2513 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcOperationActivity.java @@ -0,0 +1,524 @@ +/** + * Copyright (c) 2013-2014 Philipp Jakubeit, Signe Rüsch, Dominik Schürmann + * + * Licensed under the Bouncy Castle License (MIT license). See LICENSE file for details. + */ + +package org.sufficientlysecure.keychain.ui; + +import android.annotation.TargetApi; +import android.app.PendingIntent; +import android.content.Intent; +import android.content.IntentFilter; +import android.nfc.NfcAdapter; +import android.nfc.Tag; +import android.nfc.tech.IsoDep; +import android.os.Build; +import android.os.Bundle; +import android.view.WindowManager; +import android.widget.Toast; + +import org.spongycastle.bcpg.HashAlgorithmTags; +import org.spongycastle.util.encoders.Hex; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; +import org.sufficientlysecure.keychain.service.input.RequiredInputParcel; +import org.sufficientlysecure.keychain.util.Iso7816TLV; +import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.Passphrase; +import org.sufficientlysecure.keychain.util.Preferences; + +import java.io.IOException; +import java.nio.ByteBuffer; + + +/** + * This class provides a communication interface to OpenPGP applications on ISO SmartCard compliant + * NFC devices. + * + * For the full specs, see http://g10code.com/docs/openpgp-card-2.0.pdf + */ +@TargetApi(Build.VERSION_CODES.GINGERBREAD_MR1) +public class NfcOperationActivity extends BaseActivity { + +    public static final int REQUEST_CODE_PASSPHRASE = 1; + +    public static final String EXTRA_REQUIRED_INPUT = "required_input"; + +    public static final String RESULT_DATA = "result_data"; + +    private static final int TIMEOUT = 100000; + +    private NfcAdapter mNfcAdapter; +    private IsoDep mIsoDep; + +    RequiredInputParcel mRequiredInput; +    private Passphrase mPin; + +    @Override +    protected void onCreate(Bundle savedInstanceState) { +        super.onCreate(savedInstanceState); +        Log.d(Constants.TAG, "NfcOperationActivity.onCreate"); + +        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + +        Intent intent = getIntent(); +        String action = intent.getAction(); +        if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(action)) { +            throw new AssertionError("should not happen: NfcOperationActivity.onCreate is called instead of onNewIntent!"); +        } + +        Bundle data = intent.getExtras(); + +        mRequiredInput = data.getParcelable(EXTRA_REQUIRED_INPUT); + +        obtainPassphrase(); + +    } + +    private void obtainPassphrase() { + +        Preferences prefs = Preferences.getPreferences(this); +        if (prefs.useDefaultYubikeyPin()) { +            mPin = new Passphrase("123456"); +            return; +        } + +        Intent intent = new Intent(this, PassphraseDialogActivity.class); +        intent.putExtra(PassphraseDialogActivity.EXTRA_REQUIRED_INPUT, +                RequiredInputParcel.createRequiredPassphrase(mRequiredInput)); +        startActivityForResult(intent, REQUEST_CODE_PASSPHRASE); + +    } + +    @Override +    protected void onActivityResult(int requestCode, int resultCode, Intent data) { +        switch (requestCode) { +            case REQUEST_CODE_PASSPHRASE: +                CryptoInputParcel input = data.getParcelableExtra(PassphraseDialogActivity.RESULT_DATA); +                mPin = input.getPassphrase(); +                break; + +            default: +                super.onActivityResult(requestCode, resultCode, data); +        } +    } + +    @Override +    protected void initLayout() { +        setContentView(R.layout.nfc_activity); +    } + +    /** +     * Called when the system is about to start resuming a previous activity, +     * disables NFC Foreground Dispatch +     */ +    public void onPause() { +        super.onPause(); +        Log.d(Constants.TAG, "NfcOperationActivity.onPause"); + +        disableNfcForegroundDispatch(); +    } + +    /** +     * Called when the activity will start interacting with the user, +     * enables NFC Foreground Dispatch +     */ +    public void onResume() { +        super.onResume(); +        Log.d(Constants.TAG, "NfcOperationActivity.onResume"); + +        enableNfcForegroundDispatch(); +    } + +    /** +     * This activity is started as a singleTop activity. +     * All new NFC Intents which are delivered to this activity are handled here +     */ +    public void onNewIntent(Intent intent) { +        if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(intent.getAction())) { +            try { +                handleNdefDiscoveredIntent(intent); +            } catch (IOException e) { +                Log.e(Constants.TAG, "Connection error!", e); +                toast("Connection Error: " + e.getMessage()); +                setResult(RESULT_CANCELED); +                finish(); +            } +        } +    } + +    /** Handle NFC communication and return a result. +     * +     * This method is called by onNewIntent above upon discovery of an NFC tag. +     * It handles initialization and login to the application, subsequently +     * calls either nfcCalculateSignature() or nfcDecryptSessionKey(), then +     * finishes the activity with an appropiate result. +     * +     * On general communication, see also +     * http://www.cardwerk.com/smartcards/smartcard_standard_ISO7816-4_annex-a.aspx +     * +     * References to pages are generally related to the OpenPGP Application +     * on ISO SmartCard Systems specification. +     * +     */ +    private void handleNdefDiscoveredIntent(Intent intent) throws IOException { + +        Tag detectedTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); + +        // Connect to the detected tag, setting a couple of settings +        mIsoDep = IsoDep.get(detectedTag); +        mIsoDep.setTimeout(TIMEOUT); // timeout is set to 100 seconds to avoid cancellation during calculation +        mIsoDep.connect(); + +        // SW1/2 0x9000 is the generic "ok" response, which we expect most of the time. +        // See specification, page 51 +        String accepted = "9000"; + +        // Command APDU (page 51) for SELECT FILE command (page 29) +        String opening = +                "00" // CLA +                        + "A4" // INS +                        + "04" // P1 +                        + "00" // P2 +                        + "06" // Lc (number of bytes) +                        + "D27600012401" // Data (6 bytes) +                        + "00"; // Le +        if ( ! card(opening).equals(accepted)) { // activate connection +            toast("Opening Error!"); +            setResult(RESULT_CANCELED); +            finish(); +            return; +        } + +        byte[] pin = new String(mPin.getCharArray()).getBytes(); + +        // Command APDU for VERIFY command (page 32) +        String login = +                "00" // CLA +                        + "20" // INS +                        + "00" // P1 +                        + "82" // P2 (PW1) +                        + String.format("%02x", pin.length) // Lc +                        + Hex.toHexString(pin); +        if ( ! card(login).equals(accepted)) { // login +            toast("Wrong PIN!"); +            setResult(RESULT_CANCELED); +            finish(); +            return; +        } + +        CryptoInputParcel resultData = new CryptoInputParcel(mRequiredInput.mSignatureTime); + +        switch (mRequiredInput.mType) { + +            case NFC_DECRYPT: +                for (int i = 0; i < mRequiredInput.mInputHashes.length; i++) { +                    byte[] hash = mRequiredInput.mInputHashes[i]; +                    byte[] decryptedSessionKey = nfcDecryptSessionKey(hash); +                    resultData.addCryptoData(hash, decryptedSessionKey); +                } +                break; + +            case NFC_SIGN: +                for (int i = 0; i < mRequiredInput.mInputHashes.length; i++) { +                    byte[] hash = mRequiredInput.mInputHashes[i]; +                    int algo = mRequiredInput.mSignAlgos[i]; +                    byte[] signedHash = nfcCalculateSignature(hash, algo); +                    resultData.addCryptoData(hash, signedHash); +                } +                break; +        } + +        // give data through for new service call +        Intent result = new Intent(); +        result.putExtra(RESULT_DATA, resultData); +        setResult(RESULT_OK, result); +        finish(); + +    } + +    /** +     * Gets the user ID +     * +     * @return the user id as "name <email>" +     * @throws java.io.IOException +     */ +    public String getUserId() throws IOException { +        String info = "00CA006500"; +        String data = "00CA005E00"; +        return getName(card(info)) + " <" + (new String(Hex.decode(getDataField(card(data))))) + ">"; +    } + +    /** Return the key id from application specific data stored on tag, or null +     * if it doesn't exist. +     * +     * @param idx Index of the key to return the fingerprint from. +     * @return The long key id of the requested key, or null if not found. +     */ +    public static Long nfcGetKeyId(IsoDep isoDep, int idx) throws IOException { +        byte[] fp = nfcGetFingerprint(isoDep, idx); +        if (fp == null) { +            return null; +        } +        ByteBuffer buf = ByteBuffer.wrap(fp); +        // skip first 12 bytes of the fingerprint +        buf.position(12); +        // the last eight bytes are the key id (big endian, which is default order in ByteBuffer) +        return buf.getLong(); +    } + +    /** Return fingerprints of all keys from application specific data stored +     * on tag, or null if data not available. +     * +     * @return The fingerprints of all subkeys in a contiguous byte array. +     */ +    public static byte[] nfcGetFingerprints(IsoDep isoDep) throws IOException { +        String data = "00CA006E00"; +        byte[] buf = isoDep.transceive(Hex.decode(data)); + +        Iso7816TLV tlv = Iso7816TLV.readSingle(buf, true); +        Log.d(Constants.TAG, "nfc tlv data:\n" + tlv.prettyPrint()); + +        Iso7816TLV fptlv = Iso7816TLV.findRecursive(tlv, 0xc5); +        if (fptlv == null) { +            return null; +        } + +        return fptlv.mV; +    } + +    /** Return the fingerprint from application specific data stored on tag, or +     * null if it doesn't exist. +     * +     * @param idx Index of the key to return the fingerprint from. +     * @return The fingerprint of the requested key, or null if not found. +     */ +    public static byte[] nfcGetFingerprint(IsoDep isoDep, int idx) throws IOException { +        byte[] data = nfcGetFingerprints(isoDep); + +        // return the master key fingerprint +        ByteBuffer fpbuf = ByteBuffer.wrap(data); +        byte[] fp = new byte[20]; +        fpbuf.position(idx*20); +        fpbuf.get(fp, 0, 20); + +        return fp; +    } + +    /** +     * Calls to calculate the signature and returns the MPI value +     * +     * @param hash the hash for signing +     * @return a big integer representing the MPI for the given hash +     * @throws java.io.IOException +     */ +    public byte[] nfcCalculateSignature(byte[] hash, int hashAlgo) throws IOException { + +        // dsi, including Lc +        String dsi; + +        Log.i(Constants.TAG, "Hash: " + hashAlgo); +        switch (hashAlgo) { +            case HashAlgorithmTags.SHA1: +                if (hash.length != 20) { +                    throw new RuntimeException("Bad hash length (" + hash.length + ", expected 10!"); +                } +                dsi = "23" // Lc +                        + "3021" // Tag/Length of Sequence, the 0x21 includes all following 33 bytes +                        + "3009" // Tag/Length of Sequence, the 0x09 are the following header bytes +                        + "0605" + "2B0E03021A" // OID of SHA1 +                        + "0500" // TLV coding of ZERO +                        + "0414" + getHex(hash); // 0x14 are 20 hash bytes +                break; +            case HashAlgorithmTags.RIPEMD160: +                if (hash.length != 20) { +                    throw new RuntimeException("Bad hash length (" + hash.length + ", expected 20!"); +                } +                dsi = "233021300906052B2403020105000414" + getHex(hash); +                break; +            case HashAlgorithmTags.SHA224: +                if (hash.length != 28) { +                    throw new RuntimeException("Bad hash length (" + hash.length + ", expected 28!"); +                } +                dsi = "2F302D300D06096086480165030402040500041C" + getHex(hash); +                break; +            case HashAlgorithmTags.SHA256: +                if (hash.length != 32) { +                    throw new RuntimeException("Bad hash length (" + hash.length + ", expected 32!"); +                } +                dsi = "333031300D060960864801650304020105000420" + getHex(hash); +                break; +            case HashAlgorithmTags.SHA384: +                if (hash.length != 48) { +                    throw new RuntimeException("Bad hash length (" + hash.length + ", expected 48!"); +                } +                dsi = "433041300D060960864801650304020205000430" + getHex(hash); +                break; +            case HashAlgorithmTags.SHA512: +                if (hash.length != 64) { +                    throw new RuntimeException("Bad hash length (" + hash.length + ", expected 64!"); +                } +                dsi = "533051300D060960864801650304020305000440" + getHex(hash); +                break; +            default: +                throw new RuntimeException("Not supported hash algo!"); +        } + +        // Command APDU for PERFORM SECURITY OPERATION: COMPUTE DIGITAL SIGNATURE (page 37) +        String apdu  = +                "002A9E9A" // CLA, INS, P1, P2 +                        + dsi // digital signature input +                        + "00"; // Le + +        String response = card(apdu); + +        // split up response into signature and status +        String status = response.substring(response.length()-4); +        String signature = response.substring(0, response.length() - 4); + +        // while we are getting 0x61 status codes, retrieve more data +        while (status.substring(0, 2).equals("61")) { +            Log.d(Constants.TAG, "requesting more data, status " + status); +            // Send GET RESPONSE command +            response = card("00C00000" + status.substring(2)); +            status = response.substring(response.length()-4); +            signature += response.substring(0, response.length()-4); +        } + +        Log.d(Constants.TAG, "final response:" + status); + +        if ( ! status.equals("9000")) { +            toast("Bad NFC response code: " + status); +            return null; +        } + +        // Make sure the signature we received is actually the expected number of bytes long! +        if (signature.length() != 256 && signature.length() != 512) { +            toast("Bad signature length! Expected 128 or 256 bytes, got " + signature.length() / 2); +            return null; +        } + +        return Hex.decode(signature); +    } + +    /** +     * Calls to calculate the signature and returns the MPI value +     * +     * @param encryptedSessionKey the encoded session key +     * @return the decoded session key +     * @throws java.io.IOException +     */ +    public byte[] nfcDecryptSessionKey(byte[] encryptedSessionKey) throws IOException { +        String firstApdu = "102a8086fe"; +        String secondApdu = "002a808603"; +        String le = "00"; + +        byte[] one = new byte[254]; +        // leave out first byte: +        System.arraycopy(encryptedSessionKey, 1, one, 0, one.length); + +        byte[] two = new byte[encryptedSessionKey.length - 1 - one.length]; +        for (int i = 0; i < two.length; i++) { +            two[i] = encryptedSessionKey[i + one.length + 1]; +        } + +        String first = card(firstApdu + getHex(one)); +        String second = card(secondApdu + getHex(two) + le); + +        String decryptedSessionKey = getDataField(second); + +        Log.d(Constants.TAG, "decryptedSessionKey: " + decryptedSessionKey); + +        return Hex.decode(decryptedSessionKey); +    } + +    /** +     * Prints a message to the screen +     * +     * @param text the text which should be contained within the toast +     */ +    private void toast(String text) { +        Toast.makeText(this, text, Toast.LENGTH_LONG).show(); +    } + +    /** +     * Receive new NFC Intents to this activity only by enabling foreground dispatch. +     * This can only be done in onResume! +     */ +    public void enableNfcForegroundDispatch() { +        mNfcAdapter = NfcAdapter.getDefaultAdapter(this); +        Intent nfcI = new Intent(this, NfcOperationActivity.class) +                .addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP); +        PendingIntent nfcPendingIntent = PendingIntent.getActivity(this, 0, nfcI, PendingIntent.FLAG_CANCEL_CURRENT); +        IntentFilter[] writeTagFilters = new IntentFilter[]{ +                new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED) +        }; + +        // https://code.google.com/p/android/issues/detail?id=62918 +        // maybe mNfcAdapter.enableReaderMode(); ? +        try { +            mNfcAdapter.enableForegroundDispatch(this, nfcPendingIntent, writeTagFilters, null); +        } catch (IllegalStateException e) { +            Log.i(Constants.TAG, "NfcForegroundDispatch Error!", e); +        } +        Log.d(Constants.TAG, "NfcForegroundDispatch has been enabled!"); +    } + +    /** +     * Disable foreground dispatch in onPause! +     */ +    public void disableNfcForegroundDispatch() { +        mNfcAdapter.disableForegroundDispatch(this); +        Log.d(Constants.TAG, "NfcForegroundDispatch has been disabled!"); +    } + +    /** +     * Gets the name of the user out of the raw card output regarding card holder related data +     * +     * @param name the raw card holder related data from the card +     * @return the name given in this data +     */ +    public String getName(String name) { +        String slength; +        int ilength; +        name = name.substring(6); +        slength = name.substring(0, 2); +        ilength = Integer.parseInt(slength, 16) * 2; +        name = name.substring(2, ilength + 2); +        name = (new String(Hex.decode(name))).replace('<', ' '); +        return (name); +    } + +    /** +     * Reduces the raw data from the card by four characters +     * +     * @param output the raw data from the card +     * @return the data field of that data +     */ +    private String getDataField(String output) { +        return output.substring(0, output.length() - 4); +    } + +    /** +     * Communicates with the OpenPgpCard via the APDU +     * +     * @param hex the hexadecimal APDU +     * @return The answer from the card +     * @throws java.io.IOException throws an exception if something goes wrong +     */ +    public String card(String hex) throws IOException { +        return getHex(mIsoDep.transceive(Hex.decode(hex))); +    } + +    /** +     * Converts a byte array into an hex string +     * +     * @param raw the byte array representation +     * @return the  hexadecimal string representation +     */ +    public static String getHex(byte[] raw) { +        return new String(Hex.encode(raw)); +    } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java index 360d30c82..9e04426eb 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java @@ -41,6 +41,7 @@ import android.widget.EditText;  import android.widget.TextView;  import android.widget.Toast; +import junit.framework.Assert;  import org.sufficientlysecure.keychain.Constants;  import org.sufficientlysecure.keychain.R;  import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround; @@ -53,6 +54,9 @@ import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;  import org.sufficientlysecure.keychain.provider.KeychainContract;  import org.sufficientlysecure.keychain.provider.ProviderHelper;  import org.sufficientlysecure.keychain.service.PassphraseCacheService; +import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; +import org.sufficientlysecure.keychain.service.input.RequiredInputParcel; +import org.sufficientlysecure.keychain.service.input.RequiredInputParcel.RequiredInputType;  import org.sufficientlysecure.keychain.ui.dialog.CustomAlertDialogBuilder;  import org.sufficientlysecure.keychain.util.Log;  import org.sufficientlysecure.keychain.util.Passphrase; @@ -64,7 +68,9 @@ import org.sufficientlysecure.keychain.util.Preferences;   */  public class PassphraseDialogActivity extends FragmentActivity {      public static final String MESSAGE_DATA_PASSPHRASE = "passphrase"; +    public static final String RESULT_DATA = "result_data"; +    public static final String EXTRA_REQUIRED_INPUT = "required_input";      public static final String EXTRA_SUBKEY_ID = "secret_key_id";      // special extra for OpenPgpService @@ -87,7 +93,16 @@ public class PassphraseDialogActivity extends FragmentActivity {          // this activity itself has no content view (see manifest) -        long keyId = getIntent().getLongExtra(EXTRA_SUBKEY_ID, 0); +        long keyId; +        if (getIntent().hasExtra(EXTRA_SUBKEY_ID)) { +            keyId = getIntent().getLongExtra(EXTRA_SUBKEY_ID, 0); +        } else { +            RequiredInputParcel requiredInput = getIntent().getParcelableExtra(EXTRA_REQUIRED_INPUT); +            if (requiredInput.mType != RequiredInputType.PASSPHRASE) { +                throw new AssertionError("Wrong required input type for PassphraseDialogActivity!"); +            } +            keyId = requiredInput.getSubKeyId(); +        }          Intent serviceIntent = getIntent().getParcelableExtra(EXTRA_DATA); @@ -411,6 +426,7 @@ public class PassphraseDialogActivity extends FragmentActivity {                  // also return passphrase back to activity                  Intent returnIntent = new Intent();                  returnIntent.putExtra(MESSAGE_DATA_PASSPHRASE, passphrase); +                returnIntent.putExtra(RESULT_DATA, new CryptoInputParcel(null, passphrase));                  getActivity().setResult(RESULT_OK, returnIntent);              } 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 947c316e0..4eb253825 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 @@ -50,7 +50,6 @@ import org.sufficientlysecure.keychain.util.Passphrase;  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; @@ -68,12 +67,11 @@ public class SetPassphraseDialogFragment extends DialogFragment implements OnEdi       * @param messenger to communicate back after setting the passphrase       * @return       */ -    public static SetPassphraseDialogFragment newInstance(Messenger messenger, Passphrase 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.putParcelable(ARG_OLD_PASSPHRASE, oldPassphrase);          frag.setArguments(args); @@ -89,7 +87,6 @@ public class SetPassphraseDialogFragment extends DialogFragment implements OnEdi          int title = getArguments().getInt(ARG_TITLE);          mMessenger = getArguments().getParcelable(ARG_MESSENGER); -        Passphrase oldPassphrase = getArguments().getParcelable(ARG_OLD_PASSPHRASE);          CustomAlertDialogBuilder alert = new CustomAlertDialogBuilder(activity); @@ -103,13 +100,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 (oldPassphrase.isEmpty()) { -            mNoPassphraseCheckBox.setChecked(true); -            mPassphraseEditText.setEnabled(false); -            mPassphraseAgainEditText.setEnabled(false); -        } -          mNoPassphraseCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {              @Override              public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { | 
