move key stripping into ChangeSubkey, support divert-to-card

This commit is contained in:
Vincent Breitmoser 2015-01-24 23:05:50 +01:00
parent 53955a8014
commit 0e0970c347
7 changed files with 59 additions and 42 deletions

View File

@ -701,7 +701,7 @@ public class PgpKeyOperationTest {
public void testSubkeyStrip() throws Exception { public void testSubkeyStrip() throws Exception {
long keyId = KeyringTestingHelper.getSubkeyId(ring, 1); long keyId = KeyringTestingHelper.getSubkeyId(ring, 1);
parcel.mStripSubKeys.add(keyId); parcel.mChangeSubKeys.add(new SubkeyChange(keyId, true, false));
applyModificationWithChecks(parcel, ring, onlyA, onlyB); applyModificationWithChecks(parcel, ring, onlyA, onlyB);
Assert.assertEquals("one extra packet in original", 1, onlyA.size()); Assert.assertEquals("one extra packet in original", 1, onlyA.size());
@ -727,7 +727,7 @@ public class PgpKeyOperationTest {
public void testMasterStrip() throws Exception { public void testMasterStrip() throws Exception {
long keyId = ring.getMasterKeyId(); long keyId = ring.getMasterKeyId();
parcel.mStripSubKeys.add(keyId); parcel.mChangeSubKeys.add(new SubkeyChange(keyId, true, false));
applyModificationWithChecks(parcel, ring, onlyA, onlyB); applyModificationWithChecks(parcel, ring, onlyA, onlyB);
Assert.assertEquals("one extra packet in original", 1, onlyA.size()); Assert.assertEquals("one extra packet in original", 1, onlyA.size());

View File

@ -20,6 +20,7 @@ package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.bcpg.CompressionAlgorithmTags; import org.spongycastle.bcpg.CompressionAlgorithmTags;
import org.spongycastle.bcpg.HashAlgorithmTags; import org.spongycastle.bcpg.HashAlgorithmTags;
import org.spongycastle.bcpg.S2K;
import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags; import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags;
import org.spongycastle.bcpg.sig.Features; import org.spongycastle.bcpg.sig.Features;
import org.spongycastle.bcpg.sig.KeyFlags; import org.spongycastle.bcpg.sig.KeyFlags;
@ -715,6 +716,24 @@ public class PgpKeyOperation {
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null); return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
} }
if (change.mDummyStrip || change.mDummyDivert) {
// IT'S DANGEROUS~
// no really, it is. this operation irrevocably removes the private key data from the key
if (change.mDummyStrip) {
sKey = PGPSecretKey.constructGnuDummyKey(sKey.getPublicKey(),
S2K.GNU_PROTECTION_MODE_NO_PRIVATE_KEY);
} else {
sKey = PGPSecretKey.constructGnuDummyKey(sKey.getPublicKey(),
S2K.GNU_PROTECTION_MODE_DIVERT_TO_CARD);
}
sKR = PGPSecretKeyRing.insertSecretKey(sKR, sKey);
}
// This doesn't concern us any further
if (change.mExpiry == null && change.mFlags == null) {
continue;
}
// expiry must not be in the past // expiry must not be in the past
if (change.mExpiry != null && change.mExpiry != 0 && if (change.mExpiry != null && change.mExpiry != 0 &&
new Date(change.mExpiry*1000).before(new Date())) { new Date(change.mExpiry*1000).before(new Date())) {
@ -805,30 +824,6 @@ public class PgpKeyOperation {
} }
subProgressPop(); subProgressPop();
// 4c. For each subkey to be stripped... do so
subProgressPush(65, 70);
for (int i = 0; i < saveParcel.mStripSubKeys.size(); i++) {
progress(R.string.progress_modify_subkeystrip, (i-1) * (100 / saveParcel.mStripSubKeys.size()));
long strip = saveParcel.mStripSubKeys.get(i);
log.add(LogType.MSG_MF_SUBKEY_STRIP,
indent, KeyFormattingUtils.convertKeyIdToHex(strip));
PGPSecretKey sKey = sKR.getSecretKey(strip);
if (sKey == null) {
log.add(LogType.MSG_MF_ERROR_SUBKEY_MISSING,
indent+1, KeyFormattingUtils.convertKeyIdToHex(strip));
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
}
// IT'S DANGEROUS~
// no really, it is. this operation irrevocably removes the private key data from the key
sKey = PGPSecretKey.constructGnuDummyKey(sKey.getPublicKey());
sKR = PGPSecretKeyRing.insertSecretKey(sKR, sKey);
}
subProgressPop();
// 5. Generate and add new subkeys // 5. Generate and add new subkeys
subProgressPush(70, 90); subProgressPush(70, 90);
for (int i = 0; i < saveParcel.mAddSubKeys.size(); i++) { for (int i = 0; i < saveParcel.mAddSubKeys.size(); i++) {

View File

@ -20,6 +20,7 @@ package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.bcpg.ArmoredOutputStream; import org.spongycastle.bcpg.ArmoredOutputStream;
import org.spongycastle.bcpg.PublicKeyAlgorithmTags; import org.spongycastle.bcpg.PublicKeyAlgorithmTags;
import org.spongycastle.bcpg.S2K;
import org.spongycastle.bcpg.SignatureSubpacketTags; import org.spongycastle.bcpg.SignatureSubpacketTags;
import org.spongycastle.bcpg.UserAttributeSubpacketTags; import org.spongycastle.bcpg.UserAttributeSubpacketTags;
import org.spongycastle.bcpg.sig.KeyFlags; import org.spongycastle.bcpg.sig.KeyFlags;
@ -1221,7 +1222,8 @@ public class UncachedKeyRing {
// if this is a secret key which does not yet occur in the secret ring // if this is a secret key which does not yet occur in the secret ring
if (sKey == null) { if (sKey == null) {
// generate a stripped secret (sub)key // generate a stripped secret (sub)key
sKey = PGPSecretKey.constructGnuDummyKey(key); sKey = PGPSecretKey.constructGnuDummyKey(key,
S2K.GNU_PROTECTION_MODE_NO_PRIVATE_KEY);
} }
sKey = PGPSecretKey.replacePublicKey(sKey, key); sKey = PGPSecretKey.replacePublicKey(sKey, key);
return PGPSecretKeyRing.insertSecretKey(secRing, sKey); return PGPSecretKeyRing.insertSecretKey(secRing, sKey);

View File

@ -58,7 +58,6 @@ public class SaveKeyringParcel implements Parcelable {
public ArrayList<String> mRevokeUserIds; public ArrayList<String> mRevokeUserIds;
public ArrayList<Long> mRevokeSubKeys; public ArrayList<Long> mRevokeSubKeys;
public ArrayList<Long> mStripSubKeys;
public SaveKeyringParcel() { public SaveKeyringParcel() {
reset(); reset();
@ -79,7 +78,6 @@ public class SaveKeyringParcel implements Parcelable {
mChangeSubKeys = new ArrayList<SubkeyChange>(); mChangeSubKeys = new ArrayList<SubkeyChange>();
mRevokeUserIds = new ArrayList<String>(); mRevokeUserIds = new ArrayList<String>();
mRevokeSubKeys = new ArrayList<Long>(); mRevokeSubKeys = new ArrayList<Long>();
mStripSubKeys = new ArrayList<Long>();
} }
// performance gain for using Parcelable here would probably be negligible, // performance gain for using Parcelable here would probably be negligible,
@ -112,10 +110,14 @@ public class SaveKeyringParcel implements Parcelable {
} }
public static class SubkeyChange implements Serializable { public static class SubkeyChange implements Serializable {
public long mKeyId; public final long mKeyId;
public Integer mFlags; public Integer mFlags;
// this is a long unix timestamp, in seconds (NOT MILLISECONDS!) // this is a long unix timestamp, in seconds (NOT MILLISECONDS!)
public Long mExpiry; public Long mExpiry;
// if this flag is true, the subkey should be changed to a stripped key
public boolean mDummyStrip;
// if this flag is true, the subkey should be changed to a divert-to-card key
public boolean mDummyDivert;
public SubkeyChange(long keyId) { public SubkeyChange(long keyId) {
mKeyId = keyId; mKeyId = keyId;
@ -127,11 +129,25 @@ public class SaveKeyringParcel implements Parcelable {
mExpiry = expiry; mExpiry = expiry;
} }
public SubkeyChange(long keyId, boolean dummyStrip, boolean dummyDivert) {
this(keyId, null, null);
// these flags are mutually exclusive!
if (dummyStrip && dummyDivert) {
throw new AssertionError(
"cannot set strip and divert flags at the same time - this is a bug!");
}
mDummyStrip = dummyStrip;
mDummyDivert = dummyDivert;
}
@Override @Override
public String toString() { public String toString() {
String out = "mKeyId: " + mKeyId + ", "; String out = "mKeyId: " + mKeyId + ", ";
out += "mFlags: " + mFlags + ", "; out += "mFlags: " + mFlags + ", ";
out += "mExpiry: " + mExpiry; out += "mExpiry: " + mExpiry + ", ";
out += "mDummyStrip: " + mDummyStrip + ", ";
out += "mDummyDivert: " + mDummyDivert;
return out; return out;
} }
@ -173,7 +189,6 @@ public class SaveKeyringParcel implements Parcelable {
mRevokeUserIds = source.createStringArrayList(); mRevokeUserIds = source.createStringArrayList();
mRevokeSubKeys = (ArrayList<Long>) source.readSerializable(); mRevokeSubKeys = (ArrayList<Long>) source.readSerializable();
mStripSubKeys = (ArrayList<Long>) source.readSerializable();
} }
@Override @Override
@ -196,7 +211,6 @@ public class SaveKeyringParcel implements Parcelable {
destination.writeStringList(mRevokeUserIds); destination.writeStringList(mRevokeUserIds);
destination.writeSerializable(mRevokeSubKeys); destination.writeSerializable(mRevokeSubKeys);
destination.writeSerializable(mStripSubKeys);
} }
public static final Creator<SaveKeyringParcel> CREATOR = new Creator<SaveKeyringParcel>() { public static final Creator<SaveKeyringParcel> CREATOR = new Creator<SaveKeyringParcel>() {
@ -224,8 +238,7 @@ public class SaveKeyringParcel implements Parcelable {
out += "mChangeSubKeys: " + mChangeSubKeys + "\n"; out += "mChangeSubKeys: " + mChangeSubKeys + "\n";
out += "mChangePrimaryUserId: " + mChangePrimaryUserId + "\n"; out += "mChangePrimaryUserId: " + mChangePrimaryUserId + "\n";
out += "mRevokeUserIds: " + mRevokeUserIds + "\n"; out += "mRevokeUserIds: " + mRevokeUserIds + "\n";
out += "mRevokeSubKeys: " + mRevokeSubKeys + "\n"; out += "mRevokeSubKeys: " + mRevokeSubKeys;
out += "mStripSubKeys: " + mStripSubKeys;
return out; return out;
} }

View File

@ -55,6 +55,7 @@ import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
import org.sufficientlysecure.keychain.service.PassphraseCacheService; import org.sufficientlysecure.keychain.service.PassphraseCacheService;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel; import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel; import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyChange;
import org.sufficientlysecure.keychain.ui.adapter.SubkeysAdapter; import org.sufficientlysecure.keychain.ui.adapter.SubkeysAdapter;
import org.sufficientlysecure.keychain.ui.adapter.SubkeysAddedAdapter; import org.sufficientlysecure.keychain.ui.adapter.SubkeysAddedAdapter;
import org.sufficientlysecure.keychain.ui.adapter.UserIdsAdapter; import org.sufficientlysecure.keychain.ui.adapter.UserIdsAdapter;
@ -478,12 +479,13 @@ public class EditKeyFragment extends LoaderFragment implements
} }
break; break;
case EditSubkeyDialogFragment.MESSAGE_STRIP: case EditSubkeyDialogFragment.MESSAGE_STRIP:
// toggle SubkeyChange change = mSaveKeyringParcel.getSubkeyChange(keyId);
if (mSaveKeyringParcel.mStripSubKeys.contains(keyId)) { if (change == null) {
mSaveKeyringParcel.mStripSubKeys.remove(keyId); mSaveKeyringParcel.mChangeSubKeys.add(new SubkeyChange(keyId, true, false));
} else { break;
mSaveKeyringParcel.mStripSubKeys.add(keyId);
} }
// toggle
change.mDummyStrip = !change.mDummyStrip;
break; break;
} }
getLoaderManager().getLoader(LOADER_ID_SUBKEYS).forceLoad(); getLoaderManager().getLoader(LOADER_ID_SUBKEYS).forceLoad();

View File

@ -35,6 +35,7 @@ import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyChange;
import org.sufficientlysecure.keychain.ui.util.FormattingUtils; import org.sufficientlysecure.keychain.ui.util.FormattingUtils;
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType; import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
@ -160,7 +161,11 @@ public class SubkeysAdapter extends CursorAdapter {
cursor.getString(INDEX_KEY_CURVE_OID) cursor.getString(INDEX_KEY_CURVE_OID)
)); ));
if (mSaveKeyringParcel != null && mSaveKeyringParcel.mStripSubKeys.contains(keyId)) { SubkeyChange change = mSaveKeyringParcel != null
? mSaveKeyringParcel.getSubkeyChange(keyId)
: null;
if (change.mDummyStrip) {
algorithmStr.append(", "); algorithmStr.append(", ");
final SpannableString boldStripped = new SpannableString( final SpannableString boldStripped = new SpannableString(
context.getString(R.string.key_stripped) context.getString(R.string.key_stripped)

2
extern/spongycastle vendored

@ -1 +1 @@
Subproject commit f13d9c202f7470ede75f2c02cc8c578acd3acee9 Subproject commit c2a91166cef1a08c97ad2e988e368fe36babfc05