diff options
| author | Vincent Breitmoser <valodim@mugenguild.com> | 2014-12-29 23:12:11 +0100 | 
|---|---|---|
| committer | Vincent Breitmoser <valodim@mugenguild.com> | 2014-12-29 23:12:11 +0100 | 
| commit | 576e6fd0cca41691a52db8e1325508f00a6e9bc6 (patch) | |
| tree | 98382f7c4ca1103577c736960859983b3d694460 /OpenKeychain/src/main | |
| parent | bf4762ef6f33543657f0eace12164bc0c5093054 (diff) | |
| download | open-keychain-576e6fd0cca41691a52db8e1325508f00a6e9bc6.tar.gz open-keychain-576e6fd0cca41691a52db8e1325508f00a6e9bc6.tar.bz2 open-keychain-576e6fd0cca41691a52db8e1325508f00a6e9bc6.zip  | |
introduce new ChangeUnlockParcel packet for extended passphrase changing capabilities
Diffstat (limited to 'OpenKeychain/src/main')
5 files changed, 173 insertions, 65 deletions
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 6e45fab99..c125165a8 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java @@ -52,6 +52,7 @@ import org.sufficientlysecure.keychain.operations.results.OperationResult.Operat  import org.sufficientlysecure.keychain.operations.results.EditKeyResult;  import org.sufficientlysecure.keychain.service.SaveKeyringParcel;  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.ui.util.KeyFormattingUtils; @@ -871,63 +872,15 @@ public class PgpKeyOperation {              }              // 6. If requested, change passphrase -            if (saveParcel.mNewPassphrase != null) { +            if (saveParcel.mNewUnlock != null) {                  progress(R.string.progress_modify_passphrase, 90);                  log.add(LogType.MSG_MF_PASSPHRASE, indent);                  indent += 1; -                PGPDigestCalculator encryptorHashCalc = new JcaPGPDigestCalculatorProviderBuilder().build() -                        .get(SECRET_KEY_ENCRYPTOR_HASH_ALGO); -                PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( -                        Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray()); -                // Build key encryptor based on new passphrase -                PBESecretKeyEncryptor keyEncryptorNew = new JcePBESecretKeyEncryptorBuilder( -                        SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO, encryptorHashCalc, SECRET_KEY_ENCRYPTOR_S2K_COUNT) -                        .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build( -                                saveParcel.mNewPassphrase.toCharArray()); - -                // noinspection unchecked -                for (PGPSecretKey sKey : new IterableIterator<PGPSecretKey>(sKR.getSecretKeys())) { -                    log.add(LogType.MSG_MF_PASSPHRASE_KEY, indent, -                            KeyFormattingUtils.convertKeyIdToHex(sKey.getKeyID())); - -                    boolean ok = false; - -                    try { -                        // try to set new passphrase -                        sKey = PGPSecretKey.copyWithNewPassword(sKey, keyDecryptor, keyEncryptorNew); -                        ok = true; -                    } catch (PGPException e) { - -                        // if this is the master key, error! -                        if (sKey.getKeyID() == masterPublicKey.getKeyID()) { -                            log.add(LogType.MSG_MF_ERROR_PASSPHRASE_MASTER, indent+1); -                            return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null); -                        } - -                        // being in here means decrypt failed, likely due to a bad passphrase try -                        // again with an empty passphrase, maybe we can salvage this -                        try { -                            log.add(LogType.MSG_MF_PASSPHRASE_EMPTY_RETRY, indent+1); -                            PBESecretKeyDecryptor emptyDecryptor = -                                new JcePBESecretKeyDecryptorBuilder().setProvider( -                                    Constants.BOUNCY_CASTLE_PROVIDER_NAME).build("".toCharArray()); -                            sKey = PGPSecretKey.copyWithNewPassword(sKey, emptyDecryptor, keyEncryptorNew); -                            ok = true; -                        } catch (PGPException e2) { -                            // non-fatal but not ok, handled below -                        } -                    } - -                    if (!ok) { -                        // for a subkey, it's merely a warning -                        log.add(LogType.MSG_MF_PASSPHRASE_FAIL, indent+1, -                                KeyFormattingUtils.convertKeyIdToHex(sKey.getKeyID())); -                        continue; -                    } - -                    sKR = PGPSecretKeyRing.insertSecretKey(sKR, sKey); - +                sKR = applyNewUnlock(sKR, masterPublicKey, passphrase, saveParcel.mNewUnlock, log, indent); +                if (sKR == null) { +                    // The error has been logged above, just return a bad state +                    return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);                  }                  indent -= 1; @@ -953,6 +906,87 @@ public class PgpKeyOperation {      } +    private static PGPSecretKeyRing applyNewUnlock( +            PGPSecretKeyRing sKR, +            PGPPublicKey masterPublicKey, +            String passphrase, +            ChangeUnlockParcel newUnlock, +            OperationLog log, int indent) throws PGPException { + +        if (newUnlock.mNewPassphrase != null) { +            return applyNewPassphrase(sKR, masterPublicKey, passphrase, newUnlock.mNewPassphrase, log, indent); +        } + +        throw new UnsupportedOperationException("PIN passphrases not yet implemented!"); + +    } + + +    private static PGPSecretKeyRing applyNewPassphrase( +            PGPSecretKeyRing sKR, +            PGPPublicKey masterPublicKey, +            String passphrase, +            String newPassphrase, +            OperationLog log, int indent) throws PGPException { + +        PGPDigestCalculator encryptorHashCalc = new JcaPGPDigestCalculatorProviderBuilder().build() +                .get(SECRET_KEY_ENCRYPTOR_HASH_ALGO); +        PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( +                Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray()); +        // Build key encryptor based on new passphrase +        PBESecretKeyEncryptor keyEncryptorNew = new JcePBESecretKeyEncryptorBuilder( +                SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO, encryptorHashCalc, SECRET_KEY_ENCRYPTOR_S2K_COUNT) +                .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build( +                        newPassphrase.toCharArray()); + +        // noinspection unchecked +        for (PGPSecretKey sKey : new IterableIterator<PGPSecretKey>(sKR.getSecretKeys())) { +            log.add(LogType.MSG_MF_PASSPHRASE_KEY, indent, +                    KeyFormattingUtils.convertKeyIdToHex(sKey.getKeyID())); + +            boolean ok = false; + +            try { +                // try to set new passphrase +                sKey = PGPSecretKey.copyWithNewPassword(sKey, keyDecryptor, keyEncryptorNew); +                ok = true; +            } catch (PGPException e) { + +                // if this is the master key, error! +                if (sKey.getKeyID() == masterPublicKey.getKeyID()) { +                    log.add(LogType.MSG_MF_ERROR_PASSPHRASE_MASTER, indent+1); +                    return null; +                } + +                // being in here means decrypt failed, likely due to a bad passphrase try +                // again with an empty passphrase, maybe we can salvage this +                try { +                    log.add(LogType.MSG_MF_PASSPHRASE_EMPTY_RETRY, indent+1); +                    PBESecretKeyDecryptor emptyDecryptor = +                            new JcePBESecretKeyDecryptorBuilder().setProvider( +                                    Constants.BOUNCY_CASTLE_PROVIDER_NAME).build("".toCharArray()); +                    sKey = PGPSecretKey.copyWithNewPassword(sKey, emptyDecryptor, keyEncryptorNew); +                    ok = true; +                } catch (PGPException e2) { +                    // non-fatal but not ok, handled below +                } +            } + +            if (!ok) { +                // for a subkey, it's merely a warning +                log.add(LogType.MSG_MF_PASSPHRASE_FAIL, indent+1, +                        KeyFormattingUtils.convertKeyIdToHex(sKey.getKeyID())); +                continue; +            } + +            sKR = PGPSecretKeyRing.insertSecretKey(sKR, sKey); + +        } + +        return sKR; + +    } +      /** Update all (non-revoked) uid signatures with new flags and expiry time. */      private static PGPPublicKey updateMasterCertificates(              PGPPrivateKey masterPrivateKey, PGPPublicKey masterPublicKey, 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 8c5050fdf..9d073256b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java @@ -399,9 +399,14 @@ public class KeychainIntentService extends IntentService implements Progressable                  }                  // cache new passphrase -                if (saveParcel.mNewPassphrase != null) { -                    PassphraseCacheService.addCachedPassphrase(this, ring.getMasterKeyId(), ring.getMasterKeyId(), -                            saveParcel.mNewPassphrase, ring.getPublicKey().getPrimaryUserIdWithFallback()); +                if (saveParcel.mNewUnlock != null) { +                    PassphraseCacheService.addCachedPassphrase(this, +                            ring.getMasterKeyId(), +                            ring.getMasterKeyId(), +                            saveParcel.mNewUnlock.mNewPassphrase != null +                                    ? saveParcel.mNewUnlock.mNewPassphrase +                                    : saveParcel.mNewUnlock.mNewPin, +                            ring.getPublicKey().getPrimaryUserIdWithFallback());                  }                  setProgress(R.string.progress_done, 100, 100); 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 c4be467e4..f5d4e5bd4 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java @@ -46,7 +46,7 @@ public class SaveKeyringParcel implements Parcelable {      // the key fingerprint, for safety. MUST be null for a new key.      public byte[] mFingerprint; -    public String mNewPassphrase; +    public ChangeUnlockParcel mNewUnlock;      public ArrayList<String> mAddUserIds;      public ArrayList<SubkeyAdd> mAddSubKeys; @@ -69,7 +69,7 @@ public class SaveKeyringParcel implements Parcelable {      }      public void reset() { -        mNewPassphrase = null; +        mNewUnlock = null;          mAddUserIds = new ArrayList<String>();          mAddSubKeys = new ArrayList<SubkeyAdd>();          mChangePrimaryUserId = null; @@ -159,7 +159,7 @@ public class SaveKeyringParcel implements Parcelable {          mMasterKeyId = source.readInt() != 0 ? source.readLong() : null;          mFingerprint = source.createByteArray(); -        mNewPassphrase = source.readString(); +        mNewUnlock = source.readParcelable(getClass().getClassLoader());          mAddUserIds = source.createStringArrayList();          mAddSubKeys = (ArrayList<SubkeyAdd>) source.readSerializable(); @@ -180,7 +180,8 @@ public class SaveKeyringParcel implements Parcelable {          }          destination.writeByteArray(mFingerprint); -        destination.writeString(mNewPassphrase); +        // yes, null values are ok for parcelables +        destination.writeParcelable(mNewUnlock, 0);          destination.writeStringList(mAddUserIds);          destination.writeSerializable(mAddSubKeys); @@ -211,7 +212,7 @@ public class SaveKeyringParcel implements Parcelable {      @Override      public String toString() {          String out = "mMasterKeyId: " + mMasterKeyId + "\n"; -        out += "mNewPassphrase: " + mNewPassphrase + "\n"; +        out += "mNewUnlock: " + mNewUnlock + "\n";          out += "mAddUserIds: " + mAddUserIds + "\n";          out += "mAddSubKeys: " + mAddSubKeys + "\n";          out += "mChangeSubKeys: " + mChangeSubKeys + "\n"; @@ -238,4 +239,64 @@ public class SaveKeyringParcel implements Parcelable {          // BRAINPOOL_P256, BRAINPOOL_P384, BRAINPOOL_P512      } +    /** This subclass contains information on how the passphrase should be changed. +     * +     * If no changes are to be made, this class should NOT be used! +     * +     * At this point, there must be *exactly one* non-null value here, which specifies the type +     * of unlocking mechanism to use. +     * +     */ +    public static class ChangeUnlockParcel implements Parcelable { + +        // The new passphrase to use +        public final String mNewPassphrase; +        // A new pin to use. Must only contain [0-9]+ +        public final String mNewPin; + +        public ChangeUnlockParcel(String newPassphrase, String newPin) { +            if (newPassphrase == null && newPin == null) { +                throw new RuntimeException("Cannot set both passphrase and pin. THIS IS A BUG!"); +            } +            if (newPin != null && !newPin.matches("[0-9]+")) { +                throw new RuntimeException("Pin must be numeric digits only. THIS IS A BUG!"); +            } +            mNewPassphrase = newPassphrase; +            mNewPin = newPin; +        } + +        public ChangeUnlockParcel(Parcel source) { +            mNewPassphrase = source.readString(); +            mNewPin = source.readString(); +        } + +        @Override +        public void writeToParcel(Parcel destination, int flags) { +            destination.writeString(mNewPassphrase); +            destination.writeString(mNewPin); +        } + +        @Override +        public int describeContents() { +            return 0; +        } + +        public static final Creator<ChangeUnlockParcel> CREATOR = new Creator<ChangeUnlockParcel>() { +            public ChangeUnlockParcel createFromParcel(final Parcel source) { +                return new ChangeUnlockParcel(source); +            } + +            public ChangeUnlockParcel[] newArray(final int size) { +                return new ChangeUnlockParcel[size]; +            } +        }; + +        public String toString() { +            return mNewPassphrase != null +                    ? ("passphrase (" + mNewPassphrase + ")") +                    : ("pin (" + mNewPin + ")"); +        } + +    } +  } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java index bcc98a5d7..b48f10bbf 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java @@ -34,6 +34,7 @@ import android.widget.TextView;  import org.spongycastle.bcpg.sig.KeyFlags;  import org.sufficientlysecure.keychain.Constants;  import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel;  import org.sufficientlysecure.keychain.util.Preferences;  import org.sufficientlysecure.keychain.pgp.KeyRing;  import org.sufficientlysecure.keychain.provider.KeychainContract; @@ -165,7 +166,9 @@ public class CreateKeyFinalFragment extends Fragment {              String userId = KeyRing.createUserId(mName, mEmail, null);              mSaveKeyringParcel.mAddUserIds.add(userId);              mSaveKeyringParcel.mChangePrimaryUserId = userId; -            mSaveKeyringParcel.mNewPassphrase = mPassphrase; +            mSaveKeyringParcel.mNewUnlock = mPassphrase != null +                    ? new ChangeUnlockParcel(mPassphrase, null) +                    : null;          }      } 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 a18eb76d1..7acd62c72 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java @@ -53,6 +53,7 @@ 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.ui.adapter.SubkeysAdapter;  import org.sufficientlysecure.keychain.ui.adapter.SubkeysAddedAdapter;  import org.sufficientlysecure.keychain.ui.adapter.UserIdsAdapter; @@ -189,7 +190,9 @@ public class EditKeyFragment extends LoaderFragment implements      private void loadSaveKeyringParcel(SaveKeyringParcel saveKeyringParcel) {          mSaveKeyringParcel = saveKeyringParcel;          mPrimaryUserId = saveKeyringParcel.mChangePrimaryUserId; -        mCurrentPassphrase = saveKeyringParcel.mNewPassphrase; +        if (saveKeyringParcel.mNewUnlock != null) { +            mCurrentPassphrase = saveKeyringParcel.mNewUnlock.mNewPassphrase; +        }          mUserIdsAddedAdapter = new UserIdsAddedAdapter(getActivity(), mSaveKeyringParcel.mAddUserIds, true);          mUserIdsAddedList.setAdapter(mUserIdsAddedAdapter); @@ -387,8 +390,10 @@ public class EditKeyFragment extends LoaderFragment implements                      Bundle data = message.getData();                      // cache new returned passphrase! -                    mSaveKeyringParcel.mNewPassphrase = data -                            .getString(SetPassphraseDialogFragment.MESSAGE_NEW_PASSPHRASE); +                    mSaveKeyringParcel.mNewUnlock = new ChangeUnlockParcel( +                        data.getString(SetPassphraseDialogFragment.MESSAGE_NEW_PASSPHRASE), +                        null +                    );                  }              }          };  | 
