(WIP) Change password when key is stripped #1692
Approach: Find the first unstripped secret key and use it for passphrase verification All unstripped keys will have their passphrase changed to new passphrase, if possible. Current Progress: Changing the passphrase of keys works fine. Refactoring to combine "modifySecretKeyring" and newly added method, "modifyKeyRingPassword" may be possible if given the go-ahead.
This commit is contained in:
@@ -0,0 +1,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;
|
||||
}
|
||||
}
|
||||
@@ -539,6 +539,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_ALL_KEYS_STRIPPED (LogLevel.ERROR, R.string.msg_mf_error_all_keys_stripped),
|
||||
MSG_MF_ERROR_DIVERT_NEWSUB (LogLevel.ERROR, R.string.msg_mf_error_divert_newsub),
|
||||
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),
|
||||
@@ -552,6 +553,7 @@ public abstract class OperationResult implements Parcelable {
|
||||
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_PASSPHRASES_UNCHANGED(LogLevel.ERROR, R.string.msg_mf_error_passphrases_unchanged),
|
||||
MSG_MF_ERROR_PAST_EXPIRY(LogLevel.ERROR, R.string.msg_mf_error_past_expiry),
|
||||
MSG_MF_ERROR_PGP (LogLevel.ERROR, R.string.msg_mf_error_pgp),
|
||||
MSG_MF_ERROR_RESTRICTED(LogLevel.ERROR, R.string.msg_mf_error_restricted),
|
||||
|
||||
Reference in New Issue
Block a user