Prevent IAE when encountering non-UTF8 User-ID on a public key

Fixes #392
This commit is contained in:
Paul Schaub 2023-07-04 14:45:09 +02:00
parent eb7bb1bebe
commit f37aa1097c
Signed by: vanitasvitae
GPG Key ID: 62BEE9264BF17311
5 changed files with 37 additions and 17 deletions

View File

@ -42,6 +42,7 @@ import org.pgpainless.exception.KeyException;
import org.pgpainless.key.OpenPgpFingerprint;
import org.pgpainless.key.SubkeyIdentifier;
import org.pgpainless.key.util.KeyIdUtil;
import org.pgpainless.key.util.KeyRingUtils;
import org.pgpainless.key.util.RevocationAttributes;
import org.pgpainless.policy.Policy;
import org.pgpainless.signature.SignatureUtils;
@ -362,8 +363,7 @@ public class KeyRingInfo {
*/
@Nonnull
public List<String> getUserIds() {
Iterator<String> iterator = getPublicKey().getUserIDs();
return iteratorToList(iterator);
return KeyRingUtils.getUserIdsIgnoringInvalidUTF8(keys.getPublicKey());
}
/**
@ -1264,8 +1264,8 @@ public class KeyRingInfo {
subkeyRevocations = new HashMap<>();
subkeyBindings = new HashMap<>();
for (Iterator<String> it = keyRing.getPublicKey().getUserIDs(); it.hasNext(); ) {
String userId = it.next();
List<String> userIds = KeyRingUtils.getUserIdsIgnoringInvalidUTF8(keyRing.getPublicKey());
for (String userId : userIds) {
PGPSignature revocation = SignaturePicker.pickCurrentUserIdRevocationSignature(keyRing, userId, policy, referenceDate);
if (revocation != null) {
userIdRevocations.put(userId, revocation);

View File

@ -25,10 +25,13 @@ import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
import org.bouncycastle.openpgp.PGPSignature;
import org.bouncycastle.openpgp.PGPUserAttributeSubpacketVector;
import org.bouncycastle.util.Strings;
import org.pgpainless.PGPainless;
import org.pgpainless.implementation.ImplementationFactory;
import org.pgpainless.key.protection.SecretKeyRingProtector;
import org.pgpainless.key.protection.UnlockSecretKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public final class KeyRingUtils {
@ -36,6 +39,8 @@ public final class KeyRingUtils {
}
private static final Logger LOGGER = LoggerFactory.getLogger(KeyRingUtils.class);
/**
* Return the primary {@link PGPSecretKey} from the provided {@link PGPSecretKeyRing}.
* If it has no primary secret key, throw a {@link NoSuchElementException}.
@ -496,4 +501,18 @@ public final class KeyRingUtils {
public static PGPPublicKey getStrippedDownPublicKey(PGPPublicKey bloatedKey) throws PGPException {
return new PGPPublicKey(bloatedKey.getPublicKeyPacket(), ImplementationFactory.getInstance().getKeyFingerprintCalculator());
}
public static List<String> getUserIdsIgnoringInvalidUTF8(PGPPublicKey key) {
List<String> userIds = new ArrayList<>();
Iterator<byte[]> it = key.getRawUserIDs();
while (it.hasNext()) {
byte[] rawUserId = it.next();
try {
userIds.add(Strings.fromUTF8ByteArray(rawUserId));
} catch (IllegalArgumentException e) {
LOGGER.warn("Invalid UTF-8 user-ID encountered: " + new String(rawUserId));
}
}
return userIds;
}
}

View File

@ -23,6 +23,7 @@ import org.bouncycastle.openpgp.PGPSignature;
import org.pgpainless.algorithm.KeyFlag;
import org.pgpainless.algorithm.SignatureType;
import org.pgpainless.exception.SignatureValidationException;
import org.pgpainless.key.util.KeyRingUtils;
import org.pgpainless.policy.Policy;
import org.pgpainless.signature.SignatureUtils;
import org.pgpainless.signature.subpackets.SignatureSubpacketsUtil;
@ -113,11 +114,10 @@ public final class CertificateValidator {
}
// User-ID signatures (certifications, revocations)
Iterator<String> userIds = primaryKey.getUserIDs();
List<String> userIds = KeyRingUtils.getUserIdsIgnoringInvalidUTF8(primaryKey);
Map<String, List<PGPSignature>> userIdSignatures = new ConcurrentHashMap<>();
while (userIds.hasNext()) {
for (String userId : userIds) {
List<PGPSignature> signaturesOnUserId = new ArrayList<>();
String userId = userIds.next();
Iterator<PGPSignature> userIdSigs = primaryKey.getSignaturesForID(userId);
while (userIdSigs.hasNext()) {
PGPSignature userIdSig = userIdSigs.next();

View File

@ -32,6 +32,7 @@ import org.bouncycastle.util.io.Streams;
import org.pgpainless.algorithm.HashAlgorithm;
import org.pgpainless.decryption_verification.OpenPgpInputStream;
import org.pgpainless.key.OpenPgpFingerprint;
import org.pgpainless.key.util.KeyRingUtils;
/**
* Utility class for dealing with ASCII armored OpenPGP data.
@ -388,13 +389,12 @@ public final class ArmorUtils {
// Quickly determine the primary user-id + number of total user-ids
// NOTE: THIS METHOD DOES NOT CRYPTOGRAPHICALLY VERIFY THE SIGNATURES
// DO NOT RELY ON IT!
Iterator<String> userIds = publicKey.getUserIDs();
List<String> userIds = KeyRingUtils.getUserIdsIgnoringInvalidUTF8(publicKey);
int countIdentities = 0;
String first = null;
String primary = null;
while (userIds.hasNext()) {
for (String userId : userIds) {
countIdentities++;
String userId = userIds.next();
// remember the first user-id
if (first == null) {
first = userId;

View File

@ -4,10 +4,11 @@
package org.pgpainless.util.selection.keyring.impl;
import java.util.Iterator;
import java.util.List;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.pgpainless.key.util.KeyRingUtils;
import org.pgpainless.util.selection.keyring.PublicKeyRingSelectionStrategy;
import org.pgpainless.util.selection.keyring.SecretKeyRingSelectionStrategy;
@ -29,9 +30,9 @@ public final class ExactUserId {
@Override
public boolean accept(String identifier, PGPPublicKeyRing keyRing) {
Iterator<String> userIds = keyRing.getPublicKey().getUserIDs();
while (userIds.hasNext()) {
if (userIds.next().equals(identifier)) return true;
List<String> userIds = KeyRingUtils.getUserIdsIgnoringInvalidUTF8(keyRing.getPublicKey());
for (String userId : userIds) {
if (userId.equals(identifier)) return true;
}
return false;
}
@ -45,9 +46,9 @@ public final class ExactUserId {
@Override
public boolean accept(String identifier, PGPSecretKeyRing keyRing) {
Iterator<String> userIds = keyRing.getPublicKey().getUserIDs();
while (userIds.hasNext()) {
if (userIds.next().equals(identifier)) return true;
List<String> userIds = KeyRingUtils.getUserIdsIgnoringInvalidUTF8(keyRing.getPublicKey());
for (String userId : userIds) {
if (userId.equals(identifier)) return true;
}
return false;
}