Get certificates by raw user ids, be more liberal about accepted user id encodings

This commit is contained in:
Dominik Schürmann 2014-09-13 19:05:53 +02:00
parent 974dc47d69
commit 9e37522bdd
5 changed files with 72 additions and 39 deletions

View File

@ -59,6 +59,10 @@ public abstract class CanonicalizedKeyRing extends KeyRing {
return getPublicKey().getPrimaryUserIdWithFallback(); return getPublicKey().getPrimaryUserIdWithFallback();
} }
public ArrayList<byte[]> getUnorderedRawUserIds() {
return getPublicKey().getUnorderedRawUserIds();
}
public ArrayList<String> getUnorderedUserIds() { public ArrayList<String> getUnorderedUserIds() {
return getPublicKey().getUnorderedUserIds(); return getPublicKey().getUnorderedUserIds();
} }

View File

@ -33,6 +33,7 @@ import org.spongycastle.openpgp.PGPSignature;
import org.spongycastle.openpgp.PGPSignatureList; import org.spongycastle.openpgp.PGPSignatureList;
import org.spongycastle.openpgp.PGPUtil; import org.spongycastle.openpgp.PGPUtil;
import org.spongycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator; import org.spongycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator;
import org.spongycastle.util.Strings;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.service.results.OperationResultParcel.LogLevel; import org.sufficientlysecure.keychain.service.results.OperationResultParcel.LogLevel;
@ -361,23 +362,25 @@ public class UncachedKeyRing {
} }
} }
ArrayList<String> processedUserIds = new ArrayList<String>(); ArrayList<byte[]> processedUserIds = new ArrayList<byte[]>();
for (String userId : new IterableIterator<String>(masterKey.getUserIDs())) { for (byte[] rawUserId : new IterableIterator<byte[]>(masterKey.getRawUserIDs())) {
String userId = Strings.fromUTF8ByteArray(rawUserId);
// check for duplicate user ids // check for duplicate user ids
if (processedUserIds.contains(userId)) { if (processedUserIds.contains(rawUserId)) {
log.add(LogLevel.WARN, LogType.MSG_KC_UID_DUP, log.add(LogLevel.WARN, LogType.MSG_KC_UID_DUP,
indent, userId); indent, userId);
// strip out the first found user id with this name // strip out the first found user id with this name
modified = PGPPublicKey.removeCertification(modified, userId); modified = PGPPublicKey.removeCertification(modified, rawUserId);
} }
processedUserIds.add(userId); processedUserIds.add(rawUserId);
PGPSignature selfCert = null; PGPSignature selfCert = null;
revocation = null; revocation = null;
// look through signatures for this specific user id // look through signatures for this specific user id
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
Iterator<PGPSignature> signaturesIt = masterKey.getSignaturesForID(userId); Iterator<PGPSignature> signaturesIt = masterKey.getSignaturesForID(rawUserId);
if (signaturesIt != null) { if (signaturesIt != null) {
for (PGPSignature zert : new IterableIterator<PGPSignature>(signaturesIt)) { for (PGPSignature zert : new IterableIterator<PGPSignature>(signaturesIt)) {
WrappedSignature cert = new WrappedSignature(zert); WrappedSignature cert = new WrappedSignature(zert);
@ -391,7 +394,7 @@ public class UncachedKeyRing {
&& type != PGPSignature.CERTIFICATION_REVOCATION) { && type != PGPSignature.CERTIFICATION_REVOCATION) {
log.add(LogLevel.WARN, LogType.MSG_KC_UID_BAD_TYPE, log.add(LogLevel.WARN, LogType.MSG_KC_UID_BAD_TYPE,
indent, "0x" + Integer.toString(zert.getSignatureType(), 16)); indent, "0x" + Integer.toString(zert.getSignatureType(), 16));
modified = PGPPublicKey.removeCertification(modified, userId, zert); modified = PGPPublicKey.removeCertification(modified, rawUserId, zert);
badCerts += 1; badCerts += 1;
continue; continue;
} }
@ -399,7 +402,7 @@ public class UncachedKeyRing {
if (cert.getCreationTime().after(now)) { if (cert.getCreationTime().after(now)) {
// Creation date in the future? No way! // Creation date in the future? No way!
log.add(LogLevel.WARN, LogType.MSG_KC_UID_BAD_TIME, indent); log.add(LogLevel.WARN, LogType.MSG_KC_UID_BAD_TIME, indent);
modified = PGPPublicKey.removeCertification(modified, userId, zert); modified = PGPPublicKey.removeCertification(modified, rawUserId, zert);
badCerts += 1; badCerts += 1;
continue; continue;
} }
@ -407,7 +410,7 @@ public class UncachedKeyRing {
if (cert.isLocal()) { if (cert.isLocal()) {
// Creation date in the future? No way! // Creation date in the future? No way!
log.add(LogLevel.WARN, LogType.MSG_KC_UID_BAD_LOCAL, indent); log.add(LogLevel.WARN, LogType.MSG_KC_UID_BAD_LOCAL, indent);
modified = PGPPublicKey.removeCertification(modified, userId, zert); modified = PGPPublicKey.removeCertification(modified, rawUserId, zert);
badCerts += 1; badCerts += 1;
continue; continue;
} }
@ -418,7 +421,7 @@ public class UncachedKeyRing {
if (isSecret()) { if (isSecret()) {
log.add(LogLevel.WARN, LogType.MSG_KC_UID_FOREIGN, log.add(LogLevel.WARN, LogType.MSG_KC_UID_FOREIGN,
indent, PgpKeyHelper.convertKeyIdToHex(certId)); indent, PgpKeyHelper.convertKeyIdToHex(certId));
modified = PGPPublicKey.removeCertification(modified, userId, zert); modified = PGPPublicKey.removeCertification(modified, rawUserId, zert);
badCerts += 1; badCerts += 1;
} }
continue; continue;
@ -427,17 +430,17 @@ public class UncachedKeyRing {
// Otherwise, first make sure it checks out // Otherwise, first make sure it checks out
try { try {
cert.init(masterKey); cert.init(masterKey);
if (!cert.verifySignature(masterKey, userId)) { if (!cert.verifySignature(masterKey, rawUserId)) {
log.add(LogLevel.WARN, LogType.MSG_KC_UID_BAD, log.add(LogLevel.WARN, LogType.MSG_KC_UID_BAD,
indent, userId); indent, userId);
modified = PGPPublicKey.removeCertification(modified, userId, zert); modified = PGPPublicKey.removeCertification(modified, rawUserId, zert);
badCerts += 1; badCerts += 1;
continue; continue;
} }
} catch (PgpGeneralException e) { } catch (PgpGeneralException e) {
log.add(LogLevel.WARN, LogType.MSG_KC_UID_BAD_ERR, log.add(LogLevel.WARN, LogType.MSG_KC_UID_BAD_ERR,
indent, userId); indent, userId);
modified = PGPPublicKey.removeCertification(modified, userId, zert); modified = PGPPublicKey.removeCertification(modified, rawUserId, zert);
badCerts += 1; badCerts += 1;
continue; continue;
} }
@ -452,13 +455,13 @@ public class UncachedKeyRing {
} else if (selfCert.getCreationTime().before(cert.getCreationTime())) { } else if (selfCert.getCreationTime().before(cert.getCreationTime())) {
log.add(LogLevel.DEBUG, LogType.MSG_KC_UID_CERT_DUP, log.add(LogLevel.DEBUG, LogType.MSG_KC_UID_CERT_DUP,
indent, userId); indent, userId);
modified = PGPPublicKey.removeCertification(modified, userId, selfCert); modified = PGPPublicKey.removeCertification(modified, rawUserId, selfCert);
redundantCerts += 1; redundantCerts += 1;
selfCert = zert; selfCert = zert;
} else { } else {
log.add(LogLevel.DEBUG, LogType.MSG_KC_UID_CERT_DUP, log.add(LogLevel.DEBUG, LogType.MSG_KC_UID_CERT_DUP,
indent, userId); indent, userId);
modified = PGPPublicKey.removeCertification(modified, userId, zert); modified = PGPPublicKey.removeCertification(modified, rawUserId, zert);
redundantCerts += 1; redundantCerts += 1;
} }
// If there is a revocation certificate, and it's older than this, drop it // If there is a revocation certificate, and it's older than this, drop it
@ -466,7 +469,7 @@ public class UncachedKeyRing {
&& revocation.getCreationTime().before(selfCert.getCreationTime())) { && revocation.getCreationTime().before(selfCert.getCreationTime())) {
log.add(LogLevel.DEBUG, LogType.MSG_KC_UID_REVOKE_OLD, log.add(LogLevel.DEBUG, LogType.MSG_KC_UID_REVOKE_OLD,
indent, userId); indent, userId);
modified = PGPPublicKey.removeCertification(modified, userId, revocation); modified = PGPPublicKey.removeCertification(modified, rawUserId, revocation);
revocation = null; revocation = null;
redundantCerts += 1; redundantCerts += 1;
} }
@ -477,7 +480,7 @@ public class UncachedKeyRing {
if (selfCert != null && selfCert.getCreationTime().after(zert.getCreationTime())) { if (selfCert != null && selfCert.getCreationTime().after(zert.getCreationTime())) {
log.add(LogLevel.DEBUG, LogType.MSG_KC_UID_REVOKE_OLD, log.add(LogLevel.DEBUG, LogType.MSG_KC_UID_REVOKE_OLD,
indent, userId); indent, userId);
modified = PGPPublicKey.removeCertification(modified, userId, zert); modified = PGPPublicKey.removeCertification(modified, rawUserId, zert);
redundantCerts += 1; redundantCerts += 1;
continue; continue;
} }
@ -488,13 +491,13 @@ public class UncachedKeyRing {
} else if (revocation.getCreationTime().before(cert.getCreationTime())) { } else if (revocation.getCreationTime().before(cert.getCreationTime())) {
log.add(LogLevel.DEBUG, LogType.MSG_KC_UID_REVOKE_DUP, log.add(LogLevel.DEBUG, LogType.MSG_KC_UID_REVOKE_DUP,
indent, userId); indent, userId);
modified = PGPPublicKey.removeCertification(modified, userId, revocation); modified = PGPPublicKey.removeCertification(modified, rawUserId, revocation);
redundantCerts += 1; redundantCerts += 1;
revocation = zert; revocation = zert;
} else { } else {
log.add(LogLevel.DEBUG, LogType.MSG_KC_UID_REVOKE_DUP, log.add(LogLevel.DEBUG, LogType.MSG_KC_UID_REVOKE_DUP,
indent, userId); indent, userId);
modified = PGPPublicKey.removeCertification(modified, userId, zert); modified = PGPPublicKey.removeCertification(modified, rawUserId, zert);
redundantCerts += 1; redundantCerts += 1;
} }
break; break;
@ -506,7 +509,7 @@ public class UncachedKeyRing {
if (selfCert == null && revocation == null) { if (selfCert == null && revocation == null) {
log.add(LogLevel.ERROR, LogType.MSG_KC_UID_REMOVE, log.add(LogLevel.ERROR, LogType.MSG_KC_UID_REMOVE,
indent, userId); indent, userId);
modified = PGPPublicKey.removeCertification(modified, userId); modified = PGPPublicKey.removeCertification(modified, rawUserId);
} }
} }
@ -817,9 +820,9 @@ public class UncachedKeyRing {
} }
// Copy over all user id certificates // Copy over all user id certificates
for (String userId : new IterableIterator<String>(key.getUserIDs())) { for (byte[] rawUserId : new IterableIterator<byte[]>(key.getRawUserIDs())) {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
Iterator<PGPSignature> signaturesIt = key.getSignaturesForID(userId); Iterator<PGPSignature> signaturesIt = key.getSignaturesForID(rawUserId);
// no signatures for this User ID, skip it // no signatures for this User ID, skip it
if (signaturesIt == null) { if (signaturesIt == null) {
continue; continue;
@ -836,7 +839,7 @@ public class UncachedKeyRing {
} }
newCerts += 1; newCerts += 1;
certs.add(encoded); certs.add(encoded);
modified = PGPPublicKey.addCertification(modified, userId, cert); modified = PGPPublicKey.addCertification(modified, rawUserId, cert);
} }
} }
// If anything changed, save the updated (sub)key // If anything changed, save the updated (sub)key

View File

@ -28,11 +28,13 @@ import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPSignature; import org.spongycastle.openpgp.PGPSignature;
import org.spongycastle.openpgp.PGPSignatureSubpacketVector; import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider; import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
import org.spongycastle.util.Strings;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.util.IterableIterator; import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date; import java.util.Date;
import java.util.GregorianCalendar; import java.util.GregorianCalendar;
@ -126,14 +128,14 @@ public class UncachedPublicKey {
* *
*/ */
public String getPrimaryUserId() { public String getPrimaryUserId() {
String found = null; byte[] found = null;
PGPSignature foundSig = null; PGPSignature foundSig = null;
// noinspection unchecked // noinspection unchecked
for (String userId : new IterableIterator<String>(mPublicKey.getUserIDs())) { for (byte[] rawUserId : new IterableIterator<byte[]>(mPublicKey.getRawUserIDs())) {
PGPSignature revocation = null; PGPSignature revocation = null;
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
Iterator<PGPSignature> signaturesIt = mPublicKey.getSignaturesForID(userId); Iterator<PGPSignature> signaturesIt = mPublicKey.getSignaturesForID(rawUserId);
// no signatures for this User ID // no signatures for this User ID
if (signaturesIt == null) { if (signaturesIt == null) {
continue; continue;
@ -147,10 +149,10 @@ public class UncachedPublicKey {
// make sure it's actually valid // make sure it's actually valid
sig.init(new JcaPGPContentVerifierBuilderProvider().setProvider( sig.init(new JcaPGPContentVerifierBuilderProvider().setProvider(
Constants.BOUNCY_CASTLE_PROVIDER_NAME), mPublicKey); Constants.BOUNCY_CASTLE_PROVIDER_NAME), mPublicKey);
if (!sig.verifyCertification(userId, mPublicKey)) { if (!sig.verifyCertification(rawUserId, mPublicKey)) {
continue; continue;
} }
if (found != null && found.equals(userId)) { if (found != null && Arrays.equals(found, rawUserId)) {
found = null; found = null;
} }
revocation = sig; revocation = sig;
@ -169,8 +171,8 @@ public class UncachedPublicKey {
// make sure it's actually valid // make sure it's actually valid
sig.init(new JcaPGPContentVerifierBuilderProvider().setProvider( sig.init(new JcaPGPContentVerifierBuilderProvider().setProvider(
Constants.BOUNCY_CASTLE_PROVIDER_NAME), mPublicKey); Constants.BOUNCY_CASTLE_PROVIDER_NAME), mPublicKey);
if (sig.verifyCertification(userId, mPublicKey)) { if (sig.verifyCertification(rawUserId, mPublicKey)) {
found = userId; found = rawUserId;
foundSig = sig; foundSig = sig;
// this one can't be relevant anymore at this point // this one can't be relevant anymore at this point
revocation = null; revocation = null;
@ -182,7 +184,11 @@ public class UncachedPublicKey {
} }
} }
} }
return found; if (found != null) {
return Strings.fromUTF8ByteArray(found);
} else {
return null;
}
} }
/** /**
@ -204,6 +210,14 @@ public class UncachedPublicKey {
return userIds; return userIds;
} }
public ArrayList<byte[]> getUnorderedRawUserIds() {
ArrayList<byte[]> userIds = new ArrayList<byte[]>();
for (byte[] userId : new IterableIterator<byte[]>(mPublicKey.getRawUserIDs())) {
userIds.add(userId);
}
return userIds;
}
public boolean isElGamalEncrypt() { public boolean isElGamalEncrypt() {
return getAlgorithm() == PGPPublicKey.ELGAMAL_ENCRYPT; return getAlgorithm() == PGPPublicKey.ELGAMAL_ENCRYPT;
} }
@ -320,8 +334,8 @@ public class UncachedPublicKey {
}; };
} }
public Iterator<WrappedSignature> getSignaturesForId(String userId) { public Iterator<WrappedSignature> getSignaturesForRawId(byte[] rawUserId) {
final Iterator<PGPSignature> it = mPublicKey.getSignaturesForID(userId); final Iterator<PGPSignature> it = mPublicKey.getSignaturesForID(rawUserId);
if (it != null) { if (it != null) {
return new Iterator<WrappedSignature>() { return new Iterator<WrappedSignature>() {
public void remove() { public void remove() {

View File

@ -187,8 +187,16 @@ public class WrappedSignature {
} }
} }
public boolean verifySignature(UncachedPublicKey key, String uid) throws PgpGeneralException { boolean verifySignature(PGPPublicKey key, byte[] rawUserId) throws PgpGeneralException {
return verifySignature(key.getPublicKey(), uid); try {
return mSig.verifyCertification(rawUserId, key);
} catch (PGPException e) {
throw new PgpGeneralException("Error!", e);
}
}
public boolean verifySignature(UncachedPublicKey key, byte[] rawUserId) throws PgpGeneralException {
return verifySignature(key.getPublicKey(), rawUserId);
} }
public boolean verifySignature(CanonicalizedPublicKey key, String uid) throws PgpGeneralException { public boolean verifySignature(CanonicalizedPublicKey key, String uid) throws PgpGeneralException {
return verifySignature(key.getPublicKey(), uid); return verifySignature(key.getPublicKey(), uid);

View File

@ -28,6 +28,7 @@ import android.net.Uri;
import android.os.RemoteException; import android.os.RemoteException;
import android.support.v4.util.LongSparseArray; import android.support.v4.util.LongSparseArray;
import org.spongycastle.util.Strings;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.Preferences; import org.sufficientlysecure.keychain.helper.Preferences;
@ -434,8 +435,11 @@ public class ProviderHelper {
} }
mIndent += 1; mIndent += 1;
List<UserIdItem> uids = new ArrayList<UserIdItem>(); List<UserIdItem> uids = new ArrayList<UserIdItem>();
for (String userId : new IterableIterator<String>( for (byte[] rawUserId : new IterableIterator<byte[]>(
masterKey.getUnorderedUserIds().iterator())) { masterKey.getUnorderedRawUserIds().iterator())) {
String userId = Strings.fromUTF8ByteArray(rawUserId);
Log.d(Constants.TAG, "userId: "+userId);
UserIdItem item = new UserIdItem(); UserIdItem item = new UserIdItem();
uids.add(item); uids.add(item);
item.userId = userId; item.userId = userId;
@ -446,7 +450,7 @@ public class ProviderHelper {
mIndent += 1; mIndent += 1;
// look through signatures for this specific key // look through signatures for this specific key
for (WrappedSignature cert : new IterableIterator<WrappedSignature>( for (WrappedSignature cert : new IterableIterator<WrappedSignature>(
masterKey.getSignaturesForId(userId))) { masterKey.getSignaturesForRawId(rawUserId))) {
long certId = cert.getKeyId(); long certId = cert.getKeyId();
try { try {
// self signature // self signature
@ -469,7 +473,7 @@ public class ProviderHelper {
if (trustedKeys.indexOfKey(certId) >= 0) { if (trustedKeys.indexOfKey(certId) >= 0) {
CanonicalizedPublicKey trustedKey = trustedKeys.get(certId); CanonicalizedPublicKey trustedKey = trustedKeys.get(certId);
cert.init(trustedKey); cert.init(trustedKey);
if (cert.verifySignature(masterKey, userId)) { if (cert.verifySignature(masterKey, rawUserId)) {
item.trustedCerts.add(cert); item.trustedCerts.add(cert);
log(LogLevel.INFO, LogType.MSG_IP_UID_CERT_GOOD, log(LogLevel.INFO, LogType.MSG_IP_UID_CERT_GOOD,
PgpKeyHelper.convertKeyIdToHexShort(trustedKey.getKeyId()) PgpKeyHelper.convertKeyIdToHexShort(trustedKey.getKeyId())