(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:
Alex Fong
2016-03-15 10:24:28 +08:00
parent 6b7a0597af
commit 525788359c
20 changed files with 346 additions and 73 deletions

View File

@@ -72,6 +72,8 @@ 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;
import org.sufficientlysecure.keychain.service.ChangeUnlockParcel;
import org.sufficientlysecure.keychain.service.PassphraseChangeParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Curve;
@@ -345,6 +347,64 @@ public class PgpKeyOperation {
}
public PgpEditKeyResult modifyKeyRingPassword(CanonicalizedSecretKeyRing wsKR,
CryptoInputParcel cryptoInput,
PassphraseChangeParcel passphraseParcel) {
OperationLog log = new OperationLog();
int indent = 0;
if (passphraseParcel.mMasterKeyId == null || passphraseParcel.mMasterKeyId != wsKR.getMasterKeyId()) {
log.add(LogType.MSG_MF_ERROR_KEYID, indent);
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
}
log.add(LogType.MSG_MF, indent,
KeyFormattingUtils.convertKeyIdToHex(wsKR.getMasterKeyId()));
indent += 1;
progress(R.string.progress_building_key, 0);
// We work on bouncycastle object level here
PGPSecretKeyRing sKR = wsKR.getRing();
PGPSecretKey masterSecretKey = sKR.getSecretKey();
PGPPublicKey masterPublicKey = masterSecretKey.getPublicKey();
// Make sure the fingerprint matches
if (passphraseParcel.mFingerprint == null || !Arrays.equals(passphraseParcel.mFingerprint,
masterSecretKey.getPublicKey().getFingerprint())) {
log.add(LogType.MSG_MF_ERROR_FINGERPRINT, indent);
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
}
if (!cryptoInput.hasPassphrase()) {
log.add(LogType.MSG_MF_REQUIRE_PASSPHRASE, indent);
return new PgpEditKeyResult(log, RequiredInputParcel.createRequiredSignPassphrase(
masterSecretKey.getKeyID(), passphraseParcel.mValidSubkeyId,
cryptoInput.getSignatureTime()), cryptoInput);
} else {
progress(R.string.progress_modify_passphrase, 70);
log.add(LogType.MSG_MF_PASSPHRASE, indent);
indent += 1;
try {
sKR = applyNewPassphrase(sKR, masterPublicKey, cryptoInput.getPassphrase(),
passphraseParcel.mNewUnlock.mNewPassphrase, log, indent);
if (sKR == null) {
// The error has been logged above, just return a bad state
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
}
} catch (PGPException e) {
throw new UnsupportedOperationException("Failed to build encryptor/decryptor!");
}
indent -= 1;
progress(R.string.progress_done, 100);
log.add(LogType.MSG_MF_SUCCESS, indent);
return new PgpEditKeyResult(OperationResult.RESULT_OK, log, new UncachedKeyRing(sKR));
}
}
/** This method introduces a list of modifications specified by a SaveKeyringParcel to a
* WrappedSecretKeyRing.
*
@@ -1223,6 +1283,7 @@ public class PgpKeyOperation {
PgpSecurityConstants.SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO, encryptorHashCalc,
PgpSecurityConstants.SECRET_KEY_ENCRYPTOR_S2K_COUNT)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(newPassphrase.getCharArray());
int keysModified = 0;
for (PGPSecretKey sKey : new IterableIterator<>(sKR.getSecretKeys())) {
log.add(LogType.MSG_MF_PASSPHRASE_KEY, indent,
@@ -1236,12 +1297,6 @@ public class PgpKeyOperation {
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 {
@@ -1264,7 +1319,12 @@ public class PgpKeyOperation {
}
sKR = PGPSecretKeyRing.insertSecretKey(sKR, sKey);
keysModified++;
}
if(keysModified == 0) {
log.add(LogType.MSG_MF_ERROR_PASSPHRASES_UNCHANGED, indent+1);
return null;
}
return sKR;