aboutsummaryrefslogtreecommitdiffstats
path: root/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
diff options
context:
space:
mode:
Diffstat (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java')
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java346
1 files changed, 303 insertions, 43 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 9dd9f660b..44fc4c8c9 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
@@ -48,8 +48,8 @@ import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyEncryptorBuilder;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.pgp.Progressable;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralMsgIdException;
+import org.sufficientlysecure.keychain.service.OldSaveKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.Primes;
@@ -63,6 +63,7 @@ import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.security.SignatureException;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
@@ -124,7 +125,7 @@ public class PgpKeyOperation {
*/
// TODO: key flags?
- public PGPSecretKey createKey(int algorithmChoice, int keySize, String passphrase,
+ public byte[] createKey(int algorithmChoice, int keySize, String passphrase,
boolean isMasterKey)
throws NoSuchAlgorithmException, PGPException, NoSuchProviderException,
PgpGeneralMsgIdException, InvalidAlgorithmParameterException {
@@ -188,43 +189,23 @@ public class PgpKeyOperation {
PGPEncryptedData.CAST5, sha1Calc)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
- return new PGPSecretKey(keyPair.getPrivateKey(), keyPair.getPublicKey(),
- sha1Calc, isMasterKey, keyEncryptor);
- }
-
- public PGPSecretKeyRing changeSecretKeyPassphrase(PGPSecretKeyRing keyRing, String oldPassphrase,
- String newPassphrase)
- throws IOException, PGPException, NoSuchProviderException {
-
- updateProgress(R.string.progress_building_key, 0, 100);
- if (oldPassphrase == null) {
- oldPassphrase = "";
- }
- if (newPassphrase == null) {
- newPassphrase = "";
+ try {
+ return new PGPSecretKey(keyPair.getPrivateKey(), keyPair.getPublicKey(),
+ sha1Calc, isMasterKey, keyEncryptor).getEncoded();
+ } catch(IOException e) {
+ throw new PgpGeneralMsgIdException(R.string.error_encoding);
}
-
- PGPSecretKeyRing newKeyRing = PGPSecretKeyRing.copyWithNewPassword(
- keyRing,
- new JcePBESecretKeyDecryptorBuilder(new JcaPGPDigestCalculatorProviderBuilder()
- .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build()).setProvider(
- Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(oldPassphrase.toCharArray()),
- new JcePBESecretKeyEncryptorBuilder(keyRing.getSecretKey()
- .getKeyEncryptionAlgorithm()).build(newPassphrase.toCharArray()));
-
- return newKeyRing;
-
}
- public Pair<PGPSecretKeyRing, PGPPublicKeyRing> buildNewSecretKey(
- SaveKeyringParcel saveParcel)
+ public Pair<UncachedKeyRing,UncachedKeyRing> buildNewSecretKey(
+ OldSaveKeyringParcel saveParcel)
throws PgpGeneralMsgIdException, PGPException, SignatureException, IOException {
int usageId = saveParcel.keysUsages.get(0);
boolean canSign;
String mainUserId = saveParcel.userIds.get(0);
- PGPSecretKey masterKey = saveParcel.keys.get(0);
+ PGPSecretKey masterKey = saveParcel.keys.get(0).getSecretKeyExternal();
// this removes all userIds and certifications previously attached to the masterPublicKey
PGPPublicKey masterPublicKey = masterKey.getPublicKey();
@@ -299,7 +280,7 @@ public class PgpKeyOperation {
for (int i = 1; i < saveParcel.keys.size(); ++i) {
updateProgress(40 + 40 * (i - 1) / (saveParcel.keys.size() - 1), 100);
- PGPSecretKey subKey = saveParcel.keys.get(i);
+ PGPSecretKey subKey = saveParcel.keys.get(i).getSecretKeyExternal();
PGPPublicKey subPublicKey = subKey.getPublicKey();
PBESecretKeyDecryptor keyDecryptor2 = new JcePBESecretKeyDecryptorBuilder()
@@ -357,17 +338,19 @@ public class PgpKeyOperation {
PGPSecretKeyRing secretKeyRing = keyGen.generateSecretKeyRing();
PGPPublicKeyRing publicKeyRing = keyGen.generatePublicKeyRing();
- return new Pair<PGPSecretKeyRing, PGPPublicKeyRing>(secretKeyRing, publicKeyRing);
+ return new Pair(new UncachedKeyRing(secretKeyRing), new UncachedKeyRing(publicKeyRing));
}
- public Pair<PGPSecretKeyRing, PGPPublicKeyRing> buildSecretKey(PGPSecretKeyRing mKR,
- PGPPublicKeyRing pKR,
- SaveKeyringParcel saveParcel)
+ public Pair<UncachedKeyRing, UncachedKeyRing> buildSecretKey(WrappedSecretKeyRing wmKR,
+ WrappedPublicKeyRing wpKR,
+ OldSaveKeyringParcel saveParcel)
throws PgpGeneralMsgIdException, PGPException, SignatureException, IOException {
+ PGPSecretKeyRing mKR = wmKR.getRing();
+ PGPPublicKeyRing pKR = wpKR.getRing();
+
updateProgress(R.string.progress_building_key, 0, 100);
- PGPSecretKey masterKey = saveParcel.keys.get(0);
if (saveParcel.oldPassphrase == null) {
saveParcel.oldPassphrase = "";
@@ -399,12 +382,12 @@ public class PgpKeyOperation {
*/
if (saveParcel.deletedKeys != null) {
- for (PGPSecretKey dKey : saveParcel.deletedKeys) {
- mKR = PGPSecretKeyRing.removeSecretKey(mKR, dKey);
+ for (UncachedSecretKey dKey : saveParcel.deletedKeys) {
+ mKR = PGPSecretKeyRing.removeSecretKey(mKR, dKey.getSecretKeyExternal());
}
}
- masterKey = mKR.getSecretKey();
+ PGPSecretKey masterKey = mKR.getSecretKey();
PGPPublicKey masterPublicKey = masterKey.getPublicKey();
int usageId = saveParcel.keysUsages.get(0);
@@ -564,7 +547,7 @@ public class PgpKeyOperation {
for (int i = 1; i < saveParcel.keys.size(); ++i) {
updateProgress(40 + 50 * i / saveParcel.keys.size(), 100);
if (saveParcel.moddedKeys[i]) {
- PGPSecretKey subKey = saveParcel.keys.get(i);
+ PGPSecretKey subKey = saveParcel.keys.get(i).getSecretKeyExternal();
PGPPublicKey subPublicKey = subKey.getPublicKey();
PBESecretKeyDecryptor keyDecryptor2;
@@ -667,7 +650,7 @@ public class PgpKeyOperation {
for(String uid : new IterableIterator<String>(secretKeyRing.getPublicKey().getUserIDs())) {
for(PGPSignature sig : new IterableIterator<PGPSignature>(
- secretKeyRing.getPublicKey().getSignaturesForID(uid))) {
+ secretKeyRing.getPublicKey().getSignaturesForId(uid))) {
Log.d(Constants.TAG, "sig: " +
PgpKeyHelper.convertKeyIdToHex(sig.getKeyID()) + " for " + uid);
}
@@ -678,7 +661,7 @@ public class PgpKeyOperation {
for(String uid : new IterableIterator<String>(publicKeyRing.getPublicKey().getUserIDs())) {
for(PGPSignature sig : new IterableIterator<PGPSignature>(
- publicKeyRing.getPublicKey().getSignaturesForID(uid))) {
+ publicKeyRing.getPublicKey().getSignaturesForId(uid))) {
Log.d(Constants.TAG, "sig: " +
PgpKeyHelper.convertKeyIdToHex(sig.getKeyID()) + " for " + uid);
}
@@ -686,10 +669,287 @@ public class PgpKeyOperation {
*/
- return new Pair<PGPSecretKeyRing, PGPPublicKeyRing>(mKR, pKR);
+ return new Pair<UncachedKeyRing,UncachedKeyRing>(new UncachedKeyRing(pKR),
+ new UncachedKeyRing(mKR));
}
+ public Pair<PGPSecretKeyRing, PGPPublicKeyRing> buildSecretKey(PGPSecretKeyRing sKR,
+ PGPPublicKeyRing pKR,
+ 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)
+ *
+ */
+
+ // 1. Unlock private key
+ updateProgress(R.string.progress_building_key, 0, 100);
+
+ PGPPublicKey masterPublicKey = sKR.getPublicKey();
+ PGPPrivateKey masterPrivateKey; {
+ PGPSecretKey masterKey = sKR.getSecretKey();
+ PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
+ Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
+ masterPrivateKey = masterKey.extractPrivateKey(keyDecryptor);
+ }
+
+ // 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>();
+
+ // 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);
+ }
+
+ // generate and add new signature
+ PGPSignature sig = generateSubkeyBindingSignature(masterPublicKey, masterPrivateKey,
+ sKey, pKey, change.mFlags, change.mExpiry, passphrase);
+ pKey = PGPPublicKey.addCertification(pKey, sig);
+ }
+ 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) {
+ PGPSignature cert = generateRevocationSignature(masterPrivateKey,
+ masterPublicKey, userId);
+ masterPublicKey = PGPPublicKey.addCertification(masterPublicKey, userId, cert);
+ }
+ }
+
+ // 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))) {
+ }
+ }
+
+ for (PGPPublicKey newKey : publicKeys) {
+ PGPPublicKey oldKey = pKR.getPublicKey(newKey.getKeyID());
+ for (PGPSignature sig : new IterableIterator<PGPSignature>(
+ oldKey.getSignatures())) {
+ }
+ }
+
+ // If requested, set new passphrase
+ if (saveParcel.newPassphrase != null) {
+ PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder().build()
+ .get(HashAlgorithmTags.SHA1);
+ PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
+ Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
+ // Build key encryptor based on new passphrase
+ PBESecretKeyEncryptor keyEncryptorNew = new JcePBESecretKeyEncryptorBuilder(
+ PGPEncryptedData.CAST5, sha1Calc)
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
+ saveParcel.newPassphrase.toCharArray());
+
+ sKR = PGPSecretKeyRing.copyWithNewPassword(sKR, keyDecryptor, keyEncryptorNew);
+ }
+
+ // 8. Return pair (PublicKeyRing,SecretKeyRing)
+
+ return new Pair<PGPSecretKeyRing, PGPPublicKeyRing>(sKR, pKR);
+
+ }
+
+ private static PGPSignature generateUserIdSignature(
+ PGPPrivateKey masterPrivateKey, PGPPublicKey pKey, String userId, boolean primary)
+ 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());
+ subHashedPacketsGen.setPreferredSymmetricAlgorithms(true, PREFERRED_SYMMETRIC_ALGORITHMS);
+ subHashedPacketsGen.setPreferredHashAlgorithms(true, PREFERRED_HASH_ALGORITHMS);
+ subHashedPacketsGen.setPreferredCompressionAlgorithms(true, PREFERRED_COMPRESSION_ALGORITHMS);
+ subHashedPacketsGen.setPrimaryUserID(false, primary);
+ sGen.setHashedSubpackets(subHashedPacketsGen.generate());
+ sGen.init(PGPSignature.POSITIVE_CERTIFICATION, masterPrivateKey);
+ return sGen.generateCertification(userId, pKey);
+ }
+
+ private static PGPSignature generateRevocationSignature(
+ PGPPrivateKey masterPrivateKey, PGPPublicKey pKey, String userId)
+ 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());
+ sGen.init(PGPSignature.CERTIFICATION_REVOCATION, masterPrivateKey);
+ return sGen.generateCertification(userId, pKey);
+ }
+
+ private static PGPSignature generateSubkeyBindingSignature(
+ PGPPublicKey masterPublicKey, PGPPrivateKey masterPrivateKey,
+ PGPSecretKey sKey, PGPPublicKey pKey,
+ int flags, Long expiry, String passphrase)
+ throws PgpGeneralMsgIdException, IOException, PGPException, SignatureException {
+
+ // date for signing
+ Date todayDate = new Date();
+ PGPSignatureSubpacketGenerator unhashedPacketsGen = new PGPSignatureSubpacketGenerator();
+
+ // If this key can sign, we need a primary key binding signature
+ if ((flags & KeyFlags.SIGN_DATA) != 0) {
+
+ PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder()
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
+ passphrase.toCharArray());
+ PGPPrivateKey subPrivateKey = sKey.extractPrivateKey(keyDecryptor);
+
+ // cross-certify signing keys
+ PGPSignatureSubpacketGenerator subHashedPacketsGen = new PGPSignatureSubpacketGenerator();
+ subHashedPacketsGen.setSignatureCreationTime(false, todayDate);
+ PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
+ pKey.getAlgorithm(), PGPUtil.SHA1)
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+ PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
+ sGen.init(PGPSignature.PRIMARYKEY_BINDING, subPrivateKey);
+ sGen.setHashedSubpackets(subHashedPacketsGen.generate());
+ PGPSignature certification = sGen.generateCertification(masterPublicKey, pKey);
+ unhashedPacketsGen.setEmbeddedSignature(false, certification);
+ }
+
+ PGPSignatureSubpacketGenerator hashedPacketsGen;
+ {
+ hashedPacketsGen = new PGPSignatureSubpacketGenerator();
+ hashedPacketsGen.setSignatureCreationTime(false, todayDate);
+ hashedPacketsGen.setKeyFlags(false, flags);
+ }
+
+ 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);
+ }
+ hashedPacketsGen.setKeyExpirationTime(false, expiry - creationDate.getTimeInMillis());
+ } else {
+ hashedPacketsGen.setKeyExpirationTime(false, 0);
+ }
+
+ PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
+ pKey.getAlgorithm(), PGPUtil.SHA1)
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+ PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
+ sGen.init(PGPSignature.SUBKEY_BINDING, masterPrivateKey);
+ sGen.setHashedSubpackets(hashedPacketsGen.generate());
+ sGen.setUnhashedSubpackets(unhashedPacketsGen.generate());
+
+ return sGen.generateCertification(masterPublicKey, pKey);
+
+ }
+
+
/**
* Certify the given pubkeyid with the given masterkeyid.
*