1
0
Fork 0
mirror of https://github.com/pgpainless/pgpainless.git synced 2024-11-16 01:12:05 +01:00

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 5dd6e08096
commit 7d0dc5e40d
Signed by: vanitasvitae
GPG key ID: 62BEE9264BF17311
5 changed files with 48 additions and 17 deletions

View file

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

View file

@ -25,10 +25,13 @@ import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection; import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
import org.bouncycastle.openpgp.PGPSignature; import org.bouncycastle.openpgp.PGPSignature;
import org.bouncycastle.openpgp.PGPUserAttributeSubpacketVector; import org.bouncycastle.openpgp.PGPUserAttributeSubpacketVector;
import org.bouncycastle.util.Strings;
import org.pgpainless.PGPainless; import org.pgpainless.PGPainless;
import org.pgpainless.implementation.ImplementationFactory; import org.pgpainless.implementation.ImplementationFactory;
import org.pgpainless.key.protection.SecretKeyRingProtector; import org.pgpainless.key.protection.SecretKeyRingProtector;
import org.pgpainless.key.protection.UnlockSecretKey; import org.pgpainless.key.protection.UnlockSecretKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public final class KeyRingUtils { 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}. * Return the primary {@link PGPSecretKey} from the provided {@link PGPSecretKeyRing}.
* If it has no primary secret key, throw a {@link NoSuchElementException}. * If it has no primary secret key, throw a {@link NoSuchElementException}.
@ -485,4 +490,29 @@ public final class KeyRingUtils {
// Parse the key back into an object // Parse the key back into an object
return new PGPSecretKeyRing(encoded.toByteArray(), ImplementationFactory.getInstance().getKeyFingerprintCalculator()); return new PGPSecretKeyRing(encoded.toByteArray(), ImplementationFactory.getInstance().getKeyFingerprintCalculator());
} }
/**
* Strip all user-ids, user-attributes and signatures from the given public key.
*
* @param bloatedKey public key
* @return stripped public key
* @throws PGPException if the packet is faulty or the required calculations fail
*/
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.KeyFlag;
import org.pgpainless.algorithm.SignatureType; import org.pgpainless.algorithm.SignatureType;
import org.pgpainless.exception.SignatureValidationException; import org.pgpainless.exception.SignatureValidationException;
import org.pgpainless.key.util.KeyRingUtils;
import org.pgpainless.policy.Policy; import org.pgpainless.policy.Policy;
import org.pgpainless.signature.SignatureUtils; import org.pgpainless.signature.SignatureUtils;
import org.pgpainless.signature.subpackets.SignatureSubpacketsUtil; import org.pgpainless.signature.subpackets.SignatureSubpacketsUtil;
@ -113,11 +114,10 @@ public final class CertificateValidator {
} }
// User-ID signatures (certifications, revocations) // User-ID signatures (certifications, revocations)
Iterator<String> userIds = primaryKey.getUserIDs(); List<String> userIds = KeyRingUtils.getUserIdsIgnoringInvalidUTF8(primaryKey);
Map<String, List<PGPSignature>> userIdSignatures = new ConcurrentHashMap<>(); Map<String, List<PGPSignature>> userIdSignatures = new ConcurrentHashMap<>();
while (userIds.hasNext()) { for (String userId : userIds) {
List<PGPSignature> signaturesOnUserId = new ArrayList<>(); List<PGPSignature> signaturesOnUserId = new ArrayList<>();
String userId = userIds.next();
Iterator<PGPSignature> userIdSigs = primaryKey.getSignaturesForID(userId); Iterator<PGPSignature> userIdSigs = primaryKey.getSignaturesForID(userId);
while (userIdSigs.hasNext()) { while (userIdSigs.hasNext()) {
PGPSignature userIdSig = userIdSigs.next(); PGPSignature userIdSig = userIdSigs.next();

View file

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

View file

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