aboutsummaryrefslogtreecommitdiffstats
path: root/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/PassphraseChangeOperation.java
blob: e95f35c218d38f558057436e523923a0856294fd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
package org.sufficientlysecure.keychain.operations;

import android.content.Context;
import android.support.annotation.NonNull;

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.PgpEditKeyResult;
import org.sufficientlysecure.keychain.operations.results.SaveKeyringResult;
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey;
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing;
import org.sufficientlysecure.keychain.pgp.PgpKeyOperation;
import org.sufficientlysecure.keychain.pgp.Progressable;
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.PassphraseChangeParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.ProgressScaler;

import java.util.Iterator;

/**
 * Created by alex on 3/14/16.
 */
public class PassphraseChangeOperation extends BaseOperation<PassphraseChangeParcel> {


    public PassphraseChangeOperation(Context context, ProviderHelper providerHelper, Progressable progressable) {
        super(context, providerHelper, progressable);
    }

    /**
     * Finds the first unstripped key & uses that for passphrase verification.
     * Might bring in complications
     *
     * @param passphraseParcel  primary input to the operation
     * @param cryptoInput input that changes if user interaction is required
     * @return the result of the operation
     */
    @NonNull
    public OperationResult execute(PassphraseChangeParcel passphraseParcel, CryptoInputParcel cryptoInput) {
        OperationResult.OperationLog log = new OperationResult.OperationLog();
        log.add(OperationResult.LogType.MSG_ED, 0);

        if (passphraseParcel == null || passphraseParcel.mMasterKeyId == null) {
            log.add(OperationResult.LogType.MSG_ED_ERROR_NO_PARCEL, 1);
            return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
        }

        // Perform actual modification
        PgpEditKeyResult modifyResult;
        {
            PgpKeyOperation keyOperations =
                    new PgpKeyOperation(new ProgressScaler(mProgressable, 0, 70, 100), mCancelled);

            try {
                    log.add(OperationResult.LogType.MSG_ED_FETCHING, 1,
                            KeyFormattingUtils.convertKeyIdToHex(passphraseParcel.mMasterKeyId));

                    CanonicalizedSecretKeyRing secRing =
                            mProviderHelper.getCanonicalizedSecretKeyRing(passphraseParcel.mMasterKeyId);
                    CachedPublicKeyRing cachedRing =
                            mProviderHelper.getCachedPublicKeyRing(passphraseParcel.mMasterKeyId);

                    passphraseParcel.mValidSubkeyId = getFirstValidKeyId(secRing, cachedRing);

                    if(passphraseParcel.mValidSubkeyId == null) {
                        log.add(OperationResult.LogType.MSG_MF_ERROR_ALL_KEYS_STRIPPED, 0);
                        return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
                    }

                    modifyResult = keyOperations.modifyKeyRingPassword(secRing, cryptoInput, passphraseParcel);

                    if (modifyResult.isPending()) {
                        log.add(modifyResult, 1);
                        return new EditKeyResult(log, modifyResult);
                    }
            } catch (ProviderHelper.NotFoundException e) {
                log.add(OperationResult.LogType.MSG_ED_ERROR_KEY_NOT_FOUND, 2);
                return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
            }
        }

        log.add(modifyResult, 1);

        // Check if the action was cancelled
        if (checkCancelled()) {
            log.add(OperationResult.LogType.MSG_OPERATION_CANCELLED, 0);
            return new EditKeyResult(PgpEditKeyResult.RESULT_CANCELLED, log, null);
        }

        if (!modifyResult.success()) {
            // error is already logged by modification
            return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
        }

        // Cannot cancel from here on out!
        mProgressable.setPreventCancel();

        // It's a success, so this must be non-null now
        UncachedKeyRing ring = modifyResult.getRing();

        SaveKeyringResult saveResult = mProviderHelper
                .saveSecretKeyRing(ring, new ProgressScaler(mProgressable, 70, 95, 100));
        log.add(saveResult, 1);

        // If the save operation didn't succeed, exit here
        if (!saveResult.success()) {
            return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
        }

        updateProgress(R.string.progress_done, 100, 100);
        log.add(OperationResult.LogType.MSG_ED_SUCCESS, 0);
        return new EditKeyResult(EditKeyResult.RESULT_OK, log, ring.getMasterKeyId());

    }

    private static Long getFirstValidKeyId (CanonicalizedSecretKeyRing secRing, CachedPublicKeyRing cachedRing) {

        Iterator<CanonicalizedSecretKey> secretKeyIterator = secRing.secretKeyIterator().iterator();

        while(secretKeyIterator.hasNext()) {
            try {
                long keyId = secretKeyIterator.next().getKeyId();
                CanonicalizedSecretKey.SecretKeyType keyType = cachedRing.getSecretKeyType(keyId);
                if( keyType == CanonicalizedSecretKey.SecretKeyType.PASSPHRASE
                        || keyType == CanonicalizedSecretKey.SecretKeyType.PASSPHRASE_EMPTY) {
                    return keyId;
                }
            } catch (ProviderHelper.NotFoundException e) {
                ;
            }
        }

        return null;
    }
}