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.java751
1 files changed, 523 insertions, 228 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 bb692555e..967a7caa9 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
- * Copyright (C) 2010-2014 Thialfihar <thi@thialfihar.org>
+ * Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -20,12 +20,12 @@ package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.bcpg.CompressionAlgorithmTags;
import org.spongycastle.bcpg.HashAlgorithmTags;
-import org.spongycastle.bcpg.PublicKeyAlgorithmTags;
import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags;
+import org.spongycastle.bcpg.sig.Features;
import org.spongycastle.bcpg.sig.KeyFlags;
import org.spongycastle.jce.spec.ElGamalParameterSpec;
-import org.spongycastle.openpgp.PGPEncryptedData;
import org.spongycastle.openpgp.PGPException;
+import org.spongycastle.openpgp.PGPKeyFlags;
import org.spongycastle.openpgp.PGPKeyPair;
import org.spongycastle.openpgp.PGPPrivateKey;
import org.spongycastle.openpgp.PGPPublicKey;
@@ -34,7 +34,6 @@ import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.spongycastle.openpgp.PGPSignature;
import org.spongycastle.openpgp.PGPSignatureGenerator;
import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator;
-import org.spongycastle.openpgp.PGPUtil;
import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
import org.spongycastle.openpgp.operator.PBESecretKeyEncryptor;
import org.spongycastle.openpgp.operator.PGPContentSignerBuilder;
@@ -53,6 +52,8 @@ import org.sufficientlysecure.keychain.service.OperationResultParcel.LogType;
import org.sufficientlysecure.keychain.service.OperationResultParcel.OperationLog;
import org.sufficientlysecure.keychain.service.OperationResults.EditKeyResult;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
+import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm;
+import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Curve;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyAdd;
import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.Log;
@@ -67,6 +68,7 @@ import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.security.SignatureException;
+import java.security.spec.ECGenParameterSpec;
import java.util.Arrays;
import java.util.Date;
import java.util.Iterator;
@@ -84,15 +86,45 @@ import java.util.Stack;
public class PgpKeyOperation {
private Stack<Progressable> mProgress;
+ // most preferred is first
private static final int[] PREFERRED_SYMMETRIC_ALGORITHMS = new int[]{
- SymmetricKeyAlgorithmTags.AES_256, SymmetricKeyAlgorithmTags.AES_192,
- SymmetricKeyAlgorithmTags.AES_128, SymmetricKeyAlgorithmTags.CAST5,
- SymmetricKeyAlgorithmTags.TRIPLE_DES};
- private static final int[] PREFERRED_HASH_ALGORITHMS = new int[]{HashAlgorithmTags.SHA1,
- HashAlgorithmTags.SHA256, HashAlgorithmTags.RIPEMD160};
+ SymmetricKeyAlgorithmTags.AES_256,
+ SymmetricKeyAlgorithmTags.AES_192,
+ SymmetricKeyAlgorithmTags.AES_128,
+ SymmetricKeyAlgorithmTags.CAST5
+ };
+ private static final int[] PREFERRED_HASH_ALGORITHMS = new int[]{
+ HashAlgorithmTags.SHA512,
+ HashAlgorithmTags.SHA384,
+ HashAlgorithmTags.SHA224,
+ HashAlgorithmTags.SHA256,
+ HashAlgorithmTags.RIPEMD160
+ };
private static final int[] PREFERRED_COMPRESSION_ALGORITHMS = new int[]{
- CompressionAlgorithmTags.ZLIB, CompressionAlgorithmTags.BZIP2,
- CompressionAlgorithmTags.ZIP};
+ CompressionAlgorithmTags.ZLIB,
+ CompressionAlgorithmTags.BZIP2,
+ CompressionAlgorithmTags.ZIP
+ };
+
+ /*
+ * Note: s2kcount is a number between 0 and 0xff that controls the
+ * number of times to iterate the password hash before use. More
+ * iterations are useful against offline attacks, as it takes more
+ * time to check each password. The actual number of iterations is
+ * rather complex, and also depends on the hash function in use.
+ * Refer to Section 3.7.1.3 in rfc4880.txt. Bigger numbers give
+ * you more iterations. As a rough rule of thumb, when using
+ * SHA256 as the hashing function, 0x10 gives you about 64
+ * iterations, 0x20 about 128, 0x30 about 256 and so on till 0xf0,
+ * or about 1 million iterations. The maximum you can go to is
+ * 0xff, or about 2 million iterations. I'll use 0xc0 as a
+ * default -- about 130,000 iterations.
+ *
+ * http://kbsriram.com/2013/01/generating-rsa-keys-with-bouncycastle.html
+ */
+ private static final int SECRET_KEY_ENCRYPTOR_HASH_ALGO = HashAlgorithmTags.SHA512;
+ private static final int SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO = SymmetricKeyAlgorithmTags.AES_256;
+ private static final int SECRET_KEY_ENCRYPTOR_S2K_COUNT = 0x60;
public PgpKeyOperation(Progressable progress) {
super();
@@ -126,31 +158,65 @@ public class PgpKeyOperation {
mProgress.peek().setProgress(message, current, 100);
}
+ private ECGenParameterSpec getEccParameterSpec(Curve curve) {
+ switch (curve) {
+ case NIST_P256: return new ECGenParameterSpec("P-256");
+ case NIST_P384: return new ECGenParameterSpec("P-384");
+ case NIST_P521: return new ECGenParameterSpec("P-521");
+
+ // @see SaveKeyringParcel
+ // case BRAINPOOL_P256: return new ECGenParameterSpec("brainpoolp256r1");
+ // case BRAINPOOL_P384: return new ECGenParameterSpec("brainpoolp384r1");
+ // case BRAINPOOL_P512: return new ECGenParameterSpec("brainpoolp512r1");
+ }
+ throw new RuntimeException("Invalid choice! (can't happen)");
+ }
+
/** Creates new secret key. */
- private PGPKeyPair createKey(int algorithmChoice, int keySize, OperationLog log, int indent) {
+ private PGPKeyPair createKey(SubkeyAdd add, OperationLog log, int indent) {
try {
- if (keySize < 512) {
- log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_KEYSIZE_512, indent);
- return null;
+ // Some safety checks
+ if (add.mAlgorithm == Algorithm.ECDH || add.mAlgorithm == Algorithm.ECDSA) {
+ if (add.mCurve == null) {
+ log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_NO_CURVE, indent);
+ return null;
+ }
+ } else {
+ if (add.mKeySize == null) {
+ log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_NO_KEYSIZE, indent);
+ return null;
+ }
+ if (add.mKeySize < 512) {
+ log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_KEYSIZE_512, indent);
+ return null;
+ }
}
int algorithm;
KeyPairGenerator keyGen;
- switch (algorithmChoice) {
- case PublicKeyAlgorithmTags.DSA: {
+ switch (add.mAlgorithm) {
+ case DSA: {
+ if ((add.mFlags & (PGPKeyFlags.CAN_ENCRYPT_COMMS | PGPKeyFlags.CAN_ENCRYPT_STORAGE)) > 0) {
+ log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_FLAGS_DSA, indent);
+ return null;
+ }
progress(R.string.progress_generating_dsa, 30);
keyGen = KeyPairGenerator.getInstance("DSA", Constants.BOUNCY_CASTLE_PROVIDER_NAME);
- keyGen.initialize(keySize, new SecureRandom());
+ keyGen.initialize(add.mKeySize, new SecureRandom());
algorithm = PGPPublicKey.DSA;
break;
}
- case PublicKeyAlgorithmTags.ELGAMAL_ENCRYPT: {
+ case ELGAMAL: {
+ if ((add.mFlags & (PGPKeyFlags.CAN_SIGN | PGPKeyFlags.CAN_CERTIFY)) > 0) {
+ log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_FLAGS_ELGAMAL, indent);
+ return null;
+ }
progress(R.string.progress_generating_elgamal, 30);
keyGen = KeyPairGenerator.getInstance("ElGamal", Constants.BOUNCY_CASTLE_PROVIDER_NAME);
- BigInteger p = Primes.getBestPrime(keySize);
+ BigInteger p = Primes.getBestPrime(add.mKeySize);
BigInteger g = new BigInteger("2");
ElGamalParameterSpec elParams = new ElGamalParameterSpec(p, g);
@@ -160,15 +226,44 @@ public class PgpKeyOperation {
break;
}
- case PublicKeyAlgorithmTags.RSA_GENERAL: {
+ case RSA: {
progress(R.string.progress_generating_rsa, 30);
keyGen = KeyPairGenerator.getInstance("RSA", Constants.BOUNCY_CASTLE_PROVIDER_NAME);
- keyGen.initialize(keySize, new SecureRandom());
+ keyGen.initialize(add.mKeySize, new SecureRandom());
algorithm = PGPPublicKey.RSA_GENERAL;
break;
}
+ case ECDSA: {
+ if ((add.mFlags & (PGPKeyFlags.CAN_ENCRYPT_COMMS | PGPKeyFlags.CAN_ENCRYPT_STORAGE)) > 0) {
+ log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_FLAGS_ECDSA, indent);
+ return null;
+ }
+ progress(R.string.progress_generating_ecdsa, 30);
+ ECGenParameterSpec ecParamSpec = getEccParameterSpec(add.mCurve);
+ keyGen = KeyPairGenerator.getInstance("ECDSA", Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+ keyGen.initialize(ecParamSpec, new SecureRandom());
+
+ algorithm = PGPPublicKey.ECDSA;
+ break;
+ }
+
+ case ECDH: {
+ // make sure there are no sign or certify flags set
+ if ((add.mFlags & (PGPKeyFlags.CAN_SIGN | PGPKeyFlags.CAN_CERTIFY)) > 0) {
+ log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_FLAGS_ECDH, indent);
+ return null;
+ }
+ progress(R.string.progress_generating_ecdh, 30);
+ ECGenParameterSpec ecParamSpec = getEccParameterSpec(add.mCurve);
+ keyGen = KeyPairGenerator.getInstance("ECDH", Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+ keyGen.initialize(ecParamSpec, new SecureRandom());
+
+ algorithm = PGPPublicKey.ECDH;
+ break;
+ }
+
default: {
log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_UNKNOWN_ALGO, indent);
return null;
@@ -181,7 +276,8 @@ public class PgpKeyOperation {
} catch(NoSuchProviderException e) {
throw new RuntimeException(e);
} catch(NoSuchAlgorithmException e) {
- throw new RuntimeException(e);
+ log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_UNKNOWN_ALGO, indent);
+ return null;
} catch(InvalidAlgorithmParameterException e) {
throw new RuntimeException(e);
} catch(PGPException e) {
@@ -218,13 +314,13 @@ public class PgpKeyOperation {
return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
- if (add.mAlgorithm == PublicKeyAlgorithmTags.ELGAMAL_ENCRYPT) {
- log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_MASTER_ELGAMAL, indent);
+ if (add.mExpiry == null) {
+ log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_NULL_EXPIRY, indent);
return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
subProgressPush(10, 30);
- PGPKeyPair keyPair = createKey(add.mAlgorithm, add.mKeysize, log, indent);
+ PGPKeyPair keyPair = createKey(add, log, indent);
subProgressPop();
// return null if this failed (an error will already have been logged by createKey)
@@ -234,13 +330,16 @@ public class PgpKeyOperation {
progress(R.string.progress_building_master_key, 40);
- // define hashing and signing algos
- PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder()
- .build().get(HashAlgorithmTags.SHA1);
// Build key encrypter and decrypter based on passphrase
+ PGPDigestCalculator encryptorHashCalc = new JcaPGPDigestCalculatorProviderBuilder()
+ .build().get(SECRET_KEY_ENCRYPTOR_HASH_ALGO);
PBESecretKeyEncryptor keyEncryptor = new JcePBESecretKeyEncryptorBuilder(
- PGPEncryptedData.CAST5, sha1Calc)
+ SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO, encryptorHashCalc, SECRET_KEY_ENCRYPTOR_S2K_COUNT)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build("".toCharArray());
+
+ // NOTE: only SHA1 is supported for key checksum calculations.
+ PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder()
+ .build().get(HashAlgorithmTags.SHA1);
PGPSecretKey masterSecretKey = new PGPSecretKey(keyPair.getPrivateKey(), keyPair.getPublicKey(),
sha1Calc, true, keyEncryptor);
@@ -248,7 +347,7 @@ public class PgpKeyOperation {
masterSecretKey.getEncoded(), new JcaKeyFingerprintCalculator());
subProgressPush(50, 100);
- return internal(sKR, masterSecretKey, add.mFlags, saveParcel, "", log);
+ return internal(sKR, masterSecretKey, add.mFlags, add.mExpiry, saveParcel, "", log);
} catch (PGPException e) {
log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_INTERNAL_PGP, indent);
@@ -314,14 +413,17 @@ public class PgpKeyOperation {
// read masterKeyFlags, and use the same as before.
// since this is the master key, this contains at least CERTIFY_OTHER
- int masterKeyFlags = readKeyFlags(masterSecretKey.getPublicKey()) | KeyFlags.CERTIFY_OTHER;
+ PGPPublicKey masterPublicKey = masterSecretKey.getPublicKey();
+ int masterKeyFlags = readKeyFlags(masterPublicKey) | KeyFlags.CERTIFY_OTHER;
+ long masterKeyExpiry = masterPublicKey.getValidSeconds() == 0L ? 0L :
+ masterPublicKey.getCreationTime().getTime() / 1000 + masterPublicKey.getValidSeconds();
- return internal(sKR, masterSecretKey, masterKeyFlags, saveParcel, passphrase, log);
+ return internal(sKR, masterSecretKey, masterKeyFlags, masterKeyExpiry, saveParcel, passphrase, log);
}
private EditKeyResult internal(PGPSecretKeyRing sKR, PGPSecretKey masterSecretKey,
- int masterKeyFlags,
+ int masterKeyFlags, long masterKeyExpiry,
SaveKeyringParcel saveParcel, String passphrase,
OperationLog log) {
@@ -346,177 +448,196 @@ public class PgpKeyOperation {
}
}
- // work on master secret key
try {
- PGPPublicKey modifiedPublicKey = masterPublicKey;
+ { // work on master secret key
- // 2a. Add certificates for new user ids
- subProgressPush(15, 25);
- for (int i = 0; i < saveParcel.mAddUserIds.size(); i++) {
+ PGPPublicKey modifiedPublicKey = masterPublicKey;
- progress(R.string.progress_modify_adduid, (i-1) * (100 / saveParcel.mAddUserIds.size()));
- String userId = saveParcel.mAddUserIds.get(i);
- log.add(LogLevel.INFO, LogType.MSG_MF_UID_ADD, indent, userId);
+ // 2a. Add certificates for new user ids
+ subProgressPush(15, 25);
+ for (int i = 0; i < saveParcel.mAddUserIds.size(); i++) {
- if (userId.equals("")) {
- log.add(LogLevel.ERROR, LogType.MSG_MF_UID_ERROR_EMPTY, indent+1);
- return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
- }
+ progress(R.string.progress_modify_adduid, (i - 1) * (100 / saveParcel.mAddUserIds.size()));
+ String userId = saveParcel.mAddUserIds.get(i);
+ log.add(LogLevel.INFO, LogType.MSG_MF_UID_ADD, indent, userId);
- // this operation supersedes all previous binding and revocation certificates,
- // so remove those to retain assertions from canonicalization for later operations
- @SuppressWarnings("unchecked")
- Iterator<PGPSignature> it = modifiedPublicKey.getSignaturesForID(userId);
- if (it != null) {
- for (PGPSignature cert : new IterableIterator<PGPSignature>(it)) {
- if (cert.getKeyID() != masterPublicKey.getKeyID()) {
- // foreign certificate?! error error error
- log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_INTEGRITY, indent);
- return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
- }
- if (cert.getSignatureType() == PGPSignature.CERTIFICATION_REVOCATION
- || cert.getSignatureType() == PGPSignature.NO_CERTIFICATION
- || cert.getSignatureType() == PGPSignature.CASUAL_CERTIFICATION
- || cert.getSignatureType() == PGPSignature.POSITIVE_CERTIFICATION
- || cert.getSignatureType() == PGPSignature.DEFAULT_CERTIFICATION) {
- modifiedPublicKey = PGPPublicKey.removeCertification(
- modifiedPublicKey, userId, cert);
- }
+ if (userId.equals("")) {
+ log.add(LogLevel.ERROR, LogType.MSG_MF_UID_ERROR_EMPTY, indent + 1);
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
- }
- // if it's supposed to be primary, we can do that here as well
- boolean isPrimary = saveParcel.mChangePrimaryUserId != null
- && userId.equals(saveParcel.mChangePrimaryUserId);
- // generate and add new certificate
- PGPSignature cert = generateUserIdSignature(masterPrivateKey,
- masterPublicKey, userId, isPrimary, masterKeyFlags);
- modifiedPublicKey = PGPPublicKey.addCertification(modifiedPublicKey, userId, cert);
- }
- subProgressPop();
+ // this operation supersedes all previous binding and revocation certificates,
+ // so remove those to retain assertions from canonicalization for later operations
+ @SuppressWarnings("unchecked")
+ Iterator<PGPSignature> it = modifiedPublicKey.getSignaturesForID(userId);
+ if (it != null) {
+ for (PGPSignature cert : new IterableIterator<PGPSignature>(it)) {
+ if (cert.getKeyID() != masterPublicKey.getKeyID()) {
+ // foreign certificate?! error error error
+ log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_INTEGRITY, indent);
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ }
+ if (cert.getSignatureType() == PGPSignature.CERTIFICATION_REVOCATION
+ || cert.getSignatureType() == PGPSignature.NO_CERTIFICATION
+ || cert.getSignatureType() == PGPSignature.CASUAL_CERTIFICATION
+ || cert.getSignatureType() == PGPSignature.POSITIVE_CERTIFICATION
+ || cert.getSignatureType() == PGPSignature.DEFAULT_CERTIFICATION) {
+ modifiedPublicKey = PGPPublicKey.removeCertification(
+ modifiedPublicKey, userId, cert);
+ }
+ }
+ }
- // 2b. Add revocations for revoked user ids
- subProgressPush(25, 40);
- for (int i = 0; i < saveParcel.mRevokeUserIds.size(); i++) {
+ // if it's supposed to be primary, we can do that here as well
+ boolean isPrimary = saveParcel.mChangePrimaryUserId != null
+ && userId.equals(saveParcel.mChangePrimaryUserId);
+ // generate and add new certificate
+ PGPSignature cert = generateUserIdSignature(masterPrivateKey,
+ masterPublicKey, userId, isPrimary, masterKeyFlags, masterKeyExpiry);
+ modifiedPublicKey = PGPPublicKey.addCertification(modifiedPublicKey, userId, cert);
+ }
+ subProgressPop();
- progress(R.string.progress_modify_revokeuid, (i-1) * (100 / saveParcel.mRevokeUserIds.size()));
- String userId = saveParcel.mRevokeUserIds.get(i);
- log.add(LogLevel.INFO, LogType.MSG_MF_UID_REVOKE, indent, userId);
+ // 2b. Add revocations for revoked user ids
+ subProgressPush(25, 40);
+ for (int i = 0; i < saveParcel.mRevokeUserIds.size(); i++) {
+
+ progress(R.string.progress_modify_revokeuid, (i - 1) * (100 / saveParcel.mRevokeUserIds.size()));
+ String userId = saveParcel.mRevokeUserIds.get(i);
+ log.add(LogLevel.INFO, LogType.MSG_MF_UID_REVOKE, indent, userId);
+
+ // Make sure the user id exists (yes these are 10 LoC in Java!)
+ boolean exists = false;
+ //noinspection unchecked
+ for (String uid : new IterableIterator<String>(modifiedPublicKey.getUserIDs())) {
+ if (userId.equals(uid)) {
+ exists = true;
+ break;
+ }
+ }
+ if (!exists) {
+ log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_NOEXIST_REVOKE, indent);
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ }
- // a duplicate revocation will be removed during canonicalization, so no need to
- // take care of that here.
- PGPSignature cert = generateRevocationSignature(masterPrivateKey,
- masterPublicKey, userId);
- modifiedPublicKey = PGPPublicKey.addCertification(modifiedPublicKey, userId, cert);
- }
- subProgressPop();
+ // a duplicate revocation will be removed during canonicalization, so no need to
+ // take care of that here.
+ PGPSignature cert = generateRevocationSignature(masterPrivateKey,
+ masterPublicKey, userId);
+ modifiedPublicKey = PGPPublicKey.addCertification(modifiedPublicKey, userId, cert);
+ }
+ subProgressPop();
- // 3. If primary user id changed, generate new certificates for both old and new
- if (saveParcel.mChangePrimaryUserId != null) {
- progress(R.string.progress_modify_primaryuid, 40);
+ // 3. If primary user id changed, generate new certificates for both old and new
+ if (saveParcel.mChangePrimaryUserId != null) {
+ progress(R.string.progress_modify_primaryuid, 40);
- // keep track if we actually changed one
- boolean ok = false;
- log.add(LogLevel.INFO, LogType.MSG_MF_UID_PRIMARY, indent);
- indent += 1;
+ // keep track if we actually changed one
+ boolean ok = false;
+ log.add(LogLevel.INFO, LogType.MSG_MF_UID_PRIMARY, indent);
+ indent += 1;
- // we work on the modifiedPublicKey here, to respect new or newly revoked uids
- // noinspection unchecked
- for (String userId : new IterableIterator<String>(modifiedPublicKey.getUserIDs())) {
- boolean isRevoked = false;
- PGPSignature currentCert = null;
+ // we work on the modifiedPublicKey here, to respect new or newly revoked uids
// noinspection unchecked
- for (PGPSignature cert : new IterableIterator<PGPSignature>(
- modifiedPublicKey.getSignaturesForID(userId))) {
- if (cert.getKeyID() != masterPublicKey.getKeyID()) {
- // foreign certificate?! error error error
+ for (String userId : new IterableIterator<String>(modifiedPublicKey.getUserIDs())) {
+ boolean isRevoked = false;
+ PGPSignature currentCert = null;
+ // noinspection unchecked
+ for (PGPSignature cert : new IterableIterator<PGPSignature>(
+ modifiedPublicKey.getSignaturesForID(userId))) {
+ if (cert.getKeyID() != masterPublicKey.getKeyID()) {
+ // foreign certificate?! error error error
+ log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_INTEGRITY, indent);
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ }
+ // we know from canonicalization that if there is any revocation here, it
+ // is valid and not superseded by a newer certification.
+ if (cert.getSignatureType() == PGPSignature.CERTIFICATION_REVOCATION) {
+ isRevoked = true;
+ continue;
+ }
+ // we know from canonicalization that there is only one binding
+ // certification here, so we can just work with the first one.
+ if (cert.getSignatureType() == PGPSignature.NO_CERTIFICATION ||
+ cert.getSignatureType() == PGPSignature.CASUAL_CERTIFICATION ||
+ cert.getSignatureType() == PGPSignature.POSITIVE_CERTIFICATION ||
+ cert.getSignatureType() == PGPSignature.DEFAULT_CERTIFICATION) {
+ currentCert = cert;
+ }
+ }
+
+ if (currentCert == null) {
+ // no certificate found?! error error error
log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_INTEGRITY, indent);
return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
- // we know from canonicalization that if there is any revocation here, it
- // is valid and not superseded by a newer certification.
- if (cert.getSignatureType() == PGPSignature.CERTIFICATION_REVOCATION) {
- isRevoked = true;
+
+ // we definitely should not update certifications of revoked keys, so just leave it.
+ if (isRevoked) {
+ // revoked user ids cannot be primary!
+ if (userId.equals(saveParcel.mChangePrimaryUserId)) {
+ log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_REVOKED_PRIMARY, indent);
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ }
continue;
}
- // we know from canonicalization that there is only one binding
- // certification here, so we can just work with the first one.
- if (cert.getSignatureType() == PGPSignature.NO_CERTIFICATION ||
- cert.getSignatureType() == PGPSignature.CASUAL_CERTIFICATION ||
- cert.getSignatureType() == PGPSignature.POSITIVE_CERTIFICATION ||
- cert.getSignatureType() == PGPSignature.DEFAULT_CERTIFICATION) {
- currentCert = cert;
- }
- }
- if (currentCert == null) {
- // no certificate found?! error error error
- log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_INTEGRITY, indent);
- return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
- }
-
- // we definitely should not update certifications of revoked keys, so just leave it.
- if (isRevoked) {
- // revoked user ids cannot be primary!
- if (userId.equals(saveParcel.mChangePrimaryUserId)) {
- log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_REVOKED_PRIMARY, indent);
- return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ // if this is~ the/a primary user id
+ if (currentCert.getHashedSubPackets() != null
+ && currentCert.getHashedSubPackets().isPrimaryUserID()) {
+ // if it's the one we want, just leave it as is
+ if (userId.equals(saveParcel.mChangePrimaryUserId)) {
+ ok = true;
+ continue;
+ }
+ // otherwise, generate new non-primary certification
+ log.add(LogLevel.DEBUG, LogType.MSG_MF_PRIMARY_REPLACE_OLD, indent);
+ modifiedPublicKey = PGPPublicKey.removeCertification(
+ modifiedPublicKey, userId, currentCert);
+ PGPSignature newCert = generateUserIdSignature(
+ masterPrivateKey, masterPublicKey, userId, false,
+ masterKeyFlags, masterKeyExpiry);
+ modifiedPublicKey = PGPPublicKey.addCertification(
+ modifiedPublicKey, userId, newCert);
+ continue;
}
- continue;
- }
- // if this is~ the/a primary user id
- if (currentCert.getHashedSubPackets() != null
- && currentCert.getHashedSubPackets().isPrimaryUserID()) {
- // if it's the one we want, just leave it as is
+ // if we are here, this is not currently a primary user id
+
+ // if it should be
if (userId.equals(saveParcel.mChangePrimaryUserId)) {
+ // add shiny new primary user id certificate
+ log.add(LogLevel.DEBUG, LogType.MSG_MF_PRIMARY_NEW, indent);
+ modifiedPublicKey = PGPPublicKey.removeCertification(
+ modifiedPublicKey, userId, currentCert);
+ PGPSignature newCert = generateUserIdSignature(
+ masterPrivateKey, masterPublicKey, userId, true,
+ masterKeyFlags, masterKeyExpiry);
+ modifiedPublicKey = PGPPublicKey.addCertification(
+ modifiedPublicKey, userId, newCert);
ok = true;
- continue;
}
- // otherwise, generate new non-primary certification
- log.add(LogLevel.DEBUG, LogType.MSG_MF_PRIMARY_REPLACE_OLD, indent);
- modifiedPublicKey = PGPPublicKey.removeCertification(
- modifiedPublicKey, userId, currentCert);
- PGPSignature newCert = generateUserIdSignature(
- masterPrivateKey, masterPublicKey, userId, false, masterKeyFlags);
- modifiedPublicKey = PGPPublicKey.addCertification(
- modifiedPublicKey, userId, newCert);
- continue;
- }
- // if we are here, this is not currently a primary user id
-
- // if it should be
- if (userId.equals(saveParcel.mChangePrimaryUserId)) {
- // add shiny new primary user id certificate
- log.add(LogLevel.DEBUG, LogType.MSG_MF_PRIMARY_NEW, indent);
- modifiedPublicKey = PGPPublicKey.removeCertification(
- modifiedPublicKey, userId, currentCert);
- PGPSignature newCert = generateUserIdSignature(
- masterPrivateKey, masterPublicKey, userId, true, masterKeyFlags);
- modifiedPublicKey = PGPPublicKey.addCertification(
- modifiedPublicKey, userId, newCert);
- ok = true;
+ // user id is not primary and is not supposed to be - nothing to do here.
+
}
- // user id is not primary and is not supposed to be - nothing to do here.
+ indent -= 1;
+ if (!ok) {
+ log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_NOEXIST_PRIMARY, indent);
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ }
}
- indent -= 1;
-
- if (!ok) {
- log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_NOEXIST_PRIMARY, indent);
- return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ // Update the secret key ring
+ if (modifiedPublicKey != masterPublicKey) {
+ masterSecretKey = PGPSecretKey.replacePublicKey(masterSecretKey, modifiedPublicKey);
+ masterPublicKey = modifiedPublicKey;
+ sKR = PGPSecretKeyRing.insertSecretKey(sKR, masterSecretKey);
}
- }
- // Update the secret key ring
- if (modifiedPublicKey != masterPublicKey) {
- masterSecretKey = PGPSecretKey.replacePublicKey(masterSecretKey, modifiedPublicKey);
- masterPublicKey = modifiedPublicKey;
- sKR = PGPSecretKeyRing.insertSecretKey(sKR, masterSecretKey);
}
// 4a. For each subkey change, generate new subkey binding certificate
@@ -528,27 +649,47 @@ public class PgpKeyOperation {
log.add(LogLevel.INFO, LogType.MSG_MF_SUBKEY_CHANGE,
indent, PgpKeyHelper.convertKeyIdToHex(change.mKeyId));
- // TODO allow changes in master key? this implies generating new user id certs...
- if (change.mKeyId == masterPublicKey.getKeyID()) {
- Log.e(Constants.TAG, "changing the master key not supported");
- return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
- }
-
PGPSecretKey sKey = sKR.getSecretKey(change.mKeyId);
if (sKey == null) {
- log.add(LogLevel.ERROR, LogType.MSG_MF_SUBKEY_MISSING,
+ log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_SUBKEY_MISSING,
indent + 1, PgpKeyHelper.convertKeyIdToHex(change.mKeyId));
return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
- PGPPublicKey pKey = sKey.getPublicKey();
// expiry must not be in the past
- if (change.mExpiry != null && new Date(change.mExpiry*1000).before(new Date())) {
- log.add(LogLevel.ERROR, LogType.MSG_MF_SUBKEY_PAST_EXPIRY,
+ if (change.mExpiry != null && change.mExpiry != 0 &&
+ new Date(change.mExpiry*1000).before(new Date())) {
+ log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_PAST_EXPIRY,
indent + 1, PgpKeyHelper.convertKeyIdToHex(change.mKeyId));
return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
+ // if this is the master key, update uid certificates instead
+ if (change.mKeyId == masterPublicKey.getKeyID()) {
+ int flags = change.mFlags == null ? masterKeyFlags : change.mFlags;
+ long expiry = change.mExpiry == null ? masterKeyExpiry : change.mExpiry;
+
+ if ((flags & KeyFlags.CERTIFY_OTHER) != KeyFlags.CERTIFY_OTHER) {
+ log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_NO_CERTIFY, indent + 1);
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ }
+
+ PGPPublicKey pKey =
+ updateMasterCertificates(masterPrivateKey, masterPublicKey,
+ flags, expiry, indent, log);
+ if (pKey == null) {
+ // error log entry has already been added by updateMasterCertificates itself
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ }
+ masterSecretKey = PGPSecretKey.replacePublicKey(masterSecretKey, pKey);
+ masterPublicKey = pKey;
+ sKR = PGPSecretKeyRing.insertSecretKey(sKR, masterSecretKey);
+ continue;
+ }
+
+ // otherwise, continue working on the public key
+ PGPPublicKey pKey = sKey.getPublicKey();
+
// keep old flags, or replace with new ones
int flags = change.mFlags == null ? readKeyFlags(pKey) : change.mFlags;
long expiry;
@@ -565,7 +706,7 @@ public class PgpKeyOperation {
//noinspection unchecked
for (PGPSignature sig : new IterableIterator<PGPSignature>(pKey.getSignatures())) {
// special case: if there is a revocation, don't use expiry from before
- if (change.mExpiry == null
+ if ( (change.mExpiry == null || change.mExpiry == 0L)
&& sig.getSignatureType() == PGPSignature.SUBKEY_REVOCATION) {
expiry = 0;
}
@@ -591,7 +732,7 @@ public class PgpKeyOperation {
PGPSecretKey sKey = sKR.getSecretKey(revocation);
if (sKey == null) {
- log.add(LogLevel.ERROR, LogType.MSG_MF_SUBKEY_MISSING,
+ log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_SUBKEY_MISSING,
indent+1, PgpKeyHelper.convertKeyIdToHex(revocation));
return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
@@ -611,10 +752,16 @@ public class PgpKeyOperation {
progress(R.string.progress_modify_subkeyadd, (i-1) * (100 / saveParcel.mAddSubKeys.size()));
SaveKeyringParcel.SubkeyAdd add = saveParcel.mAddSubKeys.get(i);
- log.add(LogLevel.INFO, LogType.MSG_MF_SUBKEY_NEW, indent);
+ log.add(LogLevel.INFO, LogType.MSG_MF_SUBKEY_NEW, indent,
+ PgpKeyHelper.getAlgorithmInfo(add.mAlgorithm, add.mKeySize, add.mCurve) );
+
+ if (add.mExpiry == null) {
+ log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_NULL_EXPIRY, indent +1);
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ }
- if (add.mExpiry != null && new Date(add.mExpiry*1000).before(new Date())) {
- log.add(LogLevel.ERROR, LogType.MSG_MF_SUBKEY_PAST_EXPIRY, indent +1);
+ if (add.mExpiry > 0L && new Date(add.mExpiry*1000).before(new Date())) {
+ log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_PAST_EXPIRY, indent +1);
return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
@@ -623,9 +770,10 @@ public class PgpKeyOperation {
(i-1) * (100 / saveParcel.mAddSubKeys.size()),
i * (100 / saveParcel.mAddSubKeys.size())
);
- PGPKeyPair keyPair = createKey(add.mAlgorithm, add.mKeysize, log, indent);
+ PGPKeyPair keyPair = createKey(add, log, indent);
subProgressPop();
- if(keyPair == null) {
+ if (keyPair == null) {
+ log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_PGP, indent +1);
return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
@@ -633,21 +781,21 @@ public class PgpKeyOperation {
PGPPublicKey pKey = keyPair.getPublicKey();
PGPSignature cert = generateSubkeyBindingSignature(
masterPublicKey, masterPrivateKey, keyPair.getPrivateKey(), pKey,
- add.mFlags, add.mExpiry == null ? 0 : add.mExpiry);
+ add.mFlags, add.mExpiry);
pKey = PGPPublicKey.addSubkeyBindingCertification(pKey, cert);
PGPSecretKey sKey; {
- // define hashing and signing algos
- PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder()
- .build().get(HashAlgorithmTags.SHA1);
-
// Build key encrypter and decrypter based on passphrase
+ PGPDigestCalculator encryptorHashCalc = new JcaPGPDigestCalculatorProviderBuilder()
+ .build().get(SECRET_KEY_ENCRYPTOR_HASH_ALGO);
PBESecretKeyEncryptor keyEncryptor = new JcePBESecretKeyEncryptorBuilder(
- PGPEncryptedData.CAST5, sha1Calc)
- .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build("".toCharArray());
+ SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO, encryptorHashCalc, SECRET_KEY_ENCRYPTOR_S2K_COUNT)
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
- sKey = new PGPSecretKey(keyPair.getPrivateKey(), pKey,
- sha1Calc, false, keyEncryptor);
+ // NOTE: only SHA1 is supported for key checksum calculations.
+ PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder()
+ .build().get(HashAlgorithmTags.SHA1);
+ sKey = new PGPSecretKey(keyPair.getPrivateKey(), pKey, sha1Calc, false, keyEncryptor);
}
log.add(LogLevel.DEBUG, LogType.MSG_MF_SUBKEY_NEW_ID,
@@ -662,21 +810,67 @@ public class PgpKeyOperation {
if (saveParcel.mNewPassphrase != null) {
progress(R.string.progress_modify_passphrase, 90);
log.add(LogLevel.INFO, LogType.MSG_MF_PASSPHRASE, indent);
- PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder().build()
- .get(HashAlgorithmTags.SHA1);
+ indent += 1;
+
+ PGPDigestCalculator encryptorHashCalc = new JcaPGPDigestCalculatorProviderBuilder().build()
+ .get(SECRET_KEY_ENCRYPTOR_HASH_ALGO);
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)
+ SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO, encryptorHashCalc, SECRET_KEY_ENCRYPTOR_S2K_COUNT)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
saveParcel.mNewPassphrase.toCharArray());
- sKR = PGPSecretKeyRing.copyWithNewPassword(sKR, keyDecryptor, keyEncryptorNew);
+ // noinspection unchecked
+ for (PGPSecretKey sKey : new IterableIterator<PGPSecretKey>(sKR.getSecretKeys())) {
+ log.add(LogLevel.DEBUG, LogType.MSG_MF_PASSPHRASE_KEY, indent,
+ PgpKeyHelper.convertKeyIdToHex(sKey.getKeyID()));
+
+ boolean ok = false;
+
+ try {
+ // try to set new passphrase
+ sKey = PGPSecretKey.copyWithNewPassword(sKey, keyDecryptor, keyEncryptorNew);
+ ok = true;
+ } catch (PGPException e) {
+
+ // if this is the master key, error!
+ if (sKey.getKeyID() == masterPublicKey.getKeyID()) {
+ log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_PASSPHRASE_MASTER, indent+1);
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, 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 {
+ log.add(LogLevel.DEBUG, LogType.MSG_MF_PASSPHRASE_EMPTY_RETRY, indent+1);
+ PBESecretKeyDecryptor emptyDecryptor =
+ new JcePBESecretKeyDecryptorBuilder().setProvider(
+ Constants.BOUNCY_CASTLE_PROVIDER_NAME).build("".toCharArray());
+ sKey = PGPSecretKey.copyWithNewPassword(sKey, emptyDecryptor, keyEncryptorNew);
+ ok = true;
+ } catch (PGPException e2) {
+ // non-fatal but not ok, handled below
+ }
+ }
+
+ if (!ok) {
+ // for a subkey, it's merely a warning
+ log.add(LogLevel.WARN, LogType.MSG_MF_PASSPHRASE_FAIL, indent+1,
+ PgpKeyHelper.convertKeyIdToHex(sKey.getKeyID()));
+ continue;
+ }
+
+ sKR = PGPSecretKeyRing.insertSecretKey(sKR, sKey);
+
+ }
+
+ indent -= 1;
}
- // This one must only be thrown by
} catch (IOException e) {
+ Log.e(Constants.TAG, "encountered IOException while modifying key", e);
log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_ENCODE, indent+1);
return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
} catch (PGPException e) {
@@ -684,6 +878,7 @@ public class PgpKeyOperation {
log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_PGP, indent+1);
return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
} catch (SignatureException e) {
+ Log.e(Constants.TAG, "encountered SignatureException while modifying key", e);
log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_SIG, indent+1);
return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
@@ -694,21 +889,121 @@ public class PgpKeyOperation {
}
+ /** Update all (non-revoked) uid signatures with new flags and expiry time. */
+ private static PGPPublicKey updateMasterCertificates(
+ PGPPrivateKey masterPrivateKey, PGPPublicKey masterPublicKey,
+ int flags, long expiry, int indent, OperationLog log)
+ throws PGPException, IOException, SignatureException {
+
+ // keep track if we actually changed one
+ boolean ok = false;
+ log.add(LogLevel.DEBUG, LogType.MSG_MF_MASTER, indent);
+ indent += 1;
+
+ PGPPublicKey modifiedPublicKey = masterPublicKey;
+
+ // we work on the modifiedPublicKey here, to respect new or newly revoked uids
+ // noinspection unchecked
+ for (String userId : new IterableIterator<String>(modifiedPublicKey.getUserIDs())) {
+ boolean isRevoked = false;
+ PGPSignature currentCert = null;
+ // noinspection unchecked
+ for (PGPSignature cert : new IterableIterator<PGPSignature>(
+ modifiedPublicKey.getSignaturesForID(userId))) {
+ if (cert.getKeyID() != masterPublicKey.getKeyID()) {
+ // foreign certificate?! error error error
+ log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_INTEGRITY, indent);
+ return null;
+ }
+ // we know from canonicalization that if there is any revocation here, it
+ // is valid and not superseded by a newer certification.
+ if (cert.getSignatureType() == PGPSignature.CERTIFICATION_REVOCATION) {
+ isRevoked = true;
+ continue;
+ }
+ // we know from canonicalization that there is only one binding
+ // certification here, so we can just work with the first one.
+ if (cert.getSignatureType() == PGPSignature.NO_CERTIFICATION ||
+ cert.getSignatureType() == PGPSignature.CASUAL_CERTIFICATION ||
+ cert.getSignatureType() == PGPSignature.POSITIVE_CERTIFICATION ||
+ cert.getSignatureType() == PGPSignature.DEFAULT_CERTIFICATION) {
+ currentCert = cert;
+ }
+ }
+
+ if (currentCert == null) {
+ // no certificate found?! error error error
+ log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_INTEGRITY, indent);
+ return null;
+ }
+
+ // we definitely should not update certifications of revoked keys, so just leave it.
+ if (isRevoked) {
+ continue;
+ }
+
+ // add shiny new user id certificate
+ boolean isPrimary = currentCert.getHashedSubPackets() != null &&
+ currentCert.getHashedSubPackets().isPrimaryUserID();
+ modifiedPublicKey = PGPPublicKey.removeCertification(
+ modifiedPublicKey, userId, currentCert);
+ PGPSignature newCert = generateUserIdSignature(
+ masterPrivateKey, masterPublicKey, userId, isPrimary, flags, expiry);
+ modifiedPublicKey = PGPPublicKey.addCertification(
+ modifiedPublicKey, userId, newCert);
+ ok = true;
+
+ }
+
+ if (!ok) {
+ // might happen, theoretically, if there is a key with no uid..
+ log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_MASTER_NONE, indent);
+ return null;
+ }
+
+ return modifiedPublicKey;
+
+ }
+
private static PGPSignature generateUserIdSignature(
- PGPPrivateKey masterPrivateKey, PGPPublicKey pKey, String userId, boolean primary, int flags)
+ PGPPrivateKey masterPrivateKey, PGPPublicKey pKey, String userId, boolean primary,
+ int flags, long expiry)
throws IOException, PGPException, SignatureException {
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
- pKey.getAlgorithm(), PGPUtil.SHA1)
+ masterPrivateKey.getPublicKeyPacket().getAlgorithm(), HashAlgorithmTags.SHA512)
.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);
- subHashedPacketsGen.setKeyFlags(false, flags);
- sGen.setHashedSubpackets(subHashedPacketsGen.generate());
+
+ PGPSignatureSubpacketGenerator hashedPacketsGen = new PGPSignatureSubpacketGenerator();
+ {
+ /*
+ * From RFC about critical subpackets:
+ * If a subpacket is encountered that is
+ * marked critical but is unknown to the evaluating software, the
+ * evaluator SHOULD consider the signature to be in error.
+ * An evaluator may "recognize" a subpacket, but not implement it. The
+ * purpose of the critical bit is to allow the signer to tell an
+ * evaluator that it would prefer a new, unknown feature to generate an
+ * error than be ignored.
+ */
+ /* non-critical subpackets: */
+ hashedPacketsGen.setPreferredSymmetricAlgorithms(false, PREFERRED_SYMMETRIC_ALGORITHMS);
+ hashedPacketsGen.setPreferredHashAlgorithms(false, PREFERRED_HASH_ALGORITHMS);
+ hashedPacketsGen.setPreferredCompressionAlgorithms(false, PREFERRED_COMPRESSION_ALGORITHMS);
+ hashedPacketsGen.setPrimaryUserID(false, primary);
+
+ /* critical subpackets: we consider those important for a modern pgp implementation */
+ hashedPacketsGen.setSignatureCreationTime(true, new Date());
+ // Request that senders add the MDC to the message (authenticate unsigned messages)
+ hashedPacketsGen.setFeature(true, Features.FEATURE_MODIFICATION_DETECTION);
+ hashedPacketsGen.setKeyFlags(true, flags);
+ if (expiry > 0) {
+ hashedPacketsGen.setKeyExpirationTime(
+ true, expiry - pKey.getCreationTime().getTime() / 1000);
+ }
+ }
+
+ sGen.setHashedSubpackets(hashedPacketsGen.generate());
sGen.init(PGPSignature.POSITIVE_CERTIFICATION, masterPrivateKey);
return sGen.generateCertification(userId, pKey);
}
@@ -717,11 +1012,11 @@ public class PgpKeyOperation {
PGPPrivateKey masterPrivateKey, PGPPublicKey pKey, String userId)
throws IOException, PGPException, SignatureException {
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
- pKey.getAlgorithm(), PGPUtil.SHA1)
+ masterPrivateKey.getPublicKeyPacket().getAlgorithm(), HashAlgorithmTags.SHA512)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
PGPSignatureSubpacketGenerator subHashedPacketsGen = new PGPSignatureSubpacketGenerator();
- subHashedPacketsGen.setSignatureCreationTime(false, new Date());
+ subHashedPacketsGen.setSignatureCreationTime(true, new Date());
sGen.setHashedSubpackets(subHashedPacketsGen.generate());
sGen.init(PGPSignature.CERTIFICATION_REVOCATION, masterPrivateKey);
return sGen.generateCertification(userId, pKey);
@@ -731,11 +1026,11 @@ public class PgpKeyOperation {
PGPPublicKey masterPublicKey, PGPPrivateKey masterPrivateKey, PGPPublicKey pKey)
throws IOException, PGPException, SignatureException {
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
- pKey.getAlgorithm(), PGPUtil.SHA1)
+ masterPublicKey.getAlgorithm(), HashAlgorithmTags.SHA512)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
PGPSignatureSubpacketGenerator subHashedPacketsGen = new PGPSignatureSubpacketGenerator();
- subHashedPacketsGen.setSignatureCreationTime(false, new Date());
+ subHashedPacketsGen.setSignatureCreationTime(true, new Date());
sGen.setHashedSubpackets(subHashedPacketsGen.generate());
// Generate key revocation or subkey revocation, depending on master/subkey-ness
if (masterPublicKey.getKeyID() == pKey.getKeyID()) {
@@ -765,38 +1060,38 @@ public class PgpKeyOperation {
throws IOException, PGPException, SignatureException {
// date for signing
- Date todayDate = new Date();
+ Date creationTime = new Date();
+
PGPSignatureSubpacketGenerator unhashedPacketsGen = new PGPSignatureSubpacketGenerator();
// If this key can sign, we need a primary key binding signature
if ((flags & KeyFlags.SIGN_DATA) > 0) {
// cross-certify signing keys
PGPSignatureSubpacketGenerator subHashedPacketsGen = new PGPSignatureSubpacketGenerator();
- subHashedPacketsGen.setSignatureCreationTime(false, todayDate);
+ subHashedPacketsGen.setSignatureCreationTime(false, creationTime);
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
- pKey.getAlgorithm(), PGPUtil.SHA1)
+ pKey.getAlgorithm(), HashAlgorithmTags.SHA512)
.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);
+ unhashedPacketsGen.setEmbeddedSignature(true, certification);
}
PGPSignatureSubpacketGenerator hashedPacketsGen;
{
hashedPacketsGen = new PGPSignatureSubpacketGenerator();
- hashedPacketsGen.setSignatureCreationTime(false, todayDate);
- hashedPacketsGen.setKeyFlags(false, flags);
- }
-
- if (expiry > 0) {
- long creationTime = pKey.getCreationTime().getTime() / 1000;
- hashedPacketsGen.setKeyExpirationTime(false, expiry - creationTime);
+ hashedPacketsGen.setSignatureCreationTime(true, creationTime);
+ hashedPacketsGen.setKeyFlags(true, flags);
+ if (expiry > 0) {
+ hashedPacketsGen.setKeyExpirationTime(true,
+ expiry - pKey.getCreationTime().getTime() / 1000);
+ }
}
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
- pKey.getAlgorithm(), PGPUtil.SHA1)
+ masterPublicKey.getAlgorithm(), HashAlgorithmTags.SHA512)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
sGen.init(PGPSignature.SUBKEY_BINDING, masterPrivateKey);