aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVincent Breitmoser <valodim@mugenguild.com>2014-06-17 20:04:25 +0200
committerVincent Breitmoser <valodim@mugenguild.com>2014-06-17 20:11:54 +0200
commit5c47143d64e8ab2e1ef00f5134d9362c08e6f529 (patch)
tree598ed4f40d23fe6b0fc4b3d4b681767c6d6f2aa1
parenta0f546739d518a924770540795bca71bfacd4b1d (diff)
downloadopen-keychain-5c47143d64e8ab2e1ef00f5134d9362c08e6f529.tar.gz
open-keychain-5c47143d64e8ab2e1ef00f5134d9362c08e6f529.tar.bz2
open-keychain-5c47143d64e8ab2e1ef00f5134d9362c08e6f529.zip
new-edit: add new save keyring stuff
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java264
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/PgpGeneralMsgIdException.java5
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java6
3 files changed, 124 insertions, 151 deletions
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
index e9d8fd295..e08c96dad 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
@@ -171,173 +171,139 @@ public class PgpKeyOperation {
return new PGPSecretKey(keyPair.getPrivateKey(), keyPair.getPublicKey(),
sha1Calc, isMasterKey, keyEncryptor);
} catch(NoSuchProviderException e) {
- throw new PgpGeneralMsgIdException(R.string.error_encoding, e);
+ throw new RuntimeException(e);
} catch(NoSuchAlgorithmException e) {
- throw new PgpGeneralMsgIdException(R.string.error_encoding, e);
+ throw new RuntimeException(e);
} catch(InvalidAlgorithmParameterException e) {
- throw new PgpGeneralMsgIdException(R.string.error_encoding, e);
+ throw new RuntimeException(e);
} catch(PGPException e) {
- throw new PgpGeneralMsgIdException(R.string.error_encoding, e);
+ throw new PgpGeneralMsgIdException(R.string.msg_mr_error_pgp, e);
}
}
- public Pair<PGPSecretKeyRing, PGPPublicKeyRing> buildSecretKey(PGPSecretKeyRing sKR,
- PGPPublicKeyRing pKR,
- SaveKeyringParcel saveParcel,
- String passphrase)
+ /** This method introduces a list of modifications specified by a SaveKeyringParcel to a
+ * PGPSecretKeyRing.
+ *
+ * Note that PGPPublicKeyRings can not be directly modified. Instead, the corresponding
+ * PGPSecretKeyRing must be modified and consequently consolidated with its public counterpart.
+ * This is a natural workflow since pgp keyrings are immutable data structures: Old semantics
+ * are changed by adding new certificates, which implicitly override older certificates.
+ *
+ */
+ public UncachedKeyRing modifySecretKeyRing(WrappedSecretKeyRing wsKR, SaveKeyringParcel saveParcel,
+ String passphrase)
throws PgpGeneralMsgIdException, PGPException, SignatureException, IOException {
- updateProgress(R.string.progress_building_key, 0, 100);
-
- // sort these, so we can use binarySearch later on
- Arrays.sort(saveParcel.revokeSubKeys);
- Arrays.sort(saveParcel.revokeUserIds);
-
/*
- * What's gonna happen here:
- *
* 1. Unlock private key
- *
- * 2. Create new secret key ring
- *
- * 3. Copy subkeys
- * - Generate revocation if requested
- * - Copy old cert, or generate new if change requested
- *
- * 4. Generate and add new subkeys
- *
- * 5. Copy user ids
- * - Generate revocation if requested
- * - Copy old cert, or generate new if primary user id status changed
- *
- * 6. Add new user ids
- *
- * 7. Generate PublicKeyRing from SecretKeyRing
- *
- * 8. Return pair (PublicKeyRing,SecretKeyRing)
- *
+ * 2a. Add certificates for new user ids
+ * 2b. Add revocations for revoked user ids
+ * 3. If primary user id changed, generate new certificates for both old and new
+ * 4a. For each subkey change, generate new subkey binding certificate
+ * 4b. For each subkey revocation, generate new subkey revocation certificate
+ * 5. Generate and add new subkeys
+ * 6. If requested, change passphrase
*/
- // 1. Unlock private key
updateProgress(R.string.progress_building_key, 0, 100);
+ // We work on bouncycastle object level here
+ PGPSecretKeyRing sKR = wsKR.getRing();
PGPPublicKey masterPublicKey = sKR.getPublicKey();
+ PGPSecretKey masterSecretKey = sKR.getSecretKey();
+
+ // 1. Unlock private key
PGPPrivateKey masterPrivateKey; {
- PGPSecretKey masterKey = sKR.getSecretKey();
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
- masterPrivateKey = masterKey.extractPrivateKey(keyDecryptor);
+ masterPrivateKey = masterSecretKey.extractPrivateKey(keyDecryptor);
+ }
+ if (!Arrays.equals(saveParcel.mFingerprint, sKR.getPublicKey().getFingerprint())) {
+ return null;
}
- // 2. Create new secret key ring
updateProgress(R.string.progress_certifying_master_key, 20, 100);
- // Note we do NOT use PGPKeyRingGeneraor, it's just one level too high and does stuff
- // we want to do manually. Instead, we simply use a list of secret keys.
- ArrayList<PGPSecretKey> secretKeys = new ArrayList<PGPSecretKey>();
- ArrayList<PGPPublicKey> publicKeys = new ArrayList<PGPPublicKey>();
+ { // work on master secret key
- // 3. Copy subkeys
- // - Generate revocation if requested
- // - Copy old cert, or generate new if change requested
- for (PGPSecretKey sKey : new IterableIterator<PGPSecretKey>(sKR.getSecretKeys())) {
- PGPPublicKey pKey = sKey.getPublicKey();
- if (Arrays.binarySearch(saveParcel.revokeSubKeys, sKey.getKeyID()) >= 0) {
- // add revocation signature to key, if there is none yet
- if (!pKey.getSignaturesOfType(PGPSignature.SUBKEY_REVOCATION).hasNext()) {
- // generate revocation signature
- }
- }
- if (saveParcel.changeSubKeys.containsKey(sKey.getKeyID())) {
- // change subkey flags?
- SaveKeyringParcel.SubkeyChange change = saveParcel.changeSubKeys.get(sKey.getKeyID());
- // remove old subkey binding signature(s?)
- for (PGPSignature sig : new IterableIterator<PGPSignature>(
- pKey.getSignaturesOfType(PGPSignature.SUBKEY_BINDING))) {
- pKey = PGPPublicKey.removeCertification(pKey, sig);
- }
+ PGPPublicKey modifiedPublicKey = masterPublicKey;
- // generate and add new signature
- PGPSignature sig = generateSubkeyBindingSignature(masterPublicKey, masterPrivateKey,
- sKey, pKey, change.mFlags, change.mExpiry, passphrase);
- pKey = PGPPublicKey.addCertification(pKey, sig);
+ // 2a. Add certificates for new user ids
+ for (String userId : saveParcel.addUserIds) {
+ PGPSignature cert = generateUserIdSignature(masterPrivateKey,
+ masterPublicKey, userId, false);
+ modifiedPublicKey = PGPPublicKey.addCertification(masterPublicKey, userId, cert);
}
- secretKeys.add(PGPSecretKey.replacePublicKey(sKey, pKey));
- publicKeys.add(pKey);
- }
- // 4. Generate and add new subkeys
- // TODO
-
- // 5. Copy user ids
- for (String userId : new IterableIterator<String>(masterPublicKey.getUserIDs())) {
- // - Copy old cert, or generate new if primary user id status changed
- boolean certified = false, revoked = false;
- for (PGPSignature sig : new IterableIterator<PGPSignature>(
- masterPublicKey.getSignaturesForID(userId))) {
- // We know there are only revocation and certification types in here.
- switch(sig.getSignatureType()) {
- case PGPSignature.CERTIFICATION_REVOCATION:
- revoked = true;
- continue;
-
- case PGPSignature.DEFAULT_CERTIFICATION:
- case PGPSignature.NO_CERTIFICATION:
- case PGPSignature.CASUAL_CERTIFICATION:
- case PGPSignature.POSITIVE_CERTIFICATION:
- // Already got one? Remove this one, then.
- if (certified) {
- masterPublicKey = PGPPublicKey.removeCertification(
- masterPublicKey, userId, sig);
- continue;
- }
- boolean primary = userId.equals(saveParcel.changePrimaryUserId);
- // Generate a new one under certain circumstances
- if (saveParcel.changePrimaryUserId != null &&
- sig.getHashedSubPackets().isPrimaryUserID() != primary) {
- PGPSignature cert = generateUserIdSignature(
- masterPrivateKey, masterPublicKey, userId, primary);
- PGPPublicKey.addCertification(masterPublicKey, userId, cert);
- }
- certified = true;
- }
- }
- // - Generate revocation if requested
- if (!revoked && Arrays.binarySearch(saveParcel.revokeUserIds, userId) >= 0) {
+ // 2b. Add revocations for revoked user ids
+ for (String userId : saveParcel.revokeUserIds) {
PGPSignature cert = generateRevocationSignature(masterPrivateKey,
masterPublicKey, userId);
- masterPublicKey = PGPPublicKey.addCertification(masterPublicKey, userId, cert);
+ modifiedPublicKey = PGPPublicKey.addCertification(masterPublicKey, userId, cert);
+ }
+
+ // 3. If primary user id changed, generate new certificates for both old and new
+ if (saveParcel.changePrimaryUserId != null) {
+ // todo
+ }
+
+ // Update the secret key ring
+ if (modifiedPublicKey != masterPublicKey) {
+ masterSecretKey = PGPSecretKey.replacePublicKey(masterSecretKey, modifiedPublicKey);
+ masterPublicKey = modifiedPublicKey;
+ sKR = PGPSecretKeyRing.insertSecretKey(sKR, masterSecretKey);
}
- }
- // 6. Add new user ids
- for(String userId : saveParcel.addUserIds) {
- PGPSignature cert = generateUserIdSignature(masterPrivateKey,
- masterPublicKey, userId, userId.equals(saveParcel.changePrimaryUserId));
- masterPublicKey = PGPPublicKey.addCertification(masterPublicKey, userId, cert);
}
- // 7. Generate PublicKeyRing from SecretKeyRing
- updateProgress(R.string.progress_building_master_key, 30, 100);
- PGPSecretKeyRing ring = new PGPSecretKeyRing(secretKeys);
- // Copy all non-self uid certificates
- for (String userId : new IterableIterator<String>(masterPublicKey.getUserIDs())) {
- // - Copy old cert, or generate new if primary user id status changed
- boolean certified = false, revoked = false;
- for (PGPSignature sig : new IterableIterator<PGPSignature>(
- masterPublicKey.getSignaturesForID(userId))) {
+ // 4a. For each subkey change, generate new subkey binding certificate
+ for (SaveKeyringParcel.SubkeyChange change : saveParcel.changeSubKeys) {
+ PGPSecretKey sKey = sKR.getSecretKey(change.mKeyId);
+ if (sKey == null) {
+ return null;
}
+ PGPPublicKey pKey = sKey.getPublicKey();
+
+ // generate and add new signature
+ PGPSignature sig = generateSubkeyBindingSignature(masterPublicKey, masterPrivateKey,
+ sKey, pKey, change.mFlags, change.mExpiry, passphrase);
+ pKey = PGPPublicKey.addCertification(pKey, sig);
+ sKR = PGPSecretKeyRing.insertSecretKey(sKR, PGPSecretKey.replacePublicKey(sKey, pKey));
}
- for (PGPPublicKey newKey : publicKeys) {
- PGPPublicKey oldKey = pKR.getPublicKey(newKey.getKeyID());
- for (PGPSignature sig : new IterableIterator<PGPSignature>(
- oldKey.getSignatures())) {
+ // 4b. For each subkey change, generate new subkey binding certificate
+ for (long revocation : saveParcel.revokeSubKeys) {
+ PGPSecretKey sKey = sKR.getSecretKey(revocation);
+ if (sKey == null) {
+ return null;
+ }
+ PGPPublicKey pKey = sKey.getPublicKey();
+
+ // generate and add new signature
+ PGPSignature sig = generateRevocationSignature(masterPublicKey, masterPrivateKey, pKey);
+
+ pKey = PGPPublicKey.addCertification(pKey, sig);
+ sKR = PGPSecretKeyRing.insertSecretKey(sKR, PGPSecretKey.replacePublicKey(sKey, pKey));
+ }
+
+ // 5. Generate and add new subkeys
+ for (SaveKeyringParcel.SubkeyAdd add : saveParcel.addSubKeys) {
+ try {
+ PGPSecretKey sKey = createKey(add.mAlgorithm, add.mKeysize, passphrase, false);
+ 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));
+ } catch (PgpGeneralMsgIdException e) {
+ return null;
}
}
- // If requested, set new passphrase
+ // 6. If requested, change passphrase
if (saveParcel.newPassphrase != null) {
PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder().build()
.get(HashAlgorithmTags.SHA1);
@@ -352,9 +318,7 @@ public class PgpKeyOperation {
sKR = PGPSecretKeyRing.copyWithNewPassword(sKR, keyDecryptor, keyEncryptorNew);
}
- // 8. Return pair (PublicKeyRing,SecretKeyRing)
-
- return new Pair<PGPSecretKeyRing, PGPPublicKeyRing>(sKR, pKR);
+ return new UncachedKeyRing(sKR);
}
@@ -390,10 +354,29 @@ public class PgpKeyOperation {
return sGen.generateCertification(userId, pKey);
}
+ private static PGPSignature generateRevocationSignature(
+ PGPPublicKey masterPublicKey, PGPPrivateKey masterPrivateKey, PGPPublicKey pKey)
+ throws IOException, PGPException, SignatureException {
+ PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
+ pKey.getAlgorithm(), PGPUtil.SHA1)
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+ PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
+ PGPSignatureSubpacketGenerator subHashedPacketsGen = new PGPSignatureSubpacketGenerator();
+ subHashedPacketsGen.setSignatureCreationTime(false, new Date());
+ sGen.setHashedSubpackets(subHashedPacketsGen.generate());
+ // Generate key revocation or subkey revocation, depending on master/subkey-ness
+ if (masterPublicKey.getKeyID() == pKey.getKeyID()) {
+ sGen.init(PGPSignature.KEY_REVOCATION, masterPrivateKey);
+ return sGen.generateCertification(masterPublicKey);
+ } else {
+ sGen.init(PGPSignature.SUBKEY_REVOCATION, masterPrivateKey);
+ return sGen.generateCertification(masterPublicKey, pKey);
+ }
+ }
+
private static PGPSignature generateSubkeyBindingSignature(
PGPPublicKey masterPublicKey, PGPPrivateKey masterPrivateKey,
- PGPSecretKey sKey, PGPPublicKey pKey,
- int flags, Long expiry, String passphrase)
+ PGPSecretKey sKey, PGPPublicKey pKey, int flags, Long expiry, String passphrase)
throws PgpGeneralMsgIdException, IOException, PGPException, SignatureException {
// date for signing
@@ -510,19 +493,4 @@ public class PgpKeyOperation {
return publicKey;
}
- /**
- * Simple static subclass that stores two values.
- * <p/>
- * This is only used to return a pair of values in one function above. We specifically don't use
- * com.android.Pair to keep this class free from android dependencies.
- */
- public static class Pair<K, V> {
- public final K first;
- public final V second;
-
- public Pair(K first, V second) {
- this.first = first;
- this.second = second;
- }
- }
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/PgpGeneralMsgIdException.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/PgpGeneralMsgIdException.java
index 3b88897ed..3700b4c34 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/PgpGeneralMsgIdException.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/PgpGeneralMsgIdException.java
@@ -30,6 +30,11 @@ public class PgpGeneralMsgIdException extends Exception {
mMessageId = messageId;
}
+ public PgpGeneralMsgIdException(int messageId, Throwable cause) {
+ super("msg[" + messageId + "]", cause);
+ mMessageId = messageId;
+ }
+
public PgpGeneralException getContextualized(Context context) {
return new PgpGeneralException(context.getString(mMessageId), this);
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java
index ca7da5ae3..c68b7c189 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java
@@ -25,14 +25,14 @@ public class SaveKeyringParcel implements Parcelable {
// the master key id to be edited
public final long mMasterKeyId;
// the key fingerprint, for safety
- private final byte[] mFingerprint;
+ public final byte[] mFingerprint;
public String newPassphrase;
public String[] addUserIds;
public SubkeyAdd[] addSubKeys;
- public HashMap<Long, SubkeyChange> changeSubKeys;
+ public SubkeyChange[] changeSubKeys;
public String changePrimaryUserId;
public String[] revokeUserIds;
@@ -76,7 +76,7 @@ public class SaveKeyringParcel implements Parcelable {
addUserIds = source.createStringArray();
addSubKeys = (SubkeyAdd[]) source.readSerializable();
- changeSubKeys = (HashMap<Long,SubkeyChange>) source.readSerializable();
+ changeSubKeys = (SubkeyChange[]) source.readSerializable();
changePrimaryUserId = source.readString();
revokeUserIds = source.createStringArray();