respect user id revocation signatures

Closes #836
This commit is contained in:
Vincent Breitmoser 2014-09-20 00:12:50 +02:00
parent 6536ca825b
commit 344bc1736d
4 changed files with 84 additions and 34 deletions

View File

@ -20,6 +20,8 @@ package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.bcpg.SignatureSubpacket; import org.spongycastle.bcpg.SignatureSubpacket;
import org.spongycastle.bcpg.SignatureSubpacketTags; import org.spongycastle.bcpg.SignatureSubpacketTags;
import org.spongycastle.bcpg.sig.Exportable;
import org.spongycastle.bcpg.sig.Revocable;
import org.spongycastle.bcpg.sig.RevocationReason; import org.spongycastle.bcpg.sig.RevocationReason;
import org.spongycastle.openpgp.PGPException; import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPObjectFactory; import org.spongycastle.openpgp.PGPObjectFactory;
@ -218,12 +220,23 @@ public class WrappedSignature {
return new WrappedSignature(signatures.get(0)); return new WrappedSignature(signatures.get(0));
} }
/** Returns true if this certificate is revocable in general. */
public boolean isRevokable () {
// If nothing is specified, the packet is considered revocable
if (mSig.getHashedSubPackets() == null
|| !mSig.getHashedSubPackets().hasSubpacket(SignatureSubpacketTags.REVOCABLE)) {
return true;
}
SignatureSubpacket p = mSig.getHashedSubPackets().getSubpacket(SignatureSubpacketTags.REVOCABLE);
return ((Revocable) p).isRevocable();
}
public boolean isLocal() { public boolean isLocal() {
if (mSig.getHashedSubPackets() == null if (mSig.getHashedSubPackets() == null
|| !mSig.getHashedSubPackets().hasSubpacket(SignatureSubpacketTags.EXPORTABLE)) { || !mSig.getHashedSubPackets().hasSubpacket(SignatureSubpacketTags.EXPORTABLE)) {
return false; return false;
} }
SignatureSubpacket p = mSig.getHashedSubPackets().getSubpacket(SignatureSubpacketTags.EXPORTABLE); SignatureSubpacket p = mSig.getHashedSubPackets().getSubpacket(SignatureSubpacketTags.EXPORTABLE);
return p.getData()[0] == 0; return ((Exportable) p).isExportable();
} }
} }

View File

@ -450,47 +450,67 @@ public class ProviderHelper {
for (WrappedSignature cert : new IterableIterator<WrappedSignature>( for (WrappedSignature cert : new IterableIterator<WrappedSignature>(
masterKey.getSignaturesForRawId(rawUserId))) { masterKey.getSignaturesForRawId(rawUserId))) {
long certId = cert.getKeyId(); long certId = cert.getKeyId();
// self signature
if (certId == masterKeyId) {
// NOTE self-certificates are already verified during canonicalization,
// AND we know there is at most one cert plus at most one revocation
if (!cert.isRevocation()) {
item.selfCert = cert;
item.isPrimary = cert.isPrimaryUserId();
} else {
item.isRevoked = true;
log(LogType.MSG_IP_UID_REVOKED);
}
continue;
}
// do we have a trusted key for this?
if (trustedKeys.indexOfKey(certId) < 0) {
unknownCerts += 1;
continue;
}
// verify signatures from known private keys
CanonicalizedPublicKey trustedKey = trustedKeys.get(certId);
try { try {
// self signature cert.init(trustedKey);
if (certId == masterKeyId) { // if it doesn't certify, leave a note and skip
if ( ! cert.verifySignature(masterKey, rawUserId)) {
// NOTE self-certificates are already verified during canonicalization, log(LogType.MSG_IP_UID_CERT_BAD);
// AND we know there is at most one cert plus at most one revocation
if (!cert.isRevocation()) {
item.selfCert = cert;
item.isPrimary = cert.isPrimaryUserId();
} else {
item.isRevoked = true;
log(LogType.MSG_IP_UID_REVOKED);
}
continue; continue;
} }
// verify signatures from known private keys log(cert.isRevocation()
if (trustedKeys.indexOfKey(certId) >= 0) { ? LogType.MSG_IP_UID_CERT_GOOD_REVOKE
CanonicalizedPublicKey trustedKey = trustedKeys.get(certId); : LogType.MSG_IP_UID_CERT_GOOD,
if (cert.isRevocation()) { KeyFormattingUtils.convertKeyIdToHexShort(trustedKey.getKeyId())
// skip for now );
// check if there is a previous certificate
WrappedSignature prev = item.trustedCerts.get(cert.getKeyId());
if (prev != null) {
// if it's newer, skip this one
if (prev.getCreationTime().after(cert.getCreationTime())) {
log(LogType.MSG_IP_UID_CERT_OLD);
continue; continue;
} }
cert.init(trustedKey); // if the previous one was a non-revokable certification, no need to look further
if (cert.verifySignature(masterKey, rawUserId)) { if (!prev.isRevocation() && !prev.isRevokable()) {
item.trustedCerts.add(cert); log(LogType.MSG_IP_UID_CERT_NONREVOKE);
log(LogType.MSG_IP_UID_CERT_GOOD, continue;
KeyFormattingUtils.convertKeyIdToHexShort(trustedKey.getKeyId())
);
} else {
log(LogType.MSG_IP_UID_CERT_BAD);
} }
log(LogType.MSG_IP_UID_CERT_NEW);
} }
item.trustedCerts.put(cert.getKeyId(), cert);
unknownCerts += 1;
} catch (PgpGeneralException e) { } catch (PgpGeneralException e) {
log(LogType.MSG_IP_UID_CERT_ERROR, log(LogType.MSG_IP_UID_CERT_ERROR,
KeyFormattingUtils.convertKeyIdToHex(cert.getKeyId())); KeyFormattingUtils.convertKeyIdToHex(cert.getKeyId()));
} }
} }
if (unknownCerts > 0) { if (unknownCerts > 0) {
@ -518,9 +538,18 @@ public class ProviderHelper {
if (item.isRevoked) { if (item.isRevoked) {
continue; continue;
} }
for (int i = 0; i < item.trustedCerts.size(); i++) {
// iterate over signatures
for (int i = 0; i < item.trustedCerts.size() ; i++) {
WrappedSignature sig = item.trustedCerts.valueAt(i);
// if it's a revocation
if (sig.isRevocation()) {
// don't further process it
continue;
}
// otherwise, build database operation
operations.add(buildCertOperations( operations.add(buildCertOperations(
masterKeyId, userIdRank, item.trustedCerts.get(i), Certs.VERIFIED_SECRET)); masterKeyId, userIdRank, sig, Certs.VERIFIED_SECRET));
} }
} }
@ -568,7 +597,7 @@ public class ProviderHelper {
boolean isPrimary = false; boolean isPrimary = false;
boolean isRevoked = false; boolean isRevoked = false;
WrappedSignature selfCert; WrappedSignature selfCert;
List<WrappedSignature> trustedCerts = new ArrayList<WrappedSignature>(); LongSparseArray<WrappedSignature> trustedCerts = new LongSparseArray<WrappedSignature>();
@Override @Override
public int compareTo(UserIdItem o) { public int compareTo(UserIdItem o) {

View File

@ -264,8 +264,12 @@ public abstract class OperationResult implements Parcelable {
MSG_IP_SUCCESS (LogLevel.OK, R.string.msg_ip_success), MSG_IP_SUCCESS (LogLevel.OK, R.string.msg_ip_success),
MSG_IP_SUCCESS_IDENTICAL (LogLevel.OK, R.string.msg_ip_success_identical), MSG_IP_SUCCESS_IDENTICAL (LogLevel.OK, R.string.msg_ip_success_identical),
MSG_IP_UID_CERT_BAD (LogLevel.WARN, R.string.msg_ip_uid_cert_bad), MSG_IP_UID_CERT_BAD (LogLevel.WARN, R.string.msg_ip_uid_cert_bad),
MSG_IP_UID_CERT_ERROR (LogLevel.ERROR, R.string.msg_ip_uid_cert_error), MSG_IP_UID_CERT_ERROR (LogLevel.WARN, R.string.msg_ip_uid_cert_error),
MSG_IP_UID_CERT_OLD (LogLevel.DEBUG, R.string.msg_ip_uid_cert_old),
MSG_IP_UID_CERT_NONREVOKE (LogLevel.DEBUG, R.string.msg_ip_uid_cert_nonrevoke),
MSG_IP_UID_CERT_NEW (LogLevel.DEBUG, R.string.msg_ip_uid_cert_new),
MSG_IP_UID_CERT_GOOD (LogLevel.DEBUG, R.string.msg_ip_uid_cert_good), MSG_IP_UID_CERT_GOOD (LogLevel.DEBUG, R.string.msg_ip_uid_cert_good),
MSG_IP_UID_CERT_GOOD_REVOKE (LogLevel.DEBUG, R.string.msg_ip_uid_cert_good_revoke),
MSG_IP_UID_CERTS_UNKNOWN (LogLevel.DEBUG, R.plurals.msg_ip_uid_certs_unknown), MSG_IP_UID_CERTS_UNKNOWN (LogLevel.DEBUG, R.plurals.msg_ip_uid_certs_unknown),
MSG_IP_UID_CLASSIFYING_ZERO (LogLevel.DEBUG, R.string.msg_ip_uid_classifying_zero), MSG_IP_UID_CLASSIFYING_ZERO (LogLevel.DEBUG, R.string.msg_ip_uid_classifying_zero),
MSG_IP_UID_CLASSIFYING (LogLevel.DEBUG, R.plurals.msg_ip_uid_classifying), MSG_IP_UID_CLASSIFYING (LogLevel.DEBUG, R.plurals.msg_ip_uid_classifying),

View File

@ -580,7 +580,11 @@
<string name="msg_ip_reinsert_secret">"Re-inserting secret key"</string> <string name="msg_ip_reinsert_secret">"Re-inserting secret key"</string>
<string name="msg_ip_uid_cert_bad">"Encountered bad certificate!"</string> <string name="msg_ip_uid_cert_bad">"Encountered bad certificate!"</string>
<string name="msg_ip_uid_cert_error">"Error processing certificate!"</string> <string name="msg_ip_uid_cert_error">"Error processing certificate!"</string>
<string name="msg_ip_uid_cert_good">"User id is certified by %1$s"</string> <string name="msg_ip_uid_cert_nonrevoke">"Already have a non-revokable certificate, skipping."</string>
<string name="msg_ip_uid_cert_old">"Certificate is older than previous, skipping."</string>
<string name="msg_ip_uid_cert_new">"Certificate is more recent, replacing previous."</string>
<string name="msg_ip_uid_cert_good">"Found good certificate by %1$s"</string>
<string name="msg_ip_uid_cert_good_revoke">"Found good certificate revocation by %1$s"</string>
<plurals name="msg_ip_uid_certs_unknown"> <plurals name="msg_ip_uid_certs_unknown">
<item quantity="one">"Ignoring one certificate issued by an unknown public key"</item> <item quantity="one">"Ignoring one certificate issued by an unknown public key"</item>
<item quantity="other">"Ignoring %s certificates issued by unknown public keys"</item> <item quantity="other">"Ignoring %s certificates issued by unknown public keys"</item>