new-edit: add logging to modifySecretKeyRing operation

This commit is contained in:
Vincent Breitmoser 2014-06-17 20:40:26 +02:00
parent 5c47143d64
commit 4bff50bffc
4 changed files with 164 additions and 78 deletions

View File

@ -47,6 +47,9 @@ import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyEncryptorBuilder;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralMsgIdException;
import org.sufficientlysecure.keychain.service.OperationResultParcel.LogLevel;
import org.sufficientlysecure.keychain.service.OperationResultParcel.LogType;
import org.sufficientlysecure.keychain.service.OperationResultParcel.OperationLog;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.Primes;
@ -98,12 +101,6 @@ public class PgpKeyOperation {
}
}
void updateProgress(int current, int total) {
if (mProgress != null) {
mProgress.setProgress(current, total);
}
}
/** Creates new secret key. */
private PGPSecretKey createKey(int algorithmChoice, int keySize, String passphrase,
boolean isMasterKey) throws PgpGeneralMsgIdException {
@ -182,7 +179,9 @@ public class PgpKeyOperation {
}
/** This method introduces a list of modifications specified by a SaveKeyringParcel to a
* PGPSecretKeyRing.
* WrappedSecretKeyRing.
*
* This method relies on WrappedSecretKeyRing's canonicalization property!
*
* Note that PGPPublicKeyRings can not be directly modified. Instead, the corresponding
* PGPSecretKeyRing must be modified and consequently consolidated with its public counterpart.
@ -191,8 +190,7 @@ public class PgpKeyOperation {
*
*/
public UncachedKeyRing modifySecretKeyRing(WrappedSecretKeyRing wsKR, SaveKeyringParcel saveParcel,
String passphrase)
throws PgpGeneralMsgIdException, PGPException, SignatureException, IOException {
String passphrase, OperationLog log, int indent) {
/*
* 1. Unlock private key
@ -205,6 +203,8 @@ public class PgpKeyOperation {
* 6. If requested, change passphrase
*/
log.add(LogLevel.START, LogType.MSG_MR, indent);
indent += 1;
updateProgress(R.string.progress_building_key, 0, 100);
// We work on bouncycastle object level here
@ -213,10 +213,16 @@ public class PgpKeyOperation {
PGPSecretKey masterSecretKey = sKR.getSecretKey();
// 1. Unlock private key
log.add(LogLevel.DEBUG, LogType.MSG_MR_UNLOCK, indent);
PGPPrivateKey masterPrivateKey; {
try {
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
masterPrivateKey = masterSecretKey.extractPrivateKey(keyDecryptor);
} catch (PGPException e) {
log.add(LogLevel.ERROR, LogType.MSG_MR_UNLOCK_ERROR, indent+1);
return null;
}
}
if (!Arrays.equals(saveParcel.mFingerprint, sKR.getPublicKey().getFingerprint())) {
return null;
@ -224,12 +230,14 @@ public class PgpKeyOperation {
updateProgress(R.string.progress_certifying_master_key, 20, 100);
{ // work on master secret key
// work on master secret key
try {
PGPPublicKey modifiedPublicKey = masterPublicKey;
// 2a. Add certificates for new user ids
for (String userId : saveParcel.addUserIds) {
log.add(LogLevel.INFO, LogType.MSG_MR_UID_ADD, indent);
PGPSignature cert = generateUserIdSignature(masterPrivateKey,
masterPublicKey, userId, false);
modifiedPublicKey = PGPPublicKey.addCertification(masterPublicKey, userId, cert);
@ -237,6 +245,7 @@ public class PgpKeyOperation {
// 2b. Add revocations for revoked user ids
for (String userId : saveParcel.revokeUserIds) {
log.add(LogLevel.INFO, LogType.MSG_MR_UID_REVOKE, indent);
PGPSignature cert = generateRevocationSignature(masterPrivateKey,
masterPublicKey, userId);
modifiedPublicKey = PGPPublicKey.addCertification(masterPublicKey, userId, cert);
@ -244,6 +253,7 @@ public class PgpKeyOperation {
// 3. If primary user id changed, generate new certificates for both old and new
if (saveParcel.changePrimaryUserId != null) {
log.add(LogLevel.INFO, LogType.MSG_MR_UID_PRIMARY, indent);
// todo
}
@ -254,17 +264,25 @@ public class PgpKeyOperation {
sKR = PGPSecretKeyRing.insertSecretKey(sKR, masterSecretKey);
}
}
// 4a. For each subkey change, generate new subkey binding certificate
for (SaveKeyringParcel.SubkeyChange change : saveParcel.changeSubKeys) {
log.add(LogLevel.INFO, LogType.MSG_MR_SUBKEY_CHANGE,
new String[]{PgpKeyHelper.convertKeyIdToHex(change.mKeyId)}, indent);
PGPSecretKey sKey = sKR.getSecretKey(change.mKeyId);
if (sKey == null) {
log.add(LogLevel.ERROR, LogType.MSG_MR_SUBKEY_MISSING,
new String[]{PgpKeyHelper.convertKeyIdToHex(change.mKeyId)}, indent + 1);
return null;
}
PGPPublicKey pKey = sKey.getPublicKey();
if (change.mExpiry != null && new Date(change.mExpiry).before(new Date())) {
log.add(LogLevel.ERROR, LogType.MSG_MR_SUBKEY_PAST_EXPIRY,
new String[]{PgpKeyHelper.convertKeyIdToHex(change.mKeyId)}, indent + 1);
return null;
}
// generate and add new signature
PGPSignature sig = generateSubkeyBindingSignature(masterPublicKey, masterPrivateKey,
sKey, pKey, change.mFlags, change.mExpiry, passphrase);
@ -272,10 +290,14 @@ public class PgpKeyOperation {
sKR = PGPSecretKeyRing.insertSecretKey(sKR, PGPSecretKey.replacePublicKey(sKey, pKey));
}
// 4b. For each subkey change, generate new subkey binding certificate
// 4b. For each subkey revocation, generate new subkey revocation certificate
for (long revocation : saveParcel.revokeSubKeys) {
log.add(LogLevel.INFO, LogType.MSG_MR_SUBKEY_REVOKE,
new String[] { PgpKeyHelper.convertKeyIdToHex(revocation) }, indent);
PGPSecretKey sKey = sKR.getSecretKey(revocation);
if (sKey == null) {
log.add(LogLevel.ERROR, LogType.MSG_MR_SUBKEY_MISSING,
new String[] { PgpKeyHelper.convertKeyIdToHex(revocation) }, indent+1);
return null;
}
PGPPublicKey pKey = sKey.getPublicKey();
@ -290,11 +312,20 @@ public class PgpKeyOperation {
// 5. Generate and add new subkeys
for (SaveKeyringParcel.SubkeyAdd add : saveParcel.addSubKeys) {
try {
if (add.mExpiry != null && new Date(add.mExpiry).before(new Date())) {
log.add(LogLevel.ERROR, LogType.MSG_MR_SUBKEY_PAST_EXPIRY, indent +1);
return null;
}
log.add(LogLevel.INFO, LogType.MSG_MR_SUBKEY_NEW, indent);
PGPSecretKey sKey = createKey(add.mAlgorithm, add.mKeysize, passphrase, false);
log.add(LogLevel.DEBUG, LogType.MSG_MR_SUBKEY_NEW_ID,
new String[] { PgpKeyHelper.convertKeyIdToHex(sKey.getKeyID()) }, indent+1);
PGPPublicKey pKey = sKey.getPublicKey();
PGPSignature cert = generateSubkeyBindingSignature(masterPublicKey, masterPrivateKey,
sKey, pKey, add.mFlags, add.mExpiry, passphrase);
pKey = PGPPublicKey.addCertification(pKey, cert);
sKey = PGPSecretKey.replacePublicKey(sKey, pKey);
sKR = PGPSecretKeyRing.insertSecretKey(sKR, PGPSecretKey.replacePublicKey(sKey, pKey));
@ -305,6 +336,7 @@ public class PgpKeyOperation {
// 6. If requested, change passphrase
if (saveParcel.newPassphrase != null) {
log.add(LogLevel.INFO, LogType.MSG_MR_PASSPHRASE, indent);
PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder().build()
.get(HashAlgorithmTags.SHA1);
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
@ -318,6 +350,19 @@ public class PgpKeyOperation {
sKR = PGPSecretKeyRing.copyWithNewPassword(sKR, keyDecryptor, keyEncryptorNew);
}
// This one must only be thrown by
} catch (IOException e) {
log.add(LogLevel.ERROR, LogType.MSG_MR_ERROR_ENCODE, indent+1);
return null;
} catch (PGPException e) {
log.add(LogLevel.ERROR, LogType.MSG_MR_ERROR_PGP, indent+1);
return null;
} catch (SignatureException e) {
log.add(LogLevel.ERROR, LogType.MSG_MR_ERROR_SIG, indent+1);
return null;
}
log.add(LogLevel.OK, LogType.MSG_MR_SUCCESS, indent);
return new UncachedKeyRing(sKR);
}
@ -377,7 +422,7 @@ public class PgpKeyOperation {
private static PGPSignature generateSubkeyBindingSignature(
PGPPublicKey masterPublicKey, PGPPrivateKey masterPrivateKey,
PGPSecretKey sKey, PGPPublicKey pKey, int flags, Long expiry, String passphrase)
throws PgpGeneralMsgIdException, IOException, PGPException, SignatureException {
throws IOException, PGPException, SignatureException {
// date for signing
Date todayDate = new Date();
@ -414,13 +459,10 @@ public class PgpKeyOperation {
if (expiry != null) {
Calendar creationDate = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
creationDate.setTime(pKey.getCreationTime());
// note that the below, (a/c) - (b/c) is *not* the same as (a - b) /c
// here we purposefully ignore partial days in each date - long type has
// no fractional part!
long numDays = (expiry / 86400000) -
(creationDate.getTimeInMillis() / 86400000);
if (numDays <= 0) {
throw new PgpGeneralMsgIdException(R.string.error_expiry_must_come_after_creation);
// (Just making sure there's no programming error here, this MUST have been checked above!)
if (new Date(expiry).before(todayDate)) {
throw new RuntimeException("Bad subkey creation date, this is a bug!");
}
hashedPacketsGen.setKeyExpirationTime(false, expiry - creationDate.getTimeInMillis());
} else {

View File

@ -52,6 +52,7 @@ import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralMsgIdException;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainDatabase;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.OperationResultParcel.OperationLog;
import org.sufficientlysecure.keychain.util.InputData;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.ProgressScaler;
@ -486,7 +487,9 @@ public class KeychainIntentService extends IntentService
String passphrase = data.getString(SAVE_KEYRING_PASSPHRASE);
WrappedSecretKeyRing secRing = providerHelper.getWrappedSecretKeyRing(masterKeyId);
UncachedKeyRing ring = keyOperations.modifySecretKeyRing(secRing, saveParcel, passphrase);
OperationLog log = new OperationLog();
UncachedKeyRing ring = keyOperations.modifySecretKeyRing(secRing, saveParcel,
passphrase, log, 0);
setProgress(R.string.progress_saving_key_ring, 90, 100);
providerHelper.saveSecretKeyRing(ring);
} catch (ProviderHelper.NotFoundException e) {

View File

@ -215,6 +215,24 @@ public class OperationResultParcel implements Parcelable {
MSG_KC_UID_NO_CERT (R.string.msg_kc_uid_no_cert),
MSG_KC_UID_REVOKE_DUP (R.string.msg_kc_uid_revoke_dup),
MSG_KC_UID_REVOKE_OLD (R.string.msg_kc_uid_revoke_old),
MSG_MR (R.string.msg_mr),
MSG_MR_ERROR_ENCODE (R.string.msg_mr_error_encode),
MSG_MR_ERROR_PGP (R.string.msg_mr_error_pgp),
MSG_MR_ERROR_SIG (R.string.msg_mr_error_sig),
MSG_MR_PASSPHRASE (R.string.msg_mr_passphrase),
MSG_MR_SUBKEY_CHANGE (R.string.msg_mr_subkey_change),
MSG_MR_SUBKEY_MISSING (R.string.msg_mr_subkey_missing),
MSG_MR_SUBKEY_NEW_ID (R.string.msg_mr_subkey_new_id),
MSG_MR_SUBKEY_NEW (R.string.msg_mr_subkey_new),
MSG_MR_SUBKEY_PAST_EXPIRY (R.string.msg_mr_subkey_past_expiry),
MSG_MR_SUBKEY_REVOKE (R.string.msg_mr_subkey_revoke),
MSG_MR_SUCCESS (R.string.msg_mr_success),
MSG_MR_UID_ADD (R.string.msg_mr_uid_add),
MSG_MR_UID_PRIMARY (R.string.msg_mr_uid_primary),
MSG_MR_UID_REVOKE (R.string.msg_mr_uid_revoke),
MSG_MR_UNLOCK_ERROR (R.string.msg_mr_unlock_error),
MSG_MR_UNLOCK (R.string.msg_mr_unlock),
;
private final int mMsgId;
@ -264,6 +282,10 @@ public class OperationResultParcel implements Parcelable {
add(new OperationResultParcel.LogEntryParcel(level, type, parameters, indent));
}
public void add(LogLevel level, LogType type, int indent) {
add(new OperationResultParcel.LogEntryParcel(level, type, null, indent));
}
public boolean containsWarnings() {
for(LogEntryParcel entry : new IterableIterator<LogEntryParcel>(iterator())) {
if (entry.mLevel == LogLevel.WARN || entry.mLevel == LogLevel.ERROR) {

View File

@ -600,6 +600,25 @@
<string name="msg_kc_uid_revoke_old">Removing outdated revocation certificate for user id "%s"</string>
<string name="msg_kc_uid_no_cert">No valid self-certificate found for user id %s, removing from ring</string>
<!-- modifySecretKeyRing -->
<string name="msg_mr">Modifying keyring %s</string>
<string name="msg_mr_error_encode">Encoding exception!</string>
<string name="msg_mr_error_pgp">PGP internal exception!</string>
<string name="msg_mr_error_sig">Signature exception!</string>
<string name="msg_mr_passphrase">Changing passphrase</string>
<string name="msg_mr_subkey_change">Modifying subkey %s</string>
<string name="msg_mr_subkey_missing">Tried to operate on missing subkey %s!</string>
<string name="msg_mr_subkey_new">Generating new %1$s bit %2$s subkey</string>
<string name="msg_mr_subkey_new_id">New subkey id: %s</string>
<string name="msg_mr_subkey_past_expiry">Expiry date cannot be in the past!</string>
<string name="msg_mr_subkey_revoke">Revoking subkey %s</string>
<string name="msg_mr_success">Keyring successfully modified</string>
<string name="msg_mr_uid_add">Adding user id %s</string>
<string name="msg_mr_uid_primary">Changing primary uid to %s</string>
<string name="msg_mr_uid_revoke">Revoking user id %s</string>
<string name="msg_mr_unlock_error">Error unlocking keyring!</string>
<string name="msg_mr_unlock">Unlocking keyring</string>
<!-- unsorted -->
<string name="section_certifier_id">Certifier</string>
<string name="section_cert">Certificate Details</string>