aboutsummaryrefslogtreecommitdiffstats
path: root/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp
diff options
context:
space:
mode:
Diffstat (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp')
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/KeyRing.java6
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java10
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java7
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java267
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java147
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java96
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedKeyRing.java20
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedPublicKeyRing.java26
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKey.java12
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKeyRing.java4
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java28
11 files changed, 417 insertions, 206 deletions
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/KeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/KeyRing.java
index 47b827677..129ffba3e 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/KeyRing.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/KeyRing.java
@@ -22,8 +22,10 @@ public abstract class KeyRing {
abstract public String getPrimaryUserId() throws PgpGeneralException;
- public String[] getSplitPrimaryUserId() throws PgpGeneralException {
- return splitUserId(getPrimaryUserId());
+ abstract public String getPrimaryUserIdWithFallback() throws PgpGeneralException;
+
+ public String[] getSplitPrimaryUserIdWithFallback() throws PgpGeneralException {
+ return splitUserId(getPrimaryUserIdWithFallback());
}
abstract public boolean isRevoked() throws PgpGeneralException;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java
index a5ccfbd3b..db9e2c6c6 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java
@@ -258,7 +258,7 @@ public class PgpDecryptVerify {
continue;
}
// get subkey which has been used for this encryption packet
- secretEncryptionKey = secretKeyRing.getSubKey(encData.getKeyID());
+ secretEncryptionKey = secretKeyRing.getSecretKey(encData.getKeyID());
if (secretEncryptionKey == null) {
// continue with the next packet in the while loop
continue;
@@ -393,7 +393,7 @@ public class PgpDecryptVerify {
signingRing = mProviderHelper.getWrappedPublicKeyRing(
KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(sigKeyId)
);
- signingKey = signingRing.getSubkey(sigKeyId);
+ signingKey = signingRing.getPublicKey(sigKeyId);
signatureIndex = i;
} catch (ProviderHelper.NotFoundException e) {
Log.d(Constants.TAG, "key not found!");
@@ -409,7 +409,7 @@ public class PgpDecryptVerify {
signatureResultBuilder.knownKey(true);
signatureResultBuilder.keyId(signingRing.getMasterKeyId());
try {
- signatureResultBuilder.userId(signingRing.getPrimaryUserId());
+ signatureResultBuilder.userId(signingRing.getPrimaryUserIdWithFallback());
} catch(PgpGeneralException e) {
Log.d(Constants.TAG, "No primary user id in key " + signingRing.getMasterKeyId());
}
@@ -578,7 +578,7 @@ public class PgpDecryptVerify {
signingRing = mProviderHelper.getWrappedPublicKeyRing(
KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(sigKeyId)
);
- signingKey = signingRing.getSubkey(sigKeyId);
+ signingKey = signingRing.getPublicKey(sigKeyId);
signatureIndex = i;
} catch (ProviderHelper.NotFoundException e) {
Log.d(Constants.TAG, "key not found!");
@@ -596,7 +596,7 @@ public class PgpDecryptVerify {
signatureResultBuilder.knownKey(true);
signatureResultBuilder.keyId(signingRing.getMasterKeyId());
try {
- signatureResultBuilder.userId(signingRing.getPrimaryUserId());
+ signatureResultBuilder.userId(signingRing.getPrimaryUserIdWithFallback());
} catch(PgpGeneralException e) {
Log.d(Constants.TAG, "No primary user id in key " + signingRing.getMasterKeyId());
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java
index 969ff1de8..0ceefc4d9 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java
@@ -24,6 +24,7 @@ import android.content.pm.PackageManager.NameNotFoundException;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.helper.Preferences;
import org.sufficientlysecure.keychain.util.Log;
import java.io.File;
@@ -60,7 +61,11 @@ public class PgpHelper {
}
public static String getFullVersion(Context context) {
- return "OpenKeychain v" + getVersion(context);
+ if(Preferences.getPreferences(context).getConcealPgpApplication()){
+ return "";
+ } else {
+ return "OpenKeychain v" + getVersion(context);
+ }
}
// public static final class content {
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 9a08290e4..69244ca14 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
@@ -65,10 +65,8 @@ import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.security.SignatureException;
import java.util.Arrays;
-import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
-import java.util.TimeZone;
/**
* This class is the single place where ALL operations that actually modify a PGP public or secret
@@ -104,11 +102,12 @@ public class PgpKeyOperation {
}
/** Creates new secret key. */
- private PGPKeyPair createKey(int algorithmChoice, int keySize) throws PgpGeneralMsgIdException {
+ private PGPKeyPair createKey(int algorithmChoice, int keySize, OperationLog log, int indent) {
try {
if (keySize < 512) {
- throw new PgpGeneralMsgIdException(R.string.error_key_size_minimum512bit);
+ log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_KEYSIZE_512, indent);
+ return null;
}
int algorithm;
@@ -143,7 +142,8 @@ public class PgpKeyOperation {
}
default: {
- throw new PgpGeneralMsgIdException(R.string.error_unknown_algorithm_choice);
+ log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_UNKNOWN_ALGO, indent);
+ return null;
}
}
@@ -157,7 +157,9 @@ public class PgpKeyOperation {
} catch(InvalidAlgorithmParameterException e) {
throw new RuntimeException(e);
} catch(PGPException e) {
- throw new PgpGeneralMsgIdException(R.string.msg_mf_error_pgp, e);
+ Log.e(Constants.TAG, "internal pgp error", e);
+ log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_INTERNAL_PGP, indent);
+ return null;
}
}
@@ -166,20 +168,36 @@ public class PgpKeyOperation {
try {
- log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_KEYID, indent);
+ log.add(LogLevel.START, LogType.MSG_CR, indent);
indent += 1;
updateProgress(R.string.progress_building_key, 0, 100);
- if (saveParcel.addSubKeys == null || saveParcel.addSubKeys.isEmpty()) {
+ if (saveParcel.mAddSubKeys.isEmpty()) {
log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_NO_MASTER, indent);
return null;
}
- SubkeyAdd add = saveParcel.addSubKeys.remove(0);
- PGPKeyPair keyPair = createKey(add.mAlgorithm, add.mKeysize);
+ if (saveParcel.mAddUserIds.isEmpty()) {
+ log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_NO_USER_ID, indent);
+ return null;
+ }
+
+ SubkeyAdd add = saveParcel.mAddSubKeys.remove(0);
+ if ((add.mFlags & KeyFlags.CERTIFY_OTHER) != KeyFlags.CERTIFY_OTHER) {
+ log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_NO_CERTIFY, indent);
+ return null;
+ }
if (add.mAlgorithm == Constants.choice.algorithm.elgamal) {
- throw new PgpGeneralMsgIdException(R.string.error_master_key_must_not_be_el_gamal);
+ log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_MASTER_ELGAMAL, indent);
+ return null;
+ }
+
+ PGPKeyPair keyPair = createKey(add.mAlgorithm, add.mKeysize, log, indent);
+
+ // return null if this failed (an error will already have been logged by createKey)
+ if (keyPair == null) {
+ return null;
}
// define hashing and signing algos
@@ -195,17 +213,15 @@ public class PgpKeyOperation {
PGPSecretKeyRing sKR = new PGPSecretKeyRing(
masterSecretKey.getEncoded(), new JcaKeyFingerprintCalculator());
- return internal(sKR, masterSecretKey, saveParcel, "", log, indent);
+ return internal(sKR, masterSecretKey, add.mFlags, saveParcel, "", log, indent);
} catch (PGPException e) {
+ log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_INTERNAL_PGP, indent);
Log.e(Constants.TAG, "pgp error encoding key", e);
return null;
} catch (IOException e) {
Log.e(Constants.TAG, "io error encoding key", e);
return null;
- } catch (PgpGeneralMsgIdException e) {
- Log.e(Constants.TAG, "pgp msg id error", e);
- return null;
}
}
@@ -257,11 +273,16 @@ public class PgpKeyOperation {
return null;
}
- return internal(sKR, masterSecretKey, saveParcel, passphrase, log, indent);
+ // 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;
+
+ return internal(sKR, masterSecretKey, masterKeyFlags, saveParcel, passphrase, log, indent);
}
private UncachedKeyRing internal(PGPSecretKeyRing sKR, PGPSecretKey masterSecretKey,
+ int masterKeyFlags,
SaveKeyringParcel saveParcel, String passphrase,
OperationLog log, int indent) {
@@ -289,18 +310,24 @@ public class PgpKeyOperation {
PGPPublicKey modifiedPublicKey = masterPublicKey;
// 2a. Add certificates for new user ids
- for (String userId : saveParcel.addUserIds) {
+ for (String userId : saveParcel.mAddUserIds) {
log.add(LogLevel.INFO, LogType.MSG_MF_UID_ADD, indent);
+ if (userId.equals("")) {
+ log.add(LogLevel.ERROR, LogType.MSG_MF_UID_ERROR_EMPTY, indent+1);
+ return null;
+ }
+
// 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 it's not a self cert, never mind
if (cert.getKeyID() != masterPublicKey.getKeyID()) {
- continue;
+ // foreign certificate?! error error error
+ log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_INTEGRITY, indent);
+ return null;
}
if (cert.getSignatureType() == PGPSignature.CERTIFICATION_REVOCATION
|| cert.getSignatureType() == PGPSignature.NO_CERTIFICATION
@@ -314,27 +341,31 @@ public class PgpKeyOperation {
}
// if it's supposed to be primary, we can do that here as well
- boolean isPrimary = saveParcel.changePrimaryUserId != null
- && userId.equals(saveParcel.changePrimaryUserId);
+ boolean isPrimary = saveParcel.mChangePrimaryUserId != null
+ && userId.equals(saveParcel.mChangePrimaryUserId);
// generate and add new certificate
PGPSignature cert = generateUserIdSignature(masterPrivateKey,
- masterPublicKey, userId, isPrimary);
- modifiedPublicKey = PGPPublicKey.addCertification(masterPublicKey, userId, cert);
+ masterPublicKey, userId, isPrimary, masterKeyFlags);
+ modifiedPublicKey = PGPPublicKey.addCertification(modifiedPublicKey, userId, cert);
}
// 2b. Add revocations for revoked user ids
- for (String userId : saveParcel.revokeUserIds) {
+ for (String userId : saveParcel.mRevokeUserIds) {
log.add(LogLevel.INFO, LogType.MSG_MF_UID_REVOKE, indent);
- // a duplicate revocatin will be removed during canonicalization, so no need to
+ // 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(masterPublicKey, userId, cert);
+ modifiedPublicKey = PGPPublicKey.addCertification(modifiedPublicKey, userId, cert);
}
// 3. If primary user id changed, generate new certificates for both old and new
- if (saveParcel.changePrimaryUserId != null) {
+ if (saveParcel.mChangePrimaryUserId != null) {
+
+ // 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
@@ -343,10 +374,11 @@ public class PgpKeyOperation {
PGPSignature currentCert = null;
// noinspection unchecked
for (PGPSignature cert : new IterableIterator<PGPSignature>(
- masterPublicKey.getSignaturesForID(userId))) {
- // if it's not a self cert, never mind
+ modifiedPublicKey.getSignaturesForID(userId))) {
if (cert.getKeyID() != masterPublicKey.getKeyID()) {
- continue;
+ // 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.
@@ -373,7 +405,7 @@ public class PgpKeyOperation {
// 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.changePrimaryUserId)) {
+ if (userId.equals(saveParcel.mChangePrimaryUserId)) {
log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_REVOKED_PRIMARY, indent);
return null;
}
@@ -383,14 +415,16 @@ public class PgpKeyOperation {
// if this is~ the/a primary user id
if (currentCert.hasSubpackets() && currentCert.getHashedSubPackets().isPrimaryUserID()) {
// if it's the one we want, just leave it as is
- if (userId.equals(saveParcel.changePrimaryUserId)) {
+ 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);
+ masterPrivateKey, masterPublicKey, userId, false, masterKeyFlags);
modifiedPublicKey = PGPPublicKey.addCertification(
modifiedPublicKey, userId, newCert);
continue;
@@ -399,20 +433,28 @@ public class PgpKeyOperation {
// if we are here, this is not currently a primary user id
// if it should be
- if (userId.equals(saveParcel.changePrimaryUserId)) {
+ 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);
+ 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.
}
+ indent -= 1;
+
+ if (!ok) {
+ log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_NOEXIST_PRIMARY, indent);
+ return null;
+ }
}
// Update the secret key ring
@@ -423,9 +465,16 @@ public class PgpKeyOperation {
}
// 4a. For each subkey change, generate new subkey binding certificate
- for (SaveKeyringParcel.SubkeyChange change : saveParcel.changeSubKeys) {
+ for (SaveKeyringParcel.SubkeyChange change : saveParcel.mChangeSubKeys) {
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 null;
+ }
+
PGPSecretKey sKey = sKR.getSecretKey(change.mKeyId);
if (sKey == null) {
log.add(LogLevel.ERROR, LogType.MSG_MF_SUBKEY_MISSING,
@@ -434,22 +483,45 @@ public class PgpKeyOperation {
}
PGPPublicKey pKey = sKey.getPublicKey();
- if (change.mExpiry != null && new Date(change.mExpiry).before(new Date())) {
+ // 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,
indent + 1, PgpKeyHelper.convertKeyIdToHex(change.mKeyId));
return null;
}
- // generate and add new signature. we can be sloppy here and just leave the old one,
- // it will be removed during canonicalization
+ // keep old flags, or replace with new ones
+ int flags = change.mFlags == null ? readKeyFlags(pKey) : change.mFlags;
+ long expiry;
+ if (change.mExpiry == null) {
+ long valid = pKey.getValidSeconds();
+ expiry = valid == 0
+ ? 0
+ : pKey.getCreationTime().getTime() / 1000 + pKey.getValidSeconds();
+ } else {
+ expiry = change.mExpiry;
+ }
+
+ // drop all old signatures, they will be superseded by the new one
+ //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
+ && sig.getSignatureType() == PGPSignature.SUBKEY_REVOCATION) {
+ expiry = 0;
+ }
+ pKey = PGPPublicKey.removeCertification(pKey, sig);
+ }
+
+ // generate and add new signature
PGPSignature sig = generateSubkeyBindingSignature(masterPublicKey, masterPrivateKey,
- sKey, pKey, change.mFlags, change.mExpiry, passphrase);
+ sKey, pKey, flags, expiry, passphrase);
pKey = PGPPublicKey.addCertification(pKey, sig);
sKR = PGPSecretKeyRing.insertSecretKey(sKR, PGPSecretKey.replacePublicKey(sKey, pKey));
}
// 4b. For each subkey revocation, generate new subkey revocation certificate
- for (long revocation : saveParcel.revokeSubKeys) {
+ for (long revocation : saveParcel.mRevokeSubKeys) {
log.add(LogLevel.INFO, LogType.MSG_MF_SUBKEY_REVOKE,
indent, PgpKeyHelper.convertKeyIdToHex(revocation));
PGPSecretKey sKey = sKR.getSecretKey(revocation);
@@ -468,52 +540,51 @@ public class PgpKeyOperation {
}
// 5. Generate and add new subkeys
- for (SaveKeyringParcel.SubkeyAdd add : saveParcel.addSubKeys) {
- try {
+ for (SaveKeyringParcel.SubkeyAdd add : saveParcel.mAddSubKeys) {
- if (add.mExpiry != null && new Date(add.mExpiry).before(new Date())) {
- log.add(LogLevel.ERROR, LogType.MSG_MF_SUBKEY_PAST_EXPIRY, indent +1);
- return null;
- }
-
- log.add(LogLevel.INFO, LogType.MSG_MF_SUBKEY_NEW, indent);
-
- // generate a new secret key (privkey only for now)
- PGPKeyPair keyPair = createKey(add.mAlgorithm, add.mKeysize);
-
- // add subkey binding signature (making this a sub rather than master key)
- PGPPublicKey pKey = keyPair.getPublicKey();
- PGPSignature cert = generateSubkeyBindingSignature(
- masterPublicKey, masterPrivateKey, keyPair.getPrivateKey(), pKey,
- add.mFlags, add.mExpiry);
- pKey = PGPPublicKey.addSubkeyBindingCertification(pKey, cert);
+ if (add.mExpiry != null && new Date(add.mExpiry*1000).before(new Date())) {
+ log.add(LogLevel.ERROR, LogType.MSG_MF_SUBKEY_PAST_EXPIRY, indent +1);
+ return null;
+ }
- PGPSecretKey sKey; {
- // define hashing and signing algos
- PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder()
- .build().get(HashAlgorithmTags.SHA1);
+ log.add(LogLevel.INFO, LogType.MSG_MF_SUBKEY_NEW, indent);
- // Build key encrypter and decrypter based on passphrase
- PBESecretKeyEncryptor keyEncryptor = new JcePBESecretKeyEncryptorBuilder(
- PGPEncryptedData.CAST5, sha1Calc)
- .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build("".toCharArray());
+ // generate a new secret key (privkey only for now)
+ PGPKeyPair keyPair = createKey(add.mAlgorithm, add.mKeysize, log, indent);
+ if(keyPair == null) {
+ return null;
+ }
- sKey = new PGPSecretKey(keyPair.getPrivateKey(), pKey,
- sha1Calc, false, keyEncryptor);
- }
+ // add subkey binding signature (making this a sub rather than master key)
+ PGPPublicKey pKey = keyPair.getPublicKey();
+ PGPSignature cert = generateSubkeyBindingSignature(
+ masterPublicKey, masterPrivateKey, keyPair.getPrivateKey(), pKey,
+ add.mFlags, add.mExpiry == null ? 0 : 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
+ PBESecretKeyEncryptor keyEncryptor = new JcePBESecretKeyEncryptorBuilder(
+ PGPEncryptedData.CAST5, sha1Calc)
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build("".toCharArray());
+
+ sKey = new PGPSecretKey(keyPair.getPrivateKey(), pKey,
+ sha1Calc, false, keyEncryptor);
+ }
- log.add(LogLevel.DEBUG, LogType.MSG_MF_SUBKEY_NEW_ID,
- indent+1, PgpKeyHelper.convertKeyIdToHex(sKey.getKeyID()));
+ log.add(LogLevel.DEBUG, LogType.MSG_MF_SUBKEY_NEW_ID,
+ indent+1, PgpKeyHelper.convertKeyIdToHex(sKey.getKeyID()));
- sKR = PGPSecretKeyRing.insertSecretKey(sKR, sKey);
+ sKR = PGPSecretKeyRing.insertSecretKey(sKR, sKey);
- } catch (PgpGeneralMsgIdException e) {
- return null;
- }
}
// 6. If requested, change passphrase
- if (saveParcel.newPassphrase != null) {
+ if (saveParcel.mNewPassphrase != null) {
log.add(LogLevel.INFO, LogType.MSG_MF_PASSPHRASE, indent);
PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder().build()
.get(HashAlgorithmTags.SHA1);
@@ -523,7 +594,7 @@ public class PgpKeyOperation {
PBESecretKeyEncryptor keyEncryptorNew = new JcePBESecretKeyEncryptorBuilder(
PGPEncryptedData.CAST5, sha1Calc)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
- saveParcel.newPassphrase.toCharArray());
+ saveParcel.mNewPassphrase.toCharArray());
sKR = PGPSecretKeyRing.copyWithNewPassword(sKR, keyDecryptor, keyEncryptorNew);
}
@@ -533,6 +604,7 @@ public class PgpKeyOperation {
log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_ENCODE, indent+1);
return null;
} catch (PGPException e) {
+ Log.e(Constants.TAG, "encountered pgp error while modifying key", e);
log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_PGP, indent+1);
return null;
} catch (SignatureException e) {
@@ -546,7 +618,7 @@ public class PgpKeyOperation {
}
private static PGPSignature generateUserIdSignature(
- PGPPrivateKey masterPrivateKey, PGPPublicKey pKey, String userId, boolean primary)
+ PGPPrivateKey masterPrivateKey, PGPPublicKey pKey, String userId, boolean primary, int flags)
throws IOException, PGPException, SignatureException {
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
pKey.getAlgorithm(), PGPUtil.SHA1)
@@ -558,6 +630,7 @@ public class PgpKeyOperation {
subHashedPacketsGen.setPreferredHashAlgorithms(true, PREFERRED_HASH_ALGORITHMS);
subHashedPacketsGen.setPreferredCompressionAlgorithms(true, PREFERRED_COMPRESSION_ALGORITHMS);
subHashedPacketsGen.setPrimaryUserID(false, primary);
+ subHashedPacketsGen.setKeyFlags(false, flags);
sGen.setHashedSubpackets(subHashedPacketsGen.generate());
sGen.init(PGPSignature.POSITIVE_CERTIFICATION, masterPrivateKey);
return sGen.generateCertification(userId, pKey);
@@ -599,7 +672,7 @@ public class PgpKeyOperation {
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 IOException, PGPException, SignatureException {
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder()
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
@@ -611,7 +684,7 @@ public class PgpKeyOperation {
private static PGPSignature generateSubkeyBindingSignature(
PGPPublicKey masterPublicKey, PGPPrivateKey masterPrivateKey,
- PGPPrivateKey subPrivateKey, PGPPublicKey pKey, int flags, Long expiry)
+ PGPPrivateKey subPrivateKey, PGPPublicKey pKey, int flags, long expiry)
throws IOException, PGPException, SignatureException {
// date for signing
@@ -640,17 +713,9 @@ public class PgpKeyOperation {
hashedPacketsGen.setKeyFlags(false, flags);
}
- if (expiry != null) {
- Calendar creationDate = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
- creationDate.setTime(pKey.getCreationTime());
-
- // (Just making sure there's no programming error here, this MUST have been checked above!)
- if (new Date(expiry).before(todayDate)) {
- throw new RuntimeException("Bad subkey creation date, this is a bug!");
- }
- hashedPacketsGen.setKeyExpirationTime(false, expiry - creationDate.getTimeInMillis());
- } else {
- hashedPacketsGen.setKeyExpirationTime(false, 0);
+ if (expiry > 0) {
+ long creationTime = pKey.getCreationTime().getTime() / 1000;
+ hashedPacketsGen.setKeyExpirationTime(false, expiry - creationTime);
}
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
@@ -665,4 +730,22 @@ public class PgpKeyOperation {
}
+ /** Returns all flags valid for this key.
+ *
+ * This method does not do any validity checks on the signature, so it should not be used on
+ * a non-canonicalized key!
+ *
+ */
+ private static int readKeyFlags(PGPPublicKey key) {
+ int flags = 0;
+ //noinspection unchecked
+ for(PGPSignature sig : new IterableIterator<PGPSignature>(key.getSignatures())) {
+ if (!sig.hasSubpackets()) {
+ continue;
+ }
+ flags |= sig.getHashedSubPackets().getKeyFlags();
+ }
+ return flags;
+ }
+
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java
index 441e2762a..8838075cd 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java
@@ -14,6 +14,7 @@ import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.spongycastle.openpgp.PGPSignature;
import org.spongycastle.openpgp.PGPSignatureList;
import org.spongycastle.openpgp.PGPUtil;
+import org.spongycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.service.OperationResultParcel.OperationLog;
@@ -22,12 +23,11 @@ import org.sufficientlysecure.keychain.service.OperationResultParcel.LogType;
import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.Log;
-import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.HashSet;
@@ -81,11 +81,15 @@ public class UncachedKeyRing {
return new UncachedPublicKey(mRing.getPublicKey());
}
+ public UncachedPublicKey getPublicKey(long keyId) {
+ return new UncachedPublicKey(mRing.getPublicKey(keyId));
+ }
+
public Iterator<UncachedPublicKey> getPublicKeys() {
final Iterator<PGPPublicKey> it = mRing.getPublicKeys();
return new Iterator<UncachedPublicKey>() {
public void remove() {
- it.remove();
+ throw new UnsupportedOperationException();
}
public UncachedPublicKey next() {
return new UncachedPublicKey(it.next());
@@ -115,42 +119,41 @@ public class UncachedKeyRing {
public static UncachedKeyRing decodeFromData(byte[] data)
throws PgpGeneralException, IOException {
- BufferedInputStream bufferedInput =
- new BufferedInputStream(new ByteArrayInputStream(data));
- if (bufferedInput.available() > 0) {
- InputStream in = PGPUtil.getDecoderStream(bufferedInput);
- PGPObjectFactory objectFactory = new PGPObjectFactory(in);
- // get first object in block
- Object obj;
- if ((obj = objectFactory.nextObject()) != null && obj instanceof PGPKeyRing) {
- return new UncachedKeyRing((PGPKeyRing) obj);
- } else {
- throw new PgpGeneralException("Object not recognized as PGPKeyRing!");
- }
- } else {
+ List<UncachedKeyRing> parsed = fromStream(new ByteArrayInputStream(data));
+
+ if (parsed.isEmpty()) {
throw new PgpGeneralException("Object not recognized as PGPKeyRing!");
}
+ if (parsed.size() > 1) {
+ throw new PgpGeneralException(
+ "Expected single keyring in stream, found " + parsed.size());
+ }
+
+ return parsed.get(0);
+
}
public static List<UncachedKeyRing> fromStream(InputStream stream)
throws PgpGeneralException, IOException {
- PGPObjectFactory objectFactory = new PGPObjectFactory(PGPUtil.getDecoderStream(stream));
-
List<UncachedKeyRing> result = new Vector<UncachedKeyRing>();
- // go through all objects in this block
- Object obj;
- while ((obj = objectFactory.nextObject()) != null) {
- Log.d(Constants.TAG, "Found class: " + obj.getClass());
+ while(stream.available() > 0) {
+ PGPObjectFactory objectFactory = new PGPObjectFactory(PGPUtil.getDecoderStream(stream));
- if (obj instanceof PGPKeyRing) {
+ // go through all objects in this block
+ Object obj;
+ while ((obj = objectFactory.nextObject()) != null) {
+ Log.d(Constants.TAG, "Found class: " + obj.getClass());
+ if (!(obj instanceof PGPKeyRing)) {
+ throw new PgpGeneralException(
+ "Bad object of type " + obj.getClass().getName() + " in stream!");
+ }
result.add(new UncachedKeyRing((PGPKeyRing) obj));
- } else {
- Log.e(Constants.TAG, "Object not recognized as PGPKeyRing!");
}
}
+
return result;
}
@@ -189,7 +192,7 @@ public class UncachedKeyRing {
* - Remove all certificates flagged as "local"
* - Remove all certificates which are superseded by a newer one on the same target,
* including revocations with later re-certifications.
- * - Remove all certificates of unknown type:
+ * - Remove all certificates in other positions if not of known type:
* - key revocation signatures on the master key
* - subkey binding signatures for subkeys
* - certifications and certification revocations for user ids
@@ -225,7 +228,7 @@ public class UncachedKeyRing {
PGPPublicKey modified = masterKey;
PGPSignature revocation = null;
- for (PGPSignature zert : new IterableIterator<PGPSignature>(masterKey.getSignatures())) {
+ for (PGPSignature zert : new IterableIterator<PGPSignature>(masterKey.getKeySignatures())) {
int type = zert.getSignatureType();
// Disregard certifications on user ids, we will deal with those later
@@ -234,6 +237,10 @@ public class UncachedKeyRing {
|| type == PGPSignature.CASUAL_CERTIFICATION
|| type == PGPSignature.POSITIVE_CERTIFICATION
|| type == PGPSignature.CERTIFICATION_REVOCATION) {
+ // These should not be here...
+ log.add(LogLevel.WARN, LogType.MSG_KC_REVOKE_BAD_TYPE_UID, indent);
+ modified = PGPPublicKey.removeCertification(modified, zert);
+ badCerts += 1;
continue;
}
WrappedSignature cert = new WrappedSignature(zert);
@@ -317,7 +324,7 @@ public class UncachedKeyRing {
if (cert.getCreationTime().after(now)) {
// Creation date in the future? No way!
- log.add(LogLevel.WARN, LogType.MSG_KC_REVOKE_BAD_TIME, indent);
+ log.add(LogLevel.WARN, LogType.MSG_KC_UID_BAD_TIME, indent);
modified = PGPPublicKey.removeCertification(modified, zert);
badCerts += 1;
continue;
@@ -325,7 +332,7 @@ public class UncachedKeyRing {
if (cert.isLocal()) {
// Creation date in the future? No way!
- log.add(LogLevel.WARN, LogType.MSG_KC_REVOKE_BAD_LOCAL, indent);
+ log.add(LogLevel.WARN, LogType.MSG_KC_UID_BAD_LOCAL, indent);
modified = PGPPublicKey.removeCertification(modified, zert);
badCerts += 1;
continue;
@@ -425,7 +432,7 @@ public class UncachedKeyRing {
// If no valid certificate (if only a revocation) remains, drop it
if (selfCert == null && revocation == null) {
modified = PGPPublicKey.removeCertification(modified, userId);
- log.add(LogLevel.ERROR, LogType.MSG_KC_UID_REVOKE_DUP,
+ log.add(LogLevel.ERROR, LogType.MSG_KC_UID_REMOVE,
indent, userId);
}
}
@@ -505,14 +512,16 @@ public class UncachedKeyRing {
continue;
}
+ // if this certificate says it allows signing for the key
if (zert.getHashedSubPackets().hasSubpacket(SignatureSubpacketTags.KEY_FLAGS)) {
+
int flags = ((KeyFlags) zert.getHashedSubPackets()
.getSubpacket(SignatureSubpacketTags.KEY_FLAGS)).getFlags();
- // If this subkey is allowed to sign data,
if ((flags & PGPKeyFlags.CAN_SIGN) == PGPKeyFlags.CAN_SIGN) {
+ boolean ok = false;
+ // it MUST have an embedded primary key binding signature
try {
PGPSignatureList list = zert.getUnhashedSubPackets().getEmbeddedSignatures();
- boolean ok = false;
for (int i = 0; i < list.size(); i++) {
WrappedSignature subsig = new WrappedSignature(list.get(i));
if (subsig.getSignatureType() == PGPSignature.PRIMARYKEY_BINDING) {
@@ -526,17 +535,19 @@ public class UncachedKeyRing {
}
}
}
- if (!ok) {
- log.add(LogLevel.WARN, LogType.MSG_KC_SUB_PRIMARY_NONE, indent);
- badCerts += 1;
- continue;
- }
} catch (Exception e) {
log.add(LogLevel.WARN, LogType.MSG_KC_SUB_PRIMARY_BAD_ERR, indent);
badCerts += 1;
continue;
}
+ // if it doesn't, get rid of this!
+ if (!ok) {
+ log.add(LogLevel.WARN, LogType.MSG_KC_SUB_PRIMARY_NONE, indent);
+ badCerts += 1;
+ continue;
+ }
}
+
}
// if we already have a cert, and this one is not newer: skip it
@@ -549,6 +560,8 @@ public class UncachedKeyRing {
selfCert = zert;
// if this is newer than a possibly existing revocation, drop that one
if (revocation != null && selfCert.getCreationTime().after(revocation.getCreationTime())) {
+ log.add(LogLevel.DEBUG, LogType.MSG_KC_SUB_REVOKE_DUP, indent);
+ redundantCerts += 1;
revocation = null;
}
@@ -558,7 +571,7 @@ public class UncachedKeyRing {
// make sure the certificate checks out
try {
cert.init(masterKey);
- if (!cert.verifySignature(key)) {
+ if (!cert.verifySignature(masterKey, key)) {
log.add(LogLevel.WARN, LogType.MSG_KC_SUB_REVOKE_BAD, indent);
badCerts += 1;
continue;
@@ -582,7 +595,7 @@ public class UncachedKeyRing {
// it is not properly bound? error!
if (selfCert == null) {
- ring = replacePublicKey(ring, modified);
+ ring = removeSubKey(ring, key);
log.add(LogLevel.ERROR, LogType.MSG_KC_SUB_NO_CERT,
indent, PgpKeyHelper.convertKeyIdToHex(key.getKeyID()));
@@ -650,7 +663,7 @@ public class UncachedKeyRing {
return left.length - right.length;
}
// compare byte-by-byte
- for (int i = 0; i < left.length && i < right.length; i++) {
+ for (int i = 0; i < left.length; i++) {
if (left[i] != right[i]) {
return (left[i] & 0xff) - (right[i] & 0xff);
}
@@ -678,7 +691,14 @@ public class UncachedKeyRing {
final PGPPublicKey resultKey = result.getPublicKey(key.getKeyID());
if (resultKey == null) {
log.add(LogLevel.DEBUG, LogType.MSG_MG_NEW_SUBKEY, indent);
- result = replacePublicKey(result, key);
+ // special case: if both rings are secret, copy over the secret key
+ if (isSecret() && other.isSecret()) {
+ PGPSecretKey sKey = ((PGPSecretKeyRing) candidate).getSecretKey(key.getKeyID());
+ result = PGPSecretKeyRing.insertSecretKey((PGPSecretKeyRing) result, sKey);
+ } else {
+ // otherwise, just insert the public key
+ result = replacePublicKey(result, key);
+ }
continue;
}
@@ -686,17 +706,7 @@ public class UncachedKeyRing {
PGPPublicKey modified = resultKey;
// Iterate certifications
- for (PGPSignature cert : new IterableIterator<PGPSignature>(key.getSignatures())) {
- int type = cert.getSignatureType();
- // Disregard certifications on user ids, we will deal with those later
- if (type == PGPSignature.NO_CERTIFICATION
- || type == PGPSignature.DEFAULT_CERTIFICATION
- || type == PGPSignature.CASUAL_CERTIFICATION
- || type == PGPSignature.POSITIVE_CERTIFICATION
- || type == PGPSignature.CERTIFICATION_REVOCATION) {
- continue;
- }
-
+ for (PGPSignature cert : new IterableIterator<PGPSignature>(key.getKeySignatures())) {
// Don't merge foreign stuff into secret keys
if (cert.getKeyID() != masterKeyId && isSecret()) {
continue;
@@ -744,8 +754,12 @@ public class UncachedKeyRing {
}
- log.add(LogLevel.DEBUG, LogType.MSG_MG_FOUND_NEW,
- indent, Integer.toString(newCerts));
+ if (newCerts > 0) {
+ log.add(LogLevel.DEBUG, LogType.MSG_MG_FOUND_NEW, indent,
+ Integer.toString(newCerts));
+ } else {
+ log.add(LogLevel.DEBUG, LogType.MSG_MG_UNCHANGED, indent);
+ }
return new UncachedKeyRing(result);
@@ -756,19 +770,20 @@ public class UncachedKeyRing {
}
- public UncachedKeyRing extractPublicKeyRing() {
+ public UncachedKeyRing extractPublicKeyRing() throws IOException {
if(!isSecret()) {
throw new RuntimeException("Tried to extract public keyring from non-secret keyring. " +
"This is a programming error and should never happen!");
}
- ArrayList<PGPPublicKey> keys = new ArrayList();
Iterator<PGPPublicKey> it = mRing.getPublicKeys();
+ ByteArrayOutputStream stream = new ByteArrayOutputStream(2048);
while (it.hasNext()) {
- keys.add(it.next());
+ stream.write(it.next().getEncoded());
}
- return new UncachedKeyRing(new PGPPublicKeyRing(keys));
+ return new UncachedKeyRing(
+ new PGPPublicKeyRing(stream.toByteArray(), new JcaKeyFingerprintCalculator()));
}
/** This method replaces a public key in a keyring.
@@ -792,4 +807,20 @@ public class UncachedKeyRing {
return PGPSecretKeyRing.insertSecretKey(secRing, sKey);
}
+ /** This method removes a subkey in a keyring.
+ *
+ * This method essentially wraps PGP*KeyRing.remove*Key, where the keyring may be of either
+ * the secret or public subclass.
+ *
+ * @return the resulting PGPKeyRing of the same type as the input
+ */
+ private static PGPKeyRing removeSubKey(PGPKeyRing ring, PGPPublicKey key) {
+ if (ring instanceof PGPPublicKeyRing) {
+ return PGPPublicKeyRing.removePublicKey((PGPPublicKeyRing) ring, key);
+ } else {
+ PGPSecretKey sKey = ((PGPSecretKeyRing) ring).getSecretKey(key.getKeyID());
+ return PGPSecretKeyRing.removeSecretKey((PGPSecretKeyRing) ring, sKey);
+ }
+ }
+
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java
index 33db7771b..358b1c552 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java
@@ -1,16 +1,14 @@
package org.sufficientlysecure.keychain.pgp;
-import org.spongycastle.bcpg.SignatureSubpacketTags;
import org.spongycastle.bcpg.sig.KeyFlags;
-import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPSignature;
import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.util.IterableIterator;
+import org.sufficientlysecure.keychain.util.Log;
-import java.security.SignatureException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
@@ -44,14 +42,19 @@ public class UncachedPublicKey {
}
public Date getExpiryTime() {
- Date creationDate = getCreationTime();
- if (mPublicKey.getValidDays() == 0) {
+ long seconds = mPublicKey.getValidSeconds();
+ if (seconds > Integer.MAX_VALUE) {
+ Log.e(Constants.TAG, "error, expiry time too large");
+ return null;
+ }
+ if (seconds == 0) {
// no expiry
return null;
}
+ Date creationDate = getCreationTime();
Calendar calendar = GregorianCalendar.getInstance();
calendar.setTime(creationDate);
- calendar.add(Calendar.DATE, mPublicKey.getValidDays());
+ calendar.add(Calendar.SECOND, (int) seconds);
return calendar.getTime();
}
@@ -77,26 +80,76 @@ public class UncachedPublicKey {
return mPublicKey.getBitStrength();
}
+ /** Returns the primary user id, as indicated by the public key's self certificates.
+ *
+ * This is an expensive operation, since potentially a lot of certificates (and revocations)
+ * have to be checked, and even then the result is NOT guaranteed to be constant through a
+ * canonicalization operation.
+ *
+ * Returns null if there is no primary user id (as indicated by certificates)
+ *
+ */
public String getPrimaryUserId() {
+ String found = null;
+ PGPSignature foundSig = null;
for (String userId : new IterableIterator<String>(mPublicKey.getUserIDs())) {
+ PGPSignature revocation = null;
+
for (PGPSignature sig : new IterableIterator<PGPSignature>(mPublicKey.getSignaturesForID(userId))) {
- if (sig.getHashedSubPackets() != null
- && sig.getHashedSubPackets().hasSubpacket(SignatureSubpacketTags.PRIMARY_USER_ID)) {
- try {
+ try {
+
+ // if this is a revocation, this is not the user id
+ if (sig.getSignatureType() == PGPSignature.CERTIFICATION_REVOCATION) {
+ // make sure it's actually valid
+ sig.init(new JcaPGPContentVerifierBuilderProvider().setProvider(
+ Constants.BOUNCY_CASTLE_PROVIDER_NAME), mPublicKey);
+ if (!sig.verifyCertification(userId, mPublicKey)) {
+ continue;
+ }
+ if (found != null && found.equals(userId)) {
+ found = null;
+ }
+ revocation = sig;
+ // this revocation may still be overridden by a newer cert
+ continue;
+ }
+
+ if (sig.getHashedSubPackets() != null && sig.getHashedSubPackets().isPrimaryUserID()) {
+ if (foundSig != null && sig.getCreationTime().before(foundSig.getCreationTime())) {
+ continue;
+ }
+ // ignore if there is a newer revocation for this user id
+ if (revocation != null && sig.getCreationTime().before(revocation.getCreationTime())) {
+ continue;
+ }
// make sure it's actually valid
sig.init(new JcaPGPContentVerifierBuilderProvider().setProvider(
Constants.BOUNCY_CASTLE_PROVIDER_NAME), mPublicKey);
if (sig.verifyCertification(userId, mPublicKey)) {
- return userId;
+ found = userId;
+ foundSig = sig;
+ // this one can't be relevant anymore at this point
+ revocation = null;
}
- } catch (Exception e) {
- // nothing bad happens, the key is just not considered the primary key id
}
- }
+ } catch (Exception e) {
+ // nothing bad happens, the key is just not considered the primary key id
+ }
}
}
- return null;
+ return found;
+ }
+
+ /**
+ * Returns primary user id if existing. If not, return first encountered user id.
+ */
+ public String getPrimaryUserIdWithFallback() {
+ String userId = getPrimaryUserId();
+ if (userId == null) {
+ userId = (String) mPublicKey.getUserIDs().next();
+ }
+ return userId;
}
public ArrayList<String> getUnorderedUserIds() {
@@ -186,6 +239,21 @@ public class UncachedPublicKey {
return mPublicKey;
}
+ public Iterator<WrappedSignature> getSignatures() {
+ final Iterator<PGPSignature> it = mPublicKey.getSignatures();
+ return new Iterator<WrappedSignature>() {
+ public void remove() {
+ it.remove();
+ }
+ public WrappedSignature next() {
+ return new WrappedSignature(it.next());
+ }
+ public boolean hasNext() {
+ return it.hasNext();
+ }
+ };
+ }
+
public Iterator<WrappedSignature> getSignaturesForId(String userId) {
final Iterator<PGPSignature> it = mPublicKey.getSignaturesForID(userId);
return new Iterator<WrappedSignature>() {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedKeyRing.java
index 6f3068261..a054255dc 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedKeyRing.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedKeyRing.java
@@ -39,8 +39,12 @@ public abstract class WrappedKeyRing extends KeyRing {
}
public String getPrimaryUserId() throws PgpGeneralException {
- return (String) getRing().getPublicKey().getUserIDs().next();
- };
+ return getPublicKey().getPrimaryUserId();
+ }
+
+ public String getPrimaryUserIdWithFallback() throws PgpGeneralException {
+ return getPublicKey().getPrimaryUserIdWithFallback();
+ }
public boolean isRevoked() throws PgpGeneralException {
// Is the master key revoked?
@@ -101,8 +105,16 @@ public abstract class WrappedKeyRing extends KeyRing {
abstract public IterableIterator<WrappedPublicKey> publicKeyIterator();
- public UncachedKeyRing getUncached() {
- return new UncachedKeyRing(getRing());
+ public WrappedPublicKey getPublicKey() {
+ return new WrappedPublicKey(this, getRing().getPublicKey());
+ }
+
+ public WrappedPublicKey getPublicKey(long id) {
+ return new WrappedPublicKey(this, getRing().getPublicKey(id));
+ }
+
+ public byte[] getEncoded() throws IOException {
+ return getRing().getEncoded();
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedPublicKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedPublicKeyRing.java
index b2abf15a4..bb1f7d778 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedPublicKeyRing.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedPublicKeyRing.java
@@ -1,14 +1,11 @@
package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.bcpg.ArmoredOutputStream;
-import org.spongycastle.openpgp.PGPKeyRing;
import org.spongycastle.openpgp.PGPObjectFactory;
import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPPublicKeyRing;
-import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.util.IterableIterator;
-import org.sufficientlysecure.keychain.util.Log;
import java.io.IOException;
import java.util.Iterator;
@@ -25,17 +22,20 @@ public class WrappedPublicKeyRing extends WrappedKeyRing {
PGPPublicKeyRing getRing() {
if(mRing == null) {
+ // get first object in block
PGPObjectFactory factory = new PGPObjectFactory(mPubKey);
- PGPKeyRing keyRing = null;
try {
- if ((keyRing = (PGPKeyRing) factory.nextObject()) == null) {
- Log.e(Constants.TAG, "No keys given!");
+ Object obj = factory.nextObject();
+ if (! (obj instanceof PGPPublicKeyRing)) {
+ throw new RuntimeException("Error constructing WrappedPublicKeyRing, should never happen!");
+ }
+ mRing = (PGPPublicKeyRing) obj;
+ if (factory.nextObject() != null) {
+ throw new RuntimeException("Encountered trailing data after keyring, should never happen!");
}
} catch (IOException e) {
- Log.e(Constants.TAG, "Error while converting to PGPKeyRing!", e);
+ throw new RuntimeException("IO Error constructing WrappedPublicKeyRing, should never happen!");
}
-
- mRing = (PGPPublicKeyRing) keyRing;
}
return mRing;
}
@@ -44,14 +44,6 @@ public class WrappedPublicKeyRing extends WrappedKeyRing {
getRing().encode(stream);
}
- public WrappedPublicKey getSubkey() {
- return new WrappedPublicKey(this, getRing().getPublicKey());
- }
-
- public WrappedPublicKey getSubkey(long id) {
- return new WrappedPublicKey(this, getRing().getPublicKey(id));
- }
-
/** Getter that returns the subkey that should be used for signing. */
WrappedPublicKey getEncryptionSubKey() throws PgpGeneralException {
PGPPublicKey key = getRing().getPublicKey(getEncryptId());
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKey.java
index ef8044a9b..f0485d801 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKey.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKey.java
@@ -51,14 +51,6 @@ public class WrappedSecretKey extends WrappedPublicKey {
return (WrappedSecretKeyRing) mRing;
}
- /** Returns the wrapped PGPSecretKeyRing.
- * This function is for compatibility only, should not be used anymore and will be removed
- */
- @Deprecated
- public PGPSecretKey getKeyExternal() {
- return mSecretKey;
- }
-
public boolean unlock(String passphrase) throws PgpGeneralException {
try {
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
@@ -97,7 +89,7 @@ public class WrappedSecretKey extends WrappedPublicKey {
signatureGenerator.init(signatureType, mPrivateKey);
PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
- spGen.setSignerUserID(false, mRing.getPrimaryUserId());
+ spGen.setSignerUserID(false, mRing.getPrimaryUserIdWithFallback());
signatureGenerator.setHashedSubpackets(spGen.generate());
return signatureGenerator;
} catch(PGPException e) {
@@ -175,7 +167,7 @@ public class WrappedSecretKey extends WrappedPublicKey {
}
// get the master subkey (which we certify for)
- PGPPublicKey publicKey = publicKeyRing.getSubkey().getPublicKey();
+ PGPPublicKey publicKey = publicKeyRing.getPublicKey().getPublicKey();
// fetch public key ring, add the certification and return it
for (String userId : new IterableIterator<String>(userIds.iterator())) {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKeyRing.java
index c737b7c46..5cb24cf88 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKeyRing.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKeyRing.java
@@ -41,11 +41,11 @@ public class WrappedSecretKeyRing extends WrappedKeyRing {
return mRing;
}
- public WrappedSecretKey getSubKey() {
+ public WrappedSecretKey getSecretKey() {
return new WrappedSecretKey(this, mRing.getSecretKey());
}
- public WrappedSecretKey getSubKey(long id) {
+ public WrappedSecretKey getSecretKey(long id) {
return new WrappedSecretKey(this, mRing.getSecretKey(id));
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java
index 196ac1dee..df19930c4 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java
@@ -15,6 +15,7 @@ import org.sufficientlysecure.keychain.util.Log;
import java.io.IOException;
import java.security.SignatureException;
+import java.util.ArrayList;
import java.util.Date;
/** OpenKeychain wrapper around PGPSignature objects.
@@ -55,12 +56,37 @@ public class WrappedSignature {
return mSig.getCreationTime();
}
+ public ArrayList<WrappedSignature> getEmbeddedSignatures() {
+ ArrayList<WrappedSignature> sigs = new ArrayList<WrappedSignature>();
+ if (!mSig.hasSubpackets()) {
+ return sigs;
+ }
+ try {
+ PGPSignatureList list;
+ list = mSig.getHashedSubPackets().getEmbeddedSignatures();
+ for(int i = 0; i < list.size(); i++) {
+ sigs.add(new WrappedSignature(list.get(i)));
+ }
+ list = mSig.getUnhashedSubPackets().getEmbeddedSignatures();
+ for(int i = 0; i < list.size(); i++) {
+ sigs.add(new WrappedSignature(list.get(i)));
+ }
+ } catch (PGPException e) {
+ // no matter
+ Log.e(Constants.TAG, "exception reading embedded signatures", e);
+ } catch (IOException e) {
+ // no matter
+ Log.e(Constants.TAG, "exception reading embedded signatures", e);
+ }
+ return sigs;
+ }
+
public byte[] getEncoded() throws IOException {
return mSig.getEncoded();
}
public boolean isRevocation() {
- return mSig.getHashedSubPackets().hasSubpacket(SignatureSubpacketTags.REVOCATION_REASON);
+ return mSig.getSignatureType() == PGPSignature.CERTIFICATION_REVOCATION;
}
public boolean isPrimaryUserId() {