aboutsummaryrefslogtreecommitdiffstats
path: root/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java
diff options
context:
space:
mode:
Diffstat (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java')
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java206
1 files changed, 199 insertions, 7 deletions
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 a445e161f..af85bd878 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java
@@ -21,6 +21,7 @@ package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.bcpg.ArmoredOutputStream;
import org.spongycastle.bcpg.PublicKeyAlgorithmTags;
import org.spongycastle.bcpg.SignatureSubpacketTags;
+import org.spongycastle.bcpg.UserAttributeSubpacketTags;
import org.spongycastle.bcpg.sig.KeyFlags;
import org.spongycastle.openpgp.PGPKeyRing;
import org.spongycastle.openpgp.PGPObjectFactory;
@@ -30,6 +31,7 @@ import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.spongycastle.openpgp.PGPSignature;
import org.spongycastle.openpgp.PGPSignatureList;
+import org.spongycastle.openpgp.PGPUserAttributeSubpacketVector;
import org.spongycastle.openpgp.PGPUtil;
import org.spongycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator;
import org.sufficientlysecure.keychain.Constants;
@@ -443,7 +445,7 @@ public class UncachedKeyRing {
}
}
- ArrayList<String> processedUserIds = new ArrayList<String>();
+ ArrayList<String> processedUserIds = new ArrayList<>();
for (byte[] rawUserId : new IterableIterator<byte[]>(masterKey.getRawUserIDs())) {
String userId = Utf8Util.fromUTF8ByteArrayReplaceBadEncoding(rawUserId);
@@ -468,7 +470,7 @@ public class UncachedKeyRing {
@SuppressWarnings("unchecked")
Iterator<PGPSignature> signaturesIt = masterKey.getSignaturesForID(rawUserId);
if (signaturesIt != null) {
- for (PGPSignature zert : new IterableIterator<PGPSignature>(signaturesIt)) {
+ for (PGPSignature zert : new IterableIterator<>(signaturesIt)) {
WrappedSignature cert = new WrappedSignature(zert);
long certId = cert.getKeyId();
@@ -605,6 +607,170 @@ public class UncachedKeyRing {
return null;
}
+ ArrayList<PGPUserAttributeSubpacketVector> processedUserAttributes = new ArrayList<>();
+ for (PGPUserAttributeSubpacketVector userAttribute :
+ new IterableIterator<PGPUserAttributeSubpacketVector>(masterKey.getUserAttributes())) {
+
+ if (userAttribute.getSubpacket(UserAttributeSubpacketTags.IMAGE_ATTRIBUTE) != null) {
+ log.add(LogType.MSG_KC_UAT_JPEG, indent);
+ } else {
+ log.add(LogType.MSG_KC_UAT_UNKNOWN, indent);
+ }
+
+ try {
+ indent += 1;
+
+ // check for duplicate user attributes
+ if (processedUserAttributes.contains(userAttribute)) {
+ log.add(LogType.MSG_KC_UAT_DUP, indent);
+ // strip out the first found user id with this name
+ modified = PGPPublicKey.removeCertification(modified, userAttribute);
+ }
+ processedUserAttributes.add(userAttribute);
+
+ PGPSignature selfCert = null;
+ revocation = null;
+
+ // look through signatures for this specific user id
+ @SuppressWarnings("unchecked")
+ Iterator<PGPSignature> signaturesIt = masterKey.getSignaturesForUserAttribute(userAttribute);
+ if (signaturesIt != null) {
+ for (PGPSignature zert : new IterableIterator<>(signaturesIt)) {
+ WrappedSignature cert = new WrappedSignature(zert);
+ long certId = cert.getKeyId();
+
+ int type = zert.getSignatureType();
+ if (type != PGPSignature.DEFAULT_CERTIFICATION
+ && type != PGPSignature.NO_CERTIFICATION
+ && type != PGPSignature.CASUAL_CERTIFICATION
+ && type != PGPSignature.POSITIVE_CERTIFICATION
+ && type != PGPSignature.CERTIFICATION_REVOCATION) {
+ log.add(LogType.MSG_KC_UAT_BAD_TYPE,
+ indent, "0x" + Integer.toString(zert.getSignatureType(), 16));
+ modified = PGPPublicKey.removeCertification(modified, userAttribute, zert);
+ badCerts += 1;
+ continue;
+ }
+
+ if (cert.getCreationTime().after(nowPlusOneDay)) {
+ // Creation date in the future? No way!
+ log.add(LogType.MSG_KC_UAT_BAD_TIME, indent);
+ modified = PGPPublicKey.removeCertification(modified, userAttribute, zert);
+ badCerts += 1;
+ continue;
+ }
+
+ if (cert.isLocal()) {
+ // Creation date in the future? No way!
+ log.add(LogType.MSG_KC_UAT_BAD_LOCAL, indent);
+ modified = PGPPublicKey.removeCertification(modified, userAttribute, zert);
+ badCerts += 1;
+ continue;
+ }
+
+ // If this is a foreign signature, ...
+ if (certId != masterKeyId) {
+ // never mind any further for public keys, but remove them from secret ones
+ if (isSecret()) {
+ log.add(LogType.MSG_KC_UAT_FOREIGN,
+ indent, KeyFormattingUtils.convertKeyIdToHex(certId));
+ modified = PGPPublicKey.removeCertification(modified, userAttribute, zert);
+ badCerts += 1;
+ }
+ continue;
+ }
+
+ // Otherwise, first make sure it checks out
+ try {
+ cert.init(masterKey);
+ if (!cert.verifySignature(masterKey, userAttribute)) {
+ log.add(LogType.MSG_KC_UAT_BAD,
+ indent);
+ modified = PGPPublicKey.removeCertification(modified, userAttribute, zert);
+ badCerts += 1;
+ continue;
+ }
+ } catch (PgpGeneralException e) {
+ log.add(LogType.MSG_KC_UAT_BAD_ERR,
+ indent);
+ modified = PGPPublicKey.removeCertification(modified, userAttribute, zert);
+ badCerts += 1;
+ continue;
+ }
+
+ switch (type) {
+ case PGPSignature.DEFAULT_CERTIFICATION:
+ case PGPSignature.NO_CERTIFICATION:
+ case PGPSignature.CASUAL_CERTIFICATION:
+ case PGPSignature.POSITIVE_CERTIFICATION:
+ if (selfCert == null) {
+ selfCert = zert;
+ } else if (selfCert.getCreationTime().before(cert.getCreationTime())) {
+ log.add(LogType.MSG_KC_UAT_CERT_DUP,
+ indent);
+ modified = PGPPublicKey.removeCertification(modified, userAttribute, selfCert);
+ redundantCerts += 1;
+ selfCert = zert;
+ } else {
+ log.add(LogType.MSG_KC_UAT_CERT_DUP,
+ indent);
+ modified = PGPPublicKey.removeCertification(modified, userAttribute, zert);
+ redundantCerts += 1;
+ }
+ // If there is a revocation certificate, and it's older than this, drop it
+ if (revocation != null
+ && revocation.getCreationTime().before(selfCert.getCreationTime())) {
+ log.add(LogType.MSG_KC_UAT_REVOKE_OLD,
+ indent);
+ modified = PGPPublicKey.removeCertification(modified, userAttribute, revocation);
+ revocation = null;
+ redundantCerts += 1;
+ }
+ break;
+
+ case PGPSignature.CERTIFICATION_REVOCATION:
+ // If this is older than the (latest) self cert, drop it
+ if (selfCert != null && selfCert.getCreationTime().after(zert.getCreationTime())) {
+ log.add(LogType.MSG_KC_UAT_REVOKE_OLD,
+ indent);
+ modified = PGPPublicKey.removeCertification(modified, userAttribute, zert);
+ redundantCerts += 1;
+ continue;
+ }
+ // first revocation? remember it.
+ if (revocation == null) {
+ revocation = zert;
+ // more revocations? at least one is superfluous, then.
+ } else if (revocation.getCreationTime().before(cert.getCreationTime())) {
+ log.add(LogType.MSG_KC_UAT_REVOKE_DUP,
+ indent);
+ modified = PGPPublicKey.removeCertification(modified, userAttribute, revocation);
+ redundantCerts += 1;
+ revocation = zert;
+ } else {
+ log.add(LogType.MSG_KC_UAT_REVOKE_DUP,
+ indent);
+ modified = PGPPublicKey.removeCertification(modified, userAttribute, zert);
+ redundantCerts += 1;
+ }
+ break;
+ }
+ }
+ }
+
+ // If no valid certificate (if only a revocation) remains, drop it
+ if (selfCert == null && revocation == null) {
+ log.add(LogType.MSG_KC_UAT_REMOVE,
+ indent);
+ modified = PGPPublicKey.removeCertification(modified, userAttribute);
+ }
+
+ } finally {
+ indent -= 1;
+ }
+ }
+
+
// Replace modified key in the keyring
ring = replacePublicKey(ring, modified);
indent -= 1;
@@ -612,7 +778,7 @@ public class UncachedKeyRing {
}
// Keep track of ids we encountered so far
- Set<Long> knownIds = new HashSet<Long>();
+ Set<Long> knownIds = new HashSet<>();
// Process all keys
for (PGPPublicKey key : new IterableIterator<PGPPublicKey>(ring.getPublicKeys())) {
@@ -852,8 +1018,8 @@ public class UncachedKeyRing {
/** This operation merges information from a different keyring, returning a combined
* UncachedKeyRing.
*
- * The combined keyring contains the subkeys and user ids of both input keyrings, but it does
- * not necessarily have the canonicalized property.
+ * The combined keyring contains the subkeys, user ids and user attributes of both input
+ * keyrings, but it does not necessarily have the canonicalized property.
*
* @param other The UncachedKeyRing to merge. Must not be empty, and of the same masterKeyId
* @return A consolidated UncachedKeyRing with the data of both input keyrings. Same type as
@@ -876,7 +1042,7 @@ public class UncachedKeyRing {
}
// remember which certs we already added. this is cheaper than semantic deduplication
- Set<byte[]> certs = new TreeSet<byte[]>(new Comparator<byte[]>() {
+ Set<byte[]> certs = new TreeSet<>(new Comparator<byte[]>() {
public int compare(byte[] left, byte[] right) {
// check for length equality
if (left.length != right.length) {
@@ -958,7 +1124,7 @@ public class UncachedKeyRing {
if (signaturesIt == null) {
continue;
}
- for (PGPSignature cert : new IterableIterator<PGPSignature>(signaturesIt)) {
+ for (PGPSignature cert : new IterableIterator<>(signaturesIt)) {
// Don't merge foreign stuff into secret keys
if (cert.getKeyID() != masterKeyId && isSecret()) {
continue;
@@ -973,6 +1139,32 @@ public class UncachedKeyRing {
modified = PGPPublicKey.addCertification(modified, rawUserId, cert);
}
}
+
+ // Copy over all user attribute certificates
+ for (PGPUserAttributeSubpacketVector vector :
+ new IterableIterator<PGPUserAttributeSubpacketVector>(key.getUserAttributes())) {
+ @SuppressWarnings("unchecked")
+ Iterator<PGPSignature> signaturesIt = key.getSignaturesForUserAttribute(vector);
+ // no signatures for this user attribute attribute, skip it
+ if (signaturesIt == null) {
+ continue;
+ }
+ for (PGPSignature cert : new IterableIterator<>(signaturesIt)) {
+ // Don't merge foreign stuff into secret keys
+ if (cert.getKeyID() != masterKeyId && isSecret()) {
+ continue;
+ }
+ byte[] encoded = cert.getEncoded();
+ // Known cert, skip it
+ if (certs.contains(encoded)) {
+ continue;
+ }
+ newCerts += 1;
+ certs.add(encoded);
+ modified = PGPPublicKey.addCertification(modified, vector, cert);
+ }
+ }
+
// If anything changed, save the updated (sub)key
if (modified != resultKey) {
result = replacePublicKey(result, modified);