1
0
Fork 0
mirror of https://github.com/pgpainless/pgpainless.git synced 2025-12-10 14:21:09 +01:00

Fix decryption of signed messages created with PGPainless < 0.2.10

This commit is contained in:
Paul Schaub 2021-10-23 16:44:40 +02:00
parent 2b2639bde7
commit 963a8170da
Signed by: vanitasvitae
GPG key ID: 62BEE9264BF17311
3 changed files with 200 additions and 46 deletions

View file

@ -34,7 +34,6 @@ import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPSecretKey;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSignature;
import org.bouncycastle.openpgp.PGPSignatureList;
import org.bouncycastle.openpgp.PGPUtil;
import org.bouncycastle.openpgp.operator.KeyFingerPrintCalculator;
import org.bouncycastle.openpgp.operator.PBEDataDecryptorFactory;
@ -150,13 +149,13 @@ public final class DecryptionStreamFactory {
// to allow for detached signature verification.
LOGGER.debug("The message appears to not be an OpenPGP message. This is probably data signed with detached signatures?");
bufferedIn.reset();
inputStream = wrapInVerifySignatureStream(bufferedIn);
inputStream = wrapInVerifySignatureStream(bufferedIn, objectFactory);
} catch (IOException e) {
if (e.getMessage().contains("invalid armor") || e.getMessage().contains("invalid header encountered")) {
// We falsely assumed the data to be armored.
LOGGER.debug("The message is apparently not armored.");
bufferedIn.reset();
inputStream = wrapInVerifySignatureStream(bufferedIn);
inputStream = wrapInVerifySignatureStream(bufferedIn, objectFactory);
} else {
throw e;
}
@ -166,10 +165,10 @@ public final class DecryptionStreamFactory {
(decoderStream instanceof ArmoredInputStream) ? decoderStream : null);
}
private InputStream wrapInVerifySignatureStream(InputStream bufferedIn) {
private InputStream wrapInVerifySignatureStream(InputStream bufferedIn, PGPObjectFactory objectFactory) {
return new SignatureInputStream.VerifySignatures(
bufferedIn, onePassSignatureChecks,
detachedSignatureChecks, options,
bufferedIn, objectFactory, onePassSignatureChecks,
onePassSignaturesWithMissingCert, detachedSignatureChecks, options,
resultBuilder);
}
@ -222,7 +221,7 @@ public final class DecryptionStreamFactory {
throws PGPException, IOException {
LOGGER.debug("Depth {}: Encountered PGPOnePassSignatureList of size {}", depth, onePassSignatures.size());
initOnePassSignatures(onePassSignatures);
return processPGPPackets(objectFactory, ++depth);
return processPGPPackets(objectFactory, depth);
}
private InputStream processPGPLiteralData(@Nonnull PGPObjectFactory objectFactory, PGPLiteralData pgpLiteralData, int depth) throws IOException {
@ -238,48 +237,11 @@ public final class DecryptionStreamFactory {
return literalDataInputStream;
}
// Parse signatures from message
PGPSignatureList signatures = parseSignatures(objectFactory);
List<PGPSignature> signatureList = SignatureUtils.toList(signatures);
// Set signatures as comparison sigs in OPS checks
for (int i = 0; i < onePassSignatureChecks.size(); i++) {
int reversedIndex = onePassSignatureChecks.size() - i - 1;
onePassSignatureChecks.get(i).setSignature(signatureList.get(reversedIndex));
}
for (PGPSignature signature : signatureList) {
if (onePassSignaturesWithMissingCert.containsKey(signature.getKeyID())) {
OnePassSignatureCheck check = onePassSignaturesWithMissingCert.remove(signature.getKeyID());
check.setSignature(signature);
resultBuilder.addInvalidInbandSignature(new SignatureVerification(signature, null),
new SignatureValidationException("Missing verification certificate " + Long.toHexString(signature.getKeyID())));
}
}
return new SignatureInputStream.VerifySignatures(literalDataInputStream,
onePassSignatureChecks, detachedSignatureChecks, options, resultBuilder) {
return new SignatureInputStream.VerifySignatures(literalDataInputStream, objectFactory,
onePassSignatureChecks, onePassSignaturesWithMissingCert, detachedSignatureChecks, options, resultBuilder) {
};
}
private PGPSignatureList parseSignatures(PGPObjectFactory objectFactory) throws IOException {
PGPSignatureList signatureList = null;
Object pgpObject = objectFactory.nextObject();
while (pgpObject != null && signatureList == null) {
if (pgpObject instanceof PGPSignatureList) {
signatureList = (PGPSignatureList) pgpObject;
} else {
pgpObject = objectFactory.nextObject();
}
}
if (signatureList == null || signatureList.isEmpty()) {
throw new IOException("Verification failed - No Signatures found");
}
return signatureList;
}
private InputStream decryptSessionKey(@Nonnull PGPEncryptedDataList encryptedDataList)
throws PGPException {
Iterator<PGPEncryptedData> encryptedDataIterator = encryptedDataList.getEncryptedDataObjects();

View file

@ -10,15 +10,20 @@ import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
import javax.annotation.Nonnull;
import org.bouncycastle.openpgp.PGPObjectFactory;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPSignature;
import org.bouncycastle.openpgp.PGPSignatureList;
import org.pgpainless.PGPainless;
import org.pgpainless.exception.SignatureValidationException;
import org.pgpainless.policy.Policy;
import org.pgpainless.signature.CertificateValidator;
import org.pgpainless.signature.DetachedSignatureCheck;
import org.pgpainless.signature.OnePassSignatureCheck;
import org.pgpainless.signature.SignatureUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -32,19 +37,25 @@ public abstract class SignatureInputStream extends FilterInputStream {
private static final Logger LOGGER = LoggerFactory.getLogger(VerifySignatures.class);
private final PGPObjectFactory objectFactory;
private final List<OnePassSignatureCheck> opSignatures;
private final Map<Long, OnePassSignatureCheck> opSignaturesWithMissingCert;
private final List<DetachedSignatureCheck> detachedSignatures;
private final ConsumerOptions options;
private final OpenPgpMetadata.Builder resultBuilder;
public VerifySignatures(
InputStream literalDataStream,
PGPObjectFactory objectFactory,
List<OnePassSignatureCheck> opSignatures,
Map<Long, OnePassSignatureCheck> onePassSignaturesWithMissingCert,
List<DetachedSignatureCheck> detachedSignatures,
ConsumerOptions options,
OpenPgpMetadata.Builder resultBuilder) {
super(literalDataStream);
this.objectFactory = objectFactory;
this.opSignatures = opSignatures;
this.opSignaturesWithMissingCert = onePassSignaturesWithMissingCert;
this.detachedSignatures = detachedSignatures;
this.options = options;
this.resultBuilder = resultBuilder;
@ -71,6 +82,7 @@ public abstract class SignatureInputStream extends FilterInputStream {
final boolean endOfStream = read == -1;
if (endOfStream) {
parseAndCombineSignatures();
verifyOnePassSignatures();
verifyDetachedSignatures();
} else {
@ -80,6 +92,51 @@ public abstract class SignatureInputStream extends FilterInputStream {
return read;
}
public void parseAndCombineSignatures() throws IOException {
// Parse signatures from message
PGPSignatureList signatures;
try {
signatures = parseSignatures(objectFactory);
} catch (IOException e) {
return;
}
List<PGPSignature> signatureList = SignatureUtils.toList(signatures);
// Set signatures as comparison sigs in OPS checks
for (int i = 0; i < opSignatures.size(); i++) {
int reversedIndex = opSignatures.size() - i - 1;
opSignatures.get(i).setSignature(signatureList.get(reversedIndex));
}
for (PGPSignature signature : signatureList) {
if (opSignaturesWithMissingCert.containsKey(signature.getKeyID())) {
OnePassSignatureCheck check = opSignaturesWithMissingCert.remove(signature.getKeyID());
check.setSignature(signature);
resultBuilder.addInvalidInbandSignature(new SignatureVerification(signature, null),
new SignatureValidationException("Missing verification certificate " + Long.toHexString(signature.getKeyID())));
}
}
}
private PGPSignatureList parseSignatures(PGPObjectFactory objectFactory) throws IOException {
PGPSignatureList signatureList = null;
Object pgpObject = objectFactory.nextObject();
while (pgpObject != null && signatureList == null) {
if (pgpObject instanceof PGPSignatureList) {
signatureList = (PGPSignatureList) pgpObject;
} else {
pgpObject = objectFactory.nextObject();
}
}
if (signatureList == null || signatureList.isEmpty()) {
throw new IOException("Verification failed - No Signatures found");
}
return signatureList;
}
private synchronized void verifyOnePassSignatures() {
Policy policy = PGPainless.getPolicy();
for (OnePassSignatureCheck opSignature : opSignatures) {