mirror of
https://github.com/pgpainless/pgpainless.git
synced 2024-12-22 19:08:00 +01:00
Fix detection of signed messages when verification keys are missing
Fixes #187, supersedes #189
This commit is contained in:
parent
0c122c1643
commit
33f516efe8
2 changed files with 67 additions and 3 deletions
|
@ -9,8 +9,10 @@ import java.io.EOFException;
|
|||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
|
@ -45,6 +47,7 @@ import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
|
|||
import org.pgpainless.exception.MessageNotIntegrityProtectedException;
|
||||
import org.pgpainless.exception.MissingDecryptionMethodException;
|
||||
import org.pgpainless.exception.MissingLiteralDataException;
|
||||
import org.pgpainless.exception.SignatureValidationException;
|
||||
import org.pgpainless.exception.UnacceptableAlgorithmException;
|
||||
import org.pgpainless.exception.WrongConsumingMethodException;
|
||||
import org.pgpainless.implementation.ImplementationFactory;
|
||||
|
@ -71,6 +74,7 @@ public final class DecryptionStreamFactory {
|
|||
private final OpenPgpMetadata.Builder resultBuilder = OpenPgpMetadata.getBuilder();
|
||||
private final List<OnePassSignatureCheck> onePassSignatureChecks = new ArrayList<>();
|
||||
private final List<DetachedSignatureCheck> detachedSignatureChecks = new ArrayList<>();
|
||||
private final Map<Long, OnePassSignatureCheck> onePassSignaturesWithMissingCert = new HashMap<>();
|
||||
|
||||
private static final PGPContentVerifierBuilderProvider verifierBuilderProvider =
|
||||
ImplementationFactory.getInstance().getPGPContentVerifierBuilderProvider();
|
||||
|
@ -96,6 +100,8 @@ public final class DecryptionStreamFactory {
|
|||
long issuerKeyId = SignatureUtils.determineIssuerKeyId(signature);
|
||||
PGPPublicKeyRing signingKeyRing = findSignatureVerificationKeyRing(issuerKeyId);
|
||||
if (signingKeyRing == null) {
|
||||
SignatureValidationException ex = new SignatureValidationException("Missing verification certificate " + Long.toHexString(issuerKeyId));
|
||||
resultBuilder.addInvalidDetachedSignature(new SignatureVerification(signature, null), ex);
|
||||
continue;
|
||||
}
|
||||
PGPPublicKey signingKey = signingKeyRing.getPublicKey(issuerKeyId);
|
||||
|
@ -105,7 +111,8 @@ public final class DecryptionStreamFactory {
|
|||
DetachedSignatureCheck detachedSignature = new DetachedSignatureCheck(signature, signingKeyRing, signingKeyIdentifier);
|
||||
detachedSignatureChecks.add(detachedSignature);
|
||||
} catch (PGPException e) {
|
||||
LOGGER.warn("Cannot verify detached signature made by {}. Reason: {}", signingKeyIdentifier, e.getMessage(), e);
|
||||
SignatureValidationException ex = new SignatureValidationException("Cannot verify detached signature made by " + signingKeyIdentifier + ".", e);
|
||||
resultBuilder.addInvalidDetachedSignature(new SignatureVerification(signature, signingKeyIdentifier), ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -223,7 +230,7 @@ public final class DecryptionStreamFactory {
|
|||
.setModificationDate(pgpLiteralData.getModificationTime())
|
||||
.setFileEncoding(StreamEncoding.fromCode(pgpLiteralData.getFormat()));
|
||||
|
||||
if (onePassSignatureChecks.isEmpty()) {
|
||||
if (onePassSignatureChecks.isEmpty() && onePassSignaturesWithMissingCert.isEmpty()) {
|
||||
LOGGER.debug("No OnePassSignatures found -> We are done");
|
||||
return literalDataInputStream;
|
||||
}
|
||||
|
@ -237,6 +244,16 @@ public final class DecryptionStreamFactory {
|
|||
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) {
|
||||
};
|
||||
|
@ -488,7 +505,7 @@ public final class DecryptionStreamFactory {
|
|||
// Find public key
|
||||
PGPPublicKeyRing verificationKeyRing = findSignatureVerificationKeyRing(keyId);
|
||||
if (verificationKeyRing == null) {
|
||||
LOGGER.debug("Missing verification key from {}", Long.toHexString(keyId));
|
||||
onePassSignaturesWithMissingCert.put(keyId, new OnePassSignatureCheck(signature, null));
|
||||
return;
|
||||
}
|
||||
PGPPublicKey verificationKey = verificationKeyRing.getPublicKey(keyId);
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package org.pgpainless.decryption_verification;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import org.bouncycastle.openpgp.PGPException;
|
||||
import org.bouncycastle.util.io.Streams;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.pgpainless.PGPainless;
|
||||
|
||||
public class SignedMessageVerificationWithoutCertIsStillSigned {
|
||||
|
||||
private static final String message = "-----BEGIN PGP MESSAGE-----\n" +
|
||||
"\n" +
|
||||
"owGbwMvMwCGmFN+gfIiXM5zxtG4SQ2Iw74rgzPS81BSFktSKEoW0/CKFlNS0xNKc\n" +
|
||||
"Eoe0nPzy5KLKghK9ktTiEq6OXhYGMQ4GUzFFFtvXL7+VX9252+LpIheYcaxMQLMO\n" +
|
||||
"iMtg183AxSkAUynizshwbBMnx4e4tn6NgJYtG/od3HL1y26GvpgqUtr2o37HpC+v\n" +
|
||||
"GRmudmly/g+Osdt3t6Rb+8t8i8Y94ZJ3P/zNlk015FihXM0JAA==\n" +
|
||||
"=A8uF\n" +
|
||||
"-----END PGP MESSAGE-----\n";
|
||||
|
||||
@Test
|
||||
public void verifyMissingVerificationCertOptionStillResultsInMessageIsSigned() throws IOException, PGPException {
|
||||
ConsumerOptions withoutVerificationCert = new ConsumerOptions();
|
||||
DecryptionStream verificationStream = PGPainless.decryptAndOrVerify()
|
||||
.onInputStream(new ByteArrayInputStream(message.getBytes(StandardCharsets.UTF_8)))
|
||||
.withOptions(withoutVerificationCert);
|
||||
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
Streams.pipeAll(verificationStream, out);
|
||||
verificationStream.close();
|
||||
|
||||
OpenPgpMetadata metadata = verificationStream.getResult();
|
||||
|
||||
assertTrue(metadata.isSigned(), "Message is signed, even though we miss the verification cert.");
|
||||
assertFalse(metadata.isVerified(), "Message is not verified because we lack the verification cert.");
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue