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.java168
1 files changed, 105 insertions, 63 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 e309ed632..78620405f 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java
@@ -178,18 +178,21 @@ public class UncachedKeyRing {
return result;
}
- /** "Canonicalizes" a key, removing inconsistencies in the process. This operation can be
- * applied to public keyrings only.
+ /** "Canonicalizes" a key, removing inconsistencies in the process.
*
* More specifically:
* - Remove all non-verifying self-certificates
* - Remove all "future" self-certificates
* - Remove all certificates flagged as "local"
- * - Remove all certificates which are superseded by a newer one on the same target
- *
- * After this cleaning, a number of checks are done: TODO implement
- * - See if each subkey retains a valid self certificate
- * - See if each user id retains a valid self certificate
+ * - 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:
+ * - key revocation signatures on the master key
+ * - subkey binding signatures for subkeys
+ * - certifications and certification revocations for user ids
+ * - If a subkey retains no valid subkey binding certificate, remove it
+ * - If a user id retains no valid self certificate, remove it
+ * - If the key is a secret key, remove all certificates by foreign keys
*
* This operation writes an OperationLog which can be used as part of a OperationResultParcel.
*
@@ -197,20 +200,16 @@ public class UncachedKeyRing {
*
*/
public UncachedKeyRing canonicalize(OperationLog log, int indent) {
- if (isSecret()) {
- throw new RuntimeException("Tried to canonicalize non-secret keyring. " +
- "This is a programming error and should never happen!");
- }
- log.add(LogLevel.START, LogType.MSG_KC,
+ log.add(LogLevel.START, isSecret() ? LogType.MSG_KC_SECRET : LogType.MSG_KC_PUBLIC,
new String[]{PgpKeyHelper.convertKeyIdToHex(getMasterKeyId())}, indent);
indent += 1;
final Date now = new Date();
- int removedCerts = 0;
+ int redundantCerts = 0, badCerts = 0;
- PGPPublicKeyRing ring = (PGPPublicKeyRing) mRing;
+ PGPKeyRing ring = mRing;
PGPPublicKey masterKey = mRing.getPublicKey();
final long masterKeyId = masterKey.getKeyID();
@@ -240,7 +239,7 @@ public class UncachedKeyRing {
"0x" + Integer.toString(type, 16)
}, indent);
modified = PGPPublicKey.removeCertification(modified, zert);
- removedCerts += 1;
+ badCerts += 1;
continue;
}
@@ -248,7 +247,7 @@ public class UncachedKeyRing {
// Creation date in the future? No way!
log.add(LogLevel.WARN, LogType.MSG_KC_REVOKE_BAD_TIME, null, indent);
modified = PGPPublicKey.removeCertification(modified, zert);
- removedCerts += 1;
+ badCerts += 1;
continue;
}
@@ -256,7 +255,7 @@ public class UncachedKeyRing {
// Creation date in the future? No way!
log.add(LogLevel.WARN, LogType.MSG_KC_REVOKE_BAD_LOCAL, null, indent);
modified = PGPPublicKey.removeCertification(modified, zert);
- removedCerts += 1;
+ badCerts += 1;
continue;
}
@@ -265,13 +264,13 @@ public class UncachedKeyRing {
if (!cert.verifySignature(masterKey)) {
log.add(LogLevel.WARN, LogType.MSG_KC_REVOKE_BAD, null, indent);
modified = PGPPublicKey.removeCertification(modified, zert);
- removedCerts += 1;
+ badCerts += 1;
continue;
}
} catch (PgpGeneralException e) {
log.add(LogLevel.WARN, LogType.MSG_KC_REVOKE_BAD_ERR, null, indent);
modified = PGPPublicKey.removeCertification(modified, zert);
- removedCerts += 1;
+ badCerts += 1;
continue;
}
@@ -281,12 +280,12 @@ public class UncachedKeyRing {
// more revocations? at least one is superfluous, then.
} else if (revocation.getCreationTime().before(zert.getCreationTime())) {
modified = PGPPublicKey.removeCertification(modified, revocation);
- removedCerts += 1;
+ redundantCerts += 1;
log.add(LogLevel.INFO, LogType.MSG_KC_REVOKE_DUP, null, indent);
revocation = zert;
} else {
modified = PGPPublicKey.removeCertification(modified, zert);
- removedCerts += 1;
+ redundantCerts += 1;
log.add(LogLevel.INFO, LogType.MSG_KC_REVOKE_DUP, null, indent);
}
}
@@ -312,14 +311,14 @@ public class UncachedKeyRing {
"0x" + Integer.toString(zert.getSignatureType(), 16)
}, indent);
modified = PGPPublicKey.removeCertification(modified, userId, zert);
- removedCerts += 1;
+ badCerts += 1;
}
if (cert.getCreationTime().after(now)) {
// Creation date in the future? No way!
log.add(LogLevel.WARN, LogType.MSG_KC_REVOKE_BAD_TIME, null, indent);
modified = PGPPublicKey.removeCertification(modified, zert);
- removedCerts += 1;
+ badCerts += 1;
continue;
}
@@ -327,12 +326,19 @@ public class UncachedKeyRing {
// Creation date in the future? No way!
log.add(LogLevel.WARN, LogType.MSG_KC_REVOKE_BAD_LOCAL, null, indent);
modified = PGPPublicKey.removeCertification(modified, zert);
- removedCerts += 1;
+ badCerts += 1;
continue;
}
- // If this is a foreign signature, never mind any further
+ // 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(LogLevel.WARN, LogType.MSG_KC_UID_FOREIGN,
+ new String[] { PgpKeyHelper.convertKeyIdToHex(certId) }, indent);
+ modified = PGPPublicKey.removeCertification(modified, userId, zert);
+ badCerts += 1;
+ }
continue;
}
@@ -343,14 +349,14 @@ public class UncachedKeyRing {
log.add(LogLevel.WARN, LogType.MSG_KC_UID_BAD,
new String[] { userId }, indent);
modified = PGPPublicKey.removeCertification(modified, userId, zert);
- removedCerts += 1;
+ badCerts += 1;
continue;
}
} catch (PgpGeneralException e) {
log.add(LogLevel.WARN, LogType.MSG_KC_UID_BAD_ERR,
new String[] { userId }, indent);
modified = PGPPublicKey.removeCertification(modified, userId, zert);
- removedCerts += 1;
+ badCerts += 1;
continue;
}
@@ -363,14 +369,14 @@ public class UncachedKeyRing {
selfCert = zert;
} else if (selfCert.getCreationTime().before(cert.getCreationTime())) {
modified = PGPPublicKey.removeCertification(modified, userId, selfCert);
- removedCerts += 1;
- log.add(LogLevel.INFO, LogType.MSG_KC_UID_DUP,
+ redundantCerts += 1;
+ log.add(LogLevel.DEBUG, LogType.MSG_KC_UID_DUP,
new String[] { userId }, indent);
selfCert = zert;
} else {
modified = PGPPublicKey.removeCertification(modified, userId, zert);
- removedCerts += 1;
- log.add(LogLevel.INFO, LogType.MSG_KC_UID_DUP,
+ redundantCerts += 1;
+ log.add(LogLevel.DEBUG, LogType.MSG_KC_UID_DUP,
new String[] { userId }, indent);
}
// If there is a revocation certificate, and it's older than this, drop it
@@ -378,8 +384,8 @@ public class UncachedKeyRing {
&& revocation.getCreationTime().before(selfCert.getCreationTime())) {
modified = PGPPublicKey.removeCertification(modified, userId, revocation);
revocation = null;
- removedCerts += 1;
- log.add(LogLevel.INFO, LogType.MSG_KC_UID_REVOKE_OLD,
+ redundantCerts += 1;
+ log.add(LogLevel.DEBUG, LogType.MSG_KC_UID_REVOKE_OLD,
new String[] { userId }, indent);
}
break;
@@ -388,8 +394,8 @@ public class UncachedKeyRing {
// If this is older than the (latest) self cert, drop it
if (selfCert != null && selfCert.getCreationTime().after(zert.getCreationTime())) {
modified = PGPPublicKey.removeCertification(modified, userId, zert);
- removedCerts += 1;
- log.add(LogLevel.INFO, LogType.MSG_KC_UID_REVOKE_OLD,
+ redundantCerts += 1;
+ log.add(LogLevel.DEBUG, LogType.MSG_KC_UID_REVOKE_OLD,
new String[] { userId }, indent);
continue;
}
@@ -399,14 +405,14 @@ public class UncachedKeyRing {
// more revocations? at least one is superfluous, then.
} else if (revocation.getCreationTime().before(cert.getCreationTime())) {
modified = PGPPublicKey.removeCertification(modified, userId, revocation);
- removedCerts += 1;
- log.add(LogLevel.INFO, LogType.MSG_KC_UID_REVOKE_DUP,
+ redundantCerts += 1;
+ log.add(LogLevel.DEBUG, LogType.MSG_KC_UID_REVOKE_DUP,
new String[] { userId }, indent);
revocation = zert;
} else {
modified = PGPPublicKey.removeCertification(modified, userId, zert);
- removedCerts += 1;
- log.add(LogLevel.INFO, LogType.MSG_KC_UID_REVOKE_DUP,
+ redundantCerts += 1;
+ log.add(LogLevel.DEBUG, LogType.MSG_KC_UID_REVOKE_DUP,
new String[] { userId }, indent);
}
break;
@@ -414,12 +420,23 @@ 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,
+ new String[] { userId }, indent);
+ }
}
- // Replace modified key in the keyring
- ring = PGPPublicKeyRing.insertPublicKey(ring, modified);
+ // If NO user ids remain, error out!
+ if (!modified.getUserIDs().hasNext()) {
+ log.add(LogLevel.ERROR, LogType.MSG_KC_FATAL_NO_UID, null, indent);
+ return null;
+ }
- log.add(LogLevel.DEBUG, LogType.MSG_KC_MASTER_SUCCESS, null, indent);
+ // Replace modified key in the keyring
+ ring = replacePublicKey(ring, modified);
indent -= 1;
}
@@ -437,18 +454,17 @@ public class UncachedKeyRing {
// certificate.
PGPPublicKey modified = key;
PGPSignature selfCert = null, revocation = null;
- uids: for (PGPSignature zig : new IterableIterator<PGPSignature>(key.getSignatures())) {
+ uids: for (PGPSignature zert : new IterableIterator<PGPSignature>(key.getSignatures())) {
// remove from keyring (for now)
- modified = PGPPublicKey.removeCertification(modified, zig);
- // add this too, easier than adding it for every single "continue" case
- removedCerts += 1;
+ modified = PGPPublicKey.removeCertification(modified, zert);
- WrappedSignature cert = new WrappedSignature(zig);
+ WrappedSignature cert = new WrappedSignature(zert);
int type = cert.getSignatureType();
// filter out bad key types...
if (cert.getKeyId() != masterKey.getKeyID()) {
log.add(LogLevel.WARN, LogType.MSG_KC_SUB_BAD_KEYID, null, indent);
+ badCerts += 1;
continue;
}
@@ -456,18 +472,21 @@ public class UncachedKeyRing {
log.add(LogLevel.WARN, LogType.MSG_KC_SUB_BAD_TYPE, new String[]{
"0x" + Integer.toString(type, 16)
}, indent);
+ badCerts += 1;
continue;
}
if (cert.getCreationTime().after(now)) {
// Creation date in the future? No way!
log.add(LogLevel.WARN, LogType.MSG_KC_SUB_BAD_TIME, null, indent);
+ badCerts += 1;
continue;
}
if (cert.isLocal()) {
// Creation date in the future? No way!
log.add(LogLevel.WARN, LogType.MSG_KC_SUB_BAD_LOCAL, null, indent);
+ badCerts += 1;
continue;
}
@@ -478,20 +497,22 @@ public class UncachedKeyRing {
cert.init(masterKey);
if (!cert.verifySignature(masterKey, key)) {
log.add(LogLevel.WARN, LogType.MSG_KC_SUB_BAD, null, indent);
+ badCerts += 1;
continue;
}
} catch (PgpGeneralException e) {
log.add(LogLevel.WARN, LogType.MSG_KC_SUB_BAD_ERR, null, indent);
+ badCerts += 1;
continue;
}
- if (zig.getHashedSubPackets().hasSubpacket(SignatureSubpacketTags.KEY_FLAGS)) {
- int flags = ((KeyFlags) zig.getHashedSubPackets()
+ 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) {
try {
- PGPSignatureList list = zig.getUnhashedSubPackets().getEmbeddedSignatures();
+ PGPSignatureList list = zert.getUnhashedSubPackets().getEmbeddedSignatures();
boolean ok = false;
for (int i = 0; i < list.size(); i++) {
WrappedSignature subsig = new WrappedSignature(list.get(i));
@@ -501,16 +522,19 @@ public class UncachedKeyRing {
ok = true;
} else {
log.add(LogLevel.WARN, LogType.MSG_KC_SUB_PRIMARY_BAD, null, indent);
+ badCerts += 1;
continue uids;
}
}
}
if (!ok) {
log.add(LogLevel.WARN, LogType.MSG_KC_SUB_PRIMARY_NONE, null, indent);
+ badCerts += 1;
continue;
}
} catch (Exception e) {
log.add(LogLevel.WARN, LogType.MSG_KC_SUB_PRIMARY_BAD_ERR, null, indent);
+ badCerts += 1;
continue;
}
}
@@ -518,10 +542,11 @@ public class UncachedKeyRing {
// if we already have a cert, and this one is not newer: skip it
if (selfCert != null && selfCert.getCreationTime().before(cert.getCreationTime())) {
+ redundantCerts += 1;
continue;
}
- selfCert = zig;
+ selfCert = zert;
// if this is newer than a possibly existing revocation, drop that one
if (revocation != null && selfCert.getCreationTime().after(revocation.getCreationTime())) {
revocation = null;
@@ -535,48 +560,56 @@ public class UncachedKeyRing {
cert.init(masterKey);
if (!cert.verifySignature(key)) {
log.add(LogLevel.WARN, LogType.MSG_KC_SUB_REVOKE_BAD, null, indent);
+ badCerts += 1;
continue;
}
} catch (PgpGeneralException e) {
log.add(LogLevel.WARN, LogType.MSG_KC_SUB_REVOKE_BAD_ERR, null, indent);
+ badCerts += 1;
continue;
}
// if there is no binding (yet), or the revocation is newer than the binding: keep it
- if (selfCert == null || selfCert.getCreationTime().before(cert.getCreationTime())) {
- revocation = zig;
+ if (selfCert != null && selfCert.getCreationTime().after(cert.getCreationTime())) {
+ redundantCerts += 1;
+ continue;
}
+
+ revocation = zert;
}
}
// it is not properly bound? error!
if (selfCert == null) {
- ring = PGPPublicKeyRing.removePublicKey(ring, modified);
+ ring = replacePublicKey(ring, modified);
log.add(LogLevel.ERROR, LogType.MSG_KC_SUB_NO_CERT,
- new String[]{PgpKeyHelper.convertKeyIdToHex(key.getKeyID())}, indent);
+ new String[]{ PgpKeyHelper.convertKeyIdToHex(key.getKeyID()) }, indent);
indent -= 1;
continue;
}
// re-add certification
modified = PGPPublicKey.addCertification(modified, selfCert);
- removedCerts -= 1;
// add revocation, if any
if (revocation != null) {
modified = PGPPublicKey.addCertification(modified, revocation);
- removedCerts -= 1;
}
// replace pubkey in keyring
- ring = PGPPublicKeyRing.insertPublicKey(ring, modified);
-
- log.add(LogLevel.DEBUG, LogType.MSG_KC_SUB_SUCCESS, null, indent);
+ ring = replacePublicKey(ring, modified);
indent -= 1;
}
- if (removedCerts > 0) {
- log.add(LogLevel.OK, LogType.MSG_KC_SUCCESS_REMOVED,
- new String[] { Integer.toString(removedCerts) }, indent);
+ if (badCerts > 0 && redundantCerts > 0) {
+ log.add(LogLevel.OK, LogType.MSG_KC_SUCCESS_BAD_AND_RED,
+ new String[] { Integer.toString(badCerts),
+ Integer.toString(redundantCerts) }, indent);
+ } else if (badCerts > 0) {
+ log.add(LogLevel.OK, LogType.MSG_KC_SUCCESS_BAD,
+ new String[] { Integer.toString(badCerts) }, indent);
+ } else if (redundantCerts > 0) {
+ log.add(LogLevel.OK, LogType.MSG_KC_SUCCESS_REDUNDANT,
+ new String[] { Integer.toString(redundantCerts) }, indent);
} else {
log.add(LogLevel.OK, LogType.MSG_KC_SUCCESS, null, indent);
}
@@ -584,5 +617,14 @@ public class UncachedKeyRing {
return new UncachedKeyRing(ring);
}
+ private static PGPKeyRing replacePublicKey(PGPKeyRing ring, PGPPublicKey key) {
+ if (ring instanceof PGPPublicKeyRing) {
+ return PGPPublicKeyRing.insertPublicKey((PGPPublicKeyRing) ring, key);
+ }
+ PGPSecretKeyRing secRing = (PGPSecretKeyRing) ring;
+ PGPSecretKey sKey = secRing.getSecretKey(key.getKeyID());
+ sKey = PGPSecretKey.replacePublicKey(sKey, key);
+ return PGPSecretKeyRing.insertSecretKey(secRing, sKey);
+ }
}